#
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. The current layout looks like this:
    5//!
    6//! ```text
    7//! # This is the root directory for all output, the top-level package
    8//! # places all of its output here.
    9//! target/
    10//!
    11//!     # Cache of `rustc -Vv` output for performance.
    12//!     .rustc-info.json
    13//!
    14//!     # All final artifacts are linked into this directory from `deps`.
    15//!     # Note that named profiles will soon be included as separate directories
    16//!     # here. They have a restricted format, similar to Rust identifiers, so
    17//!     # Cargo-specific directories added in the future should use some prefix
    18//!     # like `.` to avoid name collisions.
    19//!     debug/  # or release/
    20//!
    21//!         # File used to lock the directory to prevent multiple cargo processes
    22//!         # from using it at the same time.
    23//!         .cargo-lock
    24//!
    25//!         # Hidden directory that holds all of the fingerprint files for all
    26//!         # packages
    27//!         .fingerprint/
    28//!             # Each package is in a separate directory.
    29//!             # Note that different target kinds have different filename prefixes.
    30//!             $pkgname-$META/
    31//!                 # Set of source filenames for this package.
    32//!                 dep-lib-$targetname
    33//!                 # Timestamp when this package was last built.
    34//!                 invoked.timestamp
    35//!                 # The fingerprint hash.
    36//!                 lib-$targetname
    37//!                 # Detailed information used for logging the reason why
    38//!                 # something is being recompiled.
    39//!                 lib-$targetname.json
    40//!                 # The console output from the compiler. This is cached
    41//!                 # so that warnings can be redisplayed for "fresh" units.
    42//!                 output-lib-$targetname
    43//!
    44//!         # This is the root directory for all rustc artifacts except build
    45//!         # scripts, examples, and test and bench executables. Almost every
    46//!         # artifact should have a metadata hash added to its filename to
    47//!         # prevent collisions. One notable exception is dynamic libraries.
    48//!         deps/
    49//!
    50//!             # Each artifact dependency gets in its own directory.
    51//!             /artifact/$pkgname-$META/$kind
    52//!
    53//!         # Root directory for all compiled examples.
    54//!         examples/
    55//!
    56//!         # Directory used to store incremental data for the compiler (when
    57//!         # incremental is enabled.
    58//!         incremental/
    59//!
    60//!         # This is the location at which the output of all custom build
    61//!         # commands are rooted.
    62//!         build/
    63//!
    64//!             # Each package gets its own directory where its build script and
    65//!             # script output are placed
    66//!             $pkgname-$META/    # For the build script itself.
    67//!                 # The build script executable (name may be changed by user).
    68//!                 build-script-build-$META
    69//!                 # Hard link to build-script-build-$META.
    70//!                 build-script-build
    71//!                 # Dependency information generated by rustc.
    72//!                 build-script-build-$META.d
    73//!                 # Debug information, depending on platform and profile
    74//!                 # settings.
    75//!                 <debug symbols>
    76//!
    77//!             # The package shows up twice with two different metadata hashes.
    78//!             $pkgname-$META/  # For the output of the build script.
    79//!                 # Timestamp when the build script was last executed.
    80//!                 invoked.timestamp
    81//!                 # Directory where script can output files ($OUT_DIR).
    82//!                 out/
    83//!                 # Output from the build script.
    84//!                 output
    85//!                 # Path to `out`, used to help when the target directory is
    86//!                 # moved.
    87//!                 root-output
    88//!                 # Stderr output from the build script.
    89//!                 stderr
    90//!
    91//!     # Output from rustdoc
    92//!     doc/
    93//!
    94//!     # Used by `cargo package` and `cargo publish` to build a `.crate` file.
    95//!     package/
    96//!
    97//!     # Experimental feature for generated build scripts.
    98//!     .metabuild/
    99//! ```
    100//!
    101//! When cross-compiling, the layout is the same, except it appears in
    102//! `target/$TRIPLE`.
    103
    104use crate::core::Workspace;
    105use crate::core::compiler::CompileTarget;
    106use crate::util::{CargoResult, FileLock};
    107use cargo_util::paths;
    108use std::path::{Path, PathBuf};
    109
    110/// Contains the paths of all target output locations.
    111///
    112/// See module docs for more information.
    113pub struct Layout {
    114    artifact_dir: ArtifactDirLayout,
    115    build_dir: BuildDirLayout,
    116}
    117
    118impl Layout {
    119    /// Calculate the paths for build output, lock the build directory, and return as a Layout.
    120    ///
    121    /// This function will block if the directory is already locked.
    122    ///
    123    /// `dest` should be the final artifact directory name. Currently either
    124    /// "debug" or "release".
    125    pub fn new(
    126        ws: &Workspace<'_>,
    127        target: Option<CompileTarget>,
    128        dest: &str,
    129    ) -> CargoResult<Layout> {
    130        let is_new_layout = ws.gctx().cli_unstable().build_dir_new_layout;
    131        let mut root = ws.target_dir();
    132        let mut build_root = ws.build_dir();
    133        if let Some(target) = target {
    134            root.push(target.short_name());
    135            build_root.push(target.short_name());
    136        }
    137        let build_dest = build_root.join(dest);
    138        let dest = root.join(dest);
    139        // If the root directory doesn't already exist go ahead and create it
    140        // here. Use this opportunity to exclude it from backups as well if the
    141        // system supports it since this is a freshly created folder.
    142        //
    143        paths::create_dir_all_excluded_from_backups_atomic(root.as_path_unlocked())?;
    144        if root != build_root {
    145            paths::create_dir_all_excluded_from_backups_atomic(build_root.as_path_unlocked())?;
    146        }
    147
    148        // Now that the excluded from backups target root is created we can create the
    149        // actual destination (sub)subdirectory.
    150        paths::create_dir_all(dest.as_path_unlocked())?;
    151
    152        // For now we don't do any more finer-grained locking on the artifact
    153        // directory, so just lock the entire thing for the duration of this
    154        // compile.
    155        let artifact_dir_lock =
    156            dest.open_rw_exclusive_create(".cargo-lock", ws.gctx(), "build directory")?;
    157
    158        let build_dir_lock = if root != build_root {
    159            Some(build_dest.open_rw_exclusive_create(
    160                ".cargo-lock",
    161                ws.gctx(),
    162                "build directory",
    163            )?)
    164        } else {
    165            None
    166        };
    167        let root = root.into_path_unlocked();
    168        let build_root = build_root.into_path_unlocked();
    169        let dest = dest.into_path_unlocked();
    170        let build_dest = build_dest.as_path_unlocked();
    171        let deps = build_dest.join("deps");
    172        let artifact = deps.join("artifact");
    173
    174        Ok(Layout {
    175            artifact_dir: ArtifactDirLayout {
    176                dest: dest.clone(),
    177                examples: dest.join("examples"),
    178                doc: root.join("doc"),
    179                timings: root.join("cargo-timings"),
    180                _lock: artifact_dir_lock,
    181            },
    182            build_dir: BuildDirLayout {
    183                root: build_root.clone(),
    184                deps,
    185                build: build_dest.join("build"),
    186                artifact,
    187                incremental: build_dest.join("incremental"),
    188                fingerprint: build_dest.join(".fingerprint"),
    189                examples: build_dest.join("examples"),
    190                tmp: build_root.join("tmp"),
    191                _lock: build_dir_lock,
    192                is_new_layout,
    193            },
    194        })
    195    }
    196
    197    /// Makes sure all directories stored in the Layout exist on the filesystem.
    198    pub fn prepare(&mut self) -> CargoResult<()> {
    199        self.artifact_dir.prepare()?;
    200        self.build_dir.prepare()?;
    201
    202        Ok(())
    203    }
    204
    205    pub fn artifact_dir(&self) -> &ArtifactDirLayout {
    206        &self.artifact_dir
    207    }
    208
    209    pub fn build_dir(&self) -> &BuildDirLayout {
    210        &self.build_dir
    211    }
    212}
    213
    214pub struct ArtifactDirLayout {
    215    /// The final artifact destination: `<artifact-dir>/debug` (or `release`).
    216    dest: PathBuf,
    217    /// The directory for examples
    218    examples: PathBuf,
    219    /// The directory for rustdoc output
    220    doc: PathBuf,
    221    /// The directory for --timings output
    222    timings: PathBuf,
    223    /// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
    224    /// struct is `drop`ped.
    225    _lock: FileLock,
    226}
    227
    228impl ArtifactDirLayout {
    229    /// Makes sure all directories stored in the Layout exist on the filesystem.
    230    pub fn prepare(&mut self) -> CargoResult<()> {
    231        paths::create_dir_all(&self.examples)?;
    232
    233        Ok(())
    234    }
    235    /// Fetch the destination path for final artifacts  (`/…/target/debug`).
    236    pub fn dest(&self) -> &Path {
    237        &self.dest
    238    }
    239    /// Fetch the examples path.
    240    pub fn examples(&self) -> &Path {
    241        &self.examples
    242    }
    243    /// Fetch the doc path.
    244    pub fn doc(&self) -> &Path {
    245        &self.doc
    246    }
    247    /// Fetch the cargo-timings path.
    248    pub fn timings(&self) -> &Path {
    249        &self.timings
    250    }
    251}
    252
    253pub struct BuildDirLayout {
    254    /// The root directory: `/path/to/build-dir`.
    255    /// If cross compiling: `/path/to/build-dir/$TRIPLE`.
    256    root: PathBuf,
    257    /// The directory with rustc artifacts
    258    deps: PathBuf,
    259    /// The primary directory for build files
    260    build: PathBuf,
    261    /// The directory for artifacts, i.e. binaries, cdylibs, staticlibs
    262    artifact: PathBuf,
    263    /// The directory for incremental files
    264    incremental: PathBuf,
    265    /// The directory for fingerprints
    266    fingerprint: PathBuf,
    267    /// The directory for pre-uplifted examples: `build-dir/debug/examples`
    268    examples: PathBuf,
    269    /// The directory for temporary data of integration tests and benches
    270    tmp: PathBuf,
    271    /// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
    272    /// struct is `drop`ped.
    273    ///
    274    /// Will be `None` when the build-dir and target-dir are the same path as we cannot
    275    /// lock the same path twice.
    276    _lock: Option<FileLock>,
    277    is_new_layout: bool,
    278}
    279
    280impl BuildDirLayout {
    281    /// Makes sure all directories stored in the Layout exist on the filesystem.
    282    pub fn prepare(&mut self) -> CargoResult<()> {
    283        if !self.is_new_layout {
    284            paths::create_dir_all(&self.deps)?;
    285            paths::create_dir_all(&self.fingerprint)?;
    286        }
    287        paths::create_dir_all(&self.incremental)?;
    288        paths::create_dir_all(&self.examples)?;
    289        paths::create_dir_all(&self.build)?;
    290
    291        Ok(())
    292    }
    293    /// Fetch the deps path.
    294    pub fn deps(&self, pkg_dir: &str) -> PathBuf {
    295        if self.is_new_layout {
    296            self.build_unit(pkg_dir).join("deps")
    297        } else {
    298            self.legacy_deps().to_path_buf()
    299        }
    300    }
    301    /// Fetch the deps path. (old layout)
    302    pub fn legacy_deps(&self) -> &Path {
    303        &self.deps
    304    }
    305    pub fn root(&self) -> &Path {
    306        &self.root
    307    }
    308    /// Fetch the build examples path.
    309    pub fn examples(&self) -> &Path {
    310        &self.examples
    311    }
    312    /// Fetch the incremental path.
    313    pub fn incremental(&self) -> &Path {
    314        &self.incremental
    315    }
    316    /// Fetch the fingerprint path.
    317    pub fn fingerprint(&self, pkg_dir: &str) -> PathBuf {
    318        if self.is_new_layout {
    319            self.build_unit(pkg_dir).join("fingerprint")
    320        } else {
    321            self.legacy_fingerprint().to_path_buf().join(pkg_dir)
    322        }
    323    }
    324    /// Fetch the fingerprint path. (old layout)
    325    pub fn legacy_fingerprint(&self) -> &Path {
    326        &self.fingerprint
    327    }
    328    /// Fetch the build path.
    329    pub fn build(&self) -> &Path {
    330        &self.build
    331    }
    332    /// Fetch the build script path.
    333    pub fn build_script(&self, pkg_dir: &str) -> PathBuf {
    334        if self.is_new_layout {
    335            self.build_unit(pkg_dir).join("build-script")
    336        } else {
    337            self.build().join(pkg_dir)
    338        }
    339    }
    340    /// Fetch the build script execution path.
    341    pub fn build_script_execution(&self, pkg_dir: &str) -> PathBuf {
    342        if self.is_new_layout {
    343            self.build_unit(pkg_dir).join("build-script-execution")
    344        } else {
    345            self.build().join(pkg_dir)
    346        }
    347    }
    348    /// Fetch the artifact path.
    349    pub fn artifact(&self) -> &Path {
    350        &self.artifact
    351    }
    352    /// Fetch the build unit path
    353    pub fn build_unit(&self, pkg_dir: &str) -> PathBuf {
    354        self.build().join(pkg_dir)
    355    }
    356    /// Create and return the tmp path.
    357    pub fn prepare_tmp(&self) -> CargoResult<&Path> {
    358        paths::create_dir_all(&self.tmp)?;
    359        Ok(&self.tmp)
    360    }
    361}