blob: c3b0830e9f78ad5d5902a6c686eec00c71a46d5a [file] [log] [blame]
David LeGare82e2b172022-03-01 18:53:05 +00001// SPDX-License-Identifier: Apache-2.0
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -07002
3//================================================
4// Macros
5//================================================
6
7#[cfg(feature = "runtime")]
8macro_rules! link {
9 (
10 @LOAD:
11 $(#[doc=$doc:expr])*
12 #[cfg($cfg:meta)]
13 fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
14 ) => (
15 $(#[doc=$doc])*
16 #[cfg($cfg)]
17 pub fn $name(library: &mut super::SharedLibrary) {
18 let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
19 library.functions.$name = match symbol {
20 Some(s) => *s,
21 None => None,
22 };
23 }
24
25 #[cfg(not($cfg))]
26 pub fn $name(_: &mut super::SharedLibrary) {}
27 );
28
29 (
30 @LOAD:
31 fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
32 ) => (
33 link!(@LOAD: #[cfg(feature = "runtime")] fn $name($($pname: $pty), *) $(-> $ret)*);
34 );
35
36 (
37 $(
38 $(#[doc=$doc:expr] #[cfg($cfg:meta)])*
39 pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
40 )+
41 ) => (
42 use std::cell::{RefCell};
43 use std::sync::{Arc};
44 use std::path::{Path, PathBuf};
45
46 /// The (minimum) version of a `libclang` shared library.
47 #[allow(missing_docs)]
48 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
49 pub enum Version {
50 V3_5 = 35,
51 V3_6 = 36,
52 V3_7 = 37,
53 V3_8 = 38,
54 V3_9 = 39,
55 V4_0 = 40,
56 V5_0 = 50,
57 V6_0 = 60,
58 V7_0 = 70,
59 V8_0 = 80,
60 V9_0 = 90,
61 }
62
63 /// The set of functions loaded dynamically.
64 #[derive(Debug, Default)]
65 pub struct Functions {
66 $(
67 $(#[doc=$doc] #[cfg($cfg)])*
68 pub $name: Option<unsafe extern fn($($pname: $pty), *) $(-> $ret)*>,
69 )+
70 }
71
72 /// A dynamically loaded instance of the `libclang` library.
73 #[derive(Debug)]
74 pub struct SharedLibrary {
75 library: libloading::Library,
76 path: PathBuf,
77 pub functions: Functions,
78 }
79
80 impl SharedLibrary {
81 fn new(library: libloading::Library, path: PathBuf) -> Self {
82 Self { library, path, functions: Functions::default() }
83 }
84
85 /// Returns the path to this `libclang` shared library.
86 pub fn path(&self) -> &Path {
87 &self.path
88 }
89
90 /// Returns the (minimum) version of this `libclang` shared library.
91 ///
92 /// If this returns `None`, it indicates that the version is too old
93 /// to be supported by this crate (i.e., `3.4` or earlier). If the
94 /// version of this shared library is more recent than that fully
95 /// supported by this crate, the most recent fully supported version
96 /// will be returned.
97 pub fn version(&self) -> Option<Version> {
98 macro_rules! check {
99 ($fn:expr, $version:ident) => {
100 if self.library.get::<unsafe extern fn()>($fn).is_ok() {
101 return Some(Version::$version);
102 }
103 };
104 }
105
106 unsafe {
107 check!(b"clang_Cursor_isAnonymousRecordDecl", V9_0);
108 check!(b"clang_Cursor_getObjCPropertyGetterName", V8_0);
109 check!(b"clang_File_tryGetRealPathName", V7_0);
110 check!(b"clang_CXIndex_setInvocationEmissionPathOption", V6_0);
111 check!(b"clang_Cursor_isExternalSymbol", V5_0);
112 check!(b"clang_EvalResult_getAsLongLong", V4_0);
113 check!(b"clang_CXXConstructor_isConvertingConstructor", V3_9);
114 check!(b"clang_CXXField_isMutable", V3_8);
115 check!(b"clang_Cursor_getOffsetOfField", V3_7);
116 check!(b"clang_Cursor_getStorageClass", V3_6);
117 check!(b"clang_Type_getNumTemplateArguments", V3_5);
118 }
119
120 None
121 }
122 }
123
124 thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None));
125
126 /// Returns whether a `libclang` shared library is loaded on this thread.
127 pub fn is_loaded() -> bool {
128 LIBRARY.with(|l| l.borrow().is_some())
129 }
130
131 fn with_library<T, F>(f: F) -> Option<T> where F: FnOnce(&SharedLibrary) -> T {
132 LIBRARY.with(|l| {
133 match l.borrow().as_ref() {
134 Some(library) => Some(f(&library)),
135 _ => None,
136 }
137 })
138 }
139
140 $(
Haibo Huang8b9513e2020-07-13 22:05:39 -0700141 #[cfg_attr(feature="cargo-clippy", allow(clippy::missing_safety_doc))]
142 #[cfg_attr(feature="cargo-clippy", allow(clippy::too_many_arguments))]
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700143 $(#[doc=$doc] #[cfg($cfg)])*
144 pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
145 let f = with_library(|l| {
Chih-Hung Hsieh97132dc2020-10-26 17:21:51 -0700146 l.functions.$name.expect(concat!(
147 "`libclang` function not loaded: `",
148 stringify!($name),
149 "`. This crate requires that `libclang` 3.9 or later be installed on your ",
150 "system. For more information on how to accomplish this, see here: ",
151 "https://rust-lang.github.io/rust-bindgen/requirements.html#installing-clang-39"))
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700152 }).expect("a `libclang` shared library is not loaded on this thread");
153 f($($pname), *)
154 }
155
156 $(#[doc=$doc] #[cfg($cfg)])*
157 pub mod $name {
158 pub fn is_loaded() -> bool {
159 super::with_library(|l| l.functions.$name.is_some()).unwrap_or(false)
160 }
161 }
162 )+
163
164 mod load {
165 $(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
166 }
167
168 /// Loads a `libclang` shared library and returns the library instance.
169 ///
170 /// This function does not attempt to load any functions from the shared library. The caller
171 /// is responsible for loading the functions they require.
172 ///
173 /// # Failures
174 ///
175 /// * a `libclang` shared library could not be found
176 /// * the `libclang` shared library could not be opened
177 pub fn load_manually() -> Result<SharedLibrary, String> {
178 mod build {
Chih-Hung Hsiehe11a4632020-10-24 23:10:41 -0700179 pub mod common { include!(concat!(env!("OUT_DIR"), "/common.rs")); }
180 pub mod dynamic { include!(concat!(env!("OUT_DIR"), "/dynamic.rs")); }
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700181 }
182
Haibo Huang8b9513e2020-07-13 22:05:39 -0700183 let (directory, filename) = build::dynamic::find(true)?;
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700184 let path = directory.join(filename);
185
Jeff Vander Stoep582dabe2021-02-19 19:59:49 +0100186 unsafe {
187 let library = libloading::Library::new(&path).map_err(|e| {
188 format!(
189 "the `libclang` shared library at {} could not be opened: {}",
190 path.display(),
191 e,
192 )
193 });
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700194
Jeff Vander Stoep582dabe2021-02-19 19:59:49 +0100195 let mut library = SharedLibrary::new(library?, path);
196 $(load::$name(&mut library);)+
197 Ok(library)
198 }
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700199 }
200
201 /// Loads a `libclang` shared library for use in the current thread.
202 ///
203 /// This functions attempts to load all the functions in the shared library. Whether a
204 /// function has been loaded can be tested by calling the `is_loaded` function on the
205 /// module with the same name as the function (e.g., `clang_createIndex::is_loaded()` for
206 /// the `clang_createIndex` function).
207 ///
208 /// # Failures
209 ///
210 /// * a `libclang` shared library could not be found
211 /// * the `libclang` shared library could not be opened
212 #[allow(dead_code)]
213 pub fn load() -> Result<(), String> {
Haibo Huang8b9513e2020-07-13 22:05:39 -0700214 let library = Arc::new(load_manually()?);
Chih-Hung Hsiehfab43802020-04-07 14:24:01 -0700215 LIBRARY.with(|l| *l.borrow_mut() = Some(library));
216 Ok(())
217 }
218
219 /// Unloads the `libclang` shared library in use in the current thread.
220 ///
221 /// # Failures
222 ///
223 /// * a `libclang` shared library is not in use in the current thread
224 pub fn unload() -> Result<(), String> {
225 let library = set_library(None);
226 if library.is_some() {
227 Ok(())
228 } else {
229 Err("a `libclang` shared library is not in use in the current thread".into())
230 }
231 }
232
233 /// Returns the library instance stored in TLS.
234 ///
235 /// This functions allows for sharing library instances between threads.
236 pub fn get_library() -> Option<Arc<SharedLibrary>> {
237 LIBRARY.with(|l| l.borrow_mut().clone())
238 }
239
240 /// Sets the library instance stored in TLS and returns the previous library.
241 ///
242 /// This functions allows for sharing library instances between threads.
243 pub fn set_library(library: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> {
244 LIBRARY.with(|l| mem::replace(&mut *l.borrow_mut(), library))
245 }
246 )
247}
248
249#[cfg(not(feature = "runtime"))]
250macro_rules! link {
251 (
252 $(
253 $(#[doc=$doc:expr] #[cfg($cfg:meta)])*
254 pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
255 )+
256 ) => (
257 extern {
258 $(
259 $(#[doc=$doc] #[cfg($cfg)])*
260 pub fn $name($($pname: $pty), *) $(-> $ret)*;
261 )+
262 }
263
264 $(
265 $(#[doc=$doc] #[cfg($cfg)])*
266 pub mod $name {
267 pub fn is_loaded() -> bool { true }
268 }
269 )+
270 )
271}