mirror of
git://git.proxmox.com/git/perlmod.git
synced 2025-07-16 20:59:13 +03:00
fix #[export(name=...)], drop deprecated make_package
The `make_package` macro is tedious to use. We now simply depend on new-enough rustc for `package` to work. The change to #[export] allows writing: #[export(name = "DESTROY")] fn destroy(#[raw] this: Value) { ... } Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
@ -64,7 +64,9 @@ impl ModuleAttrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct FunctionAttrs {
|
pub struct FunctionAttrs {
|
||||||
|
pub perl_name: Option<Ident>,
|
||||||
pub xs_name: Option<Ident>,
|
pub xs_name: Option<Ident>,
|
||||||
pub raw_return: bool,
|
pub raw_return: bool,
|
||||||
}
|
}
|
||||||
@ -73,8 +75,7 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
|
|||||||
type Error = syn::Error;
|
type Error = syn::Error;
|
||||||
|
|
||||||
fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
|
fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
|
||||||
let mut xs_name = None;
|
let mut attrs = FunctionAttrs::default();
|
||||||
let mut raw_return = false;
|
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
match arg {
|
match arg {
|
||||||
@ -83,15 +84,17 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
|
|||||||
lit: syn::Lit::Str(litstr),
|
lit: syn::Lit::Str(litstr),
|
||||||
..
|
..
|
||||||
})) => {
|
})) => {
|
||||||
if path.is_ident("name") {
|
if path.is_ident("xs_name") {
|
||||||
xs_name = Some(Ident::new(&litstr.value(), litstr.span()));
|
attrs.xs_name = Some(Ident::new(&litstr.value(), litstr.span()));
|
||||||
|
} else if path.is_ident("name") {
|
||||||
|
attrs.perl_name = Some(Ident::new(&litstr.value(), litstr.span()));
|
||||||
} else {
|
} else {
|
||||||
bail!(path => "unknown argument");
|
bail!(path => "unknown argument");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
|
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
|
||||||
if path.is_ident("raw_return") {
|
if path.is_ident("raw_return") {
|
||||||
raw_return = true;
|
attrs.raw_return = true;
|
||||||
} else {
|
} else {
|
||||||
bail!(path => "unknown attribute");
|
bail!(path => "unknown attribute");
|
||||||
}
|
}
|
||||||
@ -100,9 +103,6 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(attrs)
|
||||||
xs_name,
|
|
||||||
raw_return,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use crate::attribs::FunctionAttrs;
|
|||||||
|
|
||||||
pub struct XSub {
|
pub struct XSub {
|
||||||
pub rust_name: Ident,
|
pub rust_name: Ident,
|
||||||
|
pub perl_name: Option<Ident>,
|
||||||
pub xs_name: Ident,
|
pub xs_name: Ident,
|
||||||
pub tokens: TokenStream,
|
pub tokens: TokenStream,
|
||||||
}
|
}
|
||||||
@ -201,6 +202,7 @@ pub fn handle_function(
|
|||||||
#wrapper_func
|
#wrapper_func
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn #impl_xs_name(
|
fn #impl_xs_name(
|
||||||
_cv: &::perlmod::ffi::CV,
|
_cv: &::perlmod::ffi::CV,
|
||||||
) -> Result<#return_type, *mut ::perlmod::ffi::SV> {
|
) -> Result<#return_type, *mut ::perlmod::ffi::SV> {
|
||||||
@ -229,6 +231,7 @@ pub fn handle_function(
|
|||||||
|
|
||||||
Ok(XSub {
|
Ok(XSub {
|
||||||
rust_name: name.to_owned(),
|
rust_name: name.to_owned(),
|
||||||
|
perl_name: attr.perl_name.clone(),
|
||||||
xs_name,
|
xs_name,
|
||||||
tokens,
|
tokens,
|
||||||
})
|
})
|
||||||
|
@ -74,7 +74,7 @@ pub fn package(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Attribute to export a function so that it can be installed as an `xsub` in perl. See the
|
/// Attribute to export a function so that it can be installed as an `xsub` in perl. See the
|
||||||
/// [`make_package!`] macro for a usage example.
|
/// [`package!`] macro for a usage example.
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
||||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||||
@ -82,68 +82,6 @@ pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
|||||||
handle_error(item.clone(), export_impl(attr, item)).into()
|
handle_error(item.clone(), export_impl(attr, item)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Proc macro to create a perl package file for rust functions.
|
|
||||||
///
|
|
||||||
/// This macro will write a perl package/module file into cargo's working directory. (Typically the
|
|
||||||
/// manifest directory.)
|
|
||||||
///
|
|
||||||
/// This macro exists mostly for backward compatibility. When using rustc 1.42 or above, a more
|
|
||||||
/// readable and less repetitive code will be produced with the [`package`](module@crate::package)
|
|
||||||
/// attribute instead.
|
|
||||||
///
|
|
||||||
/// This macro always has to be used in conjunction with the [`export!]` macro, like this:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # mod testmod {
|
|
||||||
/// use anyhow::{bail, Error};
|
|
||||||
/// use perlmod::export;
|
|
||||||
///
|
|
||||||
/// #[export]
|
|
||||||
/// fn sum_except_42(a: u32, b: u32) -> Result<u32, Error> {
|
|
||||||
/// if a == 42 {
|
|
||||||
/// // Errors 'die' in perl, so newlines at the end of error messages make a difference!
|
|
||||||
/// bail!("dying on magic number\n");
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// Ok(a + b)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[export(name = "xs_sub_name")]
|
|
||||||
/// fn double(a: u32) -> Result<u32, Error> {
|
|
||||||
/// Ok(2 * a)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// perlmod::make_package! {
|
|
||||||
/// // First we need to specify the package, similar to perl's syntax:
|
|
||||||
/// package "RSPM::DocTest1";
|
|
||||||
///
|
|
||||||
/// // The library name is usually derived from the crate name in Cargo.toml automatically.
|
|
||||||
/// // So this line is optional:
|
|
||||||
/// lib "perlmod_test";
|
|
||||||
///
|
|
||||||
/// // An optional output file name can be specified as follows:
|
|
||||||
/// // (we use this here to prevent doc tests from creating files...)
|
|
||||||
/// file "/dev/null";
|
|
||||||
///
|
|
||||||
/// // The list of xsubs we want to export:
|
|
||||||
/// subs {
|
|
||||||
/// // When only providing the name, default naming convention will be used:
|
|
||||||
/// // This is used like: `RSPM::DocTest1::sum_except_42(4, 5);` in perl.
|
|
||||||
/// sum_except_42,
|
|
||||||
/// // If we used an explicit export name, we need to also explicitly export the renamed
|
|
||||||
/// // function here:
|
|
||||||
/// // This is used like: `RSPM::DocTest1::double_the_number(5);` in perl.
|
|
||||||
/// xs_sub_name as double_the_number,
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn make_package(item: TokenStream_1) -> TokenStream_1 {
|
|
||||||
let item: TokenStream = item.into();
|
|
||||||
handle_error(item.clone(), make_package_impl(item)).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Error> {
|
fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Error> {
|
||||||
let item: syn::Item = syn::parse2(item)?;
|
let item: syn::Item = syn::parse2(item)?;
|
||||||
|
|
||||||
@ -161,9 +99,3 @@ fn export_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Er
|
|||||||
let func = function::handle_function(attr, func, None)?;
|
let func = function::handle_function(attr, func, None)?;
|
||||||
Ok(func.tokens)
|
Ok(func.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_package_impl(item: TokenStream) -> Result<TokenStream, Error> {
|
|
||||||
let pkg: package::Package = syn::parse2(item)?;
|
|
||||||
pkg.write()?;
|
|
||||||
Ok(TokenStream::new())
|
|
||||||
}
|
|
||||||
|
@ -55,6 +55,7 @@ pub fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result<To
|
|||||||
|
|
||||||
package.export_named(
|
package.export_named(
|
||||||
func.rust_name,
|
func.rust_name,
|
||||||
|
func.perl_name,
|
||||||
func.xs_name,
|
func.xs_name,
|
||||||
"src/FIXME.rs".to_string(),
|
"src/FIXME.rs".to_string(),
|
||||||
);
|
);
|
||||||
|
@ -8,10 +8,7 @@ use anyhow::Error;
|
|||||||
|
|
||||||
use proc_macro2::{Ident, Span};
|
use proc_macro2::{Ident, Span};
|
||||||
|
|
||||||
use syn::parse::Parse;
|
|
||||||
use syn::punctuated::Punctuated;
|
|
||||||
use syn::AttributeArgs;
|
use syn::AttributeArgs;
|
||||||
use syn::Token;
|
|
||||||
|
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
|
||||||
@ -56,6 +53,7 @@ const MODULE_TAIL: &str = "}\n";
|
|||||||
|
|
||||||
struct Export {
|
struct Export {
|
||||||
rust_name: Ident,
|
rust_name: Ident,
|
||||||
|
perl_name: Option<Ident>,
|
||||||
xs_name: Ident,
|
xs_name: Ident,
|
||||||
file_name: String,
|
file_name: String,
|
||||||
}
|
}
|
||||||
@ -73,18 +71,16 @@ impl Package {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_named(&mut self, rust_name: Ident, xs_name: Ident, file_name: String) {
|
pub fn export_named(
|
||||||
|
&mut self,
|
||||||
|
rust_name: Ident,
|
||||||
|
perl_name: Option<Ident>,
|
||||||
|
xs_name: Ident,
|
||||||
|
file_name: String,
|
||||||
|
) {
|
||||||
self.exported.push(Export {
|
self.exported.push(Export {
|
||||||
rust_name,
|
rust_name,
|
||||||
xs_name,
|
perl_name,
|
||||||
file_name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn export_direct(&mut self, name: Ident, file_name: String) {
|
|
||||||
let xs_name = Ident::new(&format!("xs_{}", name), name.span());
|
|
||||||
self.exported.push(Export {
|
|
||||||
rust_name: name,
|
|
||||||
xs_name,
|
xs_name,
|
||||||
file_name,
|
file_name,
|
||||||
});
|
});
|
||||||
@ -97,7 +93,7 @@ impl Package {
|
|||||||
source = format!(
|
source = format!(
|
||||||
"{} newXS('{}', '{}', \"{}\");\n",
|
"{} newXS('{}', '{}', \"{}\");\n",
|
||||||
source,
|
source,
|
||||||
export.rust_name,
|
export.perl_name.as_ref().unwrap_or(&export.rust_name),
|
||||||
export.xs_name,
|
export.xs_name,
|
||||||
export.file_name.replace('"', "\\\""),
|
export.file_name.replace('"', "\\\""),
|
||||||
);
|
);
|
||||||
@ -132,88 +128,6 @@ impl Package {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod kw {
|
|
||||||
syn::custom_keyword!(package);
|
|
||||||
syn::custom_keyword!(lib);
|
|
||||||
syn::custom_keyword!(file);
|
|
||||||
syn::custom_keyword!(subs);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Package {
|
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
||||||
let mut pkg = Package {
|
|
||||||
attrs: ModuleAttrs {
|
|
||||||
package_name: String::new(),
|
|
||||||
file_name: None,
|
|
||||||
lib_name: None,
|
|
||||||
},
|
|
||||||
exported: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// `package "Package::Name";` comes first
|
|
||||||
let _pkg: kw::package = input.parse()?;
|
|
||||||
let package: syn::LitStr = input.parse()?;
|
|
||||||
let _semicolon: Token![;] = input.parse()?;
|
|
||||||
pkg.attrs.package_name = package.value();
|
|
||||||
|
|
||||||
// `lib "lib_name";` optionally comes second
|
|
||||||
let lookahead = input.lookahead1();
|
|
||||||
if lookahead.peek(kw::lib) {
|
|
||||||
let _lib: kw::lib = input.parse()?;
|
|
||||||
let lib: syn::LitStr = input.parse()?;
|
|
||||||
pkg.attrs.lib_name = Some(lib.value());
|
|
||||||
let _semicolon: Token![;] = input.parse()?;
|
|
||||||
}
|
|
||||||
drop(lookahead);
|
|
||||||
|
|
||||||
// `file "File/Name.pm";` optionally comes third
|
|
||||||
let lookahead = input.lookahead1();
|
|
||||||
if lookahead.peek(kw::file) {
|
|
||||||
let _file: kw::file = input.parse()?;
|
|
||||||
let file: syn::LitStr = input.parse()?;
|
|
||||||
pkg.attrs.file_name = Some(file.value());
|
|
||||||
let _semicolon: Token![;] = input.parse()?;
|
|
||||||
}
|
|
||||||
drop(lookahead);
|
|
||||||
|
|
||||||
// `sub { ... }` must follow:
|
|
||||||
let _sub: kw::subs = input.parse()?;
|
|
||||||
let content;
|
|
||||||
let _brace_token: syn::token::Brace = syn::braced!(content in input);
|
|
||||||
let items: Punctuated<ExportItem, Token![,]> =
|
|
||||||
content.parse_terminated(ExportItem::parse)?;
|
|
||||||
|
|
||||||
for item in items {
|
|
||||||
match item {
|
|
||||||
ExportItem::Direct(name) => pkg.export_direct(name, "src/FIXME.rs".to_string()),
|
|
||||||
ExportItem::Named(name, as_name) => {
|
|
||||||
pkg.export_named(as_name, name, "src/FIXME.rs".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ExportItem {
|
|
||||||
Direct(Ident),
|
|
||||||
Named(Ident, Ident),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for ExportItem {
|
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
||||||
let name: Ident = input.parse()?;
|
|
||||||
let lookahead = input.lookahead1();
|
|
||||||
if lookahead.peek(syn::token::As) {
|
|
||||||
let _as: syn::token::As = input.parse()?;
|
|
||||||
Ok(ExportItem::Named(name, input.parse()?))
|
|
||||||
} else {
|
|
||||||
Ok(ExportItem::Direct(name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_cargo_toml(why: Span) -> Result<HashMap<String, Value>, syn::Error> {
|
fn read_cargo_toml(why: Span) -> Result<HashMap<String, Value>, syn::Error> {
|
||||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR")
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR")
|
||||||
.map_err(|err| format_err!(why, "failed to get CARGO_MANIFEST_DIR variable: {}", err))?;
|
.map_err(|err| format_err!(why, "failed to get CARGO_MANIFEST_DIR variable: {}", err))?;
|
||||||
|
@ -26,8 +26,8 @@ mod export {
|
|||||||
println!("Called something!");
|
println!("Called something!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export]
|
#[export(name = "DESTROY")]
|
||||||
fn DESTROY(#[raw] this: Value) {
|
fn destroy(#[raw] this: Value) {
|
||||||
match this
|
match this
|
||||||
.dereference()
|
.dereference()
|
||||||
.ok_or_else(|| format_err!("not a reference"))
|
.ok_or_else(|| format_err!("not a reference"))
|
||||||
|
@ -7,8 +7,5 @@ mod pkg142;
|
|||||||
/// possible on nightly with #![feature(custom_inner_attributes)]
|
/// possible on nightly with #![feature(custom_inner_attributes)]
|
||||||
mod pkginline;
|
mod pkginline;
|
||||||
|
|
||||||
/// This is possible on stable rust with some 1.3x already.
|
|
||||||
mod pkgstable;
|
|
||||||
|
|
||||||
/// A test for blessed values.
|
/// A test for blessed values.
|
||||||
mod bless;
|
mod bless;
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
use anyhow::{bail, Error};
|
|
||||||
|
|
||||||
#[perlmod::export]
|
|
||||||
fn foo(a: u32, b: u32) -> Result<u32, Error> {
|
|
||||||
if a == 42 {
|
|
||||||
bail!("dying on magic number");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(a + b)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[perlmod::export(name = "xs_a")]
|
|
||||||
fn func_b(a: u32) -> Result<u32, Error> {
|
|
||||||
Ok(a * 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
perlmod::make_package! {
|
|
||||||
package "RSPM::Foo";
|
|
||||||
|
|
||||||
//lib "perlmod_test";
|
|
||||||
|
|
||||||
subs {
|
|
||||||
foo,
|
|
||||||
xs_a as b, // func_b's exported xsub was renamed to xs_a, and in perl it's called b
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,13 @@
|
|||||||
//! Crate for creating perl packages/bindings for rust code.
|
//! Crate for creating perl packages/bindings for rust code.
|
||||||
//!
|
//!
|
||||||
//! The main feature of this crate are the [`package`], [`export`] and [`make_package!`] macros
|
//! The main feature of this crate is the [`package`] macro provided by the `perlmod-macro` crate
|
||||||
//! provided by the `perlmod-macro` crate. These are documented here.
|
//! and documented here.
|
||||||
//!
|
//!
|
||||||
//! The underlying machinery for these macros is contained in this crate and provides ways to
|
//! The underlying machinery for these macros is contained in this crate and provides ways to
|
||||||
//! serialize and deserialize data between perl and rust.
|
//! serialize and deserialize data between perl and rust.
|
||||||
//!
|
//!
|
||||||
//! For now, see the [`make_package!`] macro for all the details, since as of the time of writing
|
|
||||||
//! this, we're only at rust 1.40.
|
|
||||||
//!
|
|
||||||
//! [`package`]: attr.package.html
|
//! [`package`]: attr.package.html
|
||||||
//! [`export`]: attr.export.html
|
//! [`export`]: attr.export.html
|
||||||
//! [`make_package!`]: macro.make_package.html
|
|
||||||
|
|
||||||
pub(crate) mod error;
|
pub(crate) mod error;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
@ -43,4 +39,4 @@ pub use value::Value;
|
|||||||
|
|
||||||
#[cfg(feature = "exporter")]
|
#[cfg(feature = "exporter")]
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use perlmod_macro::{export, make_package, package};
|
pub use perlmod_macro::{export, package};
|
||||||
|
@ -272,7 +272,7 @@ impl ScalarRef {
|
|||||||
///
|
///
|
||||||
/// The user is responsible for making sure the underlying pointer is correct.
|
/// The user is responsible for making sure the underlying pointer is correct.
|
||||||
pub unsafe fn pv_ref<T>(&self) -> Result<&T, Error> {
|
pub unsafe fn pv_ref<T>(&self) -> Result<&T, Error> {
|
||||||
self.pv_raw().map(|p| unsafe { &*p })
|
self.pv_raw().map(|p| &*p)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interpret the byte string as a pointer and return it as a mutable reference for
|
/// Interpret the byte string as a pointer and return it as a mutable reference for
|
||||||
@ -282,7 +282,7 @@ impl ScalarRef {
|
|||||||
///
|
///
|
||||||
/// The user is responsible for making sure the underlying pointer is correct.
|
/// The user is responsible for making sure the underlying pointer is correct.
|
||||||
pub unsafe fn pv_mut_ref<T>(&self) -> Result<&mut T, Error> {
|
pub unsafe fn pv_mut_ref<T>(&self) -> Result<&mut T, Error> {
|
||||||
self.pv_raw().map(|p| unsafe { &mut *p })
|
self.pv_raw().map(|p| &mut *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create another owned reference to this value.
|
/// Create another owned reference to this value.
|
||||||
|
Reference in New Issue
Block a user