|
| 1 | +use crate::{Result, TestGenerator}; |
| 2 | +use std::env; |
| 3 | +use std::fs::{canonicalize, File}; |
| 4 | +use std::io::Write; |
| 5 | +use std::path::{Path, PathBuf}; |
| 6 | +use std::process::Command; |
| 7 | + |
| 8 | +/// Generate all tests for the given crate and output the Rust side to a file. |
| 9 | +#[doc(hidden)] |
| 10 | +pub fn generate_test( |
| 11 | + generator: &mut TestGenerator, |
| 12 | + crate_path: impl AsRef<Path>, |
| 13 | + output_file_path: impl AsRef<Path>, |
| 14 | +) -> Result<PathBuf> { |
| 15 | + let output_file_path = generator.generate_files(crate_path, output_file_path)?; |
| 16 | + |
| 17 | + // Search for the target and host to build for if specified manually |
| 18 | + // (generator.target, generator.host), |
| 19 | + // via build script (TARGET, HOST), or for internal testing (TARGET_PLATFORM, HOST_PLATFORM). |
| 20 | + let target = generator.target.clone().unwrap_or_else(|| { |
| 21 | + env::var("TARGET").unwrap_or_else(|_| env::var("TARGET_PLATFORM").unwrap()) |
| 22 | + }); |
| 23 | + let host = env::var("HOST").unwrap_or_else(|_| env::var("HOST_PLATFORM").unwrap()); |
| 24 | + |
| 25 | + let mut cfg = cc::Build::new(); |
| 26 | + // FIXME(ctest): Cpp not supported. |
| 27 | + cfg.file(output_file_path.with_extension("c")); |
| 28 | + cfg.host(&host); |
| 29 | + |
| 30 | + if target.contains("msvc") { |
| 31 | + cfg.flag("/W3") |
| 32 | + .flag("/Wall") |
| 33 | + .flag("/WX") |
| 34 | + // ignored warnings |
| 35 | + .flag("/wd4820") // warning about adding padding? |
| 36 | + .flag("/wd4100") // unused parameters |
| 37 | + .flag("/wd4996") // deprecated functions |
| 38 | + .flag("/wd4296") // '<' being always false |
| 39 | + .flag("/wd4255") // converting () to (void) |
| 40 | + .flag("/wd4668") // using an undefined thing in preprocessor? |
| 41 | + .flag("/wd4366") // taking ref to packed struct field might be unaligned |
| 42 | + .flag("/wd4189") // local variable initialized but not referenced |
| 43 | + .flag("/wd4710") // function not inlined |
| 44 | + .flag("/wd5045") // compiler will insert Spectre mitigation |
| 45 | + .flag("/wd4514") // unreferenced inline function removed |
| 46 | + .flag("/wd4711"); // function selected for automatic inline |
| 47 | + } else { |
| 48 | + cfg.flag("-Wall") |
| 49 | + .flag("-Wextra") |
| 50 | + .flag("-Werror") |
| 51 | + .flag("-Wno-unused-parameter") |
| 52 | + .flag("-Wno-type-limits") |
| 53 | + // allow taking address of packed struct members: |
| 54 | + .flag("-Wno-address-of-packed-member") |
| 55 | + .flag("-Wno-unknown-warning-option") |
| 56 | + .flag("-Wno-deprecated-declarations"); // allow deprecated items |
| 57 | + } |
| 58 | + |
| 59 | + for p in &generator.includes { |
| 60 | + cfg.include(p); |
| 61 | + } |
| 62 | + |
| 63 | + let stem: &str = output_file_path.file_stem().unwrap().to_str().unwrap(); |
| 64 | + cfg.target(&target) |
| 65 | + .out_dir(output_file_path.parent().unwrap()) |
| 66 | + .compile(stem); |
| 67 | + |
| 68 | + Ok(output_file_path) |
| 69 | +} |
| 70 | + |
| 71 | +/// Compiles a Rust source file and links it against a static library. |
| 72 | +/// |
| 73 | +/// Returns the path to the generated binary. |
| 74 | +#[doc(hidden)] |
| 75 | +pub fn __compile_test( |
| 76 | + output_dir: impl AsRef<Path>, |
| 77 | + crate_path: impl AsRef<Path>, |
| 78 | + library_file: impl AsRef<Path>, |
| 79 | +) -> Result<PathBuf> { |
| 80 | + let rustc = env::var("RUSTC").unwrap_or_else(|_| "rustc".into()); |
| 81 | + let output_dir = output_dir.as_ref(); |
| 82 | + let crate_path = crate_path.as_ref(); |
| 83 | + let library_file = library_file.as_ref().file_stem().unwrap(); |
| 84 | + |
| 85 | + let rust_file = output_dir |
| 86 | + .join(crate_path.file_stem().unwrap()) |
| 87 | + .with_extension("rs"); |
| 88 | + let binary_path = output_dir.join(rust_file.file_stem().unwrap()); |
| 89 | + |
| 90 | + // Create a file that contains the Rust 'bindings' as well as the generated test code. |
| 91 | + File::create(&rust_file)?.write_all( |
| 92 | + format!( |
| 93 | + "include!(r#\"{}\"#);\ninclude!(r#\"{}.rs\"#);", |
| 94 | + canonicalize(crate_path)?.display(), |
| 95 | + library_file.to_str().unwrap() |
| 96 | + ) |
| 97 | + .as_bytes(), |
| 98 | + )?; |
| 99 | + |
| 100 | + // Compile the test file with the compiled C library file found in `output_dir` |
| 101 | + // into a binary file, ignoring all warnings about unused items. (not all items |
| 102 | + // are currently tested) |
| 103 | + |
| 104 | + let mut cmd = Command::new(rustc); |
| 105 | + cmd.arg(&rust_file) |
| 106 | + .arg(format!("-Lnative={}", output_dir.display())) |
| 107 | + .arg(format!("-lstatic={}", library_file.to_str().unwrap())) |
| 108 | + .arg("--edition") |
| 109 | + .arg("2021") // Defaults to 2015. |
| 110 | + .arg("-o") |
| 111 | + .arg(&binary_path) |
| 112 | + .arg("-Aunused"); |
| 113 | + |
| 114 | + // Pass in a different target, linker or flags if set, useful for cross compilation. |
| 115 | + |
| 116 | + let target = env::var("TARGET_PLATFORM").unwrap_or_default(); |
| 117 | + if !target.is_empty() { |
| 118 | + cmd.arg("--target").arg(target); |
| 119 | + } |
| 120 | + |
| 121 | + let linker = env::var("LINKER").unwrap_or_default(); |
| 122 | + if !linker.is_empty() { |
| 123 | + cmd.arg(format!("-Clinker={linker}")); |
| 124 | + } |
| 125 | + |
| 126 | + let flags = env::var("FLAGS").unwrap_or_default(); |
| 127 | + if !flags.is_empty() { |
| 128 | + cmd.args(flags.split_whitespace()); |
| 129 | + } |
| 130 | + |
| 131 | + let output = cmd.output()?; |
| 132 | + if !output.status.success() { |
| 133 | + return Err(std::str::from_utf8(&output.stderr)?.into()); |
| 134 | + } |
| 135 | + |
| 136 | + Ok(binary_path) |
| 137 | +} |
| 138 | + |
| 139 | +/// Executes the compiled test binary and returns its output. |
| 140 | +/// |
| 141 | +/// If a RUNNER environment variable is present, it will use that to run the binary. |
| 142 | +#[doc(hidden)] |
| 143 | +pub fn __run_test<P: AsRef<Path>>(test_binary: P) -> Result<String> { |
| 144 | + let runner = env::var("RUNNER").unwrap_or_default(); |
| 145 | + let mut cmd; |
| 146 | + if runner.is_empty() { |
| 147 | + cmd = Command::new(test_binary.as_ref()); |
| 148 | + } else { |
| 149 | + let mut args = runner.split_whitespace(); |
| 150 | + cmd = Command::new(args.next().unwrap()); |
| 151 | + cmd.args(args); |
| 152 | + }; |
| 153 | + |
| 154 | + cmd.arg(test_binary.as_ref()); |
| 155 | + let output = cmd.output()?; |
| 156 | + |
| 157 | + if !output.status.success() { |
| 158 | + return Err(std::str::from_utf8(&output.stderr)?.into()); |
| 159 | + } |
| 160 | + |
| 161 | + Ok(std::str::from_utf8(&output.stdout)?.to_string()) |
| 162 | +} |
0 commit comments