use crate::core::InternedString;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use crate::core::compiler::unit_dependencies;
use crate::core::compiler::{BuildConfig, BuildContext, CompileKind, CompileMode, Context};
use crate::core::compiler::{RustcTargetData, UnitInterner};
use crate::core::profiles::{Profiles, UnitFor};
use crate::core::resolver::features::{FeatureResolver, HasDevUnits, RequestedFeatures};
use crate::core::{PackageIdSpec, 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,
pub spec: Vec<String>,
pub target: Option<String>,
pub profile_specified: bool,
pub requested_profile: InternedString,
pub doc: bool,
}
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
let mut target_dir = ws.target_dir();
let config = ws.config();
if opts.doc {
target_dir = target_dir.join("doc");
return rm_rf(&target_dir.into_path_unlocked(), config);
}
let profiles = Profiles::new(ws.profiles(), config, opts.requested_profile, ws.features())?;
if opts.profile_specified {
let dir_name = profiles.get_dir_name();
target_dir = target_dir.join(dir_name);
}
if opts.spec.is_empty() {
return rm_rf(&target_dir.into_path_unlocked(), config);
}
let (packages, resolve) = ops::resolve_ws(ws)?;
let interner = UnitInterner::new();
let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?;
build_config.requested_profile = opts.requested_profile;
let target_data = RustcTargetData::new(ws, build_config.requested_kind)?;
let bcx = BuildContext::new(
ws,
&packages,
opts.config,
&build_config,
profiles,
&interner,
HashMap::new(),
target_data,
)?;
let requested_features = RequestedFeatures::new_all(true);
let specs = opts
.spec
.iter()
.map(|spec| PackageIdSpec::parse(spec))
.collect::<CargoResult<Vec<_>>>()?;
let features = FeatureResolver::resolve(
ws,
&bcx.target_data,
&resolve,
&requested_features,
&specs,
bcx.build_config.requested_kind,
HasDevUnits::Yes,
)?;
let mut units = Vec::new();
for spec in opts.spec.iter() {
let pkgid = resolve.query(spec)?;
let pkg = packages.get_one(pkgid)?;
for target in pkg.targets() {
for kind in [CompileKind::Host, build_config.requested_kind].iter() {
for mode in CompileMode::all_modes() {
for unit_for in UnitFor::all_values() {
let profile = if mode.is_run_custom_build() {
bcx.profiles
.get_profile_run_custom_build(&bcx.profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
CompileMode::Build,
))
} else {
bcx.profiles.get_profile(
pkg.package_id(),
ws.is_member(pkg),
*unit_for,
*mode,
)
};
let features_for = unit_for.map_to_features_for();
let features =
features.activated_features_unverified(pkg.package_id(), features_for);
units.push(bcx.units.intern(
pkg, target, profile, *kind, *mode, features, false,
));
}
}
}
}
}
let unit_dependencies =
unit_dependencies::build_unit_dependencies(&bcx, &resolve, &features, None, &units, &[])?;
let mut cx = Context::new(config, &bcx, unit_dependencies, build_config.requested_kind)?;
cx.prepare_units(None, &units)?;
for unit in units.iter() {
if unit.mode.is_doc() || unit.mode.is_doc_test() {
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(|| anyhow::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(|| anyhow::format_err!("failed to remove build artifact"))?;
}
Ok(())
}