Skip to content

docs: resolve the cargo target dir in rust build steps - #762

Merged
raymondk merged 7 commits into
mainfrom
fix/recipe-target-dir-docs
Sep 8, 2026
Merged

docs: resolve the cargo target dir in rust build steps#762
raymondk merged 7 commits into
mainfrom
fix/recipe-target-dir-docs

Conversation

@marc0olo

@marc0olo marc0olo commented Sep 8, 2026

Copy link
Copy Markdown
Member

Follow-up after reviewing #760.

Rust build steps that hardcode target/... compile successfully and then fail to find the wasm, because cargo does not necessarily write to ./target.

The dominant case is not an exotic env var. A build step runs in the canister directory (crates/icp/src/operations/build.rs:62 passes canister_path), so in the standard Rust monorepo layout the path is simply wrong:

repo/
├── Cargo.toml          # workspace root — cargo writes target/ HERE
├── target/
└── canisters/backend/
    ├── Cargo.toml      # workspace member
    └── icp.yaml        # build step runs HERE, so "target/" → canisters/backend/target → missing

CARGO_TARGET_DIR and build.target-dir move it too. Reproduced against examples/icp-rust:

mv: rename target/wasm32-unknown-unknown/release/icp_template_rust.wasm to …: No such file or directory

The official rust recipe fixed this in rust-v3.1.0. This applies the same lookup wherever we still hardcode the path, and bumps our pins.

Changes

  • All rust build snippets in docs/ resolve the target dir via cargo metadata, in a |- block so the lookup and the copy read as two lines.
  • examples/icp-rust, examples/icp-sync-plugin likewise. Both examples are flat, so this is defensive for them as-run — it matters because they are templates people copy into projects that are not flat.
  • scripts/validate-examples.sh no longer assumes ./target/debug. This one was a live bug in our own tooling.
  • Every @dfinity/rust pin bumped v3.0.0 → v3.4.0, in both the type: and release-URL forms. v3.0.0 predates the recipe's own fix.
  • docs/guides/creating-recipes.md gains a short section explaining the rule and the |- block form.
  • package is documented as optional, which the bump exposed: examples/icp-rust-recipe called it a required field in both its manifest comment and its README, but it has defaulted to the canister name since rust-v3.3.0. Dropped from the five snippets where it only repeated the canister name; kept where the crate name genuinely differs, and in the custom-recipe examples whose own package parameter is unrelated.

