Skip to content

Commit 6e57d30

Browse files
committed
fix: support header_contents in macro fallback
1 parent 2048231 commit 6e57d30

5 files changed

Lines changed: 306 additions & 60 deletions

File tree

bindgen-tests/tests/tests.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,186 @@ fn test_macro_fallback_non_system_dir() {
617617
}
618618
}
619619

620+
#[test]
621+
fn test_macro_fallback_header_contents() {
622+
if let Some((9, _)) = clang_version().parsed {
623+
return;
624+
}
625+
626+
let tmpdir = tempfile::tempdir().unwrap();
627+
let actual = builder()
628+
.disable_header_comment()
629+
.header_contents(
630+
"test.h",
631+
"#define UINT32_C(c) c ## U\n\
632+
#define SIMPLE 42\n\
633+
#define COMPOUND UINT32_C(69)\n",
634+
)
635+
.clang_macro_fallback()
636+
.clang_macro_fallback_build_dir(tmpdir.path())
637+
.clang_arg("--target=x86_64-unknown-linux")
638+
.generate()
639+
.unwrap()
640+
.to_string();
641+
642+
let actual = format_code(actual).unwrap();
643+
let expected = format_code(
644+
"pub const SIMPLE: u32 = 42;\npub const COMPOUND: u32 = 69;\n",
645+
)
646+
.unwrap();
647+
648+
assert_eq!(expected, actual);
649+
}
650+
651+
#[test]
652+
fn test_macro_fallback_header_contents_preserves_build_dir_files() {
653+
if let Some((9, _)) = clang_version().parsed {
654+
return;
655+
}
656+
657+
let tmpdir = tempfile::tempdir().unwrap();
658+
let victim = tmpdir.path().join(".macro_eval_header_0.h");
659+
fs::write(&victim, "must survive\n").unwrap();
660+
661+
let actual = builder()
662+
.disable_header_comment()
663+
.header_contents(
664+
"test.h",
665+
"#define UINT32_C(c) c ## U\n\
666+
#define COMPOUND UINT32_C(69)\n",
667+
)
668+
.clang_macro_fallback()
669+
.clang_macro_fallback_build_dir(tmpdir.path())
670+
.clang_arg("--target=x86_64-unknown-linux")
671+
.generate()
672+
.unwrap()
673+
.to_string();
674+
675+
let actual = format_code(actual).unwrap();
676+
let expected = format_code("pub const COMPOUND: u32 = 69;\n").unwrap();
677+
678+
assert_eq!(expected, actual);
679+
assert_eq!("must survive\n", fs::read_to_string(victim).unwrap());
680+
}
681+
682+
#[test]
683+
fn test_macro_fallback_header_contents_preserves_primary_header_order() {
684+
if let Some((9, _)) = clang_version().parsed {
685+
return;
686+
}
687+
688+
let tmpdir = tempfile::tempdir().unwrap();
689+
let header = tmpdir.path().join("primary.h");
690+
fs::write(
691+
&header,
692+
"#undef ORDER_VALUE\n#define ORDER_VALUE UINT32_C(1)\n",
693+
)
694+
.unwrap();
695+
696+
let actual = builder()
697+
.disable_header_comment()
698+
.header(header.to_str().unwrap())
699+
.header_contents(
700+
"override.h",
701+
"#define UINT32_C(c) c ## U\n\
702+
#undef ORDER_VALUE\n\
703+
#define ORDER_VALUE UINT32_C(2)\n",
704+
)
705+
.clang_macro_fallback()
706+
.clang_macro_fallback_build_dir(tmpdir.path())
707+
.clang_arg("--target=x86_64-unknown-linux")
708+
.generate()
709+
.unwrap()
710+
.to_string();
711+
712+
let actual = format_code(actual).unwrap();
713+
let expected = format_code("pub const ORDER_VALUE: u32 = 1;\n").unwrap();
714+
715+
assert_eq!(expected, actual);
716+
}
717+
718+
#[test]
719+
fn test_macro_fallback_header_contents_preserves_include_identity() {
720+
if let Some((9, _)) = clang_version().parsed {
721+
return;
722+
}
723+
724+
let tmpdir = tempfile::tempdir().unwrap();
725+
let header = tmpdir.path().join("primary.h");
726+
fs::write(
727+
&header,
728+
"#undef BINDGEN_VIRTUAL_FUNC_3353\n\
729+
#include \"bindgen_virtual_dependency_3353.h\"\n\
730+
#define FROM_INCLUDE BINDGEN_VIRTUAL_FUNC_3353(7)\n",
731+
)
732+
.unwrap();
733+
let virtual_dir = tmpdir.path().join("virtual");
734+
fs::create_dir(&virtual_dir).unwrap();
735+
let virtual_header = virtual_dir.join("bindgen_virtual_dependency_3353.h");
736+
737+
let actual = builder()
738+
.disable_header_comment()
739+
.header(header.to_str().unwrap())
740+
.header_contents(
741+
virtual_header.to_str().unwrap(),
742+
"#define BINDGEN_VIRTUAL_FUNC_3353(c) c ## U\n",
743+
)
744+
.clang_macro_fallback()
745+
.clang_macro_fallback_build_dir(tmpdir.path())
746+
.clang_arg(format!("-I{}", virtual_dir.display()))
747+
.clang_arg("--target=x86_64-unknown-linux")
748+
.generate()
749+
.unwrap()
750+
.to_string();
751+
752+
let actual = format_code(actual).unwrap();
753+
let expected = format_code("pub const FROM_INCLUDE: u32 = 7;\n").unwrap();
754+
755+
assert_eq!(expected, actual);
756+
}
757+
758+
#[test]
759+
fn test_macro_fallback_header_contents_preserves_full_include_identity() {
760+
if let Some((9, _)) = clang_version().parsed {
761+
return;
762+
}
763+
764+
let tmpdir = tempfile::tempdir().unwrap();
765+
let header = tmpdir.path().join("primary.h");
766+
fs::write(&header, "#define FROM_COMMON COMMON_MACRO(9)\n").unwrap();
767+
768+
let virtual_dir = tmpdir.path().join("virtual");
769+
let a_dir = virtual_dir.join("a");
770+
let b_dir = virtual_dir.join("b");
771+
fs::create_dir_all(&a_dir).unwrap();
772+
fs::create_dir_all(&b_dir).unwrap();
773+
let a_header = a_dir.join("common.h");
774+
let b_header = b_dir.join("common.h");
775+
776+
let actual = builder()
777+
.disable_header_comment()
778+
.header(header.to_str().unwrap())
779+
.header_contents(
780+
a_header.to_str().unwrap(),
781+
"#define COMMON_MACRO(c) 0\n",
782+
)
783+
.header_contents(
784+
b_header.to_str().unwrap(),
785+
"#undef COMMON_MACRO\n#define COMMON_MACRO(c) c ## U\n",
786+
)
787+
.clang_macro_fallback()
788+
.clang_macro_fallback_build_dir(tmpdir.path())
789+
.clang_arg("--target=x86_64-unknown-linux")
790+
.generate()
791+
.unwrap()
792+
.to_string();
793+
794+
let actual = format_code(actual).unwrap();
795+
let expected = format_code("pub const FROM_COMMON: u32 = 9;\n").unwrap();
796+
797+
assert_eq!(expected, actual);
798+
}
799+
620800
#[test]
621801
// Doesn't support executing sh file on Windows.
622802
// We may want to implement it in Rust so that we support all systems.

