Skip to content

Commit 21e6d18

Browse files
authored
Implement loading shared libraries for proc macro plugins (#1093)
Resolves #1128 commit-id:a3155bbf --- **Stack**: - #1159 - #1157 - #1143 - #1151 - #1150 - #1148 - #1100 - #1155 - #1110 - #1093⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do not merge manually using the UI - doing so may have unexpected results.*
1 parent d5430cb commit 21e6d18

File tree

9 files changed

+125
-17
lines changed

9 files changed

+125
-17
lines changed

Cargo.lock

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ indoc = "2"
7979
io_tee = "0.1"
8080
itertools = "0.12"
8181
libc = "0.2"
82+
libloading = "0.8.1"
8283
log = "0.4"
8384
ntest = "0.9"
8485
num-bigint = { version = "0.4", features = ["rand"] }

scarb/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ ignore.workspace = true
4646
include_dir.workspace = true
4747
indoc.workspace = true
4848
itertools.workspace = true
49+
libloading.workspace = true
4950
once_cell.workspace = true
5051
pathdiff.workspace = true
5152
petgraph.workspace = true

scarb/src/compiler/db.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ fn load_plugins(
4343
let instance = plugin.instantiate()?;
4444
builder.with_plugin_suite(instance.plugin_suite());
4545
} else {
46-
proc_macros.register(plugin_info.package.clone())?;
46+
proc_macros.register(plugin_info.package.clone(), ws.config())?;
4747
}
4848
}
4949
builder.with_plugin_suite(proc_macros.into_plugin_suite());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::compiler::plugin::proc_macro::PROC_MACRO_BUILD_PROFILE;
2+
use crate::core::{Config, Package};
3+
use crate::flock::Filesystem;
4+
use camino::Utf8PathBuf;
5+
use libloading::library_filename;
6+
7+
/// This trait is used to define the shared library path for a package.
8+
pub trait SharedLibraryProvider {
9+
/// Location of Cargo `target` directory.
10+
fn target_path(&self, config: &Config) -> Filesystem;
11+
/// Location of the shared library for the package.
12+
fn shared_lib_path(&self, config: &Config) -> Utf8PathBuf;
13+
}
14+
15+
impl SharedLibraryProvider for Package {
16+
fn target_path(&self, config: &Config) -> Filesystem {
17+
let ident = format!("{}-{}", self.id.name, self.id.source_id.ident());
18+
// Defines the Cargo target directory in cache, as:
19+
// `/(..)/SCARB_CACHE/plugins/proc_macro/<package_name>-<source_id_ident>/v<version>/target/`
20+
config
21+
.dirs()
22+
.procedural_macros_dir()
23+
.into_child(ident)
24+
.into_child(format!("v{}", self.id.version))
25+
.into_child("target")
26+
}
27+
28+
fn shared_lib_path(&self, config: &Config) -> Utf8PathBuf {
29+
let lib_name = library_filename(self.id.name.to_string());
30+
let lib_name = lib_name
31+
.into_string()
32+
.expect("library name must be valid UTF-8");
33+
// Defines the shared library path inside the target directory, as:
34+
// `/(..)/target/release/[lib]<package_name>.[so|dll|dylib]`
35+
self.target_path(config)
36+
.into_child(PROC_MACRO_BUILD_PROFILE)
37+
.path_unchecked()
38+
.join(lib_name)
39+
}
40+
}
+63-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
use crate::core::{Package, PackageId};
2-
use anyhow::Result;
1+
use crate::core::{Config, Package, PackageId};
2+
use anyhow::{Context, Result};
33
use cairo_lang_defs::patcher::PatchBuilder;
4+
use cairo_lang_macro::stable_abi::{StableProcMacroResult, StableTokenStream};
45
use cairo_lang_macro::{ProcMacroResult, TokenStream};
56
use cairo_lang_syntax::node::db::SyntaxGroup;
67
use cairo_lang_syntax::node::{ast, TypedSyntaxNode};
8+
use camino::Utf8PathBuf;
9+
use libloading::{Library, Symbol};
710
use std::fmt::Debug;
811

