From e9df793c47579bcf84c228c25b650c9724925893 Mon Sep 17 00:00:00 2001 From: Naman Garg Date: Tue, 24 Jun 2025 00:22:57 +0530 Subject: [PATCH 1/4] Add tests for multiple build scripts execution --- tests/testsuite/build_scripts_multiple.rs | 199 ++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/tests/testsuite/build_scripts_multiple.rs b/tests/testsuite/build_scripts_multiple.rs index 9a7c02700ef..1c91139521e 100644 --- a/tests/testsuite/build_scripts_multiple.rs +++ b/tests/testsuite/build_scripts_multiple.rs @@ -409,6 +409,205 @@ path = "src/main.rs" .run(); } +#[cargo_test] +fn custom_build_script_first_index_script_failed() { + // In this, the script that is at first index in the build script array fails + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["multiple-build-scripts"] + + [package] + name = "foo" + version = "0.1.0" + edition = "2024" + + build = ["build1.rs", "build2.rs"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build1.rs", "fn main() { std::process::exit(101); }") + .file("build2.rs", "fn main() {}") + .build(); + + p.cargo("check -v") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) + .with_status(101) + .with_stderr_data(str![[r#" +[COMPILING] foo v0.1.0 ([ROOT]/foo) +[RUNNING] `rustc --crate-name build_script_build1 --edition=2024 build1.rs [..]--crate-type bin [..]` +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` +[ERROR] failed to run custom build command for `foo v0.1.0 ([ROOT]/foo)` + +Caused by: + process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` ([EXIT_STATUS]: 101) + +"#]]) + .run(); +} + +#[cargo_test] +fn custom_build_script_second_index_script_failed() { + // In this, the script that is at second index in the build script array fails + // This test was necessary because earlier, the program failed only if first script failed. + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["multiple-build-scripts"] + + [package] + name = "foo" + version = "0.1.0" + edition = "2024" + + build = ["build1.rs", "build2.rs"] + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("build1.rs", "fn main() {}") + .file("build2.rs", "fn main() { std::process::exit(101); }") + .build(); + + p.cargo("check -v") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) + .with_status(0) + .with_stderr_data(str![[r#" +[COMPILING] foo v0.1.0 ([ROOT]/foo) +[RUNNING] `rustc --crate-name build_script_build1 --edition=2024 build1.rs [..]--crate-type bin [..]` +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` +[RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..]` +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn build_script_with_conflicting_environment_variables() { + // In this, multiple scripts set different values to same environment variables + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["multiple-build-scripts"] + + [package] + name = "foo" + version = "0.1.0" + edition = "2024" + + build = ["build1.rs", "build2.rs"] + "#, + ) + .file( + "src/main.rs", + r#" + const FOO: &'static str = env!("FOO"); + fn main() { + println!("{}", FOO); + } + "#, + ) + .file( + "build1.rs", + r#"fn main() { println!("cargo::rustc-env=FOO=bar1"); }"#, + ) + .file( + "build2.rs", + r#"fn main() { println!("cargo::rustc-env=FOO=bar2"); }"#, + ) + .build(); + + p.cargo("run -v") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) + .with_status(0) + .with_stdout_data(str![[r#" +bar1 + +"#]]) + .run(); +} + +#[cargo_test] +fn build_script_with_conflicting_out_dirs() { + // In this, multiple scripts create file with same name in their respective OUT_DIR. + + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["multiple-build-scripts"] + + [package] + name = "foo" + version = "0.1.0" + edition = "2024" + + build = ["build1.rs", "build2.rs"] + "#, + ) + // OUT_DIR is set to the lexicographically largest build script's OUT_DIR by default + .file( + "src/main.rs", + r#" + include!(concat!(env!("OUT_DIR"), "/foo.rs")); + fn main() { + println!("{}", message()); + } + "#, + ) + .file( + "build1.rs", + r#" + use std::env; + use std::fs; + use std::path::Path; + + fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("foo.rs"); + fs::write( + &dest_path, + "pub fn message() -> &'static str { + \"Hello, from Build Script 1!\" + } + " + ).unwrap(); + }"#, + ) + .file( + "build2.rs", + r#" + use std::env; + use std::fs; + use std::path::Path; + + fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("foo.rs"); + fs::write( + &dest_path, + "pub fn message() -> &'static str { + \"Hello, from Build Script 2!\" + } + " + ).unwrap(); + }"#, + ) + .build(); + + p.cargo("run -v") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) + .with_status(0) + .with_stdout_data(str![[r#" +Hello, from Build Script 1! + +"#]]) + .run(); +} + #[cargo_test] fn rerun_untracks_other_files() { let p = project() From 72290ad03a0477c05177348436576a366d86a9e5 Mon Sep 17 00:00:00 2001 From: Naman Garg Date: Sat, 28 Jun 2025 01:36:07 +0530 Subject: [PATCH 2/4] Replace `build` with `check` in test `rerun_untracks_other_files` --- tests/testsuite/build_scripts_multiple.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testsuite/build_scripts_multiple.rs b/tests/testsuite/build_scripts_multiple.rs index 1c91139521e..4aefeb7dffe 100644 --- a/tests/testsuite/build_scripts_multiple.rs +++ b/tests/testsuite/build_scripts_multiple.rs @@ -639,12 +639,12 @@ fn bar() { .file("assets/foo.txt", "foo") .file("assets/bar.txt", "bar") .build(); - p.cargo("build").run(); + p.cargo("check").run(); // Editing foo.txt won't recompile, leading to unnoticed changes p.change_file("assets/foo.txt", "foo updated"); - p.cargo("build -v") + p.cargo("check -v") .with_stderr_data(str![[r#" [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -655,7 +655,7 @@ fn bar() { // Editing bar.txt will recompile p.change_file("assets/bar.txt", "bar updated"); - p.cargo("build -v") + p.cargo("check -v") .with_stderr_data(str![[r#" [DIRTY] foo v0.1.0 ([ROOT]/foo): the file `assets/bar.txt` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) [COMPILING] foo v0.1.0 ([ROOT]/foo) From a24abd6fc2347b3c0de0a96be40bf98df4ea030f Mon Sep 17 00:00:00 2001 From: Naman Garg Date: Fri, 27 Jun 2025 03:23:33 +0530 Subject: [PATCH 3/4] Add multiple build scripts to unit deps --- src/cargo/core/compiler/unit_dependencies.rs | 111 ++++++++++--------- tests/testsuite/build_scripts_multiple.rs | 54 ++++++--- 2 files changed, 97 insertions(+), 68 deletions(-) diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 425a2bab47e..1f508cb5ba3 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -343,7 +343,11 @@ fn compute_deps( if unit.target.is_custom_build() { return Ok(ret); } - ret.extend(dep_build_script(unit, unit_for, state)?); + ret.extend( + dep_build_script(unit, unit_for, state)? + .into_iter() + .flatten(), + ); // If this target is a binary, test, example, etc, then it depends on // the library of the same package. The call to `resolve.deps` above @@ -645,7 +649,11 @@ fn compute_deps_doc( } // Be sure to build/run the build script for documented libraries. - ret.extend(dep_build_script(unit, unit_for, state)?); + ret.extend( + dep_build_script(unit, unit_for, state)? + .into_iter() + .flatten(), + ); // If we document a binary/example, we need the library available. if unit.target.is_bin() || unit.target.is_example() { @@ -731,54 +739,57 @@ fn dep_build_script( unit: &Unit, unit_for: UnitFor, state: &State<'_, '_>, -) -> CargoResult> { - unit.pkg - .targets() - .iter() - .find(|t| t.is_custom_build()) - .map(|t| { - // The profile stored in the Unit is the profile for the thing - // the custom build script is running for. - let profile = state.profiles.get_profile_run_custom_build(&unit.profile); - // UnitFor::for_custom_build is used because we want the `host` flag set - // for all of our build dependencies (so they all get - // build-override profiles), including compiling the build.rs - // script itself. - // - // If `is_for_host_features` here is `false`, that means we are a - // build.rs script for a normal dependency and we want to set the - // CARGO_FEATURE_* environment variables to the features as a - // normal dep. - // - // If `is_for_host_features` here is `true`, that means that this - // package is being used as a build dependency or proc-macro, and - // so we only want to set CARGO_FEATURE_* variables for the host - // side of the graph. - // - // Keep in mind that the RunCustomBuild unit and the Compile - // build.rs unit use the same features. This is because some - // people use `cfg!` and `#[cfg]` expressions to check for enabled - // features instead of just checking `CARGO_FEATURE_*` at runtime. - // In the case with the new feature resolver (decoupled host - // deps), and a shared dependency has different features enabled - // for normal vs. build, then the build.rs script will get - // compiled twice. I believe it is not feasible to only build it - // once because it would break a large number of scripts (they - // would think they have the wrong set of features enabled). - let script_unit_for = unit_for.for_custom_build(); - new_unit_dep_with_profile( - state, - unit, - &unit.pkg, - t, - script_unit_for, - unit.kind, - CompileMode::RunCustomBuild, - profile, - IS_NO_ARTIFACT_DEP, - ) - }) - .transpose() +) -> CargoResult>> { + Some( + unit.pkg + .targets() + .iter() + .filter(|t| t.is_custom_build()) + .map(|t| { + // The profile stored in the Unit is the profile for the thing + // the custom build script is running for. + let profile = state.profiles.get_profile_run_custom_build(&unit.profile); + // UnitFor::for_custom_build is used because we want the `host` flag set + // for all of our build dependencies (so they all get + // build-override profiles), including compiling the build.rs + // script itself. + // + // If `is_for_host_features` here is `false`, that means we are a + // build.rs script for a normal dependency and we want to set the + // CARGO_FEATURE_* environment variables to the features as a + // normal dep. + // + // If `is_for_host_features` here is `true`, that means that this + // package is being used as a build dependency or proc-macro, and + // so we only want to set CARGO_FEATURE_* variables for the host + // side of the graph. + // + // Keep in mind that the RunCustomBuild unit and the Compile + // build.rs unit use the same features. This is because some + // people use `cfg!` and `#[cfg]` expressions to check for enabled + // features instead of just checking `CARGO_FEATURE_*` at runtime. + // In the case with the new feature resolver (decoupled host + // deps), and a shared dependency has different features enabled + // for normal vs. build, then the build.rs script will get + // compiled twice. I believe it is not feasible to only build it + // once because it would break a large number of scripts (they + // would think they have the wrong set of features enabled). + let script_unit_for = unit_for.for_custom_build(); + new_unit_dep_with_profile( + state, + unit, + &unit.pkg, + t, + script_unit_for, + unit.kind, + CompileMode::RunCustomBuild, + profile, + IS_NO_ARTIFACT_DEP, + ) + }) + .collect(), + ) + .transpose() } /// Choose the correct mode for dependencies. diff --git a/tests/testsuite/build_scripts_multiple.rs b/tests/testsuite/build_scripts_multiple.rs index 4aefeb7dffe..88d1683010b 100644 --- a/tests/testsuite/build_scripts_multiple.rs +++ b/tests/testsuite/build_scripts_multiple.rs @@ -436,13 +436,12 @@ fn custom_build_script_first_index_script_failed() { .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) -[RUNNING] `rustc --crate-name build_script_build1 --edition=2024 build1.rs [..]--crate-type bin [..]` -[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` +... [ERROR] failed to run custom build command for `foo v0.1.0 ([ROOT]/foo)` Caused by: process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` ([EXIT_STATUS]: 101) - +... "#]]) .run(); } @@ -472,14 +471,15 @@ fn custom_build_script_second_index_script_failed() { p.cargo("check -v") .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) - .with_status(0) + .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.1.0 ([ROOT]/foo) -[RUNNING] `rustc --crate-name build_script_build1 --edition=2024 build1.rs [..]--crate-type bin [..]` -[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` -[RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..]` -[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +... +[ERROR] failed to run custom build command for `foo v0.1.0 ([ROOT]/foo)` +Caused by: + process didn't exit successfully: `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build2` ([EXIT_STATUS]: 101) +... "#]]) .run(); } @@ -602,7 +602,7 @@ fn build_script_with_conflicting_out_dirs() { .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stdout_data(str![[r#" -Hello, from Build Script 1! +Hello, from Build Script 2! "#]]) .run(); @@ -614,23 +614,34 @@ fn rerun_untracks_other_files() { .file( "Cargo.toml", r#" + cargo-features = ["multiple-build-scripts"] + [package] name = "foo" version = "0.1.0" edition = "2024" + build = ["build1.rs", "build2.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file( - "build.rs", + "build1.rs", r#" fn main() { foo(); - bar(); } fn foo() { let _path = "assets/foo.txt"; } +"#, + ) + .file( + "build2.rs", + r#" +fn main() { + bar(); +} + fn bar() { let path = "assets/bar.txt"; println!("cargo::rerun-if-changed={path}"); @@ -639,27 +650,34 @@ fn bar() { .file("assets/foo.txt", "foo") .file("assets/bar.txt", "bar") .build(); - p.cargo("check").run(); - - // Editing foo.txt won't recompile, leading to unnoticed changes + p.cargo("check") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) + .run(); + // Editing foo.txt will also recompile now since they are separate build scripts p.change_file("assets/foo.txt", "foo updated"); p.cargo("check -v") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_stderr_data(str![[r#" -[FRESH] foo v0.1.0 ([ROOT]/foo) +[DIRTY] foo v0.1.0 ([ROOT]/foo): the [..] +[COMPILING] foo v0.1.0 ([ROOT]/foo) +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build1` +[RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); - // Editing bar.txt will recompile + // Editing bar.txt will also recompile now since they are separate build scripts p.change_file("assets/bar.txt", "bar updated"); p.cargo("check -v") + .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_stderr_data(str![[r#" -[DIRTY] foo v0.1.0 ([ROOT]/foo): the file `assets/bar.txt` has changed ([TIME_DIFF_AFTER_LAST_BUILD]) +[DIRTY] foo v0.1.0 ([ROOT]/foo): the [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) -[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build[..]` +[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build[..]` [RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s From bfb869748c57c60eb8e769fab0ab36f66cd1acca Mon Sep 17 00:00:00 2001 From: Naman Garg Date: Thu, 26 Jun 2025 04:10:19 +0530 Subject: [PATCH 4/4] Use Vector for build script `Unit`s and `UnitHash` --- src/cargo/core/compiler/build_runner/mod.rs | 65 ++++++++++++--------- src/cargo/core/compiler/compilation.rs | 26 +++++---- src/cargo/core/compiler/custom_build.rs | 8 ++- src/cargo/core/compiler/job_queue/mod.rs | 40 +++++++------ src/cargo/core/compiler/mod.rs | 32 +++++----- src/cargo/core/compiler/output_depinfo.rs | 26 +++++---- src/cargo/ops/cargo_run.rs | 4 +- src/cargo/ops/cargo_test.rs | 16 ++--- tests/testsuite/build_scripts_multiple.rs | 2 +- 9 files changed, 121 insertions(+), 98 deletions(-) diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index bd50b48b89e..f6f2a287137 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -248,23 +248,25 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { args.extend(compiler::features_args(unit)); args.extend(compiler::check_cfg_args(unit)); - let script_meta = self.find_build_script_metadata(unit); - if let Some(meta) = script_meta { - if let Some(output) = self.build_script_outputs.lock().unwrap().get(meta) { - for cfg in &output.cfgs { - args.push("--cfg".into()); - args.push(cfg.into()); - } + let script_metas = self.find_build_script_metadatas(unit); + if let Some(meta_vec) = script_metas.clone() { + for meta in meta_vec { + if let Some(output) = self.build_script_outputs.lock().unwrap().get(meta) { + for cfg in &output.cfgs { + args.push("--cfg".into()); + args.push(cfg.into()); + } - for check_cfg in &output.check_cfgs { - args.push("--check-cfg".into()); - args.push(check_cfg.into()); - } + for check_cfg in &output.check_cfgs { + args.push("--check-cfg".into()); + args.push(check_cfg.into()); + } - for (lt, arg) in &output.linker_args { - if lt.applies_to(&unit.target, unit.mode) { - args.push("-C".into()); - args.push(format!("link-arg={}", arg).into()); + for (lt, arg) in &output.linker_args { + if lt.applies_to(&unit.target, unit.mode) { + args.push("-C".into()); + args.push(format!("link-arg={}", arg).into()); + } } } } @@ -285,7 +287,7 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { args, unstable_opts, linker: self.compilation.target_linker(unit.kind).clone(), - script_meta, + script_metas, env: artifact::get_env(&self, self.unit_deps(unit))?, }); } @@ -420,29 +422,40 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { &self.bcx.unit_graph[unit] } - /// Returns the `RunCustomBuild` Unit associated with the given Unit. + /// Returns the `RunCustomBuild` Units associated with the given Unit. /// /// If the package does not have a build script, this returns None. - pub fn find_build_script_unit(&self, unit: &Unit) -> Option { + pub fn find_build_script_units(&self, unit: &Unit) -> Option> { if unit.mode.is_run_custom_build() { - return Some(unit.clone()); + return Some(vec![unit.clone()]); } - self.bcx.unit_graph[unit] + + let build_script_units: Vec = self.bcx.unit_graph[unit] .iter() - .find(|unit_dep| { + .filter(|unit_dep| { unit_dep.unit.mode.is_run_custom_build() && unit_dep.unit.pkg.package_id() == unit.pkg.package_id() }) .map(|unit_dep| unit_dep.unit.clone()) + .collect(); + if build_script_units.is_empty() { + None + } else { + Some(build_script_units) + } } /// Returns the metadata hash for the `RunCustomBuild` Unit associated with /// the given unit. /// /// If the package does not have a build script, this returns None. - pub fn find_build_script_metadata(&self, unit: &Unit) -> Option { - let script_unit = self.find_build_script_unit(unit)?; - Some(self.get_run_build_script_metadata(&script_unit)) + pub fn find_build_script_metadatas(&self, unit: &Unit) -> Option> { + self.find_build_script_units(unit).map(|units| { + units + .iter() + .map(|u| self.get_run_build_script_metadata(u)) + .collect() + }) } /// Returns the metadata hash for a `RunCustomBuild` unit. @@ -480,11 +493,11 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { /// Returns a [`UnitOutput`] which represents some information about the /// output of a unit. pub fn unit_output(&self, unit: &Unit, path: &Path) -> UnitOutput { - let script_meta = self.find_build_script_metadata(unit); + let script_metas = self.find_build_script_metadatas(unit); UnitOutput { unit: unit.clone(), path: path.to_path_buf(), - script_meta, + script_metas, } } diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 5eed28ae680..68b59ba0cac 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -45,7 +45,7 @@ pub struct Doctest { /// The script metadata, if this unit's package has a build script. /// /// This is used for indexing [`Compilation::extra_env`]. - pub script_meta: Option, + pub script_metas: Option>, /// Environment variables to set in the rustdoc process. pub env: HashMap, @@ -61,7 +61,7 @@ pub struct UnitOutput { /// The script metadata, if this unit's package has a build script. /// /// This is used for indexing [`Compilation::extra_env`]. - pub script_meta: Option, + pub script_metas: Option>, } /// A structure returning the result of a compilation. @@ -197,14 +197,14 @@ impl<'gctx> Compilation<'gctx> { pub fn rustdoc_process( &self, unit: &Unit, - script_meta: Option, + script_metas: Option<&Vec>, ) -> CargoResult { let mut rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?); if self.gctx.extra_verbose() { rustdoc.display_env_vars(); } let cmd = fill_rustc_tool_env(rustdoc, unit); - let mut cmd = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, ToolKind::Rustdoc)?; + let mut cmd = self.fill_env(cmd, &unit.pkg, script_metas, unit.kind, ToolKind::Rustdoc)?; cmd.retry_with_argfile(true); unit.target.edition().cmd_edition_arg(&mut cmd); @@ -248,7 +248,7 @@ impl<'gctx> Compilation<'gctx> { /// target platform. This is typically used for `cargo run` and `cargo /// test`. /// - /// `script_meta` is the metadata for the `RunCustomBuild` unit that this + /// `script_metas` is the metadata for the `RunCustomBuild` unit that this /// unit used for its build script. Use `None` if the package did not have /// a build script. pub fn target_process>( @@ -256,7 +256,7 @@ impl<'gctx> Compilation<'gctx> { cmd: T, kind: CompileKind, pkg: &Package, - script_meta: Option, + script_metas: Option<&Vec>, ) -> CargoResult { let builder = if let Some((runner, args)) = self.target_runner(kind) { let mut builder = ProcessBuilder::new(runner); @@ -267,7 +267,7 @@ impl<'gctx> Compilation<'gctx> { ProcessBuilder::new(cmd) }; let tool_kind = ToolKind::TargetProcess; - let mut builder = self.fill_env(builder, pkg, script_meta, kind, tool_kind)?; + let mut builder = self.fill_env(builder, pkg, script_metas, kind, tool_kind)?; if let Some(client) = self.gctx.jobserver_from_env() { builder.inherit_jobserver(client); @@ -285,7 +285,7 @@ impl<'gctx> Compilation<'gctx> { &self, mut cmd: ProcessBuilder, pkg: &Package, - script_meta: Option, + script_metas: Option<&Vec>, kind: CompileKind, tool_kind: ToolKind, ) -> CargoResult { @@ -343,10 +343,12 @@ impl<'gctx> Compilation<'gctx> { let search_path = paths::join_paths(&search_path, paths::dylib_path_envvar())?; cmd.env(paths::dylib_path_envvar(), &search_path); - if let Some(meta) = script_meta { - if let Some(env) = self.extra_env.get(&meta) { - for (k, v) in env { - cmd.env(k, v); + if let Some(meta_vec) = script_metas { + for meta in meta_vec { + if let Some(env) = self.extra_env.get(meta) { + for (k, v) in env { + cmd.env(k, v); + } } } } diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 417baddc15a..0cec4660de3 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -1286,10 +1286,12 @@ pub fn build_map(build_runner: &mut BuildRunner<'_, '_>) -> CargoResult<()> { // If a package has a build script, add itself as something to inspect for linking. if !unit.target.is_custom_build() && unit.pkg.has_custom_build() { - let script_meta = build_runner - .find_build_script_metadata(unit) + let script_metas = build_runner + .find_build_script_metadatas(unit) .expect("has_custom_build should have RunCustomBuild"); - add_to_link(&mut ret, unit.pkg.package_id(), script_meta); + for script_meta in script_metas { + add_to_link(&mut ret, unit.pkg.package_id(), script_meta); + } } if unit.mode.is_run_custom_build() { diff --git a/src/cargo/core/compiler/job_queue/mod.rs b/src/cargo/core/compiler/job_queue/mod.rs index 744b2ea9a91..518f3739671 100644 --- a/src/cargo/core/compiler/job_queue/mod.rs +++ b/src/cargo/core/compiler/job_queue/mod.rs @@ -981,28 +981,30 @@ impl<'gctx> DrainState<'gctx> { show_warnings: bool, ) -> CargoResult<()> { let outputs = build_runner.build_script_outputs.lock().unwrap(); - let Some(metadata) = build_runner.find_build_script_metadata(unit) else { + let Some(metadata_vec) = build_runner.find_build_script_metadatas(unit) else { return Ok(()); }; let bcx = &mut build_runner.bcx; - if let Some(output) = outputs.get(metadata) { - if !output.log_messages.is_empty() - && (show_warnings - || output - .log_messages - .iter() - .any(|(severity, _)| *severity == Severity::Error)) - { - let msg_with_package = - |msg: &str| format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), msg); - - for (severity, message) in output.log_messages.iter() { - match severity { - Severity::Error => { - bcx.gctx.shell().error(msg_with_package(message))?; - } - Severity::Warning => { - bcx.gctx.shell().warn(msg_with_package(message))?; + for metadata in metadata_vec { + if let Some(output) = outputs.get(metadata) { + if !output.log_messages.is_empty() + && (show_warnings + || output + .log_messages + .iter() + .any(|(severity, _)| *severity == Severity::Error)) + { + let msg_with_package = + |msg: &str| format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), msg); + + for (severity, message) in output.log_messages.iter() { + match severity { + Severity::Error => { + bcx.gctx.shell().error(msg_with_package(message))?; + } + Severity::Warning => { + bcx.gctx.shell().warn(msg_with_package(message))?; + } } } } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 926cf28a63e..4b990181fe4 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -313,7 +313,7 @@ fn rustc( .unwrap_or_else(|| build_runner.bcx.gctx.cwd()) .to_path_buf(); let fingerprint_dir = build_runner.files().fingerprint_dir(unit); - let script_metadata = build_runner.find_build_script_metadata(unit); + let script_metadatas = build_runner.find_build_script_metadatas(unit); let is_local = unit.is_local(); let artifact = unit.artifact; let sbom_files = build_runner.sbom_output_files(unit)?; @@ -371,7 +371,7 @@ fn rustc( )?; add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?; } - add_custom_flags(&mut rustc, &script_outputs, script_metadata)?; + add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?; } for output in outputs.iter() { @@ -920,7 +920,7 @@ fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult, unit: &Unit) -> CargoResult, + metadata_vec: Option>, ) -> CargoResult<()> { - if let Some(metadata) = metadata { - if let Some(output) = build_script_outputs.get(metadata) { - for cfg in output.cfgs.iter() { - cmd.arg("--cfg").arg(cfg); - } - for check_cfg in &output.check_cfgs { - cmd.arg("--check-cfg").arg(check_cfg); - } - for (name, value) in output.env.iter() { - cmd.env(name, value); + if let Some(metadata_vec) = metadata_vec { + for metadata in metadata_vec { + if let Some(output) = build_script_outputs.get(metadata) { + for cfg in output.cfgs.iter() { + cmd.arg("--cfg").arg(cfg); + } + for check_cfg in &output.check_cfgs { + cmd.arg("--check-cfg").arg(check_cfg); + } + for (name, value) in output.env.iter() { + cmd.env(name, value); + } } } } diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs index e106595c27b..d59be10f2dc 100644 --- a/src/cargo/core/compiler/output_depinfo.rs +++ b/src/cargo/core/compiler/output_depinfo.rs @@ -75,18 +75,20 @@ fn add_deps_for_unit( } // Add rerun-if-changed dependencies - if let Some(metadata) = build_runner.find_build_script_metadata(unit) { - if let Some(output) = build_runner - .build_script_outputs - .lock() - .unwrap() - .get(metadata) - { - for path in &output.rerun_if_changed { - // The paths we have saved from the unit are of arbitrary relativeness and may be - // relative to the crate root of the dependency. - let path = unit.pkg.root().join(path); - deps.insert(path); + if let Some(metadata_vec) = build_runner.find_build_script_metadatas(unit) { + for metadata in metadata_vec { + if let Some(output) = build_runner + .build_script_outputs + .lock() + .unwrap() + .get(metadata) + { + for path in &output.rerun_if_changed { + // The paths we have saved from the unit are of arbitrary relativeness and may be + // relative to the crate root of the dependency. + let path = unit.pkg.root().join(path); + deps.insert(path); + } } } } diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index 25d13e923a3..8853619f3a5 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -95,7 +95,7 @@ pub fn run( let UnitOutput { unit, path, - script_meta, + script_metas, } = &compile.binaries[0]; let exe = match path.strip_prefix(gctx.cwd()) { Ok(path) if path.file_name() == Some(path.as_os_str()) => Path::new(".").join(path), @@ -103,7 +103,7 @@ pub fn run( Err(_) => path.to_path_buf(), }; let pkg = bins[0].0; - let mut process = compile.target_process(exe, unit.kind, pkg, *script_meta)?; + let mut process = compile.target_process(exe, unit.kind, pkg, script_metas.as_ref())?; // Sets the working directory of the child process to the current working // directory of the parent process. diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index f79a30e219d..cbe6c127262 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -125,7 +125,7 @@ fn run_unit_tests( for UnitOutput { unit, path, - script_meta, + script_metas, } in compilation.tests.iter() { let (exe_display, mut cmd) = cmd_builds( @@ -133,7 +133,7 @@ fn run_unit_tests( cwd, unit, path, - script_meta, + script_metas.as_ref(), test_args, compilation, "unittests", @@ -184,12 +184,12 @@ fn run_doc_tests( unstable_opts, unit, linker, - script_meta, + script_metas, env, } = doctest_info; gctx.shell().status("Doc-tests", unit.target.name())?; - let mut p = compilation.rustdoc_process(unit, *script_meta)?; + let mut p = compilation.rustdoc_process(unit, script_metas.as_ref())?; for (var, value) in env { p.env(var, value); @@ -295,7 +295,7 @@ fn display_no_run_information( for UnitOutput { unit, path, - script_meta, + script_metas, } in compilation.tests.iter() { let (exe_display, cmd) = cmd_builds( @@ -303,7 +303,7 @@ fn display_no_run_information( cwd, unit, path, - script_meta, + script_metas.as_ref(), test_args, compilation, exec_type, @@ -327,7 +327,7 @@ fn cmd_builds( cwd: &Path, unit: &Unit, path: &PathBuf, - script_meta: &Option, + script_metas: Option<&Vec>, test_args: &[&str], compilation: &Compilation<'_>, exec_type: &str, @@ -352,7 +352,7 @@ fn cmd_builds( ), }; - let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, *script_meta)?; + let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, script_metas)?; cmd.args(test_args); if unit.target.harness() && gctx.shell().verbosity() == Verbosity::Quiet { cmd.arg("--quiet"); diff --git a/tests/testsuite/build_scripts_multiple.rs b/tests/testsuite/build_scripts_multiple.rs index 88d1683010b..71eaadc9273 100644 --- a/tests/testsuite/build_scripts_multiple.rs +++ b/tests/testsuite/build_scripts_multiple.rs @@ -524,7 +524,7 @@ fn build_script_with_conflicting_environment_variables() { .masquerade_as_nightly_cargo(&["multiple-build-scripts"]) .with_status(0) .with_stdout_data(str![[r#" -bar1 +bar2 "#]]) .run();