Skip to content

Commit c7b38c2

Browse files
authored
Allow excluding packages from verification with --exclude (model-checking#2399)
1 parent e1bc7bf commit c7b38c2

File tree

30 files changed

+309
-11
lines changed

30 files changed

+309
-11
lines changed

kani-driver/src/args.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ pub struct CargoArgs {
302302
/// Do not activate the `default` feature
303303
#[arg(long)]
304304
pub no_default_features: bool,
305+
305306
// This tolerates spaces too, but we say "comma" only because this is the least error-prone approach...
306307
/// Comma separated list of features to activate
307308
#[arg(short = 'F', long)]
@@ -314,9 +315,14 @@ pub struct CargoArgs {
314315
/// Build all packages in the workspace
315316
#[arg(long)]
316317
pub workspace: bool,
318+
317319
/// Run Kani on the specified packages.
318320
#[arg(long, short, conflicts_with("workspace"), num_args(1..))]
319321
pub package: Vec<String>,
322+
323+
/// Exclude the specified packages
324+
#[arg(long, short, requires("workspace"), conflicts_with("package"), num_args(1..))]
325+
pub exclude: Vec<String>,
320326
}
321327

322328
impl CargoArgs {

kani-driver/src/call_cargo.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl KaniSession {
104104
pkg_args.extend(["--".to_string(), self.reachability_arg()]);
105105

106106
let mut found_target = false;
107-
let packages = packages_to_verify(&self.args, &metadata);
107+
let packages = packages_to_verify(&self.args, &metadata)?;
108108
let mut artifacts = vec![];
109109
let mut failed_targets = vec![];
110110
for package in packages {
@@ -261,35 +261,57 @@ fn print_msg(diagnostic: &Diagnostic, use_rendered: bool) -> Result<()> {
261261
Ok(())
262262
}
263263

264+
/// Check that all package names are present in the workspace, otherwise return which aren't.
265+
fn validate_package_names(package_names: &[String], packages: &[Package]) -> Result<()> {
266+
let package_list: Vec<String> = packages.iter().map(|pkg| pkg.name.clone()).collect();
267+
let unknown_packages: Vec<&String> =
268+
package_names.iter().filter(|pkg_name| !package_list.contains(pkg_name)).collect();
269+
270+
// Some packages aren't in the workspace. Return an error which includes their names.
271+
if !unknown_packages.is_empty() {
272+
let fmt_packages: Vec<String> =
273+
unknown_packages.iter().map(|pkg| format!("`{pkg}`")).collect();
274+
let error_msg = if unknown_packages.len() == 1 {
275+
format!("couldn't find package {}", fmt_packages[0])
276+
} else {
277+
format!("couldn't find packages {}", fmt_packages.join(", "))
278+
};
279+
bail!(error_msg);
280+
};
281+
Ok(())
282+
}
283+
264284
/// Extract the packages that should be verified.
265285
/// If `--package <pkg>` is given, return the list of packages selected.
286+
/// If `--exclude <pkg>` is given, return the list of packages not excluded.
266287
/// If `--workspace` is given, return the list of workspace members.
267288
/// If no argument provided, return the root package if there's one or all members.
268289
/// - I.e.: Do whatever cargo does when there's no `default_members`.
269290
/// - This is because `default_members` is not available in cargo metadata.
270291
/// See <https://github.com/rust-lang/cargo/issues/8033>.
271-
fn packages_to_verify<'b>(args: &KaniArgs, metadata: &'b Metadata) -> Vec<&'b Package> {
272-
debug!(package_selection=?args.cargo.package, workspace=args.cargo.workspace, "packages_to_verify args");
292+
/// In addition, if either `--package <pkg>` or `--exclude <pkg>` is given,
293+
/// validate that `<pkg>` is a package name in the workspace, or return an error
294+
/// otherwise.
295+
fn packages_to_verify<'b>(args: &KaniArgs, metadata: &'b Metadata) -> Result<Vec<&'b Package>> {
296+
debug!(package_selection=?args.cargo.package, package_exclusion=?args.cargo.exclude, workspace=args.cargo.workspace, "packages_to_verify args");
273297
let packages = if !args.cargo.package.is_empty() {
298+
validate_package_names(&args.cargo.package, &metadata.packages)?;
274299
args.cargo
275300
.package
276301
.iter()
277-
.map(|pkg_name| {
278-
metadata
279-
.packages
280-
.iter()
281-
.find(|pkg| pkg.name == *pkg_name)
282-
.expect(&format!("Cannot find package '{pkg_name}'"))
283-
})
302+
.map(|pkg_name| metadata.packages.iter().find(|pkg| pkg.name == *pkg_name).unwrap())
284303
.collect()
304+
} else if !args.cargo.exclude.is_empty() {
305+
validate_package_names(&args.cargo.exclude, &metadata.packages)?;
306+
metadata.packages.iter().filter(|pkg| !args.cargo.exclude.contains(&pkg.name)).collect()
285307
} else {
286308
match (args.cargo.workspace, metadata.root_package()) {
287309
(true, _) | (_, None) => metadata.workspace_packages(),
288310
(_, Some(root_pkg)) => vec![root_pkg],
289311
}
290312
};
291313
trace!(?packages, "packages_to_verify result");
292-
packages
314+
Ok(packages)
293315
}
294316

295317
/// Extract Kani artifact that might've been generated from a given rustc artifact.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
# This workspace test checks that the `--exclude` option prints a user-friendly
5+
# error when the package isn't found in the workspace.
6+
7+
[package]
8+
name = "ws-package-exclude"
9+
version = "0.1.0"
10+
edition = "2021"
11+
12+
[workspace]
13+
members = ["lib_package", "bin_package"]
14+
15+
[workspace.metadata.kani.flags]
16+
workspace = true
17+
exclude = ["unknown_package"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
[package]
5+
name = "bin_package"
6+
version = "0.1.0"
7+
edition = "2021"
8+
9+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10+
11+
[dependencies]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
fn main() {}
5+
6+
#[kani::proof]
7+
fn harness_in_bin_package() {
8+
assert!(1 + 1 == 2);
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
error: couldn't find package `unknown_package`
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
[package]
5+
name = "lib_package"
6+
version = "0.1.0"
7+
edition = "2021"
8+
9+
[lib]
10+
name = "lib_package"
11+
path = "src/lib.rs"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
pub fn api() {}
5+
6+
#[kani::proof]
7+
fn harness_in_lib_package() {
8+
assert!(1 + 1 == 2);
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
fn main() {}
5+
6+
#[kani::proof]
7+
fn harness_in_ws_package() {
8+
assert!(1 + 1 == 3);
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
# This workspace test checks that the `--exclude` option removes the
5+
# `ws-package-exclude` package from the packages to verify.
6+
7+
[package]
8+
name = "ws-package-exclude"
9+
version = "0.1.0"
10+
edition = "2021"
11+
12+
[workspace]
13+
members = ["lib_package", "bin_package"]
14+
15+
[workspace.metadata.kani.flags]
16+
workspace = true
17+
exclude = ["ws-package-exclude"]

0 commit comments

Comments
 (0)