14
14
15
15
#![ warn( clippy:: all, clippy:: cargo) ]
16
16
17
+ mod cmd;
18
+ mod setup;
19
+
17
20
use std:: env;
18
21
use std:: ffi:: OsString ;
19
22
use std:: os:: unix:: prelude:: CommandExt ;
20
- use std:: path:: { Path , PathBuf } ;
23
+ use std:: path:: PathBuf ;
21
24
use std:: process:: Command ;
22
25
23
26
use anyhow:: { bail, Context , Result } ;
24
27
25
- /// Comes from our Cargo.toml manifest file. Must correspond to our release verion.
26
- const VERSION : & str = env ! ( "CARGO_PKG_VERSION" ) ;
27
- /// Set by our `build.rs`, reflects the Rust target triple we're building for
28
- const TARGET : & str = env ! ( "TARGET" ) ;
29
-
30
- /// Typically `~/.kani/kani-1.x/`
31
- fn kani_dir ( ) -> PathBuf {
32
- home:: home_dir ( )
33
- . expect ( "Couldn't find home dir?" )
34
- . join ( ".kani" )
35
- . join ( format ! ( "kani-{}" , VERSION ) )
36
- }
37
-
38
- /// The filename of the release bundle
39
- fn download_filename ( ) -> String {
40
- format ! ( "kani-{}-{}.tar.gz" , VERSION , TARGET )
41
- }
42
-
43
- /// Helper to find the download URL for this version of Kani
44
- fn download_url ( ) -> String {
45
- let tag: & str = & format ! ( "kani-{}" , VERSION ) ;
46
- let file: & str = & download_filename ( ) ;
47
- format ! ( "https://github.com/model-checking/kani/releases/download/{}/{}" , tag, file)
48
- }
49
-
50
28
/// Effectively the entry point (i.e. `main` function) for both our proxy binaries.
29
+ /// `bin` should be either `kani` or `cargo-kani`
51
30
pub fn proxy ( bin : & str ) -> Result < ( ) > {
52
31
// In an effort to keep our dependencies minimal, we do the bare minimum argument parsing
53
- let args: Vec < _ > = std :: env:: args_os ( ) . collect ( ) ;
32
+ let args: Vec < _ > = env:: args_os ( ) . collect ( ) ;
54
33
if args. len ( ) >= 2 && args[ 1 ] == "setup" {
55
34
if args. len ( ) >= 4 && args[ 2 ] == "--use-local-bundle" {
56
- setup ( Some ( args[ 3 ] . clone ( ) ) )
35
+ setup:: setup ( Some ( args[ 3 ] . clone ( ) ) )
57
36
} else {
58
- setup ( None )
37
+ setup:: setup ( None )
59
38
}
60
39
} else {
61
40
fail_if_in_dev_environment ( ) ?;
62
- if !appears_setup ( ) {
63
- setup ( None ) ?;
41
+ if !setup :: appears_setup ( ) {
42
+ setup:: setup ( None ) ?;
64
43
}
65
44
exec ( bin)
66
45
}
67
46
}
68
47
69
- /// Fast check to see if we look setup already
70
- fn appears_setup ( ) -> bool {
71
- kani_dir ( ) . exists ( )
72
- }
73
-
74
48
/// In dev environments, this proxy shouldn't be used.
75
49
/// But accidentally using it (with the test suite) can fire off
76
50
/// hundreds of HTTP requests trying to download a non-existent release bundle.
@@ -90,157 +64,31 @@ fn fail_if_in_dev_environment() -> Result<()> {
90
64
Ok ( ( ) )
91
65
}
92
66
93
- /// Give users a better error message than "404" if we're on an unsupported platform.
94
- fn fail_if_unsupported_target ( ) -> Result < ( ) > {
95
- // This is basically going to be reduced to a compile-time constant
96
- match TARGET {
97
- "x86_64-unknown-linux-gnu" | "x86_64-apple-darwin" => Ok ( ( ) ) ,
98
- _ => bail ! ( "Kani does not support this platform (Rust target {})" , TARGET ) ,
99
- }
100
- }
101
-
102
- /// Sets up Kani by unpacking/installing to `~/.kani/kani-VERSION`
103
- fn setup ( use_local_bundle : Option < OsString > ) -> Result < ( ) > {
104
- let kani_dir = kani_dir ( ) ;
105
- // e.g. `~/.kani/`
106
- let base_dir = kani_dir. parent ( ) . expect ( "No base directory?" ) ;
107
-
108
- println ! ( "[0/6] Running Kani first-time setup..." ) ;
109
-
110
- println ! ( "[1/6] Ensuring the existence of: {}" , base_dir. display( ) ) ;
111
- std:: fs:: create_dir_all ( & base_dir) ?;
112
-
113
- if let Some ( pathstr) = use_local_bundle {
114
- println ! ( "[2/6] Installing local Kani bundle: {}" , pathstr. to_string_lossy( ) ) ;
115
- let path = Path :: new ( & pathstr) . canonicalize ( ) ?;
116
- // When given a local bundle, it's often "-latest" but we expect "-1.0" or something.
117
- // tar supports "stripping" the first directory from the bundle, so do that and
118
- // extract it directly into the expected (kani_dir) directory (instead of base_dir).
119
- if !kani_dir. exists ( ) {
120
- std:: fs:: create_dir ( & kani_dir) ?;
121
- }
122
- Command :: new ( "tar" )
123
- . arg ( "--strip-components=1" )
124
- . arg ( "-zxf" )
125
- . arg ( & path)
126
- . current_dir ( & kani_dir)
127
- . run ( ) ?;
128
- } else {
129
- let filename = download_filename ( ) ;
130
- println ! ( "[2/6] Downloading Kani release bundle: {}" , & filename) ;
131
- fail_if_unsupported_target ( ) ?;
132
- let bundle = base_dir. join ( filename) ;
133
- Command :: new ( "curl" )
134
- . args ( & [ "-sSLf" , "-o" ] )
135
- . arg ( & bundle)
136
- . arg ( download_url ( ) )
137
- . run ( )
138
- . context ( "Failed to download Kani release bundle" ) ?;
139
-
140
- Command :: new ( "tar" ) . arg ( "zxf" ) . arg ( & bundle) . current_dir ( base_dir) . run ( ) ?;
141
-
142
- std:: fs:: remove_file ( bundle) ?;
143
- }
144
-
145
- let toolchain_version = std:: fs:: read_to_string ( kani_dir. join ( "rust-toolchain-version" ) )
146
- . context ( "Reading release bundle rust-toolchain-version" ) ?;
147
- println ! ( "[3/6] Installing rust toolchain version: {}" , & toolchain_version) ;
148
- Command :: new ( "rustup" ) . args ( & [ "toolchain" , "install" , & toolchain_version] ) . run ( ) ?;
149
-
150
- let toolchain = home:: rustup_home ( ) ?. join ( "toolchains" ) . join ( & toolchain_version) ;
151
-
152
- symlink_rust_toolchain ( & toolchain, & kani_dir) ?;
153
-
154
- println ! ( "[4/6] Installing Kani python dependencies..." ) ;
155
- let pyroot = kani_dir. join ( "pyroot" ) ;
156
-
157
- // TODO: this is a repetition of versions from elsewhere
158
- Command :: new ( "python3" )
159
- . args ( & [ "-m" , "pip" , "install" , "cbmc-viewer==3.2" , "--target" ] )
160
- . arg ( & pyroot)
161
- . run ( ) ?;
162
- Command :: new ( "python3" )
163
- . args ( & [ "-m" , "pip" , "install" , "colorama==0.4.3" , "--target" ] )
164
- . arg ( & pyroot)
165
- . run ( ) ?;
166
-
167
- println ! ( "[5/6] Building Kani library prelude..." ) ;
168
- // We need a workspace to build them in, otherwise repeated builds generate different hashes and `kani` can't find `kani_macros`
169
- let contents = "[workspace]\n members = [\" kani\" ,\" kani_macros\" ,\" std\" ]" ;
170
- std:: fs:: write ( kani_dir. join ( "library" ) . join ( "Cargo.toml" ) , contents) ?;
171
-
172
- // A little helper for invoking Cargo repeatedly here
173
- let cargo = |crate_name : & str | -> Result < ( ) > {
174
- let manifest = format ! ( "library/{}/Cargo.toml" , crate_name) ;
175
- Command :: new ( "cargo" )
176
- . args ( & [
177
- & format ! ( "+{}" , toolchain_version) ,
178
- "build" ,
179
- "-Z" ,
180
- "unstable-options" ,
181
- "--manifest-path" ,
182
- & manifest,
183
- "--out-dir" ,
184
- "lib" ,
185
- "--target-dir" ,
186
- "target" ,
187
- ] )
188
- . current_dir ( & kani_dir)
189
- // https://doc.rust-lang.org/cargo/reference/environment-variables.html
190
- . env ( "CARGO_ENCODED_RUSTFLAGS" , "--cfg=kani" )
191
- . run ( )
192
- . with_context ( || format ! ( "Failed to build Kani prelude library {}" , crate_name) )
193
- } ;
194
-
195
- // We seem to need 3 invocations because of the behavior of the `--out-dir` flag.
196
- // It only seems to produce the requested artifact, not its dependencies.
197
- cargo ( "kani" ) ?;
198
- cargo ( "kani_macros" ) ?;
199
- cargo ( "std" ) ?;
200
-
201
- std:: fs:: remove_dir_all ( kani_dir. join ( "target" ) ) ?;
202
-
203
- println ! ( "[6/6] Successfully completed Kani first-time setup." ) ;
204
-
205
- Ok ( ( ) )
206
- }
207
-
208
- /// Creates a `kani_dir/toolchain` symlink pointing to `toolchain`.
209
- fn symlink_rust_toolchain ( toolchain : & Path , kani_dir : & Path ) -> Result < ( ) > {
210
- let path = kani_dir. join ( "toolchain" ) ;
211
- // We want to be idempotent, so if the symlink already exists, delete it first
212
- if path. exists ( ) && path. is_symlink ( ) {
213
- std:: fs:: remove_file ( & path) ?;
214
- }
215
- std:: os:: unix:: fs:: symlink ( toolchain, path) ?;
216
- Ok ( ( ) )
217
- }
218
-
219
67
/// Executes `kani-driver` in `bin` mode (kani or cargo-kani)
220
68
/// augmenting environment variables to accomodate our release environment
221
69
fn exec ( bin : & str ) -> Result < ( ) > {
222
- let kani_dir = kani_dir ( ) ;
70
+ let kani_dir = setup :: kani_dir ( ) ;
223
71
let program = kani_dir. join ( "bin" ) . join ( "kani-driver" ) ;
224
72
let pyroot = kani_dir. join ( "pyroot" ) ;
225
73
let bin_kani = kani_dir. join ( "bin" ) ;
226
74
let bin_pyroot = pyroot. join ( "bin" ) ;
227
75
let bin_toolchain = kani_dir. join ( "toolchain" ) . join ( "bin" ) ;
228
76
229
77
// Allow python scripts to find dependencies under our pyroot
230
- let pythonpath = augment_search ( & [ pyroot] , env:: var_os ( "PYTHONPATH" ) ) ?;
78
+ let pythonpath = prepend_search_path ( & [ pyroot] , env:: var_os ( "PYTHONPATH" ) ) ?;
231
79
// Add: kani, cbmc, viewer (pyroot), and our rust toolchain directly to our PATH
232
- let path = augment_search ( & [ bin_kani, bin_pyroot, bin_toolchain] , env:: var_os ( "PATH" ) ) ?;
80
+ let path = prepend_search_path ( & [ bin_kani, bin_pyroot, bin_toolchain] , env:: var_os ( "PATH" ) ) ?;
233
81
234
82
let mut cmd = Command :: new ( program) ;
235
- cmd. args ( std :: env:: args_os ( ) . skip ( 1 ) ) . env ( "PYTHONPATH" , pythonpath) . env ( "PATH" , path) . arg0 ( bin) ;
83
+ cmd. args ( env:: args_os ( ) . skip ( 1 ) ) . env ( "PYTHONPATH" , pythonpath) . env ( "PATH" , path) . arg0 ( bin) ;
236
84
237
85
let result = cmd. status ( ) . context ( "Failed to invoke kani-driver" ) ?;
238
86
239
87
std:: process:: exit ( result. code ( ) . expect ( "No exit code?" ) ) ;
240
88
}
241
89
242
- /// Prepend paths to an environment variable
243
- fn augment_search ( paths : & [ PathBuf ] , original : Option < OsString > ) -> Result < OsString > {
90
+ /// Prepend paths to an environment variable search string like PATH
91
+ fn prepend_search_path ( paths : & [ PathBuf ] , original : Option < OsString > ) -> Result < OsString > {
244
92
match original {
245
93
None => Ok ( env:: join_paths ( paths) ?) ,
246
94
Some ( original) => {
@@ -250,53 +98,3 @@ fn augment_search(paths: &[PathBuf], original: Option<OsString>) -> Result<OsStr
250
98
}
251
99
}
252
100
}
253
-
254
- /// Helper trait to fallibly run commands
255
- trait AutoRun {
256
- fn run ( & mut self ) -> Result < ( ) > ;
257
- }
258
- impl AutoRun for Command {
259
- fn run ( & mut self ) -> Result < ( ) > {
260
- // This can sometimes fail during the set-up of the forked process before exec,
261
- // for example by setting `current_dir` to a directory that does not exist.
262
- let status = self . status ( ) . with_context ( || {
263
- format ! (
264
- "Internal failure before invoking command: {}" ,
265
- render_command( self ) . to_string_lossy( )
266
- )
267
- } ) ?;
268
- if !status. success ( ) {
269
- bail ! ( "Failed command: {}" , render_command( self ) . to_string_lossy( ) ) ;
270
- }
271
- Ok ( ( ) )
272
- }
273
- }
274
-
275
- /// Render a Command as a string, to log it
276
- pub fn render_command ( cmd : & Command ) -> OsString {
277
- let mut str = OsString :: new ( ) ;
278
-
279
- for ( k, v) in cmd. get_envs ( ) {
280
- if let Some ( v) = v {
281
- str. push ( k) ;
282
- str. push ( "=\" " ) ;
283
- str. push ( v) ;
284
- str. push ( "\" " ) ;
285
- }
286
- }
287
-
288
- str. push ( cmd. get_program ( ) ) ;
289
-
290
- for a in cmd. get_args ( ) {
291
- str. push ( " " ) ;
292
- if a. to_string_lossy ( ) . contains ( ' ' ) {
293
- str. push ( "\" " ) ;
294
- str. push ( a) ;
295
- str. push ( "\" " ) ;
296
- } else {
297
- str. push ( a) ;
298
- }
299
- }
300
-
301
- str
302
- }
0 commit comments