Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions e2e/bats/assets/transitive_deps_canisters/a/main.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
actor {
public func greet(name : Text) : async Text {
return "Namaste, " # name # "!";
};
};
5 changes: 5 additions & 0 deletions e2e/bats/assets/transitive_deps_canisters/b/main.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
actor {
public func greet(name : Text) : async Text {
return "Hola, " # name # "!";
};
};
5 changes: 5 additions & 0 deletions e2e/bats/assets/transitive_deps_canisters/c/main.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
actor {
public func greet(name : Text) : async Text {
return "Hello, " # name # "!";
};
};
5 changes: 5 additions & 0 deletions e2e/bats/assets/transitive_deps_canisters/d/main.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
actor {
public func greet(name : Text) : async Text {
return "Hello, " # name # "!";
};
};
38 changes: 38 additions & 0 deletions e2e/bats/assets/transitive_deps_canisters/dfx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"canisters": {
"canister_a": {
"main": "./a/main.mo",
"type": "motoko"
},
"canister_b": {
"dependencies": ["canister_a"],
"main": "./b/main.mo",
"type": "motoko"
},
"canister_c": {
"dependencies": ["canister_b"],
"main": "./c/main.mo",
"type": "motoko"
},
"canister_d": {
"dependencies": ["canister_e"],
"main": "./d/main.mo",
"type": "motoko"
},
"canister_e": {
"dependencies": ["canister_d"],
"main": "./e/main.mo",
"type": "motoko"
}
},
"defaults": {
"build": {
"packtool": ""
}
},
"networks": {
"local": {
"bind": "127.0.0.1:8000"
}
}
}
5 changes: 5 additions & 0 deletions e2e/bats/assets/transitive_deps_canisters/e/main.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
actor {
public func greet(name : Text) : async Text {
return "Hello, " # name # "!";
};
};
1 change: 1 addition & 0 deletions e2e/bats/assets/transitive_deps_canisters/patch.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Do nothing
96 changes: 96 additions & 0 deletions e2e/bats/build_granular.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bats

load utils/_

setup() {
# We want to work from a temporary directory, different for every test.
cd $(mktemp -d -t dfx-e2e-XXXXXXXX)

dfx_new
}

teardown() {
dfx_stop
}

@test "direct dependencies are built" {
dfx_start
dfx canister create --all
#specify build for only assets_canister
dfx build e2e_project_assets

#validate direct dependency built and is callable
assert_command dfx canister install e2e_project
assert_command dfx canister call e2e_project greet World
}

@test "transitive dependencies are built" {
install_asset transitive_deps_canisters
dfx_start
dfx canister create --all
#install of tertiary dependency canister will fail since its not built
assert_command_fail dfx canister install canister_a
#specify build for primary canister
dfx build canister_c

#validate tertiary transitive dependency is built and callable
assert_command dfx canister install canister_a
assert_command dfx canister call canister_a greet World
assert_match '("Namaste, World!")'
}

@test "unspecified dependencies are not built" {
dfx_start
dfx canister create --all
# only build motoko canister
dfx build e2e_project
# validate assets canister wasn't built and can't be installed
assert_command_fail dfx canister install e2e_project_assets
assert_match "No such file or directory"
}


@test "manual build of specified canisters succeeds" {
install_asset assetscanister

dfx_start
dfx canister create e2e_project
dfx build e2e_project
assert_command dfx canister install e2e_project
assert_command dfx canister call e2e_project greet World

assert_command_fail dfx canister install e2e_project_assets
assert_match "Cannot find canister id. Please issue 'dfx canister create e2e_project_assets'."
dfx canister create e2e_project_assets
dfx build e2e_project_assets
dfx canister install e2e_project_assets

assert_command dfx canister call --query e2e_project_assets retrieve '("binary/noise.txt")'
assert_eq '(vec { 184; 1; 32; 128; 10; 119; 49; 50; 32; 0; 120; 121; 10; 75; 76; 11; 10; 106; 107; })'

assert_command dfx canister call --query e2e_project_assets retrieve '("text-with-newlines.txt")'
assert_eq '(vec { 99; 104; 101; 114; 114; 105; 101; 115; 10; 105; 116; 39; 115; 32; 99; 104; 101; 114; 114; 121; 32; 115; 101; 97; 115; 111; 110; 10; 67; 72; 69; 82; 82; 73; 69; 83; })'

}

