2
2
// SPDX-License-Identifier: Apache-2.0 OR MIT
3
3
4
4
use crate :: args:: VerificationArgs ;
5
- use crate :: call_single_file:: to_rustc_arg;
5
+ use crate :: call_single_file:: { to_rustc_arg, LibConfig } ;
6
6
use crate :: project:: Artifact ;
7
- use crate :: session:: { setup_cargo_command, KaniSession } ;
7
+ use crate :: session:: { lib_folder , lib_no_core_folder , setup_cargo_command, KaniSession } ;
8
8
use crate :: util;
9
9
use anyhow:: { bail, Context , Result } ;
10
10
use cargo_metadata:: diagnostic:: { Diagnostic , DiagnosticLevel } ;
11
- use cargo_metadata:: { Message , Metadata , MetadataCommand , Package , Target } ;
11
+ use cargo_metadata:: {
12
+ Artifact as RustcArtifact , Message , Metadata , MetadataCommand , Package , Target ,
13
+ } ;
12
14
use kani_metadata:: { ArtifactType , CompilerArtifactStub } ;
13
15
use std:: ffi:: { OsStr , OsString } ;
14
16
use std:: fmt:: { self , Display } ;
15
17
use std:: fs:: { self , File } ;
16
18
use std:: io:: BufReader ;
17
19
use std:: io:: IsTerminal ;
18
- use std:: path:: PathBuf ;
20
+ use std:: path:: { Path , PathBuf } ;
19
21
use std:: process:: Command ;
20
22
use tracing:: { debug, trace} ;
21
23
@@ -43,6 +45,47 @@ pub struct CargoOutputs {
43
45
}
44
46
45
47
impl KaniSession {
48
+ /// Create a new cargo library in the given path.
49
+ pub fn cargo_init_lib ( & self , path : & Path ) -> Result < ( ) > {
50
+ let mut cmd = setup_cargo_command ( ) ?;
51
+ cmd. args ( [ "init" , "--lib" , path. to_string_lossy ( ) . as_ref ( ) ] ) ;
52
+ self . run_terminal ( cmd)
53
+ }
54
+
55
+ pub fn cargo_build_std ( & self , std_path : & Path , krate_path : & Path ) -> Result < Vec < Artifact > > {
56
+ let lib_path = lib_no_core_folder ( ) . unwrap ( ) ;
57
+ let mut rustc_args = self . kani_rustc_flags ( LibConfig :: new_no_core ( lib_path) ) ;
58
+ rustc_args. push ( to_rustc_arg ( self . kani_compiler_flags ( ) ) . into ( ) ) ;
59
+ rustc_args. push ( self . reachability_arg ( ) . into ( ) ) ;
60
+
61
+ let mut cargo_args: Vec < OsString > = vec ! [ "build" . into( ) ] ;
62
+ cargo_args. append ( & mut cargo_config_args ( ) ) ;
63
+
64
+ // Configuration needed to parse cargo compilation status.
65
+ cargo_args. push ( "--message-format" . into ( ) ) ;
66
+ cargo_args. push ( "json-diagnostic-rendered-ansi" . into ( ) ) ;
67
+ cargo_args. push ( "-Z" . into ( ) ) ;
68
+ cargo_args. push ( "build-std=panic_abort,core,std" . into ( ) ) ;
69
+
70
+ if self . args . common_args . verbose {
71
+ cargo_args. push ( "-v" . into ( ) ) ;
72
+ }
73
+
74
+ // Since we are verifying the standard library, we set the reachability to all crates.
75
+ let mut cmd = setup_cargo_command ( ) ?;
76
+ cmd. args ( & cargo_args)
77
+ . current_dir ( krate_path)
78
+ . env ( "RUSTC" , & self . kani_compiler )
79
+ // Use CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See
80
+ // https://doc.rust-lang.org/cargo/reference/environment-variables.html
81
+ . env ( "CARGO_ENCODED_RUSTFLAGS" , rustc_args. join ( OsStr :: new ( "\x1f " ) ) )
82
+ . env ( "CARGO_TERM_PROGRESS_WHEN" , "never" )
83
+ . env ( "__CARGO_TESTS_ONLY_SRC_ROOT" , std_path. as_os_str ( ) ) ;
84
+
85
+ let build_artifacts = self . run_build ( cmd) ?;
86
+ Ok ( build_artifacts. into_iter ( ) . filter_map ( map_kani_artifact) . collect ( ) )
87
+ }
88
+
46
89
/// Calls `cargo_build` to generate `*.symtab.json` files in `target_dir`
47
90
pub fn cargo_build ( & self , keep_going : bool ) -> Result < CargoOutputs > {
48
91
let build_target = env ! ( "TARGET" ) ; // see build.rs
@@ -60,7 +103,8 @@ impl KaniSession {
60
103
fs:: remove_dir_all ( & target_dir) ?;
61
104
}
62
105
63
- let mut rustc_args = self . kani_rustc_flags ( ) ;
106
+ let lib_path = lib_folder ( ) . unwrap ( ) ;
107
+ let mut rustc_args = self . kani_rustc_flags ( LibConfig :: new ( lib_path) ) ;
64
108
rustc_args. push ( to_rustc_arg ( self . kani_compiler_flags ( ) ) . into ( ) ) ;
65
109
66
110
let mut cargo_args: Vec < OsString > = vec ! [ "rustc" . into( ) ] ;
@@ -120,7 +164,7 @@ impl KaniSession {
120
164
. env ( "CARGO_ENCODED_RUSTFLAGS" , rustc_args. join ( OsStr :: new ( "\x1f " ) ) )
121
165
. env ( "CARGO_TERM_PROGRESS_WHEN" , "never" ) ;
122
166
123
- match self . run_cargo ( cmd, verification_target. target ( ) ) {
167
+ match self . run_build_target ( cmd, verification_target. target ( ) ) {
124
168
Err ( err) => {
125
169
if keep_going {
126
170
let target_str = format ! ( "{verification_target}" ) ;
@@ -179,9 +223,9 @@ impl KaniSession {
179
223
180
224
/// Run cargo and collect any error found.
181
225
/// We also collect the metadata file generated during compilation if any.
182
- fn run_cargo ( & self , cargo_cmd : Command , target : & Target ) -> Result < Option < Artifact > > {
226
+ fn run_build ( & self , cargo_cmd : Command ) -> Result < Vec < RustcArtifact > > {
183
227
let support_color = std:: io:: stdout ( ) . is_terminal ( ) ;
184
- let mut artifact = None ;
228
+ let mut artifacts = vec ! [ ] ;
185
229
if let Some ( mut cargo_process) = self . run_piped ( cargo_cmd) ? {
186
230
let reader = BufReader :: new ( cargo_process. stdout . take ( ) . unwrap ( ) ) ;
187
231
let mut error_count = 0 ;
@@ -211,33 +255,9 @@ impl KaniSession {
211
255
}
212
256
} ,
213
257
Message :: CompilerArtifact ( rustc_artifact) => {
214
- /// Compares two targets, and falls back to a weaker
215
- /// comparison where we avoid dashes in their names.
216
- fn same_target ( t1 : & Target , t2 : & Target ) -> bool {
217
- ( t1 == t2)
218
- || ( t1. name . replace ( '-' , "_" ) == t2. name . replace ( '-' , "_" )
219
- && t1. kind == t2. kind
220
- && t1. src_path == t2. src_path
221
- && t1. edition == t2. edition
222
- && t1. doctest == t2. doctest
223
- && t1. test == t2. test
224
- && t1. doc == t2. doc )
225
- }
226
- // This used to be `rustc_artifact == *target`, but it
227
- // started to fail after the `cargo` change in
228
- // <https://github.com/rust-lang/cargo/pull/12783>
229
- //
230
- // We should revisit this check after a while to see if
231
- // it's not needed anymore or it can be restricted to
232
- // certain cases.
233
- // TODO: <https://github.com/model-checking/kani/issues/3111>
234
- if same_target ( & rustc_artifact. target , target) {
235
- debug_assert ! (
236
- artifact. is_none( ) ,
237
- "expected only one artifact for `{target:?}`" ,
238
- ) ;
239
- artifact = Some ( rustc_artifact) ;
240
- }
258
+ // Compares two targets, and falls back to a weaker
259
+ // comparison where we avoid dashes in their names.
260
+ artifacts. push ( rustc_artifact)
241
261
}
242
262
Message :: BuildScriptExecuted ( _) | Message :: BuildFinished ( _) => {
243
263
// do nothing
@@ -263,11 +283,40 @@ impl KaniSession {
263
283
) ;
264
284
}
265
285
}
286
+ Ok ( artifacts)
287
+ }
288
+
289
+ /// Run cargo and collect any error found.
290
+ /// We also collect the metadata file generated during compilation if any for the given target.
291
+ fn run_build_target ( & self , cargo_cmd : Command , target : & Target ) -> Result < Option < Artifact > > {
292
+ /// This used to be `rustc_artifact == *target`, but it
293
+ /// started to fail after the `cargo` change in
294
+ /// <https://github.com/rust-lang/cargo/pull/12783>
295
+ ///
296
+ /// We should revisit this check after a while to see if
297
+ /// it's not needed anymore or it can be restricted to
298
+ /// certain cases.
299
+ /// TODO: <https://github.com/model-checking/kani/issues/3111>
300
+ fn same_target ( t1 : & Target , t2 : & Target ) -> bool {
301
+ ( t1 == t2)
302
+ || ( t1. name . replace ( '-' , "_" ) == t2. name . replace ( '-' , "_" )
303
+ && t1. kind == t2. kind
304
+ && t1. src_path == t2. src_path
305
+ && t1. edition == t2. edition
306
+ && t1. doctest == t2. doctest
307
+ && t1. test == t2. test
308
+ && t1. doc == t2. doc )
309
+ }
310
+
311
+ let artifacts = self . run_build ( cargo_cmd) ?;
312
+ debug ! ( ?artifacts, "run_build_target" ) ;
313
+
266
314
// We generate kani specific artifacts only for the build target. The build target is
267
315
// always the last artifact generated in a build, and all the other artifacts are related
268
- // to dependencies or build scripts. Hence, we need to invoke `map_kani_artifact` only
269
- // for the last compiler artifact.
270
- Ok ( artifact. and_then ( map_kani_artifact) )
316
+ // to dependencies or build scripts.
317
+ Ok ( artifacts. into_iter ( ) . rev ( ) . find_map ( |artifact| {
318
+ if same_target ( & artifact. target , target) { map_kani_artifact ( artifact) } else { None }
319
+ } ) )
271
320
}
272
321
}
273
322
0 commit comments