1 // Copyright 2016 Kyle Mayes 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //================================================ 16 // Macros 17 //================================================ 18 19 #[cfg(feature = "runtime")] 20 macro_rules! link { 21 ( 22 @LOAD: 23 $(#[doc=$doc:expr])* 24 #[cfg($cfg:meta)] 25 fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)* 26 ) => ( 27 $(#[doc=$doc])* 28 #[cfg($cfg)] 29 pub fn $name(library: &mut super::SharedLibrary) { 30 let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok(); 31 library.functions.$name = match symbol { 32 Some(s) => *s, 33 None => None, 34 }; 35 } 36 37 #[cfg(not($cfg))] 38 pub fn $name(_: &mut super::SharedLibrary) {} 39 ); 40 41 ( 42 @LOAD: 43 fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)* 44 ) => ( 45 link!(@LOAD: #[cfg(feature = "runtime")] fn $name($($pname: $pty), *) $(-> $ret)*); 46 ); 47 48 ( 49 $( 50 $(#[doc=$doc:expr] #[cfg($cfg:meta)])* 51 pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*; 52 )+ 53 ) => ( 54 use std::cell::{RefCell}; 55 use std::sync::{Arc}; 56 use std::path::{Path, PathBuf}; 57 58 /// The (minimum) version of a `libclang` shared library. 59 #[allow(missing_docs)] 60 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 61 pub enum Version { 62 V3_5 = 35, 63 V3_6 = 36, 64 V3_7 = 37, 65 V3_8 = 38, 66 V3_9 = 39, 67 V4_0 = 40, 68 V5_0 = 50, 69 V6_0 = 60, 70 V7_0 = 70, 71 V8_0 = 80, 72 V9_0 = 90, 73 } 74 75 /// The set of functions loaded dynamically. 76 #[derive(Debug, Default)] 77 pub struct Functions { 78 $( 79 $(#[doc=$doc] #[cfg($cfg)])* 80 pub $name: Option<unsafe extern fn($($pname: $pty), *) $(-> $ret)*>, 81 )+ 82 } 83 84 /// A dynamically loaded instance of the `libclang` library. 85 #[derive(Debug)] 86 pub struct SharedLibrary { 87 library: libloading::Library, 88 path: PathBuf, 89 pub functions: Functions, 90 } 91 92 impl SharedLibrary { 93 fn new(library: libloading::Library, path: PathBuf) -> Self { 94 Self { library, path, functions: Functions::default() } 95 } 96 97 /// Returns the path to this `libclang` shared library. 98 pub fn path(&self) -> &Path { 99 &self.path 100 } 101 102 /// Returns the (minimum) version of this `libclang` shared library. 103 /// 104 /// If this returns `None`, it indicates that the version is too old 105 /// to be supported by this crate (i.e., `3.4` or earlier). If the 106 /// version of this shared library is more recent than that fully 107 /// supported by this crate, the most recent fully supported version 108 /// will be returned. 109 pub fn version(&self) -> Option<Version> { 110 macro_rules! check { 111 ($fn:expr, $version:ident) => { 112 if self.library.get::<unsafe extern fn()>($fn).is_ok() { 113 return Some(Version::$version); 114 } 115 }; 116 } 117 118 unsafe { 119 check!(b"clang_Cursor_isAnonymousRecordDecl", V9_0); 120 check!(b"clang_Cursor_getObjCPropertyGetterName", V8_0); 121 check!(b"clang_File_tryGetRealPathName", V7_0); 122 check!(b"clang_CXIndex_setInvocationEmissionPathOption", V6_0); 123 check!(b"clang_Cursor_isExternalSymbol", V5_0); 124 check!(b"clang_EvalResult_getAsLongLong", V4_0); 125 check!(b"clang_CXXConstructor_isConvertingConstructor", V3_9); 126 check!(b"clang_CXXField_isMutable", V3_8); 127 check!(b"clang_Cursor_getOffsetOfField", V3_7); 128 check!(b"clang_Cursor_getStorageClass", V3_6); 129 check!(b"clang_Type_getNumTemplateArguments", V3_5); 130 } 131 132 None 133 } 134 } 135 136 thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None)); 137 138 /// Returns whether a `libclang` shared library is loaded on this thread. 139 pub fn is_loaded() -> bool { 140 LIBRARY.with(|l| l.borrow().is_some()) 141 } 142 143 fn with_library<T, F>(f: F) -> Option<T> where F: FnOnce(&SharedLibrary) -> T { 144 LIBRARY.with(|l| { 145 match l.borrow().as_ref() { 146 Some(library) => Some(f(&library)), 147 _ => None, 148 } 149 }) 150 } 151 152 $( 153 #[cfg_attr(feature="cargo-clippy", allow(too_many_arguments))] 154 $(#[doc=$doc] #[cfg($cfg)])* 155 pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* { 156 let f = with_library(|l| { 157 match l.functions.$name { 158 Some(f) => f, 159 _ => panic!(concat!("function not loaded: ", stringify!($name))), 160 } 161 }).expect("a `libclang` shared library is not loaded on this thread"); 162 f($($pname), *) 163 } 164 165 $(#[doc=$doc] #[cfg($cfg)])* 166 pub mod $name { 167 pub fn is_loaded() -> bool { 168 super::with_library(|l| l.functions.$name.is_some()).unwrap_or(false) 169 } 170 } 171 )+ 172 173 mod load { 174 $(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+ 175 } 176 177 /// Loads a `libclang` shared library and returns the library instance. 178 /// 179 /// This function does not attempt to load any functions from the shared library. The caller 180 /// is responsible for loading the functions they require. 181 /// 182 /// # Failures 183 /// 184 /// * a `libclang` shared library could not be found 185 /// * the `libclang` shared library could not be opened 186 pub fn load_manually() -> Result<SharedLibrary, String> { 187 mod build { 188 pub mod common { include!(concat!(env!("OUT_DIR"), "/common.rs")); } 189 pub mod dynamic { include!(concat!(env!("OUT_DIR"), "/dynamic.rs")); } 190 } 191 192 let (directory, filename) = try!(build::dynamic::find(true)); 193 let path = directory.join(filename); 194 195 let library = libloading::Library::new(&path).map_err(|e| { 196 format!( 197 "the `libclang` shared library at {} could not be opened: {}", 198 path.display(), 199 e, 200 ) 201 }); 202 203 let mut library = SharedLibrary::new(try!(library), path); 204 $(load::$name(&mut library);)+ 205 Ok(library) 206 } 207 208 /// Loads a `libclang` shared library for use in the current thread. 209 /// 210 /// This functions attempts to load all the functions in the shared library. Whether a 211 /// function has been loaded can be tested by calling the `is_loaded` function on the 212 /// module with the same name as the function (e.g., `clang_createIndex::is_loaded()` for 213 /// the `clang_createIndex` function). 214 /// 215 /// # Failures 216 /// 217 /// * a `libclang` shared library could not be found 218 /// * the `libclang` shared library could not be opened 219 #[allow(dead_code)] 220 pub fn load() -> Result<(), String> { 221 let library = Arc::new(try!(load_manually())); 222 LIBRARY.with(|l| *l.borrow_mut() = Some(library)); 223 Ok(()) 224 } 225 226 /// Unloads the `libclang` shared library in use in the current thread. 227 /// 228 /// # Failures 229 /// 230 /// * a `libclang` shared library is not in use in the current thread 231 pub fn unload() -> Result<(), String> { 232 let library = set_library(None); 233 if library.is_some() { 234 Ok(()) 235 } else { 236 Err("a `libclang` shared library is not in use in the current thread".into()) 237 } 238 } 239 240 /// Returns the library instance stored in TLS. 241 /// 242 /// This functions allows for sharing library instances between threads. 243 pub fn get_library() -> Option<Arc<SharedLibrary>> { 244 LIBRARY.with(|l| l.borrow_mut().clone()) 245 } 246 247 /// Sets the library instance stored in TLS and returns the previous library. 248 /// 249 /// This functions allows for sharing library instances between threads. 250 pub fn set_library(library: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> { 251 LIBRARY.with(|l| mem::replace(&mut *l.borrow_mut(), library)) 252 } 253 ) 254 } 255 256 #[cfg(not(feature = "runtime"))] 257 macro_rules! link { 258 ( 259 $( 260 $(#[doc=$doc:expr] #[cfg($cfg:meta)])* 261 pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*; 262 )+ 263 ) => ( 264 extern { 265 $( 266 $(#[doc=$doc] #[cfg($cfg)])* 267 pub fn $name($($pname: $pty), *) $(-> $ret)*; 268 )+ 269 } 270 271 $( 272 $(#[doc=$doc] #[cfg($cfg)])* 273 pub mod $name { 274 pub fn is_loaded() -> bool { true } 275 } 276 )+ 277 ) 278 } 279