1use crate::core::InternedString;
2use std::collections::HashMap;
3use std::fs;
4use std::path::Path;
5
6use crate::core::compiler::unit_dependencies;
7use crate::core::compiler::{BuildConfig, BuildContext, CompileKind, CompileMode, Context};
8use crate::core::compiler::{RustcTargetData, UnitInterner};
9use crate::core::profiles::{Profiles, UnitFor};
10use crate::core::resolver::features::HasDevUnits;
11use crate::core::resolver::ResolveOpts;
12use crate::core::{PackageIdSpec, Workspace};
13use crate::ops;
14use crate::ops::resolve::WorkspaceResolve;
15use crate::util::errors::{CargoResult, CargoResultExt};
16use crate::util::paths;
17use crate::util::Config;
18
19pub struct CleanOptions<'a> {
20 pub config: &'a Config,
21 pub spec: Vec<String>,
23 pub target: Option<String>,
25 pub profile_specified: bool,
27 pub requested_profile: InternedString,
29 pub doc: bool,
31}
32
33pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
35 let mut target_dir = ws.target_dir();
36 let config = ws.config();
37
38 if opts.doc {
40 target_dir = target_dir.join("doc");
41 return rm_rf(&target_dir.into_path_unlocked(), config);
42 }
43
44 let profiles = Profiles::new(ws.profiles(), config, opts.requested_profile, ws.features())?;
45
46 if opts.profile_specified {
47 let dir_name = profiles.get_dir_name();
51 target_dir = target_dir.join(dir_name);
52 }
53
54 if opts.spec.is_empty() {
60 return rm_rf(&target_dir.into_path_unlocked(), config);
61 }
62 let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?;
63 build_config.requested_profile = opts.requested_profile;
64 let target_data = RustcTargetData::new(ws, build_config.requested_kind)?;
65 let resolve_opts = ResolveOpts::everything();
66 let specs = opts
67 .spec
68 .iter()
69 .map(|spec| PackageIdSpec::parse(spec))
70 .collect::<CargoResult<Vec<_>>>()?;
71 let ws_resolve = ops::resolve_ws_with_opts(
72 ws,
73 &target_data,
74 build_config.requested_kind,
75 &resolve_opts,
76 &specs,
77 HasDevUnits::Yes,
78 )?;
79 let WorkspaceResolve {
80 pkg_set,
81 targeted_resolve: resolve,
82 resolved_features: features,
83 ..
84 } = ws_resolve;
85
86 let interner = UnitInterner::new();
87 let bcx = BuildContext::new(
88 ws,
89 &pkg_set,
90 opts.config,
91 &build_config,
92 profiles,
93 &interner,
94 HashMap::new(),
95 target_data,
96 )?;
97 let mut units = Vec::new();
98
99 for spec in opts.spec.iter() {
100 let pkgid = resolve.query(spec)?;
102 let pkg = pkg_set.get_one(pkgid)?;
103
104 for target in pkg.targets() {
106 for kind in [CompileKind::Host, build_config.requested_kind].iter() {
107 for mode in CompileMode::all_modes() {
108 for unit_for in UnitFor::all_values() {
109 let profile = if mode.is_run_custom_build() {
110 bcx.profiles
111 .get_profile_run_custom_build(&bcx.profiles.get_profile(
112 pkg.package_id(),
113 ws.is_member(pkg),
114 *unit_for,
115 CompileMode::Build,
116 ))
117 } else {
118 bcx.profiles.get_profile(
119 pkg.package_id(),
120 ws.is_member(pkg),
121 *unit_for,
122 *mode,
123 )
124 };
125 let features_for = unit_for.map_to_features_for();
128 let features =
129 features.activated_features_unverified(pkg.package_id(), features_for);
130 units.push(bcx.units.intern(
131 pkg, target, profile, *kind, *mode, features, false,
132 ));
133 }
134 }
135 }
136 }
137 }
138
139 let unit_dependencies =
140 unit_dependencies::build_unit_dependencies(&bcx, &resolve, &features, None, &units, &[])?;
141 let mut cx = Context::new(config, &bcx, unit_dependencies, build_config.requested_kind)?;
142 cx.prepare_units(None, &units)?;
143
144 for unit in units.iter() {
145 if unit.mode.is_doc() || unit.mode.is_doc_test() {
146 continue;
151 }
152 rm_rf(&cx.files().fingerprint_dir(unit), config)?;
153 if unit.target.is_custom_build() {
154 if unit.mode.is_run_custom_build() {
155 rm_rf(&cx.files().build_script_out_dir(unit), config)?;
156 } else {
157 rm_rf(&cx.files().build_script_dir(unit), config)?;
158 }
159 continue;
160 }
161
162 for output in cx.outputs(unit)?.iter() {
163 rm_rf(&output.path, config)?;
164 if let Some(ref dst) = output.hardlink {
165 rm_rf(dst, config)?;
166 }
167 }
168 }
169
170 Ok(())
171}
172
173fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
174 let m = fs::metadata(path);
175 if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
176 config
177 .shell()
178 .verbose(|shell| shell.status("Removing", path.display()))?;
179 paths::remove_dir_all(path)
180 .chain_err(|| anyhow::format_err!("could not remove build directory"))?;
181 } else if m.is_ok() {
182 config
183 .shell()
184 .verbose(|shell| shell.status("Removing", path.display()))?;
185 paths::remove_file(path)
186 .chain_err(|| anyhow::format_err!("failed to remove build artifact"))?;
187 }
188 Ok(())
189}