|
1 |
| -use std::path::Path; |
| 1 | +use std::{ |
| 2 | + env, |
| 3 | + fs::File, |
| 4 | + io::Write, |
| 5 | + path::{Path, PathBuf}, |
| 6 | +}; |
2 | 7 |
|
| 8 | +use askama::Template; |
3 | 9 | use syn::visit::Visit;
|
4 | 10 |
|
5 |
| -use crate::{expand, ffi_items::FfiItems, Result}; |
| 11 | +use crate::{ |
| 12 | + expand, |
| 13 | + ffi_items::FfiItems, |
| 14 | + template::{CTestTemplate, RustTestTemplate}, |
| 15 | + Result, |
| 16 | +}; |
6 | 17 |
|
7 | 18 | /// A builder used to generate a test suite.
|
8 | 19 | #[non_exhaustive]
|
9 | 20 | #[derive(Default, Debug, Clone)]
|
10 |
| -pub struct TestGenerator {} |
| 21 | +pub struct TestGenerator { |
| 22 | + headers: Vec<String>, |
| 23 | + target: Option<String>, |
| 24 | + host: Option<String>, |
| 25 | + includes: Vec<PathBuf>, |
| 26 | + out_dir: Option<PathBuf>, |
| 27 | +} |
11 | 28 |
|
12 | 29 | impl TestGenerator {
|
13 | 30 | /// Creates a new blank test generator.
|
14 | 31 | pub fn new() -> Self {
|
15 | 32 | Self::default()
|
16 | 33 | }
|
17 | 34 |
|
| 35 | + /// Add a header to be included as part of the generated C file. |
| 36 | + pub fn header(&mut self, header: &str) -> &mut Self { |
| 37 | + self.headers.push(header.to_string()); |
| 38 | + self |
| 39 | + } |
| 40 | + |
| 41 | + /// Configures the target to compile C code for. |
| 42 | + pub fn target(&mut self, target: &str) -> &mut Self { |
| 43 | + self.target = Some(target.to_string()); |
| 44 | + self |
| 45 | + } |
| 46 | + |
| 47 | + /// Configures the host. |
| 48 | + pub fn host(&mut self, host: &str) -> &mut Self { |
| 49 | + self.host = Some(host.to_string()); |
| 50 | + self |
| 51 | + } |
| 52 | + |
| 53 | + /// Add a path to the C compiler header lookup path. |
| 54 | + /// |
| 55 | + /// This is useful for if the C library is installed to a nonstandard |
| 56 | + /// location to ensure that compiling the C file succeeds. |
| 57 | + pub fn include<P: AsRef<Path>>(&mut self, p: P) -> &mut Self { |
| 58 | + self.includes.push(p.as_ref().to_owned()); |
| 59 | + self |
| 60 | + } |
| 61 | + |
| 62 | + /// Configures the output directory of the generated Rust and C code. |
| 63 | + pub fn out_dir<P: AsRef<Path>>(&mut self, p: P) -> &mut Self { |
| 64 | + self.out_dir = Some(p.as_ref().to_owned()); |
| 65 | + self |
| 66 | + } |
| 67 | + |
18 | 68 | /// Generate all tests for the given crate and output the Rust side to a file.
|
19 |
| - pub fn generate<P: AsRef<Path>>(&mut self, crate_path: P, _output_file_path: P) -> Result<()> { |
| 69 | + pub fn generate<P: AsRef<Path>>(&mut self, crate_path: P, output_file_path: P) -> Result<()> { |
| 70 | + let output_file_path = self.generate_files(crate_path, output_file_path)?; |
| 71 | + |
| 72 | + let target = self |
| 73 | + .target |
| 74 | + .clone() |
| 75 | + .unwrap_or(env::var("TARGET_PLATFORM").unwrap()); |
| 76 | + let host = self |
| 77 | + .host |
| 78 | + .clone() |
| 79 | + .unwrap_or(env::var("HOST_PLATFORM").unwrap()); |
| 80 | + |
| 81 | + let mut cfg = cc::Build::new(); |
| 82 | + // FIXME: Cpp not supported. |
| 83 | + cfg.file(output_file_path.with_extension("c")); |
| 84 | + cfg.host(&host); |
| 85 | + if target.contains("msvc") { |
| 86 | + cfg.flag("/W3") |
| 87 | + .flag("/Wall") |
| 88 | + .flag("/WX") |
| 89 | + // ignored warnings |
| 90 | + .flag("/wd4820") // warning about adding padding? |
| 91 | + .flag("/wd4100") // unused parameters |
| 92 | + .flag("/wd4996") // deprecated functions |
| 93 | + .flag("/wd4296") // '<' being always false |
| 94 | + .flag("/wd4255") // converting () to (void) |
| 95 | + .flag("/wd4668") // using an undefined thing in preprocessor? |
| 96 | + .flag("/wd4366") // taking ref to packed struct field might be unaligned |
| 97 | + .flag("/wd4189") // local variable initialized but not referenced |
| 98 | + .flag("/wd4710") // function not inlined |
| 99 | + .flag("/wd5045") // compiler will insert Spectre mitigation |
| 100 | + .flag("/wd4514") // unreferenced inline function removed |
| 101 | + .flag("/wd4711"); // function selected for automatic inline |
| 102 | + } else { |
| 103 | + cfg.flag("-Wall") |
| 104 | + .flag("-Wextra") |
| 105 | + .flag("-Werror") |
| 106 | + .flag("-Wno-unused-parameter") |
| 107 | + .flag("-Wno-type-limits") |
| 108 | + // allow taking address of packed struct members: |
| 109 | + .flag("-Wno-address-of-packed-member") |
| 110 | + .flag("-Wno-unknown-warning-option") |
| 111 | + .flag("-Wno-deprecated-declarations"); // allow deprecated items |
| 112 | + } |
| 113 | + |
| 114 | + for p in &self.includes { |
| 115 | + cfg.include(p); |
| 116 | + } |
| 117 | + |
| 118 | + let stem: &str = output_file_path.file_stem().unwrap().to_str().unwrap(); |
| 119 | + cfg.target(&target) |
| 120 | + .out_dir(output_file_path.parent().unwrap()) |
| 121 | + .compile(stem); |
| 122 | + |
| 123 | + Ok(()) |
| 124 | + } |
| 125 | + |
| 126 | + /// Generate the Rust and C testing files. |
| 127 | + /// |
| 128 | + /// Returns the path to the generated file. |
| 129 | + pub(crate) fn generate_files<P: AsRef<Path>>( |
| 130 | + &mut self, |
| 131 | + crate_path: P, |
| 132 | + output_file_path: P, |
| 133 | + ) -> Result<PathBuf> { |
20 | 134 | let expanded = expand(crate_path)?;
|
21 | 135 | let ast = syn::parse_file(&expanded)?;
|
22 | 136 |
|
23 | 137 | let mut ffi_items = FfiItems::new();
|
24 | 138 | ffi_items.visit_file(&ast);
|
25 | 139 |
|
26 |
| - Ok(()) |
| 140 | + let output_directory = self |
| 141 | + .out_dir |
| 142 | + .clone() |
| 143 | + .unwrap_or_else(|| PathBuf::from(env::var_os("OUT_DIR").unwrap())); |
| 144 | + let output_file_path = output_directory.join(output_file_path); |
| 145 | + |
| 146 | + // Generate the Rust side of the tests. |
| 147 | + File::create(output_file_path.with_extension("rs"))? |
| 148 | + .write_all(RustTestTemplate::new(&ffi_items)?.render()?.as_bytes())?; |
| 149 | + |
| 150 | + // Generate the C side of the tests. |
| 151 | + // FIXME: Cpp not supported yet. |
| 152 | + let c_output_path = output_file_path.with_extension("c"); |
| 153 | + let headers = self.headers.iter().map(|h| h.as_str()).collect(); |
| 154 | + File::create(&c_output_path)? |
| 155 | + .write_all(CTestTemplate::new(headers, &ffi_items).render()?.as_bytes())?; |
| 156 | + |
| 157 | + Ok(output_file_path) |
27 | 158 | }
|
28 | 159 | }
|
0 commit comments