Make sure tempfiles don't get dropped too early

This commit is contained in:
Richard Feldman 2022-11-22 20:22:08 -05:00
parent 6b446fe592
commit 0b73ea69af
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
6 changed files with 46 additions and 25 deletions

View file

@ -14,7 +14,6 @@ use roc_target::TargetInfo;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{path::PathBuf, thread::JoinHandle}; use std::{path::PathBuf, thread::JoinHandle};
use target_lexicon::Triple; use target_lexicon::Triple;
use tempfile::Builder;
fn report_timing(buf: &mut String, label: &str, duration: Duration) { fn report_timing(buf: &mut String, label: &str, duration: Duration) {
use std::fmt::Write; use std::fmt::Write;
@ -328,7 +327,7 @@ pub fn build_file<'a>(
problems problems
} }
(LinkingStrategy::Legacy, _) => { (LinkingStrategy::Legacy, _) => {
let app_o_file = Builder::new() let app_o_file = tempfile::Builder::new()
.prefix("roc_app") .prefix("roc_app")
.suffix(&format!(".{}", app_extension)) .suffix(&format!(".{}", app_extension))
.tempfile() .tempfile()
@ -342,27 +341,31 @@ pub fn build_file<'a>(
app_o_file.to_str().unwrap(), app_o_file.to_str().unwrap(),
]; ];
let builtins_host_file = tempfile::Builder::new() let builtins_host_tempfile = tempfile::Builder::new()
.prefix("host_bitcode") .prefix("host_bitcode")
.suffix(".o") .suffix(".o")
.rand_bytes(5) .rand_bytes(5)
.tempfile() .tempfile()
.unwrap(); .unwrap();
std::fs::write(builtins_host_file.path(), bitcode::HOST_UNIX) std::fs::write(builtins_host_tempfile.path(), bitcode::HOST_UNIX)
.expect("failed to write host builtins object to tempfile"); .expect("failed to write host builtins object to tempfile");
if matches!(code_gen_options.backend, program::CodeGenBackend::Assembly) { if matches!(code_gen_options.backend, program::CodeGenBackend::Assembly) {
inputs.push(builtins_host_file.path().to_str().unwrap()); inputs.push(builtins_host_tempfile.path().to_str().unwrap());
} }
let (mut child, _) = // TODO use lld let (mut child, _) = // TODO use lld
link(target, binary_path.clone(), &inputs, link_type) link(target, binary_path.clone(), &inputs, link_type)
.map_err(|_| todo!("gracefully handle `ld` failing to spawn."))?; .map_err(|_| todo!("gracefully handle `ld` failing to spawn."))?;
let exit_status = child let exit_status = child
.wait() .wait()
.map_err(|_| todo!("gracefully handle error after `ld` spawned"))?; .map_err(|_| todo!("gracefully handle error after `ld` spawned"))?;
// Extend the lifetime of the tempfile so it doesn't get dropped
// (and thus deleted) before the child process is done using it!
let _ = builtins_host_tempfile;
if exit_status.success() { if exit_status.success() {
problems problems
} else { } else {

View file

@ -1417,19 +1417,19 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P
(but seems to be an unofficial API) (but seems to be an unofficial API)
*/ */
let builtins_host_file = tempfile::Builder::new() let builtins_host_tempfile = tempfile::Builder::new()
.prefix("host_bitcode") .prefix("host_bitcode")
.suffix(".wasm") .suffix(".wasm")
.rand_bytes(5) .rand_bytes(5)
.tempfile() .tempfile()
.unwrap(); .unwrap();
std::fs::write(builtins_host_file.path(), bitcode::HOST_WASM) std::fs::write(builtins_host_tempfile.path(), bitcode::HOST_WASM)
.expect("failed to write host builtins object to tempfile"); .expect("failed to write host builtins object to tempfile");
let mut zig_cmd = zig(); let mut zig_cmd = zig();
let args = &[ let args = &[
"wasm-ld", "wasm-ld",
builtins_host_file.path().to_str().unwrap(), builtins_host_tempfile.path().to_str().unwrap(),
host_input, host_input,
WASI_LIBC_PATH, WASI_LIBC_PATH,
WASI_COMPILER_RT_PATH, // builtins need __multi3, __udivti3, __fixdfti WASI_COMPILER_RT_PATH, // builtins need __multi3, __udivti3, __fixdfti
@ -1446,7 +1446,11 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P
// println!("\npreprocess_host_wasm32"); // println!("\npreprocess_host_wasm32");
// println!("zig {}\n", args.join(" ")); // println!("zig {}\n", args.join(" "));
run_build_command(zig_cmd, output_file, 0) run_build_command(zig_cmd, output_file, 0);
// Extend the lifetime of the tempfile so it doesn't get dropped
// (and thus deleted) before the Zig process is done using it!
let _ = builtins_host_tempfile;
} }
fn run_build_command(mut command: Command, file_to_build: &str, flaky_fail_counter: usize) { fn run_build_command(mut command: Command, file_to_build: &str, flaky_fail_counter: usize) {

View file

@ -100,18 +100,18 @@ fn build_wasm_test_host() {
let mut outfile = PathBuf::from(&out_dir).join(PLATFORM_FILENAME); let mut outfile = PathBuf::from(&out_dir).join(PLATFORM_FILENAME);
outfile.set_extension("wasm"); outfile.set_extension("wasm");
let builtins_host_file = tempfile::Builder::new() let builtins_host_tempfile = tempfile::Builder::new()
.prefix("host_bitcode") .prefix("host_bitcode")
.suffix(".wasm") .suffix(".wasm")
.rand_bytes(5) .rand_bytes(5)
.tempfile() .tempfile()
.unwrap(); .unwrap();
std::fs::write(builtins_host_file.path(), bitcode::HOST_WASM) std::fs::write(builtins_host_tempfile.path(), bitcode::HOST_WASM)
.expect("failed to write host builtins object to tempfile"); .expect("failed to write host builtins object to tempfile");
run_zig(&[ run_zig(&[
"wasm-ld", "wasm-ld",
builtins_host_file.path().to_str().unwrap(), builtins_host_tempfile.path().to_str().unwrap(),
platform_path.to_str().unwrap(), platform_path.to_str().unwrap(),
WASI_COMPILER_RT_PATH, WASI_COMPILER_RT_PATH,
WASI_LIBC_PATH, WASI_LIBC_PATH,
@ -120,6 +120,10 @@ fn build_wasm_test_host() {
"--no-entry", "--no-entry",
"--relocatable", "--relocatable",
]); ]);
// Extend the lifetime of the tempfile so it doesn't get dropped
// (and thus deleted) before the Zig process is done using it!
let _ = builtins_host_tempfile;
} }
fn build_wasm_platform(out_dir: &str, source_path: &str) -> PathBuf { fn build_wasm_platform(out_dir: &str, source_path: &str) -> PathBuf {

View file

@ -193,13 +193,13 @@ pub fn helper(
.expect("failed to build output object"); .expect("failed to build output object");
std::fs::write(&app_o_file, module_out).expect("failed to write object to file"); std::fs::write(&app_o_file, module_out).expect("failed to write object to file");
let builtins_host_file = tempfile::Builder::new() let builtins_host_tempfile = tempfile::Builder::new()
.prefix("host_bitcode") .prefix("host_bitcode")
.suffix(".o") .suffix(".o")
.rand_bytes(5) .rand_bytes(5)
.tempfile() .tempfile()
.unwrap(); .unwrap();
std::fs::write(builtins_host_file.path(), bitcode::HOST_UNIX) std::fs::write(builtins_host_tempfile.path(), bitcode::HOST_UNIX)
.expect("failed to write host builtins object to tempfile"); .expect("failed to write host builtins object to tempfile");
let (mut child, dylib_path) = link( let (mut child, dylib_path) = link(
@ -209,7 +209,7 @@ pub fn helper(
// With the current method all methods are kept and it adds about 100k to all outputs. // With the current method all methods are kept and it adds about 100k to all outputs.
&[ &[
app_o_file.to_str().unwrap(), app_o_file.to_str().unwrap(),
builtins_host_file.path().to_str().unwrap(), builtins_host_tempfile.path().to_str().unwrap(),
], ],
LinkType::Dylib, LinkType::Dylib,
) )
@ -217,6 +217,10 @@ pub fn helper(
child.wait().unwrap(); child.wait().unwrap();
// Extend the lifetime of the tempfile so it doesn't get dropped
// (and thus deleted) before the linking process is done using it!
let _ = builtins_host_tempfile;
// Load the dylib // Load the dylib
let path = dylib_path.as_path().to_str().unwrap(); let path = dylib_path.as_path().to_str().unwrap();

View file

@ -4,19 +4,17 @@ use roc_error_macros::internal_error;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use target_lexicon::Triple; use target_lexicon::Triple;
use tempfile::Builder;
// TODO: Eventually do this from scratch and in memory instead of with ld. // TODO: Eventually do this from scratch and in memory instead of with ld.
pub fn create_dylib_macho( pub fn create_dylib_macho(
custom_names: &[String], custom_names: &[String],
triple: &Triple, triple: &Triple,
) -> object::read::Result<Vec<u8>> { ) -> object::read::Result<Vec<u8>> {
let dummy_obj_file = Builder::new() let dummy_obj_file = tempfile::Builder::new()
.prefix("roc_lib") .prefix("roc_lib")
.suffix(".o") .suffix(".o")
.tempfile() .tempfile()
.unwrap_or_else(|e| internal_error!("{}", e)); .unwrap_or_else(|e| internal_error!("{}", e));
let dummy_obj_file = dummy_obj_file.path();
let tmp = tempfile::tempdir().unwrap_or_else(|e| internal_error!("{}", e)); let tmp = tempfile::tempdir().unwrap_or_else(|e| internal_error!("{}", e));
let dummy_lib_file = tmp.path().to_path_buf().with_file_name("libapp.so"); let dummy_lib_file = tmp.path().to_path_buf().with_file_name("libapp.so");
@ -47,7 +45,7 @@ pub fn create_dylib_macho(
} }
std::fs::write( std::fs::write(
dummy_obj_file, dummy_obj_file.path(),
out_object.write().expect("failed to build output object"), out_object.write().expect("failed to build output object"),
) )
.expect("failed to write object to file"); .expect("failed to write object to file");
@ -70,13 +68,17 @@ pub fn create_dylib_macho(
.args([ .args([
ld_flag_soname, ld_flag_soname,
dummy_lib_file.file_name().unwrap().to_str().unwrap(), dummy_lib_file.file_name().unwrap().to_str().unwrap(),
dummy_obj_file.to_str().unwrap(), dummy_obj_file.path().to_str().unwrap(),
"-o", "-o",
dummy_lib_file.to_str().unwrap(), dummy_lib_file.to_str().unwrap(),
]) ])
.output() .output()
.unwrap(); .unwrap();
// Extend the lifetime of the tempfile so it doesn't get dropped
// (and thus deleted) before the linker process is done using it!
let _ = dummy_obj_file;
if !output.status.success() { if !output.status.success() {
match std::str::from_utf8(&output.stderr) { match std::str::from_utf8(&output.stderr) {
Ok(stderr) => panic!( Ok(stderr) => panic!(

View file

@ -23,19 +23,19 @@ fn main() {
pre_linked_binary_path.extend(["pre_linked_binary"]); pre_linked_binary_path.extend(["pre_linked_binary"]);
pre_linked_binary_path.set_extension("o"); pre_linked_binary_path.set_extension("o");
let builtins_host_file = tempfile::Builder::new() let builtins_host_tempfile = tempfile::Builder::new()
.prefix("host_bitcode") .prefix("host_bitcode")
.suffix(".wasm") .suffix(".wasm")
.rand_bytes(5) .rand_bytes(5)
.tempfile() .tempfile()
.unwrap(); .unwrap();
std::fs::write(builtins_host_file.path(), bitcode::HOST_WASM) std::fs::write(builtins_host_tempfile.path(), bitcode::HOST_WASM)
.expect("failed to write host builtins object to tempfile"); .expect("failed to write host builtins object to tempfile");
let output = Command::new(&zig_executable()) let output = Command::new(&zig_executable())
.args([ .args([
"wasm-ld", "wasm-ld",
builtins_host_file.path().to_str().unwrap(), builtins_host_tempfile.path().to_str().unwrap(),
platform_obj.to_str().unwrap(), platform_obj.to_str().unwrap(),
WASI_COMPILER_RT_PATH, WASI_COMPILER_RT_PATH,
WASI_LIBC_PATH, WASI_LIBC_PATH,
@ -48,6 +48,10 @@ fn main() {
.output() .output()
.unwrap(); .unwrap();
// Extend the lifetime of the tempfile so it doesn't get dropped
// (and thus deleted) before the Zig process is done using it!
let _ = builtins_host_tempfile;
assert!(output.status.success(), "{:#?}", output); assert!(output.status.success(), "{:#?}", output);
assert!(output.stdout.is_empty(), "{:#?}", output); assert!(output.stdout.is_empty(), "{:#?}", output);
assert!(output.stderr.is_empty(), "{:#?}", output); assert!(output.stderr.is_empty(), "{:#?}", output);