bindgen/clang.rs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ use std::fs::OpenOptions;
1414
use std::hash::Hash;
1515
use std::hash::Hasher;
1616
use std::os::raw::{c_char, c_int, c_longlong, c_uint, c_ulong, c_ulonglong};
17+
use std::path::Path;
18+
use std::sync::atomic::{AtomicUsize, Ordering};
1719
use std::sync::OnceLock;
1820
use std::{mem, ptr, slice};
1921

22+
/// Counter used to create collision-free macro fallback scratch directories.
23+
static NEXT_FALLBACK_ARTIFACT_DIR: AtomicUsize = AtomicUsize::new(0);
24+
2025
/// Type representing a clang attribute.
2126
///
2227
/// Values of this type can be used to check for different attributes using the `has_attrs`
@@ -1911,12 +1916,61 @@ impl Drop for TranslationUnit {
19111916
}
19121917
}
19131918

1914-
/// Translation unit used for macro fallback parsing
1915-
pub(crate) struct FallbackTranslationUnit {
1919+
/// Scratch files used by macro fallback parsing.
1920+
pub(crate) struct FallbackArtifacts {
19161921
file_path: String,
19171922
pch_path: String,
1923+
dir_path: String,
1924+
}
1925+
1926+
impl FallbackArtifacts {
1927+
/// Create a private artifact directory under `build_dir`.
1928+
pub(crate) fn new(build_dir: &str) -> Option<Self> {
1929+
let process_id = std::process::id();
1930+
let dir = loop {
1931+
let dir_index =
1932+
NEXT_FALLBACK_ARTIFACT_DIR.fetch_add(1, Ordering::Relaxed);
1933+
let path = Path::new(build_dir)
1934+
.join(format!(".macro_eval_{process_id}_{dir_index}"));
1935+
match std::fs::create_dir(&path) {
1936+
Ok(()) => break path,
1937+
Err(error)
1938+
if error.kind() == std::io::ErrorKind::AlreadyExists => {}
1939+
Err(_) => return None,
1940+
}
1941+
};
1942+
let dir_path = dir.to_str()?.to_owned();
1943+
Some(FallbackArtifacts {
1944+
file_path: format!("{dir_path}/.macro_eval.c"),
1945+
pch_path: format!("{dir_path}/.macro_eval-precompile.h.pch"),
1946+
dir_path,
1947+
})
1948+
}
1949+
1950+
/// Path to the C file used for fallback macro parsing.
1951+
pub(crate) fn file_path(&self) -> &str {
1952+
&self.file_path
1953+
}
1954+
1955+
/// Path to the precompiled header used for fallback macro parsing.
1956+
pub(crate) fn pch_path(&self) -> &str {
1957+
&self.pch_path
1958+
}
1959+
}
1960+
1961+
impl Drop for FallbackArtifacts {
1962+
fn drop(&mut self) {
1963+
let _ = std::fs::remove_file(&self.file_path);
1964+
let _ = std::fs::remove_file(&self.pch_path);
1965+
let _ = std::fs::remove_dir(&self.dir_path);
1966+
}
1967+
}
1968+
1969+
/// Translation unit used for macro fallback parsing.
1970+
pub(crate) struct FallbackTranslationUnit {
19181971
idx: Box<Index>,
19191972
tu: TranslationUnit,
1973+
artifacts: FallbackArtifacts,
19201974
}
19211975

19221976
impl fmt::Debug for FallbackTranslationUnit {
@@ -1928,31 +1982,29 @@ impl fmt::Debug for FallbackTranslationUnit {
19281982
impl FallbackTranslationUnit {
19291983
/// Create a new fallback translation unit
19301984
pub(crate) fn new(
1931-
file: String,
1932-
pch_path: String,
1985+
artifacts: FallbackArtifacts,
19331986
c_args: &[Box<str>],
19341987
) -> Option<Self> {
19351988
// Create empty file
19361989
OpenOptions::new()
19371990
.write(true)
19381991
.create(true)
19391992
.truncate(true)
1940-
.open(&file)
1993+
.open(artifacts.file_path())
19411994
.ok()?;
19421995

19431996
let f_index = Box::new(Index::new(true, false));
19441997
let f_translation_unit = TranslationUnit::parse(
19451998
&f_index,
1946-
&file,
1999+
artifacts.file_path(),
19472000
c_args,
19482001
&[],
19492002
CXTranslationUnit_None,
19502003
)?;
19512004
Some(FallbackTranslationUnit {
1952-
file_path: file,
1953-
pch_path,
19542005
tu: f_translation_unit,
19552006
idx: f_index,
2007+
artifacts,
19562008
})
19572009
}
19582010

@@ -1966,7 +2018,10 @@ impl FallbackTranslationUnit {
19662018
&mut self,
19672019
unsaved_contents: &str,
19682020
) -> Result<(), CXErrorCode> {
1969-
let unsaved = &[UnsavedFile::new(&self.file_path, unsaved_contents)];
2021+
let unsaved = &[UnsavedFile::new(
2022+
self.artifacts.file_path(),
2023+
unsaved_contents,
2024+
)];
19702025
let mut c_unsaved: Vec<CXUnsavedFile> =
19712026
unsaved.iter().map(|f| f.x).collect();
19722027
let ret = unsafe {
@@ -1985,13 +2040,6 @@ impl FallbackTranslationUnit {
19852040
}
19862041
}
19872042

1988-
impl Drop for FallbackTranslationUnit {
1989-
fn drop(&mut self) {
1990-
let _ = std::fs::remove_file(&self.file_path);
1991-
let _ = std::fs::remove_file(&self.pch_path);
1992-
}
1993-
}
1994-
19952043
/// A diagnostic message generated while parsing a translation unit.
19962044
pub(crate) struct Diagnostic {
19972045
x: CXDiagnostic,

0 commit comments

Comments
 (0)