|
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::BuildContext; |
8 |
| -use crate::core::compiler::{ |
9 |
| - BuildConfig, CompileKind, CompileMode, Context, RustcTargetData, UnitInterner, |
10 |
| -}; |
11 |
| -use crate::core::profiles::{Profiles, UnitFor}; |
12 |
| -use crate::core::resolver::features::HasDevUnits; |
13 |
| -use crate::core::resolver::ResolveOpts; |
14 |
| -use crate::core::{PackageIdSpec, Workspace}; |
| 1 | +use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData}; |
| 2 | +use crate::core::profiles::Profiles; |
| 3 | +use crate::core::{InternedString, PackageIdSpec, Workspace}; |
15 | 4 | use crate::ops;
|
16 |
| -use crate::ops::resolve::WorkspaceResolve; |
17 | 5 | use crate::util::errors::{CargoResult, CargoResultExt};
|
18 | 6 | use crate::util::paths;
|
19 | 7 | use crate::util::Config;
|
| 8 | +use std::fs; |
| 9 | +use std::path::Path; |
20 | 10 |
|
21 | 11 | pub struct CleanOptions<'a> {
|
22 | 12 | pub config: &'a Config,
|
@@ -61,144 +51,150 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
|
61 | 51 | if opts.spec.is_empty() {
|
62 | 52 | return rm_rf(&target_dir.into_path_unlocked(), config);
|
63 | 53 | }
|
64 |
| - let mut build_config = BuildConfig::new(config, Some(1), &opts.targets, CompileMode::Build)?; |
65 |
| - build_config.requested_profile = opts.requested_profile; |
66 |
| - let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?; |
67 |
| - // Resolve for default features. In the future, `cargo clean` should be rewritten |
68 |
| - // so that it doesn't need to guess filename hashes. |
69 |
| - let resolve_opts = ResolveOpts::new( |
70 |
| - /*dev_deps*/ true, |
71 |
| - &[], |
72 |
| - /*all features*/ false, |
73 |
| - /*default*/ true, |
74 |
| - ); |
75 |
| - let specs = opts |
76 |
| - .spec |
77 |
| - .iter() |
78 |
| - .map(|spec| PackageIdSpec::parse(spec)) |
79 |
| - .collect::<CargoResult<Vec<_>>>()?; |
80 |
| - let ws_resolve = ops::resolve_ws_with_opts( |
81 |
| - ws, |
82 |
| - &target_data, |
83 |
| - &build_config.requested_kinds, |
84 |
| - &resolve_opts, |
85 |
| - &specs, |
86 |
| - HasDevUnits::Yes, |
87 |
| - )?; |
88 |
| - let WorkspaceResolve { |
89 |
| - pkg_set, |
90 |
| - targeted_resolve: resolve, |
91 |
| - resolved_features: features, |
92 |
| - .. |
93 |
| - } = ws_resolve; |
94 |
| - |
95 |
| - let interner = UnitInterner::new(); |
96 |
| - let mut units = Vec::new(); |
97 |
| - |
98 |
| - for spec in opts.spec.iter() { |
99 |
| - // Translate the spec to a Package |
100 |
| - let pkgid = resolve.query(spec)?; |
101 |
| - let pkg = pkg_set.get_one(pkgid)?; |
102 |
| - |
103 |
| - // Generate all relevant `Unit` targets for this package |
104 |
| - for target in pkg.targets() { |
105 |
| - for kind in build_config |
106 |
| - .requested_kinds |
107 |
| - .iter() |
108 |
| - .chain(Some(&CompileKind::Host)) |
109 |
| - { |
110 |
| - for mode in CompileMode::all_modes() { |
111 |
| - for unit_for in UnitFor::all_values() { |
112 |
| - let profile = if mode.is_run_custom_build() { |
113 |
| - profiles.get_profile_run_custom_build(&profiles.get_profile( |
114 |
| - pkg.package_id(), |
115 |
| - ws.is_member(pkg), |
116 |
| - /*is_local*/ true, |
117 |
| - *unit_for, |
118 |
| - CompileMode::Build, |
119 |
| - )) |
120 |
| - } else { |
121 |
| - profiles.get_profile( |
122 |
| - pkg.package_id(), |
123 |
| - ws.is_member(pkg), |
124 |
| - /*is_local*/ true, |
125 |
| - *unit_for, |
126 |
| - *mode, |
127 |
| - ) |
128 |
| - }; |
129 |
| - // Use unverified here since this is being more |
130 |
| - // exhaustive than what is actually needed. |
131 |
| - let features_for = unit_for.map_to_features_for(); |
132 |
| - let features = features |
133 |
| - .activated_features_unverified(pkg.package_id(), features_for) |
134 |
| - .unwrap_or_default(); |
135 |
| - units.push(interner.intern( |
136 |
| - pkg, target, profile, *kind, *mode, features, /*is_std*/ false, |
137 |
| - )); |
138 |
| - } |
139 |
| - } |
140 |
| - } |
| 54 | + |
| 55 | + // Clean specific packages. |
| 56 | + let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?; |
| 57 | + let target_data = RustcTargetData::new(ws, &requested_kinds)?; |
| 58 | + let (pkg_set, resolve) = ops::resolve_ws(ws)?; |
| 59 | + let prof_dir_name = profiles.get_dir_name(); |
| 60 | + let host_layout = Layout::new(ws, None, &prof_dir_name)?; |
| 61 | + // Convert requested kinds to a Vec of layouts. |
| 62 | + let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds |
| 63 | + .into_iter() |
| 64 | + .filter_map(|kind| match kind { |
| 65 | + CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) { |
| 66 | + Ok(layout) => Some(Ok((kind, layout))), |
| 67 | + Err(e) => Some(Err(e)), |
| 68 | + }, |
| 69 | + CompileKind::Host => None, |
| 70 | + }) |
| 71 | + .collect::<CargoResult<_>>()?; |
| 72 | + // A Vec of layouts. This is a little convoluted because there can only be |
| 73 | + // one host_layout. |
| 74 | + let layouts = if opts.targets.is_empty() { |
| 75 | + vec![(CompileKind::Host, &host_layout)] |
| 76 | + } else { |
| 77 | + target_layouts |
| 78 | + .iter() |
| 79 | + .map(|(kind, layout)| (*kind, layout)) |
| 80 | + .collect() |
| 81 | + }; |
| 82 | + // Create a Vec that also includes the host for things that need to clean both. |
| 83 | + let layouts_with_host: Vec<(CompileKind, &Layout)> = |
| 84 | + std::iter::once((CompileKind::Host, &host_layout)) |
| 85 | + .chain(layouts.iter().map(|(k, l)| (*k, *l))) |
| 86 | + .collect(); |
| 87 | + |
| 88 | + // Cleaning individual rustdoc crates is currently not supported. |
| 89 | + // For example, the search index would need to be rebuilt to fully |
| 90 | + // remove it (otherwise you're left with lots of broken links). |
| 91 | + // Doc tests produce no output. |
| 92 | + |
| 93 | + // Get Packages for the specified specs. |
| 94 | + let mut packages = Vec::new(); |
| 95 | + for spec_str in opts.spec.iter() { |
| 96 | + // Translate the spec to a Package. |
| 97 | + let spec = PackageIdSpec::parse(spec_str)?; |
| 98 | + if spec.version().is_some() { |
| 99 | + config.shell().warn(&format!( |
| 100 | + "version qualifier in `-p {}` is ignored, \ |
| 101 | + cleaning all versions of `{}` found", |
| 102 | + spec_str, |
| 103 | + spec.name() |
| 104 | + ))?; |
| 105 | + } |
| 106 | + if spec.url().is_some() { |
| 107 | + config.shell().warn(&format!( |
| 108 | + "url qualifier in `-p {}` ignored, \ |
| 109 | + cleaning all versions of `{}` found", |
| 110 | + spec_str, |
| 111 | + spec.name() |
| 112 | + ))?; |
141 | 113 | }
|
| 114 | + let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect(); |
| 115 | + if matches.is_empty() { |
| 116 | + anyhow::bail!("package ID specification `{}` matched no packages", spec); |
| 117 | + } |
| 118 | + packages.extend(pkg_set.get_many(matches)?); |
142 | 119 | }
|
143 | 120 |
|
144 |
| - let unit_graph = unit_dependencies::build_unit_dependencies( |
145 |
| - ws, |
146 |
| - &pkg_set, |
147 |
| - &resolve, |
148 |
| - &features, |
149 |
| - None, |
150 |
| - &units, |
151 |
| - &Default::default(), |
152 |
| - build_config.mode, |
153 |
| - &target_data, |
154 |
| - &profiles, |
155 |
| - &interner, |
156 |
| - )?; |
157 |
| - let extra_args = HashMap::new(); |
158 |
| - let bcx = BuildContext::new( |
159 |
| - ws, |
160 |
| - pkg_set, |
161 |
| - &build_config, |
162 |
| - profiles, |
163 |
| - extra_args, |
164 |
| - target_data, |
165 |
| - units, |
166 |
| - unit_graph, |
167 |
| - )?; |
168 |
| - let mut cx = Context::new(&bcx)?; |
169 |
| - cx.prepare_units()?; |
170 |
| - |
171 |
| - for unit in &bcx.roots { |
172 |
| - if unit.mode.is_doc() || unit.mode.is_doc_test() { |
173 |
| - // Cleaning individual rustdoc crates is currently not supported. |
174 |
| - // For example, the search index would need to be rebuilt to fully |
175 |
| - // remove it (otherwise you're left with lots of broken links). |
176 |
| - // Doc tests produce no output. |
177 |
| - continue; |
| 121 | + for pkg in packages { |
| 122 | + let pkg_dir = format!("{}-*", pkg.name()); |
| 123 | + |
| 124 | + // Clean fingerprints. |
| 125 | + for (_, layout) in &layouts_with_host { |
| 126 | + rm_rf_glob(&layout.fingerprint().join(&pkg_dir), config)?; |
178 | 127 | }
|
179 |
| - rm_rf(&cx.files().fingerprint_dir(unit), config)?; |
180 |
| - if unit.target.is_custom_build() { |
181 |
| - if unit.mode.is_run_custom_build() { |
182 |
| - rm_rf(&cx.files().build_script_out_dir(unit), config)?; |
183 |
| - } else { |
184 |
| - rm_rf(&cx.files().build_script_dir(unit), config)?; |
| 128 | + |
| 129 | + for target in pkg.targets() { |
| 130 | + if target.is_custom_build() { |
| 131 | + // Get both the build_script_build and the output directory. |
| 132 | + for (_, layout) in &layouts_with_host { |
| 133 | + rm_rf_glob(&layout.build().join(&pkg_dir), config)?; |
| 134 | + } |
| 135 | + continue; |
185 | 136 | }
|
186 |
| - continue; |
187 |
| - } |
| 137 | + let crate_name = target.crate_name(); |
| 138 | + for &mode in &[ |
| 139 | + CompileMode::Build, |
| 140 | + CompileMode::Test, |
| 141 | + CompileMode::Check { test: false }, |
| 142 | + ] { |
| 143 | + for (compile_kind, layout) in &layouts { |
| 144 | + let triple = target_data.short_name(compile_kind); |
| 145 | + |
| 146 | + let (file_types, _unsupported) = target_data |
| 147 | + .info(*compile_kind) |
| 148 | + .rustc_outputs(mode, target.kind(), triple)?; |
| 149 | + let (dir, uplift_dir) = if target.is_example() { |
| 150 | + (layout.examples(), layout.examples()) |
| 151 | + } else { |
| 152 | + (layout.deps(), layout.dest()) |
| 153 | + }; |
| 154 | + for file_type in file_types { |
| 155 | + // Some files include a hash in the filename, some don't. |
| 156 | + let hashed_name = file_type.output_filename(target, Some("*")); |
| 157 | + let unhashed_name = file_type.output_filename(target, None); |
| 158 | + rm_rf_glob(&dir.join(&hashed_name), config)?; |
| 159 | + rm_rf(&dir.join(&unhashed_name), config)?; |
| 160 | + // Remove dep-info file generated by rustc. It is not tracked in |
| 161 | + // file_types. It does not have a prefix. |
| 162 | + let hashed_dep_info = dir.join(format!("{}-*.d", crate_name)); |
| 163 | + let unhashed_dep_info = dir.join(format!("{}.d", crate_name)); |
| 164 | + rm_rf_glob(&hashed_dep_info, config)?; |
| 165 | + rm_rf(&unhashed_dep_info, config)?; |
188 | 166 |
|
189 |
| - for output in cx.outputs(unit)?.iter() { |
190 |
| - rm_rf(&output.path, config)?; |
191 |
| - if let Some(ref dst) = output.hardlink { |
192 |
| - rm_rf(dst, config)?; |
| 167 | + // Remove the uplifted copy. |
| 168 | + let uplifted_path = uplift_dir.join(file_type.uplift_filename(target)); |
| 169 | + rm_rf(&uplifted_path, config)?; |
| 170 | + // Dep-info generated by Cargo itself. |
| 171 | + let dep_info = uplifted_path.with_extension("d"); |
| 172 | + rm_rf(&dep_info, config)?; |
| 173 | + } |
| 174 | + // TODO: what to do about build_script_build? |
| 175 | + let incremental = layout.incremental().join(format!("{}-*", crate_name)); |
| 176 | + rm_rf_glob(&incremental, config)?; |
| 177 | + } |
193 | 178 | }
|
194 | 179 | }
|
195 | 180 | }
|
196 | 181 |
|
197 | 182 | Ok(())
|
198 | 183 | }
|
199 | 184 |
|
| 185 | +fn rm_rf_glob(pattern: &Path, config: &Config) -> CargoResult<()> { |
| 186 | + // TODO: Display utf8 warning to user? Or switch to globset? |
| 187 | + let pattern = pattern |
| 188 | + .to_str() |
| 189 | + .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?; |
| 190 | + for path in glob::glob(pattern)? { |
| 191 | + rm_rf(&path?, config)?; |
| 192 | + } |
| 193 | + Ok(()) |
| 194 | +} |
| 195 | + |
200 | 196 | fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
|
201 |
| - let m = fs::metadata(path); |
| 197 | + let m = fs::symlink_metadata(path); |
202 | 198 | if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
|
203 | 199 | config
|
204 | 200 | .shell()
|
|
0 commit comments