|
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;
|
@@ -74,11 +74,76 @@ pub enum Severity {
|
74 | 74 |
|
75 | 75 | pub type LogMessage = (Severity, String);
|
76 | 76 |
|
| 77 | +/// Represents a path added to the library search path. |
| 78 | +/// |
| 79 | +/// We need to keep track of requests to add search paths within the cargo build directory |
| 80 | +/// separately from paths outside of Cargo. The reason is that we want to give precedence to linking |
| 81 | +/// against libraries within the Cargo build directory even if a similar library exists in the |
| 82 | +/// system (e.g. crate A adds `/usr/lib` to the search path and then a later build of crate B adds |
| 83 | +/// `target/debug/...` to satisfy its request to link against the library B that it built, but B is |
| 84 | +/// also found in `/usr/lib`). |
| 85 | +/// |
| 86 | +/// There's some nuance here because we want to preserve relative order of paths of the same type. |
| 87 | +/// For example, if the build process would in declaration order emit the following linker line: |
| 88 | +/// ```bash |
| 89 | +/// -L/usr/lib -Ltarget/debug/build/crate1/libs -L/lib -Ltarget/debug/build/crate2/libs) |
| 90 | +/// ``` |
| 91 | +/// |
| 92 | +/// we want the linker to actually receive: |
| 93 | +/// ```bash |
| 94 | +/// -Ltarget/debug/build/crate1/libs -Ltarget/debug/build/crate2/libs) -L/usr/lib -L/lib |
| 95 | +/// ``` |
| 96 | +/// |
| 97 | +/// so that the library search paths within the crate artifacts directory come first but retain |
| 98 | +/// relative ordering while the system library paths come after while still retaining relative |
| 99 | +/// ordering among them; ordering is the order they are emitted within the build process, |
| 100 | +/// not lexicographic order. |
| 101 | +/// |
| 102 | +/// WARNING: Even though this type implements PartialOrd + Ord, this is a lexicographic ordering. |
| 103 | +/// The linker line will require an explicit sorting algorithm. PartialOrd + Ord is derived because |
| 104 | +/// BuildOutput requires it but that ordering is different from the one for the linker search path, |
| 105 | +/// at least today. It may be worth reconsidering & perhaps it's ok if BuildOutput doesn't have |
| 106 | +/// a lexicographic ordering for the library_paths? I'm not sure the consequence of that. |
| 107 | +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] |
| 108 | +pub enum LibraryPath { |
| 109 | + /// The path is pointing within the output folder of the crate and takes priority over |
| 110 | + /// external paths when passed to the linker. |
| 111 | + CargoArtifact(PathBuf), |
| 112 | + /// The path is pointing outside of the crate's build location. The linker will always |
| 113 | + /// receive such paths after `CargoArtifact`. |
| 114 | + External(PathBuf), |
| 115 | +} |
| 116 | + |
| 117 | +impl LibraryPath { |
| 118 | + fn new(p: PathBuf, script_out_dir: &Path) -> Self { |
| 119 | + let search_path = get_dynamic_search_path(&p); |
| 120 | + if search_path.starts_with(script_out_dir) { |
| 121 | + Self::CargoArtifact(p) |
| 122 | + } else { |
| 123 | + Self::External(p) |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + pub fn into_path_buf(self) -> PathBuf { |
| 128 | + match self { |
| 129 | + LibraryPath::CargoArtifact(p) | LibraryPath::External(p) => p, |
| 130 | + } |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +impl AsRef<PathBuf> for LibraryPath { |
| 135 | + fn as_ref(&self) -> &PathBuf { |
| 136 | + match self { |
| 137 | + LibraryPath::CargoArtifact(p) | LibraryPath::External(p) => p, |
| 138 | + } |
| 139 | + } |
| 140 | +} |
| 141 | + |
77 | 142 | /// Contains the parsed output of a custom build script.
|
78 | 143 | #[derive(Clone, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
|
79 | 144 | pub struct BuildOutput {
|
80 | 145 | /// Paths to pass to rustc with the `-L` flag.
|
81 |
| - pub library_paths: Vec<PathBuf>, |
| 146 | + pub library_paths: Vec<LibraryPath>, |
82 | 147 | /// Names and link kinds of libraries, suitable for the `-l` flag.
|
83 | 148 | pub library_links: Vec<String>,
|
84 | 149 | /// Linker arguments suitable to be passed to `-C link-arg=<args>`
|
@@ -237,7 +302,7 @@ fn emit_build_output(
|
237 | 302 | let library_paths = output
|
238 | 303 | .library_paths
|
239 | 304 | .iter()
|
240 |
| - .map(|l| l.display().to_string()) |
| 305 | + .map(|l| l.as_ref().display().to_string()) |
241 | 306 | .collect::<Vec<_>>();
|
242 | 307 |
|
243 | 308 | let msg = machine_message::BuildScript {
|
@@ -884,10 +949,16 @@ impl BuildOutput {
|
884 | 949 | "rustc-flags" => {
|
885 | 950 | let (paths, links) = BuildOutput::parse_rustc_flags(&value, &whence)?;
|
886 | 951 | library_links.extend(links.into_iter());
|
887 |
| - library_paths.extend(paths.into_iter()); |
| 952 | + library_paths.extend( |
| 953 | + paths |
| 954 | + .into_iter() |
| 955 | + .map(|p| LibraryPath::new(p, script_out_dir)), |
| 956 | + ); |
888 | 957 | }
|
889 | 958 | "rustc-link-lib" => library_links.push(value.to_string()),
|
890 |
| - "rustc-link-search" => library_paths.push(PathBuf::from(value)), |
| 959 | + "rustc-link-search" => { |
| 960 | + library_paths.push(LibraryPath::new(PathBuf::from(value), script_out_dir)) |
| 961 | + } |
891 | 962 | "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
|
892 | 963 | if !targets.iter().any(|target| target.is_cdylib()) {
|
893 | 964 | log_messages.push((
|
|
0 commit comments