Skip to content

Commit 6769c8b

Browse files
maciektrDelevoXDG
andauthored
Load prebuilt macros (#1856)
Originally submitted as #1812 --------- Co-authored-by: Maksim Zdobnikau <[email protected]>
1 parent 3555a26 commit 6769c8b

19 files changed

+555
-104
lines changed

Diff for: .github/workflows/ci.yml

+24
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,30 @@ jobs:
7878
- name: run tests
7979
run: cargo test -p scarb-metadata
8080

81+
test-prebuilt-plugins:
82+
name: test prebuilt plugins ${{ matrix.platform.name }}
83+
runs-on: ${{ matrix.platform.os }}
84+
# This is isolated, so it can be run on more platforms.
85+
strategy:
86+
fail-fast: false
87+
matrix:
88+
platform:
89+
- name: linux x86-64
90+
os: ubuntu-latest
91+
- name: windows x86-64
92+
os: windows-latest
93+
- name: macos arm64
94+
os: macos-latest
95+
- name: macos x86-64
96+
os: macos-13
97+
steps:
98+
- uses: actions/checkout@v4
99+
- uses: dtolnay/rust-toolchain@stable
100+
- uses: Swatinem/rust-cache@v2
101+
- name: Run prebuilt plugin tests
102+
run: |
103+
cargo test -p scarb --test proc_macro_prebuilt -- --ignored
104+
81105
check-rust:
82106
runs-on: ubuntu-latest
83107
steps:

Diff for: Cargo.lock

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

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ xshell = "0.2"
146146
xxhash-rust = { version = "0.8", features = ["xxh3"] }
147147
zip = { version = "0.6", default-features = false, features = ["deflate"] }
148148
zstd = "0.13"
149+
target-triple = "0.1"
149150

150151
# Here we specify real dependency specifications for Cairo crates *if* currently we want to use
151152
# a particular unreleased commit (which is frequent mid-development).

Diff for: scarb/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ windows-sys.workspace = true
9090
zstd.workspace = true
9191
cargo_metadata.workspace = true
9292
flate2.workspace = true
93+
target-triple.workspace = true
9394

9495
[target.'cfg(not(target_os = "linux"))'.dependencies]
9596
reqwest = { workspace = true, default-features = true }

Diff for: scarb/src/bin/scarb/commands/proc_macro_server.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::Result;
2+
use scarb::ops::CompilationUnitsOpts;
23
use scarb::{
34
compiler::{plugin::proc_macro::ProcMacroHost, CairoCompilationUnit, CompilationUnit},
45
core::{Config, Workspace},
@@ -15,8 +16,11 @@ pub fn run(config: &mut Config) -> Result<()> {
1516
features: FeaturesSelector::AllFeatures,
1617
no_default_features: false,
1718
},
18-
true,
1919
&ws,
20+
CompilationUnitsOpts {
21+
ignore_cairo_version: true,
22+
load_prebuilt_macros: true,
23+
},
2024
)?;
2125

2226
// Compile procedural macros only.
@@ -43,12 +47,12 @@ fn load_plugins(
4347
ws: &Workspace<'_>,
4448
proc_macros: &mut ProcMacroHost,
4549
) -> Result<()> {
46-
for plugin_info in unit
47-
.cairo_plugins
48-
.into_iter()
49-
.filter(|plugin_info| !plugin_info.builtin)
50-
{
51-
proc_macros.register(plugin_info.package, ws.config())?;
50+
for plugin_info in unit.cairo_plugins.into_iter().filter(|p| !p.builtin) {
51+
if let Some(prebuilt) = plugin_info.prebuilt {
52+
proc_macros.register_instance(prebuilt);
53+
} else {
54+
proc_macros.register_new(plugin_info.package, ws.config())?;
55+
}
5256
}
5357

5458
Ok(())

Diff for: scarb/src/compiler/compilation_unit.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
use std::fmt::Write;
2-
use std::hash::{Hash, Hasher};
3-
41
use anyhow::{ensure, Result};
52
use cairo_lang_filesystem::cfg::CfgSet;
63
use cairo_lang_filesystem::db::CrateIdentifier;
74
use itertools::Itertools;
85
use serde::{Deserialize, Serialize};
96
use smol_str::SmolStr;
7+
use std::fmt::Write;
8+
use std::hash::{Hash, Hasher};
9+
use std::sync::Arc;
1010
use typed_builder::TypedBuilder;
1111

12+
use crate::compiler::plugin::proc_macro::ProcMacroInstance;
1213
use crate::compiler::Profile;
1314
use crate::core::{
1415
ManifestCompilerConfig, Package, PackageId, PackageName, Target, TargetKind, Workspace,
@@ -72,6 +73,9 @@ pub struct ProcMacroCompilationUnit {
7273

7374
/// Rust compiler configuration parameters to use in this unit.
7475
pub compiler_config: serde_json::Value,
76+
77+
/// Instance of the proc macro loaded from prebuilt library, if available.
78+
pub prebuilt: Option<Arc<ProcMacroInstance>>,
7579
}
7680

7781
/// Information about a single package that is part of a [`CompilationUnit`].
@@ -96,8 +100,11 @@ pub struct CompilationUnitComponent {
96100
pub struct CompilationUnitCairoPlugin {
97101
/// The Scarb plugin [`Package`] to load.
98102
pub package: Package,
103+
/// Indicate whether the plugin is built into Scarb, or compiled from source.
99104
pub builtin: bool,
100105
pub prebuilt_allowed: bool,
106+
/// Instance of the proc macro loaded from prebuilt library, if available.
107+
pub prebuilt: Option<Arc<ProcMacroInstance>>,
101108
}
102109

103110
/// Unique identifier of the compilation unit component.

Diff for: scarb/src/compiler/db.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use crate::compiler::plugin::proc_macro::{ProcMacroHost, ProcMacroHostPlugin};
2+
use crate::compiler::{CairoCompilationUnit, CompilationUnitAttributes, CompilationUnitComponent};
3+
use crate::core::Workspace;
4+
use crate::DEFAULT_MODULE_MAIN_FILE;
15
use anyhow::{anyhow, Result};
26
use cairo_lang_compiler::db::{RootDatabase, RootDatabaseBuilder};
37
use cairo_lang_compiler::project::{AllCratesConfig, ProjectConfig, ProjectConfigContent};
@@ -14,11 +18,6 @@ use std::path::PathBuf;
1418
use std::sync::Arc;
1519
use tracing::trace;
1620

17-
use crate::compiler::plugin::proc_macro::{ProcMacroHost, ProcMacroHostPlugin};
18-
use crate::compiler::{CairoCompilationUnit, CompilationUnitAttributes, CompilationUnitComponent};
19-
use crate::core::Workspace;
20-
use crate::DEFAULT_MODULE_MAIN_FILE;
21-
2221
pub struct ScarbDatabase {
2322
pub db: RootDatabase,
2423
pub proc_macro_host: Arc<ProcMacroHostPlugin>,
@@ -59,8 +58,10 @@ fn load_plugins(
5958
let plugin = ws.config().cairo_plugins().fetch(package_id)?;
6059
let instance = plugin.instantiate()?;
6160
builder.with_plugin_suite(instance.plugin_suite());
61+
} else if let Some(prebuilt) = &plugin_info.prebuilt {
62+
proc_macros.register_instance(prebuilt.clone());
6263
} else {
63-
proc_macros.register(plugin_info.package.clone(), ws.config())?;
64+
proc_macros.register_new(plugin_info.package.clone(), ws.config())?;
6465
}
6566
}
6667
let macro_host = Arc::new(proc_macros.into_plugin()?);

Diff for: scarb/src/compiler/plugin/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use itertools::Itertools;
99
use serde::{Deserialize, Serialize};
1010

1111
use crate::compiler::plugin::builtin::BuiltinCairoRunPlugin;
12+
use crate::compiler::plugin::proc_macro::compilation::SharedLibraryProvider;
1213
use crate::core::{Package, PackageId, TargetKind, Workspace};
1314

1415
use self::builtin::{BuiltinStarkNetPlugin, BuiltinTestPlugin};
@@ -29,8 +30,10 @@ pub fn fetch_cairo_plugin(package: &Package, ws: &Workspace<'_>) -> Result<()> {
2930
assert!(package.is_cairo_plugin());
3031
let target = package.fetch_target(&TargetKind::CAIRO_PLUGIN)?;
3132
let props: CairoPluginProps = target.props()?;
32-
// No need to fetch for buildin plugins.
33-
if !props.builtin {
33+
// There is no need to run `cargo fetch` for builtin plugins.
34+
// The `fetch` will not be run for a proc macro that contains a prebuilt library file.
35+
// Note, that in case the prebuilt lib file is corrupted, it will be later compiled with Cargo anyway.
36+
if !props.builtin && package.prebuilt_lib_path().is_none() {
3437
proc_macro::fetch_crate(package, ws)?;
3538
}
3639
Ok(())

Diff for: scarb/src/compiler/plugin/proc_macro/compilation.rs

+25
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ use ra_ap_toolchain::Tool;
1515
use scarb_ui::{Message, OutputFormat};
1616
use serde::{Serialize, Serializer};
1717
use serde_json::value::RawValue;
18+
use std::env::consts::DLL_SUFFIX;
1819
use std::fmt::Display;
1920
use std::fs;
2021
use std::io::{Seek, SeekFrom};
2122
use std::ops::Deref;
2223
use std::process::Command;
2324
use tar::Archive;
25+
use target_triple::target;
2426
use tracing::trace_span;
2527

2628
pub const PROC_MACRO_BUILD_PROFILE: &str = "release";
@@ -31,6 +33,8 @@ pub trait SharedLibraryProvider {
3133
fn target_path(&self, config: &Config) -> Filesystem;
3234
/// Location of the shared library for the package.
3335
fn shared_lib_path(&self, config: &Config) -> Result<Utf8PathBuf>;
36+
/// Location of the prebuilt binary for the package, if defined.
37+
fn prebuilt_lib_path(&self) -> Option<Utf8PathBuf>;
3438
}
3539

3640
impl SharedLibraryProvider for Package {
@@ -61,6 +65,27 @@ impl SharedLibraryProvider for Package {
6165
.path_unchecked()
6266
.join(lib_name))
6367
}
68+
69+
fn prebuilt_lib_path(&self) -> Option<Utf8PathBuf> {
70+
let target_triple = target!();
71+
72+
let prebuilt_name = format!(
73+
"{name}_v{version}_{target}{suffix}",
74+
name = self.id.name,
75+
version = self.id.version,
76+
target = target_triple,
77+
suffix = DLL_SUFFIX
78+
);
79+
80+
let prebuilt_path = self
81+
.root()
82+
.join("target")
83+
.join("scarb")
84+
.join("cairo-plugin")
85+
.join(prebuilt_name);
86+
87+
prebuilt_path.exists().then_some(prebuilt_path)
88+
}
6489
}
6590

6691
pub fn compile_unit(unit: ProcMacroCompilationUnit, ws: &Workspace<'_>) -> Result<()> {

Diff for: scarb/src/compiler/plugin/proc_macro/ffi.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::core::{Config, Package, PackageId};
1+
use crate::core::{Package, PackageId};
22
use anyhow::{ensure, Context, Result};
33
use cairo_lang_defs::patcher::PatchBuilder;
44
use cairo_lang_macro::{
@@ -26,6 +26,7 @@ use libloading::os::unix::Symbol as RawSymbol;
2626
#[cfg(windows)]
2727
use libloading::os::windows::Symbol as RawSymbol;
2828
use smol_str::SmolStr;
29+
use tracing::trace;
2930

3031
pub trait FromSyntaxNode {
3132
fn from_syntax_node(db: &dyn SyntaxGroup, node: &impl TypedSyntaxNode) -> Self;
@@ -61,11 +62,22 @@ impl Debug for ProcMacroInstance {
6162

6263
impl ProcMacroInstance {
6364
/// Load shared library
64-
pub fn try_new(package: Package, config: &Config) -> Result<Self> {
65-
let lib_path = package
66-
.shared_lib_path(config)
67-
.context("could not resolve shared library path")?;
68-
let plugin = unsafe { Plugin::try_new(lib_path.to_path_buf())? };
65+
pub fn try_new(package_id: PackageId, lib_path: Utf8PathBuf) -> Result<Self> {
66+
trace!("loading compiled macro for `{}` package", package_id);
67+
let plugin = unsafe { Plugin::try_new(lib_path)? };
68+
Ok(Self {
69+
expansions: unsafe { Self::load_expansions(&plugin, package_id)? },
70+
package_id,
71+
plugin,
72+
})
73+
}
74+
75+
pub fn try_load_prebuilt(package: Package) -> Result<Self> {
76+
trace!("loading prebuilt macro for `{}` package", package.id);
77+
let prebuilt_path = package
78+
.prebuilt_lib_path()
79+
.context("could not resolve prebuilt library path")?;
80+
let plugin = unsafe { Plugin::try_new(prebuilt_path)? };
6981
Ok(Self {
7082
expansions: unsafe { Self::load_expansions(&plugin, package.id)? },
7183
package_id: package.id,

Diff for: scarb/src/compiler/plugin/proc_macro/host.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
use crate::compiler::plugin::proc_macro::compilation::SharedLibraryProvider;
12
use crate::compiler::plugin::proc_macro::{
23
Expansion, ExpansionKind, FromSyntaxNode, ProcMacroInstance,
34
};
45
use crate::core::{Config, Package, PackageId};
5-
use anyhow::{ensure, Result};
6+
use anyhow::{ensure, Context, Result};
67
use cairo_lang_defs::ids::{ModuleItemId, TopLevelLanguageElementId};
78
use cairo_lang_defs::patcher::{PatchBuilder, RewriteNode};
89
use cairo_lang_defs::plugin::{
@@ -1150,9 +1151,16 @@ pub struct ProcMacroHost {
11501151
}
11511152

11521153
impl ProcMacroHost {
1153-
pub fn register(&mut self, package: Package, config: &Config) -> Result<()> {
1154-
let instance = ProcMacroInstance::try_new(package, config)?;
1155-
self.macros.push(Arc::new(instance));
1154+
pub fn register_instance(&mut self, instance: Arc<ProcMacroInstance>) {
1155+
self.macros.push(instance);
1156+
}
1157+
1158+
pub fn register_new(&mut self, package: Package, config: &Config) -> Result<()> {
1159+
let lib_path = package
1160+
.shared_lib_path(config)
1161+
.context("could not resolve shared library path")?;
1162+
let instance = ProcMacroInstance::try_new(package.id, lib_path)?;
1163+
self.register_instance(Arc::new(instance));
11561164
Ok(())
11571165
}
11581166

0 commit comments

Comments
 (0)