dinghy_build/
bindgen_macros.rs

1/// Create a new `bindgen::Builder` set up and ready for cross-compilation.
2///
3/// This macro should be used for bindgen versions from 0.49 and above.
4#[macro_export]
5macro_rules! dinghy_bindgen {
6    () => {{
7        let bindgen = $crate::dinghy_bindgen_pre_0_49!();
8
9        if $crate::build::is_cross_compiling().expect("Couldn't determine if it is cross-compiling")
10        {
11            bindgen.detect_include_paths(false)
12        } else {
13            bindgen
14        }
15    }};
16}
17
18/// Compatibility macro for bindgen versions below 0.49
19#[macro_export]
20macro_rules! dinghy_bindgen_pre_0_49 {
21    () => {{
22        use $crate::build::is_cross_compiling;
23        use $crate::build_env::sysroot_path;
24        use $crate::utils::path_to_str;
25        use $crate::{Result, Context};
26
27        fn apple_patch(builder: bindgen::Builder) -> Result<bindgen::Builder> {
28            if is_cross_compiling()? {
29                let target = env::var("TARGET")?;
30                if target.contains("apple") && target.contains("aarch64") {
31                    // The official Apple tools use "-arch arm64" instead of specifying
32                    // -target directly; -arch only works when the default target is
33                    // Darwin-based to put Clang into "Apple mode" as it were. But it does
34                    // sort of explain why arm64 works better than aarch64, which is the
35                    // preferred name everywhere else.
36                    return Ok(builder
37                        .clang_arg(format!("-arch"))
38                        .clang_arg(format!("arm64")));
39                }
40            }
41            Ok(builder)
42        }
43
44        fn libclang_path_patch(builder: bindgen::Builder) -> Result<bindgen::Builder> {
45            if is_cross_compiling()? {
46                if let Ok(libclang_path) = env::var("DINGHY_BUILD_LIBCLANG_PATH") {
47                    env::set_var("LIBCLANG_PATH", libclang_path)
48                }
49            }
50            Ok(builder)
51        }
52
53        fn detect_toolchain(builder: bindgen::Builder) -> Result<bindgen::Builder> {
54            if is_cross_compiling()? {
55                let target = env::var("TARGET")?;
56                let builder = if let Ok(_) = env::var("TARGET_SYSROOT") {
57                    builder.clang_arg(format!("--sysroot={}", path_to_str(&sysroot_path()?)?))
58                } else {
59                    println!("cargo:warning=No Sysroot detected, assuming the target is baremetal. If you have a sysroot, you must either define a TARGET_SYSROOT or use Dinghy to build your project.");
60                    builder
61                };
62                Ok(builder.clang_arg(format!("--target={}", target)))
63            } else {
64                Ok(builder)
65            }
66        }
67
68        fn include_gcc_system_headers(builder: bindgen::Builder) -> Result<bindgen::Builder> {
69            if is_cross_compiling()? {
70                // Add a path to the private headers for the target compiler. Borderline,
71                // as we are likely using a gcc header with clang frontend.
72                let path = cc::Build::new()
73                    .get_compiler()
74                    .to_command()
75                    .arg("--print-file-name=include")
76                    .output()
77                    .with_context(|| "Couldn't find target GCC executable.")
78                    .and_then(|output| {
79                        if output.status.success() {
80                            Ok(String::from_utf8(output.stdout)?)
81                        } else {
82                            panic!("Couldn't determine target GCC include dir.")
83                        }
84                    })?;
85
86                Ok(builder.clang_arg("-isystem").clang_arg(path.trim()))
87            } else {
88                Ok(builder)
89            }
90        }
91
92        libclang_path_patch(
93            apple_patch(
94                include_gcc_system_headers(
95                    detect_toolchain(bindgen::Builder::default().clang_arg("--verbose")).unwrap(),
96                )
97                .unwrap(),
98            )
99            .unwrap()
100        )
101        .unwrap()
102    }};
103}
104
105/// Generate a file containing the bindgen bindings in a standard path.
106///
107/// The standard path is `${OUT_DIR}/bindings.rs`.
108///
109/// To use it, simply perform the call like
110/// `generate_default_bindgen_bindings!(bindgen_builder)`
111#[macro_export]
112macro_rules! generate_bindgen_bindings {
113    ($builder:expr) => {{
114        let out_path = env::var("OUT_DIR")
115            .map(PathBuf::from)
116            .expect("Couldn't convert OUT_DIR var into a path")
117            .join("bindings.rs");
118        $builder
119            .generate()
120            .expect("Unable to generate bindings")
121            .write_to_file(out_path)
122            .expect("Unable to write the bindings in the file")
123    }};
124}