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