3 use proc_macro2
::{Ident, Span, TokenStream}
;
6 use syn
::punctuated
::Punctuated
;
7 use syn
::{Error, Meta, Token}
;
9 use crate::attribs
::ModuleAttrs
;
11 const MODULE_HEAD
: &str = r
#"
14 sub autodirs { map { "$_/auto" } @INC; }
15 sub envdirs { grep { length($_) } split(/:+/, $ENV{LD_LIBRARY_PATH} // '') }
19 my ($mod_name) = {{LIB_NAME}};
20 my $bootstrap_name = 'boot_' . ($pkg =~ s/::/__/gr);
22 my @dirs = map { "-L$_" } (envdirs(), autodirs());
23 my $mod_file = DynaLoader::dl_findfile("#;
25 #[cfg(debug_assertions)]
26 const MODULE_HEAD_DEBUG
: &str = r
#"'-L./target/debug', "#;
28 #[cfg(not(debug_assertions))]
29 const MODULE_HEAD_DEBUG
: &str = "";
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;
34 my $lib = DynaLoader::dl_load_file($mod_file)
35 or die "failed to load library '$mod_file'\n";
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");
43 __PACKAGE__->bootstrap;
50 perl_name
: Option
<Ident
>,
52 prototype
: Option
<String
>,
56 pub attrs
: ModuleAttrs
,
57 exported
: Vec
<Export
>,
61 pub fn with_attrs(attr
: Punctuated
<Meta
, Token
![,]>) -> Result
<Self, Error
> {
63 attrs
: ModuleAttrs
::try_from(attr
)?
,
71 perl_name
: Option
<Ident
>,
73 prototype
: Option
<String
>,
75 self.exported
.push(Export
{
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());
90 let xs_name
= &export
.xs_name
;
92 let prototype
= match export
.prototype
.as_deref() {
93 Some(proto
) => quote
! {
94 concat
!(#proto, "\0").as_bytes().as_ptr() as *const i8
96 None
=> quote
!(::std
::ptr
::null()),
101 #sub_lit.as_ptr() as *const i8,
103 concat
!(::std
::file
!(), "\0").as_bytes().as_ptr() as *const i8,
110 let bootstrap_name
= format
!("boot_{}", self.attrs
.package_name
).replace("::", "__");
111 let bootstrap_ident
= Ident
::new(&bootstrap_name
, Span
::call_site());
113 let boot
= match &self.attrs
.boot
{
114 Some(boot
) => quote
! { #boot(); }
,
115 None
=> TokenStream
::new(),
120 pub extern "C" fn #bootstrap_ident(
121 _cv
: Option
<&::perlmod
::ffi
::CV
>,
123 static ONCE
: ::std
::sync
::Once
= ::std
::sync
::Once
::new();
126 use ::perlmod
::ffi
::RSPL_newXS_flags
;
128 let argmark
= ::perlmod
::ffi
::pop_arg_mark();
140 pub fn write(&self) -> Result
<(), Error
> {
141 let mut source
= format
!(
142 "package {};\n{}{}{}",
143 self.attrs
.package_name
, MODULE_HEAD
, MODULE_HEAD_DEBUG
, MODULE_HEAD_2
146 if let Some(lib
) = &self.attrs
.lib_name
{
147 source
= source
.replace("{{LIB_NAME}}", &format
!("('{lib}')"));
149 let lib_name
= get_default_lib_name(Span
::call_site())?
;
150 source
= source
.replace("{{LIB_NAME}}", &format
!("('{lib_name}')"));
157 .unwrap_or_else(|| format
!("{}.pm", self.attrs
.package_name
.replace("::", "/")));
159 let path
= std
::path
::Path
::new(&file_name
);
160 if let Some(parent
) = path
.parent() {
161 std
::fs
::create_dir_all(parent
).map_err(io_err
)?
;
163 std
::fs
::write(path
, source
.as_bytes()).map_err(io_err
)?
;
168 pub fn mangle_package_name(&self) -> String
{
169 self.attrs
.mangle_package_name()
173 fn io_err
<E
: ToString
>(err
: E
) -> Error
{
174 Error
::new(Span
::call_site(), err
.to_string())
177 pub fn get_default_lib_name(why
: Span
) -> Result
<String
, Error
> {
178 env
::var("CARGO_PKG_NAME")
179 .map(|s
| s
.replace('
-'
, "_"))
183 "failed to get CARGO_PKG_NAME environment variable: {}",