]>
Commit | Line | Data |
---|---|---|
b813397b | 1 | use std::env; |
b03c1494 | 2 | |
83f19b95 | 3 | use proc_macro2::{Ident, Span, TokenStream}; |
b03c1494 | 4 | |
83f19b95 | 5 | use quote::quote; |
b03c1494 | 6 | use syn::AttributeArgs; |
83f19b95 | 7 | use syn::Error; |
b03c1494 | 8 | |
b813397b | 9 | use crate::attribs::ModuleAttrs; |
b03c1494 WB |
10 | |
11 | const MODULE_HEAD: &str = r#" | |
83f19b95 | 12 | require DynaLoader; |
b03c1494 | 13 | |
83f19b95 WB |
14 | sub bootstrap { |
15 | my ($pkg) = @_; | |
16 | my ($mod_name) = {{LIB_NAME}}; | |
08c14b8f | 17 | my $bootstrap_name = 'boot_' . ($pkg =~ s/::/__/gr); |
b03c1494 | 18 | |
83f19b95 WB |
19 | my @dirs = (map "-L$_/auto", @INC); |
20 | my $mod_file = DynaLoader::dl_findfile("#; | |
241e69ee WB |
21 | |
22 | #[cfg(debug_assertions)] | |
83f19b95 | 23 | const MODULE_HEAD_DEBUG: &str = r#"'-L./target/debug', "#; |
241e69ee WB |
24 | |
25 | #[cfg(not(debug_assertions))] | |
26 | const MODULE_HEAD_DEBUG: &str = ""; | |
27 | ||
83f19b95 WB |
28 | const MODULE_HEAD_2: &str = r#"@dirs, $mod_name); |
29 | die "failed to locate shared library for '$pkg' (lib${mod_name}.so)\n" if !$mod_file; | |
b03c1494 | 30 | |
83f19b95 WB |
31 | my $lib = DynaLoader::dl_load_file($mod_file) |
32 | or die "failed to load library '$mod_file'\n"; | |
b03c1494 | 33 | |
83f19b95 WB |
34 | my $sym = DynaLoader::dl_find_symbol($lib, $bootstrap_name); |
35 | die "failed to locate '$bootstrap_name'\n" if !defined $sym; | |
36 | my $boot = DynaLoader::dl_install_xsub($bootstrap_name, $sym, "src/FIXME.rs"); | |
37 | $boot->(); | |
38 | } | |
39 | ||
40 | __PACKAGE__->bootstrap; | |
b03c1494 | 41 | |
83f19b95 WB |
42 | 1; |
43 | "#; | |
b03c1494 WB |
44 | |
45 | struct Export { | |
46 | rust_name: Ident, | |
2991a46a | 47 | perl_name: Option<Ident>, |
b03c1494 | 48 | xs_name: Ident, |
4b5b75f1 | 49 | prototype: Option<String>, |
b03c1494 WB |
50 | } |
51 | ||
52 | pub struct Package { | |
daf48419 | 53 | pub attrs: ModuleAttrs, |
b03c1494 WB |
54 | exported: Vec<Export>, |
55 | } | |
56 | ||
57 | impl Package { | |
58 | pub fn with_attrs(attr: AttributeArgs) -> Result<Self, Error> { | |
59 | Ok(Self { | |
60 | attrs: ModuleAttrs::try_from(attr)?, | |
61 | exported: Vec::new(), | |
62 | }) | |
63 | } | |
64 | ||
4b5b75f1 WB |
65 | pub fn export_named( |
66 | &mut self, | |
67 | rust_name: Ident, | |
68 | perl_name: Option<Ident>, | |
69 | xs_name: Ident, | |
70 | prototype: Option<String>, | |
71 | ) { | |
b03c1494 WB |
72 | self.exported.push(Export { |
73 | rust_name, | |
2991a46a | 74 | perl_name, |
b03c1494 | 75 | xs_name, |
4b5b75f1 | 76 | prototype, |
b03c1494 WB |
77 | }); |
78 | } | |
79 | ||
83f19b95 WB |
80 | pub fn bootstrap_function(&self) -> TokenStream { |
81 | let mut newxs = TokenStream::new(); | |
82 | for export in &self.exported { | |
83 | let perl_name = export.perl_name.as_ref().unwrap_or(&export.rust_name); | |
84 | let sub_name = format!("{}::{}\0", self.attrs.package_name, perl_name); | |
85 | let sub_lit = syn::LitByteStr::new(sub_name.as_bytes(), perl_name.span()); | |
86 | ||
87 | let xs_name = &export.xs_name; | |
88 | ||
4b5b75f1 WB |
89 | let prototype = match export.prototype.as_deref() { |
90 | Some(proto) => quote! { | |
91 | concat!(#proto, "\0").as_bytes().as_ptr() as *const i8 | |
92 | }, | |
93 | None => quote!(::std::ptr::null()), | |
94 | }; | |
95 | ||
83f19b95 WB |
96 | newxs.extend(quote! { |
97 | RSPL_newXS_flags( | |
98 | #sub_lit.as_ptr() as *const i8, | |
99 | #xs_name as _, | |
100 | concat!(::std::file!(), "\0").as_bytes().as_ptr() as *const i8, | |
4b5b75f1 | 101 | #prototype, |
83f19b95 WB |
102 | 0, |
103 | ); | |
104 | }); | |
105 | } | |
106 | ||
b94cbb8f | 107 | let bootstrap_name = format!("boot_{}", self.attrs.package_name).replace("::", "__"); |
83f19b95 WB |
108 | let bootstrap_ident = Ident::new(&bootstrap_name, Span::call_site()); |
109 | ||
110 | quote! { | |
111 | #[no_mangle] | |
112 | pub extern "C" fn #bootstrap_ident( | |
113 | _cv: &::perlmod::ffi::CV, | |
114 | ) { | |
b94cbb8f WB |
115 | static ONCE: ::std::sync::Once = ::std::sync::Once::new(); |
116 | ONCE.call_once(|| { | |
117 | unsafe { | |
118 | use ::perlmod::ffi::RSPL_newXS_flags; | |
83f19b95 | 119 | |
b94cbb8f WB |
120 | let argmark = ::perlmod::ffi::pop_arg_mark(); |
121 | argmark.set_stack(); | |
83f19b95 | 122 | |
b94cbb8f WB |
123 | #newxs |
124 | } | |
125 | }); | |
83f19b95 WB |
126 | } |
127 | } | |
128 | } | |
129 | ||
b03c1494 | 130 | pub fn write(&self) -> Result<(), Error> { |
241e69ee WB |
131 | let mut source = format!( |
132 | "package {};\n{}{}{}", | |
133 | self.attrs.package_name, MODULE_HEAD, MODULE_HEAD_DEBUG, MODULE_HEAD_2 | |
134 | ); | |
b03c1494 | 135 | |
b03c1494 | 136 | if let Some(lib) = &self.attrs.lib_name { |
76f6a079 | 137 | source = source.replace("{{LIB_NAME}}", &format!("('{lib}')")); |
b03c1494 | 138 | } else { |
b813397b | 139 | let lib_name = get_default_lib_name(Span::call_site())?; |
76f6a079 | 140 | source = source.replace("{{LIB_NAME}}", &format!("('{lib_name}')")); |
b03c1494 WB |
141 | } |
142 | ||
06a18771 WB |
143 | let file_name = self |
144 | .attrs | |
145 | .file_name | |
146 | .clone() | |
147 | .unwrap_or_else(|| format!("{}.pm", self.attrs.package_name.replace("::", "/"))); | |
148 | ||
149 | let path = std::path::Path::new(&file_name); | |
b03c1494 | 150 | if let Some(parent) = path.parent() { |
f888c202 | 151 | std::fs::create_dir_all(parent).map_err(io_err)?; |
b03c1494 | 152 | } |
f888c202 | 153 | std::fs::write(path, source.as_bytes()).map_err(io_err)?; |
b03c1494 WB |
154 | |
155 | Ok(()) | |
156 | } | |
9525acd6 WB |
157 | |
158 | pub fn mangle_package_name(&self) -> String { | |
159 | self.attrs.mangle_package_name() | |
160 | } | |
b03c1494 | 161 | } |
06a18771 | 162 | |
f888c202 WB |
163 | fn io_err<E: ToString>(err: E) -> Error { |
164 | Error::new(Span::call_site(), err.to_string()) | |
165 | } | |
166 | ||
167 | pub fn get_default_lib_name(why: Span) -> Result<String, Error> { | |
9de06554 WB |
168 | env::var("CARGO_PKG_NAME") |
169 | .map(|s| s.replace('-', "_")) | |
170 | .map_err(|err| { | |
171 | format_err!( | |
172 | why, | |
173 | "failed to get CARGO_PKG_NAME environment variable: {}", | |
174 | err | |
175 | ) | |
176 | }) | |
b813397b | 177 | } |