xref: /third_party/rust/crates/clang-sys/src/link.rs (revision 6cdb10c1)
16cdb10c1Sopenharmony_ci// SPDX-License-Identifier: Apache-2.0
26cdb10c1Sopenharmony_ci
36cdb10c1Sopenharmony_ci//================================================
46cdb10c1Sopenharmony_ci// Macros
56cdb10c1Sopenharmony_ci//================================================
66cdb10c1Sopenharmony_ci
76cdb10c1Sopenharmony_ci#[cfg(feature = "runtime")]
86cdb10c1Sopenharmony_cimacro_rules! link {
96cdb10c1Sopenharmony_ci    (
106cdb10c1Sopenharmony_ci        @LOAD:
116cdb10c1Sopenharmony_ci        $(#[doc=$doc:expr])*
126cdb10c1Sopenharmony_ci        #[cfg($cfg:meta)]
136cdb10c1Sopenharmony_ci        fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
146cdb10c1Sopenharmony_ci    ) => (
156cdb10c1Sopenharmony_ci        $(#[doc=$doc])*
166cdb10c1Sopenharmony_ci        #[cfg($cfg)]
176cdb10c1Sopenharmony_ci        pub fn $name(library: &mut super::SharedLibrary) {
186cdb10c1Sopenharmony_ci            let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
196cdb10c1Sopenharmony_ci            library.functions.$name = match symbol {
206cdb10c1Sopenharmony_ci                Some(s) => *s,
216cdb10c1Sopenharmony_ci                None => None,
226cdb10c1Sopenharmony_ci            };
236cdb10c1Sopenharmony_ci        }
246cdb10c1Sopenharmony_ci
256cdb10c1Sopenharmony_ci        #[cfg(not($cfg))]
266cdb10c1Sopenharmony_ci        pub fn $name(_: &mut super::SharedLibrary) {}
276cdb10c1Sopenharmony_ci    );
286cdb10c1Sopenharmony_ci
296cdb10c1Sopenharmony_ci    (
306cdb10c1Sopenharmony_ci        @LOAD:
316cdb10c1Sopenharmony_ci        fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
326cdb10c1Sopenharmony_ci    ) => (
336cdb10c1Sopenharmony_ci        link!(@LOAD: #[cfg(feature = "runtime")] fn $name($($pname: $pty), *) $(-> $ret)*);
346cdb10c1Sopenharmony_ci    );
356cdb10c1Sopenharmony_ci
366cdb10c1Sopenharmony_ci    (
376cdb10c1Sopenharmony_ci        $(
386cdb10c1Sopenharmony_ci            $(#[doc=$doc:expr] #[cfg($cfg:meta)])*
396cdb10c1Sopenharmony_ci            pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
406cdb10c1Sopenharmony_ci        )+
416cdb10c1Sopenharmony_ci    ) => (
426cdb10c1Sopenharmony_ci        use std::cell::{RefCell};
436cdb10c1Sopenharmony_ci        use std::sync::{Arc};
446cdb10c1Sopenharmony_ci        use std::path::{Path, PathBuf};
456cdb10c1Sopenharmony_ci
466cdb10c1Sopenharmony_ci        /// The (minimum) version of a `libclang` shared library.
476cdb10c1Sopenharmony_ci        #[allow(missing_docs)]
486cdb10c1Sopenharmony_ci        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
496cdb10c1Sopenharmony_ci        pub enum Version {
506cdb10c1Sopenharmony_ci            V3_5 = 35,
516cdb10c1Sopenharmony_ci            V3_6 = 36,
526cdb10c1Sopenharmony_ci            V3_7 = 37,
536cdb10c1Sopenharmony_ci            V3_8 = 38,
546cdb10c1Sopenharmony_ci            V3_9 = 39,
556cdb10c1Sopenharmony_ci            V4_0 = 40,
566cdb10c1Sopenharmony_ci            V5_0 = 50,
576cdb10c1Sopenharmony_ci            V6_0 = 60,
586cdb10c1Sopenharmony_ci            V7_0 = 70,
596cdb10c1Sopenharmony_ci            V8_0 = 80,
606cdb10c1Sopenharmony_ci            V9_0 = 90,
616cdb10c1Sopenharmony_ci        }
626cdb10c1Sopenharmony_ci
636cdb10c1Sopenharmony_ci        /// The set of functions loaded dynamically.
646cdb10c1Sopenharmony_ci        #[derive(Debug, Default)]
656cdb10c1Sopenharmony_ci        pub struct Functions {
666cdb10c1Sopenharmony_ci            $(
676cdb10c1Sopenharmony_ci                $(#[doc=$doc] #[cfg($cfg)])*
686cdb10c1Sopenharmony_ci                pub $name: Option<unsafe extern fn($($pname: $pty), *) $(-> $ret)*>,
696cdb10c1Sopenharmony_ci            )+
706cdb10c1Sopenharmony_ci        }
716cdb10c1Sopenharmony_ci
726cdb10c1Sopenharmony_ci        /// A dynamically loaded instance of the `libclang` library.
736cdb10c1Sopenharmony_ci        #[derive(Debug)]
746cdb10c1Sopenharmony_ci        pub struct SharedLibrary {
756cdb10c1Sopenharmony_ci            library: libloading::Library,
766cdb10c1Sopenharmony_ci            path: PathBuf,
776cdb10c1Sopenharmony_ci            pub functions: Functions,
786cdb10c1Sopenharmony_ci        }
796cdb10c1Sopenharmony_ci
806cdb10c1Sopenharmony_ci        impl SharedLibrary {
816cdb10c1Sopenharmony_ci            fn new(library: libloading::Library, path: PathBuf) -> Self {
826cdb10c1Sopenharmony_ci                Self { library, path, functions: Functions::default() }
836cdb10c1Sopenharmony_ci            }
846cdb10c1Sopenharmony_ci
856cdb10c1Sopenharmony_ci            /// Returns the path to this `libclang` shared library.
866cdb10c1Sopenharmony_ci            pub fn path(&self) -> &Path {
876cdb10c1Sopenharmony_ci                &self.path
886cdb10c1Sopenharmony_ci            }
896cdb10c1Sopenharmony_ci
906cdb10c1Sopenharmony_ci            /// Returns the (minimum) version of this `libclang` shared library.
916cdb10c1Sopenharmony_ci            ///
926cdb10c1Sopenharmony_ci            /// If this returns `None`, it indicates that the version is too old
936cdb10c1Sopenharmony_ci            /// to be supported by this crate (i.e., `3.4` or earlier). If the
946cdb10c1Sopenharmony_ci            /// version of this shared library is more recent than that fully
956cdb10c1Sopenharmony_ci            /// supported by this crate, the most recent fully supported version
966cdb10c1Sopenharmony_ci            /// will be returned.
976cdb10c1Sopenharmony_ci            pub fn version(&self) -> Option<Version> {
986cdb10c1Sopenharmony_ci                macro_rules! check {
996cdb10c1Sopenharmony_ci                    ($fn:expr, $version:ident) => {
1006cdb10c1Sopenharmony_ci                        if self.library.get::<unsafe extern fn()>($fn).is_ok() {
1016cdb10c1Sopenharmony_ci                            return Some(Version::$version);
1026cdb10c1Sopenharmony_ci                        }
1036cdb10c1Sopenharmony_ci                    };
1046cdb10c1Sopenharmony_ci                }
1056cdb10c1Sopenharmony_ci
1066cdb10c1Sopenharmony_ci                unsafe {
1076cdb10c1Sopenharmony_ci                    check!(b"clang_Cursor_isAnonymousRecordDecl", V9_0);
1086cdb10c1Sopenharmony_ci                    check!(b"clang_Cursor_getObjCPropertyGetterName", V8_0);
1096cdb10c1Sopenharmony_ci                    check!(b"clang_File_tryGetRealPathName", V7_0);
1106cdb10c1Sopenharmony_ci                    check!(b"clang_CXIndex_setInvocationEmissionPathOption", V6_0);
1116cdb10c1Sopenharmony_ci                    check!(b"clang_Cursor_isExternalSymbol", V5_0);
1126cdb10c1Sopenharmony_ci                    check!(b"clang_EvalResult_getAsLongLong", V4_0);
1136cdb10c1Sopenharmony_ci                    check!(b"clang_CXXConstructor_isConvertingConstructor", V3_9);
1146cdb10c1Sopenharmony_ci                    check!(b"clang_CXXField_isMutable", V3_8);
1156cdb10c1Sopenharmony_ci                    check!(b"clang_Cursor_getOffsetOfField", V3_7);
1166cdb10c1Sopenharmony_ci                    check!(b"clang_Cursor_getStorageClass", V3_6);
1176cdb10c1Sopenharmony_ci                    check!(b"clang_Type_getNumTemplateArguments", V3_5);
1186cdb10c1Sopenharmony_ci                }
1196cdb10c1Sopenharmony_ci
1206cdb10c1Sopenharmony_ci                None
1216cdb10c1Sopenharmony_ci            }
1226cdb10c1Sopenharmony_ci        }
1236cdb10c1Sopenharmony_ci
1246cdb10c1Sopenharmony_ci        thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None));
1256cdb10c1Sopenharmony_ci
1266cdb10c1Sopenharmony_ci        /// Returns whether a `libclang` shared library is loaded on this thread.
1276cdb10c1Sopenharmony_ci        pub fn is_loaded() -> bool {
1286cdb10c1Sopenharmony_ci            LIBRARY.with(|l| l.borrow().is_some())
1296cdb10c1Sopenharmony_ci        }
1306cdb10c1Sopenharmony_ci
1316cdb10c1Sopenharmony_ci        fn with_library<T, F>(f: F) -> Option<T> where F: FnOnce(&SharedLibrary) -> T {
1326cdb10c1Sopenharmony_ci            LIBRARY.with(|l| {
1336cdb10c1Sopenharmony_ci                match l.borrow().as_ref() {
1346cdb10c1Sopenharmony_ci                    Some(library) => Some(f(&library)),
1356cdb10c1Sopenharmony_ci                    _ => None,
1366cdb10c1Sopenharmony_ci                }
1376cdb10c1Sopenharmony_ci            })
1386cdb10c1Sopenharmony_ci        }
1396cdb10c1Sopenharmony_ci
1406cdb10c1Sopenharmony_ci        $(
1416cdb10c1Sopenharmony_ci            #[cfg_attr(feature="cargo-clippy", allow(clippy::missing_safety_doc))]
1426cdb10c1Sopenharmony_ci            #[cfg_attr(feature="cargo-clippy", allow(clippy::too_many_arguments))]
1436cdb10c1Sopenharmony_ci            $(#[doc=$doc] #[cfg($cfg)])*
1446cdb10c1Sopenharmony_ci            pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
1456cdb10c1Sopenharmony_ci                let f = with_library(|l| {
1466cdb10c1Sopenharmony_ci                    l.functions.$name.expect(concat!(
1476cdb10c1Sopenharmony_ci                        "`libclang` function not loaded: `",
1486cdb10c1Sopenharmony_ci                        stringify!($name),
1496cdb10c1Sopenharmony_ci                        "`. This crate requires that `libclang` 3.9 or later be installed on your ",
1506cdb10c1Sopenharmony_ci                        "system. For more information on how to accomplish this, see here: ",
1516cdb10c1Sopenharmony_ci                        "https://rust-lang.github.io/rust-bindgen/requirements.html#installing-clang-39"))
1526cdb10c1Sopenharmony_ci                }).expect("a `libclang` shared library is not loaded on this thread");
1536cdb10c1Sopenharmony_ci                f($($pname), *)
1546cdb10c1Sopenharmony_ci            }
1556cdb10c1Sopenharmony_ci
1566cdb10c1Sopenharmony_ci            $(#[doc=$doc] #[cfg($cfg)])*
1576cdb10c1Sopenharmony_ci            pub mod $name {
1586cdb10c1Sopenharmony_ci                pub fn is_loaded() -> bool {
1596cdb10c1Sopenharmony_ci                    super::with_library(|l| l.functions.$name.is_some()).unwrap_or(false)
1606cdb10c1Sopenharmony_ci                }
1616cdb10c1Sopenharmony_ci            }
1626cdb10c1Sopenharmony_ci        )+
1636cdb10c1Sopenharmony_ci
1646cdb10c1Sopenharmony_ci        mod load {
1656cdb10c1Sopenharmony_ci            $(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
1666cdb10c1Sopenharmony_ci        }
1676cdb10c1Sopenharmony_ci
1686cdb10c1Sopenharmony_ci        /// Loads a `libclang` shared library and returns the library instance.
1696cdb10c1Sopenharmony_ci        ///
1706cdb10c1Sopenharmony_ci        /// This function does not attempt to load any functions from the shared library. The caller
1716cdb10c1Sopenharmony_ci        /// is responsible for loading the functions they require.
1726cdb10c1Sopenharmony_ci        ///
1736cdb10c1Sopenharmony_ci        /// # Failures
1746cdb10c1Sopenharmony_ci        ///
1756cdb10c1Sopenharmony_ci        /// * a `libclang` shared library could not be found
1766cdb10c1Sopenharmony_ci        /// * the `libclang` shared library could not be opened
1776cdb10c1Sopenharmony_ci        pub fn load_manually() -> Result<SharedLibrary, String> {
1786cdb10c1Sopenharmony_ci            mod build {
1796cdb10c1Sopenharmony_ci                pub mod common { include!(concat!(env!("OUT_DIR"), "/common.rs")); }
1806cdb10c1Sopenharmony_ci                pub mod dynamic { include!(concat!(env!("OUT_DIR"), "/dynamic.rs")); }
1816cdb10c1Sopenharmony_ci            }
1826cdb10c1Sopenharmony_ci
1836cdb10c1Sopenharmony_ci            let (directory, filename) = build::dynamic::find(true)?;
1846cdb10c1Sopenharmony_ci            let path = directory.join(filename);
1856cdb10c1Sopenharmony_ci
1866cdb10c1Sopenharmony_ci            unsafe {
1876cdb10c1Sopenharmony_ci                let library = libloading::Library::new(&path).map_err(|e| {
1886cdb10c1Sopenharmony_ci                    format!(
1896cdb10c1Sopenharmony_ci                        "the `libclang` shared library at {} could not be opened: {}",
1906cdb10c1Sopenharmony_ci                        path.display(),
1916cdb10c1Sopenharmony_ci                        e,
1926cdb10c1Sopenharmony_ci                    )
1936cdb10c1Sopenharmony_ci                });
1946cdb10c1Sopenharmony_ci
1956cdb10c1Sopenharmony_ci                let mut library = SharedLibrary::new(library?, path);
1966cdb10c1Sopenharmony_ci                $(load::$name(&mut library);)+
1976cdb10c1Sopenharmony_ci                Ok(library)
1986cdb10c1Sopenharmony_ci            }
1996cdb10c1Sopenharmony_ci        }
2006cdb10c1Sopenharmony_ci
2016cdb10c1Sopenharmony_ci        /// Loads a `libclang` shared library for use in the current thread.
2026cdb10c1Sopenharmony_ci        ///
2036cdb10c1Sopenharmony_ci        /// This functions attempts to load all the functions in the shared library. Whether a
2046cdb10c1Sopenharmony_ci        /// function has been loaded can be tested by calling the `is_loaded` function on the
2056cdb10c1Sopenharmony_ci        /// module with the same name as the function (e.g., `clang_createIndex::is_loaded()` for
2066cdb10c1Sopenharmony_ci        /// the `clang_createIndex` function).
2076cdb10c1Sopenharmony_ci        ///
2086cdb10c1Sopenharmony_ci        /// # Failures
2096cdb10c1Sopenharmony_ci        ///
2106cdb10c1Sopenharmony_ci        /// * a `libclang` shared library could not be found
2116cdb10c1Sopenharmony_ci        /// * the `libclang` shared library could not be opened
2126cdb10c1Sopenharmony_ci        #[allow(dead_code)]
2136cdb10c1Sopenharmony_ci        pub fn load() -> Result<(), String> {
2146cdb10c1Sopenharmony_ci            let library = Arc::new(load_manually()?);
2156cdb10c1Sopenharmony_ci            LIBRARY.with(|l| *l.borrow_mut() = Some(library));
2166cdb10c1Sopenharmony_ci            Ok(())
2176cdb10c1Sopenharmony_ci        }
2186cdb10c1Sopenharmony_ci
2196cdb10c1Sopenharmony_ci        /// Unloads the `libclang` shared library in use in the current thread.
2206cdb10c1Sopenharmony_ci        ///
2216cdb10c1Sopenharmony_ci        /// # Failures
2226cdb10c1Sopenharmony_ci        ///
2236cdb10c1Sopenharmony_ci        /// * a `libclang` shared library is not in use in the current thread
2246cdb10c1Sopenharmony_ci        pub fn unload() -> Result<(), String> {
2256cdb10c1Sopenharmony_ci            let library = set_library(None);
2266cdb10c1Sopenharmony_ci            if library.is_some() {
2276cdb10c1Sopenharmony_ci                Ok(())
2286cdb10c1Sopenharmony_ci            } else {
2296cdb10c1Sopenharmony_ci                Err("a `libclang` shared library is not in use in the current thread".into())
2306cdb10c1Sopenharmony_ci            }
2316cdb10c1Sopenharmony_ci        }
2326cdb10c1Sopenharmony_ci
2336cdb10c1Sopenharmony_ci        /// Returns the library instance stored in TLS.
2346cdb10c1Sopenharmony_ci        ///
2356cdb10c1Sopenharmony_ci        /// This functions allows for sharing library instances between threads.
2366cdb10c1Sopenharmony_ci        pub fn get_library() -> Option<Arc<SharedLibrary>> {
2376cdb10c1Sopenharmony_ci            LIBRARY.with(|l| l.borrow_mut().clone())
2386cdb10c1Sopenharmony_ci        }
2396cdb10c1Sopenharmony_ci
2406cdb10c1Sopenharmony_ci        /// Sets the library instance stored in TLS and returns the previous library.
2416cdb10c1Sopenharmony_ci        ///
2426cdb10c1Sopenharmony_ci        /// This functions allows for sharing library instances between threads.
2436cdb10c1Sopenharmony_ci        pub fn set_library(library: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> {
2446cdb10c1Sopenharmony_ci            LIBRARY.with(|l| mem::replace(&mut *l.borrow_mut(), library))
2456cdb10c1Sopenharmony_ci        }
2466cdb10c1Sopenharmony_ci    )
2476cdb10c1Sopenharmony_ci}
2486cdb10c1Sopenharmony_ci
2496cdb10c1Sopenharmony_ci#[cfg(not(feature = "runtime"))]
2506cdb10c1Sopenharmony_cimacro_rules! link {
2516cdb10c1Sopenharmony_ci    (
2526cdb10c1Sopenharmony_ci        $(
2536cdb10c1Sopenharmony_ci            $(#[doc=$doc:expr] #[cfg($cfg:meta)])*
2546cdb10c1Sopenharmony_ci            pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
2556cdb10c1Sopenharmony_ci        )+
2566cdb10c1Sopenharmony_ci    ) => (
2576cdb10c1Sopenharmony_ci        extern {
2586cdb10c1Sopenharmony_ci            $(
2596cdb10c1Sopenharmony_ci                $(#[doc=$doc] #[cfg($cfg)])*
2606cdb10c1Sopenharmony_ci                pub fn $name($($pname: $pty), *) $(-> $ret)*;
2616cdb10c1Sopenharmony_ci            )+
2626cdb10c1Sopenharmony_ci        }
2636cdb10c1Sopenharmony_ci
2646cdb10c1Sopenharmony_ci        $(
2656cdb10c1Sopenharmony_ci            $(#[doc=$doc] #[cfg($cfg)])*
2666cdb10c1Sopenharmony_ci            pub mod $name {
2676cdb10c1Sopenharmony_ci                pub fn is_loaded() -> bool { true }
2686cdb10c1Sopenharmony_ci            }
2696cdb10c1Sopenharmony_ci        )+
2706cdb10c1Sopenharmony_ci    )
2716cdb10c1Sopenharmony_ci}
272