3131//! [`CompileMode::RunCustomBuild`]: super::CompileMode
3232//! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
3333
34- use super :: { fingerprint, BuildRunner , Job , Unit , Work } ;
34+ use super :: { fingerprint, get_dynamic_search_path , BuildRunner , Job , Unit , Work } ;
3535use crate :: core:: compiler:: artifact;
3636use crate :: core:: compiler:: build_runner:: UnitHash ;
3737use crate :: core:: compiler:: fingerprint:: DirtyReason ;
@@ -46,6 +46,7 @@ use cargo_util::paths;
4646use cargo_util_schemas:: manifest:: RustVersion ;
4747use std:: collections:: hash_map:: { Entry , HashMap } ;
4848use std:: collections:: { BTreeSet , HashSet } ;
49+ use std:: ffi:: OsStr ;
4950use std:: path:: { Path , PathBuf } ;
5051use std:: str:: { self , FromStr } ;
5152use std:: sync:: { Arc , Mutex } ;
@@ -74,11 +75,96 @@ pub enum Severity {
7475
7576pub type LogMessage = ( Severity , String ) ;
7677
78+ /// This represents a path added to the library search path. We need to keep track of requests
79+ /// to add search paths within the cargo build directory separately from paths outside of Cargo.
80+ /// The reason is that we want to give precedence to linking against libraries within the Cargo
81+ /// build directory even if a similar library exists in the system (e.g. crate A adds /usr/lib
82+ /// to the search path and then a later build of crate B adds target/debug/... to satisfy
83+ /// it's request to link against the library B that it built, but B is also found in /usr/lib).
84+ ///
85+ /// There's some nuance here because we want to preserve relative order of paths of the same type.
86+ /// For example, if the build process would in declaration order emit the following linker line:
87+ /// ```bash
88+ /// -L/usr/lib -Ltarget/debug/build/crate1/libs -L/lib -Ltarget/debug/build/crate2/libs)
89+ /// ```
90+ ///
91+ /// we want the linker to actually receive:
92+ /// ```bash
93+ /// -Ltarget/debug/build/crate1/libs -Ltarget/debug/build/crate2/libs) -L/usr/lib -L/lib
94+ /// ```
95+ ///
96+ /// so that the library search paths within the crate artifacts directory come first but retain
97+ /// relative ordering while the system library paths come after while still retaining relative
98+ /// ordering among them; ordering is the order they are emitted within the build process,
99+ /// not lexicographic order.
100+ ///
101+ /// WARNING: Even though this type implements PartialOrd + Ord, this is a lexicographic ordering.
102+ /// The linker line will require an explicit sorting algorithm. PartialOrd + Ord is derived because
103+ /// BuildOutput requires it but that ordering is different from the one for the linker search path,
104+ /// at least today. It may be worth reconsidering & perhaps it's ok if BuildOutput doesn't have
105+ /// a lexicographic ordering for the library_paths? I'm not sure the consequence of that.
106+ #[ derive( Clone , Debug , Hash , PartialEq , Eq , PartialOrd , Ord ) ]
107+ pub enum LibraryPath {
108+ /// The path is pointing within the output folder of the crate and takes priority over
109+ /// external paths when passed to the linker.
110+ CargoArtifact ( PathBuf ) ,
111+ /// The path is pointing outside of the crate's build location. The linker will always
112+ /// receive such paths after `CargoArtifact`.
113+ External ( PathBuf ) ,
114+ }
115+
116+ impl LibraryPath {
117+ fn new ( p : PathBuf , script_out_dir : & Path ) -> Self {
118+ let search_path = get_dynamic_search_path ( & p) ;
119+ if search_path. starts_with ( script_out_dir) {
120+ Self :: CargoArtifact ( p)
121+ } else {
122+ Self :: External ( p)
123+ }
124+ }
125+
126+ pub fn into_path_buf ( self ) -> std:: path:: PathBuf {
127+ match self {
128+ LibraryPath :: CargoArtifact ( p) | LibraryPath :: External ( p) => p,
129+ }
130+ }
131+
132+ pub fn display ( & self ) -> std:: path:: Display < ' _ > {
133+ match self {
134+ LibraryPath :: CargoArtifact ( p) | LibraryPath :: External ( p) => p. display ( ) ,
135+ }
136+ }
137+ }
138+
139+ impl AsRef < Path > for LibraryPath {
140+ fn as_ref ( & self ) -> & Path {
141+ match self {
142+ LibraryPath :: CargoArtifact ( p) | LibraryPath :: External ( p) => p,
143+ }
144+ }
145+ }
146+
147+ impl AsRef < PathBuf > for LibraryPath {
148+ fn as_ref ( & self ) -> & PathBuf {
149+ match self {
150+ LibraryPath :: CargoArtifact ( p) | LibraryPath :: External ( p) => p,
151+ }
152+ }
153+ }
154+
155+ impl AsRef < OsStr > for LibraryPath {
156+ fn as_ref ( & self ) -> & OsStr {
157+ match self {
158+ LibraryPath :: CargoArtifact ( p) | LibraryPath :: External ( p) => p. as_os_str ( ) ,
159+ }
160+ }
161+ }
162+
77163/// Contains the parsed output of a custom build script.
78164#[ derive( Clone , Debug , Hash , Default , PartialEq , Eq , PartialOrd , Ord ) ]
79165pub struct BuildOutput {
80166 /// Paths to pass to rustc with the `-L` flag.
81- pub library_paths : Vec < PathBuf > ,
167+ pub library_paths : Vec < LibraryPath > ,
82168 /// Names and link kinds of libraries, suitable for the `-l` flag.
83169 pub library_links : Vec < String > ,
84170 /// Linker arguments suitable to be passed to `-C link-arg=<args>`
@@ -884,10 +970,14 @@ impl BuildOutput {
884970 "rustc-flags" => {
885971 let ( paths, links) = BuildOutput :: parse_rustc_flags ( & value, & whence) ?;
886972 library_links. extend ( links. into_iter ( ) ) ;
887- library_paths. extend ( paths. into_iter ( ) ) ;
973+ library_paths. extend ( paths. into_iter ( ) . map ( |p| {
974+ LibraryPath :: new ( p, script_out_dir)
975+ } ) ) ;
888976 }
889977 "rustc-link-lib" => library_links. push ( value. to_string ( ) ) ,
890- "rustc-link-search" => library_paths. push ( PathBuf :: from ( value) ) ,
978+ "rustc-link-search" => {
979+ library_paths. push ( LibraryPath :: new ( PathBuf :: from ( value) , script_out_dir) )
980+ }
891981 "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
892982 if !targets. iter ( ) . any ( |target| target. is_cdylib ( ) ) {
893983 log_messages. push ( (
0 commit comments