@test "cyclic dependencies are detected" {
install_asset transitive_deps_canisters
dfx_start
dfx canister create --all
assert_command dfx build canister_e
assert_match "Possible circular dependency detected during evaluation of canister_d's dependency on canister_e."
}

@test "the all flag builds everything" {
dfx_start
dfx canister create --all
assert_command dfx build --all
assert_command dfx canister install --all
}


@test "the all flags conflicts with canister name" {
dfx_start
dfx canister create --all
assert_command_fail dfx build e2e_project --all
}
2 changes: 1 addition & 1 deletion e2e/bats/create.bash
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ teardown() {
@test "build fails without create" {
dfx_start
assert_command_fail dfx build
assert_match "Cannot find canister id. Please issue 'dfx canister create e2e_project'"
assert_match "Cannot find canister id."
}

@test "build fails if all canisters in project are not created" {
Expand Down
4 changes: 2 additions & 2 deletions e2e/bats/frontend.bash
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ teardown() {
@test "dfx start serves a frontend" {
dfx_start
dfx canister create --all
dfx build --skip-frontend
dfx build e2e_project

sleep 1
assert_command curl http://localhost:8000 # 8000 = default port.
Expand All @@ -31,7 +31,7 @@ teardown() {
cat <<<$(jq '.networks.local.bind="127.0.0.1:12345"' dfx.json) >dfx.json

dfx canister create --all
dfx build --skip-frontend
dfx build e2e_project

assert_command curl http://localhost:12345 # 8000 = default port.
assert_match "<html>"
Expand Down
28 changes: 19 additions & 9 deletions src/dfx/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ pub fn construct() -> App<'static, 'static> {
SubCommand::with_name("build")
.about(UserMessage::BuildCanister.to_str())
.arg(
Arg::with_name("skip-frontend")
.long("skip-frontend")
.takes_value(false)
.help(UserMessage::SkipFrontend.to_str()),
Arg::with_name("canister_name")
.takes_value(true)
.conflicts_with("all")
.help(UserMessage::BuildCanisterName.to_str())
.required(false),
)
.arg(
Arg::with_name("all")
.long("all")
.conflicts_with("canister_name")
.help(UserMessage::BuildAll.to_str())
.takes_value(false),
)
.arg(
Arg::with_name("check")
Expand Down Expand Up @@ -45,8 +53,12 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult {
env.get_cache().install()?;

let build_mode_check = args.is_present("check");
// First build.
let canister_pool = CanisterPool::load(&env, build_mode_check)?;

// Option can be None in which case --all was specified
let some_canister = args.value_of("canister_name");

// Get pool of canisters to build
let canister_pool = CanisterPool::load(&env, build_mode_check, some_canister)?;

// Create canisters on the replica and associate canister ids locally.
if args.is_present("check") {
Expand All @@ -66,9 +78,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult {
slog::info!(logger, "Building canisters...");

canister_pool.build_or_fail(
BuildConfig::from_config(&config)?
.with_skip_frontend(args.is_present("skip-frontend"))
.with_build_mode_check(build_mode_check),
BuildConfig::from_config(&config)?.with_build_mode_check(build_mode_check),
)?;

Ok(())
Expand Down
73 changes: 31 additions & 42 deletions src/dfx/src/lib/builders/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,43 +104,36 @@ impl CanisterBuilder for AssetsBuilder {
info: &CanisterInfo,
config: &BuildConfig,
) -> DfxResult {
if !config.skip_frontend {
let deps = match info.get_extra_value("dependencies") {
None => vec![],
Some(v) => Vec::<String>::deserialize(v).map_err(|_| {
DfxError::Unknown(String::from("Field 'dependencies' is of the wrong type"))
})?,
};
let dependencies = deps
.iter()
.map(|name| {
pool.get_first_canister_with_name(name)
.map(|c| c.canister_id())
.map_or_else(
|| Err(DfxError::UnknownCanisterNamed(name.clone())),
DfxResult::Ok,
)
})
.collect::<DfxResult<Vec<CanisterId>>>()?;

build_frontend(
pool.get_logger(),
info.get_workspace_root(),
&config.network_name,
dependencies,
pool,
)?;
}
let deps = match info.get_extra_value("dependencies") {
None => vec![],
Some(v) => Vec::<String>::deserialize(v).map_err(|_| {
DfxError::Unknown(String::from("Field 'dependencies' is of the wrong type"))
})?,
};
let dependencies = deps
.iter()
.map(|name| {
pool.get_first_canister_with_name(name)
.map(|c| c.canister_id())
.map_or_else(
|| Err(DfxError::UnknownCanisterNamed(name.clone())),
DfxResult::Ok,
)
})
.collect::<DfxResult<Vec<CanisterId>>>()?;

let assets_canister_info = info.as_info::<AssetsCanisterInfo>()?;
if !config.skip_frontend {
assets_canister_info.assert_source_paths()?;
}
copy_assets(
build_frontend(
pool.get_logger(),
&assets_canister_info,
config.skip_frontend,
info.get_workspace_root(),
&config.network_name,
dependencies,
pool,
)?;

let assets_canister_info = info.as_info::<AssetsCanisterInfo>()?;
assets_canister_info.assert_source_paths()?;

copy_assets(pool.get_logger(), &assets_canister_info)?;
Ok(())
}
}
Expand Down Expand Up @@ -170,20 +163,16 @@ fn delete_output_directory(
Ok(())
}

fn copy_assets(
logger: &slog::Logger,
assets_canister_info: &AssetsCanisterInfo,
skip_frontend: bool,
) -> DfxResult {
fn copy_assets(logger: &slog::Logger, assets_canister_info: &AssetsCanisterInfo) -> DfxResult {
let source_paths = assets_canister_info.get_source_paths();
let output_assets_path = assets_canister_info.get_output_assets_path();

for source_path in source_paths {
// If we skip-frontend and the source doesn't exist, we ignore it.
if skip_frontend && !source_path.exists() {
// If the source doesn't exist, we ignore it.
if !source_path.exists() {
slog::warn!(
logger,
r#"Skip copying "{}" because --skip-frontend was used."#,
r#"Source path "{}" does not exist."#,
source_path.to_string_lossy()
);

Expand Down
9 changes: 0 additions & 9 deletions src/dfx/src/lib/builders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ pub trait CanisterBuilder {
#[derive(Clone)]
pub struct BuildConfig {
profile: Profile,
pub skip_frontend: bool,
pub build_mode_check: bool,
pub network_name: String,

Expand All @@ -98,20 +97,12 @@ impl BuildConfig {
Ok(BuildConfig {
network_name,
profile: config_intf.profile.unwrap_or(Profile::Debug),
skip_frontend: false,
build_mode_check: false,
build_root: build_root.clone(),
idl_root: build_root.join("idl/"),
})
}

pub fn with_skip_frontend(self, skip_frontend: bool) -> Self {
Self {
skip_frontend,
..self
}
}

pub fn with_build_mode_check(self, build_mode_check: bool) -> Self {
Self {
build_mode_check,
Expand Down
3 changes: 2 additions & 1 deletion src/dfx/src/lib/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ user_message!(
RequestId => "Specifies the request identifier. The request identifier is an hexadecimal string starting with 0x.",

// dfx build
BuildAll => "Builds all canisters configured in dfx.json.",
BuildCanisterName => "Specifies the canister name. Either this or the --all flag are required.",
BuildCanister => "Builds all or specific canisters from the code in your project. By default, all canisters are built.",
SkipFrontend => "Skip building the frontend, only build the canisters.",
BuildCheck => "Build canisters without creating them. This can be used to check that canisters build ok.",
CanisterComputeNetwork => "Override the compute network to connect to. By default uses the local network.",

Expand Down
Loading