Skip to content

Commit a8997cb

Browse files
committed
Implement new clean -p using globs.
1 parent eac3b66 commit a8997cb

File tree

3 files changed

+301
-142
lines changed

3 files changed

+301
-142
lines changed

src/cargo/core/compiler/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
4141
pub use self::job::Freshness;
4242
use self::job::{Job, Work};
4343
use self::job_queue::{JobQueue, JobState};
44+
pub(crate) use self::layout::Layout;
4445
use self::output_depinfo::output_depinfo;
4546
use self::unit_graph::UnitDep;
4647
pub use crate::core::compiler::unit::{Unit, UnitInterner};

src/cargo/ops/cargo_clean.rs

Lines changed: 135 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
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};
154
use crate::ops;
16-
use crate::ops::resolve::WorkspaceResolve;
175
use crate::util::errors::{CargoResult, CargoResultExt};
186
use crate::util::paths;
197
use crate::util::Config;
8+
use std::fs;
9+
use std::path::Path;
2010

2111
pub struct CleanOptions<'a> {
2212
pub config: &'a Config,
@@ -61,144 +51,150 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
6151
if opts.spec.is_empty() {
6252
return rm_rf(&target_dir.into_path_unlocked(), config);
6353
}
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+
))?;
141113
}
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)?);
142119
}
143120

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)?;
178127
}
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;
185136
}
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)?;
188166

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+
}
193178
}
194179
}
195180
}
196181

197182
Ok(())
198183
}
199184

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+
200196
fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
201-
let m = fs::metadata(path);
197+
let m = fs::symlink_metadata(path);
202198
if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
203199
config
204200
.shell()

0 commit comments

Comments
 (0)