feat: glob support for target selection

This commit is contained in:
Weihang Lo 2020-10-04 23:22:54 +08:00
parent be31989a43
commit 42696ae234
No known key found for this signature in database
GPG key ID: D7DBF189825E82E7
3 changed files with 55 additions and 4 deletions

View file

@ -41,8 +41,11 @@ use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::ops::resolve::WorkspaceResolve;
use crate::util::config::Config;
use crate::util::restricted_names::is_glob_pattern;
use crate::util::{closest_msg, profile, CargoResult, StableHasher};
use anyhow::Context as _;
/// Contains information about how a package should be compiled.
///
/// Note on distinction between `CompileOptions` and `BuildConfig`:
@ -577,6 +580,13 @@ impl FilterRule {
FilterRule::Just(ref targets) => Some(targets.clone()),
}
}
pub(crate) fn contains_glob_patterns(&self) -> bool {
match self {
FilterRule::All => false,
FilterRule::Just(targets) => targets.iter().any(is_glob_pattern),
}
}
}
impl CompileFilter {
@ -706,6 +716,24 @@ impl CompileFilter {
CompileFilter::Only { .. } => true,
}
}
pub(crate) fn contains_glob_patterns(&self) -> bool {
match self {
CompileFilter::Default { .. } => false,
CompileFilter::Only {
bins,
examples,
tests,
benches,
..
} => {
bins.contains_glob_patterns()
|| examples.contains_glob_patterns()
|| tests.contains_glob_patterns()
|| benches.contains_glob_patterns()
}
}
}
}
/// A proposed target.
@ -1163,8 +1191,16 @@ fn find_named_targets<'a>(
is_expected_kind: fn(&Target) -> bool,
mode: CompileMode,
) -> CargoResult<Vec<Proposal<'a>>> {
let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
let proposals = filter_targets(packages, filter, true, mode);
let is_glob = is_glob_pattern(target_name);
let proposals = if is_glob {
let pattern = build_glob(target_name)?;
let filter = |t: &Target| is_expected_kind(t) && pattern.matches(t.name());
filter_targets(packages, filter, true, mode)
} else {
let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
filter_targets(packages, filter, true, mode)
};
if proposals.is_empty() {
let targets = packages.iter().flat_map(|pkg| {
pkg.targets()
@ -1173,8 +1209,9 @@ fn find_named_targets<'a>(
});
let suggestion = closest_msg(target_name, targets, |t| t.name());
anyhow::bail!(
"no {} target named `{}`{}",
"no {} target {} `{}`{}",
target_desc,
if is_glob { "matches pattern" } else { "named" },
target_name,
suggestion
);
@ -1291,3 +1328,8 @@ fn traverse_and_share(
new_graph.entry(new_unit.clone()).or_insert(new_deps);
new_unit
}
/// TODO: @weihanglo
fn build_glob(pat: &str) -> CargoResult<glob::Pattern> {
glob::Pattern::new(pat).with_context(|| format!("Cannot build glob pattern from `{}`", pat))
}

View file

@ -13,6 +13,10 @@ pub fn run(
) -> CargoResult<()> {
let config = ws.config();
if options.filter.contains_glob_patterns() {
anyhow::bail!("`cargo run` does not support glob patterns on target selection")
}
// We compute the `bins` here *just for diagnosis*. The actual set of
// packages to be run is determined by the `ops::compile` call below.
let packages = options.spec.get_packages(ws)?;

View file

@ -83,7 +83,7 @@ pub fn validate_package_name(name: &str, what: &str, help: &str) -> CargoResult<
Ok(())
}
// Check the entire path for names reserved in Windows.
/// Check the entire path for names reserved in Windows.
pub fn is_windows_reserved_path(path: &Path) -> bool {
path.iter()
.filter_map(|component| component.to_str())
@ -92,3 +92,8 @@ pub fn is_windows_reserved_path(path: &Path) -> bool {
is_windows_reserved(stem)
})
}
/// Returns `true` if the name contains any glob pattern wildcards.
pub fn is_glob_pattern<T: AsRef<str>>(name: T) -> bool {
name.as_ref().contains(&['*', '?', '[', ']'][..])
}