summaryrefslogtreecommitdiffstats
path: root/build/rust/windows-targets/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--build/rust/windows-targets/lib.rs86
1 files changed, 86 insertions, 0 deletions
diff --git a/build/rust/windows-targets/lib.rs b/build/rust/windows-targets/lib.rs
new file mode 100644
index 0000000000..439bc46946
--- /dev/null
+++ b/build/rust/windows-targets/lib.rs
@@ -0,0 +1,86 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::parse::{Parse, ParseStream, Result};
+use syn::{parse_macro_input, Attribute, LitStr, Signature};
+
+/* Proc macro equivalent to the following rust macro:
+ * ```
+ * macro_rules! link {
+ * ($library:literal $abi:literal $($link_name:literal)? $(#[$($doc:tt)*])* fn $name:ident($($arg:ident: $argty:ty),*)->$ret:ty) => (
+ * extern $abi {
+ * #[link(name = $library)]
+ * $(#[link_name=$link_name])?
+ * pub fn $name($($arg: $argty),*) -> $ret;
+ * }
+ * )
+ * }
+ * ```
+ * with the additional feature of removing ".dll" from the $library literal.
+ *
+ * The macro is derived from the equivalent macro in the real windows-targets crate,
+ * with the difference that it uses #[link] with the name of the library rather than
+ * a single "windows.$version" library, so as to avoid having to vendor all the fake
+ * "windows.$version" import libraries. We can do that because we also require MSVC
+ * to build, so we do have the real import libraries available.
+ *
+ * As the library name is there in the original for raw-dylib support, it contains
+ * a suffixed name, but plain #[link] expects a non-suffixed name, which is why we
+ * remove the suffix (and why this had to be a proc-macro).
+ *
+ * Once raw-dylib is more widely available and tested, we'll be able to use the
+ * raw-dylib variants directly.
+ */
+
+struct LinkMacroInput {
+ library: LitStr,
+ abi: LitStr,
+ link_name: Option<LitStr>,
+ function: Signature,
+}
+
+impl Parse for LinkMacroInput {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let library: LitStr = input.parse()?;
+ let abi: LitStr = input.parse()?;
+ let link_name: Option<LitStr> = input.parse().ok();
+ let _doc_comments = Attribute::parse_outer(input)?;
+ let function: Signature = input.parse()?;
+ Ok(LinkMacroInput {
+ library,
+ abi,
+ link_name,
+ function,
+ })
+ }
+}
+
+#[proc_macro]
+pub fn link(input: TokenStream) -> TokenStream {
+ let LinkMacroInput {
+ library,
+ abi,
+ link_name,
+ function,
+ } = parse_macro_input!(input as LinkMacroInput);
+
+ let link_name_attr = link_name.map(|lit| quote! { #[link_name = #lit] });
+
+ let library = library.value();
+ let library = library.strip_suffix(".dll").unwrap_or(&library);
+
+ let generated = quote! {
+ extern #abi {
+ #[link(name = #library)]
+ #link_name_attr
+ pub #function;
+ }
+ };
+
+ TokenStream::from(generated)
+}