diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 1f508cb5b..947fb17e8 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -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); diff --git a/tests/testsuite/build_scripts_multiple.rs b/tests/testsuite/build_scripts_multiple.rs index 71eaadc92..72851dff4 100644 --- a/tests/testsuite/build_scripts_multiple.rs +++ b/tests/testsuite/build_scripts_multiple.rs @@ -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()