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