]>
Commit | Line | Data |
---|---|---|
c057b876 EH |
1 | //! # Fingerprints |
2 | //! | |
3 | //! This module implements change-tracking so that Cargo can know whether or | |
4 | //! not something needs to be recompiled. A Cargo `Unit` can be either "dirty" | |
5 | //! (needs to be recompiled) or "fresh" (it does not need to be recompiled). | |
6 | //! There are several mechanisms that influence a Unit's freshness: | |
7 | //! | |
14e86cc7 EH |
8 | //! - The `Fingerprint` is a hash, saved to the filesystem in the |
9 | //! `.fingerprint` directory, that tracks information about the Unit. If the | |
10 | //! fingerprint is missing (such as the first time the unit is being | |
11 | //! compiled), then the unit is dirty. If any of the fingerprint fields | |
12 | //! change (like the name of the source file), then the Unit is considered | |
13 | //! dirty. | |
14 | //! | |
15 | //! The `Fingerprint` also tracks the fingerprints of all its dependencies, | |
16 | //! so a change in a dependency will propagate the "dirty" status up. | |
17 | //! | |
18 | //! - Filesystem mtime tracking is also used to check if a unit is dirty. | |
19 | //! See the section below on "Mtime comparison" for more details. There | |
20 | //! are essentially two parts to mtime tracking: | |
21 | //! | |
22 | //! 1. The mtime of a Unit's output files is compared to the mtime of all | |
23 | //! its dependencies' output file mtimes (see `check_filesystem`). If any | |
24 | //! output is missing, or is older than a dependency's output, then the | |
25 | //! unit is dirty. | |
26 | //! 2. The mtime of a Unit's source files is compared to the mtime of its | |
27 | //! dep-info file in the fingerprint directory (see `find_stale_file`). | |
28 | //! The dep-info file is used as an anchor to know when the last build of | |
29 | //! the unit was done. See the "dep-info files" section below for more | |
30 | //! details. If any input files are missing, or are newer than the | |
31 | //! dep-info, then the unit is dirty. | |
c057b876 EH |
32 | //! |
33 | //! Note: Fingerprinting is not a perfect solution. Filesystem mtime tracking | |
34 | //! is notoriously imprecise and problematic. Only a small part of the | |
35 | //! environment is captured. This is a balance of performance, simplicity, and | |
36 | //! completeness. Sandboxing, hashing file contents, tracking every file | |
37 | //! access, environment variable, and network operation would ensure more | |
38 | //! reliable and reproducible builds at the cost of being complex, slow, and | |
39 | //! platform-dependent. | |
40 | //! | |
41 | //! ## Fingerprints and Metadata | |
42 | //! | |
14e86cc7 EH |
43 | //! The `Metadata` hash is a hash added to the output filenames to isolate |
44 | //! each unit. See the documentation in the `compilation_files` module for | |
45 | //! more details. NOTE: Not all output files are isolated via filename hashes | |
7438770b EH |
46 | //! (like dylibs). The fingerprint directory uses a hash, but sometimes units |
47 | //! share the same fingerprint directory (when they don't have Metadata) so | |
48 | //! care should be taken to handle this! | |
14e86cc7 | 49 | //! |
c057b876 EH |
50 | //! Fingerprints and Metadata are similar, and track some of the same things. |
51 | //! The Metadata contains information that is required to keep Units separate. | |
52 | //! The Fingerprint includes additional information that should cause a | |
cc53ecad | 53 | //! recompile, but it is desired to reuse the same filenames. A comparison |
c057b876 EH |
54 | //! of what is tracked: |
55 | //! | |
56 | //! Value | Fingerprint | Metadata | |
57 | //! -------------------------------------------|-------------|---------- | |
58 | //! rustc | ✓ | ✓ | |
59 | //! Profile | ✓ | ✓ | |
60 | //! `cargo rustc` extra args | ✓ | ✓ | |
61 | //! CompileMode | ✓ | ✓ | |
62 | //! Target Name | ✓ | ✓ | |
ef425b77 | 63 | //! Target CompileKind (bin/lib/etc.) | ✓ | ✓ |
c057b876 EH |
64 | //! Enabled Features | ✓ | ✓ |
65 | //! Immediate dependency’s hashes | ✓[^1] | ✓ | |
66 | //! Target or Host mode | | ✓ | |
67 | //! __CARGO_DEFAULT_LIB_METADATA[^4] | | ✓ | |
c057b876 | 68 | //! package_id | | ✓ |
27a95d0e | 69 | //! authors, description, homepage, repo | ✓ | |
cc53ecad | 70 | //! Target src path relative to ws | ✓ | |
c057b876 | 71 | //! Target flags (test/bench/for_host/edition) | ✓ | |
c057b876 EH |
72 | //! -C incremental=… flag | ✓ | |
73 | //! mtime of sources | ✓[^3] | | |
f3c92ed5 | 74 | //! RUSTFLAGS/RUSTDOCFLAGS | ✓ | |
e2219254 | 75 | //! LTO flags | ✓ | |
e0f9643b | 76 | //! config settings[^5] | ✓ | |
ebba7a37 | 77 | //! is_std | | ✓ |
c057b876 EH |
78 | //! |
79 | //! [^1]: Build script and bin dependencies are not included. | |
80 | //! | |
cc53ecad | 81 | //! [^3]: See below for details on mtime tracking. |
c057b876 EH |
82 | //! |
83 | //! [^4]: `__CARGO_DEFAULT_LIB_METADATA` is set by rustbuild to embed the | |
84 | //! release channel (bootstrap/stable/beta/nightly) in libstd. | |
85 | //! | |
e0f9643b EH |
86 | //! [^5]: Config settings that are not otherwise captured anywhere else. |
87 | //! Currently, this is only `doc.extern-map`. | |
88 | //! | |
cc53ecad EH |
89 | //! When deciding what should go in the Metadata vs the Fingerprint, consider |
90 | //! that some files (like dylibs) do not have a hash in their filename. Thus, | |
14e86cc7 EH |
91 | //! if a value changes, only the fingerprint will detect the change (consider, |
92 | //! for example, swapping between different features). Fields that are only in | |
93 | //! Metadata generally aren't relevant to the fingerprint because they | |
94 | //! fundamentally change the output (like target vs host changes the directory | |
95 | //! where it is emitted). | |
cc53ecad | 96 | //! |
c057b876 EH |
97 | //! ## Fingerprint files |
98 | //! | |
99 | //! Fingerprint information is stored in the | |
100 | //! `target/{debug,release}/.fingerprint/` directory. Each Unit is stored in a | |
101 | //! separate directory. Each Unit directory contains: | |
102 | //! | |
103 | //! - A file with a 16 hex-digit hash. This is the Fingerprint hash, used for | |
104 | //! quick loading and comparison. | |
105 | //! - A `.json` file that contains details about the Fingerprint. This is only | |
106 | //! used to log details about *why* a fingerprint is considered dirty. | |
782266aa | 107 | //! `CARGO_LOG=cargo::core::compiler::fingerprint=trace cargo build` can be |
c057b876 EH |
108 | //! used to display this log information. |
109 | //! - A "dep-info" file which contains a list of source filenames for the | |
cc53ecad | 110 | //! target. See below for details. |
c057b876 | 111 | //! - An `invoked.timestamp` file whose filesystem mtime is updated every time |
eac3b66b EH |
112 | //! the Unit is built. This is used for capturing the time when the build |
113 | //! starts, to detect if files are changed in the middle of the build. See | |
114 | //! below for more details. | |
c057b876 EH |
115 | //! |
116 | //! Note that some units are a little different. A Unit for *running* a build | |
117 | //! script or for `rustdoc` does not have a dep-info file (it's not | |
118 | //! applicable). Build script `invoked.timestamp` files are in the build | |
119 | //! output directory. | |
120 | //! | |
121 | //! ## Fingerprint calculation | |
122 | //! | |
123 | //! After the list of Units has been calculated, the Units are added to the | |
124 | //! `JobQueue`. As each one is added, the fingerprint is calculated, and the | |
71f80951 AC |
125 | //! dirty/fresh status is recorded. A closure is used to update the fingerprint |
126 | //! on-disk when the Unit successfully finishes. The closure will recompute the | |
127 | //! Fingerprint based on the updated information. If the Unit fails to compile, | |
128 | //! the fingerprint is not updated. | |
c057b876 EH |
129 | //! |
130 | //! Fingerprints are cached in the `Context`. This makes computing | |
131 | //! Fingerprints faster, but also is necessary for properly updating | |
132 | //! dependency information. Since a Fingerprint includes the Fingerprints of | |
133 | //! all dependencies, when it is updated, by using `Arc` clones, it | |
134 | //! automatically picks up the updates to its dependencies. | |
135 | //! | |
cc53ecad EH |
136 | //! ### dep-info files |
137 | //! | |
138 | //! Cargo passes the `--emit=dep-info` flag to `rustc` so that `rustc` will | |
139 | //! generate a "dep info" file (with the `.d` extension). This is a | |
140 | //! Makefile-like syntax that includes all of the source files used to build | |
141 | //! the crate. This file is used by Cargo to know which files to check to see | |
142 | //! if the crate will need to be rebuilt. | |
143 | //! | |
144 | //! After `rustc` exits successfully, Cargo will read the dep info file and | |
145 | //! translate it into a binary format that is stored in the fingerprint | |
146 | //! directory (`translate_dep_info`). The mtime of the fingerprint dep-info | |
147 | //! file itself is used as the reference for comparing the source files to | |
148 | //! determine if any of the source files have been modified (see below for | |
149 | //! more detail). | |
150 | //! | |
151 | //! There is also a third dep-info file. Cargo will extend the file created by | |
152 | //! rustc with some additional information and saves this into the output | |
153 | //! directory. This is intended for build system integration. See the | |
154 | //! `output_depinfo` module for more detail. | |
155 | //! | |
156 | //! #### -Zbinary-dep-depinfo | |
157 | //! | |
158 | //! `rustc` has an experimental flag `-Zbinary-dep-depinfo`. This causes | |
159 | //! `rustc` to include binary files (like rlibs) in the dep-info file. This is | |
160 | //! primarily to support rustc development, so that Cargo can check the | |
161 | //! implicit dependency to the standard library (which lives in the sysroot). | |
162 | //! We want Cargo to recompile whenever the standard library rlib/dylibs | |
163 | //! change, and this is a generic mechanism to make that work. | |
164 | //! | |
165 | //! ### Mtime comparison | |
166 | //! | |
167 | //! The use of modification timestamps is the most common way a unit will be | |
168 | //! determined to be dirty or fresh between builds. There are many subtle | |
169 | //! issues and edge cases with mtime comparisons. This gives a high-level | |
170 | //! overview, but you'll need to read the code for the gritty details. Mtime | |
171 | //! handling is different for different unit kinds. The different styles are | |
172 | //! driven by the `Fingerprint.local` field, which is set based on the unit | |
173 | //! kind. | |
174 | //! | |
175 | //! The status of whether or not the mtime is "stale" or "up-to-date" is | |
176 | //! stored in `Fingerprint.fs_status`. | |
177 | //! | |
178 | //! All units will compare the mtime of its newest output file with the mtimes | |
179 | //! of the outputs of all its dependencies. If any output file is missing, | |
180 | //! then the unit is stale. If any dependency is newer, the unit is stale. | |
181 | //! | |
182 | //! #### Normal package mtime handling | |
183 | //! | |
184 | //! `LocalFingerprint::CheckDepinfo` is used for checking the mtime of | |
185 | //! packages. It compares the mtime of the input files (the source files) to | |
186 | //! the mtime of the dep-info file (which is written last after a build is | |
187 | //! finished). If the dep-info is missing, the unit is stale (it has never | |
188 | //! been built). The list of input files comes from the dep-info file. See the | |
189 | //! section above for details on dep-info files. | |
190 | //! | |
191 | //! Also note that although registry and git packages use `CheckDepInfo`, none | |
192 | //! of their source files are included in the dep-info (see | |
193 | //! `translate_dep_info`), so for those kinds no mtime checking is done | |
194 | //! (unless `-Zbinary-dep-depinfo` is used). Repository and git packages are | |
195 | //! static, so there is no need to check anything. | |
196 | //! | |
197 | //! When a build is complete, the mtime of the dep-info file in the | |
198 | //! fingerprint directory is modified to rewind it to the time when the build | |
199 | //! started. This is done by creating an `invoked.timestamp` file when the | |
200 | //! build starts to capture the start time. The mtime is rewound to the start | |
201 | //! to handle the case where the user modifies a source file while a build is | |
202 | //! running. Cargo can't know whether or not the file was included in the | |
203 | //! build, so it takes a conservative approach of assuming the file was *not* | |
204 | //! included, and it should be rebuilt during the next build. | |
205 | //! | |
206 | //! #### Rustdoc mtime handling | |
207 | //! | |
208 | //! Rustdoc does not emit a dep-info file, so Cargo currently has a relatively | |
209 | //! simple system for detecting rebuilds. `LocalFingerprint::Precalculated` is | |
210 | //! used for rustdoc units. For registry packages, this is the package | |
211 | //! version. For git packages, it is the git hash. For path packages, it is | |
212 | //! the a string of the mtime of the newest file in the package. | |
213 | //! | |
214 | //! There are some known bugs with how this works, so it should be improved at | |
215 | //! some point. | |
216 | //! | |
217 | //! #### Build script mtime handling | |
218 | //! | |
219 | //! Build script mtime handling runs in different modes. There is the "old | |
220 | //! style" where the build script does not emit any `rerun-if` directives. In | |
221 | //! this mode, Cargo will use `LocalFingerprint::Precalculated`. See the | |
222 | //! "rustdoc" section above how it works. | |
223 | //! | |
224 | //! In the new-style, each `rerun-if` directive is translated to the | |
225 | //! corresponding `LocalFingerprint` variant. The `RerunIfChanged` variant | |
226 | //! compares the mtime of the given filenames against the mtime of the | |
227 | //! "output" file. | |
228 | //! | |
229 | //! Similar to normal units, the build script "output" file mtime is rewound | |
230 | //! to the time just before the build script is executed to handle mid-build | |
231 | //! modifications. | |
232 | //! | |
71f80951 AC |
233 | //! ## Considerations for inclusion in a fingerprint |
234 | //! | |
235 | //! Over time we've realized a few items which historically were included in | |
236 | //! fingerprint hashings should not actually be included. Examples are: | |
237 | //! | |
238 | //! * Modification time values. We strive to never include a modification time | |
239 | //! inside a `Fingerprint` to get hashed into an actual value. While | |
240 | //! theoretically fine to do, in practice this causes issues with common | |
241 | //! applications like Docker. Docker, after a layer is built, will zero out | |
242 | //! the nanosecond part of all filesystem modification times. This means that | |
243 | //! the actual modification time is different for all build artifacts, which | |
244 | //! if we tracked the actual values of modification times would cause | |
245 | //! unnecessary recompiles. To fix this we instead only track paths which are | |
246 | //! relevant. These paths are checked dynamically to see if they're up to | |
34f2d471 | 247 | //! date, and the modification time doesn't make its way into the fingerprint |
71f80951 AC |
248 | //! hash. |
249 | //! | |
250 | //! * Absolute path names. We strive to maintain a property where if you rename | |
251 | //! a project directory Cargo will continue to preserve all build artifacts | |
252 | //! and reuse the cache. This means that we can't ever hash an absolute path | |
253 | //! name. Instead we always hash relative path names and the "root" is passed | |
254 | //! in at runtime dynamically. Some of this is best effort, but the general | |
b4cd6095 | 255 | //! idea is that we assume all accesses within a crate stay within that |
71f80951 AC |
256 | //! crate. |
257 | //! | |
258 | //! These are pretty tricky to test for unfortunately, but we should have a good | |
259 | //! test suite nowadays and lord knows Cargo gets enough testing in the wild! | |
260 | //! | |
c057b876 EH |
261 | //! ## Build scripts |
262 | //! | |
263 | //! The *running* of a build script (`CompileMode::RunCustomBuild`) is treated | |
264 | //! significantly different than all other Unit kinds. It has its own function | |
71f80951 AC |
265 | //! for calculating the Fingerprint (`calculate_run_custom_build`) and has some |
266 | //! unique considerations. It does not track the same information as a normal | |
267 | //! Unit. The information tracked depends on the `rerun-if-changed` and | |
c057b876 EH |
268 | //! `rerun-if-env-changed` statements produced by the build script. If the |
269 | //! script does not emit either of these statements, the Fingerprint runs in | |
270 | //! "old style" mode where an mtime change of *any* file in the package will | |
271 | //! cause the build script to be re-run. Otherwise, the fingerprint *only* | |
272 | //! tracks the individual "rerun-if" items listed by the build script. | |
273 | //! | |
274 | //! The "rerun-if" statements from a *previous* build are stored in the build | |
275 | //! output directory in a file called `output`. Cargo parses this file when | |
276 | //! the Unit for that build script is prepared for the `JobQueue`. The | |
277 | //! Fingerprint code can then use that information to compute the Fingerprint | |
278 | //! and compare against the old fingerprint hash. | |
279 | //! | |
280 | //! Care must be taken with build script Fingerprints because the | |
281 | //! `Fingerprint::local` value may be changed after the build script runs | |
282 | //! (such as if the build script adds or removes "rerun-if" items). | |
283 | //! | |
284 | //! Another complication is if a build script is overridden. In that case, the | |
285 | //! fingerprint is the hash of the output of the override. | |
286 | //! | |
287 | //! ## Special considerations | |
288 | //! | |
289 | //! Registry dependencies do not track the mtime of files. This is because | |
290 | //! registry dependencies are not expected to change (if a new version is | |
291 | //! used, the Package ID will change, causing a rebuild). Cargo currently | |
292 | //! partially works with Docker caching. When a Docker image is built, it has | |
293 | //! normal mtime information. However, when a step is cached, the nanosecond | |
294 | //! portions of all files is zeroed out. Currently this works, but care must | |
295 | //! be taken for situations like these. | |
296 | //! | |
297 | //! HFS on macOS only supports 1 second timestamps. This causes a significant | |
298 | //! number of problems, particularly with Cargo's testsuite which does rapid | |
299 | //! builds in succession. Other filesystems have various degrees of | |
300 | //! resolution. | |
301 | //! | |
302 | //! Various weird filesystems (such as network filesystems) also can cause | |
303 | //! complications. Network filesystems may track the time on the server | |
304 | //! (except when the time is set manually such as with | |
305 | //! `filetime::set_file_times`). Not all filesystems support modifying the | |
306 | //! mtime. | |
307 | //! | |
308 | //! See the `A-rebuild-detection` flag on the issue tracker for more: | |
309 | //! <https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AA-rebuild-detection> | |
310 | ||
b1b9b79c | 311 | use std::collections::hash_map::{Entry, HashMap}; |
fe8bbb7a | 312 | use std::env; |
7dcedd85 | 313 | use std::hash::{self, Hasher}; |
a6dad622 | 314 | use std::path::{Path, PathBuf}; |
77622892 | 315 | use std::sync::{Arc, Mutex}; |
3eaa70e2 | 316 | use std::time::SystemTime; |
d3c350b9 | 317 | |
3a18c89a | 318 | use anyhow::{bail, format_err}; |
7d49029a | 319 | use filetime::FileTime; |
9ed82b57 AC |
320 | use log::{debug, info}; |
321 | use serde::de; | |
8a4be789 | 322 | use serde::ser; |
9ed82b57 | 323 | use serde::{Deserialize, Serialize}; |
7d49029a | 324 | |
aa80a984 | 325 | use crate::core::compiler::unit_graph::UnitDep; |
1f14fa31 | 326 | use crate::core::{InternedString, Package}; |
04ddd4d0 DW |
327 | use crate::util; |
328 | use crate::util::errors::{CargoResult, CargoResultExt}; | |
329 | use crate::util::paths; | |
e9428cba | 330 | use crate::util::{internal, profile}; |
d3c350b9 | 331 | |
fe8bbb7a | 332 | use super::custom_build::BuildDeps; |
e9428cba AC |
333 | use super::job::{ |
334 | Freshness::{Dirty, Fresh}, | |
335 | Job, Work, | |
336 | }; | |
aa99e9f2 | 337 | use super::{BuildContext, Context, FileFlavor, Unit}; |
d3c350b9 | 338 | |
e9428cba AC |
339 | /// Determines if a `unit` is up-to-date, and if not prepares necessary work to |
340 | /// update the persisted fingerprint. | |
d3c350b9 | 341 | /// |
e9428cba AC |
342 | /// This function will inspect `unit`, calculate a fingerprint for it, and then |
343 | /// return an appropriate `Job` to run. The returned `Job` will be a noop if | |
344 | /// `unit` is considered "fresh", or if it was previously built and cached. | |
345 | /// Otherwise the `Job` returned will write out the true fingerprint to the | |
346 | /// filesystem, to be executed after the unit's work has completed. | |
9f5d9b81 | 347 | /// |
e9428cba AC |
348 | /// The `force` flag is a way to force the `Job` to be "dirty", or always |
349 | /// update the fingerprint. **Beware using this flag** because it does not | |
350 | /// transitively propagate throughout the dependency graph, it only forces this | |
351 | /// one unit which is very unlikely to be what you want unless you're | |
352 | /// exclusively talking about top-level units. | |
c85ed044 | 353 | pub fn prepare_target(cx: &mut Context<'_, '_>, unit: &Unit, force: bool) -> CargoResult<Job> { |
1e682848 AC |
354 | let _p = profile::start(format!( |
355 | "fingerprint: {} / {}", | |
356 | unit.pkg.package_id(), | |
357 | unit.target.name() | |
358 | )); | |
c32e395c | 359 | let bcx = cx.bcx; |
7438770b | 360 | let loc = cx.files().fingerprint_file_path(unit, ""); |
c0127391 | 361 | |
659f8244 | 362 | debug!("fingerprint at: {}", loc.display()); |
bfc15194 | 363 | |
e9428cba AC |
364 | // Figure out if this unit is up to date. After calculating the fingerprint |
365 | // compare it to an old version, if any, and attempt to print diagnostic | |
366 | // information about failed comparisons to aid in debugging. | |
82655b46 | 367 | let fingerprint = calculate(cx, unit)?; |
5f6ede29 EH |
368 | let mtime_on_use = cx.bcx.config.cli_unstable().mtime_on_use; |
369 | let compare = compare_old_fingerprint(&loc, &*fingerprint, mtime_on_use); | |
c447e9d0 | 370 | log_compare(unit, &compare); |
659f8244 | 371 | |
f7c91ba6 | 372 | // If our comparison failed (e.g., we're going to trigger a rebuild of this |
4814a84e AC |
373 | // crate), then we also ensure the source of the crate passes all |
374 | // verification checks before we build it. | |
375 | // | |
376 | // The `Source::verify` method is intended to allow sources to execute | |
377 | // pre-build checks to ensure that the relevant source code is all | |
378 | // up-to-date and as expected. This is currently used primarily for | |
379 | // directory sources which will use this hook to perform an integrity check | |
380 | // on all files in the source to ensure they haven't changed. If they have | |
381 | // changed then an error is issued. | |
7fd5243c AC |
382 | if compare.is_err() { |
383 | let source_id = unit.pkg.package_id().source_id(); | |
c32e395c | 384 | let sources = bcx.packages.sources(); |
1e682848 AC |
385 | let source = sources |
386 | .get(source_id) | |
387 | .ok_or_else(|| internal("missing package source"))?; | |
82655b46 | 388 | source.verify(unit.pkg.package_id())?; |
7fd5243c AC |
389 | } |
390 | ||
8df842f5 | 391 | if compare.is_ok() && !force { |
e9428cba AC |
392 | return Ok(Job::new(Work::noop(), Fresh)); |
393 | } | |
79768eb0 | 394 | |
cd396f34 EH |
395 | // Clear out the old fingerprint file if it exists. This protects when |
396 | // compilation is interrupted leaving a corrupt file. For example, a | |
14e86cc7 | 397 | // project with a lib.rs and integration test (two units): |
cd396f34 | 398 | // |
14e86cc7 EH |
399 | // 1. Build the library and integration test. |
400 | // 2. Make a change to lib.rs (NOT the integration test). | |
401 | // 3. Build the integration test, hit Ctrl-C while linking. With gcc, this | |
402 | // will leave behind an incomplete executable (zero size, or partially | |
403 | // written). NOTE: The library builds successfully, it is the linking | |
404 | // of the integration test that we are interrupting. | |
cd396f34 EH |
405 | // 4. Build the integration test again. |
406 | // | |
14e86cc7 EH |
407 | // Without the following line, then step 3 will leave a valid fingerprint |
408 | // on the disk. Then step 4 will think the integration test is "fresh" | |
409 | // because: | |
410 | // | |
411 | // - There is a valid fingerprint hash on disk (written in step 1). | |
412 | // - The mtime of the output file (the corrupt integration executable | |
413 | // written in step 3) is newer than all of its dependencies. | |
414 | // - The mtime of the integration test fingerprint dep-info file (written | |
415 | // in step 1) is newer than the integration test's source files, because | |
416 | // we haven't modified any of its source files. | |
417 | // | |
418 | // But the executable is corrupt and needs to be rebuilt. Clearing the | |
419 | // fingerprint at step 3 ensures that Cargo never mistakes a partially | |
420 | // written output as up-to-date. | |
cd396f34 | 421 | if loc.exists() { |
14e86cc7 EH |
422 | // Truncate instead of delete so that compare_old_fingerprint will |
423 | // still log the reason for the fingerprint failure instead of just | |
424 | // reporting "failed to read fingerprint" during the next build if | |
425 | // this build fails. | |
cd396f34 EH |
426 | paths::write(&loc, b"")?; |
427 | } | |
428 | ||
e9428cba AC |
429 | let write_fingerprint = if unit.mode.is_run_custom_build() { |
430 | // For build scripts the `local` field of the fingerprint may change | |
431 | // while we're executing it. For example it could be in the legacy | |
432 | // "consider everything a dependency mode" and then we switch to "deps | |
433 | // are explicitly specified" mode. | |
434 | // | |
49f63844 | 435 | // To handle this movement we need to regenerate the `local` field of a |
e9428cba AC |
436 | // build script's fingerprint after it's executed. We do this by |
437 | // using the `build_script_local_fingerprints` function which returns a | |
438 | // thunk we can invoke on a foreign thread to calculate this. | |
bd31c081 | 439 | let build_script_outputs = Arc::clone(&cx.build_script_outputs); |
2296af27 EH |
440 | let pkg_id = unit.pkg.package_id(); |
441 | let metadata = cx.get_run_build_script_metadata(unit); | |
e9428cba AC |
442 | let (gen_local, _overridden) = build_script_local_fingerprints(cx, unit); |
443 | let output_path = cx.build_explicit_deps[unit].build_script_output.clone(); | |
444 | Work::new(move |_| { | |
bd31c081 | 445 | let outputs = build_script_outputs.lock().unwrap(); |
2296af27 EH |
446 | let output = outputs |
447 | .get(pkg_id, metadata) | |
448 | .expect("output must exist after running"); | |
449 | let deps = BuildDeps::new(&output_path, Some(output)); | |
22691b94 AC |
450 | |
451 | // FIXME: it's basically buggy that we pass `None` to `call_box` | |
452 | // here. See documentation on `build_script_local_fingerprints` | |
453 | // below for more information. Despite this just try to proceed and | |
454 | // hobble along if it happens to return `Some`. | |
ae4b792f | 455 | if let Some(new_local) = (gen_local)(&deps, None)? { |
22691b94 | 456 | *fingerprint.local.lock().unwrap() = new_local; |
e9428cba | 457 | } |
22691b94 AC |
458 | |
459 | write_fingerprint(&loc, &fingerprint) | |
e9428cba AC |
460 | }) |
461 | } else { | |
22691b94 | 462 | Work::new(move |_| write_fingerprint(&loc, &fingerprint)) |
e9428cba | 463 | }; |
7dcedd85 | 464 | |
e9428cba | 465 | Ok(Job::new(write_fingerprint, Dirty)) |
a40d3b03 AC |
466 | } |
467 | ||
78924725 | 468 | /// Dependency edge information for fingerprints. This is generated for each |
78c788f9 | 469 | /// dependency and is stored in a `Fingerprint` below. |
e9428cba | 470 | #[derive(Clone)] |
14f0f89b | 471 | struct DepFingerprint { |
78924725 | 472 | /// The hash of the package id that this dependency points to |
e9428cba | 473 | pkg_id: u64, |
78924725 AC |
474 | /// The crate name we're using for this dependency, which if we change we'll |
475 | /// need to recompile! | |
1f14fa31 | 476 | name: InternedString, |
78924725 | 477 | /// Whether or not this dependency is flagged as a public dependency or not. |
8185564a | 478 | public: bool, |
78924725 AC |
479 | /// Whether or not this dependency is an rmeta dependency or a "full" |
480 | /// dependency. In the case of an rmeta dependency our dependency edge only | |
481 | /// actually requires the rmeta from what we depend on, so when checking | |
482 | /// mtime information all files other than the rmeta can be ignored. | |
6b28a0c0 | 483 | only_requires_rmeta: bool, |
78924725 AC |
484 | /// The dependency's fingerprint we recursively point to, containing all the |
485 | /// other hash information we'd otherwise need. | |
14f0f89b DW |
486 | fingerprint: Arc<Fingerprint>, |
487 | } | |
336ce24c | 488 | |
a40d3b03 AC |
489 | /// A fingerprint can be considered to be a "short string" representing the |
490 | /// state of a world for a package. | |
491 | /// | |
492 | /// If a fingerprint ever changes, then the package itself needs to be | |
493 | /// recompiled. Inputs to the fingerprint include source code modifications, | |
494 | /// compiler flags, compiler version, etc. This structure is not simply a | |
495 | /// `String` due to the fact that some fingerprints cannot be calculated lazily. | |
496 | /// | |
497 | /// Path sources, for example, use the mtime of the corresponding dep-info file | |
498 | /// as a fingerprint (all source files must be modified *before* this mtime). | |
499 | /// This dep-info file is not generated, however, until after the crate is | |
500 | /// compiled. As a result, this structure can be thought of as a fingerprint | |
7dcedd85 | 501 | /// to-be. The actual value can be calculated via `hash()`, but the operation |
a40d3b03 AC |
502 | /// may fail as some files may not have been generated. |
503 | /// | |
504 | /// Note that dependencies are taken into account for fingerprints because rustc | |
505 | /// requires that whenever an upstream crate is recompiled that all downstream | |
677b24ad | 506 | /// dependents are also recompiled. This is typically tracked through |
a40d3b03 AC |
507 | /// `DependencyQueue`, but it also needs to be retained here because Cargo can |
508 | /// be interrupted while executing, losing the state of the `DependencyQueue` | |
509 | /// graph. | |
a5a298f1 | 510 | #[derive(Serialize, Deserialize)] |
c447e9d0 | 511 | pub struct Fingerprint { |
27a95d0e | 512 | /// Hash of the version of `rustc` used. |
c447e9d0 | 513 | rustc: u64, |
27a95d0e | 514 | /// Sorted list of cfg features enabled. |
c447e9d0 | 515 | features: String, |
27a95d0e EH |
516 | /// Hash of the `Target` struct, including the target name, |
517 | /// package-relative source path, edition, etc. | |
c447e9d0 | 518 | target: u64, |
27a95d0e EH |
519 | /// Hash of the `Profile`, `CompileMode`, and any extra flags passed via |
520 | /// `cargo rustc` or `cargo rustdoc`. | |
c447e9d0 | 521 | profile: u64, |
27a95d0e EH |
522 | /// Hash of the path to the base source file. This is relative to the |
523 | /// workspace root for path members, or absolute for other sources. | |
8647a87d | 524 | path: u64, |
27a95d0e | 525 | /// Fingerprints of dependencies. |
336ce24c | 526 | deps: Vec<DepFingerprint>, |
27a95d0e EH |
527 | /// Information about the inputs that affect this Unit (such as source |
528 | /// file mtimes or build script environment variables). | |
22691b94 | 529 | local: Mutex<Vec<LocalFingerprint>>, |
27a95d0e EH |
530 | /// Cached hash of the `Fingerprint` struct. Used to improve performance |
531 | /// for hashing. | |
49f63844 | 532 | #[serde(skip)] |
5c7979cd | 533 | memoized_hash: Mutex<Option<u64>>, |
27a95d0e | 534 | /// RUSTFLAGS/RUSTDOCFLAGS environment variable value (or config value). |
e4baac0a | 535 | rustflags: Vec<String>, |
27a95d0e EH |
536 | /// Hash of some metadata from the manifest, such as "authors", or |
537 | /// "description", which are exposed as environment variables during | |
538 | /// compilation. | |
539 | metadata: u64, | |
e0f9643b EH |
540 | /// Hash of various config settings that change how things are compiled. |
541 | config: u64, | |
71f80951 AC |
542 | /// Description of whether the filesystem status for this unit is up to date |
543 | /// or should be considered stale. | |
49f63844 | 544 | #[serde(skip)] |
8df842f5 | 545 | fs_status: FsStatus, |
71f80951 | 546 | /// Files, relative to `target_root`, that are produced by the step that |
8df842f5 AC |
547 | /// this `Fingerprint` represents. This is used to detect when the whole |
548 | /// fingerprint is out of date if this is missing, or if previous | |
549 | /// fingerprints output files are regenerated and look newer than this one. | |
49f63844 | 550 | #[serde(skip)] |
8df842f5 AC |
551 | outputs: Vec<PathBuf>, |
552 | } | |
553 | ||
71f80951 | 554 | /// Indication of the status on the filesystem for a particular unit. |
8df842f5 | 555 | enum FsStatus { |
71f80951 AC |
556 | /// This unit is to be considered stale, even if hash information all |
557 | /// matches. The filesystem inputs have changed (or are missing) and the | |
558 | /// unit needs to subsequently be recompiled. | |
8df842f5 | 559 | Stale, |
71f80951 | 560 | |
6b28a0c0 AC |
561 | /// This unit is up-to-date. All outputs and their corresponding mtime are |
562 | /// listed in the payload here for other dependencies to compare against. | |
563 | UpToDate { mtimes: HashMap<PathBuf, FileTime> }, | |
8df842f5 AC |
564 | } |
565 | ||
566 | impl FsStatus { | |
567 | fn up_to_date(&self) -> bool { | |
568 | match self { | |
6b28a0c0 | 569 | FsStatus::UpToDate { .. } => true, |
71f80951 | 570 | FsStatus::Stale => false, |
8df842f5 AC |
571 | } |
572 | } | |
573 | } | |
574 | ||
575 | impl Default for FsStatus { | |
576 | fn default() -> FsStatus { | |
577 | FsStatus::Stale | |
578 | } | |
a40d3b03 AC |
579 | } |
580 | ||
14f0f89b DW |
581 | impl Serialize for DepFingerprint { |
582 | fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error> | |
583 | where | |
584 | S: ser::Serializer, | |
585 | { | |
63a9c7aa | 586 | ( |
587 | &self.pkg_id, | |
588 | &self.name, | |
589 | &self.public, | |
590 | &self.fingerprint.hash(), | |
591 | ) | |
592 | .serialize(ser) | |
14f0f89b | 593 | } |
a5a298f1 AC |
594 | } |
595 | ||
14f0f89b DW |
596 | impl<'de> Deserialize<'de> for DepFingerprint { |
597 | fn deserialize<D>(d: D) -> Result<DepFingerprint, D::Error> | |
598 | where | |
599 | D: de::Deserializer<'de>, | |
600 | { | |
8185564a | 601 | let (pkg_id, name, public, hash) = <(u64, String, bool, u64)>::deserialize(d)?; |
14f0f89b DW |
602 | Ok(DepFingerprint { |
603 | pkg_id, | |
1f14fa31 | 604 | name: InternedString::new(&name), |
8185564a | 605 | public, |
14f0f89b | 606 | fingerprint: Arc::new(Fingerprint { |
14f0f89b DW |
607 | memoized_hash: Mutex::new(Some(hash)), |
608 | ..Fingerprint::new() | |
609 | }), | |
78924725 AC |
610 | // This field is never read since it's only used in |
611 | // `check_filesystem` which isn't used by fingerprints loaded from | |
612 | // disk. | |
613 | only_requires_rmeta: false, | |
1e682848 | 614 | }) |
14f0f89b | 615 | } |
a5a298f1 AC |
616 | } |
617 | ||
49f63844 AC |
618 | /// A `LocalFingerprint` represents something that we use to detect direct |
619 | /// changes to a `Fingerprint`. | |
620 | /// | |
621 | /// This is where we track file information, env vars, etc. This | |
622 | /// `LocalFingerprint` struct is hashed and if the hash changes will force a | |
623 | /// recompile of any fingerprint it's included into. Note that the "local" | |
624 | /// terminology comes from the fact that it only has to do with one crate, and | |
625 | /// `Fingerprint` tracks the transitive propagation of fingerprint changes. | |
626 | /// | |
627 | /// Note that because this is hashed its contents are carefully managed. Like | |
628 | /// mentioned in the above module docs, we don't want to hash absolute paths or | |
629 | /// mtime information. | |
630 | /// | |
631 | /// Also note that a `LocalFingerprint` is used in `check_filesystem` to detect | |
632 | /// when the filesystem contains stale information (based on mtime currently). | |
633 | /// The paths here don't change much between compilations but they're used as | |
634 | /// inputs when we probe the filesystem looking at information. | |
c057b876 | 635 | #[derive(Debug, Serialize, Deserialize, Hash)] |
4cdde7fa | 636 | enum LocalFingerprint { |
49f63844 | 637 | /// This is a precalculated fingerprint which has an opaque string we just |
cc53ecad EH |
638 | /// hash as usual. This variant is primarily used for rustdoc where we |
639 | /// don't have a dep-info file to compare against. | |
49f63844 AC |
640 | /// |
641 | /// This is also used for build scripts with no `rerun-if-*` statements, but | |
642 | /// that's overall a mistake and causes bugs in Cargo. We shouldn't use this | |
643 | /// for build scripts. | |
4cdde7fa | 644 | Precalculated(String), |
49f63844 AC |
645 | |
646 | /// This is used for crate compilations. The `dep_info` file is a relative | |
647 | /// path anchored at `target_root(...)` to the dep-info file that Cargo | |
648 | /// generates (which is a custom serialization after parsing rustc's own | |
649 | /// `dep-info` output). | |
650 | /// | |
651 | /// The `dep_info` file, when present, also lists a number of other files | |
652 | /// for us to look at. If any of those files are newer than this file then | |
653 | /// we need to recompile. | |
ea40fc4f | 654 | CheckDepInfo { dep_info: PathBuf }, |
49f63844 AC |
655 | |
656 | /// This represents a nonempty set of `rerun-if-changed` annotations printed | |
677b24ad | 657 | /// out by a build script. The `output` file is a relative file anchored at |
49f63844 AC |
658 | /// `target_root(...)` which is the actual output of the build script. That |
659 | /// output has already been parsed and the paths printed out via | |
660 | /// `rerun-if-changed` are listed in `paths`. The `paths` field is relative | |
661 | /// to `pkg.root()` | |
662 | /// | |
663 | /// This is considered up-to-date if all of the `paths` are older than | |
664 | /// `output`, otherwise we need to recompile. | |
8df842f5 AC |
665 | RerunIfChanged { |
666 | output: PathBuf, | |
667 | paths: Vec<PathBuf>, | |
668 | }, | |
49f63844 AC |
669 | |
670 | /// This represents a single `rerun-if-env-changed` annotation printed by a | |
671 | /// build script. The exact env var and value are hashed here. There's no | |
672 | /// filesystem dependence here, and if the values are changed the hash will | |
673 | /// change forcing a recompile. | |
ea40fc4f | 674 | RerunIfEnvChanged { var: String, val: Option<String> }, |
a40d3b03 AC |
675 | } |
676 | ||
8df842f5 AC |
677 | enum StaleFile { |
678 | Missing(PathBuf), | |
679 | Changed { | |
680 | reference: PathBuf, | |
681 | reference_mtime: FileTime, | |
682 | stale: PathBuf, | |
683 | stale_mtime: FileTime, | |
684 | }, | |
685 | } | |
e9428cba | 686 | |
8df842f5 | 687 | impl LocalFingerprint { |
71f80951 AC |
688 | /// Checks dynamically at runtime if this `LocalFingerprint` has a stale |
689 | /// file. | |
690 | /// | |
691 | /// This will use the absolute root paths passed in if necessary to guide | |
692 | /// file accesses. | |
8df842f5 AC |
693 | fn find_stale_file( |
694 | &self, | |
b1b9b79c | 695 | mtime_cache: &mut HashMap<PathBuf, FileTime>, |
8df842f5 AC |
696 | pkg_root: &Path, |
697 | target_root: &Path, | |
698 | ) -> CargoResult<Option<StaleFile>> { | |
e9428cba | 699 | match self { |
71f80951 AC |
700 | // We need to parse `dep_info`, learn about all the files the crate |
701 | // depends on, and then see if any of them are newer than the | |
702 | // dep_info file itself. If the `dep_info` file is missing then this | |
703 | // unit has never been compiled! | |
8df842f5 | 704 | LocalFingerprint::CheckDepInfo { dep_info } => { |
71f80951 | 705 | let dep_info = target_root.join(dep_info); |
34fd5cc8 | 706 | if let Some(paths) = parse_dep_info(pkg_root, target_root, &dep_info)? { |
b1b9b79c | 707 | Ok(find_stale_file(mtime_cache, &dep_info, paths.iter())) |
71f80951 AC |
708 | } else { |
709 | Ok(Some(StaleFile::Missing(dep_info))) | |
710 | } | |
8df842f5 | 711 | } |
71f80951 AC |
712 | |
713 | // We need to verify that no paths listed in `paths` are newer than | |
714 | // the `output` path itself, or the last time the build script ran. | |
8df842f5 | 715 | LocalFingerprint::RerunIfChanged { output, paths } => Ok(find_stale_file( |
b1b9b79c | 716 | mtime_cache, |
8df842f5 AC |
717 | &target_root.join(output), |
718 | paths.iter().map(|p| pkg_root.join(p)), | |
719 | )), | |
71f80951 AC |
720 | |
721 | // These have no dependencies on the filesystem, and their values | |
722 | // are included natively in the `Fingerprint` hash so nothing | |
723 | // tocheck for here. | |
8df842f5 AC |
724 | LocalFingerprint::RerunIfEnvChanged { .. } => Ok(None), |
725 | LocalFingerprint::Precalculated(..) => Ok(None), | |
e9428cba AC |
726 | } |
727 | } | |
728 | ||
729 | fn kind(&self) -> &'static str { | |
730 | match self { | |
731 | LocalFingerprint::Precalculated(..) => "precalculated", | |
8df842f5 AC |
732 | LocalFingerprint::CheckDepInfo { .. } => "dep-info", |
733 | LocalFingerprint::RerunIfChanged { .. } => "rerun-if-changed", | |
734 | LocalFingerprint::RerunIfEnvChanged { .. } => "rerun-if-env-changed", | |
e9428cba AC |
735 | } |
736 | } | |
8647a87d AC |
737 | } |
738 | ||
c057b876 | 739 | #[derive(Debug)] |
c447e9d0 AC |
740 | struct MtimeSlot(Mutex<Option<FileTime>>); |
741 | ||
742 | impl Fingerprint { | |
37956e8c DW |
743 | fn new() -> Fingerprint { |
744 | Fingerprint { | |
745 | rustc: 0, | |
746 | target: 0, | |
747 | profile: 0, | |
748 | path: 0, | |
749 | features: String::new(), | |
750 | deps: Vec::new(), | |
22691b94 | 751 | local: Mutex::new(Vec::new()), |
37956e8c | 752 | memoized_hash: Mutex::new(None), |
37956e8c | 753 | rustflags: Vec::new(), |
27a95d0e | 754 | metadata: 0, |
e0f9643b | 755 | config: 0, |
8df842f5 AC |
756 | fs_status: FsStatus::Stale, |
757 | outputs: Vec::new(), | |
e9428cba AC |
758 | } |
759 | } | |
760 | ||
c3868bb6 AC |
761 | /// For performance reasons fingerprints will memoize their own hash, but |
762 | /// there's also internal mutability with its `local` field which can | |
763 | /// change, for example with build scripts, during a build. | |
764 | /// | |
765 | /// This method can be used to bust all memoized hashes just before a build | |
766 | /// to ensure that after a build completes everything is up-to-date. | |
767 | pub fn clear_memoized(&self) { | |
768 | *self.memoized_hash.lock().unwrap() = None; | |
769 | } | |
770 | ||
7dcedd85 AC |
771 | fn hash(&self) -> u64 { |
772 | if let Some(s) = *self.memoized_hash.lock().unwrap() { | |
1e682848 | 773 | return s; |
c447e9d0 | 774 | } |
7dcedd85 AC |
775 | let ret = util::hash_u64(self); |
776 | *self.memoized_hash.lock().unwrap() = Some(ret); | |
3a852a0f | 777 | ret |
c447e9d0 AC |
778 | } |
779 | ||
71f80951 AC |
780 | /// Compares this fingerprint with an old version which was previously |
781 | /// serialized to filesystem. | |
782 | /// | |
783 | /// The purpose of this is exclusively to produce a diagnostic message | |
784 | /// indicating why we're recompiling something. This function always returns | |
785 | /// an error, it will never return success. | |
c447e9d0 AC |
786 | fn compare(&self, old: &Fingerprint) -> CargoResult<()> { |
787 | if self.rustc != old.rustc { | |
c057b876 | 788 | bail!("rust compiler has changed") |
c447e9d0 AC |
789 | } |
790 | if self.features != old.features { | |
c057b876 | 791 | bail!( |
1e682848 AC |
792 | "features have changed: {} != {}", |
793 | self.features, | |
794 | old.features | |
795 | ) | |
c447e9d0 AC |
796 | } |
797 | if self.target != old.target { | |
c057b876 | 798 | bail!("target configuration has changed") |
c447e9d0 | 799 | } |
8647a87d | 800 | if self.path != old.path { |
1b70e573 | 801 | bail!("path to the source has changed") |
8647a87d | 802 | } |
c447e9d0 | 803 | if self.profile != old.profile { |
c057b876 | 804 | bail!("profile configuration has changed") |
c447e9d0 | 805 | } |
e4baac0a | 806 | if self.rustflags != old.rustflags { |
25275b07 EH |
807 | bail!( |
808 | "RUSTFLAGS has changed: {:?} != {:?}", | |
809 | self.rustflags, | |
810 | old.rustflags | |
811 | ) | |
e4baac0a | 812 | } |
27a95d0e EH |
813 | if self.metadata != old.metadata { |
814 | bail!("metadata changed") | |
711b4fa3 | 815 | } |
e0f9643b EH |
816 | if self.config != old.config { |
817 | bail!("configuration settings have changed") | |
818 | } | |
22691b94 AC |
819 | let my_local = self.local.lock().unwrap(); |
820 | let old_local = old.local.lock().unwrap(); | |
821 | if my_local.len() != old_local.len() { | |
822 | bail!("local lens changed"); | |
823 | } | |
824 | for (new, old) in my_local.iter().zip(old_local.iter()) { | |
fe8bbb7a | 825 | match (new, old) { |
8df842f5 | 826 | (LocalFingerprint::Precalculated(a), LocalFingerprint::Precalculated(b)) => { |
fe8bbb7a | 827 | if a != b { |
c057b876 | 828 | bail!("precalculated components have changed: {} != {}", a, b) |
fe8bbb7a | 829 | } |
c447e9d0 | 830 | } |
1e682848 | 831 | ( |
8df842f5 AC |
832 | LocalFingerprint::CheckDepInfo { dep_info: adep }, |
833 | LocalFingerprint::CheckDepInfo { dep_info: bdep }, | |
1e682848 | 834 | ) => { |
8df842f5 AC |
835 | if adep != bdep { |
836 | bail!("dep info output changed: {:?} != {:?}", adep, bdep) | |
837 | } | |
838 | } | |
839 | ( | |
840 | LocalFingerprint::RerunIfChanged { | |
841 | output: aout, | |
842 | paths: apaths, | |
843 | }, | |
844 | LocalFingerprint::RerunIfChanged { | |
845 | output: bout, | |
846 | paths: bpaths, | |
847 | }, | |
848 | ) => { | |
849 | if aout != bout { | |
850 | bail!("rerun-if-changed output changed: {:?} != {:?}", aout, bout) | |
851 | } | |
852 | if apaths != bpaths { | |
c057b876 | 853 | bail!( |
8df842f5 AC |
854 | "rerun-if-changed output changed: {:?} != {:?}", |
855 | apaths, | |
856 | bpaths, | |
1e682848 | 857 | ) |
fe8bbb7a | 858 | } |
c447e9d0 | 859 | } |
1e682848 | 860 | ( |
8df842f5 AC |
861 | LocalFingerprint::RerunIfEnvChanged { |
862 | var: akey, | |
863 | val: avalue, | |
864 | }, | |
865 | LocalFingerprint::RerunIfEnvChanged { | |
866 | var: bkey, | |
867 | val: bvalue, | |
868 | }, | |
1e682848 | 869 | ) => { |
fe8bbb7a | 870 | if *akey != *bkey { |
c057b876 | 871 | bail!("env vars changed: {} != {}", akey, bkey); |
fe8bbb7a AC |
872 | } |
873 | if *avalue != *bvalue { | |
c057b876 | 874 | bail!( |
1e682848 AC |
875 | "env var `{}` changed: previously {:?} now {:?}", |
876 | akey, | |
877 | bvalue, | |
878 | avalue | |
879 | ) | |
fe8bbb7a AC |
880 | } |
881 | } | |
e9428cba AC |
882 | (a, b) => bail!( |
883 | "local fingerprint type has changed ({} => {})", | |
884 | b.kind(), | |
885 | a.kind() | |
886 | ), | |
c447e9d0 | 887 | } |
c447e9d0 AC |
888 | } |
889 | ||
890 | if self.deps.len() != old.deps.len() { | |
c057b876 | 891 | bail!("number of dependencies has changed") |
c447e9d0 AC |
892 | } |
893 | for (a, b) in self.deps.iter().zip(old.deps.iter()) { | |
e9428cba AC |
894 | if a.name != b.name { |
895 | let e = format_err!("`{}` != `{}`", a.name, b.name) | |
896 | .context("unit dependency name changed"); | |
6eefe3c2 | 897 | return Err(e); |
c447e9d0 | 898 | } |
e9428cba AC |
899 | |
900 | if a.fingerprint.hash() != b.fingerprint.hash() { | |
901 | let e = format_err!( | |
902 | "new ({}/{:x}) != old ({}/{:x})", | |
903 | a.name, | |
904 | a.fingerprint.hash(), | |
905 | b.name, | |
906 | b.fingerprint.hash() | |
907 | ) | |
908 | .context("unit dependency information changed"); | |
6eefe3c2 | 909 | return Err(e); |
e9428cba AC |
910 | } |
911 | } | |
912 | ||
8df842f5 AC |
913 | if !self.fs_status.up_to_date() { |
914 | bail!("current filesystem status shows we're outdated"); | |
e9428cba AC |
915 | } |
916 | ||
71f80951 AC |
917 | // This typically means some filesystem modifications happened or |
918 | // something transitive was odd. In general we should strive to provide | |
919 | // a better error message than this, so if you see this message a lot it | |
920 | // likely means this method needs to be updated! | |
8df842f5 AC |
921 | bail!("two fingerprint comparison turned up nothing obvious"); |
922 | } | |
e9428cba | 923 | |
71f80951 AC |
924 | /// Dynamically inspect the local filesystem to update the `fs_status` field |
925 | /// of this `Fingerprint`. | |
926 | /// | |
927 | /// This function is used just after a `Fingerprint` is constructed to check | |
928 | /// the local state of the filesystem and propagate any dirtiness from | |
929 | /// dependencies up to this unit as well. This function assumes that the | |
930 | /// unit starts out as `FsStatus::Stale` and then it will optionally switch | |
931 | /// it to `UpToDate` if it can. | |
b1b9b79c EH |
932 | fn check_filesystem( |
933 | &mut self, | |
934 | mtime_cache: &mut HashMap<PathBuf, FileTime>, | |
935 | pkg_root: &Path, | |
936 | target_root: &Path, | |
937 | ) -> CargoResult<()> { | |
71f80951 AC |
938 | assert!(!self.fs_status.up_to_date()); |
939 | ||
6b28a0c0 AC |
940 | let mut mtimes = HashMap::new(); |
941 | ||
71f80951 AC |
942 | // Get the `mtime` of all outputs. Optionally update their mtime |
943 | // afterwards based on the `mtime_on_use` flag. Afterwards we want the | |
944 | // minimum mtime as it's the one we'll be comparing to inputs and | |
945 | // dependencies. | |
6b28a0c0 AC |
946 | for output in self.outputs.iter() { |
947 | let mtime = match paths::mtime(output) { | |
948 | Ok(mtime) => mtime, | |
949 | ||
950 | // This path failed to report its `mtime`. It probably doesn't | |
951 | // exists, so leave ourselves as stale and bail out. | |
952 | Err(e) => { | |
298b7703 | 953 | debug!("failed to get mtime of {:?}: {}", output, e); |
6b28a0c0 | 954 | return Ok(()); |
8df842f5 | 955 | } |
6b28a0c0 | 956 | }; |
6b28a0c0 AC |
957 | assert!(mtimes.insert(output.clone(), mtime).is_none()); |
958 | } | |
959 | ||
677b24ad EH |
960 | let opt_max = mtimes.iter().max_by_key(|kv| kv.1); |
961 | let (max_path, max_mtime) = match opt_max { | |
6b28a0c0 | 962 | Some(mtime) => mtime, |
71f80951 | 963 | |
71f80951 AC |
964 | // We had no output files. This means we're an overridden build |
965 | // script and we're just always up to date because we aren't | |
966 | // watching the filesystem. | |
8df842f5 | 967 | None => { |
6b28a0c0 | 968 | self.fs_status = FsStatus::UpToDate { mtimes }; |
8df842f5 AC |
969 | return Ok(()); |
970 | } | |
971 | }; | |
677b24ad EH |
972 | debug!( |
973 | "max output mtime for {:?} is {:?} {}", | |
974 | pkg_root, max_path, max_mtime | |
975 | ); | |
8df842f5 AC |
976 | |
977 | for dep in self.deps.iter() { | |
6b28a0c0 AC |
978 | let dep_mtimes = match &dep.fingerprint.fs_status { |
979 | FsStatus::UpToDate { mtimes } => mtimes, | |
71f80951 | 980 | // If our dependency is stale, so are we, so bail out. |
8df842f5 | 981 | FsStatus::Stale => return Ok(()), |
6b28a0c0 | 982 | }; |
71f80951 | 983 | |
c2234069 | 984 | // If our dependency edge only requires the rmeta file to be present |
6b28a0c0 AC |
985 | // then we only need to look at that one output file, otherwise we |
986 | // need to consider all output files to see if we're out of date. | |
298b7703 | 987 | let (dep_path, dep_mtime) = if dep.only_requires_rmeta { |
6b28a0c0 AC |
988 | dep_mtimes |
989 | .iter() | |
13cd4fb1 | 990 | .find(|(path, _mtime)| { |
298b7703 | 991 | path.extension().and_then(|s| s.to_str()) == Some("rmeta") |
6b28a0c0 | 992 | }) |
6b28a0c0 AC |
993 | .expect("failed to find rmeta") |
994 | } else { | |
298b7703 EH |
995 | match dep_mtimes.iter().max_by_key(|kv| kv.1) { |
996 | Some(dep_mtime) => dep_mtime, | |
6b28a0c0 AC |
997 | // If our dependencies is up to date and has no filesystem |
998 | // interactions, then we can move on to the next dependency. | |
999 | None => continue, | |
1000 | } | |
8df842f5 | 1001 | }; |
298b7703 EH |
1002 | debug!( |
1003 | "max dep mtime for {:?} is {:?} {}", | |
1004 | pkg_root, dep_path, dep_mtime | |
1005 | ); | |
71f80951 AC |
1006 | |
1007 | // If the dependency is newer than our own output then it was | |
1008 | // recompiled previously. We transitively become stale ourselves in | |
1009 | // that case, so bail out. | |
49f63844 AC |
1010 | // |
1011 | // Note that this comparison should probably be `>=`, not `>`, but | |
1012 | // for a discussion of why it's `>` see the discussion about #5918 | |
1013 | // below in `find_stale`. | |
6b28a0c0 | 1014 | if dep_mtime > max_mtime { |
677b24ad EH |
1015 | info!( |
1016 | "dependency on `{}` is newer than we are {} > {} {:?}", | |
298b7703 | 1017 | dep.name, dep_mtime, max_mtime, pkg_root |
677b24ad | 1018 | ); |
8df842f5 AC |
1019 | return Ok(()); |
1020 | } | |
1021 | } | |
71f80951 AC |
1022 | |
1023 | // If we reached this far then all dependencies are up to date. Check | |
1024 | // all our `LocalFingerprint` information to see if we have any stale | |
1025 | // files for this package itself. If we do find something log a helpful | |
1026 | // message and bail out so we stay stale. | |
22691b94 | 1027 | for local in self.local.get_mut().unwrap().iter() { |
b1b9b79c | 1028 | if let Some(file) = local.find_stale_file(mtime_cache, pkg_root, target_root)? { |
8df842f5 AC |
1029 | file.log(); |
1030 | return Ok(()); | |
1031 | } | |
1032 | } | |
71f80951 AC |
1033 | |
1034 | // Everything was up to date! Record such. | |
6b28a0c0 | 1035 | self.fs_status = FsStatus::UpToDate { mtimes }; |
677b24ad | 1036 | debug!("filesystem up-to-date {:?}", pkg_root); |
71f80951 | 1037 | |
c447e9d0 AC |
1038 | Ok(()) |
1039 | } | |
1040 | } | |
1041 | ||
7dcedd85 AC |
1042 | impl hash::Hash for Fingerprint { |
1043 | fn hash<H: Hasher>(&self, h: &mut H) { | |
24ba7c80 LL |
1044 | let Fingerprint { |
1045 | rustc, | |
1046 | ref features, | |
1047 | target, | |
8647a87d | 1048 | path, |
24ba7c80 LL |
1049 | profile, |
1050 | ref deps, | |
1051 | ref local, | |
27a95d0e | 1052 | metadata, |
e0f9643b | 1053 | config, |
24ba7c80 | 1054 | ref rustflags, |
676edacf | 1055 | .. |
24ba7c80 | 1056 | } = *self; |
22691b94 | 1057 | let local = local.lock().unwrap(); |
1e682848 | 1058 | ( |
e0f9643b | 1059 | rustc, features, target, path, profile, &*local, metadata, config, rustflags, |
dae87a26 E |
1060 | ) |
1061 | .hash(h); | |
04c30d61 GS |
1062 | |
1063 | h.write_usize(deps.len()); | |
14f0f89b DW |
1064 | for DepFingerprint { |
1065 | pkg_id, | |
1066 | name, | |
8185564a | 1067 | public, |
14f0f89b | 1068 | fingerprint, |
78924725 | 1069 | only_requires_rmeta: _, // static property, no need to hash |
14f0f89b DW |
1070 | } in deps |
1071 | { | |
fc72bcb4 | 1072 | pkg_id.hash(h); |
04c30d61 | 1073 | name.hash(h); |
8185564a | 1074 | public.hash(h); |
04c30d61 GS |
1075 | // use memoized dep hashes to avoid exponential blowup |
1076 | h.write_u64(Fingerprint::hash(fingerprint)); | |
1077 | } | |
7dcedd85 AC |
1078 | } |
1079 | } | |
1080 | ||
7dcedd85 | 1081 | impl hash::Hash for MtimeSlot { |
c447e9d0 AC |
1082 | fn hash<H: Hasher>(&self, h: &mut H) { |
1083 | self.0.lock().unwrap().hash(h) | |
1084 | } | |
1085 | } | |
1086 | ||
a5a298f1 AC |
1087 | impl ser::Serialize for MtimeSlot { |
1088 | fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> | |
1e682848 AC |
1089 | where |
1090 | S: ser::Serializer, | |
a5a298f1 | 1091 | { |
1e682848 AC |
1092 | self.0 |
1093 | .lock() | |
1094 | .unwrap() | |
039ca019 | 1095 | .map(|ft| (ft.unix_seconds(), ft.nanoseconds())) |
1e682848 | 1096 | .serialize(s) |
c447e9d0 AC |
1097 | } |
1098 | } | |
1099 | ||
0b59f17d | 1100 | impl<'de> de::Deserialize<'de> for MtimeSlot { |
a5a298f1 | 1101 | fn deserialize<D>(d: D) -> Result<MtimeSlot, D::Error> |
1e682848 AC |
1102 | where |
1103 | D: de::Deserializer<'de>, | |
a5a298f1 | 1104 | { |
039ca019 | 1105 | let kind: Option<(i64, u32)> = de::Deserialize::deserialize(d)?; |
dae87a26 E |
1106 | Ok(MtimeSlot(Mutex::new( |
1107 | kind.map(|(s, n)| FileTime::from_unix_time(s, n)), | |
1108 | ))) | |
a40d3b03 AC |
1109 | } |
1110 | } | |
1111 | ||
e9428cba | 1112 | impl DepFingerprint { |
c85ed044 | 1113 | fn new(cx: &mut Context<'_, '_>, parent: &Unit, dep: &UnitDep) -> CargoResult<DepFingerprint> { |
1f14fa31 | 1114 | let fingerprint = calculate(cx, &dep.unit)?; |
e9428cba AC |
1115 | // We need to be careful about what we hash here. We have a goal of |
1116 | // supporting renaming a project directory and not rebuilding | |
1117 | // everything. To do that, however, we need to make sure that the cwd | |
1118 | // doesn't make its way into any hashes, and one source of that is the | |
1119 | // `SourceId` for `path` packages. | |
1120 | // | |
1121 | // We already have a requirement that `path` packages all have unique | |
1122 | // names (sort of for this same reason), so if the package source is a | |
1123 | // `path` then we just hash the name, but otherwise we hash the full | |
1124 | // id as it won't change when the directory is renamed. | |
1f14fa31 EH |
1125 | let pkg_id = if dep.unit.pkg.package_id().source_id().is_path() { |
1126 | util::hash_u64(dep.unit.pkg.package_id().name()) | |
e9428cba | 1127 | } else { |
1f14fa31 | 1128 | util::hash_u64(dep.unit.pkg.package_id()) |
e9428cba AC |
1129 | }; |
1130 | ||
1131 | Ok(DepFingerprint { | |
1132 | pkg_id, | |
1f14fa31 EH |
1133 | name: dep.extern_crate_name, |
1134 | public: dep.public, | |
e9428cba | 1135 | fingerprint, |
1f14fa31 | 1136 | only_requires_rmeta: cx.only_requires_rmeta(parent, &dep.unit), |
e9428cba AC |
1137 | }) |
1138 | } | |
1139 | } | |
1140 | ||
8df842f5 | 1141 | impl StaleFile { |
71f80951 AC |
1142 | /// Use the `log` crate to log a hopefully helpful message in diagnosing |
1143 | /// what file is considered stale and why. This is intended to be used in | |
782266aa | 1144 | /// conjunction with `CARGO_LOG` to determine why Cargo is recompiling |
71f80951 AC |
1145 | /// something. Currently there's no user-facing usage of this other than |
1146 | /// that. | |
8df842f5 AC |
1147 | fn log(&self) { |
1148 | match self { | |
1149 | StaleFile::Missing(path) => { | |
677b24ad | 1150 | info!("stale: missing {:?}", path); |
8df842f5 AC |
1151 | } |
1152 | StaleFile::Changed { | |
1153 | reference, | |
1154 | reference_mtime, | |
1155 | stale, | |
1156 | stale_mtime, | |
1157 | } => { | |
677b24ad EH |
1158 | info!("stale: changed {:?}", stale); |
1159 | info!(" (vs) {:?}", reference); | |
1160 | info!(" {:?} != {:?}", reference_mtime, stale_mtime); | |
8df842f5 AC |
1161 | } |
1162 | } | |
1163 | } | |
1164 | } | |
1165 | ||
e9428cba | 1166 | /// Calculates the fingerprint for a `unit`. |
a40d3b03 AC |
1167 | /// |
1168 | /// This fingerprint is used by Cargo to learn about when information such as: | |
1169 | /// | |
1170 | /// * A non-path package changes (changes version, changes revision, etc). | |
1171 | /// * Any dependency changes | |
1172 | /// * The compiler changes | |
1173 | /// * The set of features a package is built with changes | |
f7c91ba6 | 1174 | /// * The profile a target is compiled with changes (e.g., opt-level changes) |
c057b876 | 1175 | /// * Any other compiler flags change that will affect the result |
a40d3b03 AC |
1176 | /// |
1177 | /// Information like file modification time is only calculated for path | |
c057b876 | 1178 | /// dependencies. |
c85ed044 | 1179 | fn calculate(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Arc<Fingerprint>> { |
e9428cba | 1180 | // This function is slammed quite a lot, so the result is memoized. |
659f8244 | 1181 | if let Some(s) = cx.fingerprints.get(unit) { |
1e682848 | 1182 | return Ok(Arc::clone(s)); |
a40d3b03 | 1183 | } |
8df842f5 | 1184 | let mut fingerprint = if unit.mode.is_run_custom_build() { |
e9428cba | 1185 | calculate_run_custom_build(cx, unit)? |
935adb31 EH |
1186 | } else if unit.mode.is_doc_test() { |
1187 | panic!("doc tests do not fingerprint"); | |
e9428cba AC |
1188 | } else { |
1189 | calculate_normal(cx, unit)? | |
1190 | }; | |
71f80951 AC |
1191 | |
1192 | // After we built the initial `Fingerprint` be sure to update the | |
1193 | // `fs_status` field of it. | |
aa99e9f2 | 1194 | let target_root = target_root(cx); |
b1b9b79c | 1195 | fingerprint.check_filesystem(&mut cx.mtime_cache, unit.pkg.root(), &target_root)?; |
71f80951 | 1196 | |
e9428cba | 1197 | let fingerprint = Arc::new(fingerprint); |
c85ed044 AC |
1198 | cx.fingerprints |
1199 | .insert(unit.clone(), Arc::clone(&fingerprint)); | |
e9428cba AC |
1200 | Ok(fingerprint) |
1201 | } | |
a40d3b03 | 1202 | |
e9428cba AC |
1203 | /// Calculate a fingerprint for a "normal" unit, or anything that's not a build |
1204 | /// script. This is an internal helper of `calculate`, don't call directly. | |
c85ed044 | 1205 | fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Fingerprint> { |
e9428cba | 1206 | // Recursively calculate the fingerprint for all of our dependencies. |
201aa61c | 1207 | // |
e9428cba AC |
1208 | // Skip fingerprints of binaries because they don't actually induce a |
1209 | // recompile, they're just dependencies in the sense that they need to be | |
1210 | // built. | |
1f14fa31 EH |
1211 | // |
1212 | // Create Vec since mutable cx is needed in closure. | |
1213 | let deps = Vec::from(cx.unit_deps(unit)); | |
1214 | let mut deps = deps | |
1215 | .into_iter() | |
1216 | .filter(|dep| !dep.unit.target.is_bin()) | |
1217 | .map(|dep| DepFingerprint::new(cx, unit, &dep)) | |
1e682848 | 1218 | .collect::<CargoResult<Vec<_>>>()?; |
035913ff | 1219 | deps.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id)); |
a40d3b03 | 1220 | |
cc53ecad | 1221 | // Afterwards calculate our own fingerprint information. |
aa99e9f2 | 1222 | let target_root = target_root(cx); |
cc53ecad EH |
1223 | let local = if unit.mode.is_doc() { |
1224 | // rustdoc does not have dep-info files. | |
9ed56cad EH |
1225 | let fingerprint = pkg_fingerprint(cx.bcx, &unit.pkg).chain_err(|| { |
1226 | format!( | |
1227 | "failed to determine package fingerprint for documenting {}", | |
1228 | unit.pkg | |
1229 | ) | |
1230 | })?; | |
cc53ecad EH |
1231 | vec![LocalFingerprint::Precalculated(fingerprint)] |
1232 | } else { | |
659f8244 | 1233 | let dep_info = dep_info_loc(cx, unit); |
8df842f5 AC |
1234 | let dep_info = dep_info.strip_prefix(&target_root).unwrap().to_path_buf(); |
1235 | vec![LocalFingerprint::CheckDepInfo { dep_info }] | |
a40d3b03 | 1236 | }; |
035913ff | 1237 | |
8df842f5 AC |
1238 | // Figure out what the outputs of our unit is, and we'll be storing them |
1239 | // into the fingerprint as well. | |
de44054d EH |
1240 | let outputs = cx |
1241 | .outputs(unit)? | |
1242 | .iter() | |
90c0bcde | 1243 | .filter(|output| !matches!(output.flavor, FileFlavor::DebugInfo | FileFlavor::Auxiliary)) |
de44054d EH |
1244 | .map(|output| output.path.clone()) |
1245 | .collect(); | |
8df842f5 | 1246 | |
e9428cba AC |
1247 | // Fill out a bunch more information that we'll be tracking typically |
1248 | // hashed to take up less space on disk as we just need to know when things | |
1249 | // change. | |
b0351e4d | 1250 | let extra_flags = if unit.mode.is_doc() { |
bd8253fd | 1251 | cx.bcx.rustdocflags_args(unit) |
f639d381 | 1252 | } else { |
bd8253fd | 1253 | cx.bcx.rustflags_args(unit) |
bd73e8da EH |
1254 | } |
1255 | .to_vec(); | |
bd73e8da | 1256 | |
e2219254 AC |
1257 | let profile_hash = util::hash_u64(( |
1258 | &unit.profile, | |
1259 | unit.mode, | |
1260 | cx.bcx.extra_args_for(unit), | |
1261 | cx.lto[unit], | |
1262 | )); | |
27a95d0e EH |
1263 | // Include metadata since it is exposed as environment variables. |
1264 | let m = unit.pkg.manifest().metadata(); | |
e9428cba | 1265 | let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository)); |
e0f9643b EH |
1266 | let config = if unit.mode.is_doc() && cx.bcx.config.cli_unstable().rustdoc_map { |
1267 | cx.bcx | |
1268 | .config | |
1269 | .doc_extern_map() | |
1270 | .map_or(0, |map| util::hash_u64(map)) | |
1271 | } else { | |
1272 | 0 | |
1273 | }; | |
e9428cba | 1274 | Ok(Fingerprint { |
949eccac | 1275 | rustc: util::hash_u64(&cx.bcx.rustc().verbose_version), |
c447e9d0 | 1276 | target: util::hash_u64(&unit.target), |
768f5739 | 1277 | profile: profile_hash, |
8647a87d AC |
1278 | // Note that .0 is hashed here, not .1 which is the cwd. That doesn't |
1279 | // actually affect the output artifact so there's no need to hash it. | |
e9428cba | 1280 | path: util::hash_u64(super::path_args(cx.bcx, unit).0), |
a7efa91e | 1281 | features: format!("{:?}", unit.features), |
0247dc42 | 1282 | deps, |
22691b94 | 1283 | local: Mutex::new(local), |
7dcedd85 | 1284 | memoized_hash: Mutex::new(None), |
27a95d0e | 1285 | metadata, |
e0f9643b | 1286 | config, |
bd73e8da | 1287 | rustflags: extra_flags, |
8df842f5 AC |
1288 | fs_status: FsStatus::Stale, |
1289 | outputs, | |
e9428cba | 1290 | }) |
a40d3b03 AC |
1291 | } |
1292 | ||
e9428cba AC |
1293 | /// Calculate a fingerprint for an "execute a build script" unit. This is an |
1294 | /// internal helper of `calculate`, don't call directly. | |
c85ed044 | 1295 | fn calculate_run_custom_build(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Fingerprint> { |
2296af27 | 1296 | assert!(unit.mode.is_run_custom_build()); |
e9428cba AC |
1297 | // Using the `BuildDeps` information we'll have previously parsed and |
1298 | // inserted into `build_explicit_deps` built an initial snapshot of the | |
1299 | // `LocalFingerprint` list for this build script. If we previously executed | |
1300 | // the build script this means we'll be watching files and env vars. | |
1301 | // Otherwise if we haven't previously executed it we'll just start watching | |
1302 | // the whole crate. | |
1303 | let (gen_local, overridden) = build_script_local_fingerprints(cx, unit); | |
1304 | let deps = &cx.build_explicit_deps[unit]; | |
9ed56cad EH |
1305 | let local = (gen_local)( |
1306 | deps, | |
1307 | Some(&|| { | |
1308 | pkg_fingerprint(cx.bcx, &unit.pkg).chain_err(|| { | |
1309 | format!( | |
1310 | "failed to determine package fingerprint for build script for {}", | |
1311 | unit.pkg | |
1312 | ) | |
1313 | }) | |
1314 | }), | |
1315 | )? | |
1316 | .unwrap(); | |
8df842f5 | 1317 | let output = deps.build_script_output.clone(); |
e9428cba AC |
1318 | |
1319 | // Include any dependencies of our execution, which is typically just the | |
1320 | // compilation of the build script itself. (if the build script changes we | |
1321 | // should be rerun!). Note though that if we're an overridden build script | |
1322 | // we have no dependencies so no need to recurse in that case. | |
1323 | let deps = if overridden { | |
c057b876 EH |
1324 | // Overridden build scripts don't need to track deps. |
1325 | vec![] | |
1326 | } else { | |
1f14fa31 EH |
1327 | // Create Vec since mutable cx is needed in closure. |
1328 | let deps = Vec::from(cx.unit_deps(unit)); | |
1329 | deps.into_iter() | |
1330 | .map(|dep| DepFingerprint::new(cx, unit, &dep)) | |
c057b876 EH |
1331 | .collect::<CargoResult<Vec<_>>>()? |
1332 | }; | |
1333 | ||
e9428cba | 1334 | Ok(Fingerprint { |
22691b94 | 1335 | local: Mutex::new(local), |
949eccac | 1336 | rustc: util::hash_u64(&cx.bcx.rustc().verbose_version), |
c057b876 | 1337 | deps, |
8df842f5 | 1338 | outputs: if overridden { Vec::new() } else { vec![output] }, |
7dcedd85 | 1339 | |
e9428cba AC |
1340 | // Most of the other info is blank here as we don't really include it |
1341 | // in the execution of the build script, but... this may be a latent | |
1342 | // bug in Cargo. | |
1343 | ..Fingerprint::new() | |
1344 | }) | |
7dcedd85 AC |
1345 | } |
1346 | ||
e9428cba AC |
1347 | /// Get ready to compute the `LocalFingerprint` values for a `RunCustomBuild` |
1348 | /// unit. | |
1349 | /// | |
1350 | /// This function has, what's on the surface, a seriously wonky interface. | |
1351 | /// You'll call this function and it'll return a closure and a boolean. The | |
1352 | /// boolean is pretty simple in that it indicates whether the `unit` has been | |
1353 | /// overridden via `.cargo/config`. The closure is much more complicated. | |
1354 | /// | |
1355 | /// This closure is intended to capture any local state necessary to compute | |
1356 | /// the `LocalFingerprint` values for this unit. It is `Send` and `'static` to | |
1357 | /// be sent to other threads as well (such as when we're executing build | |
1358 | /// scripts). That deduplication is the rationale for the closure at least. | |
c057b876 | 1359 | /// |
e9428cba | 1360 | /// The arguments to the closure are a bit weirder, though, and I'll apologize |
ae4b792f MR |
1361 | /// in advance for the weirdness too. The first argument to the closure is a |
1362 | /// `&BuildDeps`. This is the parsed version of a build script, and when Cargo | |
1363 | /// starts up this is cached from previous runs of a build script. After a | |
1364 | /// build script executes the output file is reparsed and passed in here. | |
e9428cba AC |
1365 | /// |
1366 | /// The second argument is the weirdest, it's *optionally* a closure to | |
1367 | /// call `pkg_fingerprint` below. The `pkg_fingerprint` below requires access | |
1368 | /// to "source map" located in `Context`. That's very non-`'static` and | |
1369 | /// non-`Send`, so it can't be used on other threads, such as when we invoke | |
1370 | /// this after a build script has finished. The `Option` allows us to for sure | |
1371 | /// calculate it on the main thread at the beginning, and then swallow the bug | |
1372 | /// for now where a worker thread after a build script has finished doesn't | |
1373 | /// have access. Ideally there would be no second argument or it would be more | |
1374 | /// "first class" and not an `Option` but something that can be sent between | |
1375 | /// threads. In any case, it's a bug for now. | |
1376 | /// | |
1377 | /// This isn't the greatest of interfaces, and if there's suggestions to | |
1378 | /// improve please do so! | |
1379 | /// | |
1380 | /// FIXME(#6779) - see all the words above | |
c85ed044 AC |
1381 | fn build_script_local_fingerprints( |
1382 | cx: &mut Context<'_, '_>, | |
1383 | unit: &Unit, | |
ae4b792f MR |
1384 | ) -> ( |
1385 | Box< | |
1386 | dyn FnOnce( | |
1387 | &BuildDeps, | |
1388 | Option<&dyn Fn() -> CargoResult<String>>, | |
1389 | ) -> CargoResult<Option<Vec<LocalFingerprint>>> | |
1390 | + Send, | |
1391 | >, | |
1392 | bool, | |
1393 | ) { | |
2296af27 | 1394 | assert!(unit.mode.is_run_custom_build()); |
fe8bbb7a | 1395 | // First up, if this build script is entirely overridden, then we just |
e9428cba | 1396 | // return the hash of what we overrode it with. This is the easy case! |
035913ff | 1397 | if let Some(fingerprint) = build_script_override_fingerprint(cx, unit) { |
677b24ad | 1398 | debug!("override local fingerprints deps {}", unit.pkg); |
e9428cba AC |
1399 | return ( |
1400 | Box::new( | |
1401 | move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult<String>>| { | |
1402 | Ok(Some(vec![fingerprint])) | |
1403 | }, | |
1404 | ), | |
1405 | true, // this is an overridden build script | |
1406 | ); | |
fe8bbb7a AC |
1407 | } |
1408 | ||
e9428cba AC |
1409 | // ... Otherwise this is a "real" build script and we need to return a real |
1410 | // closure. Our returned closure classifies the build script based on | |
1411 | // whether it prints `rerun-if-*`. If it *doesn't* print this it's where the | |
1412 | // magical second argument comes into play, which fingerprints a whole | |
1413 | // package. Remember that the fact that this is an `Option` is a bug, but a | |
1414 | // longstanding bug, in Cargo. Recent refactorings just made it painfully | |
1415 | // obvious. | |
e9428cba | 1416 | let pkg_root = unit.pkg.root().to_path_buf(); |
aa99e9f2 | 1417 | let target_dir = target_root(cx); |
e9428cba AC |
1418 | let calculate = |
1419 | move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult<String>>| { | |
1420 | if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() { | |
1421 | match pkg_fingerprint { | |
71f80951 AC |
1422 | // FIXME: this is somewhat buggy with respect to docker and |
1423 | // weird filesystems. The `Precalculated` variant | |
1424 | // constructed below will, for `path` dependencies, contain | |
1425 | // a stringified version of the mtime for the local crate. | |
1426 | // This violates one of the things we describe in this | |
1427 | // module's doc comment, never hashing mtimes. We should | |
1428 | // figure out a better scheme where a package fingerprint | |
1429 | // may be a string (like for a registry) or a list of files | |
1430 | // (like for a path dependency). Those list of files would | |
1431 | // be stored here rather than the the mtime of them. | |
e9428cba | 1432 | Some(f) => { |
e9428cba | 1433 | let s = f()?; |
677b24ad EH |
1434 | debug!( |
1435 | "old local fingerprints deps {:?} precalculated={:?}", | |
1436 | pkg_root, s | |
1437 | ); | |
e9428cba AC |
1438 | return Ok(Some(vec![LocalFingerprint::Precalculated(s)])); |
1439 | } | |
1440 | None => return Ok(None), | |
1441 | } | |
1442 | } | |
1443 | ||
1444 | // Ok so now we're in "new mode" where we can have files listed as | |
1445 | // dependencies as well as env vars listed as dependencies. Process | |
1446 | // them all here. | |
aa99e9f2 | 1447 | Ok(Some(local_fingerprints_deps(deps, &target_dir, &pkg_root))) |
e9428cba | 1448 | }; |
fe8bbb7a | 1449 | |
e9428cba AC |
1450 | // Note that `false` == "not overridden" |
1451 | (Box::new(calculate), false) | |
fe8bbb7a AC |
1452 | } |
1453 | ||
c057b876 | 1454 | /// Create a `LocalFingerprint` for an overridden build script. |
035913ff | 1455 | /// Returns None if it is not overridden. |
c85ed044 AC |
1456 | fn build_script_override_fingerprint( |
1457 | cx: &mut Context<'_, '_>, | |
1458 | unit: &Unit, | |
035913ff | 1459 | ) -> Option<LocalFingerprint> { |
bd31c081 EH |
1460 | // Build script output is only populated at this stage when it is |
1461 | // overridden. | |
1462 | let build_script_outputs = cx.build_script_outputs.lock().unwrap(); | |
2296af27 | 1463 | let metadata = cx.get_run_build_script_metadata(unit); |
bd31c081 | 1464 | // Returns None if it is not overridden. |
2296af27 | 1465 | let output = build_script_outputs.get(unit.pkg.package_id(), metadata)?; |
e9428cba AC |
1466 | let s = format!( |
1467 | "overridden build state with hash: {}", | |
1468 | util::hash_u64(output) | |
1469 | ); | |
1470 | Some(LocalFingerprint::Precalculated(s)) | |
035913ff EH |
1471 | } |
1472 | ||
c057b876 | 1473 | /// Compute the `LocalFingerprint` values for a `RunCustomBuild` unit for |
e9428cba AC |
1474 | /// non-overridden new-style build scripts only. This is only used when `deps` |
1475 | /// is already known to have a nonempty `rerun-if-*` somewhere. | |
1e682848 AC |
1476 | fn local_fingerprints_deps( |
1477 | deps: &BuildDeps, | |
1478 | target_root: &Path, | |
1479 | pkg_root: &Path, | |
1480 | ) -> Vec<LocalFingerprint> { | |
677b24ad | 1481 | debug!("new local fingerprints deps {:?}", pkg_root); |
fe8bbb7a | 1482 | let mut local = Vec::new(); |
e9428cba | 1483 | |
fe8bbb7a | 1484 | if !deps.rerun_if_changed.is_empty() { |
71f80951 AC |
1485 | // Note that like the module comment above says we are careful to never |
1486 | // store an absolute path in `LocalFingerprint`, so ensure that we strip | |
1487 | // absolute prefixes from them. | |
8df842f5 AC |
1488 | let output = deps |
1489 | .build_script_output | |
1490 | .strip_prefix(target_root) | |
1491 | .unwrap() | |
1492 | .to_path_buf(); | |
1493 | let paths = deps | |
1494 | .rerun_if_changed | |
1495 | .iter() | |
1496 | .map(|p| p.strip_prefix(pkg_root).unwrap_or(p).to_path_buf()) | |
1497 | .collect(); | |
1498 | local.push(LocalFingerprint::RerunIfChanged { output, paths }); | |
fe8bbb7a AC |
1499 | } |
1500 | ||
1501 | for var in deps.rerun_if_env_changed.iter() { | |
1502 | let val = env::var(var).ok(); | |
8df842f5 AC |
1503 | local.push(LocalFingerprint::RerunIfEnvChanged { |
1504 | var: var.clone(), | |
1505 | val, | |
1506 | }); | |
fe8bbb7a AC |
1507 | } |
1508 | ||
23591fe5 | 1509 | local |
fe8bbb7a AC |
1510 | } |
1511 | ||
7dcedd85 | 1512 | fn write_fingerprint(loc: &Path, fingerprint: &Fingerprint) -> CargoResult<()> { |
eb8720a5 E |
1513 | debug_assert_ne!(fingerprint.rustc, 0); |
1514 | // fingerprint::new().rustc == 0, make sure it doesn't make it to the file system. | |
1515 | // This is mostly so outside tools can reliably find out what rust version this file is for, | |
1516 | // as we can use the full hash. | |
7dcedd85 | 1517 | let hash = fingerprint.hash(); |
e9428cba | 1518 | debug!("write fingerprint ({:x}) : {}", hash, loc.display()); |
23591fe5 | 1519 | paths::write(loc, util::to_hex(hash).as_bytes())?; |
e9428cba AC |
1520 | |
1521 | let json = serde_json::to_string(fingerprint).unwrap(); | |
1522 | if cfg!(debug_assertions) { | |
1523 | let f: Fingerprint = serde_json::from_str(&json).unwrap(); | |
1524 | assert_eq!(f.hash(), hash); | |
1525 | } | |
1526 | paths::write(&loc.with_extension("json"), json.as_bytes())?; | |
7dcedd85 | 1527 | Ok(()) |
79768eb0 AC |
1528 | } |
1529 | ||
b6aac11d | 1530 | /// Prepare for work when a package starts to build |
c85ed044 | 1531 | pub fn prepare_init(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<()> { |
cf0bb13d | 1532 | let new1 = cx.files().fingerprint_dir(unit); |
79768eb0 | 1533 | |
935adb31 EH |
1534 | // Doc tests have no output, thus no fingerprint. |
1535 | if !new1.exists() && !unit.mode.is_doc_test() { | |
5102de2b | 1536 | paths::create_dir_all(&new1)?; |
659f8244 | 1537 | } |
b6aac11d | 1538 | |
659f8244 | 1539 | Ok(()) |
79768eb0 AC |
1540 | } |
1541 | ||
71f80951 AC |
1542 | /// Returns the location that the dep-info file will show up at for the `unit` |
1543 | /// specified. | |
c85ed044 | 1544 | pub fn dep_info_loc(cx: &mut Context<'_, '_>, unit: &Unit) -> PathBuf { |
7438770b | 1545 | cx.files().fingerprint_file_path(unit, "dep-") |
dd2c5980 AC |
1546 | } |
1547 | ||
aa99e9f2 EH |
1548 | /// Returns an absolute path that target directory. |
1549 | /// All paths are rewritten to be relative to this. | |
1550 | fn target_root(cx: &Context<'_, '_>) -> PathBuf { | |
1551 | cx.bcx.ws.target_dir().into_path_unlocked() | |
8df842f5 AC |
1552 | } |
1553 | ||
5f6ede29 EH |
1554 | fn compare_old_fingerprint( |
1555 | loc: &Path, | |
1556 | new_fingerprint: &Fingerprint, | |
1557 | mtime_on_use: bool, | |
1558 | ) -> CargoResult<()> { | |
82655b46 | 1559 | let old_fingerprint_short = paths::read(loc)?; |
3eaa70e2 | 1560 | |
5f6ede29 EH |
1561 | if mtime_on_use { |
1562 | // update the mtime so other cleaners know we used it | |
1563 | let t = FileTime::from_system_time(SystemTime::now()); | |
298b7703 | 1564 | debug!("mtime-on-use forcing {:?} to {}", loc, t); |
5f9d9f21 | 1565 | paths::set_file_time_no_err(loc, t); |
5f6ede29 | 1566 | } |
3eaa70e2 | 1567 | |
7dcedd85 | 1568 | let new_hash = new_fingerprint.hash(); |
d3c350b9 | 1569 | |
8df842f5 | 1570 | if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() { |
1e682848 | 1571 | return Ok(()); |
c447e9d0 AC |
1572 | } |
1573 | ||
82655b46 | 1574 | let old_fingerprint_json = paths::read(&loc.with_extension("json"))?; |
e9428cba | 1575 | let old_fingerprint: Fingerprint = serde_json::from_str(&old_fingerprint_json) |
23591fe5 | 1576 | .chain_err(|| internal("failed to deserialize json"))?; |
cd396f34 EH |
1577 | // Fingerprint can be empty after a failed rebuild (see comment in prepare_target). |
1578 | if !old_fingerprint_short.is_empty() { | |
1579 | debug_assert_eq!(util::to_hex(old_fingerprint.hash()), old_fingerprint_short); | |
1580 | } | |
71f80951 AC |
1581 | let result = new_fingerprint.compare(&old_fingerprint); |
1582 | assert!(result.is_err()); | |
ea40fc4f | 1583 | result |
c447e9d0 | 1584 | } |
d3c350b9 | 1585 | |
c85ed044 | 1586 | fn log_compare(unit: &Unit, compare: &CargoResult<()>) { |
e9428cba | 1587 | let ce = match compare { |
c447e9d0 | 1588 | Ok(..) => return, |
e9428cba | 1589 | Err(e) => e, |
c447e9d0 | 1590 | }; |
e9428cba AC |
1591 | info!( |
1592 | "fingerprint error for {}/{:?}/{:?}", | |
1593 | unit.pkg, unit.mode, unit.target, | |
1594 | ); | |
3a18c89a | 1595 | info!(" err: {:?}", ce); |
d3c350b9 AC |
1596 | } |
1597 | ||
f9fdc764 | 1598 | // Parse the dep-info into a list of paths |
34fd5cc8 MR |
1599 | pub fn parse_dep_info( |
1600 | pkg_root: &Path, | |
1601 | target_root: &Path, | |
1602 | dep_info: &Path, | |
1603 | ) -> CargoResult<Option<Vec<PathBuf>>> { | |
f688e9c2 AC |
1604 | let data = match paths::read_bytes(dep_info) { |
1605 | Ok(data) => data, | |
1606 | Err(_) => return Ok(None), | |
dd2c5980 | 1607 | }; |
dae87a26 E |
1608 | let paths = data |
1609 | .split(|&x| x == 0) | |
f688e9c2 | 1610 | .filter(|x| !x.is_empty()) |
34fd5cc8 MR |
1611 | .map(|p| { |
1612 | let ty = match DepInfoPathType::from_byte(p[0]) { | |
1613 | Some(ty) => ty, | |
1614 | None => return Err(internal("dep-info invalid")), | |
1615 | }; | |
1616 | let path = util::bytes2path(&p[1..])?; | |
1617 | match ty { | |
1618 | DepInfoPathType::PackageRootRelative => Ok(pkg_root.join(path)), | |
1619 | // N.B. path might be absolute here in which case the join will have no effect | |
1620 | DepInfoPathType::TargetRootRelative => Ok(target_root.join(path)), | |
1621 | } | |
1622 | }) | |
f688e9c2 | 1623 | .collect::<Result<Vec<_>, _>>()?; |
b1b9b79c | 1624 | Ok(Some(paths)) |
f9fdc764 | 1625 | } |
79768eb0 | 1626 | |
b8b7faee | 1627 | fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult<String> { |
7dcedd85 | 1628 | let source_id = pkg.package_id().source_id(); |
c32e395c | 1629 | let sources = bcx.packages.sources(); |
e95044e3 | 1630 | |
1e682848 AC |
1631 | let source = sources |
1632 | .get(source_id) | |
1633 | .ok_or_else(|| internal("missing package source"))?; | |
79768eb0 | 1634 | source.fingerprint(pkg) |
d3c350b9 | 1635 | } |
dd2c5980 | 1636 | |
b1b9b79c EH |
1637 | fn find_stale_file<I>( |
1638 | mtime_cache: &mut HashMap<PathBuf, FileTime>, | |
1639 | reference: &Path, | |
1640 | paths: I, | |
1641 | ) -> Option<StaleFile> | |
1e682848 AC |
1642 | where |
1643 | I: IntoIterator, | |
1644 | I::Item: AsRef<Path>, | |
7dcedd85 | 1645 | { |
8df842f5 | 1646 | let reference_mtime = match paths::mtime(reference) { |
8c5c9e39 | 1647 | Ok(mtime) => mtime, |
8df842f5 | 1648 | Err(..) => return Some(StaleFile::Missing(reference.to_path_buf())), |
7c97c5bf | 1649 | }; |
7c97c5bf | 1650 | |
8df842f5 | 1651 | for path in paths { |
7dcedd85 | 1652 | let path = path.as_ref(); |
b1b9b79c EH |
1653 | let path_mtime = match mtime_cache.entry(path.to_path_buf()) { |
1654 | Entry::Occupied(o) => *o.get(), | |
1655 | Entry::Vacant(v) => { | |
1656 | let mtime = match paths::mtime(path) { | |
1657 | Ok(mtime) => mtime, | |
1658 | Err(..) => return Some(StaleFile::Missing(path.to_path_buf())), | |
1659 | }; | |
1660 | *v.insert(mtime) | |
1661 | } | |
7c97c5bf | 1662 | }; |
b1fbafd0 | 1663 | |
f7c91ba6 | 1664 | // TODO: fix #5918. |
40a07791 E |
1665 | // Note that equal mtimes should be considered "stale". For filesystems with |
1666 | // not much timestamp precision like 1s this is would be a conservative approximation | |
b1fbafd0 | 1667 | // to handle the case where a file is modified within the same second after |
d2c71cac | 1668 | // a build starts. We want to make sure that incremental rebuilds pick that up! |
b1fbafd0 RJ |
1669 | // |
1670 | // For filesystems with nanosecond precision it's been seen in the wild that | |
1671 | // its "nanosecond precision" isn't really nanosecond-accurate. It turns out that | |
1672 | // kernels may cache the current time so files created at different times actually | |
1673 | // list the same nanosecond precision. Some digging on #5919 picked up that the | |
1674 | // kernel caches the current time between timer ticks, which could mean that if | |
d2c71cac | 1675 | // a file is updated at most 10ms after a build starts then Cargo may not |
b1fbafd0 RJ |
1676 | // pick up the build changes. |
1677 | // | |
40a07791 | 1678 | // All in all, an equality check here would be a conservative assumption that, |
b1fbafd0 | 1679 | // if equal, files were changed just after a previous build finished. |
40a07791 E |
1680 | // Unfortunately this became problematic when (in #6484) cargo switch to more accurately |
1681 | // measuring the start time of builds. | |
8df842f5 AC |
1682 | if path_mtime <= reference_mtime { |
1683 | continue; | |
7c97c5bf | 1684 | } |
7dcedd85 | 1685 | |
8df842f5 AC |
1686 | return Some(StaleFile::Changed { |
1687 | reference: reference.to_path_buf(), | |
1688 | reference_mtime, | |
1689 | stale: path.to_path_buf(), | |
1690 | stale_mtime: path_mtime, | |
1691 | }); | |
7c97c5bf | 1692 | } |
8df842f5 | 1693 | |
677b24ad EH |
1694 | debug!( |
1695 | "all paths up-to-date relative to {:?} mtime={}", | |
1696 | reference, reference_mtime | |
1697 | ); | |
ea40fc4f | 1698 | None |
7c97c5bf AC |
1699 | } |
1700 | ||
34fd5cc8 MR |
1701 | #[repr(u8)] |
1702 | enum DepInfoPathType { | |
1703 | // src/, e.g. src/lib.rs | |
1704 | PackageRootRelative = 1, | |
1705 | // target/debug/deps/lib... | |
1706 | // or an absolute path /.../sysroot/... | |
1707 | TargetRootRelative = 2, | |
1708 | } | |
1709 | ||
1710 | impl DepInfoPathType { | |
1711 | fn from_byte(b: u8) -> Option<DepInfoPathType> { | |
1712 | match b { | |
1713 | 1 => Some(DepInfoPathType::PackageRootRelative), | |
1714 | 2 => Some(DepInfoPathType::TargetRootRelative), | |
1715 | _ => None, | |
1716 | } | |
1717 | } | |
1718 | } | |
1719 | ||
f688e9c2 AC |
1720 | /// Parses the dep-info file coming out of rustc into a Cargo-specific format. |
1721 | /// | |
1722 | /// This function will parse `rustc_dep_info` as a makefile-style dep info to | |
1723 | /// learn about the all files which a crate depends on. This is then | |
1724 | /// re-serialized into the `cargo_dep_info` path in a Cargo-specific format. | |
1725 | /// | |
1726 | /// The `pkg_root` argument here is the absolute path to the directory | |
1727 | /// containing `Cargo.toml` for this crate that was compiled. The paths listed | |
1728 | /// in the rustc dep-info file may or may not be absolute but we'll want to | |
1729 | /// consider all of them relative to the `root` specified. | |
1730 | /// | |
1731 | /// The `rustc_cwd` argument is the absolute path to the cwd of the compiler | |
1732 | /// when it was invoked. | |
1733 | /// | |
ff532eca EH |
1734 | /// If the `allow_package` argument is true, then package-relative paths are |
1735 | /// included. If it is false, then package-relative paths are skipped and | |
1736 | /// ignored (typically used for registry or git dependencies where we assume | |
1737 | /// the source never changes, and we don't want the cost of running `stat` on | |
cc53ecad EH |
1738 | /// all those files). See the module-level docs for the note about |
1739 | /// `-Zbinary-dep-depinfo` for more details on why this is done. | |
b1b9b79c | 1740 | /// |
f688e9c2 | 1741 | /// The serialized Cargo format will contain a list of files, all of which are |
57a7e126 | 1742 | /// relative if they're under `root`. or absolute if they're elsewhere. |
1e682848 AC |
1743 | pub fn translate_dep_info( |
1744 | rustc_dep_info: &Path, | |
1745 | cargo_dep_info: &Path, | |
1e682848 | 1746 | rustc_cwd: &Path, |
34fd5cc8 MR |
1747 | pkg_root: &Path, |
1748 | target_root: &Path, | |
b1b9b79c | 1749 | allow_package: bool, |
1e682848 | 1750 | ) -> CargoResult<()> { |
f6056766 | 1751 | let target = parse_rustc_dep_info(rustc_dep_info)?; |
1e682848 AC |
1752 | let deps = &target |
1753 | .get(0) | |
1754 | .ok_or_else(|| internal("malformed dep-info format, no targets".to_string()))? | |
1755 | .1; | |
f688e9c2 | 1756 | |
4f6553ab EH |
1757 | let target_root = target_root.canonicalize()?; |
1758 | let pkg_root = pkg_root.canonicalize()?; | |
f688e9c2 | 1759 | let mut new_contents = Vec::new(); |
f6056766 | 1760 | for file in deps { |
4f6553ab EH |
1761 | // The path may be absolute or relative, canonical or not. Make sure |
1762 | // it is canonicalized so we are comparing the same kinds of paths. | |
4f6553ab | 1763 | let abs_file = rustc_cwd.join(file); |
ec9222a3 EH |
1764 | // If canonicalization fails, just use the abs path. There is currently |
1765 | // a bug where --remap-path-prefix is affecting .d files, causing them | |
1766 | // to point to non-existent paths. | |
1767 | let canon_file = abs_file.canonicalize().unwrap_or_else(|_| abs_file.clone()); | |
4f6553ab EH |
1768 | |
1769 | let (ty, path) = if let Ok(stripped) = canon_file.strip_prefix(&target_root) { | |
34fd5cc8 | 1770 | (DepInfoPathType::TargetRootRelative, stripped) |
4f6553ab | 1771 | } else if let Ok(stripped) = canon_file.strip_prefix(&pkg_root) { |
b1b9b79c EH |
1772 | if !allow_package { |
1773 | continue; | |
1774 | } | |
aa99e9f2 | 1775 | (DepInfoPathType::PackageRootRelative, stripped) |
34fd5cc8 MR |
1776 | } else { |
1777 | // It's definitely not target root relative, but this is an absolute path (since it was | |
1778 | // joined to rustc_cwd) and as such re-joining it later to the target root will have no | |
1779 | // effect. | |
4f6553ab | 1780 | (DepInfoPathType::TargetRootRelative, &*abs_file) |
34fd5cc8 MR |
1781 | }; |
1782 | new_contents.push(ty as u8); | |
9fa65608 | 1783 | new_contents.extend(util::path2bytes(path)?); |
f688e9c2 AC |
1784 | new_contents.push(0); |
1785 | } | |
1786 | paths::write(cargo_dep_info, &new_contents)?; | |
1787 | Ok(()) | |
1788 | } | |
1789 | ||
78c788f9 EH |
1790 | /// Parse the `.d` dep-info file generated by rustc. |
1791 | /// | |
1792 | /// Result is a Vec of `(target, prerequisites)` tuples where `target` is the | |
1793 | /// rule name, and `prerequisites` is a list of files that it depends on. | |
1e682848 | 1794 | pub fn parse_rustc_dep_info(rustc_dep_info: &Path) -> CargoResult<Vec<(String, Vec<String>)>> { |
f6056766 | 1795 | let contents = paths::read(rustc_dep_info)?; |
1e682848 AC |
1796 | contents |
1797 | .lines() | |
f6056766 AC |
1798 | .filter_map(|l| l.find(": ").map(|i| (l, i))) |
1799 | .map(|(line, pos)| { | |
1800 | let target = &line[..pos]; | |
1801 | let mut deps = line[pos + 2..].split_whitespace(); | |
1802 | ||
1803 | let mut ret = Vec::new(); | |
1804 | while let Some(s) = deps.next() { | |
1805 | let mut file = s.to_string(); | |
1806 | while file.ends_with('\\') { | |
1807 | file.pop(); | |
1808 | file.push(' '); | |
1809 | file.push_str(deps.next().ok_or_else(|| { | |
1810 | internal("malformed dep-info format, trailing \\".to_string()) | |
1811 | })?); | |
1812 | } | |
1813 | ret.push(file); | |
1814 | } | |
1815 | Ok((target.to_string(), ret)) | |
1816 | }) | |
1817 | .collect() | |
1818 | } |