31
31
//! [`CompileMode::RunCustomBuild`]: super::CompileMode
32
32
//! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
33
33
34
- use super :: { fingerprint, BuildRunner , Job , Unit , Work } ;
34
+ use super :: { fingerprint, get_dynamic_search_path , BuildRunner , Job , Unit , Work } ;
35
35
use crate :: core:: compiler:: artifact;
36
36
use crate :: core:: compiler:: build_runner:: UnitHash ;
37
37
use crate :: core:: compiler:: fingerprint:: DirtyReason ;
@@ -46,6 +46,7 @@ use cargo_util::paths;
46
46
use cargo_util_schemas:: manifest:: RustVersion ;
47
47
use std:: collections:: hash_map:: { Entry , HashMap } ;
48
48
use std:: collections:: { BTreeSet , HashSet } ;
49
+ use std:: ffi:: OsStr ;
49
50
use std:: path:: { Path , PathBuf } ;
50
51
use std:: str:: { self , FromStr } ;
51
52
use std:: sync:: { Arc , Mutex } ;
@@ -74,11 +75,96 @@ pub enum Severity {
74
75
75
76
pub type LogMessage = ( Severity , String ) ;
76
77
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
+
77
163
/// Contains the parsed output of a custom build script.
78
164
#[ derive( Clone , Debug , Hash , Default , PartialEq , Eq , PartialOrd , Ord ) ]
79
165
pub struct BuildOutput {
80
166
/// Paths to pass to rustc with the `-L` flag.
81
- pub library_paths : Vec < PathBuf > ,
167
+ pub library_paths : Vec < LibraryPath > ,
82
168
/// Names and link kinds of libraries, suitable for the `-l` flag.
83
169
pub library_links : Vec < String > ,
84
170
/// Linker arguments suitable to be passed to `-C link-arg=<args>`
@@ -884,10 +970,14 @@ impl BuildOutput {
884
970
"rustc-flags" => {
885
971
let ( paths, links) = BuildOutput :: parse_rustc_flags ( & value, & whence) ?;
886
972
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
+ } ) ) ;
888
976
}
889
977
"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
+ }
891
981
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
892
982
if !targets. iter ( ) . any ( |target| target. is_cdylib ( ) ) {
893
983
log_messages. push ( (
0 commit comments