cargo/core/compiler/layout.rs
1//! Management of the directory layout of a build
2//!
3//! The directory layout is a little tricky at times, hence a separate file to
4//! house this logic. Cargo stores build artifacts in two directories: `artifact-dir` and
5//! `build-dir`
6//!
7//! ## `artifact-dir` layout
8//!
9//! `artifact-dir` is where final artifacts like binaries are stored.
10//! The `artifact-dir` layout is consider part of the public API and
11//! cannot be easily changed.
12//!
13//! ```text
14//! <artifact-dir>/
15//!
16//! # Compilation files are grouped by build target and profile.
17//! # The target is omitted if not explicitly specified.
18//! [<target>]/<profile>/ # e.g. `debug` / `release`
19//!
20//! # File used to lock the directory to prevent multiple cargo processes
21//! # from using it at the same time.
22//! .cargo-lock
23//!
24//! # Root directory for all compiled examples.
25//! examples/
26//!
27//! # Output from rustdoc
28//! doc/
29//!
30//! # Output from `cargo package` to build a `.crate` file.
31//! package/
32//! ```
33//!
34//! ## `build-dir` layout
35//!
36//! `build-dir` is where intermediate build artifacts are stored.
37//! The `build-dir` layout is considered an internal implementation detail of Cargo
38//! meaning that we can change this if needed. However, in reality many tools rely on
39//! implementation details of Cargo so breaking changes need to be done carefully.
40//!
41//! ```text
42//! <build-dir>/
43//!
44//! # Cache of `rustc -Vv` output for performance.
45//! .rustc-info.json
46//!
47//! # Compilation files are grouped by build target and profile.
48//! # The target is omitted if not explicitly specified.
49//! [<target>]/<profile>/ # e.g. `debug` / `release`
50//!
51//! # File used to lock the directory to prevent multiple cargo processes
52//! # from using it at the same time.
53//! .cargo-lock
54//!
55//! # Hidden directory that holds all of the fingerprint files for all
56//! # packages
57//! .fingerprint/
58//! # Each package is in a separate directory.
59//! # Note that different target kinds have different filename prefixes.
60//! $pkgname-$META/
61//! # Set of source filenames for this package.
62//! dep-lib-$targetname
63//! # Timestamp when this package was last built.
64//! invoked.timestamp
65//! # The fingerprint hash.
66//! lib-$targetname
67//! # Detailed information used for logging the reason why
68//! # something is being recompiled.
69//! lib-$targetname.json
70//! # The console output from the compiler. This is cached
71//! # so that warnings can be redisplayed for "fresh" units.
72//! output-lib-$targetname
73//!
74//! # This is the root directory for all rustc artifacts except build
75//! # scripts, examples, and test and bench executables. Almost every
76//! # artifact should have a metadata hash added to its filename to
77//! # prevent collisions. One notable exception is dynamic libraries.
78//! deps/
79//!
80//! # Each artifact dependency gets in its own directory.
81//! /artifact/$pkgname-$META/$kind
82//!
83//! # Root directory for all compiled examples.
84//! examples/
85//!
86//! # Directory used to store incremental data for the compiler (when
87//! # incremental is enabled.
88//! incremental/
89//!
90//! # This is the location at which the output of all custom build
91//! # commands are rooted.
92//! build/
93//!
94//! # Each package gets its own directory where its build script and
95//! # script output are placed
96//! $pkgname-$META/ # For the build script itself.
97//! # The build script executable (name may be changed by user).
98//! build-script-build-$META
99//! # Hard link to build-script-build-$META.
100//! build-script-build
101//! # Dependency information generated by rustc.
102//! build-script-build-$META.d
103//! # Debug information, depending on platform and profile
104//! # settings.
105//! <debug symbols>
106//!
107//! # The package shows up twice with two different metadata hashes.
108//! $pkgname-$META/ # For the output of the build script.
109//! # Timestamp when the build script was last executed.
110//! invoked.timestamp
111//! # Directory where script can output files ($OUT_DIR).
112//! out/
113//! # Output from the build script.
114//! output
115//! # Path to `out`, used to help when the target directory is
116//! # moved.
117//! root-output
118//! # Stderr output from the build script.
119//! stderr
120//!
121//! # Used by `cargo package` and `cargo publish` to build a `.crate` file.
122//! package/
123//!
124//! # Experimental feature for generated build scripts.
125//! .metabuild/
126//! ```
127//!
128//! ### New `build-dir` layout
129//!
130//! `build-dir` supports a new "build unit" based layout that is unstable.
131//! It can be enabled via `-Zbuild-dir-new-layout`.
132//! For more info about the layout transition see: [#15010](https://github.com/rust-lang/cargo/issues/15010)
133//!
134//! ```text
135//! <build-dir>/
136//!
137//! # Cache of `rustc -Vv` output for performance.
138//! .rustc-info.json
139//!
140//! # Compilation files are grouped by build target and profile.
141//! # The target is omitted if not explicitly specified.
142//! [<target>]/<profile>/ # e.g. `debug` / `release`
143//!
144//! # File used to lock the directory to prevent multiple cargo processes
145//! # from using it at the same time.
146//! .cargo-lock
147//!
148//! # Directory used to store incremental data for the compiler (when
149//! # incremental is enabled.
150//! incremental/
151//!
152//! # Main directory for storing build unit related files.
153//! # Files are organized by Cargo build unit (`$pkgname/$META`) so that
154//! # related files are stored in a single directory.
155//! build/
156//!
157//! # This is the location at which the output of all files related to
158//! # a given build unit. These files are organized together so that we can
159//! # treat this directly like a single unit for locking and caching.
160//! $pkgname/
161//! $META/
162//! # The general purpose output directory for build units.
163//! # For compilation units, the rustc artifact will be located here.
164//! # For build script run units, this is the $OUT_DIR
165//! out/
166//!
167//! # For artifact dependency units, the output is nested by the kind
168//! artifact/$kind
169//!
170//! # Directory that holds all of the fingerprint files for the build unit.
171//! fingerprint/
172//! # Set of source filenames for this package.
173//! dep-lib-$targetname
174//! # Timestamp when this package was last built.
175//! invoked.timestamp
176//! # The fingerprint hash.
177//! lib-$targetname
178//! # Detailed information used for logging the reason why
179//! # something is being recompiled.
180//! lib-$targetname.json
181//! # The console output from the compiler. This is cached
182//! # so that warnings can be redisplayed for "fresh" units.
183//! output-lib-$targetname
184//!
185//! # Directory for "execution" units that spawn a process (excluding compilation with
186//! # rustc). Contains the process execution details.
187//! # Currently the only execution unit Cargo supports is running build script
188//! # binaries.
189//! run/
190//! # Timestamp of last execution.
191//! invoked.timestamp
192//! # Stdout output from the process.
193//! stdout
194//! # Stderr output from the process.
195//! stderr
196//! # Path to `out`, used to help when the target directory is
197//! # moved. (build scripts)
198//! root-output
199//!
200//! # Used by `cargo package` and `cargo publish` to build a `.crate` file.
201//! package/
202//!
203//! # Experimental feature for generated build scripts.
204//! .metabuild/
205//! ```
206//!
207//! When cross-compiling, the layout is the same, except it appears in
208//! `target/$TRIPLE`.
209
210use crate::core::Workspace;
211use crate::core::compiler::CompileTarget;
212use crate::util::flock::is_on_nfs_mount;
213use crate::util::{CargoResult, FileLock};
214use cargo_util::paths;
215use std::path::{Path, PathBuf};
216
217/// Contains the paths of all target output locations.
218///
219/// See module docs for more information.
220pub struct Layout {
221 artifact_dir: Option<ArtifactDirLayout>,
222 build_dir: BuildDirLayout,
223}
224
225impl Layout {
226 /// Calculate the paths for build output, lock the build directory, and return as a Layout.
227 ///
228 /// This function will block if the directory is already locked.
229 ///
230 /// `dest` should be the final artifact directory name. Currently either
231 /// "debug" or "release".
232 pub fn new(
233 ws: &Workspace<'_>,
234 target: Option<CompileTarget>,
235 dest: &str,
236 must_take_artifact_dir_lock: bool,
237 must_take_build_dir_lock_exclusively: bool,
238 ) -> CargoResult<Layout> {
239 let is_new_layout = ws.gctx().cli_unstable().build_dir_new_layout;
240 let mut root = ws.target_dir();
241 let mut build_root = ws.build_dir();
242 if let Some(target) = target {
243 root.push(target.short_name());
244 build_root.push(target.short_name());
245 }
246 let build_dest = build_root.join(dest);
247 let dest = root.join(dest);
248 // If the root directory doesn't already exist go ahead and create it
249 // here. Use this opportunity to exclude it from backups as well if the
250 // system supports it since this is a freshly created folder.
251 //
252 paths::create_dir_all_excluded_from_backups_atomic(root.as_path_unlocked())?;
253 if root != build_root {
254 paths::create_dir_all_excluded_from_backups_atomic(build_root.as_path_unlocked())?;
255 }
256
257 // Now that the excluded from backups target root is created we can create the
258 // actual destination (sub)subdirectory.
259 paths::create_dir_all(dest.as_path_unlocked())?;
260
261 let build_dir_lock = if is_on_nfs_mount(build_root.as_path_unlocked()) {
262 None
263 } else {
264 if ws.gctx().cli_unstable().fine_grain_locking && !must_take_build_dir_lock_exclusively
265 {
266 Some(build_dest.open_ro_shared_create(
267 ".cargo-build-lock",
268 ws.gctx(),
269 "build directory",
270 )?)
271 } else {
272 Some(build_dest.open_rw_exclusive_create(
273 ".cargo-build-lock",
274 ws.gctx(),
275 "build directory",
276 )?)
277 }
278 };
279 let build_root = build_root.into_path_unlocked();
280 let build_dest = build_dest.as_path_unlocked();
281 let deps = build_dest.join("deps");
282 let artifact = deps.join("artifact");
283
284 let artifact_dir = if must_take_artifact_dir_lock {
285 // For now we don't do any more finer-grained locking on the artifact
286 // directory, so just lock the entire thing for the duration of this
287 // compile.
288 let artifact_dir_lock = if is_on_nfs_mount(root.as_path_unlocked()) {
289 None
290 } else {
291 Some(dest.open_rw_exclusive_create(
292 ".cargo-lock",
293 ws.gctx(),
294 "artifact directory",
295 )?)
296 };
297 let root = root.into_path_unlocked();
298 let dest = dest.into_path_unlocked();
299 Some(ArtifactDirLayout {
300 dest: dest.clone(),
301 examples: dest.join("examples"),
302 doc: root.join("doc"),
303 timings: root.join("cargo-timings"),
304 _lock: artifact_dir_lock,
305 })
306 } else {
307 None
308 };
309 Ok(Layout {
310 artifact_dir,
311 build_dir: BuildDirLayout {
312 root: build_root.clone(),
313 deps,
314 build: build_dest.join("build"),
315 artifact,
316 incremental: build_dest.join("incremental"),
317 fingerprint: build_dest.join(".fingerprint"),
318 examples: build_dest.join("examples"),
319 tmp: build_root.join("tmp"),
320 _lock: build_dir_lock,
321 is_new_layout,
322 },
323 })
324 }
325
326 /// Makes sure all directories stored in the Layout exist on the filesystem.
327 pub fn prepare(&mut self) -> CargoResult<()> {
328 if let Some(ref mut artifact_dir) = self.artifact_dir {
329 artifact_dir.prepare()?;
330 }
331 self.build_dir.prepare()?;
332
333 Ok(())
334 }
335
336 pub fn artifact_dir(&self) -> Option<&ArtifactDirLayout> {
337 self.artifact_dir.as_ref()
338 }
339
340 pub fn build_dir(&self) -> &BuildDirLayout {
341 &self.build_dir
342 }
343}
344
345pub struct ArtifactDirLayout {
346 /// The final artifact destination: `<artifact-dir>/debug` (or `release`).
347 dest: PathBuf,
348 /// The directory for examples
349 examples: PathBuf,
350 /// The directory for rustdoc output
351 doc: PathBuf,
352 /// The directory for --timings output
353 timings: PathBuf,
354 /// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
355 /// struct is `drop`ped.
356 _lock: Option<FileLock>,
357}
358
359impl ArtifactDirLayout {
360 /// Makes sure all directories stored in the Layout exist on the filesystem.
361 pub fn prepare(&mut self) -> CargoResult<()> {
362 paths::create_dir_all(&self.examples)?;
363
364 Ok(())
365 }
366 /// Fetch the destination path for final artifacts (`/…/target/debug`).
367 pub fn dest(&self) -> &Path {
368 &self.dest
369 }
370 /// Fetch the examples path.
371 pub fn examples(&self) -> &Path {
372 &self.examples
373 }
374 /// Fetch the doc path.
375 pub fn doc(&self) -> &Path {
376 &self.doc
377 }
378 /// Fetch the cargo-timings path.
379 pub fn timings(&self) -> &Path {
380 &self.timings
381 }
382}
383
384pub struct BuildDirLayout {
385 /// The root directory: `/path/to/build-dir`.
386 /// If cross compiling: `/path/to/build-dir/$TRIPLE`.
387 root: PathBuf,
388 /// The directory with rustc artifacts
389 deps: PathBuf,
390 /// The primary directory for build files
391 build: PathBuf,
392 /// The directory for artifacts, i.e. binaries, cdylibs, staticlibs
393 artifact: PathBuf,
394 /// The directory for incremental files
395 incremental: PathBuf,
396 /// The directory for fingerprints
397 fingerprint: PathBuf,
398 /// The directory for pre-uplifted examples: `build-dir/debug/examples`
399 examples: PathBuf,
400 /// The directory for temporary data of integration tests and benches
401 tmp: PathBuf,
402 /// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
403 /// struct is `drop`ped.
404 ///
405 /// Will be `None` when the build-dir and target-dir are the same path as we cannot
406 /// lock the same path twice.
407 _lock: Option<FileLock>,
408 is_new_layout: bool,
409}
410
411impl BuildDirLayout {
412 /// Makes sure all directories stored in the Layout exist on the filesystem.
413 pub fn prepare(&mut self) -> CargoResult<()> {
414 if !self.is_new_layout {
415 paths::create_dir_all(&self.deps)?;
416 paths::create_dir_all(&self.fingerprint)?;
417 paths::create_dir_all(&self.examples)?;
418 }
419 paths::create_dir_all(&self.incremental)?;
420 paths::create_dir_all(&self.build)?;
421
422 Ok(())
423 }
424 /// Fetch the deps path.
425 pub fn deps(&self, pkg_dir: &str) -> PathBuf {
426 if self.is_new_layout {
427 self.out_force_new_layout(pkg_dir)
428 } else {
429 self.legacy_deps().to_path_buf()
430 }
431 }
432 /// Fetch the output path for build units. (new layout only)
433 ///
434 /// New features should consider using this so we can avoid their migrations.
435 pub fn out_force_new_layout(&self, pkg_dir: &str) -> PathBuf {
436 self.build_unit(pkg_dir).join("out")
437 }
438 /// Fetch the deps path. (old layout)
439 pub fn legacy_deps(&self) -> &Path {
440 &self.deps
441 }
442 pub fn root(&self) -> &Path {
443 &self.root
444 }
445 /// Fetch the build examples path.
446 pub fn examples(&self) -> &Path {
447 &self.examples
448 }
449 /// Fetch the incremental path.
450 pub fn incremental(&self) -> &Path {
451 &self.incremental
452 }
453 /// Fetch the fingerprint path.
454 pub fn fingerprint(&self, pkg_dir: &str) -> PathBuf {
455 if self.is_new_layout {
456 self.build_unit(pkg_dir).join("fingerprint")
457 } else {
458 self.legacy_fingerprint().to_path_buf().join(pkg_dir)
459 }
460 }
461 /// Fetch the fingerprint path. (old layout)
462 pub fn legacy_fingerprint(&self) -> &Path {
463 &self.fingerprint
464 }
465 /// Fetch the build path.
466 pub fn build(&self) -> &Path {
467 &self.build
468 }
469 /// Fetch the build script path.
470 pub fn build_script(&self, pkg_dir: &str) -> PathBuf {
471 if self.is_new_layout {
472 self.deps(pkg_dir)
473 } else {
474 self.build().join(pkg_dir)
475 }
476 }
477 /// Fetch the build script execution path.
478 pub fn build_script_execution(&self, pkg_dir: &str) -> PathBuf {
479 if self.is_new_layout {
480 self.build_unit(pkg_dir).join("run")
481 } else {
482 self.build().join(pkg_dir)
483 }
484 }
485 /// Fetch the artifact path.
486 pub fn artifact(&self, pkg_dir: &str, kind: &str) -> PathBuf {
487 if self.is_new_layout {
488 self.build_unit(pkg_dir).join("artifact").join(kind)
489 } else {
490 self.artifact.join(pkg_dir).join(kind)
491 }
492 }
493 /// Fetch the build unit path
494 pub fn build_unit(&self, pkg_dir: &str) -> PathBuf {
495 self.build().join(pkg_dir)
496 }
497 /// Create and return the tmp path.
498 pub fn prepare_tmp(&self) -> CargoResult<&Path> {
499 paths::create_dir_all(&self.tmp)?;
500 Ok(&self.tmp)
501 }
502}