diff options
Diffstat (limited to 'third_party/rust/clang-sys/src/link.rs')
-rw-r--r-- | third_party/rust/clang-sys/src/link.rs | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/third_party/rust/clang-sys/src/link.rs b/third_party/rust/clang-sys/src/link.rs new file mode 100644 index 0000000000..1f0cbda4dd --- /dev/null +++ b/third_party/rust/clang-sys/src/link.rs @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: Apache-2.0
+
+//================================================
+// Macros
+//================================================
+
+#[cfg(feature = "runtime")]
+macro_rules! link {
+ (
+ @LOAD:
+ $(#[doc=$doc:expr])*
+ #[cfg($cfg:meta)]
+ fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
+ ) => (
+ $(#[doc=$doc])*
+ #[cfg($cfg)]
+ pub fn $name(library: &mut super::SharedLibrary) {
+ let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
+ library.functions.$name = match symbol {
+ Some(s) => *s,
+ None => None,
+ };
+ }
+
+ #[cfg(not($cfg))]
+ pub fn $name(_: &mut super::SharedLibrary) {}
+ );
+
+ (
+ @LOAD:
+ fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
+ ) => (
+ link!(@LOAD: #[cfg(feature = "runtime")] fn $name($($pname: $pty), *) $(-> $ret)*);
+ );
+
+ (
+ $(
+ $(#[doc=$doc:expr] #[cfg($cfg:meta)])*
+ pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
+ )+
+ ) => (
+ use std::cell::{RefCell};
+ use std::sync::{Arc};
+ use std::path::{Path, PathBuf};
+
+ /// The (minimum) version of a `libclang` shared library.
+ #[allow(missing_docs)]
+ #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub enum Version {
+ V3_5 = 35,
+ V3_6 = 36,
+ V3_7 = 37,
+ V3_8 = 38,
+ V3_9 = 39,
+ V4_0 = 40,
+ V5_0 = 50,
+ V6_0 = 60,
+ V7_0 = 70,
+ V8_0 = 80,
+ V9_0 = 90,
+ }
+
+ /// The set of functions loaded dynamically.
+ #[derive(Debug, Default)]
+ pub struct Functions {
+ $(
+ $(#[doc=$doc] #[cfg($cfg)])*
+ pub $name: Option<unsafe extern fn($($pname: $pty), *) $(-> $ret)*>,
+ )+
+ }
+
+ /// A dynamically loaded instance of the `libclang` library.
+ #[derive(Debug)]
+ pub struct SharedLibrary {
+ library: libloading::Library,
+ path: PathBuf,
+ pub functions: Functions,
+ }
+
+ impl SharedLibrary {
+ fn new(library: libloading::Library, path: PathBuf) -> Self {
+ Self { library, path, functions: Functions::default() }
+ }
+
+ /// Returns the path to this `libclang` shared library.
+ pub fn path(&self) -> &Path {
+ &self.path
+ }
+
+ /// Returns the (minimum) version of this `libclang` shared library.
+ ///
+ /// If this returns `None`, it indicates that the version is too old
+ /// to be supported by this crate (i.e., `3.4` or earlier). If the
+ /// version of this shared library is more recent than that fully
+ /// supported by this crate, the most recent fully supported version
+ /// will be returned.
+ pub fn version(&self) -> Option<Version> {
+ macro_rules! check {
+ ($fn:expr, $version:ident) => {
+ if self.library.get::<unsafe extern fn()>($fn).is_ok() {
+ return Some(Version::$version);
+ }
+ };
+ }
+
+ unsafe {
+ check!(b"clang_Cursor_isAnonymousRecordDecl", V9_0);
+ check!(b"clang_Cursor_getObjCPropertyGetterName", V8_0);
+ check!(b"clang_File_tryGetRealPathName", V7_0);
+ check!(b"clang_CXIndex_setInvocationEmissionPathOption", V6_0);
+ check!(b"clang_Cursor_isExternalSymbol", V5_0);
+ check!(b"clang_EvalResult_getAsLongLong", V4_0);
+ check!(b"clang_CXXConstructor_isConvertingConstructor", V3_9);
+ check!(b"clang_CXXField_isMutable", V3_8);
+ check!(b"clang_Cursor_getOffsetOfField", V3_7);
+ check!(b"clang_Cursor_getStorageClass", V3_6);
+ check!(b"clang_Type_getNumTemplateArguments", V3_5);
+ }
+
+ None
+ }
+ }
+
+ thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None));
+
+ /// Returns whether a `libclang` shared library is loaded on this thread.
+ pub fn is_loaded() -> bool {
+ LIBRARY.with(|l| l.borrow().is_some())
+ }
+
+ fn with_library<T, F>(f: F) -> Option<T> where F: FnOnce(&SharedLibrary) -> T {
+ LIBRARY.with(|l| {
+ match l.borrow().as_ref() {
+ Some(library) => Some(f(&library)),
+ _ => None,
+ }
+ })
+ }
+
+ $(
+ #[cfg_attr(feature="cargo-clippy", allow(clippy::missing_safety_doc))]
+ #[cfg_attr(feature="cargo-clippy", allow(clippy::too_many_arguments))]
+ $(#[doc=$doc] #[cfg($cfg)])*
+ pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
+ let f = with_library(|l| {
+ l.functions.$name.expect(concat!(
+ "`libclang` function not loaded: `",
+ stringify!($name),
+ "`. This crate requires that `libclang` 3.9 or later be installed on your ",
+ "system. For more information on how to accomplish this, see here: ",
+ "https://rust-lang.github.io/rust-bindgen/requirements.html#installing-clang-39"))
+ }).expect("a `libclang` shared library is not loaded on this thread");
+ f($($pname), *)
+ }
+
+ $(#[doc=$doc] #[cfg($cfg)])*
+ pub mod $name {
+ pub fn is_loaded() -> bool {
+ super::with_library(|l| l.functions.$name.is_some()).unwrap_or(false)
+ }
+ }
+ )+
+
+ mod load {
+ $(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
+ }
+
+ /// Loads a `libclang` shared library and returns the library instance.
+ ///
+ /// This function does not attempt to load any functions from the shared library. The caller
+ /// is responsible for loading the functions they require.
+ ///
+ /// # Failures
+ ///
+ /// * a `libclang` shared library could not be found
+ /// * the `libclang` shared library could not be opened
+ pub fn load_manually() -> Result<SharedLibrary, String> {
+ mod build {
+ pub mod common { include!(concat!(env!("OUT_DIR"), "/common.rs")); }
+ pub mod dynamic { include!(concat!(env!("OUT_DIR"), "/dynamic.rs")); }
+ }
+
+ let (directory, filename) = build::dynamic::find(true)?;
+ let path = directory.join(filename);
+
+ unsafe {
+ let library = libloading::Library::new(&path).map_err(|e| {
+ format!(
+ "the `libclang` shared library at {} could not be opened: {}",
+ path.display(),
+ e,
+ )
+ });
+
+ let mut library = SharedLibrary::new(library?, path);
+ $(load::$name(&mut library);)+
+ Ok(library)
+ }
+ }
+
+ /// Loads a `libclang` shared library for use in the current thread.
+ ///
+ /// This functions attempts to load all the functions in the shared library. Whether a
+ /// function has been loaded can be tested by calling the `is_loaded` function on the
+ /// module with the same name as the function (e.g., `clang_createIndex::is_loaded()` for
+ /// the `clang_createIndex` function).
+ ///
+ /// # Failures
+ ///
+ /// * a `libclang` shared library could not be found
+ /// * the `libclang` shared library could not be opened
+ #[allow(dead_code)]
+ pub fn load() -> Result<(), String> {
+ let library = Arc::new(load_manually()?);
+ LIBRARY.with(|l| *l.borrow_mut() = Some(library));
+ Ok(())
+ }
+
+ /// Unloads the `libclang` shared library in use in the current thread.
+ ///
+ /// # Failures
+ ///
+ /// * a `libclang` shared library is not in use in the current thread
+ pub fn unload() -> Result<(), String> {
+ let library = set_library(None);
+ if library.is_some() {
+ Ok(())
+ } else {
+ Err("a `libclang` shared library is not in use in the current thread".into())
+ }
+ }
+
+ /// Returns the library instance stored in TLS.
+ ///
+ /// This functions allows for sharing library instances between threads.
+ pub fn get_library() -> Option<Arc<SharedLibrary>> {
+ LIBRARY.with(|l| l.borrow_mut().clone())
+ }
+
+ /// Sets the library instance stored in TLS and returns the previous library.
+ ///
+ /// This functions allows for sharing library instances between threads.
+ pub fn set_library(library: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> {
+ LIBRARY.with(|l| mem::replace(&mut *l.borrow_mut(), library))
+ }
+ )
+}
+
+#[cfg(not(feature = "runtime"))]
+macro_rules! link {
+ (
+ $(
+ $(#[doc=$doc:expr] #[cfg($cfg:meta)])*
+ pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
+ )+
+ ) => (
+ extern {
+ $(
+ $(#[doc=$doc] #[cfg($cfg)])*
+ pub fn $name($($pname: $pty), *) $(-> $ret)*;
+ )+
+ }
+
+ $(
+ $(#[doc=$doc] #[cfg($cfg)])*
+ pub mod $name {
+ pub fn is_loaded() -> bool { true }
+ }
+ )+
+ )
+}
|