diff --git a/ctest/Cargo.toml b/ctest/Cargo.toml index 88db9c33d788..57eb02ec0e10 100644 --- a/ctest/Cargo.toml +++ b/ctest/Cargo.toml @@ -14,6 +14,7 @@ cc = "1.2.43" proc-macro2 = { version = "1.0.103", features = ["span-locations"] } quote = "1.0.41" syn = { version = "2.0.108", features = ["full", "visit", "extra-traits"] } +tempfile = "3.23.0" thiserror = "2.0.17" [lints] @@ -21,4 +22,3 @@ workspace = true [dev-dependencies] pretty_assertions = "1.4.1" -tempfile = "3.23.0" diff --git a/ctest/src/generator.rs b/ctest/src/generator.rs index a622b73fee7b..5e7df3e0a93a 100644 --- a/ctest/src/generator.rs +++ b/ctest/src/generator.rs @@ -13,6 +13,7 @@ use syn::visit::Visit; use thiserror::Error; use crate::ffi_items::FfiItems; +use crate::macro_expansion::expand_with_args; use crate::template::{ CTestTemplate, RustTestTemplate, @@ -32,7 +33,6 @@ use crate::{ Type, Union, VolatileItemKind, - expand, get_build_target, }; @@ -78,6 +78,10 @@ pub struct TestGenerator { cfg: Vec<(String, Option)>, /// A list of functions that remaps names used in the tests. mapped_names: Vec, + /// Extra command line args to pass to cargo when generating macro expansions. + macro_expansion_cargo_args: Vec, + /// Crate name to use when performing macro expansion. + crate_name: Option, /// The programming language to generate tests in. pub(crate) language: Language, /// A list of functions that determine what items to skip all tests for. @@ -723,6 +727,22 @@ impl TestGenerator { self } + /// Configures extra arguments to be passed to cargo during macro expansion. + /// This can be used, for example, to pass `-Zbuild-std`` if required. + pub fn macro_expansion_cargo_args_mut(&mut self) -> &mut Vec { + &mut self.macro_expansion_cargo_args + } + + /// Configures the crate name which should be used during macro expansion. + /// + /// If the tested crate uses `#![crate_name = "..."]`, this must be called with the + /// same name. Otherwise, there will be an error about `--crate-name` not + /// matching. + pub fn crate_name(&mut self, name: String) -> &mut Self { + self.crate_name = Some(name); + self + } + /// Configures whether tests for the type of a field is skipped or not. /// /// The closure is given a Rust struct as well as a field within that @@ -1087,7 +1107,14 @@ impl TestGenerator { crate_path: impl AsRef, output_file_path: impl AsRef, ) -> Result { - let expanded = expand(&crate_path, &self.cfg, get_build_target(self)?).map_err(|e| { + let expanded = expand_with_args( + &crate_path, + &self.cfg, + get_build_target(self)?, + self.crate_name.as_deref(), + &self.macro_expansion_cargo_args, + ) + .map_err(|e| { GenerationError::MacroExpansion(crate_path.as_ref().to_path_buf(), e.to_string()) })?; let ast = syn::parse_file(&expanded) diff --git a/ctest/src/macro_expansion.rs b/ctest/src/macro_expansion.rs index fc5ff1e4b312..9a3dcc488965 100644 --- a/ctest/src/macro_expansion.rs +++ b/ctest/src/macro_expansion.rs @@ -1,4 +1,3 @@ -use std::env; use std::fs::canonicalize; use std::path::Path; use std::process::Command; @@ -14,19 +13,63 @@ pub fn expand>( cfg: &[(String, Option)], target: String, ) -> Result { - let rustc = env::var("RUSTC").unwrap_or_else(|_| String::from("rustc")); + expand_with_args(crate_path, cfg, target, None, &[]) +} + +/// Extension of `expand` which allows a crate_name override for +/// libc and other clients which require crate_name to match the +/// original crate's name. +pub(crate) fn expand_with_args>( + crate_path: P, + cfg: &[(String, Option)], + target: String, + crate_name: Option<&str>, + extra_cargo_args: &[String], +) -> Result { + let dir = tempfile::tempdir()?; + + // make a Cargo.toml pointing to the crate path + let cargo_toml_path = dir.path().join("Cargo.toml"); + let crate_name = crate_name.unwrap_or("ctest-expansion-tmp"); + // FIXME(#5238): allow building using an existing manifest + let cargo_toml = format!( + r#" +[package] +name = '''{crate_name}''' # Needs to match #![crate_name = "..."] if it exists. +edition = '''{EDITION}''' +[lib] +path = '''{}''' +[lints.rust] +unexpected_cfgs = "allow" +"#, + canonicalize(crate_path)?.display() + ); + std::fs::write(cargo_toml_path, cargo_toml)?; - let mut cmd = Command::new(rustc); + let mut cmd = Command::new(std::env::var("CARGO").unwrap_or("cargo".into())); cmd.env("RUSTC_BOOTSTRAP", "1") - .arg("-Zunpretty=expanded") - .arg("--edition") - .arg(EDITION) // By default, -Zunpretty=expanded uses 2015 edition. - .arg(canonicalize(crate_path)?); + .current_dir(dir.path()) + .arg("rustc") + .arg("--lib") + .arg("--profile") + .arg("check") + .args(extra_cargo_args); + + // In order to avoid warnings causing failures here when the + // environment's RUSTFLAGS contains -Dwarnings (or similar), + // we will clear RUSTFLAGS. + cmd.env_remove("RUSTFLAGS"); + + // set an independent target dir so we don't deadlock on the cargo build lock. + cmd.arg("--target-dir").arg(dir.path().join("target")); if !target.is_empty() { cmd.arg("--target").arg(target); } + cmd.arg("--"); + cmd.arg("-Zunpretty=expanded"); + // `libc` uses non standard cfg flags as well, which have to be manually expanded. for (k, v) in cfg { match v { @@ -39,7 +82,11 @@ pub fn expand>( if !output.status.success() { let stderr = std::str::from_utf8(&output.stderr)?; - return Err(format!("macro expansion failed with {}: {}", output.status, stderr).into()); + return Err(format!( + "macro expansion failed with {}: {}, {:?}", + output.status, stderr, cmd + ) + .into()); } let expanded = std::str::from_utf8(&output.stdout)?.to_string(); diff --git a/libc-test/build.rs b/libc-test/build.rs index d909df071b07..697157dec4a9 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -106,6 +106,11 @@ fn ctest_cfg() -> ctest::TestGenerator { // FIXME(1.0): These aliases will eventually be removed. cfg.skip_alias(|ty| ty.ident() == "__uint128"); + if env::var("LIBC_CI_ZBUILD_STD").is_ok() { + *cfg.macro_expansion_cargo_args_mut() = vec!["-Zbuild-std=core,std".into()]; + } + cfg.crate_name("libc".into()); + cfg }