Skip to content

Commit ab5b306

Browse files
committed
ctest: Add translation of Rust types.
1 parent 62fc5c3 commit ab5b306

File tree

18 files changed

+500
-552
lines changed

18 files changed

+500
-552
lines changed

ctest-next/.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
rustflags = ["--cfg", "procmacro2_semver_exempt"]

ctest-next/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ publish = false
1010
[dependencies]
1111
askama = "0.14.0"
1212
cc = "1.2.25"
13+
proc-macro2 = { version = "1.0.95", features = ["span-locations"] }
1314
quote = "1.0.40"
1415
syn = { version = "2.0.101", features = ["full", "visit", "extra-traits"] }
1516
thiserror = "2.0.12"
1617

1718
[dev-dependencies]
1819
tempfile = "3.20.0"
19-

ctest-next/build.rs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,27 @@ use std::env;
66
//
77
// This is already set by the CI for all platforms, so there is no problem up till here.
88
//
9-
// The integration tests (which are run in qemu, but use host rustc and cc) require the following:
9+
// The integration tests (which are run in qemu, but use host rustc and cc) require the
10+
// following:
1011
// - TARGET_PLATFORM or target set manually. (We forward TARGET in build.rs for this.)
1112
// - HOST_PLATFORM or host set manually. (We forward HOST in build.rs for this.)
1213
// - LINKER: To link the C headers. (We forward CARGO_TARGET_{}_LINKER for this.)
13-
// - FLAGS: Any flags to pass when compiling the test binary for the cross compiled platform. (Forwarded from CARGO_TARGET_{}_RUSTFLAGS)
14+
// - FLAGS: Any flags to pass when compiling the test binary for the cross compiled platform.
15+
// (Forwarded from CARGO_TARGET_{}_RUSTFLAGS)
1416
// - RUNNER: To run the test binary with. (Forward the same runner as CARGO_TARGET_{}_RUNNER)
1517
//
16-
// The TARGET_PLATFORM and HOST_PLATFORM variables are not an issue, cargo will
17-
// automatically set TARGET and PLATFORM and we will forward them.
18+
// The TARGET_PLATFORM and HOST_PLATFORM variables are not an issue, cargo will automatically set
19+
// TARGET and PLATFORM and we will forward them.
1820
//
19-
// Similarly FLAGS and RUNNER are also not an issue, if CARGO_TARGET_{}_RUSTFLAGS are present they're forwarded.
20-
// And RUSTFLAGS works by default anyway. Similarly the test binary doesn't require any external applications so just
21-
// the RUNNER is enough to run it.
21+
// Similarly FLAGS and RUNNER are also not an issue, if CARGO_TARGET_{}_RUSTFLAGS are present
22+
// they're forwarded. And RUSTFLAGS works by default anyway. Similarly the test binary doesn't
23+
// require any external applications so just the RUNNER is enough to run it.
2224
//
23-
// However since rustc and cc are the host versions, they will only work if we specify the correct variables for them.
24-
// Because we only use them to compile, not run things.
25-
// For CC we MUST specify CC or CC_target otherwise it will fail. (Other flags like AR etc. work without forwarding because it is run in the host.)
26-
// For rustc we MUST specify the correct linker. Usually this is the same as CC or CC_target.
25+
// However since rustc and cc are the host versions, they will only work if we specify the
26+
// correct variables for them. Because we only use them to compile, not run things. For CC we
27+
// MUST specify CC or CC_target otherwise it will fail. (Other flags like AR etc. work without
28+
// forwarding because it is run in the host.) For rustc we MUST specify the correct linker.
29+
// Usually this is the same as CC or CC_target.
2730
//
2831
// In the CI, the CARGO_TARGET_{} variables are always set.
2932

