@@ -9,10 +9,54 @@ use std::process::Command;
9
9
use toml:: value:: Table ;
10
10
use toml:: Value ;
11
11
12
- /// Produces a set of arguments to pass to ourself (cargo-kani) from a Cargo.toml project file
13
- pub fn config_toml_to_args ( ) -> Result < Vec < OsString > > {
12
+ /// Produce the list of arguments to pass to ourself (cargo-kani).
13
+ ///
14
+ /// The arguments passed via command line have precedence over the ones from the Cargo.toml.
15
+ pub fn join_args ( input_args : Vec < OsString > ) -> Result < Vec < OsString > > {
14
16
let file = std:: fs:: read_to_string ( cargo_locate_project ( ) ?) ?;
15
- toml_to_args ( & file)
17
+ let ( kani_args, cbmc_args) = toml_to_args ( & file) ?;
18
+ merge_args ( input_args, kani_args, cbmc_args)
19
+ }
20
+
21
+ /// Join the arguments passed via command line with the ones found in the Cargo.toml.
22
+ ///
23
+ /// The arguments passed via command line have precedence over the ones from the Cargo.toml. Thus,
24
+ /// we need to inject the command line arguments after the ones read from Cargo.toml. This can be
25
+ /// a bit annoying given that cbmc args have to be at the end of the arguments and the "--cbmc-args"
26
+ /// flag must only be included once.
27
+ ///
28
+ /// This function will return the arguments in the following order:
29
+ /// ```
30
+ /// <bin_name> [<cfg_kani_args>]* [<cmd_kani_args>]* [--cbmc-args [<cfg_cbmc_args>]* [<cmd_cbmc_args>]*]
31
+ /// ```
32
+ fn merge_args (
33
+ cmd_args : Vec < OsString > ,
34
+ cfg_kani_args : Vec < OsString > ,
35
+ cfg_cbmc_args : Vec < OsString > ,
36
+ ) -> Result < Vec < OsString > > {
37
+ let mut merged_args =
38
+ vec ! [ cmd_args. first( ) . expect( "Expected binary path as one argument" ) . clone( ) ] ;
39
+ merged_args. extend ( cfg_kani_args) ;
40
+ if cfg_cbmc_args. is_empty ( ) {
41
+ // No need to handle cbmc_args. Just merge the Cargo.toml args with the original input:
42
+ // [<config_kani_args>]* [input_args]*
43
+ merged_args. extend_from_slice ( & cmd_args[ 1 ..] ) ;
44
+ } else {
45
+ let cbmc_flag = cmd_args. iter ( ) . enumerate ( ) . find ( |& f| f. 1 . eq ( "--cbmc-args" . into ( ) ) ) ;
46
+ if let Some ( ( idx, _) ) = cbmc_flag {
47
+ // Both command line and config file have --cbmc-args. Merge them to be in order.
48
+ merged_args. extend_from_slice ( & cmd_args[ 1 ..idx] ) ;
49
+ merged_args. extend ( cfg_cbmc_args) ;
50
+ // Remove --cbmc-args from the input.
51
+ merged_args. extend_from_slice ( & cmd_args[ idx + 1 ..] ) ;
52
+ } else {
53
+ // Command line doesn't have --cbmc-args. Put command line arguments in the middle.
54
+ // [<cfg_kani_args>]* [<cmd_args>]* --cbmc-args [<cfg_cbmc_args>]+
55
+ merged_args. extend_from_slice ( & cmd_args[ 1 ..] ) ;
56
+ merged_args. extend ( cfg_cbmc_args) ;
57
+ }
58
+ }
59
+ Ok ( merged_args)
16
60
}
17
61
18
62
/// `locate-project` produces a response like: `/full/path/to/src/cargo-kani/Cargo.toml`
@@ -28,8 +72,9 @@ fn cargo_locate_project() -> Result<PathBuf> {
28
72
Ok ( path. trim ( ) . into ( ) )
29
73
}
30
74
31
- /// Parse a config toml string and extract the cargo-kani arguments we should try injecting
32
- fn toml_to_args ( tomldata : & str ) -> Result < Vec < OsString > > {
75
+ /// Parse a config toml string and extract the cargo-kani arguments we should try injecting.
76
+ /// This returns two different vectors since all cbmc-args have to be at the end.
77
+ fn toml_to_args ( tomldata : & str ) -> Result < ( Vec < OsString > , Vec < OsString > ) > {
33
78
let config = tomldata. parse :: < Value > ( ) ?;
34
79
// To make testing easier, our function contract is to produce a stable ordering of flags for a given input.
35
80
// Consequently, we use BTreeMap instead of HashMap here.
@@ -43,20 +88,18 @@ fn toml_to_args(tomldata: &str) -> Result<Vec<OsString>> {
43
88
}
44
89
45
90
let mut args = Vec :: new ( ) ;
46
- let mut suffixed_args = Vec :: new ( ) ;
91
+ let mut cbmc_args = Vec :: new ( ) ;
47
92
48
93
for ( flag, value) in map {
49
94
if flag == "cbmc-args" {
50
95
// --cbmc-args has to come last because it eats all remaining arguments
51
- insert_arg_from_toml ( & flag, & value, & mut suffixed_args ) ?;
96
+ insert_arg_from_toml ( & flag, & value, & mut cbmc_args ) ?;
52
97
} else {
53
98
insert_arg_from_toml ( & flag, & value, & mut args) ?;
54
99
}
55
100
}
56
101
57
- args. extend ( suffixed_args) ;
58
-
59
- Ok ( args)
102
+ Ok ( ( args, cbmc_args) )
60
103
}
61
104
62
105
/// Translates one toml entry (flag, value) into arguments and inserts it into `args`
@@ -113,6 +156,44 @@ mod tests {
113
156
let b = toml_to_args ( a) . unwrap ( ) ;
114
157
// default first, then unwind thanks to btree ordering.
115
158
// cbmc-args always last.
116
- assert_eq ! ( b, vec![ "--no-default-checks" , "--unwind" , "2" , "--cbmc-args" , "--fake" ] ) ;
159
+ assert_eq ! ( b. 0 , vec![ "--no-default-checks" , "--unwind" , "2" ] ) ;
160
+ assert_eq ! ( b. 1 , vec![ "--cbmc-args" , "--fake" ] ) ;
161
+ }
162
+
163
+ #[ test]
164
+ fn check_merge_args_with_only_command_line_args ( ) {
165
+ let cmd_args: Vec < OsString > =
166
+ [ "cargo_kani" , "--no-default-checks" , "--unwind" , "2" , "--cbmc-args" , "--fake" ]
167
+ . iter ( )
168
+ . map ( |& s| s. into ( ) )
169
+ . collect ( ) ;
170
+ let merged = merge_args ( cmd_args. clone ( ) , Vec :: new ( ) , Vec :: new ( ) ) . unwrap ( ) ;
171
+ assert_eq ! ( merged, cmd_args) ;
172
+ }
173
+
174
+ #[ test]
175
+ fn check_merge_args_with_only_config_kani_args ( ) {
176
+ let cfg_args: Vec < OsString > =
177
+ [ "--no-default-checks" , "--unwind" , "2" ] . iter ( ) . map ( |& s| s. into ( ) ) . collect ( ) ;
178
+ let merged = merge_args ( vec ! [ "kani" . into( ) ] , cfg_args. clone ( ) , Vec :: new ( ) ) . unwrap ( ) ;
179
+ assert_eq ! ( merged[ 0 ] , OsString :: from( "kani" ) ) ;
180
+ assert_eq ! ( merged[ 1 ..] , cfg_args) ;
181
+ }
182
+
183
+ #[ test]
184
+ fn check_merge_args_order ( ) {
185
+ let cmd_args: Vec < OsString > =
186
+ vec ! [ "kani" . into( ) , "--debug" . into( ) , "--cbmc-args" . into( ) , "--fake" . into( ) ] ;
187
+ let cfg_kani_args: Vec < OsString > = vec ! [ "--no-default-checks" . into( ) ] ;
188
+ let cfg_cbmc_args: Vec < OsString > = vec ! [ "--cbmc-args" . into( ) , "--trace" . into( ) ] ;
189
+ let merged =
190
+ merge_args ( cmd_args. clone ( ) , cfg_kani_args. clone ( ) , cfg_cbmc_args. clone ( ) ) . unwrap ( ) ;
191
+ assert_eq ! ( merged. len( ) , cmd_args. len( ) + cfg_kani_args. len( ) + cfg_cbmc_args. len( ) - 1 ) ;
192
+ assert_eq ! ( merged[ 0 ] , OsString :: from( "kani" ) ) ;
193
+ assert_eq ! ( merged[ 1 ] , OsString :: from( "--no-default-checks" ) ) ;
194
+ assert_eq ! ( merged[ 2 ] , OsString :: from( "--debug" ) ) ;
195
+ assert_eq ! ( merged[ 3 ] , OsString :: from( "--cbmc-args" ) ) ;
196
+ assert_eq ! ( merged[ 4 ] , OsString :: from( "--trace" ) ) ;
197
+ assert_eq ! ( merged[ 5 ] , OsString :: from( "--fake" ) ) ;
117
198
}
118
199
}
0 commit comments