From 6758f9c56c160b2eaa55e3bfe7d7d9c450a5efe0 Mon Sep 17 00:00:00 2001 From: martinjrobins Date: Wed, 6 Sep 2023 11:03:35 +0000 Subject: [PATCH] add standalone option to compiler functions --- src/bin/diffeq.rs | 13 +++++- src/lib.rs | 115 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/src/bin/diffeq.rs b/src/bin/diffeq.rs index 35f9677..23e2d42 100644 --- a/src/bin/diffeq.rs +++ b/src/bin/diffeq.rs @@ -1,6 +1,6 @@ use clap::Parser; use anyhow::Result; -use diffeq::compile; +use diffeq::{compile, CompilerOptions}; /// compiles a model in continuous (.cs) or discrete (.ds) format to an object file #[derive(Parser, Debug)] @@ -24,10 +24,19 @@ struct Args { /// Compile to WASM #[arg(short, long)] wasm: bool, + + /// Compile to standalone executable + #[arg(short, long)] + standalone: bool, } fn main() -> Result<()> { let cli = Args::parse(); - compile(&cli.input, cli.out.as_deref(), cli.model.as_deref(), cli.compile, cli.wasm) + let options = CompilerOptions { + compile: cli.compile, + wasm: cli.wasm, + standalone: cli.standalone, + }; + compile(&cli.input, cli.out.as_deref(), cli.model.as_deref(), options) } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e0b0ad3..96a466a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,14 @@ pub mod discretise; pub mod continuous; pub mod codegen; +pub struct CompilerOptions { + pub compile: bool, + pub wasm: bool, + pub standalone: bool, +} + -pub fn compile(input: &str, out: Option<&str>, model: Option<&str>, compile: bool, wasm: bool) -> Result<()> { +pub fn compile(input: &str, out: Option<&str>, model: Option<&str>, options: CompilerOptions) -> Result<()> { let inputfile = Path::new(input); let is_discrete = inputfile.extension().unwrap_or(OsStr::new("")).to_str().unwrap() == "ds"; let is_continuous = inputfile.extension().unwrap_or(OsStr::new("")).to_str().unwrap() == "cs"; @@ -32,18 +38,20 @@ pub fn compile(input: &str, out: Option<&str>, model: Option<&str>, compile: boo } else { inputfile.file_stem().unwrap().to_str().unwrap() }; - let text = std::fs::read_to_string(inputfile)?; - compile_text(text.as_str(), out, model_name, compile, wasm, is_discrete) -} - -pub fn compile_text(text: &str, out: Option<&str>, model_name: &str, compile: bool, wasm: bool, is_discrete: bool) -> Result<()> { let out = if let Some(out) = out { out.clone() - } else if compile { + } else if options.compile { "out.o" } else { "out" }; + let text = std::fs::read_to_string(inputfile)?; + compile_text(text.as_str(), out, model_name, options, is_discrete) +} + +pub fn compile_text(text: &str, out: &str, model_name: &str, options: CompilerOptions, is_discrete: bool) -> Result<()> { + let CompilerOptions { compile, wasm, standalone } = options; + let is_continuous = !is_discrete; let objectname = if compile { out.to_owned() } else { format!("{}.o", out) }; @@ -101,55 +109,94 @@ pub fn compile_text(text: &str, out: Option<&str>, model_name: &str, compile: bo return Ok(()); } + let command_name = if wasm { "emcc" } else { "clang" }; + // link the object file and our runtime library let output = if wasm { + let exported_functions = vec![ + "Vector_destroy", + "Vector_create", + "Vector_create_with_capacity", + "Vector_push", + + "Options_destroy", + "Options_create", + + "Sundials_destroy", + "Sundials_create", + "Sundials_init", + "Sundials_solve", + ]; + let mut linked_files = vec![ + "libdiffeq_runtime_lib_wasm.a", + "libsundials_idas_wasm.a", + "libargparse_wasm.a", + ]; + if standalone { + linked_files.push("libdiffeq_runtime_wasm.a"); + } + let linked_files = linked_files; let library_paths_env = env::var("LIBRARY_PATH").unwrap_or("".to_owned()); let library_paths = library_paths_env.split(":").collect::>(); let mut runtime_path = None; for path in library_paths { + println!("checking {}", path); // check if the library path contains the runtime library - let runtime_path_test = Path::new(path).join("libdiffeq_runtime_wasm.a"); - if runtime_path_test.exists() { + let all_exist = linked_files.iter().all(|file| { + let file_path = Path::new(path).join(file); + file_path.exists() + }); + if all_exist { runtime_path = Some(path); } }; if runtime_path.is_none() { - return Err(anyhow!("Could not find libdiffeq_runtime_wasm.a in LIBRARY_PATH")); + return Err(anyhow!("Could not find {:?} in LIBRARY_PATH", linked_files)); } let runtime_path = runtime_path.unwrap(); - let runtime_file = Path::new(runtime_path).join("libdiffeq_runtime_wasm.a"); - let runtime_lib_file = Path::new(runtime_path).join("libdiffeq_runtime_lib_wasm.a"); - let sundials_lib_file = Path::new(runtime_path).join("libsundials_idas_wasm.a"); - let argparse_lib_file = Path::new(runtime_path).join("libargparse_wasm.a"); - Command::new("emcc") - .arg("-o") - .arg(out) - .arg(objectname.clone()) - .arg(runtime_file) - .arg(runtime_lib_file) - .arg(sundials_lib_file) - .arg(argparse_lib_file) - .output()? + println!("using runtime path {}", runtime_path); + let mut command = Command::new(command_name); + command.arg("-o").arg(out).arg(objectname.clone()); + for file in linked_files { + command.arg(Path::new(runtime_path).join(file)); + } + if !standalone { + let exported_functions = exported_functions.into_iter().map(|s| format!("_{}", s)).collect::>().join(","); + command.arg("-s").arg(format!("EXPORTED_FUNCTIONS={}", exported_functions)); + command.arg("--no-entry"); + } + command.output() } else { - Command::new("clang") - .arg("-o") - .arg(out) - .arg(objectname.clone()) - .arg("-ldiffeq_runtime") - .output()? + let mut command = Command::new(command_name); + command.arg("-o").arg(out).arg(objectname.clone()); + if standalone { + command.arg("-ldiffeq_runtime"); + } else { + command.arg("-ldiffeq_runtime_lib"); + } + command.output() + }; + + let output = match output { + Ok(output) => output, + Err(e) => { + return Err(anyhow!("Error running {}: {}", command_name, e)); + } }; - - - // clean up the object file - std::fs::remove_file(objectfile)?; if let Some(code) = output.status.code() { if code != 0 { println!("{}", String::from_utf8_lossy(&output.stderr)); - return Err(anyhow!("clang returned error code {}", code)); + return Err(anyhow!("{} returned error code {}", command_name, code)); } } + + println!("Compiled to {:?}", output); + + // clean up the object file + std::fs::remove_file(objectfile)?; + Ok(()) }