Notes for the reviewer

  • The lookup and the copy are one commands: entry, written as a |- block. They must share a shell — each entry gets its own sh -c, so a naive split leaves $TARGET_DIR empty and fails with cp: /wasm32-unknown-unknown/…: No such file or directory — but they do not have to share a line.
  • cp, not mv, matching the recipe: cargo materializes the top-level artifact from deps/, so a build step has no reason to remove its output. (It does not cause a rebuild either way — cargo re-creates the top-level file in ~0.03s.)
  • The sed is inherited from the recipe, which avoids jq "in order not to introduce a dependency on jq". There is no better form on stable: --artifact-dir would remove the lookup entirely but is nightly-only (cargo#6790, open since 2019), cargo locate-project --workspace misses CARGO_TARGET_DIR, and tr/cut variants break on paths containing a comma.
  • Only validate-examples.sh guards against an unresolved lookup, because an empty result there was genuinely silent — it prepended /debug to PATH and surfaced later as icp: not found. A build step needs no guard: the lookup only runs after a successful cargo build (a step aborts on its first failing command), and an empty result makes cp fail and fail the step.
  • path: on a plugin sync step is a plain relative path with no expansion (crates/icp/src/manifest/adapter/plugin.rs:136), so it cannot reference the target dir. The sync plugin guide now says so.

Verification

  • icp build on examples/icp-rust, with and without CARGO_TARGET_DIR: fails on main, passes here, no stray target/ in the source tree, artifact left in place by cp.
  • Confirmed a naive split into two commands: entries does fail, and that the |- block does not.
  • icp build with package omitted and the canister named after the crate: the recipe derives --package icp-canister and icp_canister.wasm, and the build succeeds.
  • icp project show on the changed examples; the bumped pin resolves and renders v3.4.0's lookup.
  • Parsed all 97 non-template YAML blocks across the changed docs.

Follow-up (separate repo)

These docs now differ from the recipe itself, which still uses the one-line form. The |- block is worth proposing upstream in icp-cli-recipes.

Cargo writes to the workspace target directory, which is not `./target` when
the canister crate is a workspace member, or when `CARGO_TARGET_DIR` or
`build.target-dir` is set. Build steps that hardcode `target/...` compile
successfully and then fail to find the wasm.

Resolve it via `cargo metadata`, matching the official rust recipe, in the
examples and in the guides that teach writing a rust build step. The lookup and
the command using it share one line because each `commands:` entry runs in its
own shell.

Also bumps the `@dfinity/rust` pins from v3.0.0 to v3.4.0; the equivalent fix
landed in the recipe in v3.1.0, so the pinned version predates it.
Applies the same `cargo metadata` lookup to the snippets where a rust build is
scaffolding for another topic, so every copyable build step in the docs is
correct regardless of where cargo writes.

The two `path:` fields on plugin sync steps keep a plain relative path: the
field is not expanded, which is now called out in the sync plugin guide.
Copilot AI balanced review requested due to automatic review settings September 8, 2026 09:34
@marc0olo
marc0olo requested a review from a team as a code owner September 8, 2026 09:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes consistently address the documented Cargo target-dir mismatch with low risk, with only a minor hardening suggestion noted in review comments.

Pull request overview

This PR updates Rust build examples and documentation to correctly locate Cargo’s actual target directory (including workspace and CARGO_TARGET_DIR/build.target-dir cases), preventing build steps from succeeding but then failing to find the produced .wasm.

Changes:

  • Replace hardcoded target/... paths with a cargo metadata-derived target directory in Rust build snippets across docs and examples.
  • Bump pinned @dfinity/rust recipe references from v3.0.0 to v3.4.0 throughout documentation and examples.
  • Update the examples validation script to locate the built icp binary via Cargo’s target directory rather than assuming ./target/debug.
File summaries
File Description
scripts/validate-examples.sh Resolve Cargo target dir before adding the icp binary directory to PATH.
examples/icp-sync-plugin/icp.yaml Use Cargo metadata target dir for the canister wasm move step.
examples/icp-rust/icp.yaml Use Cargo metadata target dir for the canister wasm move step.
examples/icp-rust-recipe/README.md Bump the documented recipe pin to @dfinity/rust@v3.4.0.
examples/icp-rust-recipe/icp.yaml Bump the recipe pin to @dfinity/rust@v3.4.0.
docs/reference/environment-variables.md Update Rust build snippet to copy wasm from Cargo metadata target dir.
docs/reference/configuration.md Update Rust build snippets and recipe pin references to v3.4.0.
docs/reference/canister-settings.md Update Rust build snippet to copy wasm from Cargo metadata target dir.
docs/migration/from-dfx.md Update Rust recipe pins and build snippet to use Cargo metadata target dir.
docs/guides/writing-sync-plugins.md Clarify where plugin wasm lands and limitations of sync.steps[].path expansion.
docs/guides/using-recipes.md Bump @dfinity/rust pin and update Rust build snippet to use metadata target dir.
docs/guides/managing-environments.md Update Rust build snippet to copy wasm from Cargo metadata target dir.
docs/guides/creating-templates.md Bump Rust recipe pin in template example to v3.4.0.
docs/guides/creating-recipes.md Update recipe examples to use metadata target dir; add guidance explaining why.
docs/concepts/recipes.md Bump recipe pins and update Rust build snippet to use metadata target dir.
docs/concepts/project-model.md Update Rust build snippet to copy wasm from Cargo metadata target dir.
docs/concepts/build-deploy-sync.md Update Rust build snippet to copy wasm from Cargo metadata target dir.
Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/validate-examples.sh
@marc0olo
marc0olo marked this pull request as draft September 8, 2026 10:13
An unmatched sed pattern left TARGET_DIR empty, which silently prepended
`/debug` to PATH and surfaced later as a confusing `icp: not found`.
The lookup and the copy have to share one shell, but they do not have to share
one line: a `|-` block is a single `commands:` entry, so the two commands stay
readable and `$TARGET_DIR` still carries across.

Switches the four remaining `mv` uses to `cp`, matching the recipe. Cargo
materializes the top-level artifact from `deps/`, so a build step has no reason
to remove its output.

`docs/concepts/recipes.md` keeps the one-line form: it shows what the official
recipe expands to and should stay identical to it.
The page says the recipe expands to "something like" and shows two commands
where the real recipe expands to nineteen, so it was never a byte-accurate
mirror - no reason for the only remaining one-liner to live here.
The bump to v3.4.0 made two statements false: `examples/icp-rust-recipe`
described `package` as a required field in both its manifest comment and its
README. It defaults to the canister name.

Documents the default in the rust recipe section of the usage guide, and drops
the declaration from the five snippets where it just repeated the canister
name. Kept where the crate name genuinely differs, and in the custom-recipe
examples, whose own `package` parameter is unrelated.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The documentation, examples, and validation script consistently resolve Cargo output locations, and the referenced recipe release exists.

Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@marc0olo
marc0olo marked this pull request as ready for review September 8, 2026 11:06
@marc0olo
marc0olo marked this pull request as draft September 8, 2026 12:19
@marc0olo
marc0olo marked this pull request as ready for review September 8, 2026 12:21
@raymondk
raymondk merged commit 4eaa093 into main Sep 8, 2026
104 checks passed
@raymondk
raymondk deleted the fix/recipe-target-dir-docs branch September 8, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants