blob: af15ab265257e982cdfd15bf4c05d739b2fab835 [file] [log] [blame]
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use crate::core::compiler::UnitInterner;
use crate::core::compiler::{BuildConfig, BuildContext, CompileMode, Context, Kind};
use crate::core::profiles::UnitFor;
use crate::core::Workspace;
use crate::ops;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::paths;
use crate::util::Config;
pub struct CleanOptions<'a> {
pub config: &'a Config,
/// A list of packages to clean. If empty, everything is cleaned.
pub spec: Vec<String>,
/// The target arch triple to clean, or None for the host arch
pub target: Option<String>,
/// Whether to clean the release directory
pub release: bool,
/// Whether to just clean the doc directory
pub doc: bool,
}
/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
let mut target_dir = ws.target_dir();
let config = ws.config();
// If the doc option is set, we just want to delete the doc directory.
if opts.doc {
target_dir = target_dir.join("doc");
return rm_rf(&target_dir.into_path_unlocked(), config);
}
// If the release option is set, we set target to release directory
if opts.release {
target_dir = target_dir.join("release");
}
// If we have a spec, then we need to delete some packages, otherwise, just
// remove the whole target directory and be done with it!
//
// Note that we don't bother grabbing a lock here as we're just going to
// blow it all away anyway.
if opts.spec.is_empty() {
return rm_rf(&target_dir.into_path_unlocked(), config);
}
let (packages, resolve) = ops::resolve_ws(ws)?;
let profiles = ws.profiles();
let interner = UnitInterner::new();
let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?;
build_config.release = opts.release;
let bcx = BuildContext::new(
ws,
&resolve,
&packages,
opts.config,
&build_config,
profiles,
&interner,
HashMap::new(),
)?;
let mut units = Vec::new();
for spec in opts.spec.iter() {
// Translate the spec to a Package
let pkgid = resolve.query(spec)?;
let pkg = packages.get_one(pkgid)?;
// Generate all relevant `Unit` targets for this package
for target in pkg.targets() {
for kind in [Kind::Host, Kind::Target].iter() {
for mode in CompileMode::all_modes() {
for unit_for in UnitFor::all_values() {
let profile = if mode.is_run_custom_build() {
profiles.get_profile_run_custom_build(&profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
CompileMode::Build,
opts.release,
))
} else {
profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
*mode,
opts.release,
)
};
units.push(bcx.units.intern(pkg, target, profile, *kind, *mode));
}
}
}
}
}
let mut cx = Context::new(config, &bcx)?;
cx.prepare_units(None, &units)?;
for unit in units.iter() {
if unit.mode.is_doc() || unit.mode.is_doc_test() {
// Cleaning individual rustdoc crates is currently not supported.
// For example, the search index would need to be rebuilt to fully
// remove it (otherwise you're left with lots of broken links).
// Doc tests produce no output.
continue;
}
rm_rf(&cx.files().fingerprint_dir(unit), config)?;
if unit.target.is_custom_build() {
if unit.mode.is_run_custom_build() {
rm_rf(&cx.files().build_script_out_dir(unit), config)?;
} else {
rm_rf(&cx.files().build_script_dir(unit), config)?;
}
continue;
}
for output in cx.outputs(unit)?.iter() {
rm_rf(&output.path, config)?;
if let Some(ref dst) = output.hardlink {
rm_rf(dst, config)?;
}
}
}
Ok(())
}
fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
let m = fs::metadata(path);
if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
config
.shell()
.verbose(|shell| shell.status("Removing", path.display()))?;
paths::remove_dir_all(path)
.chain_err(|| failure::format_err!("could not remove build directory"))?;
} else if m.is_ok() {
config
.shell()
.verbose(|shell| shell.status("Removing", path.display()))?;
paths::remove_file(path)
.chain_err(|| failure::format_err!("failed to remove build artifact"))?;
}
Ok(())
}