Skip to content

Commit 231660a

Browse files
committed
Auto merge of #4724 - pornel:sortpaths, r=alexcrichton
Sort native library paths for deterministic builds Fixes #3800 by using a `BTreeSet`, which guarantees a deterministic iteration order. Since the order was previously arbitrary, a sorted order is just as good. The list is so small, that any performance difference between `BTreeSet` and `HashSet` is negligible.
2 parents e5f6718 + c31ad2b commit 231660a

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

src/cargo/ops/cargo_rustc/compilation.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::{HashMap, HashSet};
1+
use std::collections::{HashMap, HashSet, BTreeSet};
22
use std::ffi::OsStr;
33
use std::path::PathBuf;
44
use semver::Version;
@@ -22,8 +22,10 @@ pub struct Compilation<'cfg> {
2222
///
2323
/// This is currently used to drive some entries which are added to the
2424
/// LD_LIBRARY_PATH as appropriate.
25+
///
26+
/// The order should be deterministic.
2527
// TODO: deprecated, remove
26-
pub native_dirs: HashSet<PathBuf>,
28+
pub native_dirs: BTreeSet<PathBuf>,
2729

2830
/// Root output directory (for the local package's artifacts)
2931
pub root_output: PathBuf,
@@ -64,7 +66,7 @@ impl<'cfg> Compilation<'cfg> {
6466
pub fn new(config: &'cfg Config) -> Compilation<'cfg> {
6567
Compilation {
6668
libraries: HashMap::new(),
67-
native_dirs: HashSet::new(), // TODO: deprecated, remove
69+
native_dirs: BTreeSet::new(), // TODO: deprecated, remove
6870
root_output: PathBuf::from("/"),
6971
deps_output: PathBuf::from("/"),
7072
host_deps_output: PathBuf::from("/"),

tests/run.rs

+43
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,49 @@ fn run_with_library_paths() {
784784
assert_that(p.cargo("run"), execs().with_status(0));
785785
}
786786

787+
#[test]
788+
fn library_paths_sorted_alphabetically() {
789+
let p = project("foo");
790+
791+
let mut dir1 = p.target_debug_dir();
792+
dir1.push("zzzzzzz");
793+
794+
let mut dir2 = p.target_debug_dir();
795+
dir2.push("BBBBBBB");
796+
797+
let mut dir3 = p.target_debug_dir();
798+
dir3.push("aaaaaaa");
799+
800+
let p = p
801+
.file("Cargo.toml", r#"
802+
[project]
803+
name = "foo"
804+
version = "0.0.1"
805+
authors = []
806+
build = "build.rs"
807+
"#)
808+
.file("build.rs", &format!(r##"
809+
fn main() {{
810+
println!(r#"cargo:rustc-link-search=native={}"#);
811+
println!(r#"cargo:rustc-link-search=native={}"#);
812+
println!(r#"cargo:rustc-link-search=native={}"#);
813+
}}
814+
"##, dir1.display(), dir2.display(), dir3.display()))
815+
.file("src/main.rs", &format!(r##"
816+
fn main() {{
817+
let search_path = std::env::var_os("{}").unwrap();
818+
let paths = std::env::split_paths(&search_path).collect::<Vec<_>>();
819+
// ASCII case-sensitive sort
820+
assert_eq!("BBBBBBB", paths[0].file_name().unwrap().to_string_lossy());
821+
assert_eq!("aaaaaaa", paths[1].file_name().unwrap().to_string_lossy());
822+
assert_eq!("zzzzzzz", paths[2].file_name().unwrap().to_string_lossy());
823+
}}
824+
"##, dylib_path_envvar()))
825+
.build();
826+
827+
assert_that(p.cargo("run"), execs().with_status(0));
828+
}
829+
787830
#[test]
788831
fn fail_no_extra_verbose() {
789832
let p = project("foo")

0 commit comments

Comments
 (0)