From a5722bff5c7728743d5d28f3dde2a61593e3cdef Mon Sep 17 00:00:00 2001 From: Luca Fulchir Date: Fri, 29 Nov 2024 16:59:22 +0100 Subject: [PATCH] Package: remove .base/defer, copy with macro Signed-off-by: Luca Fulchir --- bok-macro/Cargo.toml | 10 +- bok-macro/src/lib.rs | 47 +++- bok-macro/src/pkgs.rs | 524 ++++++++++++++++++++++++++++-------- bok-utils/src/main.rs | 4 + bok-utils/src/pkgs/mod.rs | 5 +- bok-utils/src/pkgs/one.rs | 12 +- bok-utils/src/pkgs/three.rs | 12 +- bok-utils/src/pkgs/two.rs | 13 +- bok/src/lib.rs | 103 +++---- flake.nix | 2 + 10 files changed, 521 insertions(+), 211 deletions(-) diff --git a/bok-macro/Cargo.toml b/bok-macro/Cargo.toml index cf16946..cb614c8 100644 --- a/bok-macro/Cargo.toml +++ b/bok-macro/Cargo.toml @@ -3,17 +3,17 @@ name = "bok-macro" version = "0.1.0" edition = "2021" authors = ["Luca Fulchir "] -homepage = "https://git.runesauth.com/RunesAuth/bok" +homepage = "https://git.runesauth.com/RunesAuth/bok" repository = "https://git.runesauth.com/RunesAuth/bok" license = "Apache-2.0 WITH LLVM-exception" -keywords = [ "bok" ] -categories = [ "config" ] +keywords = ["bok"] +categories = ["config"] [lib] proc-macro = true [dependencies] -syn = { version = "2.0", features = [ "full", "extra-traits" ] } +syn = { version = "2.0", features = ["full", "extra-traits"] } quote = { version = "1.0" } proc-macro2 = "1.0" -macro_magic = { version = "0.5", features = [ "proc_support" ] } +macro_magic = { version = "0.5", features = ["proc_support"] } diff --git a/bok-macro/src/lib.rs b/bok-macro/src/lib.rs index b665e2c..c541afd 100644 --- a/bok-macro/src/lib.rs +++ b/bok-macro/src/lib.rs @@ -15,6 +15,7 @@ * limitations under the License. */ +#![feature(proc_macro_span)] use ::proc_macro::TokenStream; mod pkgs; @@ -103,17 +104,19 @@ pub fn pkg_fn_to_code(attrs: TokenStream, input: TokenStream) -> TokenStream { /// #[::bok::package(::bok::PkgEmpty)] /// pub struct MyPkg {} /// ``` +#[::macro_magic::import_tokens_attr] #[proc_macro_attribute] pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream { - crate::pkgs::package(attrs, input) + crate::pkgs::package(attrs, input, __source_path) } -/// Use as #[::bok::impl_package] -/// Will include basic macros on functions and .base() method +/// Use as #[::bok::package_impl] +/// will add `#[::bok::package_impl_base(..)]` with proper arguments +/// and export the resulting symbols /// /// e.g.: /// ``` -/// #[::bok::impl_package] +/// #[::bok::package_impl] /// impl ::bok::Pkg for MyPackage { /// fn build(&self) -> Result<(),()> { /// ... @@ -121,8 +124,40 @@ pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream { /// } /// ``` #[proc_macro_attribute] -pub fn impl_package(attrs: TokenStream, input: TokenStream) -> TokenStream { - crate::pkgs::impl_package(attrs, input) +pub fn package_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { + crate::pkgs::package_impl(attrs, input) +} + +/// Internal, **don't use unless you know what you are doing.** +/// Use as #[::bok::package_impl_base(::my::Package)] +/// implements common ::bok::Pkg methods and takes the package methods +/// not implemented by the user from the base package +/// +/// does not export symbols. +/// +/// should be called with the same path as the package that implements +/// ::bok::Pkg +/// +/// will rewrite itself with the exported impl path of the base package +/// so that macro_magic can import the base impl +/// +/// e.g.: +/// ``` +/// #[::bok::package_impl_base(MyPackage)] +/// #[::macro_magic::export_tokens(bok_pkg_mypackage)] +/// impl ::bok::Pkg for MyPackage { +/// fn build(&self) -> Result<(),()> { +/// ... +/// } +/// } +/// ``` +#[::macro_magic::import_tokens_attr] +#[proc_macro_attribute] +pub fn package_impl_base( + attrs: TokenStream, + input: TokenStream, +) -> TokenStream { + crate::pkgs::package_impl_base(attrs, input) } /// Unless you know what you are doing, use `#[::bok::package(MyBasePackage)]` diff --git a/bok-macro/src/pkgs.rs b/bok-macro/src/pkgs.rs index c991a33..2213aec 100644 --- a/bok-macro/src/pkgs.rs +++ b/bok-macro/src/pkgs.rs @@ -18,33 +18,20 @@ use ::proc_macro::TokenStream; use ::quote::quote; use ::syn::{parse::Parser, parse_macro_input, DeriveInput}; +use syn::{parse_quote, spanned::Spanned}; pub(crate) fn pkg_fn_to_code( _attrs: TokenStream, input: TokenStream, ) -> TokenStream { - let mut ast_orig: ::syn::ItemFn = - parse_macro_input!(input as ::syn::ItemFn); + let ast_orig: ::syn::ItemFn = parse_macro_input!(input as ::syn::ItemFn); let mut ast_new = ast_orig.clone(); - ast_new.block = ast_orig.block.clone(); - // Add a prefix and suffix statement - // where we declare and return the result - let prefix_ret: ::syn::Stmt = ::syn::parse_quote! { - let mut ret : ::core::result::Result<(),()> = ::core::result::Result::Err(()); - }; - let suffix_ret: ::syn::Stmt = ::syn::parse_quote! { - return ret; - }; - ast_orig.block.stmts.insert(0, prefix_ret); - ast_orig.block.stmts.push(suffix_ret); - - let new_fn_name = - quote::format_ident!("{}", ast_orig.sig.ident.to_string() + "_code"); - ast_new.sig.ident = quote::format_ident!("{}", new_fn_name); + let new_fn_name = quote::format_ident!("{}_code", ast_orig.sig.ident); + ast_new.sig.ident = quote::format_ident!("{}_code", new_fn_name); use ::quote::ToTokens; - let fn_block = ast_new.block.to_token_stream(); + let fn_block = ast_orig.block.to_token_stream(); let asdf = quote! { #ast_orig @@ -56,50 +43,327 @@ pub(crate) fn pkg_fn_to_code( asdf.into() } -pub(crate) fn package(attrs: TokenStream, input: TokenStream) -> TokenStream { - let mut ast = parse_macro_input!(input as DeriveInput); - match &mut ast.data { - syn::Data::Struct(ref mut struct_data) => { - match &mut struct_data.fields { - syn::Fields::Named(fields) => { - let base = proc_macro2::TokenStream::from(attrs); - fields.named.push( - syn::Field::parse_named - .parse2(quote! { base: #base }) - .unwrap(), - ); - fields.named.push( - syn::Field::parse_named - .parse2(quote! { version: ::bok::Version }) - .unwrap(), - ); - } - _ => (), - } +pub(crate) fn package( + attrs: TokenStream, + input: TokenStream, + __source_path: TokenStream, +) -> TokenStream { + use ::syn::{spanned::Spanned, Fields, ItemStruct}; + let local = parse_macro_input!(input as ItemStruct); + let local_span = local.span(); + let Fields::Named(local_fields) = local.fields else { + use ::syn::spanned::Spanned; + return ::syn::Error::new( + local.fields.span(), + "unnamed fields are not supported", + ) + .to_compile_error() + .into(); + }; + let local_attrs = local.attrs.iter(); + let generics = local.generics; + let ident = local.ident; + let vis = local.vis; - quote! { - #[derive(::bok::Package,::std::fmt::Debug, Clone)] - #ast - } - .into() + let source_path = parse_macro_input!(__source_path as ::syn::Path); + + let base = parse_macro_input!(attrs as ItemStruct); + let Fields::Named(base_fields) = &base.fields else { + use ::syn::spanned::Spanned; + return ::syn::Error::new( + base.fields.span(), + "unnamed fields are not supported", + ) + .to_compile_error() + .into(); + }; + // make sure the 'version' field is the first after the user's fields + // we will use as a separator from the other imported fields + let mut base_fields_extra = Vec::with_capacity(base.fields.len()); + match base_fields.named.iter().find(|&x| match &x.ident { + Some(id_local) => id_local == "version", + _ => false, + }) { + Some(_) => { + let pkg_version = ::syn::Field::parse_named + .parse2(quote! { + version: ::bok::Version + }) + .unwrap(); + let base_marker = ::syn::Field::parse_named + .parse2(quote! { + _bok_base: ::std::marker::PhantomData<#source_path> + }) + .unwrap(); + base_fields_extra.push(pkg_version); + base_fields_extra.push(base_marker); + } + None => { + return ::syn::Error::new( + local_span, + "pkg: \"".to_owned() + + &ident.to_string() + + "\" base package \"" + + &base.ident.to_string() + + "\" does not have 'version' field", + ) + .to_compile_error() + .into(); } - _ => panic!("`package` has to be used with a struct"), } + for f in base_fields.named.iter() { + let Some(id) = &f.ident else { continue }; + // don't add 'version' again + if id.to_string() == "version" || id.to_string() == "_bok_base" { + continue; + } + // compiler error if you try to extend a package while + // reusing its fields names + if let Some(repeated) = local_fields.named.iter().find(|&x| { + let Some(id_local) = &x.ident else { + return false; + }; + id_local.to_string() == id.to_string() + }) { + return ::syn::Error::new( + local_span, + "pkg: \"".to_owned() + + &ident.to_string() + + "\" field \"" + + &repeated.ident.as_ref().unwrap().to_string() + + "\" is already in package " + + &base.ident.to_string() + + "\" or its base", + ) + .to_compile_error() + .into(); + } + base_fields_extra.push(f.clone()); + } + + let mut all_fields = Vec::new(); + all_fields.extend(local_fields.named.iter()); + all_fields.extend(base_fields_extra.iter()); + quote! { + #(#local_attrs) + * + #[::macro_magic::export_tokens] + #[derive(::bok::Package, ::std::fmt::Debug, Clone)] + #vis struct #ident<#generics> { + #(#all_fields), + * + } + } + .into() } -pub(crate) fn impl_package( +// package_impl has 3 stages, and uses macro_magic import for the last two: +// * called without parameters => add default ::bok::Pkg functions, then add the +// same macro with the package type as argument +// * called with the package type as argument => get the base type, then add the +// same macro with the base type IMPL as argument +// * called with the a package impl as argument, copy the non-implemented +// functions from the base +pub(crate) fn package_impl( _attrs: TokenStream, input: TokenStream, ) -> TokenStream { - let mut ast = parse_macro_input!(input as ::syn::ItemImpl); + let ast = parse_macro_input!(input as ::syn::ItemImpl); let name_pkg = match &*ast.self_ty { ::syn::Type::Path(tp) => match tp.path.get_ident() { Some(id) => id.clone(), - _ => panic!("impl_package expected path ident"), + _ => { + return ::syn::Error::new( + ast.span(), + "package_impl: type::Path but no ident", + ) + .to_compile_error() + .into() + } }, - _ => panic!("impl_package expected path"), + _ => { + return ::syn::Error::new( + ast.span(), + "package_impl: not a type::Path", + ) + .to_compile_error() + .into() + } }; + let full_path_name = proc_macro2::Ident::new( + &("_bok_pkg_".to_owned() + &name_pkg.to_string()), + proc_macro2::Span::call_site(), + ); + + quote! { + use ::bok_macro::package_impl_base; + #[::bok_macro::package_impl_base(#name_pkg)] + #[::macro_magic::export_tokens(#full_path_name)] + #ast + } + .into() +} + +// get the actual base type and re-call the macro with the impl symbols +pub(crate) fn package_impl_base( + attrs: TokenStream, + input: TokenStream, +) -> TokenStream { + // This is a two-phase macro + // * The first time this is called the argument will be the same path as the + // struct that implements ::bok::Pkg + // * The macro will be rewritten with the base package name that is extended + // by the package + // And now that we have the proper base name we can add the missing + // methods from the base + + let mut ast = parse_macro_input!(input as ::syn::ItemImpl); + let name_pkg = match &*ast.self_ty { + ::syn::Type::Path(tp) => match tp.path.get_ident() { + Some(id) => id.clone(), + _ => panic!("::bok::package_impl expected path ident"), + }, + _ => panic!("::bok::package_impl expected path"), + }; + let Ok(syntax_tree) = syn::parse::<::syn::ItemStruct>(attrs.clone()) else { + return package_impl_base_add(attrs, &mut ast); + }; + // we were passed an exported struct. first phase. + if syntax_tree.ident.to_string() != name_pkg.to_string() { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" with wrong initial name" + + &syntax_tree.ident.to_string() + + "\" ", + ) + .to_compile_error() + .into(); + } + // go through the struct definition, extract the base type + // from the `_bok_base` phantom data marker + let base_path = match syntax_tree.fields.iter().find(|&x| { + let Some(id) = &x.ident else { + return false; + }; + return id.to_string() == "_bok_base"; + }) { + Some(field) => match &field.ty { + ::syn::Type::Path(tp) => { + if tp.path.segments.len() != 3 + || tp.path.segments[0].ident.to_string() != "std" + || tp.path.segments[1].ident.to_string() != "marker" + || tp.path.segments[2].ident.to_string() != "PhantomData" + { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" base is not PhantomData, " + + "what are you doing?", + ) + .to_compile_error() + .into(); + } + let ::syn::PathArguments::AngleBracketed(psa) = + &tp.path.segments[2].arguments + else { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" base PhantomData has no arguments " + + "what are you doing?", + ) + .to_compile_error() + .into(); + }; + if psa.args.len() != 1 { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" base PhantomData has too many arguments " + + "what are you doing?", + ) + .to_compile_error() + .into(); + } + let ::syn::GenericArgument::Type(::syn::Type::Path( + base_pkg_path, + )) = &psa.args[0] + else { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" base PhantomData has wrong argument type " + + "what are you doing?", + ) + .to_compile_error() + .into(); + }; + base_pkg_path.path.clone() + } + _ => { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" base of unknown type", + ) + .to_compile_error() + .into(); + } + }, + None => { + return ::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + &name_pkg.to_string() + + "\" can't find base", + ) + .to_compile_error() + .into(); + } + }; + // now, `base_path` is an absolute path. + // but it does not mean that it is in our crate + // we need to maintain the whole path and change only the last ident, + // to the one generated by us instead of by macro_magic + + // now `base_type` is the base struct. but what we actually want is + // to import the `impl ::bok::Pkg` for `base_type` + let mut exported_impl_name = base_path.clone(); + let last_segment_n = exported_impl_name.segments.len() - 1; + let last_segment = &mut exported_impl_name.segments[last_segment_n]; + last_segment.ident = + ::quote::format_ident!("_bok_pkg_{}", last_segment.ident); + + return quote! { + #[::bok_macro::package_impl_base(#exported_impl_name)] + #ast + } + .into(); +} + +// implements ::bok::Pkg: +// * add base methods +// * copy the non-implemented methods from the base +pub(crate) fn package_impl_base_add( + attrs: TokenStream, + ast: &mut ::syn::ItemImpl, +) -> TokenStream { + let name_pkg = match &*ast.self_ty { + ::syn::Type::Path(tp) => match tp.path.get_ident() { + Some(id) => id.clone(), + _ => panic!("::bok::package_impl expected path ident"), + }, + _ => panic!("::bok::package_impl expected path"), + }; + let base_ast = parse_macro_input!(attrs as ::syn::ItemImpl); let name: ::syn::ImplItem = ::syn::parse_quote! { fn name(&self) -> ::bok::PkgName { (module_path!().to_owned() + "::" + ::std::stringify!(#name_pkg)).as_str().into() @@ -115,16 +379,6 @@ pub(crate) fn impl_package( self.version.clone() } }; - let base: ::syn::ImplItem = ::syn::parse_quote! { - fn base(&self) -> ::core::option::Option<&dyn ::bok::Pkg> { - Some(&self.base) - } - }; - let base_mut: ::syn::ImplItem = ::syn::parse_quote! { - fn base_mut(&mut self) -> ::core::option::Option<&mut dyn ::bok::Pkg> { - Some(&mut self.base) - } - }; let any: ::syn::ImplItem = ::syn::parse_quote! { fn as_any(&self) -> &dyn ::std::any::Any { self @@ -163,7 +417,11 @@ pub(crate) fn impl_package( for mut impl_item in ast.items.iter_mut() { if let ::syn::ImplItem::Fn(ref mut fn_impl) = &mut impl_item { - if fn_impl.sig.ident.to_string() == "dependencies_set" { + if ["prepare", "configure", "build", "check", "install"] + .iter() + .find(|&f| *f == fn_impl.sig.ident.to_string().as_str()) + .is_none() + { continue; } let fn_with_code: ::syn::ImplItemFn = ::syn::parse_quote! { @@ -179,13 +437,85 @@ pub(crate) fn impl_package( ast.items.push(name); ast.items.push(path); ast.items.push(version); - ast.items.push(base); - ast.items.push(base_mut); ast.items.push(any); ast.items.push(any_mut); ast.items.push(hash); ast.items.push(hash_code); + fn maybe_add_fn( + name_pkg: &str, + ast: &mut ::syn::ItemImpl, + base_ast: &::syn::ItemImpl, + name: &str, + ) -> Result<(), TokenStream> { + let Some(base_fn) = base_ast.items.iter().find(|&f| { + let ::syn::ImplItem::Fn(func) = f else { + return false; + }; + return func.sig.ident.to_string() == name; + }) else { + return Err(::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + name_pkg + + "\" maybe_add_fn can't find " + + name + + " on base", + ) + .to_compile_error() + .into()); + }; + let ::syn::ImplItem::Fn(mut base_fn_no_code) = base_fn.clone() else { + return Err(::syn::Error::new( + ast.span(), + "package_impl: called on \"".to_owned() + + name_pkg + + "\" what was Fn is not anymore", + ) + .to_compile_error() + .into()); + }; + base_fn_no_code.attrs.retain_mut(|a| { + let ::syn::Meta::Path(mp) = &a.meta else { + return true; + }; + if mp.segments.len() == 2 + && mp.segments[0].ident.to_string() == "bok" + && mp.segments[1].ident.to_string() == "pkg_fn_to_code" + { + return false; + } + + return true; + }); + if ast + .items + .iter() + .find(|&f| { + let ::syn::ImplItem::Fn(func) = f else { + return false; + }; + return func.sig.ident.to_string() == name; + }) + .is_none() + { + let new_fn: ::syn::ImplItem = parse_quote!( + #[::bok::pkg_fn_to_code] + #base_fn_no_code + ); + ast.items.push(new_fn); + } + Ok(()) + } + for name in + ["prepare", "configure", "build", "check", "install"].into_iter() + { + let Err(e) = maybe_add_fn(&name_pkg.to_string(), ast, &base_ast, name) + else { + continue; + }; + return e; + } quote! { #ast } @@ -193,23 +523,22 @@ pub(crate) fn impl_package( } pub(crate) fn derive_package(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let mut input_nobase = input.clone(); + let mut input = parse_macro_input!(input as DeriveInput); let name = input.ident.clone(); let name_builder = quote::format_ident!("{name}Builder"); let name_builder2 = name_builder.clone(); let name_builder3 = name_builder.clone(); - let elements = match input.data { - ::syn::Data::Struct(s) => match s.fields { - syn::Fields::Named(n) => n.named, + let elements = match &input.data { + ::syn::Data::Struct(s) => match &s.fields { + syn::Fields::Named(n) => n.named.clone(), _ => panic!("only named supported"), }, _ => panic!("only struct allowed"), }; let all_fields = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() != "base" && id.to_string() != "version" { + if id.to_string() != "version" && id.to_string() != "_bok_base" { return Some(&field.ident); } } @@ -219,26 +548,15 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { let all_fields3 = all_fields.clone(); let all_fields_mut = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() != "base" && id.to_string() != "version" { + if id.to_string() != "version" && id.to_string() != "_bok_base" { return Some(quote::format_ident!("{}_mut", id.to_string())); } } None }); - let base_type = elements - .iter() - .find_map(|field| { - if let Some(id) = field.ident.clone() { - if id.to_string() == "base" { - return Some(&field.ty); - } - } - None - }) - .expect("expected a base type"); let all_types = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() != "base" && id.to_string() != "version" { + if id.to_string() != "version" && id.to_string() != "_bok_base" { return Some(&field.ty); } } @@ -247,7 +565,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { let all_types2 = all_types.clone(); let non_opt_fields = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() == "base" || id.to_string() == "version" { + if id.to_string() == "version" || id.to_string() == "_bok_base" { return None; } } @@ -266,7 +584,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { }); let opt_fields = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() == "base" || id.to_string() == "version" { + if id.to_string() == "version" || id.to_string() == "_bok_base" { return None; } } @@ -285,7 +603,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { }); let non_opt_types = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() == "base" || id.to_string() == "version" { + if id.to_string() == "version" || id.to_string() == "_bok_base" { return None; } } @@ -304,7 +622,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { }); let opt_types = elements.iter().filter_map(|field| { if let Some(id) = field.ident.clone() { - if id.to_string() == "base" || id.to_string() == "version" { + if id.to_string() == "version" || id.to_string() == "_bok_base" { return None; } } @@ -337,29 +655,15 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { let non_opt_types2 = non_opt_types.clone(); { - // remove the `base` field from the struct - let ::syn::Data::Struct(ref mut ds) = input_nobase.data else { + let ::syn::Data::Struct(ref mut ds) = input.data else { panic!("::bok::package expected a struct"); }; - let ::syn::Fields::Named(ref mut old_nf) = ds.fields else { + let ::syn::Fields::Named(ref mut _old_nf) = ds.fields else { panic!("::bok::package expected a named field"); }; - let mut new_nf = ::syn::punctuated::Punctuated::< - ::syn::Field, - ::syn::token::Comma, - >::new(); - for f in old_nf.named.iter() { - if let Some(ref id) = f.ident { - if id.to_string() == "base" { - continue; - } - } - new_nf.push(f.clone()); - } - old_nf.named = new_nf; } let pkg_struct = quote! { - #input_nobase + #input }; let mut pkg_struct_str = String::new(); { @@ -391,6 +695,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { // don't add a method if it is just the base Trait implementation fn maybe_add_fn(out: &mut String, name: &str, fn_str: &str) -> ::core::fmt::Result { + //FIXME: check with the code exported from macro_magic base let str_base = "self . base () . unwrap () . ".to_owned() + name + " ()"; if fn_str.trim() != str_base { @@ -414,13 +719,13 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { let pkg_empty = ::bok::PkgEmpty::default(); let mut pkg_string = String::new(); + // FIXME: filter out the things added from base pkg write!(&mut pkg_string, - "#[::bok::package({})]\n\ + "#[::bok::package(::bok::PkgEmpty)]\n\ {}", - ::std::stringify!(#base_type), #pkg_struct_str)?; write!(&mut pkg_string, - "#[::bok::impl_package]\n\ + "#[::bok::package_impl]\n\ impl ::bok::Pkg for {} {{\n", ::std::stringify!(#name) )?; @@ -457,14 +762,6 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { #(#non_opt_fields: ::std::option::Option<#non_opt_types>,)* #(#opt_fields: ::std::option::Option<#opt_types>,)* } - /* - impl ::std::ops::Deref for #name_builder { - type Target = #base_type; - fn deref(&self) -> &#base_type { - &self.base - } - } - */ impl ::bok::PkgBuilder for #name_builder2 { fn name(&self) -> ::bok::PkgName { use ::bok::Pkg; @@ -492,8 +789,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { })* Ok( Box::new(#name { - // FIXME: user must be able to override. Trait? - base: #base_type::default(), + _bok_base: ::std::marker::PhantomData::default(), version: #name::default().version, #(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)* #(#opt_fields2 : self.#opt_fields2.clone(),)* diff --git a/bok-utils/src/main.rs b/bok-utils/src/main.rs index 770858b..21dbc03 100644 --- a/bok-utils/src/main.rs +++ b/bok-utils/src/main.rs @@ -37,12 +37,15 @@ impl TP for P { */ fn main() { + let one = pkgs::one::One::default(); + let pkgs1 = pkgs::Pkgs1::default(); let pkgs2 = pkgs::Pkgs2::default(); use ::bok::Repository; println!("pkgs1: {}", pkgs1.name()); println!("pkgs2: {}", pkgs2.name()); + /* let mut pb: pkgs::one::OneBuilder = pkgs1.one(); let b = pb.as_builder(); @@ -57,6 +60,7 @@ fn main() { println!("hash one: {}", pkg.hash()); println!("hash one code: {}", pkg.hash_code()); } + */ /* println!("pkgs1 - 1:{}", pkgs1.one().default_unused().build().my_attr); diff --git a/bok-utils/src/pkgs/mod.rs b/bok-utils/src/pkgs/mod.rs index 69bc9e6..839c054 100644 --- a/bok-utils/src/pkgs/mod.rs +++ b/bok-utils/src/pkgs/mod.rs @@ -40,10 +40,8 @@ pub struct Pkgs1 { bok::repo_packages! { Pkgs1 { One, - Two, } } - /// /// This repository extends and changes Pkgs1 #[::bok::repository(Pkgs1)] @@ -59,6 +57,7 @@ bok::repo_packages! { } } +/* impl Pkgs2 { /// override the package `two` options from the base repostiory (Pkgs1) pub fn two(&self) -> Two { @@ -72,8 +71,10 @@ impl Pkgs2 { two.clone() } } + /// This repository extends both Pkgs1 and Pkgs2 #[::bok::repository(Pkgs2)] #[::bok::repository(Pkgs1)] #[derive(::std::default::Default)] pub struct Pkgs3 {} +*/ diff --git a/bok-utils/src/pkgs/one.rs b/bok-utils/src/pkgs/one.rs index 3e9ca01..c2eb7e6 100644 --- a/bok-utils/src/pkgs/one.rs +++ b/bok-utils/src/pkgs/one.rs @@ -15,9 +15,13 @@ * limitations under the License. */ +use ::bok::package; +//use ::bok::{package, PkgEmpty}; +//use ::bok::*; + /// Example package /// Automatically implements `.builder().my_attr(42).build()` pattern -#[::bok_macro::package(::bok::PkgEmpty)] +#[::bok::package(::bok::PkgEmpty)] pub struct One { pub my_attr: u32, } @@ -25,7 +29,7 @@ pub struct One { impl ::std::default::Default for One { fn default() -> Self { One { - base: ::bok::PkgEmpty::default(), + _bok_base: ::std::marker::PhantomData::default(), my_attr: 1, version: ::bok::Version { major: 0, @@ -38,10 +42,10 @@ impl ::std::default::Default for One { } } -#[::bok::impl_package] +#[::bok::package_impl] impl ::bok::Pkg for One { fn build(&self) -> ::core::result::Result<(), ()> { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()) } fn dependencies_set( &self, diff --git a/bok-utils/src/pkgs/three.rs b/bok-utils/src/pkgs/three.rs index 04d727b..19cc64c 100644 --- a/bok-utils/src/pkgs/three.rs +++ b/bok-utils/src/pkgs/three.rs @@ -15,9 +15,10 @@ * limitations under the License. */ +use ::bok::package; + /// Example package -/// Automatically implements `.builder().my_attr(42).build()` pattern -#[::bok_macro::package(::bok::PkgEmpty)] +#[::bok::package(::bok::PkgEmpty)] pub struct Three { pub my_attr: u32, } @@ -25,7 +26,7 @@ pub struct Three { impl ::std::default::Default for Three { fn default() -> Self { Three { - base: bok::PkgEmpty::default(), + _bok_base: ::std::marker::PhantomData::default(), version: ::bok::Version { major: 0, minor: 0, @@ -37,10 +38,11 @@ impl ::std::default::Default for Three { } } } -#[::bok::impl_package] + +#[::bok::package_impl] impl ::bok::Pkg for Three { fn build(&self) -> ::core::result::Result<(), ()> { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()) } fn dependencies_set( &self, diff --git a/bok-utils/src/pkgs/two.rs b/bok-utils/src/pkgs/two.rs index 3d4767f..4f8727f 100644 --- a/bok-utils/src/pkgs/two.rs +++ b/bok-utils/src/pkgs/two.rs @@ -15,18 +15,20 @@ * limitations under the License. */ +use ::bok::package; + /// Example package -/// Automatically implements `.builder().my_attr(42).build()` pattern -#[::bok_macro::package(::bok::PkgEmpty)] +#[::bok::package(crate::pkgs::One)] pub struct Two { - pub my_attr: u32, + pub my_attr2: u32, } impl ::std::default::Default for Two { fn default() -> Self { Two { - base: ::bok::PkgEmpty::default(), + _bok_base: ::std::marker::PhantomData::default(), my_attr: 2, + my_attr2: 2, version: ::bok::Version { major: 0, minor: 0, @@ -37,7 +39,8 @@ impl ::std::default::Default for Two { } } } -#[::bok::impl_package] + +#[::bok::package_impl] impl ::bok::Pkg for Two { fn dependencies_set( &self, diff --git a/bok/src/lib.rs b/bok/src/lib.rs index 953cbed..b2a9c8a 100644 --- a/bok/src/lib.rs +++ b/bok/src/lib.rs @@ -1,20 +1,19 @@ /* - -* Copyright 2024 Luca Fulchir -* -* Licensed under the Apache License, Version 2.0 with LLVM exception (the -* "License"); you may not use this file except in compliance with the -* License. You may obtain a copy of the License and of the exception at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* https://spdx.org/licenses/LLVM-exception.html -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright 2024 Luca Fulchir + * + * Licensed under the Apache License, Version 2.0 with LLVM exception (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License and of the exception at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * https://spdx.org/licenses/LLVM-exception.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ use ::std::sync::Arc; @@ -28,7 +27,7 @@ pub mod deps { } pub use ::bok_macro::{ - impl_package, package, pkg_fn_to_code, repository, Package, Repository, + package, package_impl, pkg_fn_to_code, repository, Package, Repository, }; pub use ::semver::{BuildMetadata, Prerelease, Version}; @@ -121,8 +120,6 @@ pub trait Pkg: fn name(&self) -> PkgName; fn path(&self) -> Path; fn version(&self) -> Version; - fn base(&self) -> ::core::option::Option<&dyn Pkg>; - fn base_mut(&mut self) -> ::core::option::Option<&mut dyn Pkg>; fn as_any(&self) -> &dyn ::std::any::Any; fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any; fn dependencies_set( @@ -131,46 +128,16 @@ pub trait Pkg: build: &deps::Build, runtime: &deps::Runtime, ) -> Result<(), ()>; - fn prepare(&self) -> ::core::result::Result<(), ()> { - self.base().unwrap().prepare() - } - fn prepare_code(&self) -> ::proc_macro2::TokenStream { - ::quote::quote! { - self.base().unwrap().prepare() - } - } - fn configure(&self) -> ::core::result::Result<(), ()> { - self.base().unwrap().configure() - } - fn configure_code(&self) -> ::proc_macro2::TokenStream { - ::quote::quote! { - self.base().unwrap().configure() - } - } - fn build(&self) -> ::core::result::Result<(), ()> { - self.base().unwrap().build() - } - fn build_code(&self) -> ::proc_macro2::TokenStream { - ::quote::quote! { - self.base().unwrap().build() - } - } - fn check(&self) -> ::core::result::Result<(), ()> { - self.base().unwrap().check() - } - fn check_code(&self) -> ::proc_macro2::TokenStream { - ::quote::quote! { - self.base().unwrap().check() - } - } - fn install(&self) -> ::core::result::Result<(), ()> { - self.base().unwrap().install() - } - fn install_code(&self) -> ::proc_macro2::TokenStream { - ::quote::quote! { - self.base().unwrap().install() - } - } + fn prepare(&self) -> ::core::result::Result<(), ()>; + fn prepare_code(&self) -> ::proc_macro2::TokenStream; + fn configure(&self) -> ::core::result::Result<(), ()>; + fn configure_code(&self) -> ::proc_macro2::TokenStream; + fn build(&self) -> ::core::result::Result<(), ()>; + fn build_code(&self) -> ::proc_macro2::TokenStream; + fn check(&self) -> ::core::result::Result<(), ()>; + fn check_code(&self) -> ::proc_macro2::TokenStream; + fn install(&self) -> ::core::result::Result<(), ()>; + fn install_code(&self) -> ::proc_macro2::TokenStream; fn package(&self) -> ::core::result::Result<(), ()> { self.prepare()?; self.configure()?; @@ -192,6 +159,7 @@ pub trait PkgBuilder: ::core::fmt::Debug + ::std::any::Any { ) -> Result, ::std::boxed::Box>; } +#[::macro_magic::export_tokens] #[derive(Debug, Clone)] pub struct PkgEmpty { version: Version, @@ -223,6 +191,7 @@ impl PkgCode for PkgEmpty { } } +#[::macro_magic::export_tokens(_bok_pkg_PkgEmpty)] impl Pkg for PkgEmpty { fn name(&self) -> PkgName { "PkgEmpty".into() @@ -233,12 +202,6 @@ impl Pkg for PkgEmpty { fn version(&self) -> Version { self.version.clone() } - fn base(&self) -> Option<&dyn Pkg> { - None - } - fn base_mut(&mut self) -> Option<&mut dyn Pkg> { - None - } fn as_any(&self) -> &dyn ::std::any::Any { self } @@ -258,7 +221,7 @@ impl Pkg for PkgEmpty { } fn prepare_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()); } } fn configure(&self) -> ::core::result::Result<(), ()> { @@ -266,7 +229,7 @@ impl Pkg for PkgEmpty { } fn configure_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()); } } fn build(&self) -> ::core::result::Result<(), ()> { @@ -274,7 +237,7 @@ impl Pkg for PkgEmpty { } fn build_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()); } } fn check(&self) -> ::core::result::Result<(), ()> { @@ -282,7 +245,7 @@ impl Pkg for PkgEmpty { } fn check_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()); } } fn install(&self) -> ::core::result::Result<(), ()> { @@ -290,7 +253,7 @@ impl Pkg for PkgEmpty { } fn install_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - ret = ::core::result::Result::Ok(()); + ::core::result::Result::Ok(()); } } fn hash(&self) -> Hash { diff --git a/flake.nix b/flake.nix index a165dba..f300fd2 100644 --- a/flake.nix +++ b/flake.nix @@ -53,6 +53,8 @@ #mold # fenrir deps hwloc + # development + nixd ]; # if you want to try the mold linker, add 'clang_16', 'mold', and append this to ~/.cargo/config.toml: # [target.x86_64-unknown-linux-gnu]