@@ -25,6 +25,9 @@ use crate::{
25
25
tmp, PKG , VSN ,
26
26
} ;
27
27
28
+ pub ( crate ) const ENV_EXECUTE_BUILDRS : & str = "CARGOGREEN_EXECUTE_BUILDRS_" ;
29
+ const WRAP_BUILDRS : bool = true ; // FIXME: finish experiment
30
+
28
31
// NOTE: this RUSTC_WRAPPER program only ever gets called by `cargo`, so we save
29
32
// ourselves some trouble and assume std::path::{Path, PathBuf} are UTF-8.
30
33
@@ -112,6 +115,188 @@ async fn call_rustc(rustc: &str, args: Vec<String>) -> Result<()> {
112
115
Ok ( ( ) )
113
116
}
114
117
118
+ pub ( crate ) async fn exec_buildrs ( green : Green , exe : Utf8PathBuf ) -> Result < ( ) > {
119
+ assert ! ( env:: var_os( ENV ) . is_none( ) , "It's turtles all the way down!" ) ;
120
+ env:: set_var ( ENV , "1" ) ;
121
+
122
+ let krate_name = env:: var ( "CARGO_PKG_NAME" ) . expect ( "$CARGO_PKG_NAME" ) ;
123
+
124
+ let krate_version = env:: var ( "CARGO_PKG_VERSION" ) . expect ( "$CARGO_PKG_VERSION" ) ;
125
+
126
+ // exe: /target/release/build/proc-macro2-2f938e044e3f79bf/build-script-build
127
+ let Some ( ( previous_md_path, previous_extra) ) = || -> Option < _ > {
128
+ // name: build_script_build
129
+ let name = exe. file_name ( ) ?. replace ( '-' , "_" ) ;
130
+ // target_path: /target/release/build/proc-macro2-2f938e044e3f79bf
131
+ let target_path = exe. parent ( ) ?;
132
+ // extra: -2f938e044e3f79bf
133
+ let extra = target_path. file_name ( ) ?. trim_start_matches ( & krate_name) . to_owned ( ) ;
134
+ // target_path: /target/release
135
+ let target_path = target_path. parent ( ) ?. parent ( ) ?;
136
+ // /target/release/build_script_build-2f938e044e3f79bf.toml
137
+ Some ( ( target_path. join ( format ! ( "{name}{extra}.toml" ) ) , extra) )
138
+ } ( ) else {
139
+ bail ! ( "BUG: malformed buildrs exe {exe:?}" )
140
+ } ;
141
+
142
+ // $OUT_DIR: /target/release/build/proc-macro2-b97492fdd0201a99/out
143
+ let out_dir_var: Utf8PathBuf = env:: var ( "OUT_DIR" ) . expect ( "$OUT_DIR" ) . into ( ) ;
144
+ let Some ( ( md_path, extra) ) = || -> Option < _ > {
145
+ // name: proc-macro2-b97492fdd0201a99
146
+ let name = out_dir_var. parent ( ) ?. file_name ( ) ?;
147
+ // extra: -b97492fdd0201a99
148
+ let extra = name. trim_start_matches ( & krate_name) . to_owned ( ) ;
149
+ // /target/release/proc-macro2-b97492fdd0201a99.toml
150
+ Some ( ( previous_md_path. with_file_name ( format ! ( "{name}.toml" ) ) , extra) )
151
+ } ( ) else {
152
+ bail ! ( "BUG: malformed $OUT_DIR {out_dir_var:?}" )
153
+ } ;
154
+
155
+ let full_krate_id = format ! ( "Z {krate_name} {krate_version}{extra}" ) ;
156
+ logging:: setup ( & full_krate_id) ;
157
+
158
+ info ! ( "{PKG}@{VSN} original args: {exe:?} green={green:?}" ) ;
159
+
160
+ do_exec_buildrs (
161
+ green,
162
+ & krate_name,
163
+ krate_version,
164
+ full_krate_id. replace ( ' ' , "-" ) ,
165
+ out_dir_var,
166
+ exe,
167
+ previous_md_path,
168
+ previous_extra,
169
+ md_path,
170
+ extra,
171
+ )
172
+ . await
173
+ . inspect_err ( |e| error ! ( "Error: {e}" ) )
174
+ }
175
+
176
+ #[ expect( clippy:: too_many_arguments) ]
177
+ async fn do_exec_buildrs (
178
+ green : Green ,
179
+ krate_name : & str ,
180
+ krate_version : String ,
181
+ crate_id : String ,
182
+ out_dir_var : Utf8PathBuf ,
183
+ exe : Utf8PathBuf ,
184
+ previous_md_path : Utf8PathBuf ,
185
+ previous_extra : String ,
186
+ md_path : Utf8PathBuf ,
187
+ extra : String ,
188
+ ) -> Result < ( ) > {
189
+ let debug = maybe_log ( ) ;
190
+
191
+ let run_stage = Stage :: try_new ( format ! ( "run-{crate_id}" ) ) ?;
192
+ let out_stage = Stage :: try_new ( format ! ( "ran{extra}" ) ) ?;
193
+
194
+ let code_stage = Stage :: try_new ( format ! ( "cratesio-{krate_name}-{krate_version}" ) ) ?; // FIXME
195
+ let code_mount_src = "/extracted" ; //FIXME
196
+ let code_mount_dst = format ! ( "/home/pete/.cargo/registry/src/index.crates.io-0000000000000000/{krate_name}-{krate_version}" ) ; //FIXME
197
+
198
+ let previous_out_stage = Stage :: try_new ( format ! ( "out{previous_extra}" ) ) ?; //FIXME
199
+ let previous_out_dst = {
200
+ let name = exe. file_name ( ) . expect ( "PROOF: already ensured path has file_name" ) ;
201
+ let name = name. replacen ( '-' , "_" , 2 ) ;
202
+ format ! ( "/{name}{previous_extra}" )
203
+ } ;
204
+
205
+ let mut md = Md :: new ( & extra[ 1 ..] ) ; // Drops leading dash
206
+ md. push_block ( & RUST , green. image . base_image_inline . clone ( ) . unwrap ( ) ) ;
207
+
208
+ let mut run_block = String :: new ( ) ;
209
+ run_block. push_str ( & format ! ( "FROM {RST} AS {run_stage}\n " ) ) ;
210
+ run_block. push_str ( & format ! ( "SHELL {:?}\n " , [ "/bin/bash" , "-eux" , "-c" ] ) ) ;
211
+ run_block. push_str ( & format ! ( "WORKDIR {out_dir_var}\n " ) ) ;
212
+ run_block. push_str ( "RUN \\ \n " ) ;
213
+ run_block. push_str ( & format ! (
214
+ " --mount=from={code_stage},source={code_mount_src},dst={code_mount_dst} \\ \n "
215
+ ) ) ;
216
+ run_block. push_str ( & format ! (
217
+ " --mount=from={previous_out_stage},source={previous_out_dst},dst={exe} \\ \n "
218
+ ) ) ;
219
+ run_block. push_str ( & format ! ( " env CARGO={:?} \\ \n " , "$(which cargo)" ) ) ;
220
+ for ( var, val) in env:: vars ( ) . filter_map ( |kv| fmap_env ( kv, true ) ) {
221
+ run_block. push_str ( & format ! ( " {var}={val} \\ \n " ) ) ;
222
+ }
223
+ run_block. push_str ( & format ! ( " {ENV}=1 \\ \n " ) ) ;
224
+ for var in & green. set_envs {
225
+ if let Ok ( val) = env:: var ( var) {
226
+ // let val = replace_target_dir_str(&val, TARGET_DIR.as_str(), VIRTUAL_TARGET_DIR);
227
+ warn ! ( "passing ${var}={val:?} env through" ) ;
228
+ run_block. push_str ( & format ! ( " {var}={val:?} \\ \n " ) ) ;
229
+ }
230
+ }
231
+ run_block. push_str ( & format ! ( " {ENV_EXECUTE_BUILDRS}= \\ \n " ) ) ;
232
+ run_block. push_str ( & format ! ( " {exe} \\ \n " ) ) ;
233
+ run_block. push_str ( & format ! ( " 1> >(sed 's/^/{MARK_STDOUT}/') \\ \n " ) ) ;
234
+ run_block. push_str ( & format ! ( " 2> >(sed 's/^/{MARK_STDERR}/' >&2)\n " ) ) ;
235
+ md. push_block ( & run_stage, run_block) ;
236
+
237
+ let mut out_block = String :: new ( ) ;
238
+ out_block. push_str ( & format ! ( "FROM scratch AS {out_stage}\n " ) ) ;
239
+ out_block. push_str ( & format ! ( "COPY --from={run_stage} {out_dir_var}/*{extra}* /\n " ) ) ;
240
+ md. push_block ( & out_stage, out_block) ;
241
+
242
+ let containerfile_path = md_path. with_extension ( "Dockerfile" ) ;
243
+
244
+ let md = md; // Drop mut
245
+ md. write_to ( & md_path) ?;
246
+ drop ( md_path) ;
247
+
248
+ let previous_md = Md :: from_file ( & previous_md_path) ?;
249
+ if !previous_md. deps . is_empty ( ) {
250
+ //FIXME: read/import/topolosort .deps
251
+ panic ! ( ">>> {previous_md:?}" )
252
+ }
253
+ let blocks = md. block_along_with_predecessors ( & [ previous_md] ) ;
254
+
255
+ let mut containerfile = green. new_containerfile ( ) ;
256
+ containerfile. pushln ( md. rust_stage ( ) ) ;
257
+ containerfile. nl ( ) ;
258
+ containerfile. push ( & blocks) ;
259
+ containerfile. write_to ( & containerfile_path) ?;
260
+ drop ( containerfile) ;
261
+
262
+ fs:: create_dir_all ( & out_dir_var)
263
+ . map_err ( |e| anyhow ! ( "Failed to `mkdir -p {out_dir_var}`: {e}" ) ) ?;
264
+
265
+ let fallback = async move {
266
+ let mut cmd = Command :: new ( & exe) ;
267
+ let cmd = cmd. kill_on_drop ( true ) ;
268
+ // Do not unset ENV_EXECUTE_BUILDRS
269
+ let status = cmd
270
+ . spawn ( )
271
+ . map_err ( |e| anyhow ! ( "Failed to spawn {}: {e}" , cmd. show( ) ) ) ?
272
+ . wait ( )
273
+ . await
274
+ . map_err ( |e| anyhow ! ( "Failed to wait {}: {e}" , cmd. show( ) ) ) ?;
275
+ if !status. success ( ) {
276
+ bail ! ( "Failed in execute_buildrs" )
277
+ }
278
+ Ok ( ( ) )
279
+ } ;
280
+
281
+ if green. runner == Runner :: None {
282
+ info ! ( "Runner disabled, falling back..." ) ;
283
+ return fallback. await ;
284
+ }
285
+ let res = build_out ( & green, & containerfile_path, out_stage, & md. contexts , & out_dir_var) . await ;
286
+
287
+ if let Err ( e) = res {
288
+ warn ! ( "Falling back due to {e}" ) ;
289
+ if debug. is_none ( ) {
290
+ // Bubble up actual error & outputs
291
+ return fallback
292
+ . await
293
+ . inspect ( |( ) | eprintln ! ( "BUG: {PKG} should not have encountered this error: {e}" ) ) ;
294
+ }
295
+ return Err ( e) ;
296
+ }
297
+ Ok ( ( ) )
298
+ }
299
+
115
300
async fn wrap_rustc (
116
301
green : Green ,
117
302
crate_name : & str ,
@@ -364,12 +549,41 @@ async fn do_wrap_rustc(
364
549
rustc_block. push_str ( " { cat ./rustc-toolchain{,.toml} 2>/dev/null || true ; } && \\ \n " ) ;
365
550
//fixme? prefix with ::rustc-toolchain::
366
551
552
+ if WRAP_BUILDRS && buildrs {
553
+ // TODO: {extrafn} STDIO consts
554
+ // TODO: this won't work with e.g. tokio-decorated main fns (async + decorator needs duplicating)
555
+
556
+ rustc_block. push_str ( & format ! (
557
+ r#" {{ \
558
+ cat {input} | sed 's/fn main/fn actual{uniq}_main/' >{input}~ && mv {input}~ {input} ; \
559
+ {{ \
560
+ echo ; \
561
+ echo 'fn main() {{' ; \
562
+ echo ' use std::env::{{args_os, var_os}};' ; \
563
+ echo ' if var_os("{ENV_EXECUTE_BUILDRS}").is_none() {{' ; \
564
+ echo ' use std::process::{{Command, Stdio}};' ; \
565
+ echo ' let mut cmd = Command::new("{PKG}");' ; \
566
+ echo ' cmd.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit());' ; \
567
+ echo ' cmd.env("{ENV_EXECUTE_BUILDRS}", args_os().next().expect("{PKG}: getting buildrs arg0"));' ; \
568
+ echo ' let res = cmd.spawn().expect("{PKG}: spawning buildrs").wait().expect("{PKG}: running builds");' ; \
569
+ echo ' assert!(res.success());' ; \
570
+ echo ' }} else {{' ; \
571
+ echo ' actual{uniq}_main()' ; \
572
+ echo ' }}' ; \
573
+ echo '}}' ; \
574
+ }} >>{input} ; \
575
+ }} && \
576
+ "# ,
577
+ uniq = extrafn. replace( '-' , "_" ) ,
578
+ ) ) ;
579
+ }
580
+
367
581
rustc_block. push_str ( & format ! ( " env CARGO={:?} \\ \n " , "$(which cargo)" ) ) ;
368
582
369
583
for ( var, val) in env:: vars ( ) . filter_map ( |kv| fmap_env ( kv, buildrs) ) {
370
584
rustc_block. push_str ( & format ! ( " {var}={val} \\ \n " ) ) ;
371
585
}
372
- rustc_block. push_str ( " CARGOGREEN =1 \\ \n " ) ;
586
+ rustc_block. push_str ( & format ! ( " {ENV} =1 \\ \n " ) ) ;
373
587
// => cargo upstream issue "pass env vars read/wrote by build script on call to rustc"
374
588
// TODO whence https://github.com/rust-lang/cargo/issues/14444#issuecomment-2305891696
375
589
for var in & green. set_envs {
@@ -586,9 +800,22 @@ fn fmap_env((var, val): (String, String), buildrs: bool) -> Option<(String, Stri
586
800
debug ! ( "env is set: {var}={val}" ) ;
587
801
}
588
802
let val = match var. as_str ( ) {
803
+ "CARGO_PKG_DESCRIPTION" => "FIXME" . to_owned ( ) ,
589
804
"CARGO_MANIFEST_DIR" | "CARGO_MANIFEST_PATH" => {
590
805
rewrite_cratesio_index ( Utf8Path :: new ( & val) ) . to_string ( )
591
806
}
807
+ "TERM" => return None ,
808
+ "RUSTC" => "rustc" . to_owned ( ) , // Rewrite host rustc so the base_image one can be used
809
+ // "CARGO_TARGET_DIR" | "CARGO_BUILD_TARGET_DIR" => {
810
+ // virtual_target_dir(Utf8Path::new(&val)).to_string()
811
+ // }
812
+ // // TODO: a constant $CARGO_TARGET_DIR possible solution is to wrap build script as it runs,
813
+ // // ie. controlling all outputs. This should help: https://github.com/trailofbits/build-wrap/blob/d7f43b76e655e43755f68e28e9d729b4ed1dd115/src/wrapper.rs#L29
814
+ // //(dbcc)=> Dirty typenum v1.12.0: stale, https://github.com/rust-lang/cargo/blob/7987d4bfe683267ba179b42af55891badde3ccbf/src/cargo/core/compiler/fingerprint/mod.rs#L2030
815
+ // //=> /tmp/clis-dbcc_2-2-1/release/deps/typenum-32188cb0392f25b9.d
816
+ // "OUT_DIR" => virtual_target_dir(Utf8Path::new(&val)).to_string(),
817
+ "CARGO_TARGET_DIR" | "CARGO_BUILD_TARGET_DIR" => return None ,
818
+ "OUT_DIR" => val,
592
819
_ => val,
593
820
} ;
594
821
return Some ( ( var, val) ) ;
0 commit comments