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
#"
16 my ($mod_name) = {{LIB_NAME}};
17 my $bootstrap_name = 'boot_' . ($pkg =~ s/::/__/gr);
19 my @dirs = (map "-L$_/auto", @INC);
20 my $mod_file = DynaLoader::dl_findfile("#;
22 #[cfg(debug_assertions)]
23 const MODULE_HEAD_DEBUG
: &str = r
#"'-L./target/debug', "#;
25 #[cfg(not(debug_assertions))]
26 const MODULE_HEAD_DEBUG
: &str = "";
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;
31 my $lib = DynaLoader::dl_load_file($mod_file)
32 or die "failed to load library '$mod_file'\n";
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");
40 __PACKAGE__->bootstrap;
47 perl_name
: Option
<Ident
>,
49 prototype
: Option
<String
>,
53 pub attrs
: ModuleAttrs
,
54 exported
: Vec
<Export
>,
58 pub fn with_attrs(attr
: Punctuated
<Meta
, Token
![,]>) -> Result
<Self, Error
> {
60 attrs
: ModuleAttrs
::try_from(attr
)?
,
68 perl_name
: Option
<Ident
>,
70 prototype
: Option
<String
>,
72 self.exported
.push(Export
{
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());
87 let xs_name
= &export
.xs_name
;
89 let prototype
= match export
.prototype
.as_deref() {
90 Some(proto
) => quote
! {
91 concat
!(#proto, "\0").as_bytes().as_ptr() as *const i8
93 None
=> quote
!(::std
::ptr
::null()),
98 #sub_lit.as_ptr() as *const i8,
100 concat
!(::std
::file
!(), "\0").as_bytes().as_ptr() as *const i8,
107 let bootstrap_name
= format
!("boot_{}", self.attrs
.package_name
).replace("::", "__");
108 let bootstrap_ident
= Ident
::new(&bootstrap_name
, Span
::call_site());
110 let boot
= match &self.attrs
.boot
{
111 Some(boot
) => quote
! { #boot(); }
,
112 None
=> TokenStream
::new(),
117 pub extern "C" fn #bootstrap_ident(
118 _cv
: Option
<&::perlmod
::ffi
::CV
>,
120 static ONCE
: ::std
::sync
::Once
= ::std
::sync
::Once
::new();
123 use ::perlmod
::ffi
::RSPL_newXS_flags
;
125 let argmark
= ::perlmod
::ffi
::pop_arg_mark();
137 pub fn write(&self) -> Result
<(), Error
> {
138 let mut source
= format
!(
139 "package {};\n{}{}{}",
140 self.attrs
.package_name
, MODULE_HEAD
, MODULE_HEAD_DEBUG
, MODULE_HEAD_2
143 if let Some(lib
) = &self.attrs
.lib_name
{
144 source
= source
.replace("{{LIB_NAME}}", &format
!("('{lib}')"));
146 let lib_name
= get_default_lib_name(Span
::call_site())?
;
147 source
= source
.replace("{{LIB_NAME}}", &format
!("('{lib_name}')"));
154 .unwrap_or_else(|| format
!("{}.pm", self.attrs
.package_name
.replace("::", "/")));
156 let path
= std
::path
::Path
::new(&file_name
);
157 if let Some(parent
) = path
.parent() {
158 std
::fs
::create_dir_all(parent
).map_err(io_err
)?
;
160 std
::fs
::write(path
, source
.as_bytes()).map_err(io_err
)?
;
165 pub fn mangle_package_name(&self) -> String
{
166 self.attrs
.mangle_package_name()
170 fn io_err
<E
: ToString
>(err
: E
) -> Error
{
171 Error
::new(Span
::call_site(), err
.to_string())
174 pub fn get_default_lib_name(why
: Span
) -> Result
<String
, Error
> {
175 env
::var("CARGO_PKG_NAME")
176 .map(|s
| s
.replace('
-'
, "_"))
180 "failed to get CARGO_PKG_NAME environment variable: {}",