lib.rs (2882B)
1 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 2 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license 3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your 4 // option. This file may not be copied, modified, or distributed 5 // except according to those terms. 6 7 use proc_macro::TokenStream; 8 use quote::quote; 9 use syn::parse::{Parse, ParseStream, Result}; 10 use syn::{parse_macro_input, Attribute, LitStr, Signature}; 11 12 /* Proc macro equivalent to the following rust macro: 13 * ``` 14 * macro_rules! link { 15 * ($library:literal $abi:literal $($link_name:literal)? $(#[$($doc:tt)*])* fn $name:ident($($arg:ident: $argty:ty),*)->$ret:ty) => ( 16 * extern $abi { 17 * #[link(name = $library)] 18 * $(#[link_name=$link_name])? 19 * pub fn $name($($arg: $argty),*) -> $ret; 20 * } 21 * ) 22 * } 23 * ``` 24 * with the additional feature of removing ".dll" from the $library literal. 25 * 26 * The macro is derived from the equivalent macro in the real windows-targets crate, 27 * with the difference that it uses #[link] with the name of the library rather than 28 * a single "windows.$version" library, so as to avoid having to vendor all the fake 29 * "windows.$version" import libraries. We can do that because we also require MSVC 30 * to build, so we do have the real import libraries available. 31 * 32 * As the library name is there in the original for raw-dylib support, it contains 33 * a suffixed name, but plain #[link] expects a non-suffixed name, which is why we 34 * remove the suffix (and why this had to be a proc-macro). 35 * 36 * Once raw-dylib is more widely available and tested, we'll be able to use the 37 * raw-dylib variants directly. 38 */ 39 40 struct LinkMacroInput { 41 library: LitStr, 42 abi: LitStr, 43 link_name: Option<LitStr>, 44 function: Signature, 45 } 46 47 impl Parse for LinkMacroInput { 48 fn parse(input: ParseStream) -> Result<Self> { 49 let library: LitStr = input.parse()?; 50 let abi: LitStr = input.parse()?; 51 let link_name: Option<LitStr> = input.parse().ok(); 52 let _doc_comments = Attribute::parse_outer(input)?; 53 let function: Signature = input.parse()?; 54 Ok(LinkMacroInput { 55 library, 56 abi, 57 link_name, 58 function, 59 }) 60 } 61 } 62 63 #[proc_macro] 64 pub fn link(input: TokenStream) -> TokenStream { 65 let LinkMacroInput { 66 library, 67 abi, 68 link_name, 69 function, 70 } = parse_macro_input!(input as LinkMacroInput); 71 72 let link_name_attr = link_name.map(|lit| quote! { #[link_name = #lit] }); 73 74 let library = library.value(); 75 let library = library.strip_suffix(".dll").unwrap_or(&library); 76 77 let generated = quote! { 78 extern #abi { 79 #[link(name = #library)] 80 #link_name_attr 81 pub #function; 82 } 83 }; 84 85 TokenStream::from(generated) 86 }