]> git.proxmox.com Git - perlmod.git/blame - perlmod-macro/src/package.rs
perlmod: search LD_LIBRARY_PATH before @INC/auto dirs
[perlmod.git] / perlmod-macro / src / package.rs
CommitLineData
b813397b 1use std::env;
b03c1494 2
83f19b95 3use proc_macro2::{Ident, Span, TokenStream};
b03c1494 4
83f19b95 5use quote::quote;
cb86559d
WB
6use syn::punctuated::Punctuated;
7use syn::{Error, Meta, Token};
b03c1494 8
b813397b 9use crate::attribs::ModuleAttrs;
b03c1494
WB
10
11const MODULE_HEAD: &str = r#"
83f19b95 12require DynaLoader;
b03c1494 13
95e8123f
WB
14sub autodirs { map { "$_/auto" } @INC; }
15sub envdirs { grep { length($_) } split(/:+/, $ENV{LD_LIBRARY_PATH} // '') }
16
83f19b95
WB
17sub 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 26const MODULE_HEAD_DEBUG: &str = r#"'-L./target/debug', "#;
241e69ee
WB
27
28#[cfg(not(debug_assertions))]
29const MODULE_HEAD_DEBUG: &str = "";
30
83f19b95
WB
31const 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
451;
46"#;
b03c1494
WB
47
48struct 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
55pub struct Package {
daf48419 56 pub attrs: ModuleAttrs,
b03c1494
WB
57 exported: Vec<Export>,
58}
59
60impl 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
173fn io_err<E: ToString>(err: E) -> Error {
174 Error::new(Span::call_site(), err.to_string())
175}
176
177pub 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}