Skip to content

Commit ea710b3

Browse files
authored
Add option to override --crate-name from kani (#3054)
Adds a hidden `--crate-name` option to standalone Kani (i.e., `kani`) only. This option allows users to override the crate name used during the compilation of single-file Rust programs, making it easier to apply Kani to non-Cargo projects (see #3046 for more details). Resolves #3046
1 parent 2f3cc47 commit ea710b3

File tree

11 files changed

+111
-27
lines changed

11 files changed

+111
-27
lines changed

kani-driver/src/args/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ pub struct StandaloneArgs {
7979

8080
#[command(subcommand)]
8181
pub command: Option<StandaloneSubcommand>,
82+
83+
#[arg(long, hide = true)]
84+
pub crate_name: Option<String>,
8285
}
8386

8487
/// Kani takes optional subcommands to request specialized behavior.

kani-driver/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ fn standalone_main() -> Result<()> {
104104
print_kani_version(InvocationType::Standalone);
105105
}
106106

107-
let project = project::standalone_project(&args.input.unwrap(), &session)?;
107+
let project = project::standalone_project(&args.input.unwrap(), args.crate_name, &session)?;
108108
if session.args.only_codegen { Ok(()) } else { verify_project(project, session) }
109109
}
110110

kani-driver/src/project.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
1919
use crate::metadata::{from_json, merge_kani_metadata, mock_proof_harness};
2020
use crate::session::KaniSession;
21-
use crate::util::{crate_name, guess_rlib_name};
21+
use crate::util::crate_name;
2222
use anyhow::{Context, Result};
2323
use kani_metadata::{
2424
artifact::convert_type, ArtifactType, ArtifactType::*, HarnessMetadata, KaniMetadata,
@@ -270,8 +270,12 @@ pub fn cargo_project(session: &KaniSession, keep_going: bool) -> Result<Project>
270270
}
271271

272272
/// Generate a project directly using `kani-compiler` on a single crate.
273-
pub fn standalone_project(input: &Path, session: &KaniSession) -> Result<Project> {
274-
StandaloneProjectBuilder::try_new(input, session)?.build()
273+
pub fn standalone_project(
274+
input: &Path,
275+
crate_name: Option<String>,
276+
session: &KaniSession,
277+
) -> Result<Project> {
278+
StandaloneProjectBuilder::try_new(input, crate_name, session)?.build()
275279
}
276280

277281
/// Builder for a standalone project.
@@ -291,15 +295,15 @@ struct StandaloneProjectBuilder<'a> {
291295
impl<'a> StandaloneProjectBuilder<'a> {
292296
/// Create a `StandaloneProjectBuilder` from the given input and session.
293297
/// This will perform a few validations before the build.
294-
fn try_new(input: &Path, session: &'a KaniSession) -> Result<Self> {
298+
fn try_new(input: &Path, krate_name: Option<String>, session: &'a KaniSession) -> Result<Self> {
295299
// Ensure the directory exist and it's in its canonical form.
296300
let outdir = if let Some(target_dir) = &session.args.target_dir {
297301
std::fs::create_dir_all(target_dir)?; // This is a no-op if directory exists.
298302
target_dir.canonicalize()?
299303
} else {
300304
input.canonicalize().unwrap().parent().unwrap().to_path_buf()
301305
};
302-
let crate_name = crate_name(&input);
306+
let crate_name = if let Some(name) = krate_name { name } else { crate_name(&input) };
303307
let metadata = standalone_artifact(&outdir, &crate_name, Metadata);
304308
Ok(StandaloneProjectBuilder {
305309
outdir,
@@ -313,7 +317,7 @@ impl<'a> StandaloneProjectBuilder<'a> {
313317
/// Build a project by compiling `self.input` file.
314318
fn build(self) -> Result<Project> {
315319
// Register artifacts that may be generated by the compiler / linker for future deletion.
316-
let rlib_path = guess_rlib_name(&self.outdir.join(self.input.file_name().unwrap()));
320+
let rlib_path = self.rlib_name();
317321
self.session.record_temporary_file(&rlib_path);
318322
self.session.record_temporary_file(&self.metadata.path);
319323

@@ -339,6 +343,17 @@ impl<'a> StandaloneProjectBuilder<'a> {
339343
}
340344
result
341345
}
346+
347+
/// Build the rlib name from the crate name.
348+
/// This is only used by 'kani', never 'cargo-kani', so we hopefully don't have too many corner
349+
/// cases to deal with.
350+
fn rlib_name(&self) -> PathBuf {
351+
let path = &self.outdir.join(self.input.file_name().unwrap());
352+
let basedir = path.parent().unwrap_or(Path::new("."));
353+
let rlib_name = format!("lib{}.rlib", self.crate_name);
354+
355+
basedir.join(rlib_name)
356+
}
342357
}
343358

344359
/// Generate a `KaniMetadata` by extending the original metadata to contain the function under

kani-driver/src/util.rs

-20
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,6 @@ pub fn crate_name(path: &Path) -> String {
2626
stem.replace(['-', '.'], "_")
2727
}
2828

29-
/// Attempt to guess the rlib name for rust source file.
30-
/// This is only used by 'kani', never 'cargo-kani', so we hopefully don't have too many corner
31-
/// cases to deal with.
32-
/// In rustc, you can find some code dealing this this naming in:
33-
/// compiler/rustc_codegen_ssa/src/back/link.rs
34-
pub fn guess_rlib_name(path: &Path) -> PathBuf {
35-
let basedir = path.parent().unwrap_or(Path::new("."));
36-
let rlib_name = format!("lib{}.rlib", crate_name(path));
37-
38-
basedir.join(rlib_name)
39-
}
40-
4129
/// Given a path of some sort (usually from argv0), this attempts to extract the basename / stem
4230
/// of the executable. e.g. "/path/foo -> foo" "./foo.exe -> foo" "foo -> foo"
4331
pub fn executable_basename(argv0: &Option<&OsString>) -> Option<OsString> {
@@ -117,14 +105,6 @@ mod tests {
117105
assert_eq!(alter_extension(&q, "symtab.json"), PathBuf::from("file.more.symtab.json"));
118106
}
119107

120-
#[test]
121-
fn check_guess_rlib_name() {
122-
assert_eq!(guess_rlib_name(Path::new("mycrate.rs")), PathBuf::from("libmycrate.rlib"));
123-
assert_eq!(guess_rlib_name(Path::new("my-crate.rs")), PathBuf::from("libmy_crate.rlib"));
124-
assert_eq!(guess_rlib_name(Path::new("./foo.rs")), PathBuf::from("./libfoo.rlib"));
125-
assert_eq!(guess_rlib_name(Path::new("a/b/foo.rs")), PathBuf::from("a/b/libfoo.rlib"));
126-
}
127-
128108
#[test]
129109
fn check_exe_basename() {
130110
assert_eq!(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
pub fn add_a(left: usize, right: usize) -> usize {
5+
left + right
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
extern crate a;
4+
5+
pub fn add_b(left: usize, right: usize) -> usize {
6+
a::add_a(left, right)
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
extern crate a;
4+
extern crate b;
5+
6+
pub fn add_c(left: usize, right: usize) -> usize {
7+
b::add_b(left, right)
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
script: crate-name.sh
4+
expected: crate-name.expected
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
No proof harnesses (functions with #[kani::proof]) were found to verify.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env bash
2+
# Copyright Kani Contributors
3+
# SPDX-License-Identifier: Apache-2.0 OR MIT
4+
5+
# This test performs multiple checks focused on crate names. The first steps
6+
# check expected results with the default naming scheme. The remaining ones
7+
# check expected results with the `--crate-name=<name>` feature which allows
8+
# users to specify the crate name used for compilation with standalone `kani`.
9+
set -eu
10+
11+
check_file_exists() {
12+
local file=$1
13+
if ! [ -e "${file}" ]
14+
then
15+
echo "error: expected \`${file}\` to have been generated"
16+
exit 1
17+
fi
18+
}
19+
20+
# 1. Check expected results with the default naming scheme.
21+
# Note: The assumed crate name is `lib`, so we generate `liblib.rlib`.
22+
kani --only-codegen --keep-temps a/src/lib.rs
23+
check_file_exists a/src/liblib.rlib
24+
check_file_exists a/src/lib.kani-metadata.json
25+
rm a/src/liblib.rlib
26+
rm a/src/lib.kani-metadata.json
27+
28+
# 2. Check expected results with the default naming scheme, which replaces
29+
# some characters.
30+
# Note: The assumed crate name is `my-code`, so we generate `libmy_code.rlib`.
31+
kani --only-codegen --keep-temps my-code.rs
32+
check_file_exists libmy_code.rlib
33+
check_file_exists my_code.kani-metadata.json
34+
35+
# 3. Check expected results with the `--crate-name=<name>` feature. This feature
36+
# allows users to specify the crate name used for compilation with standalone
37+
# `kani`, enabling the compilation of multiple dependencies with similar
38+
# names.
39+
# Note: In the example below, compiling without `--crate-name=<name>` would
40+
# result in files named `liblib.rlib` for each dependency.
41+
kani --only-codegen --keep-temps a/src/lib.rs --crate-name="a"
42+
check_file_exists a/src/liba.rlib
43+
check_file_exists a/src/a.kani-metadata.json
44+
45+
RUSTFLAGS="--extern a=a/src/liba.rlib" kani --only-codegen --keep-temps b/src/lib.rs --crate-name="b"
46+
check_file_exists b/src/libb.rlib
47+
check_file_exists b/src/b.kani-metadata.json
48+
49+
RUSTFLAGS="--extern b=b/src/libb.rlib --extern a=a/src/liba.rlib" kani c/src/lib.rs
50+
51+
rm a/src/liba.rlib
52+
rm a/src/a.kani-metadata.json
53+
rm b/src/libb.rlib
54+
rm b/src/b.kani-metadata.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
pub fn add_a(left: usize, right: usize) -> usize {
5+
left + right
6+
}

0 commit comments

Comments
 (0)