diff --git a/distributed-canisters.nix b/distributed-canisters.nix index b10234a15d..cf40e30a34 100644 --- a/distributed-canisters.nix +++ b/distributed-canisters.nix @@ -33,7 +33,7 @@ pkgs.runCommandNoCCLocal "distributed-canisters" {} '' for ext in did wasm do - cp $canister_root/canisters/$canister_name/$canister_name.$ext $output_dir + cp $canister_root/.dfx/local/canisters/$canister_name/$canister_name.$ext $output_dir done done '' diff --git a/e2e/bats/bootstrap.bash b/e2e/bats/bootstrap.bash index 40ff79a6d0..8a08648aac 100644 --- a/e2e/bats/bootstrap.bash +++ b/e2e/bats/bootstrap.bash @@ -20,10 +20,10 @@ teardown() { ID=$(dfx canister id hello) assert_command curl http://localhost:8000/_/candid?canisterId="$ID" -o ./web.txt - assert_command diff canisters/hello/hello.did ./web.txt + assert_command diff .dfx/local/canisters/hello/hello.did ./web.txt assert_command curl http://localhost:8000/_/candid?canisterId="$ID"\&format=js -o ./web.txt # Relax diff as it's produced by two different compilers. - assert_command diff --ignore-all-space --ignore-blank-lines canisters/hello/hello.did.js ./web.txt + assert_command diff --ignore-all-space --ignore-blank-lines .dfx/local/canisters/hello/hello.did.js ./web.txt } @test "forbid starting webserver with a forwarded port" { diff --git a/e2e/bats/build.bash b/e2e/bats/build.bash index 03c0304738..df7a20ce06 100644 --- a/e2e/bats/build.bash +++ b/e2e/bats/build.bash @@ -44,9 +44,9 @@ teardown() { dfx_start dfx canister create --all assert_command dfx build - cp canisters/e2e_project/main.wasm ./old.wasm + cp .dfx/local/canisters/e2e_project/main.wasm ./old.wasm assert_command dfx build - assert_command diff canisters/e2e_project/main.wasm ./old.wasm + assert_command diff .dfx/local/canisters/e2e_project/main.wasm ./old.wasm } @test "build outputs warning" { @@ -97,3 +97,21 @@ teardown() { assert_command dfx canister --network tungsten create --all assert_command dfx build --network tungsten } + +@test "build output for local network is in expected directory" { + dfx_start + dfx canister create --all + assert_command dfx build + assert_command ls .dfx/local/canisters/e2e_project/ + assert_command ls .dfx/local/canisters/e2e_project/main.wasm +} + +@test "build output for non-local network is in expected directory" { + dfx_start + assert_command dfx config networks.tungsten.providers '[ "http://127.0.0.1:8000" ]' + dfx canister --network tungsten create --all + assert_command dfx build --network tungsten + assert_command ls .dfx/tungsten/canisters/e2e_project/ + assert_command ls .dfx/tungsten/canisters/e2e_project/main.wasm +} + diff --git a/src/dfx/src/commands/bootstrap.rs b/src/dfx/src/commands/bootstrap.rs index 4ea32b3195..3fe5a037d3 100644 --- a/src/dfx/src/commands/bootstrap.rs +++ b/src/dfx/src/commands/bootstrap.rs @@ -3,7 +3,7 @@ use crate::lib::environment::Environment; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::message::UserMessage; use crate::lib::network::network_descriptor::NetworkDescriptor; -use crate::lib::provider::get_network_descriptor; +use crate::lib::provider::{get_network_context, get_network_descriptor}; use crate::lib::webserver::webserver; use clap::{App, Arg, ArgMatches, SubCommand}; use slog::info; @@ -55,12 +55,22 @@ pub fn construct() -> App<'static, 'static> { /// Runs the bootstrap server. pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { let logger = env.get_logger(); + let config = env + .get_config() + .ok_or(DfxError::CommandMustBeRunInAProject)?; let config_defaults = get_config_defaults_from_file(env); let base_config_bootstrap = config_defaults.get_bootstrap().to_owned(); let config_bootstrap = apply_arguments(&base_config_bootstrap, env, args)?; - let build_output_root = PathBuf::from(config_defaults.get_build().get_output()); let network_descriptor = get_network_descriptor(env, args)?; + + let network_name = get_network_context()?; + + let build_root = config_defaults.get_build().get_output(); + + let build_output_root = config.get_temp_path().join(network_name); + let build_output_root = build_output_root.join(build_root); + let providers = get_providers(&network_descriptor)?; let (sender, receiver) = crossbeam::unbounded(); diff --git a/src/dfx/src/commands/build.rs b/src/dfx/src/commands/build.rs index 83f0f9eedc..0a5c1010b6 100644 --- a/src/dfx/src/commands/build.rs +++ b/src/dfx/src/commands/build.rs @@ -67,7 +67,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { // TODO: remove the forcing of generating canister id once we have an update flow. canister_pool.build_or_fail( - BuildConfig::from_config(&config) + BuildConfig::from_config(&config)? .with_skip_frontend(args.is_present("skip-frontend")) .with_build_mode_check(build_mode_check), )?; diff --git a/src/dfx/src/commands/start.rs b/src/dfx/src/commands/start.rs index 366607e0ff..f9ff842da2 100644 --- a/src/dfx/src/commands/start.rs +++ b/src/dfx/src/commands/start.rs @@ -2,7 +2,7 @@ use crate::config::dfinity::Config; use crate::lib::environment::Environment; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::message::UserMessage; -use crate::lib::provider::get_network_descriptor; +use crate::lib::provider::{get_network_context, get_network_descriptor}; use crate::lib::proxy::{CoordinateProxy, ProxyConfig}; use crate::lib::proxy_process::spawn_and_update_proxy; use crate::lib::replica_config::ReplicaConfig; @@ -157,11 +157,15 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { // By default we reach to no external IC nodes. let providers = Vec::new(); - let build_output_root = - PathBuf::from(config.get_config().get_defaults().get_build().get_output()); - let network_descriptor = get_network_descriptor(env, args)?; + let network_name = get_network_context()?; + + let build_root = config.get_config().get_defaults().get_build().get_output(); + + let build_output_root = config.get_temp_path().join(network_name); + let build_output_root = build_output_root.join(build_root); + let proxy_config = ProxyConfig { logger: env.get_logger().clone(), client_api_port: address_and_port.port(), diff --git a/src/dfx/src/config/dfinity.rs b/src/dfx/src/config/dfinity.rs index 16a801afe2..51362f5c41 100644 --- a/src/dfx/src/config/dfinity.rs +++ b/src/dfx/src/config/dfinity.rs @@ -314,6 +314,9 @@ impl Config { pub fn get_path(&self) -> &PathBuf { &self.path } + pub fn get_temp_path(&self) -> PathBuf { + self.get_path().parent().unwrap().join(".dfx") + } pub fn get_json(&self) -> &Value { &self.json } diff --git a/src/dfx/src/lib/builders/mod.rs b/src/dfx/src/lib/builders/mod.rs index 2e61b52659..97b21afe25 100644 --- a/src/dfx/src/lib/builders/mod.rs +++ b/src/dfx/src/lib/builders/mod.rs @@ -2,7 +2,9 @@ use crate::config::dfinity::{Config, Profile}; use crate::lib::canister_info::CanisterInfo; use crate::lib::environment::Environment; use crate::lib::error::DfxResult; + use crate::lib::models::canister::CanisterPool; +use crate::lib::provider::get_network_context; use ic_agent::CanisterId; use std::path::PathBuf; use std::sync::Arc; @@ -84,17 +86,18 @@ pub struct BuildConfig { } impl BuildConfig { - pub fn from_config(config: &Config) -> Self { - let workspace_root = config.get_path().parent().unwrap(); - let config = config.get_config(); - let build_root = workspace_root.join(config.get_defaults().get_build().get_output()); - - BuildConfig { - profile: config.profile.unwrap_or(Profile::Debug), + pub fn from_config(config: &Config) -> DfxResult { + let config_intf = config.get_config(); + let network_name = get_network_context()?; + let build_root = config.get_temp_path().join(network_name); + let build_root = build_root.join(config_intf.get_defaults().get_build().get_output()); + + Ok(BuildConfig { + profile: config_intf.profile.unwrap_or(Profile::Debug), skip_frontend: false, build_mode_check: false, idl_root: build_root.join("idl/"), - } + }) } pub fn with_skip_frontend(self, skip_frontend: bool) -> Self { diff --git a/src/dfx/src/lib/canister_info.rs b/src/dfx/src/lib/canister_info.rs index 4c281343f9..f8837cd0f6 100644 --- a/src/dfx/src/lib/canister_info.rs +++ b/src/dfx/src/lib/canister_info.rs @@ -4,6 +4,7 @@ use crate::lib::canister_info::assets::AssetsCanisterInfo; use crate::lib::canister_info::custom::CustomCanisterInfo; use crate::lib::canister_info::motoko::MotokoCanisterInfo; use crate::lib::error::{DfxError, DfxResult}; +use crate::lib::provider::get_network_context; use ic_agent::CanisterId; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; @@ -47,7 +48,9 @@ impl CanisterInfo { ) -> DfxResult { let workspace_root = config.get_path().parent().unwrap(); let build_defaults = config.get_config().get_defaults().get_build(); - let build_root = workspace_root.join(build_defaults.get_output()); + let network_name = get_network_context()?; + let build_root = config.get_temp_path().join(network_name); + let build_root = build_root.join(build_defaults.get_output()); std::fs::create_dir_all(&build_root)?; let canister_map = (&config.get_config().canisters).as_ref().ok_or_else(|| { diff --git a/src/dfx/src/lib/error/mod.rs b/src/dfx/src/lib/error/mod.rs index d7ab42fca0..f4bb345b60 100644 --- a/src/dfx/src/lib/error/mod.rs +++ b/src/dfx/src/lib/error/mod.rs @@ -106,6 +106,9 @@ pub enum DfxError { /// Could not parse an URL for some reason. InvalidUrl(String, url::ParseError), + /// The value of the --network argument was not set. + ComputeNetworkNotSet, + /// The value of the --network argument was not found in dfx.json. ComputeNetworkNotFound(String), @@ -182,6 +185,9 @@ impl Display for DfxError { "The `_language-service` command is meant to be run by editors to start a language service. You probably don't want to run it from a terminal.\nIf you _really_ want to, you can pass the --force-tty flag.", )?; } + DfxError::ComputeNetworkNotSet => { + f.write_str("Expected to find a network context, but found none")?; + } DfxError::NoLocalNetworkProviderFound => { f.write_str("Expected there to be a local network with a bind address")?; } diff --git a/src/dfx/src/lib/provider.rs b/src/dfx/src/lib/provider.rs index 10aac5acbe..82e6b41585 100644 --- a/src/dfx/src/lib/provider.rs +++ b/src/dfx/src/lib/provider.rs @@ -3,18 +3,40 @@ use crate::lib::environment::{AgentEnvironment, Environment}; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::network::network_descriptor::NetworkDescriptor; use clap::ArgMatches; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; use url::Url; +lazy_static! { + static ref NETWORK_CONTEXT: Arc>> = Arc::new(RwLock::new(None)); +} + +fn set_network_context(args: &ArgMatches<'_>) { + let name = args.value_of("network").unwrap_or("local").to_string(); + + let mut n = NETWORK_CONTEXT.write().unwrap(); + *n = Some(name); +} + +pub fn get_network_context() -> DfxResult { + NETWORK_CONTEXT + .read() + .unwrap() + .clone() + .ok_or_else(|| DfxError::ComputeNetworkNotSet) +} + // always returns at least one url pub fn get_network_descriptor<'a>( env: &'a (dyn Environment + 'a), args: &ArgMatches<'_>, ) -> DfxResult { - let network_name = args.value_of("network").unwrap_or("local"); + set_network_context(args); let config = env .get_config() .ok_or(DfxError::CommandMustBeRunInAProject)?; let config = config.as_ref().get_config(); + let network_name = get_network_context()?; match config.get_network(&network_name) { Some(ConfigNetwork::ConfigNetworkProvider(network_provider)) => { let provider_urls = match &network_provider.providers {