Accessing each build script's OUT_DIR and in the correct order (#15776)

Hi Everyone!

This PR is aimed to have some improvements over #15704

### What does this PR try to resolve?

Now, multiple build scripts are built correctly. But there are some
underlying issues, that this PR is targeting.

- Preserving the order of the build scripts: Earlier the build scripts
were sorted by default, but now, the order will be preserved.

### How to test and review this PR?

There is a feature gate `multiple-build-scripts` that can be passed via
`cargo-features` in `Cargo.toml`. So, you have to add
```toml
cargo-features = ["multiple-build-scripts"]
```
Preferably on the top of the `Cargo.toml` and use nightly toolchain to
use the feature
This commit is contained in:
Ed Page 2025-08-06 19:58:35 +00:00 committed by GitHub
commit e87ec1a12a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 103 additions and 3 deletions

View File

@ -30,7 +30,9 @@ use crate::core::dependency::{Artifact, ArtifactKind, ArtifactTarget, DepKind};
use crate::core::profiles::{Profile, Profiles, UnitFor};
use crate::core::resolver::Resolve;
use crate::core::resolver::features::{FeaturesFor, ResolvedFeatures};
use crate::core::{Dependency, Package, PackageId, PackageSet, Target, TargetKind, Workspace};
use crate::core::{
Dependency, Feature, Package, PackageId, PackageSet, Target, TargetKind, Workspace,
};
use crate::ops::resolve_all_features;
use crate::util::GlobalContext;
use crate::util::interning::InternedString;
@ -142,8 +144,27 @@ pub fn build_unit_dependencies<'a, 'gctx>(
// which affect the determinism of the build itself. As a result be sure
// that dependency lists are always sorted to ensure we've always got a
// deterministic output.
for list in state.unit_dependencies.values_mut() {
list.sort();
for (unit, list) in &mut state.unit_dependencies {
let is_multiple_build_scripts_enabled = unit
.pkg
.manifest()
.unstable_features()
.require(Feature::multiple_build_scripts())
.is_ok();
if is_multiple_build_scripts_enabled {
list.sort_by_key(|unit_dep| {
if unit_dep.unit.target.is_custom_build() {
// We do not sort build scripts to preserve the user-defined order.
// In terms of determinism, we are assuming nothing interferes with order from when the user set it in `Cargo.toml` to here
(0, None)
} else {
(1, Some(unit_dep.clone()))
}
});
} else {
list.sort();
}
}
trace!("ALL UNIT DEPENDENCIES {:#?}", state.unit_dependencies);

View File

@ -608,6 +608,85 @@ Hello, from Build Script 2!
.run();
}
#[cargo_test]
fn build_script_with_conflicts_reverse_sorted() {
// In this, multiple scripts create file with same name in their respective OUT_DIR.
// It is different from above because `package.build` is not sorted in this.
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["multiple-build-scripts"]
[package]
name = "foo"
version = "0.1.0"
edition = "2024"
build = ["build2.rs", "build1.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()