diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d4e6e07f74..fd995fbee6 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -5,6 +5,10 @@ == DFX +=== feat: Better build scripts for type:custom + +Build scripts now always receive a CWD of the DFX project root, instead of wherever `+dfx+` was invoked from, and a bare script `+script.sh+` can be specified without needing to prefix with `+./+`. + === feat: rust, custom, and asset canisters now include candid:service metadata Motoko canisters already included this metadata. diff --git a/Cargo.lock b/Cargo.lock index 2615725f72..6ceceede2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1076,6 +1076,7 @@ dependencies = [ "walkdir", "walrus", "wasmparser 0.86.0", + "which", ] [[package]] @@ -4075,6 +4076,17 @@ dependencies = [ "webpki", ] +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/e2e/assets/custom_canister/build.sh b/e2e/assets/custom_canister/build.sh new file mode 100755 index 0000000000..b473e4cc25 --- /dev/null +++ b/e2e/assets/custom_canister/build.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +echo CUSTOM_CANISTER2_BUILD_DONE \ No newline at end of file diff --git a/e2e/assets/custom_canister/dfx.json b/e2e/assets/custom_canister/dfx.json index 7ad56db795..66454e0324 100644 --- a/e2e/assets/custom_canister/dfx.json +++ b/e2e/assets/custom_canister/dfx.json @@ -6,6 +6,12 @@ "candid": "main.did", "wasm": "main.wasm", "build": "echo CUSTOM_CANISTER_BUILD_DONE" + }, + "custom2": { + "type": "custom", + "candid": "main.did", + "wasm": "main.wasm", + "build": "build.sh" } }, "defaults": { diff --git a/e2e/tests-dfx/build.bash b/e2e/tests-dfx/build.bash index a13d44671e..3710d4e130 100644 --- a/e2e/tests-dfx/build.bash +++ b/e2e/tests-dfx/build.bash @@ -128,11 +128,26 @@ teardown() { install_asset custom_canister dfx_start dfx canister create --all - assert_command dfx build + assert_command dfx build custom assert_match "CUSTOM_CANISTER_BUILD_DONE" + assert_command dfx build custom2 + assert_match "CUSTOM_CANISTER2_BUILD_DONE" dfx canister install --all assert_command dfx canister call custom fromQuery + assert_command dfx canister call custom2 fromQuery +} + +@test "custom canister build script picks local executable first" { + install_asset custom_canister + dfx_start + dfx canister create custom2 + #shellcheck disable=SC2094 + cat <<<"$(jq '.canisters.custom2.build="ln"' dfx.json)" >dfx.json + mv ./build.sh ./ln + + assert_command dfx build custom2 + assert_match CUSTOM_CANISTER2_BUILD_DONE } @test "build succeeds with network parameter" { diff --git a/e2e/tests-dfx/error_context.bash b/e2e/tests-dfx/error_context.bash index a73b2ea519..3e15aff331 100644 --- a/e2e/tests-dfx/error_context.bash +++ b/e2e/tests-dfx/error_context.bash @@ -171,5 +171,5 @@ teardown() { # expect to see the name of the canister assert_match "custom_bad_build_step" # expect to see the underlying cause - assert_match "No such file or directory" + assert_match "Cannot find command or file" } diff --git a/src/dfx/Cargo.toml b/src/dfx/Cargo.toml index 599a546f2e..e98dd6a41e 100644 --- a/src/dfx/Cargo.toml +++ b/src/dfx/Cargo.toml @@ -84,6 +84,7 @@ url = "2.1.0" walkdir = "2.2.9" walrus = "0.19.0" wasmparser = "0.86.0" +which = "4.2.5" [dependencies.ic-agent] version = "0.17.0" diff --git a/src/dfx/src/lib/builders/custom.rs b/src/dfx/src/lib/builders/custom.rs index 77edd3c65f..0fbe0ffa2c 100644 --- a/src/dfx/src/lib/builders/custom.rs +++ b/src/dfx/src/lib/builders/custom.rs @@ -14,8 +14,8 @@ use ic_types::principal::Principal as CanisterId; use serde::Deserialize; use slog::info; use slog::Logger; -use std::path::PathBuf; -use std::process::Stdio; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; /// Set of extras that can be specified in the dfx.json. struct CustomBuilderExtra { @@ -139,7 +139,8 @@ impl CanisterBuilder for CustomBuilder { .with_context(|| format!("Cannot parse command '{}'.", command))?; // No commands, noop. if !args.is_empty() { - run_command(args, &vars).with_context(|| format!("Failed to run {}.", command))?; + run_command(args, &vars, info.get_workspace_root()) + .with_context(|| format!("Failed to run {}.", command))?; } } @@ -190,12 +191,17 @@ impl CanisterBuilder for CustomBuilder { } } -fn run_command(args: Vec, vars: &[super::Env<'_>]) -> DfxResult<()> { +fn run_command(args: Vec, vars: &[super::Env<'_>], cwd: &Path) -> DfxResult<()> { let (command_name, arguments) = args.split_first().unwrap(); - - let mut cmd = std::process::Command::new(command_name); + let canonicalized = cwd + .join(command_name) + .canonicalize() + .or_else(|_| which::which(command_name)) + .map_err(|_| anyhow!("Cannot find command or file {command_name}"))?; + let mut cmd = Command::new(&canonicalized); cmd.args(arguments) + .current_dir(cwd) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); @@ -205,7 +211,7 @@ fn run_command(args: Vec, vars: &[super::Env<'_>]) -> DfxResult<()> { let output = cmd .output() - .with_context(|| format!("Error executing custom build step {:#?}", cmd))?; + .with_context(|| format!("Error executing custom build step {cmd:#?}"))?; if output.status.success() { Ok(()) } else {