Skip to content

icp-dev-env-rust: missing clang prevents cargo build --target wasm32-unknown-unknown for canisters with C dependencies #40

Description

@marc0olo

Problem

The icp-dev-env-rust image cannot build Rust canisters that have C/C++ FFI dependencies (e.g. secp256k1, ring, openssl) when cross-compiling to wasm32-unknown-unknown.

The root cause is that the cc Rust crate — used internally by secp256k1-sys and similar — selects clang as the C compiler when targeting wasm32-unknown-unknown. gcc has no wasm32 backend, so clang is required. The current image has neither clang nor llvm-ar installed.

Current Dockerfile

FROM rust:1.95.0-slim-bookworm
RUN apt-get -yq update && apt-get -yq upgrade && apt-get -yqq install --no-install-recommends \
    curl ca-certificates git jq make libdbus-1-3 && \
    apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/*
#                                                    ^^^^^^^^^^^^^^^^^^^
# Package lists are wiped after install (standard slim image practice).
# Any subsequent apt-get install in CI requires apt-get update first.

RUN rustup target add wasm32-unknown-unknown

Observed error

When running cargo build --target wasm32-unknown-unknown --release inside the container for any crate with a C dependency (bitcoin, secp256k1, ring, etc.):

error occurred in cc-rs: failed to find tool "clang": No such file or directory (os error 2)

Important: The @dfinity/rust icp-cli recipe runs cargo build --target wasm32-unknown-unknown --release verbatim with no C toolchain setup. So icp build and icp deploy fail identically for affected canisters.

Real-world impact

Discovered while migrating rust/threshold-schnorr in dfinity/examples to icp-cli. The canister uses bitcoin = "0.32" (pulls in secp256k1-sys). Neither cargo build nor icp build/icp deploy work in the container without clang.

Affected: any canister using bitcoin, secp256k1, k256, p256, ring, or any crate with a C build script targeting wasm32.

Current workaround

Add an explicit step before any build in affected CI workflows:

- name: Install clang
  run: apt-get update && apt-get install -y --no-install-recommends clang

apt-get update is required because the slim image wipes /var/lib/apt/lists/*. This adds ~30-60 s per CI job and must be repeated in every workflow.

Proposed fix

Add clang to the apt install step in rust/Dockerfile:

RUN apt-get -yq update && apt-get -yq upgrade && apt-get -yqq install --no-install-recommends \
    curl ca-certificates git jq make libdbus-1-3 clang && \
    apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/*

On Debian Bookworm, clang installs the current default LLVM release and creates /usr/bin/clang. No version pinning needed.

Tradeoff: ~80 MB image size increase. Once in the image, no per-workflow overhead, and all cargo and icp-cli workflows work uniformly for any Rust canister.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions