use sixtyfps_compilerlib::*; use std::error::Error; use std::io::Write; pub struct Driver { native_library_dependencies: Vec, } impl Driver { pub fn new() -> Result> { let deps = test_driver_lib::native_library_dependencies( env!("CARGO"), &[], "sixtyfps_rendering_backend_gl", )?; Ok(Self { native_library_dependencies: deps.split(" ").map(String::from).collect() }) } pub fn test(&self, testcase: &test_driver_lib::TestCase) -> Result<(), Box> { let (syntax_node, mut diag) = parser::parse(&testcase.source); diag.current_path = testcase.path.clone(); let mut tr = typeregister::TypeRegister::builtin(); let doc = object_tree::Document::from_node(syntax_node, &mut diag, &mut tr); run_passes(&doc, &mut diag, &mut tr); let (mut diag, source) = diag.check_and_exit_on_error(testcase.source.clone()); let mut generated_cpp: Vec = Vec::new(); generator::generate(&mut generated_cpp, &doc.root_component, &mut diag)?; diag.check_and_exit_on_error(source); write!( &mut generated_cpp, "int main() {{ static {} component; return 0; }}", &doc.root_component.id )?; //println!("CODE: {}", String::from_utf8(generated_cpp.clone())?); let mut cpp_file = tempfile::Builder::new().suffix(".cpp").tempfile()?; cpp_file .write(&generated_cpp) .map_err(|err| format!("Error writing generated code: {}", err))?; cpp_file .as_file() .sync_all() .map_err(|err| format!("Error flushing generated code to disk: {}", err))?; let compiler = cc::Build::new() .cargo_metadata(false) .cpp(true) .opt_level_str(env!("OPT_LEVEL")) .target(env!("TARGET")) .host(env!("HOST")) .include(env!("GENERATED_CPP_HEADERS_PATH")) .include(env!("CPP_API_HEADERS_PATH")) .try_get_compiler()?; let mut compiler_command = compiler.to_command(); let binary_path = cpp_file.path().with_extension(std::env::consts::EXE_EXTENSION); let keep_temp_files = std::env::var("KEEP_TEMP_FILES").is_ok(); let _binary_deletion_guard = scopeguard::guard(binary_path.clone(), |path| { if !keep_temp_files { std::fs::remove_file(path).unwrap_or(()); } }); compiler_command.arg("-o").arg(binary_path.clone()).arg(cpp_file.path()); if compiler.is_like_clang() || compiler.is_like_gnu() { compiler_command.arg("-std=c++17"); compiler_command.arg(concat!("-L", env!("CPP_LIB_PATH"))); compiler_command.arg("-lsixtyfps_rendering_backend_gl"); } compiler_command.args(&self.native_library_dependencies); let _output = compiler_command.output()?; std::process::Command::new(binary_path.clone()) .spawn() .map_err(|err| format!("Error launching testcase binary: {}", err))? .wait() .map_err(|err| format!("Test case could not be run: {}", err)) .and_then(|status| { if status.success() { Ok(()) } else if let Some(exit_code) = status.code() { Err(format!("Test case exited with non-zero code: {}", exit_code)) } else { Err("Test case exited by signal".into()) } })?; if keep_temp_files { println!( "Left temporary files behind for {} : source {} binary {}", testcase.path.display(), cpp_file.path().display(), binary_path.display() ); cpp_file.keep()?; } Ok(()) } }