diff --git a/Cargo.lock b/Cargo.lock index 227bbb858..ba6613a31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4454,6 +4454,7 @@ dependencies = [ "cairo-lang-sierra-to-casm", "cairo-lang-starknet", "cairo-lang-starknet-classes", + "cairo-lang-syntax", "cairo-lang-test-plugin", "cairo-lang-utils", "camino", diff --git a/scarb/Cargo.toml b/scarb/Cargo.toml index ac567c20e..806e8fe67 100644 --- a/scarb/Cargo.toml +++ b/scarb/Cargo.toml @@ -26,6 +26,7 @@ cairo-lang-sierra-to-casm.workspace = true cairo-lang-sierra.workspace = true cairo-lang-starknet-classes.workspace = true cairo-lang-starknet.workspace = true +cairo-lang-syntax.workspace = true cairo-lang-test-plugin.workspace = true cairo-lang-utils.workspace = true camino.workspace = true diff --git a/scarb/src/compiler/db.rs b/scarb/src/compiler/db.rs index 5c3be339c..e36190a91 100644 --- a/scarb/src/compiler/db.rs +++ b/scarb/src/compiler/db.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Result}; -use cairo_lang_compiler::db::RootDatabase; +use cairo_lang_compiler::db::{RootDatabase, RootDatabaseBuilder}; use cairo_lang_compiler::project::{AllCratesConfig, ProjectConfig, ProjectConfigContent}; use cairo_lang_defs::db::DefsGroup; use cairo_lang_defs::ids::ModuleId; @@ -11,6 +11,8 @@ use smol_str::SmolStr; use std::sync::Arc; use tracing::trace; +use crate::compiler::plugin::proc_macro::ProcMacroHostPlugin; +use crate::compiler::plugin::CairoPlugin; use crate::compiler::{CompilationUnit, CompilationUnitComponent}; use crate::core::Workspace; use crate::DEFAULT_MODULE_MAIN_FILE; @@ -23,19 +25,32 @@ pub(crate) fn build_scarb_root_database( let mut b = RootDatabase::builder(); b.with_project_config(build_project_config(unit)?); b.with_cfg(unit.cfg_set.clone()); - - for plugin_info in &unit.cairo_plugins { - let package_id = plugin_info.package.id; - let plugin = ws.config().cairo_plugins().fetch(package_id)?; - let instance = plugin.instantiate()?; - b.with_plugin_suite(instance.plugin_suite()); - } - + load_plugins(unit, ws, &mut b)?; let mut db = b.build()?; inject_virtual_wrapper_lib(&mut db, unit)?; Ok(db) } +fn load_plugins( + unit: &CompilationUnit, + ws: &Workspace<'_>, + builder: &mut RootDatabaseBuilder, +) -> Result<()> { + let mut proc_macros = ProcMacroHostPlugin::default(); + for plugin_info in &unit.cairo_plugins { + if plugin_info.builtin { + let package_id = plugin_info.package.id; + let plugin = ws.config().cairo_plugins().fetch(package_id)?; + let instance = plugin.instantiate()?; + builder.with_plugin_suite(instance.plugin_suite()); + } else { + proc_macros.register(plugin_info.package.clone()); + } + } + builder.with_plugin_suite(proc_macros.instantiate()?.plugin_suite()); + Ok(()) +} + /// Generates a wrapper lib file for appropriate compilation units. /// /// This approach allows compiling crates that do not define `lib.cairo` file. diff --git a/scarb/src/compiler/plugin/mod.rs b/scarb/src/compiler/plugin/mod.rs index 3eea2e11e..6f7fe3c3a 100644 --- a/scarb/src/compiler/plugin/mod.rs +++ b/scarb/src/compiler/plugin/mod.rs @@ -11,6 +11,7 @@ use crate::core::PackageId; use self::builtin::{BuiltinStarkNetPlugin, BuiltinTestPlugin}; pub mod builtin; +pub mod proc_macro; pub trait CairoPlugin: Sync { fn id(&self) -> PackageId; diff --git a/scarb/src/compiler/plugin/proc_macro/host.rs b/scarb/src/compiler/plugin/proc_macro/host.rs new file mode 100644 index 000000000..e473317b0 --- /dev/null +++ b/scarb/src/compiler/plugin/proc_macro/host.rs @@ -0,0 +1,101 @@ +use crate::compiler::plugin::{CairoPlugin, CairoPluginInstance}; +use crate::core::{Package, PackageId, PackageName, SourceId}; +use crate::internal::to_version::ToVersion; +use anyhow::Result; +use cairo_lang_defs::plugin::{MacroPlugin, MacroPluginMetadata, PluginResult}; +use cairo_lang_semantic::plugin::PluginSuite; +use cairo_lang_syntax::node::ast::ModuleItem; +use cairo_lang_syntax::node::db::SyntaxGroup; +use smol_str::SmolStr; +use std::collections::HashMap; +use std::sync::Arc; +use typed_builder::TypedBuilder; + +#[derive(Debug, Clone)] +pub struct ProcMacroInstance {} + +impl ProcMacroInstance { + pub fn new(_package: Package) -> Self { + // Load shared library + // TODO(maciektr): Implement + Self {} + } +} + +#[derive(Debug, TypedBuilder)] +pub struct ProcMacroHost { + macros: HashMap>, +} + +impl MacroPlugin for ProcMacroHost { + fn generate_code( + &self, + _db: &dyn SyntaxGroup, + _item_ast: ModuleItem, + _metadata: &MacroPluginMetadata<'_>, + ) -> PluginResult { + // Apply expansion to `item_ast` where needed. + // TODO(maciektr): Implement + PluginResult::default() + } + + fn declared_attributes(&self) -> Vec { + self.macros.keys().map(|name| name.to_string()).collect() + } +} + +#[derive(Default)] +pub struct ProcMacroHostPlugin { + macros: HashMap>, +} + +impl ProcMacroHostPlugin { + pub fn plugin_id() -> PackageId { + PackageId::new( + PackageName::PROC_MACRO_HOST, + crate::version::get().cairo.version.to_version().unwrap(), + SourceId::for_std(), + ) + } + + pub fn register(&mut self, package: Package) { + // Create instance + // Register instance in hash map + let name = package.id.name.to_smol_str(); + let instance = ProcMacroInstance::new(package); + self.macros.insert(name, Box::new(instance)); + } +} + +impl CairoPlugin for ProcMacroHostPlugin { + fn id(&self) -> PackageId { + Self::plugin_id() + } + + fn instantiate(&self) -> Result> { + let instance = ProcMacroHostPluginInstance::builder() + .macros(self.macros.clone()) + .build(); + Ok(Box::new(instance)) + } +} + +#[derive(TypedBuilder)] +pub struct ProcMacroHostPluginInstance { + macros: HashMap>, +} + +impl ProcMacroHostPluginInstance { + /// Build compiler `MacroPlugin` from Scarb representation `ProcMacroHostPluginInstance`. + pub fn build(&self) -> ProcMacroHost { + ProcMacroHost::builder().macros(self.macros.clone()).build() + } +} + +impl CairoPluginInstance for ProcMacroHostPluginInstance { + fn plugin_suite(&self) -> PluginSuite { + let mut suite = PluginSuite::default(); + suite.add_plugin_ex(Arc::new(self.build())); + suite + } +} diff --git a/scarb/src/compiler/plugin/proc_macro/mod.rs b/scarb/src/compiler/plugin/proc_macro/mod.rs new file mode 100644 index 000000000..b4662903f --- /dev/null +++ b/scarb/src/compiler/plugin/proc_macro/mod.rs @@ -0,0 +1,3 @@ +mod host; + +pub use host::*; diff --git a/scarb/src/core/package/name.rs b/scarb/src/core/package/name.rs index 7177f2705..5490560e2 100644 --- a/scarb/src/core/package/name.rs +++ b/scarb/src/core/package/name.rs @@ -30,6 +30,7 @@ impl PackageName { pub const CORE: Self = PackageName(SmolStr::new_inline(CORELIB_CRATE_NAME)); pub const STARKNET: Self = PackageName(SmolStr::new_inline("starknet")); pub const TEST_PLUGIN: Self = PackageName(SmolStr::new_inline("test_plugin")); + pub const PROC_MACRO_HOST: Self = PackageName(SmolStr::new_inline("proc_macro_host")); /// Constructs and validates new [`PackageName`]. ///