From f77d4d7b2a480a74b7295d471db5c632038dbdbc Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 14 Nov 2023 18:31:38 -0800 Subject: [PATCH] Draft clang detection --- Cargo.lock | 1 + pgrx-pg-sys/Cargo.toml | 1 + pgrx-pg-sys/build.rs | 59 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3209feb9cf..77a4c9de13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1597,6 +1597,7 @@ name = "pgrx-pg-sys" version = "0.11.0" dependencies = [ "bindgen", + "clang-sys", "eyre", "libc", "memoffset", diff --git a/pgrx-pg-sys/Cargo.toml b/pgrx-pg-sys/Cargo.toml index 3f3f4e8b42..22dba4224e 100644 --- a/pgrx-pg-sys/Cargo.toml +++ b/pgrx-pg-sys/Cargo.toml @@ -48,6 +48,7 @@ libc = "0.2" [build-dependencies] bindgen = { version = "0.68.1", default-features = false, features = ["runtime"] } +clang-sys = { version = "1", features = ["clang_6_0", "runtime"] } pgrx-pg-config= { path = "../pgrx-pg-config/", version = "=0.11.0" } proc-macro2 = "1.0.69" quote = "1.0.33" diff --git a/pgrx-pg-sys/build.rs b/pgrx-pg-sys/build.rs index 233ab854f8..d038f0a7a4 100644 --- a/pgrx-pg-sys/build.rs +++ b/pgrx-pg-sys/build.rs @@ -1,3 +1,4 @@ +use bindgen::ClangVersion; //LICENSE Portions Copyright 2019-2021 ZomboDB, LLC. //LICENSE //LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc. @@ -715,7 +716,10 @@ fn run_bindgen( ) -> eyre::Result { eprintln!("Generating bindings for pg{major_version}"); let configure = pg_config.configure()?; - eprintln!("pg_config --configure: {:?}", configure); + let preferred_clang: Option<&std::path::Path> = configure.get("CLANG").map(|s| s.as_ref()); + eprintln!("pg_config --configure CLANG = {:?}", preferred_clang); + let (autodetect, _includes) = detect_include_paths_for_correct_clang(preferred_clang); + eprintln!("passed detect_include_paths_for_correct_clang"); let mut binder = bindgen::Builder::default(); binder = add_blocklists(binder); binder = add_derives(binder); @@ -725,7 +729,7 @@ fn run_bindgen( .clang_arg("-H") .clang_args(extra_bindgen_clang_args(pg_config)?) .clang_args(pg_target_include_flags(major_version, pg_config)?) - .detect_include_paths(target_env_tracked("PGRX_BINDGEN_NO_DETECT_INCLUDES").is_none()) + .detect_include_paths(autodetect) .parse_callbacks(Box::new(PgrxOverrides::default())) // The NodeTag enum is closed: additions break existing values in the set, so it is not extensible .rustified_non_exhaustive_enum("NodeTag") @@ -739,6 +743,57 @@ fn run_bindgen( Ok(bindings.to_string()) } +/// pgrx's bindgen needs to detect include paths, to keep code building, +/// but the way rust-bindgen does it breaks on Postgres 16 due to code like +/// ```c +/// #include +/// ``` +/// This will pull in builtin headers, but rust-bindgen uses a $CLANG_PATH lookup from clang-sys +/// which is not guaranteed to find the clang that uses the $LIBCLANG_PATH that bindgen intends. +/// +/// Returns the set of paths to include. +fn detect_include_paths_for_correct_clang( + preferred_clang: Option<&std::path::Path>, +) -> (bool, Vec) { + if target_env_tracked("PGRX_BINDGEN_NO_DETECT_INCLUDES").is_some() { + return (false, vec![]); + } + + // By asking bindgen for the version, we force it to pull an appropriate libclang, + // allowing users to override it however they would usually override bindgen. + let clang_major = match bindgen::clang_version() { + ClangVersion { parsed: Some((major, _)), full } => { + eprintln!("Bindgen found {full}"); + major + } + ClangVersion { full, .. } => { + // If bindgen doesn't know what version it has, bail and hope for the best. + eprintln!("Bindgen failed to parse clang version: {full}"); + return (true, vec![]); + } + }; + + // If Postgres is configured --with-llvm, then it may have recorded a CLANG to use + // Ask if there's a clang at the path that Postgres would use for JIT purposes. + // If it matches the major version that bindgen has pulled for us, we can use it. + if let Some(clang_sys::support::Clang { path, version: Some(v), c_search_paths, .. }) = + clang_sys::support::Clang::find(preferred_clang, &[]) + { + if Some(&*path) == preferred_clang && v.Major as u32 == clang_major { + return (false, c_search_paths.unwrap_or_default()); + } + } + + // Oh no, still here? + // Let's go behind bindgen's back to get libclang's path + let libclang_path = clang_sys::get_library().expect("libclang should have been loaded?").path().to_owned(); + eprintln!("found libclang at {}", libclang_path.display()); + // libclang will be in a library directory, + // which means it will probably be adjacent to its headers. + + todo!() +} + fn add_blocklists(bind: bindgen::Builder) -> bindgen::Builder { bind.blocklist_type("Datum") // manually wrapping datum for correctness .blocklist_type("Oid") // "Oid" is not just any u32