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
19 changes: 15 additions & 4 deletions src/cargo/core/compiler/build_runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
plan.output_plan(self.bcx.gctx);
}

// Add `OUT_DIR` to env vars if unit has a build script.
// Add `OUT_DIR` to runtime env vars if unit has a build script.
Copy link
Contributor

Choose a reason for hiding this comment

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

Saying runtime here might be confusing as we are adding it for use at compile time of the package and not for cargo run or cargo test running binaries.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wait, this is us setting the specialized OUT_DIR on the build script itself? We probably shouldn't be doing that. Depending on how we do build script delegation, the name will be dependent on how the caller sees the build script which will make it undiscoverable for build scripts to use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, should I remove it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. While this could make it easier to match up build script to rust source, it runs into the problem mentioned.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not the earlier code, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure what you are asking about. We should only set the new environment variable on the libs/bins/etc and not on the build script execution.

let units_with_build_script = &self
.bcx
.roots
Expand All @@ -227,11 +227,22 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> {
.display()
.to_string();
let script_meta = self.get_run_build_script_metadata(&dep.unit);
self.compilation

// It creates environment variable named build-script-<name>_OUT_DIR
let mut out_dir_name = dep.unit.target.name().to_owned();
out_dir_name = out_dir_name
.strip_prefix("build-script-")
.unwrap()
.to_string();
out_dir_name.push_str("_OUT_DIR");

let env = self
.compilation
.extra_env
.entry(script_meta)
.or_insert_with(Vec::new)
.push(("OUT_DIR".to_string(), out_dir));
.or_insert_with(Vec::new);
env.push(("OUT_DIR".to_string(), out_dir.clone()));
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 make OUT_DIR an explicit choice for when we write it, rather than relying on it by incident? That'll make it less likely for people to break this in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll get back to this

env.push((out_dir_name, out_dir));
Comment on lines +239 to +245
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In a separate commit maybe? Or should I update the latest commit itself?

Copy link
Contributor

Choose a reason for hiding this comment

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

Unless the commit is ginormous (e.g. adding all of cargo add into cargo), I would generally update docs as part of commit that added the feature, like tests.

}
}
}
Expand Down
13 changes: 9 additions & 4 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1706,10 +1706,15 @@ fn build_deps_args(

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 mut out_dir_name = dep.unit.target.name().to_owned();
out_dir_name = out_dir_name
.strip_prefix("build-script-")
.unwrap()
.to_string();
out_dir_name.push_str("_OUT_DIR");
cmd.env("OUT_DIR", &out_dir);
cmd.env(&out_dir_name, &out_dir);
Copy link
Contributor

Choose a reason for hiding this comment

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

don't forget to update the test from 6c8bc08

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I added an extra test for duplicated/conflicting declarations of same function

}
}

Expand Down
179 changes: 177 additions & 2 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 last build script in the array
.file(
"src/main.rs",
r#"
Expand Down Expand Up @@ -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 last build script in the array
.file(
"src/main.rs",
r#"
Expand Down Expand Up @@ -764,3 +764,178 @@ 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 build_script_with_fn_conflicts() {
// In this, multiple scripts have duplicated/conflicting declarations of same function

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!("{}", 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("check -v")
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
.with_status(101)
.with_stderr_data(str![[r#"
[COMPILING] foo v0.1.0 ([ROOT]/foo)
...
--> [ROOT]/foo/target/debug/build/foo-[HASH]/out/foo.rs:1:1
|
1 | pub fn message() -> &'static str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` redefined here
|
::: [ROOT]/foo/target/debug/build/foo-[HASH]/out/foo.rs:1:1
|
1 | pub fn message() -> &'static str {
| -------------------------------- previous definition of the value `message` here
|
= [NOTE] `message` must be defined only once in the value namespace of this module

For more information about this error, try `rustc --explain E0428`.
[ERROR] could not compile `foo` (bin "foo") due to 1 previous error

Caused by:
process didn't exit successfully: `rustc --crate-name foo --edition=2024 src/main.rs [..] ([EXIT_STATUS]: 1)

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