From a663d335fcad5e90918cefd9c2ca824e19c922ee Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 11 Apr 2022 14:19:56 +0100 Subject: [PATCH] repl_test: update build.rs to work with Zig 0.9 (like test_gen) --- compiler/gen_wasm/src/wasm_module/sections.rs | 2 +- compiler/test_gen/build.rs | 4 +- repl_wasm/build.rs | 112 ++++++++++++++---- 3 files changed, 91 insertions(+), 27 deletions(-) diff --git a/compiler/gen_wasm/src/wasm_module/sections.rs b/compiler/gen_wasm/src/wasm_module/sections.rs index fd6d66ed48..1cd361b5a2 100644 --- a/compiler/gen_wasm/src/wasm_module/sections.rs +++ b/compiler/gen_wasm/src/wasm_module/sections.rs @@ -115,7 +115,7 @@ fn section_size(bytes: &[u8]) -> usize { } fn parse_section<'a>(id: SectionId, module_bytes: &'a [u8], cursor: &mut usize) -> (u32, &'a [u8]) { - if module_bytes[*cursor] != id as u8 { + if (*cursor >= module_bytes.len()) || (module_bytes[*cursor] != id as u8) { return (0, &[]); } *cursor += 1; diff --git a/compiler/test_gen/build.rs b/compiler/test_gen/build.rs index 458902d1cd..ab974b93be 100644 --- a/compiler/test_gen/build.rs +++ b/compiler/test_gen/build.rs @@ -39,13 +39,13 @@ fn build_wasm() { &platform_path, &compiler_rt_path, "-L", - &libc_dir, + libc_dir, "-lc", "-o", &format!("{}/{}.o", out_dir, PLATFORM_FILENAME), "--export-all", "--no-entry", - // "--emit-relocs", // TODO: resize stack by relocating __heap_base (issue #2480) + // "--emit-relocs", // TODO: resize stack by relocating __heap_base (issue #2480) here and in repl_test build ]; let zig = zig_executable(); diff --git a/repl_wasm/build.rs b/repl_wasm/build.rs index eb820852c8..b050c20673 100644 --- a/repl_wasm/build.rs +++ b/repl_wasm/build.rs @@ -1,6 +1,6 @@ use std::env; use std::ffi::OsStr; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; use roc_builtins::bitcode; @@ -10,7 +10,8 @@ const PRE_LINKED_BINARY: &str = "data/pre_linked_binary.o"; fn main() { println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=src/{}.c", PLATFORM_FILENAME); + let source_path = format!("src/{}.c", PLATFORM_FILENAME); + println!("cargo:rerun-if-changed={}", source_path); // When we build on Netlify, zig is not installed (but also not used, // since all we're doing is generating docs), so we can skip the steps @@ -23,26 +24,38 @@ fn main() { std::fs::create_dir_all("./data").unwrap(); - // Build a pre-linked binary with platform, builtins and all their libc dependencies - // This builds a library file that exports all symbols. It has no linker data but we don't need it. - // See discussion with Luuk de Gram (Zig contributor) - // https://github.com/rtfeldman/roc/pull/2181#pullrequestreview-839608063 - let args = [ - "build-lib", - "-target", - "wasm32-wasi", - "-lc", - "-dynamic", // -dynamic ensures libc code goes into the binary + // Zig can produce *either* an object containing relocations OR an object containing libc code + // But we want both, so we have to compile twice with different flags, then link them + + // Create an object file with relocations + let out_dir = env::var("OUT_DIR").unwrap(); + let platform_obj = build_wasm_platform(&out_dir, &source_path); + + // Compile again to get libc path + let (libc_archive, compiler_rt_obj) = build_wasm_libc_compilerrt(&out_dir, &source_path); + let mut libc_pathbuf = PathBuf::from(&libc_archive); + libc_pathbuf.pop(); + let libc_dir = libc_pathbuf.to_str().unwrap(); + + let args = &[ + "wasm-ld", bitcode::BUILTINS_WASM32_OBJ_PATH, - &format!("src/{}.c", PLATFORM_FILENAME), - &format!("-femit-bin={}", PRE_LINKED_BINARY), + &platform_obj, + &compiler_rt_obj, + "-L", + libc_dir, + "-lc", + "-o", + PRE_LINKED_BINARY, + "--export-all", + "--no-entry", ]; let zig = zig_executable(); // println!("{} {}", zig, args.join(" ")); - run_command(Path::new("."), &zig, args); + run_command(&zig, args); } fn zig_executable() -> String { @@ -52,26 +65,77 @@ fn zig_executable() -> String { } } -fn run_command>(path: P, command_str: &str, args: I) -> String -where - I: IntoIterator, - S: AsRef, -{ +fn build_wasm_platform(out_dir: &str, source_path: &str) -> String { + let platform_obj = format!("{}/{}.o", out_dir, PLATFORM_FILENAME); + + run_command( + &zig_executable(), + &[ + "build-lib", + "-target", + "wasm32-wasi", + "-lc", + source_path, + &format!("-femit-bin={}", &platform_obj), + ], + ); + + platform_obj +} + +fn build_wasm_libc_compilerrt(out_dir: &str, source_path: &str) -> (String, String) { + let zig_cache_dir = format!("{}/zig-cache-wasm32", out_dir); + + run_command( + &zig_executable(), + &[ + "build-lib", + "-dynamic", // ensure libc code is actually generated (not just linked against header) + "-target", + "wasm32-wasi", + "-lc", + source_path, + "-femit-bin=/dev/null", + "--global-cache-dir", + &zig_cache_dir, + ], + ); + + ( + run_command("find", &[&zig_cache_dir, "-name", "libc.a"]), + run_command("find", &[&zig_cache_dir, "-name", "compiler_rt.o"]), + ) +} + +fn run_command(command_str: &str, args: &[&str]) -> String { let output_result = Command::new(OsStr::new(&command_str)) - .current_dir(path) + .current_dir(Path::new(".")) .args(args) .output(); + + let fail = |err: String| { + panic!( + "\n\nFailed command:\n\t{} {}\n\n{}", + command_str, + args.join(" "), + err + ); + }; + match output_result { Ok(output) => match output.status.success() { - true => std::str::from_utf8(&output.stdout).unwrap().to_string(), + true => std::str::from_utf8(&output.stdout) + .unwrap() + .trim() + .to_string(), false => { let error_str = match std::str::from_utf8(&output.stderr) { Ok(stderr) => stderr.to_string(), Err(_) => format!("Failed to run \"{}\"", command_str), }; - panic!("{} failed: {}", command_str, error_str); + fail(error_str) } }, - Err(reason) => panic!("{} failed: {}", command_str, reason), + Err(reason) => fail(reason.to_string()), } }