@@ -33,23 +36,34 @@ fn main() {
3336
let target_key = target.replace('-', "_").to_uppercase();
3437

3538
println!("cargo:rustc-env=HOST_PLATFORM={host}");
39+
println!("cargo:rerun-if-changed-env=HOST");
40+
3641
println!("cargo:rustc-env=TARGET_PLATFORM={target}");
42+
println!("cargo:rerun-if-changed-env=TARGET");
3743

38-
let linker = env::var(format!("CARGO_TARGET_{target_key}_LINKER")).unwrap_or_default();
39-
let runner = env::var(format!("CARGO_TARGET_{target_key}_RUNNER")).unwrap_or_default();
40-
// As we invoke rustc directly this does not get passed to it, although RUSTFLAGS does.
41-
let flags = env::var(format!("CARGO_TARGET_{target_key}_RUSTFLAGS")).unwrap_or_default();
44+
let link_var = format!("CARGO_TARGET_{target_key}_LINKER");
45+
println!("cargo:rerun-if-changed-env={link_var}");
46+
if let Ok(linker) = env::var(link_var) {
47+
println!("cargo:rustc-env=LINKER={linker}");
48+
}
4249

43-
// Forward build environment variables so they can be used in tests.
44-
println!("cargo:rustc-env=LINKER={linker}");
45-
println!("cargo:rustc-env=RUNNER={runner}");
46-
println!("cargo:rustc-env=FLAGS={flags}");
50+
let run_var = format!("CARGO_TARGET_{target_key}_RUNNER");
51+
println!("cargo:rerun-if-changed-env={run_var}");
52+
if let Ok(runner) = env::var(run_var) {
53+
println!("cargo:rustc-env=RUNNER={runner}");
54+
}
4755

48-
// Rerun this build script if any of the environment variables change.
49-
println!("cargo:rerun-if-changed-env=HOST");
50-
println!("cargo:rerun-if-changed-env=TARGET");
51-
println!("cargo:rerun-if-changed-env=CARGO_TARGET_{target_key}_LINKER",);
56+
// As we invoke rustc directly this does not get passed to it, although RUSTFLAGS does.
57+
let flag_var = format!("CARGO_TARGET_{target_key}_RUSTFLAGS");
58+
println!("cargo:rerun-if-changed-env={flag_var}");
59+
if let Ok(flags) = env::var(flag_var) {
60+
println!("cargo:rustc-env=FLAGS={flags}");
61+
}
62+
63+
// Rerun this build script if any of these environment variables change.
5264
println!("cargo:rerun-if-changed-env=CC");
53-
println!("cargo:rerun-if-changed-env=CC_{target_key}");
54-
println!("cargo:rerun-if-changed-env=CARGO_TARGET_{target_key}_RUNNER",);
65+
println!(
66+
"cargo:rerun-if-changed-env=CC_{}",
67+
target_key.to_lowercase()
68+
);
5569
}

