From 51778893c4cb18eb11cbfe83a72046f7cb0f3eac Mon Sep 17 00:00:00 2001 From: Andrey Pushkar Date: Sat, 1 Aug 2020 19:26:16 +0300 Subject: [PATCH] Use absolute paths for unsaved files passed to clang and prepend -include directives to them. Fixes #1771 Closes #1857 --- src/lib.rs | 15 ++- .../test_mixed_header_and_header_contents.rs | 111 ++++++++++++++++++ tests/tests.rs | 54 +++++++++ 3 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 tests/expectations/tests/test_mixed_header_and_header_contents.rs diff --git a/src/lib.rs b/src/lib.rs index b9e1bcc5..2329dee9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -596,8 +596,16 @@ impl Builder { /// /// The file `name` will be added to the clang arguments. pub fn header_contents(mut self, name: &str, contents: &str) -> Builder { + // Apparently clang relies on having virtual FS correspondent to + // the real one, so we need absolute paths here + let absolute_path = env::current_dir() + .expect("Cannot retrieve current directory") + .join(name) + .to_str() + .expect("Cannot convert current directory name to string") + .to_owned(); self.input_header_contents - .push((name.into(), contents.into())); + .push((absolute_path, contents.into())); self } @@ -2154,7 +2162,10 @@ impl Bindings { } } - for f in options.input_unsaved_files.iter() { + for (idx, f) in options.input_unsaved_files.iter().enumerate() { + if idx != 0 || options.input_header.is_some() { + options.clang_args.push("-include".to_owned()); + } options.clang_args.push(f.name.to_str().unwrap().to_owned()) } diff --git a/tests/expectations/tests/test_mixed_header_and_header_contents.rs b/tests/expectations/tests/test_mixed_header_and_header_contents.rs new file mode 100644 index 00000000..c97be9b0 --- /dev/null +++ b/tests/expectations/tests/test_mixed_header_and_header_contents.rs @@ -0,0 +1,111 @@ +extern "C" { + pub static mut foo: ::std::option::Option< + unsafe extern "C" fn( + x: ::std::os::raw::c_int, + y: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >; +} +extern "C" { + pub fn bar(a: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn bar2(b: *const ::std::os::raw::c_char) -> f32; +} +pub type Char = ::std::os::raw::c_char; +pub type SChar = ::std::os::raw::c_schar; +pub type UChar = ::std::os::raw::c_uchar; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Test { + pub ch: ::std::os::raw::c_char, + pub u: ::std::os::raw::c_uchar, + pub d: ::std::os::raw::c_schar, + pub cch: ::std::os::raw::c_char, + pub cu: ::std::os::raw::c_uchar, + pub cd: ::std::os::raw::c_schar, + pub Cch: Char, + pub Cu: UChar, + pub Cd: SChar, + pub Ccch: Char, + pub Ccu: UChar, + pub Ccd: SChar, +} +#[test] +fn bindgen_test_layout_Test() { + assert_eq!( + ::std::mem::size_of::(), + 12usize, + concat!("Size of: ", stringify!(Test)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(Test)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).ch as *const _ as usize }, + 0usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(ch)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).u as *const _ as usize }, + 1usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(u)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).d as *const _ as usize }, + 2usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(d)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cch as *const _ as usize }, + 3usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(cch)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cu as *const _ as usize }, + 4usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(cu)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).cd as *const _ as usize }, + 5usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(cd)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).Cch as *const _ as usize }, + 6usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(Cch)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).Cu as *const _ as usize }, + 7usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(Cu)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).Cd as *const _ as usize }, + 8usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(Cd)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).Ccch as *const _ as usize }, + 9usize, + concat!( + "Offset of field: ", + stringify!(Test), + "::", + stringify!(Ccch) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).Ccu as *const _ as usize }, + 10usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(Ccu)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).Ccd as *const _ as usize }, + 11usize, + concat!("Offset of field: ", stringify!(Test), "::", stringify!(Ccd)) + ); +} diff --git a/tests/tests.rs b/tests/tests.rs index 8b5a91f7..cd621177 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -473,6 +473,60 @@ fn test_multiple_header_calls_in_builder() { } } +#[test] +fn test_multiple_header_contents() { + let actual = builder() + .header_contents("test.h", "int foo(const char* a);") + .header_contents("test2.h", "float foo2(const char* b);") + .clang_arg("--target=x86_64-unknown-linux") + .generate() + .unwrap() + .to_string(); + + let (actual, stderr) = rustfmt(actual); + println!("{}", stderr); + + let (expected, _) = rustfmt( + "extern \"C\" { + pub fn foo2(b: *const ::std::os::raw::c_char) -> f32; +} +extern \"C\" { + pub fn foo(a: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; +} +" + .to_string(), + ); + + assert_eq!(expected, actual); +} + +#[test] +fn test_mixed_header_and_header_contents() { + let actual = builder() + .header(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/headers/func_ptr.h" + )) + .header(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/headers/char.h")) + .header_contents("test.h", "int bar(const char* a);") + .header_contents("test2.h", "float bar2(const char* b);") + .clang_arg("--target=x86_64-unknown-linux") + .generate() + .unwrap() + .to_string(); + + let (actual, stderr) = rustfmt(actual); + println!("{}", stderr); + + let expected = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/expectations/tests/test_mixed_header_and_header_contents.rs" + )); + let (expected, _) = rustfmt(expected.to_string()); + + assert_eq!(expected, actual); +} + #[test] // Doesn't support executing sh file on Windows. // We may want to implement it in Rust so that we support all systems.