Skip to content
Draft
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
18 changes: 14 additions & 4 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1704,12 +1704,22 @@ fn build_deps_args(

let mut unstable_opts = false;

// Add `OUT_DIR` environment variables for build scripts
let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
if let Some(dep) = first_custom_build_dep {
let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
cmd.env("OUT_DIR", &out_dir);
}

for dep in deps {
if dep.unit.mode.is_run_custom_build() {
cmd.env(
"OUT_DIR",
&build_runner.files().build_script_out_dir(&dep.unit),
);
let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
let target_name = dep.unit.target.name();
let out_dir_prefix = target_name
.strip_prefix("build-script-")
.unwrap_or(target_name);
let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
cmd.env(&out_dir_name, &out_dir);
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/cargo/util/toml/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub(super) fn to_targets(
if metabuild.is_some() {
anyhow::bail!("cannot specify both `metabuild` and `build`");
}
validate_unique_build_scripts(custom_build)?;
for script in custom_build {
let script_path = Path::new(script);
let name = format!(
Expand Down Expand Up @@ -901,6 +902,22 @@ fn validate_unique_names(targets: &[TomlTarget], target_kind: &str) -> CargoResu
Ok(())
}

/// Will check a list of build scripts, and make sure script file stems are unique within a vector.
fn validate_unique_build_scripts(scripts: &[String]) -> CargoResult<()> {
let mut seen = HashSet::new();
for script in scripts {
let stem = Path::new(script).file_stem().unwrap().to_str().unwrap();
if !seen.insert(stem) {
anyhow::bail!(
"found duplicate build script file stem {}, \
but all build scripts must have a unique file stem",
stem
);
}
Comment on lines +911 to +916
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we gather this into a HashMap<Name, Vec<Path>> and then iterate through and if Vec<Path> has more than one element, we then error? That would led us report all errors at once and report the full path for each conflicting build script so users can better understand what they need to change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I was just following how earlier validate_unique_name functions were written.

}
Ok(())
}

fn configure(
toml: &TomlTarget,
target: &mut Target,
Expand Down
122 changes: 118 additions & 4 deletions tests/testsuite/build_scripts_multiple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ fn build_script_with_conflicting_out_dirs() {
build = ["build1.rs", "build2.rs"]
"#,
)
// OUT_DIR is set to the lexicographically largest build script's OUT_DIR by default
// By default, OUT_DIR is set to that of the first build script in the array
.file(
"src/main.rs",
r#"
Expand Down Expand Up @@ -603,7 +603,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 2!
Hello, from Build Script 1!

"#]])
.run();
Expand All @@ -628,7 +628,7 @@ fn build_script_with_conflicts_reverse_sorted() {
build = ["build2.rs", "build1.rs"]
"#,
)
// OUT_DIR is set to the lexicographically largest build script's OUT_DIR by default
// By default, OUT_DIR is set to that of the first build script in the array
.file(
"src/main.rs",
r#"
Expand Down Expand Up @@ -682,7 +682,7 @@ fn build_script_with_conflicts_reverse_sorted() {
.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();
Expand Down Expand Up @@ -764,3 +764,117 @@ fn bar() {
"#]])
.run();
}

#[cargo_test]
fn multiple_out_dirs() {
// Test to verify access to the `OUT_DIR` of the respective build scripts.

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#"
include!(concat!(env!("build1_OUT_DIR"), "/foo.rs"));
include!(concat!(env!("build2_OUT_DIR"), "/foo.rs"));
fn main() {
println!("{}", message1());
println!("{}", message2());
}
"#,
)
.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 message1() -> &'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 message2() -> &'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!
Hello, from Build Script 2!

"#]])
.run();
}

#[cargo_test]
fn duplicate_build_script_stems() {
// Test to verify that duplicate build script file stems throws error.

let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["multiple-build-scripts"]

[package]
name = "foo"
version = "0.1.0"
edition = "2024"
build = ["build1.rs", "foo/build1.rs"]
"#,
)
.file("src/main.rs", "fn main() {}")
.file("build1.rs", "fn main() {}")
.file("foo/build1.rs", "fn main() {}")
.build();

p.cargo("check -v")
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`

Caused by:
found duplicate build script file stem build1, but all build scripts must have a unique file stem

"#]])
.run();
}
Loading