diff --git a/e2e/bats/build.bash b/e2e/bats/build.bash index 25eb5b5935..7347eabff2 100644 --- a/e2e/bats/build.bash +++ b/e2e/bats/build.bash @@ -58,3 +58,10 @@ teardown() { assert_command_fail dfx build assert_match 'import error, canister alias "random" not defined' } + +@test "build generates IDs every rebuild" { + assert_command dfx build + cp canisters/e2e_project/_canister.id previous_cid + assert_command dfx build + assert_command_fail diff canisters/e2e_project/_canister.id previous_cid +} diff --git a/src/dfx/src/commands/build.rs b/src/dfx/src/commands/build.rs index 5bc27823e4..1068f96efd 100644 --- a/src/dfx/src/commands/build.rs +++ b/src/dfx/src/commands/build.rs @@ -32,7 +32,10 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { let canister_pool = CanisterPool::load(env)?; // First build. slog::info!(logger, "Building canisters..."); - canister_pool.build_or_fail(BuildConfig::from_config(config.get_config()))?; + + // TODO: remove the forcing of generating canister id once we have an update flow. + canister_pool + .build_or_fail(BuildConfig::from_config(config.get_config()).with_generate_id(true))?; // If there is not a package.json, we don't have a frontend and can quit early. if !config.get_project_root().join("package.json").exists() || args.is_present("skip-frontend") diff --git a/src/dfx/src/lib/builders/mod.rs b/src/dfx/src/lib/builders/mod.rs index 24b8fb3335..86f8506242 100644 --- a/src/dfx/src/lib/builders/mod.rs +++ b/src/dfx/src/lib/builders/mod.rs @@ -58,6 +58,7 @@ pub trait CanisterBuilder { pub struct BuildConfig { profile: Profile, assets: bool, + pub generate_id: bool, metadata: BTreeMap, } @@ -66,6 +67,7 @@ impl BuildConfig { BuildConfig { profile: config.profile.unwrap_or(Profile::Debug), assets: false, + generate_id: false, metadata: BTreeMap::new(), } } @@ -73,6 +75,13 @@ impl BuildConfig { pub fn with_assets(self, assets: bool) -> Self { Self { assets, ..self } } + + pub fn with_generate_id(self, generate_id: bool) -> Self { + Self { + generate_id, + ..self + } + } } pub struct BuilderPool { diff --git a/src/dfx/src/lib/models/canister.rs b/src/dfx/src/lib/models/canister.rs index b3afb0edd3..7fdc675efe 100644 --- a/src/dfx/src/lib/models/canister.rs +++ b/src/dfx/src/lib/models/canister.rs @@ -102,6 +102,42 @@ impl CanisterPool { None } + pub fn generate_canister_id(&self, force: bool) -> DfxResult { + // Write all canister IDs if needed. + for canister in &self.canisters { + let canister_info = &canister.info; + + let canister_id = if force { + None + } else { + canister_info.get_canister_id() + }; + let canister_id = match canister_id { + Some(cid) => cid, + None => { + std::fs::create_dir_all( + canister_info + .get_canister_id_path() + .parent() + .expect("Cannot use root."), + )?; + let cid = canister_info.generate_canister_id()?; + std::fs::write( + canister_info.get_canister_id_path(), + cid.clone().into_blob().0, + ) + .map_err(DfxError::from)?; + + cid + } + }; + + slog::debug!(self.logger, " {} => {}", canister.get_name(), canister_id); + } + + Ok(()) + } + fn build_dependencies_graph(&self) -> DfxResult> { let mut graph: DiGraph = DiGraph::new(); let mut id_set: BTreeMap> = BTreeMap::new(); @@ -146,24 +182,8 @@ impl CanisterPool { /// Build all canisters, returning a vector of results of each builds. pub fn build(&self, build_config: BuildConfig) -> DfxResult>> { - // Write all canister IDs if needed. - for canister in &self.canisters { - slog::debug!(self.logger, " '{}'", canister.get_name()); - let canister_info = &canister.info; - - if canister_info.get_canister_id().is_none() { - std::fs::create_dir_all( - canister_info - .get_canister_id_path() - .parent() - .expect("Cannot use root."), - )?; - std::fs::write( - canister_info.get_canister_id_path(), - canister_info.generate_canister_id()?.into_blob().0, - ) - .map_err(DfxError::from)?; - } + if build_config.generate_id { + self.generate_canister_id(true)?; } let graph = self.build_dependencies_graph()?;