ctest-next/src/generator.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use crate::{
2020
pub struct TestGenerator {
2121
headers: Vec<String>,
2222
pub(crate) target: Option<String>,
23-
pub(crate) host: Option<String>,
2423
pub(crate) includes: Vec<PathBuf>,
2524
out_dir: Option<PathBuf>,
2625
}
@@ -50,12 +49,6 @@ impl TestGenerator {
5049
self
5150
}
5251

53-
/// Configures the host.
54-
pub fn host(&mut self, host: &str) -> &mut Self {
55-
self.host = Some(host.to_string());
56-
self
57-
}
58-
5952
/// Add a path to the C compiler header lookup path.
6053
///
6154
/// This is useful for if the C library is installed to a nonstandard
@@ -96,7 +89,7 @@ impl TestGenerator {
9689
.write_all(RustTestTemplate::new(&ffi_items)?.render()?.as_bytes())?;
9790

9891
// Generate the C side of the tests.
99-
// FIXME: Cpp not supported yet.
92+
// FIXME(ctest): Cpp not supported yet.
10093
let c_output_path = output_file_path.with_extension("c");
10194
let headers = self.headers.iter().map(|h| h.as_str()).collect();
10295
File::create(&c_output_path)?

ctest-next/src/runner.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,24 @@ use std::path::{Path, PathBuf};
66
use std::process::Command;
77

88
/// Generate all tests for the given crate and output the Rust side to a file.
9+
#[doc(hidden)]
910
pub fn generate_test(
1011
generator: &mut TestGenerator,
1112
crate_path: impl AsRef<Path>,
1213
output_file_path: impl AsRef<Path>,
1314
) -> Result<PathBuf> {
1415
let output_file_path = generator.generate_files(crate_path, output_file_path)?;
1516

16-
// Search for the target and host to build for if specified manually (generator.target, generator.host),
17+
// Search for the target and host to build for if specified manually
18+
// (generator.target, generator.host),
1719
// via build script (TARGET, HOST), or for internal testing (TARGET_PLATFORM, HOST_PLATFORM).
1820
let target = generator.target.clone().unwrap_or_else(|| {
1921
env::var("TARGET").unwrap_or_else(|_| env::var("TARGET_PLATFORM").unwrap())
2022
});
21-
let host = generator
22-
.host
23-
.clone()
24-
.unwrap_or_else(|| env::var("HOST").unwrap_or_else(|_| env::var("HOST_PLATFORM").unwrap()));
23+
let host = env::var("HOST").unwrap_or_else(|_| env::var("HOST_PLATFORM").unwrap());
2524

2625
let mut cfg = cc::Build::new();
27-
// FIXME: Cpp not supported.
26+
// FIXME(ctest): Cpp not supported.
2827
cfg.file(output_file_path.with_extension("c"));
2928
cfg.host(&host);
3029

@@ -143,18 +142,21 @@ pub fn __compile_test(
143142
#[doc(hidden)]
144143
pub fn __run_test<P: AsRef<Path>>(test_binary: P) -> Result<String> {
145144
let runner = env::var("RUNNER").unwrap_or_default();
146-
let output = if runner.is_empty() {
147-
Command::new(test_binary.as_ref()).output()?
145+
let mut cmd;
146+
if runner.is_empty() {
147+
cmd = Command::new(test_binary.as_ref());
148148
} else {
149149
let mut args = runner.split_whitespace();
150-
let mut cmd = Command::new(args.next().unwrap());
151-
cmd.args(args).arg(test_binary.as_ref()).output()?
150+
cmd = Command::new(args.next().unwrap());
151+
cmd.args(args);
152152
};
153153

154+
cmd.arg(test_binary.as_ref());
155+
let output = cmd.output()?;
156+
154157
if !output.status.success() {
155158
return Err(std::str::from_utf8(&output.stderr)?.into());
156159
}
157160

158-
// The template prints to stderr regardless.
159-
Ok(std::str::from_utf8(&output.stderr)?.to_string())
161+
Ok(std::str::from_utf8(&output.stdout)?.to_string())
160162
}

ctest-next/src/tests.rs

Lines changed: 27 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{ffi_items::FfiItems, translator::Translator};
1+
use crate::{ffi_items::FfiItems, translator::Translator, Result, TranslationError};
22

33
use syn::visit::Visit;
44

@@ -34,6 +34,12 @@ macro_rules! collect_idents {
3434
};
3535
}
3636

37+
fn ty(s: &str) -> Result<String, TranslationError> {
38+
let translator = Translator {};
39+
let ty: syn::Type = syn::parse_str(s).unwrap();
40+
translator.translate_type(&ty)
41+
}
42+
3743
#[test]
3844
fn test_extraction_ffi_items() {
3945
let ast = syn::parse_file(ALL_ITEMS).unwrap();
@@ -51,127 +57,50 @@ fn test_extraction_ffi_items() {
5157

5258
#[test]
5359
fn test_translation_type_ptr() {
54-
let translator = Translator {};
55-
let ty: syn::Type = syn::parse_str("*const *mut i32").unwrap();
56-
5760
assert_eq!(
58-
translator.translate_type(&ty),
59-
Ok("int32_t * const*".to_string())
61+
ty("*const *mut i32").unwrap(),
62+
"int32_t * const*".to_string()
6063
);
61-
}
62-
63-
#[test]
64-
fn test_translation_type_ptr_to_array() {
65-
let translator = Translator {};
66-
let ty: syn::Type = syn::parse_str("*const [u128; 2 + 3]").unwrap();
67-
6864
assert_eq!(
69-
translator.translate_type(&ty),
70-
Ok("unsigned __int128 (*const) [2 + 3]".to_string())
65+
ty("*const [u128; 2 + 3]").unwrap(),
66+
"unsigned __int128 (*const) [2 + 3]".to_string()
7167
);
72-
}
73-
74-
#[test]
75-
fn test_translation_type_ptr_to_ptr_array() {
76-
let translator = Translator {};
77-
let ty: syn::Type = syn::parse_str("*const *mut [u8; 5]").unwrap();
78-
7968
assert_eq!(
80-
translator.translate_type(&ty),
81-
Ok("uint8_t (*const *) [5]".to_string())
82-
);
83-
}
84-
85-
#[test]
86-
fn test_translation_type_ptr_to_fn_with_array() {
87-
let translator = Translator {};
88-
let ty: syn::Type =
89-
syn::parse_str("*const fn(*mut u8, &mut [u8; 16]) -> &mut *mut u8").unwrap();
90-
91-
assert_eq!(
92-
translator.translate_type(&ty),
93-
Ok("uint8_t * *(*const)(uint8_t *, uint8_t (*) [16])".to_string())
69+
ty("*const *mut [u8; 5]").unwrap(),
70+
"uint8_t (*const *) [5]".to_string()
9471
);
9572
}
9673

9774
#[test]
9875
fn test_translation_type_reference() {
99-
let translator = Translator {};
100-
let ty: syn::Type = syn::parse_str("&u8").unwrap();
101-
102-
assert_eq!(
103-
translator.translate_type(&ty),
104-
Ok("const uint8_t*".to_string())
105-
);
106-
}
107-
108-
#[test]
109-
fn test_translation_type_double_reference() {
110-
let translator = Translator {};
111-
let ty: syn::Type = syn::parse_str("&&u8").unwrap();
112-
113-
assert_eq!(
114-
translator.translate_type(&ty),
115-
Ok("const uint8_t* const*".to_string())
116-
);
76+
assert_eq!(ty("&u8").unwrap(), "const uint8_t*".to_string());
77+
assert_eq!(ty("&&u8").unwrap(), "const uint8_t* const*".to_string());
78+
assert_eq!(ty("*mut &u8").unwrap(), "const uint8_t* *".to_string());
79+
assert_eq!(ty("& &mut u8").unwrap(), "uint8_t* const*".to_string());
11780
}
11881

11982
#[test]
120-
fn test_translation_type_pointer_reference() {
121-
let translator = Translator {};
122-
let ty: syn::Type = syn::parse_str("*mut &u8").unwrap();
123-
83+
fn test_translation_type_bare_fn() {
12484
assert_eq!(
125-
translator.translate_type(&ty),
126-
Ok("const uint8_t* *".to_string())
85+
ty("fn(*mut u8, i16) -> *const char").unwrap(),
86+
"char const*(*)(uint8_t *, int16_t)".to_string()
12787
);
128-
}
129-
130-
#[test]
131-
fn test_translation_type_reference_pointer() {
132-
let translator = Translator {};
133-
let ty: syn::Type = syn::parse_str("& &mut u8").unwrap();
134-
13588
assert_eq!(
136-
translator.translate_type(&ty),
137-
Ok("uint8_t* const*".to_string())
138-
);
139-
}
140-
141-
#[test]
142-
fn test_translation_type_bare_fn() {
143-
let translator = Translator {};
144-
let ty: syn::Type = syn::parse_str("fn(*mut u8, i16) -> *const char").unwrap();
145-
146-
assert_eq!(
147-
translator.translate_type(&ty),
148-
Ok("char const*(*)(uint8_t *, int16_t)".to_string())
89+
ty("*const fn(*mut u8, &mut [u8; 16]) -> &mut *mut u8").unwrap(),
90+
"uint8_t * *(*const)(uint8_t *, uint8_t (*) [16])".to_string()
14991
);
15092
}
15193

15294
#[test]
15395
fn test_translation_type_array() {
154-
let translator = Translator {};
155-
let ty: syn::Type = syn::parse_str("[&u8; 2 + 2]").unwrap();
156-
15796
assert_eq!(
158-
translator.translate_type(&ty),
159-
Ok("const uint8_t*[2 + 2]".to_string())
97+
ty("[&u8; 2 + 2]").unwrap(),
98+
"const uint8_t*[2 + 2]".to_string()
16099
);
161100
}
162101

163102
#[test]
164-
fn test_translation_fails_for_str() {
165-
let translator = Translator {};
166-
let ty: syn::Type = syn::parse_str("[&str; 2 + 2]").unwrap();
167-
168-
assert!(translator.translate_type(&ty).is_err());
169-
}
170-
171-
#[test]
172-
fn test_translation_fails_for_slice() {
173-
let translator = Translator {};
174-
let ty: syn::Type = syn::parse_str("fn(*mut [u8], i16) -> *const char").unwrap();
175-
176-
assert!(translator.translate_type(&ty).is_err());
103+
fn test_translation_fails_for_unsupported() {
104+
assert!(ty("[&str; 2 + 2]").is_err());
105+
assert!(ty("fn(*mut [u8], i16) -> *const char").is_err());
177106
}

0 commit comments

Comments
 (0)