blob: 57b65907ca2c4bf8f6e0298d127cf660c28a5e35 [file] [log] [blame]
//! Management of the directory layout of a build
//!
//! The directory layout is a little tricky at times, hence a separate file to
//! house this logic. The current layout looks like this:
//!
//! ```text
//! # This is the root directory for all output, the top-level package
//! # places all of its output here.
//! target/
//!
//! # Cache of `rustc -Vv` output for performance.
//! .rustc-info.json
//!
//! # All final artifacts are linked into this directory from `deps`.
//! # Note that named profiles will soon be included as separate directories
//! # here. They have a restricted format, similar to Rust identifiers, so
//! # Cargo-specific directories added in the future should use some prefix
//! # like `.` to avoid name collisions.
//! debug/ # or release/
//!
//! # File used to lock the directory to prevent multiple cargo processes
//! # from using it at the same time.
//! .cargo-lock
//!
//! # Hidden directory that holds all of the fingerprint files for all
//! # packages
//! .fingerprint/
//! # Each package is in a separate directory.
//! # Note that different target kinds have different filename prefixes.
//! $pkgname-$META/
//! # Set of source filenames for this package.
//! dep-lib-$targetname
//! # Timestamp when this package was last built.
//! invoked.timestamp
//! # The fingerprint hash.
//! lib-$targetname
//! # Detailed information used for logging the reason why
//! # something is being recompiled.
//! lib-$targetname.json
//! # The console output from the compiler. This is cached
//! # so that warnings can be redisplayed for "fresh" units.
//! output-lib-$targetname
//!
//! # This is the root directory for all rustc artifacts except build
//! # scripts, examples, and test and bench executables. Almost every
//! # artifact should have a metadata hash added to its filename to
//! # prevent collisions. One notable exception is dynamic libraries.
//! deps/
//!
//! # Each artifact dependency gets in its own directory.
//! /artifact/$pkgname-$META/$kind
//!
//! # Root directory for all compiled examples.
//! examples/
//!
//! # Directory used to store incremental data for the compiler (when
//! # incremental is enabled.
//! incremental/
//!
//! # This is the location at which the output of all custom build
//! # commands are rooted.
//! build/
//!
//! # Each package gets its own directory where its build script and
//! # script output are placed
//! $pkgname-$META/ # For the build script itself.
//! # The build script executable (name may be changed by user).
//! build-script-build-$META
//! # Hard link to build-script-build-$META.
//! build-script-build
//! # Dependency information generated by rustc.
//! build-script-build-$META.d
//! # Debug information, depending on platform and profile
//! # settings.
//! <debug symbols>
//!
//! # The package shows up twice with two different metadata hashes.
//! $pkgname-$META/ # For the output of the build script.
//! # Timestamp when the build script was last executed.
//! invoked.timestamp
//! # Directory where script can output files ($OUT_DIR).
//! out/
//! # Output from the build script.
//! output
//! # Path to `out`, used to help when the target directory is
//! # moved.
//! root-output
//! # Stderr output from the build script.
//! stderr
//!
//! # Output from rustdoc
//! doc/
//!
//! # Used by `cargo package` and `cargo publish` to build a `.crate` file.
//! package/
//!
//! # Experimental feature for generated build scripts.
//! .metabuild/
//! ```
//!
//! When cross-compiling, the layout is the same, except it appears in
//! `target/$TRIPLE`.
use crate::core::compiler::CompileTarget;
use crate::core::Workspace;
use crate::util::{CargoResult, FileLock};
use cargo_util::paths;
use std::path::{Path, PathBuf};
/// Contains the paths of all target output locations.
///
/// See module docs for more information.
pub struct Layout {
/// The root directory: `/path/to/target`.
/// If cross compiling: `/path/to/target/$TRIPLE`.
root: PathBuf,
/// The final artifact destination: `$root/debug` (or `release`).
dest: PathBuf,
/// The directory with rustc artifacts: `$dest/deps`
deps: PathBuf,
/// The directory for build scripts: `$dest/build`
build: PathBuf,
/// The directory for artifacts, i.e. binaries, cdylibs, staticlibs: `$dest/deps/artifact`
artifact: PathBuf,
/// The directory for incremental files: `$dest/incremental`
incremental: PathBuf,
/// The directory for fingerprints: `$dest/.fingerprint`
fingerprint: PathBuf,
/// The directory for examples: `$dest/examples`
examples: PathBuf,
/// The directory for rustdoc output: `$root/doc`
doc: PathBuf,
/// The directory for temporary data of integration tests and benches: `$dest/tmp`
tmp: PathBuf,
/// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
/// struct is `drop`ped.
_lock: FileLock,
}
impl Layout {
/// Calculate the paths for build output, lock the build directory, and return as a Layout.
///
/// This function will block if the directory is already locked.
///
/// `dest` should be the final artifact directory name. Currently either
/// "debug" or "release".
pub fn new(
ws: &Workspace<'_>,
target: Option<CompileTarget>,
dest: &str,
) -> CargoResult<Layout> {
let mut root = ws.target_dir();
if let Some(target) = target {
root.push(target.short_name());
}
let dest = root.join(dest);
// If the root directory doesn't already exist go ahead and create it
// here. Use this opportunity to exclude it from backups as well if the
// system supports it since this is a freshly created folder.
//
paths::create_dir_all_excluded_from_backups_atomic(root.as_path_unlocked())?;
// Now that the excluded from backups target root is created we can create the
// actual destination (sub)subdirectory.
paths::create_dir_all(dest.as_path_unlocked())?;
// For now we don't do any more finer-grained locking on the artifact
// directory, so just lock the entire thing for the duration of this
// compile.
let lock = dest.open_rw_exclusive_create(".cargo-lock", ws.config(), "build directory")?;
let root = root.into_path_unlocked();
let dest = dest.into_path_unlocked();
let deps = dest.join("deps");
let artifact = deps.join("artifact");
Ok(Layout {
deps,
build: dest.join("build"),
artifact,
incremental: dest.join("incremental"),
fingerprint: dest.join(".fingerprint"),
examples: dest.join("examples"),
doc: root.join("doc"),
tmp: root.join("tmp"),
root,
dest,
_lock: lock,
})
}
/// Makes sure all directories stored in the Layout exist on the filesystem.
pub fn prepare(&mut self) -> CargoResult<()> {
paths::create_dir_all(&self.deps)?;
paths::create_dir_all(&self.incremental)?;
paths::create_dir_all(&self.fingerprint)?;
paths::create_dir_all(&self.examples)?;
paths::create_dir_all(&self.build)?;
Ok(())
}
/// Fetch the destination path for final artifacts (`/…/target/debug`).
pub fn dest(&self) -> &Path {
&self.dest
}
/// Fetch the deps path.
pub fn deps(&self) -> &Path {
&self.deps
}
/// Fetch the examples path.
pub fn examples(&self) -> &Path {
&self.examples
}
/// Fetch the doc path.
pub fn doc(&self) -> &Path {
&self.doc
}
/// Fetch the root path (`/…/target`).
pub fn root(&self) -> &Path {
&self.root
}
/// Fetch the incremental path.
pub fn incremental(&self) -> &Path {
&self.incremental
}
/// Fetch the fingerprint path.
pub fn fingerprint(&self) -> &Path {
&self.fingerprint
}
/// Fetch the build script path.
pub fn build(&self) -> &Path {
&self.build
}
/// Fetch the artifact path.
pub fn artifact(&self) -> &Path {
&self.artifact
}
/// Create and return the tmp path.
pub fn prepare_tmp(&self) -> CargoResult<&Path> {
paths::create_dir_all(&self.tmp)?;
Ok(&self.tmp)
}
}