3
3
4
4
use crate :: args:: KaniArgs ;
5
5
use crate :: call_single_file:: to_rustc_arg;
6
+ use crate :: project:: Artifact ;
6
7
use crate :: session:: KaniSession ;
7
8
use anyhow:: { bail, Context , Result } ;
8
9
use cargo_metadata:: diagnostic:: { Diagnostic , DiagnosticLevel } ;
9
- use cargo_metadata:: { Message , Metadata , MetadataCommand , Package } ;
10
+ use cargo_metadata:: { Message , Metadata , MetadataCommand , Package , Target } ;
11
+ use kani_metadata:: { ArtifactType , CompilerArtifactStub } ;
10
12
use std:: ffi:: { OsStr , OsString } ;
11
- use std:: fs;
13
+ use std:: fs:: { self , File } ;
12
14
use std:: io:: BufReader ;
13
- use std:: path:: { Path , PathBuf } ;
15
+ use std:: path:: PathBuf ;
14
16
use std:: process:: Command ;
15
17
use tracing:: { debug, trace} ;
16
18
@@ -29,12 +31,8 @@ pub struct CargoOutputs {
29
31
/// The directory where compiler outputs should be directed.
30
32
/// Usually 'target/BUILD_TRIPLE/debug/deps/'
31
33
pub outdir : PathBuf ,
32
- /// The collection of *.symtab.out goto binary files written.
33
- pub symtab_gotos : Vec < PathBuf > ,
34
- /// The location of vtable restrictions files (a directory of *.restrictions.json)
35
- pub restrictions : Option < PathBuf > ,
36
34
/// The kani-metadata.json files written by kani-compiler.
37
- pub metadata : Vec < PathBuf > ,
35
+ pub metadata : Vec < Artifact > ,
38
36
/// Recording the cargo metadata from the build
39
37
pub cargo_metadata : Metadata ,
40
38
}
@@ -105,20 +103,21 @@ impl KaniSession {
105
103
106
104
let mut found_target = false ;
107
105
let packages = packages_to_verify ( & self . args , & metadata) ;
106
+ let mut artifacts = vec ! [ ] ;
108
107
for package in packages {
109
- for target in package_targets ( & self . args , package) {
108
+ for verification_target in package_targets ( & self . args , package) {
110
109
let mut cmd = Command :: new ( "cargo" ) ;
111
110
cmd. args ( & cargo_args)
112
111
. args ( vec ! [ "-p" , & package. name] )
113
- . args ( & target . to_args ( ) )
112
+ . args ( & verification_target . to_args ( ) )
114
113
. args ( & pkg_args)
115
114
. env ( "RUSTC" , & self . kani_compiler )
116
115
// Use CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See
117
116
// https://doc.rust-lang.org/cargo/reference/environment-variables.html
118
117
. env ( "CARGO_ENCODED_RUSTFLAGS" , rustc_args. join ( OsStr :: new ( "\x1f " ) ) )
119
118
. env ( "CARGO_TERM_PROGRESS_WHEN" , "never" ) ;
120
119
121
- self . run_cargo ( cmd) ? ;
120
+ artifacts . extend ( self . run_cargo ( cmd, verification_target . target ( ) ) ? . into_iter ( ) ) ;
122
121
found_target = true ;
123
122
}
124
123
}
@@ -127,13 +126,7 @@ impl KaniSession {
127
126
bail ! ( "No supported targets were found." ) ;
128
127
}
129
128
130
- Ok ( CargoOutputs {
131
- outdir : outdir. clone ( ) ,
132
- symtab_gotos : glob ( & outdir. join ( "*.symtab.out" ) ) ?,
133
- metadata : glob ( & outdir. join ( "*.kani-metadata.json" ) ) ?,
134
- restrictions : self . args . restrict_vtable ( ) . then_some ( outdir) ,
135
- cargo_metadata : metadata,
136
- } )
129
+ Ok ( CargoOutputs { outdir, metadata : artifacts, cargo_metadata : metadata } )
137
130
}
138
131
139
132
fn cargo_metadata ( & self , build_target : & str ) -> Result < Metadata > {
@@ -165,9 +158,10 @@ impl KaniSession {
165
158
}
166
159
167
160
/// Run cargo and collect any error found.
168
- /// TODO: We should also use this to collect the artifacts generated by cargo .
169
- fn run_cargo ( & self , cargo_cmd : Command ) -> Result < ( ) > {
161
+ /// We also collect the metadata file generated during compilation if any .
162
+ fn run_cargo ( & self , cargo_cmd : Command , target : & Target ) -> Result < Option < Artifact > > {
170
163
let support_color = atty:: is ( atty:: Stream :: Stdout ) ;
164
+ let mut artifact = None ;
171
165
if let Some ( mut cargo_process) = self . run_piped ( cargo_cmd) ? {
172
166
let reader = BufReader :: new ( cargo_process. stdout . take ( ) . unwrap ( ) ) ;
173
167
let mut error_count = 0 ;
@@ -196,9 +190,16 @@ impl KaniSession {
196
190
}
197
191
}
198
192
} ,
199
- Message :: CompilerArtifact ( _)
200
- | Message :: BuildScriptExecuted ( _)
201
- | Message :: BuildFinished ( _) => {
193
+ Message :: CompilerArtifact ( rustc_artifact) => {
194
+ if rustc_artifact. target == * target {
195
+ debug_assert ! (
196
+ artifact. is_none( ) ,
197
+ "expected only one artifact for `{target:?}`" ,
198
+ ) ;
199
+ artifact = Some ( rustc_artifact) ;
200
+ }
201
+ }
202
+ Message :: BuildScriptExecuted ( _) | Message :: BuildFinished ( _) => {
202
203
// do nothing
203
204
}
204
205
Message :: TextLine ( msg) => {
@@ -222,7 +223,11 @@ impl KaniSession {
222
223
) ;
223
224
}
224
225
}
225
- Ok ( ( ) )
226
+ // We generate kani specific artifacts only for the build target. The build target is
227
+ // always the last artifact generated in a build, and all the other artifacts are related
228
+ // to dependencies or build scripts. Hence, we need to invoke `map_kani_artifact` only
229
+ // for the last compiler artifact.
230
+ Ok ( artifact. and_then ( map_kani_artifact) )
226
231
}
227
232
}
228
233
@@ -236,15 +241,6 @@ fn print_msg(diagnostic: &Diagnostic, use_rendered: bool) -> Result<()> {
236
241
Ok ( ( ) )
237
242
}
238
243
239
- /// Given a `path` with glob characters in it (e.g. `*.json`), return a vector of matching files
240
- fn glob ( path : & Path ) -> Result < Vec < PathBuf > > {
241
- let results = glob:: glob ( path. to_str ( ) . context ( "Non-UTF-8 path enountered" ) ?) ?;
242
- // the logic to turn "Iter<Result<T, E>>" into "Result<Vec<T>, E>" doesn't play well
243
- // with anyhow, so a type annotation is required
244
- let v: core:: result:: Result < Vec < PathBuf > , glob:: GlobError > = results. collect ( ) ;
245
- Ok ( v?)
246
- }
247
-
248
244
/// Extract the packages that should be verified.
249
245
/// If `--package <pkg>` is given, return the list of packages selected.
250
246
/// If `--workspace` is given, return the list of workspace members.
@@ -276,20 +272,69 @@ fn packages_to_verify<'b>(args: &KaniArgs, metadata: &'b Metadata) -> Vec<&'b Pa
276
272
packages
277
273
}
278
274
275
+ /// Extract Kani artifact that might've been generated from a given rustc artifact.
276
+ /// Not every rustc artifact will map to a kani artifact, hence the `Option<>`.
277
+ ///
278
+ /// Unfortunately, we cannot always rely on the messages to get the path for the original artifact
279
+ /// that `rustc` produces. So we hack the content of the output path to point to the original
280
+ /// metadata file. See <https://github.com/model-checking/kani/issues/2234> for more details.
281
+ fn map_kani_artifact ( rustc_artifact : cargo_metadata:: Artifact ) -> Option < Artifact > {
282
+ debug ! ( ?rustc_artifact, "map_kani_artifact" ) ;
283
+ if rustc_artifact. target . is_custom_build ( ) {
284
+ // We don't verify custom builds.
285
+ return None ;
286
+ }
287
+ let result = rustc_artifact. filenames . iter ( ) . find_map ( |path| {
288
+ if path. extension ( ) == Some ( "rmeta" ) {
289
+ let file_stem = path. file_stem ( ) ?. strip_prefix ( "lib" ) ?;
290
+ let parent =
291
+ path. parent ( ) . map ( |p| p. as_std_path ( ) . to_path_buf ( ) ) . unwrap_or ( PathBuf :: new ( ) ) ;
292
+ let mut meta_path = parent. join ( file_stem) ;
293
+ meta_path. set_extension ( ArtifactType :: Metadata ) ;
294
+ trace ! ( rmeta=?path, kani_meta=?meta_path. display( ) , "map_kani_artifact" ) ;
295
+
296
+ // This will check if the file exists and we just skip if it doesn't.
297
+ Artifact :: try_new ( & meta_path, ArtifactType :: Metadata ) . ok ( )
298
+ } else if path. extension ( ) == Some ( "rlib" ) {
299
+ // We skip `rlib` files since we should also generate a `rmeta`.
300
+ trace ! ( rlib=?path, "map_kani_artifact" ) ;
301
+ None
302
+ } else {
303
+ // For all the other cases we write the path of the metadata into the output file.
304
+ // The compiler should always write a valid stub into the artifact file, however the
305
+ // kani-metadata file only exists if there were valid targets.
306
+ trace ! ( artifact=?path, "map_kani_artifact" ) ;
307
+ let input_file = File :: open ( path) . ok ( ) ?;
308
+ let stub: CompilerArtifactStub = serde_json:: from_reader ( input_file) . unwrap ( ) ;
309
+ Artifact :: try_new ( & stub. metadata_path , ArtifactType :: Metadata ) . ok ( )
310
+ }
311
+ } ) ;
312
+ debug ! ( ?result, "map_kani_artifact" ) ;
313
+ result
314
+ }
315
+
279
316
/// Possible verification targets.
280
317
enum VerificationTarget {
281
- Bin ( String ) ,
282
- Lib ,
283
- Test ( String ) ,
318
+ Bin ( Target ) ,
319
+ Lib ( Target ) ,
320
+ Test ( Target ) ,
284
321
}
285
322
286
323
impl VerificationTarget {
287
324
/// Convert to cargo argument that select the specific target.
288
325
fn to_args ( & self ) -> Vec < String > {
289
326
match self {
290
- VerificationTarget :: Test ( name) => vec ! [ String :: from( "--test" ) , name. clone( ) ] ,
291
- VerificationTarget :: Bin ( name) => vec ! [ String :: from( "--bin" ) , name. clone( ) ] ,
292
- VerificationTarget :: Lib => vec ! [ String :: from( "--lib" ) ] ,
327
+ VerificationTarget :: Test ( target) => vec ! [ String :: from( "--test" ) , target. name. clone( ) ] ,
328
+ VerificationTarget :: Bin ( target) => vec ! [ String :: from( "--bin" ) , target. name. clone( ) ] ,
329
+ VerificationTarget :: Lib ( _) => vec ! [ String :: from( "--lib" ) ] ,
330
+ }
331
+ }
332
+
333
+ fn target ( & self ) -> & Target {
334
+ match self {
335
+ VerificationTarget :: Test ( target)
336
+ | VerificationTarget :: Bin ( target)
337
+ | VerificationTarget :: Lib ( target) => target,
293
338
}
294
339
}
295
340
}
@@ -318,7 +363,7 @@ fn package_targets(args: &KaniArgs, package: &Package) -> Vec<VerificationTarget
318
363
match kind. as_str ( ) {
319
364
CRATE_TYPE_BIN => {
320
365
// Binary targets.
321
- verification_targets. push ( VerificationTarget :: Bin ( target. name . clone ( ) ) ) ;
366
+ verification_targets. push ( VerificationTarget :: Bin ( target. clone ( ) ) ) ;
322
367
}
323
368
CRATE_TYPE_LIB | CRATE_TYPE_RLIB | CRATE_TYPE_CDYLIB | CRATE_TYPE_DYLIB
324
369
| CRATE_TYPE_STATICLIB => {
@@ -331,7 +376,7 @@ fn package_targets(args: &KaniArgs, package: &Package) -> Vec<VerificationTarget
331
376
CRATE_TYPE_TEST => {
332
377
// Test target.
333
378
if args. tests {
334
- verification_targets. push ( VerificationTarget :: Test ( target. name . clone ( ) ) ) ;
379
+ verification_targets. push ( VerificationTarget :: Test ( target. clone ( ) ) ) ;
335
380
} else {
336
381
ignored_tests. push ( target. name . as_str ( ) ) ;
337
382
}
@@ -347,7 +392,7 @@ fn package_targets(args: &KaniArgs, package: &Package) -> Vec<VerificationTarget
347
392
`proc-macro`.",
348
393
target. name,
349
394
) ,
350
- ( true , false ) => verification_targets. push ( VerificationTarget :: Lib ) ,
395
+ ( true , false ) => verification_targets. push ( VerificationTarget :: Lib ( target . clone ( ) ) ) ,
351
396
( _, _) => { }
352
397
}
353
398
}
0 commit comments