]> git.proxmox.com Git - cargo.git/blob - src/cargo/ops/cargo_clean.rs
Rewrite new feature resolver to simplify DepKind handling.
[cargo.git] / src / cargo / ops / cargo_clean.rs
1 use crate::core::InternedString;
2 use std::collections::HashMap;
3 use std::fs;
4 use std::path::Path;
5
6 use crate::core::compiler::unit_dependencies;
7 use crate::core::compiler::{BuildConfig, BuildContext, CompileKind, CompileMode, Context};
8 use crate::core::compiler::{RustcTargetData, UnitInterner};
9 use crate::core::profiles::{Profiles, UnitFor};
10 use crate::core::resolver::features::{FeatureResolver, RequestedFeatures};
11 use crate::core::{PackageIdSpec, Workspace};
12 use crate::ops;
13 use crate::util::errors::{CargoResult, CargoResultExt};
14 use crate::util::paths;
15 use crate::util::Config;
16
17 pub struct CleanOptions<'a> {
18 pub config: &'a Config,
19 /// A list of packages to clean. If empty, everything is cleaned.
20 pub spec: Vec<String>,
21 /// The target arch triple to clean, or None for the host arch
22 pub target: Option<String>,
23 /// Whether to clean the release directory
24 pub profile_specified: bool,
25 /// Whether to clean the directory of a certain build profile
26 pub requested_profile: InternedString,
27 /// Whether to just clean the doc directory
28 pub doc: bool,
29 }
30
31 /// Cleans the package's build artifacts.
32 pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
33 let mut target_dir = ws.target_dir();
34 let config = ws.config();
35
36 // If the doc option is set, we just want to delete the doc directory.
37 if opts.doc {
38 target_dir = target_dir.join("doc");
39 return rm_rf(&target_dir.into_path_unlocked(), config);
40 }
41
42 let profiles = Profiles::new(ws.profiles(), config, opts.requested_profile, ws.features())?;
43
44 if opts.profile_specified {
45 // After parsing profiles we know the dir-name of the profile, if a profile
46 // was passed from the command line. If so, delete only the directory of
47 // that profile.
48 let dir_name = profiles.get_dir_name();
49 target_dir = target_dir.join(dir_name);
50 }
51
52 // If we have a spec, then we need to delete some packages, otherwise, just
53 // remove the whole target directory and be done with it!
54 //
55 // Note that we don't bother grabbing a lock here as we're just going to
56 // blow it all away anyway.
57 if opts.spec.is_empty() {
58 return rm_rf(&target_dir.into_path_unlocked(), config);
59 }
60 let (packages, resolve) = ops::resolve_ws(ws)?;
61
62 let interner = UnitInterner::new();
63 let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?;
64 build_config.requested_profile = opts.requested_profile;
65 let target_data = RustcTargetData::new(ws, build_config.requested_kind)?;
66 let bcx = BuildContext::new(
67 ws,
68 &packages,
69 opts.config,
70 &build_config,
71 profiles,
72 &interner,
73 HashMap::new(),
74 target_data,
75 )?;
76 let requested_features = RequestedFeatures::new_all(true);
77 let specs = opts
78 .spec
79 .iter()
80 .map(|spec| PackageIdSpec::parse(spec))
81 .collect::<CargoResult<Vec<_>>>()?;
82 let features = FeatureResolver::resolve(
83 ws,
84 &bcx.target_data,
85 &resolve,
86 &requested_features,
87 &specs,
88 bcx.build_config.requested_kind,
89 true,
90 )?;
91 let mut units = Vec::new();
92
93 for spec in opts.spec.iter() {
94 // Translate the spec to a Package
95 let pkgid = resolve.query(spec)?;
96 let pkg = packages.get_one(pkgid)?;
97
98 // Generate all relevant `Unit` targets for this package
99 for target in pkg.targets() {
100 for kind in [CompileKind::Host, build_config.requested_kind].iter() {
101 for mode in CompileMode::all_modes() {
102 for unit_for in UnitFor::all_values() {
103 let profile = if mode.is_run_custom_build() {
104 bcx.profiles
105 .get_profile_run_custom_build(&bcx.profiles.get_profile(
106 pkg.package_id(),
107 ws.is_member(pkg),
108 *unit_for,
109 CompileMode::Build,
110 ))
111 } else {
112 bcx.profiles.get_profile(
113 pkg.package_id(),
114 ws.is_member(pkg),
115 *unit_for,
116 *mode,
117 )
118 };
119 let features = features
120 .activated_features(pkg.package_id(), unit_for.is_for_build_dep());
121 units.push(bcx.units.intern(
122 pkg, target, profile, *kind, *mode, features, /*is_std*/ false,
123 ));
124 }
125 }
126 }
127 }
128 }
129
130 let unit_dependencies =
131 unit_dependencies::build_unit_dependencies(&bcx, &resolve, &features, None, &units, &[])?;
132 let mut cx = Context::new(config, &bcx, unit_dependencies, build_config.requested_kind)?;
133 cx.prepare_units(None, &units)?;
134
135 for unit in units.iter() {
136 if unit.mode.is_doc() || unit.mode.is_doc_test() {
137 // Cleaning individual rustdoc crates is currently not supported.
138 // For example, the search index would need to be rebuilt to fully
139 // remove it (otherwise you're left with lots of broken links).
140 // Doc tests produce no output.
141 continue;
142 }
143 rm_rf(&cx.files().fingerprint_dir(unit), config)?;
144 if unit.target.is_custom_build() {
145 if unit.mode.is_run_custom_build() {
146 rm_rf(&cx.files().build_script_out_dir(unit), config)?;
147 } else {
148 rm_rf(&cx.files().build_script_dir(unit), config)?;
149 }
150 continue;
151 }
152
153 for output in cx.outputs(unit)?.iter() {
154 rm_rf(&output.path, config)?;
155 if let Some(ref dst) = output.hardlink {
156 rm_rf(dst, config)?;
157 }
158 }
159 }
160
161 Ok(())
162 }
163
164 fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
165 let m = fs::metadata(path);
166 if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
167 config
168 .shell()
169 .verbose(|shell| shell.status("Removing", path.display()))?;
170 paths::remove_dir_all(path)
171 .chain_err(|| anyhow::format_err!("could not remove build directory"))?;
172 } else if m.is_ok() {
173 config
174 .shell()
175 .verbose(|shell| shell.status("Removing", path.display()))?;
176 paths::remove_file(path)
177 .chain_err(|| anyhow::format_err!("failed to remove build artifact"))?;
178 }
179 Ok(())
180 }