12+
use crate::compiler::plugin::proc_macro::compilation::SharedLibraryProvider;
13+
#[cfg(not(windows))]
14+
use libloading::os::unix::Symbol as RawSymbol;
15+
#[cfg(windows)]
16+
use libloading::os::windows::Symbol as RawSymbol;
17+
18+
pub const PROC_MACRO_BUILD_PROFILE: &str = "release";
19+
920
pub trait FromItemAst {
1021
fn from_item_ast(db: &dyn SyntaxGroup, item_ast: ast::ModuleItem) -> Self;
1122
}
@@ -22,31 +33,72 @@ impl FromItemAst for TokenStream {
2233
///
2334
/// This struct is a wrapper around a shared library containing the procedural macro implementation.
2435
/// It is responsible for loading the shared library and providing a safe interface for code expansion.
25-
#[derive(Debug, Clone)]
2636
pub struct ProcMacroInstance {
2737
package_id: PackageId,
38+
plugin: Plugin,
39+
}
40+
41+
impl Debug for ProcMacroInstance {
42+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43+
f.debug_struct("ProcMacroInstance")
44+
.field("package_id", &self.package_id)
45+
.finish()
46+
}
2847
}
2948

3049
impl ProcMacroInstance {
3150
pub fn package_id(&self) -> PackageId {
3251
self.package_id
3352
}
3453

35-
pub fn try_new(package: Package) -> Result<Self> {
36-
// Load shared library
37-
// TODO(maciektr): Implement
54+
/// Load shared library
55+
pub fn try_new(package: Package, config: &Config) -> Result<Self> {
56+
let lib_path = package.shared_lib_path(config);
57+
let plugin = unsafe { Plugin::try_new(lib_path.to_path_buf())? };
3858
Ok(Self {
59+
plugin,
3960
package_id: package.id,
4061
})
4162
}
42-
4363
pub fn declared_attributes(&self) -> Vec<String> {
4464
vec![self.package_id.name.to_string()]
4565
}
4666

47-
pub(crate) fn generate_code(&self, _token_stream: TokenStream) -> ProcMacroResult {
48-
// Apply expansion to token stream.
49-
// TODO(maciektr): Implement
50-
ProcMacroResult::Leave
67+
/// Apply expansion to token stream.
68+
pub(crate) fn generate_code(&self, token_stream: TokenStream) -> ProcMacroResult {
69+
let ffi_token_stream = unsafe { StableTokenStream::from_token_stream(token_stream) };
70+
let result = (self.plugin.vtable.expand)(ffi_token_stream);
71+
unsafe { result.into_proc_macro_result() }
72+
}
73+
}
74+
75+
type ExpandCode = extern "C" fn(StableTokenStream) -> StableProcMacroResult;
76+
77+
struct VTableV0 {
78+
expand: RawSymbol<ExpandCode>,
79+
}
80+
81+
impl VTableV0 {
82+
unsafe fn try_new(library: &Library) -> Result<VTableV0> {
83+
let expand: Symbol<'_, ExpandCode> = library
84+
.get(b"expand\0")
85+
.context("failed to load expand function for procedural macro")?;
86+
let expand = expand.into_raw();
87+
Ok(VTableV0 { expand })
88+
}
89+
}
90+
91+
struct Plugin {
92+
#[allow(dead_code)]
93+
library: Library,
94+
vtable: VTableV0,
95+
}
96+
97+
impl Plugin {
98+
unsafe fn try_new(library_path: Utf8PathBuf) -> Result<Plugin> {
99+
let library = Library::new(library_path)?;
100+
let vtable = VTableV0::try_new(&library)?;
101+
102+
Ok(Plugin { library, vtable })
51103
}
52104
}

scarb/src/compiler/plugin/proc_macro/host.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::compiler::plugin::proc_macro::{FromItemAst, ProcMacroInstance};
2-
use crate::core::{Package, PackageId};
2+
use crate::core::{Config, Package, PackageId};
33
use anyhow::Result;
44
use cairo_lang_defs::plugin::{
55
MacroPlugin, MacroPluginMetadata, PluginGeneratedFile, PluginResult,
@@ -178,10 +178,8 @@ pub struct ProcMacroHost {
178178
}
179179

180180
impl ProcMacroHost {
181-
pub fn register(&mut self, package: Package) -> Result<()> {
182-
// Create instance
183-
// Register instance in hash map
184-
let instance = ProcMacroInstance::try_new(package)?;
181+
pub fn register(&mut self, package: Package, config: &Config) -> Result<()> {
182+
let instance = ProcMacroInstance::try_new(package, config)?;
185183
self.macros.push(Arc::new(instance));
186184
Ok(())
187185
}

scarb/src/compiler/plugin/proc_macro/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod compilation;
12
mod ffi;
23
mod host;
34

scarb/src/core/dirs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ impl AppDirs {
7070
pub fn registry_dir(&self) -> Filesystem {
7171
self.cache_dir.child("registry")
7272
}
73+
74+
pub fn procedural_macros_dir(&self) -> Filesystem {
75+
self.cache_dir.child("plugins").child("proc_macro")
76+
}
7377
}
7478

7579
impl fmt::Display for AppDirs {

0 commit comments

Comments
 (0)