diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 144278208..dd1781180 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,3 +107,29 @@ jobs: cache-dependency-path: website/package-lock.json - run: npm ci - run: npm run fmt:check + + test-prebuilt-plugins: + name: test prebuilt plugins ${{ matrix.platform.name }} + runs-on: ${{ matrix.platform.os }} + strategy: + fail-fast: false + matrix: + platform: + - name: linux x86-64 + os: ubuntu-latest + - name: windows x86-64 + os: windows-latest + - name: macos arm64 + os: macos-latest + - name: macos x86-64 + os: macos-13 + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Run prebuilt plugin tests + run: | + cargo test -p scarb --test build_cairo_plugin compile_with_prebuilt_plugins -- --exact + cargo test -p scarb --test build_cairo_plugin compile_with_invalid_prebuilt_plugins -- --exact + cargo test -p scarb --test proc_macro_server load_prebuilt_proc_macros -- --exact diff --git a/Cargo.lock b/Cargo.lock index 144805935..ebcc34365 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3736,6 +3736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", + "libc", "pkg-config", "vcpkg", ] @@ -4994,6 +4995,7 @@ dependencies = [ "smol_str", "snapbox", "tar", + "target-triple", "test-case", "test-for-each-example", "thiserror 2.0.3", @@ -5791,6 +5793,12 @@ dependencies = [ "xattr", ] +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempfile" version = "3.12.0" diff --git a/Cargo.toml b/Cargo.toml index 9f3eb970e..271daa1ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,7 @@ xshell = "0.2" xxhash-rust = { version = "0.8", features = ["xxh3"] } zip = { version = "0.6", default-features = false, features = ["deflate"] } zstd = "0.13" +target-triple = "0.1" [profile.release] lto = true diff --git a/scarb/Cargo.toml b/scarb/Cargo.toml index d1d093ab7..02e149bc7 100644 --- a/scarb/Cargo.toml +++ b/scarb/Cargo.toml @@ -89,6 +89,7 @@ windows-sys.workspace = true zstd.workspace = true cargo_metadata.workspace = true flate2.workspace = true +target-triple.workspace = true [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { workspace = true, default-features = true } diff --git a/scarb/src/bin/scarb/commands/proc_macro_server.rs b/scarb/src/bin/scarb/commands/proc_macro_server.rs index 212a9faa1..b2165899b 100644 --- a/scarb/src/bin/scarb/commands/proc_macro_server.rs +++ b/scarb/src/bin/scarb/commands/proc_macro_server.rs @@ -16,6 +16,7 @@ pub fn run(config: &mut Config) -> Result<()> { no_default_features: false, }, true, + true, &ws, )?; @@ -43,12 +44,12 @@ fn load_plugins( ws: &Workspace<'_>, proc_macros: &mut ProcMacroHost, ) -> Result<()> { - for plugin_info in unit - .cairo_plugins - .into_iter() - .filter(|plugin_info| !plugin_info.builtin) - { - proc_macros.register(plugin_info.package, ws.config())?; + for plugin_info in unit.cairo_plugins.into_iter().filter(|p| !p.builtin) { + if let Some(prebuilt) = plugin_info.prebuilt { + proc_macros.register_instance(prebuilt); + } else { + proc_macros.register_new(plugin_info.package, ws.config())?; + } } Ok(()) diff --git a/scarb/src/compiler/compilation_unit.rs b/scarb/src/compiler/compilation_unit.rs index 14af04350..789aff27c 100644 --- a/scarb/src/compiler/compilation_unit.rs +++ b/scarb/src/compiler/compilation_unit.rs @@ -1,14 +1,16 @@ -use std::fmt::Write; -use std::hash::{Hash, Hasher}; - use anyhow::{ensure, Result}; use cairo_lang_filesystem::cfg::CfgSet; use cairo_lang_filesystem::db::CrateIdentifier; use itertools::Itertools; use serde::{Deserialize, Serialize}; use smol_str::SmolStr; +use std::fmt::Write; +use std::hash::{Hash, Hasher}; +use std::sync::Arc; use typed_builder::TypedBuilder; +use crate::compiler::plugin::proc_macro::compilation::SharedLibraryProvider; +use crate::compiler::plugin::proc_macro::ProcMacroInstance; use crate::compiler::Profile; use crate::core::{ ManifestCompilerConfig, Package, PackageId, PackageName, Target, TargetKind, Workspace, @@ -72,6 +74,9 @@ pub struct ProcMacroCompilationUnit { /// Rust compiler configuration parameters to use in this unit. pub compiler_config: serde_json::Value, + + /// Instance of the proc macro loaded from prebuilt library, if available. + pub prebuilt: Option>, } /// Information about a single package that is part of a [`CompilationUnit`]. @@ -97,6 +102,9 @@ pub struct CompilationUnitCairoPlugin { /// The Scarb plugin [`Package`] to load. pub package: Package, pub builtin: bool, + + /// Instance of the proc macro loaded from prebuilt library, if available. + pub prebuilt: Option>, } /// Unique identifier of the compilation unit component. @@ -130,6 +138,8 @@ pub trait CompilationUnitAttributes { fn components(&self) -> &[CompilationUnitComponent]; fn digest(&self) -> String; + fn is_prebuilt(&self) -> bool; + fn main_component(&self) -> &CompilationUnitComponent { // NOTE: This uses the order invariant of `component` field. let component = &self.components()[0]; @@ -195,6 +205,12 @@ impl CompilationUnitAttributes for CompilationUnit { Self::ProcMacro(unit) => unit.digest(), } } + fn is_prebuilt(&self) -> bool { + match self { + Self::Cairo(unit) => unit.is_prebuilt(), + Self::ProcMacro(unit) => unit.is_prebuilt(), + } + } } impl CompilationUnitAttributes for CairoCompilationUnit { @@ -215,6 +231,10 @@ impl CompilationUnitAttributes for CairoCompilationUnit { self.compiler_config.hash(&mut hasher); hasher.finish_as_short_hash() } + + fn is_prebuilt(&self) -> bool { + false + } } impl CompilationUnitAttributes for ProcMacroCompilationUnit { @@ -233,6 +253,13 @@ impl CompilationUnitAttributes for ProcMacroCompilationUnit { } hasher.finish_as_short_hash() } + + fn is_prebuilt(&self) -> bool { + self.components + .first() + .map(|c| c.package.is_prebuilt()) + .unwrap_or(false) + } } impl CairoCompilationUnit { diff --git a/scarb/src/compiler/db.rs b/scarb/src/compiler/db.rs index 429dd5813..66fad856d 100644 --- a/scarb/src/compiler/db.rs +++ b/scarb/src/compiler/db.rs @@ -1,3 +1,7 @@ +use crate::compiler::plugin::proc_macro::{ProcMacroHost, ProcMacroHostPlugin}; +use crate::compiler::{CairoCompilationUnit, CompilationUnitAttributes, CompilationUnitComponent}; +use crate::core::Workspace; +use crate::DEFAULT_MODULE_MAIN_FILE; use anyhow::{anyhow, Result}; use cairo_lang_compiler::db::{RootDatabase, RootDatabaseBuilder}; use cairo_lang_compiler::project::{AllCratesConfig, ProjectConfig, ProjectConfigContent}; @@ -14,11 +18,6 @@ use std::path::PathBuf; use std::sync::Arc; use tracing::trace; -use crate::compiler::plugin::proc_macro::{ProcMacroHost, ProcMacroHostPlugin}; -use crate::compiler::{CairoCompilationUnit, CompilationUnitAttributes, CompilationUnitComponent}; -use crate::core::Workspace; -use crate::DEFAULT_MODULE_MAIN_FILE; - pub struct ScarbDatabase { pub db: RootDatabase, pub proc_macro_host: Arc, @@ -59,8 +58,10 @@ fn load_plugins( let plugin = ws.config().cairo_plugins().fetch(package_id)?; let instance = plugin.instantiate()?; builder.with_plugin_suite(instance.plugin_suite()); + } else if let Some(prebuilt) = &plugin_info.prebuilt { + proc_macros.register_instance(prebuilt.clone()); } else { - proc_macros.register(plugin_info.package.clone(), ws.config())?; + proc_macros.register_new(plugin_info.package.clone(), ws.config())?; } } let macro_host = Arc::new(proc_macros.into_plugin()?); diff --git a/scarb/src/compiler/plugin/mod.rs b/scarb/src/compiler/plugin/mod.rs index 30838566f..91edc9362 100644 --- a/scarb/src/compiler/plugin/mod.rs +++ b/scarb/src/compiler/plugin/mod.rs @@ -9,6 +9,7 @@ use itertools::Itertools; use serde::{Deserialize, Serialize}; use crate::compiler::plugin::builtin::BuiltinCairoRunPlugin; +use crate::compiler::plugin::proc_macro::compilation::SharedLibraryProvider; use crate::core::{Package, PackageId, TargetKind, Workspace}; use self::builtin::{BuiltinStarkNetPlugin, BuiltinTestPlugin}; @@ -29,8 +30,8 @@ pub fn fetch_cairo_plugin(package: &Package, ws: &Workspace<'_>) -> Result<()> { assert!(package.is_cairo_plugin()); let target = package.fetch_target(&TargetKind::CAIRO_PLUGIN)?; let props: CairoPluginProps = target.props()?; - // No need to fetch for buildin plugins. - if !props.builtin { + // No need to fetch for builtin or prebuilt plugins. + if !props.builtin && !package.is_prebuilt() { proc_macro::fetch_crate(package, ws)?; } Ok(()) diff --git a/scarb/src/compiler/plugin/proc_macro/compilation.rs b/scarb/src/compiler/plugin/proc_macro/compilation.rs index a51a48b7e..305ac18fe 100644 --- a/scarb/src/compiler/plugin/proc_macro/compilation.rs +++ b/scarb/src/compiler/plugin/proc_macro/compilation.rs @@ -15,12 +15,14 @@ use ra_ap_toolchain::Tool; use scarb_ui::{Message, OutputFormat}; use serde::{Serialize, Serializer}; use serde_json::value::RawValue; +use std::env::consts::DLL_SUFFIX; use std::fmt::Display; use std::fs; use std::io::{Seek, SeekFrom}; use std::ops::Deref; use std::process::Command; use tar::Archive; +use target_triple::target; use tracing::trace_span; pub const PROC_MACRO_BUILD_PROFILE: &str = "release"; @@ -31,6 +33,10 @@ pub trait SharedLibraryProvider { fn target_path(&self, config: &Config) -> Filesystem; /// Location of the shared library for the package. fn shared_lib_path(&self, config: &Config) -> Result; + /// Location of the prebuilt binary for the package. + fn prebuilt_lib_path(&self) -> Result; + /// Returns true if the package contains a prebuilt binary. + fn is_prebuilt(&self) -> bool; } impl SharedLibraryProvider for Package { @@ -61,6 +67,34 @@ impl SharedLibraryProvider for Package { .path_unchecked() .join(lib_name)) } + + fn prebuilt_lib_path(&self) -> Result { + let target_triple = target!(); + + let prebuilt_name = format!( + "{name}_v{version}_{target}{suffix}", + name = self.id.name, + version = self.id.version, + target = target_triple, + suffix = DLL_SUFFIX + ); + + let prebuilt_path = self + .root() + .join("target") + .join("scarb") + .join("cairo-plugin") + .join(prebuilt_name); + + prebuilt_path + .exists() + .then_some(prebuilt_path) + .ok_or_else(|| anyhow!("prebuilt library not found")) + } + + fn is_prebuilt(&self) -> bool { + self.prebuilt_lib_path().is_ok() + } } pub fn compile_unit(unit: ProcMacroCompilationUnit, ws: &Workspace<'_>) -> Result<()> { diff --git a/scarb/src/compiler/plugin/proc_macro/ffi.rs b/scarb/src/compiler/plugin/proc_macro/ffi.rs index 4d42bab6c..9c1a376ef 100644 --- a/scarb/src/compiler/plugin/proc_macro/ffi.rs +++ b/scarb/src/compiler/plugin/proc_macro/ffi.rs @@ -1,4 +1,4 @@ -use crate::core::{Config, Package, PackageId}; +use crate::core::{Package, PackageId}; use anyhow::{ensure, Context, Result}; use cairo_lang_defs::patcher::PatchBuilder; use cairo_lang_macro::{ @@ -61,11 +61,20 @@ impl Debug for ProcMacroInstance { impl ProcMacroInstance { /// Load shared library - pub fn try_new(package: Package, config: &Config) -> Result { - let lib_path = package - .shared_lib_path(config) - .context("could not resolve shared library path")?; - let plugin = unsafe { Plugin::try_new(lib_path.to_path_buf())? }; + pub fn try_new(package_id: PackageId, lib_path: Utf8PathBuf) -> Result { + let plugin = unsafe { Plugin::try_new(lib_path)? }; + Ok(Self { + expansions: unsafe { Self::load_expansions(&plugin, package_id)? }, + package_id, + plugin, + }) + } + + pub fn try_load_prebuilt(package: Package) -> Result { + let prebuilt_path = package + .prebuilt_lib_path() + .context("could not resolve prebuilt library path")?; + let plugin = unsafe { Plugin::try_new(prebuilt_path)? }; Ok(Self { expansions: unsafe { Self::load_expansions(&plugin, package.id)? }, package_id: package.id, diff --git a/scarb/src/compiler/plugin/proc_macro/host.rs b/scarb/src/compiler/plugin/proc_macro/host.rs index 0886abb81..7e8765ef9 100644 --- a/scarb/src/compiler/plugin/proc_macro/host.rs +++ b/scarb/src/compiler/plugin/proc_macro/host.rs @@ -1,8 +1,9 @@ +use crate::compiler::plugin::proc_macro::compilation::SharedLibraryProvider; use crate::compiler::plugin::proc_macro::{ Expansion, ExpansionKind, FromSyntaxNode, ProcMacroInstance, }; use crate::core::{Config, Package, PackageId}; -use anyhow::{ensure, Result}; +use anyhow::{ensure, Context, Result}; use cairo_lang_defs::ids::{ModuleItemId, TopLevelLanguageElementId}; use cairo_lang_defs::patcher::{PatchBuilder, RewriteNode}; use cairo_lang_defs::plugin::{ @@ -1150,9 +1151,16 @@ pub struct ProcMacroHost { } impl ProcMacroHost { - pub fn register(&mut self, package: Package, config: &Config) -> Result<()> { - let instance = ProcMacroInstance::try_new(package, config)?; - self.macros.push(Arc::new(instance)); + pub fn register_instance(&mut self, instance: Arc) { + self.macros.push(instance); + } + + pub fn register_new(&mut self, package: Package, config: &Config) -> Result<()> { + let lib_path = package + .shared_lib_path(config) + .context("could not resolve shared library path")?; + let instance = ProcMacroInstance::try_new(package.id, lib_path)?; + self.register_instance(Arc::new(instance)); Ok(()) } diff --git a/scarb/src/ops/compile.rs b/scarb/src/ops/compile.rs index 3e63617e8..64cbd9c75 100644 --- a/scarb/src/ops/compile.rs +++ b/scarb/src/ops/compile.rs @@ -125,36 +125,41 @@ where validate_features(&packages_to_process, &opts.features)?; // Add test compilation units to build let packages = get_test_package_ids(packages, ws); - let compilation_units = - ops::generate_compilation_units(&resolve, &opts.features, opts.ignore_cairo_version, ws)? - .into_iter() - .filter(|cu| { - let is_excluded = opts - .exclude_target_kinds - .contains(&cu.main_component().target_kind()); - let is_included = opts.include_target_kinds.is_empty() - || opts - .include_target_kinds - .contains(&cu.main_component().target_kind()); - let is_included = is_included - && (opts.include_target_names.is_empty() - || cu - .main_component() - .targets - .iter() - .any(|t| opts.include_target_names.contains(&t.name))); - let is_selected = packages.contains(&cu.main_package_id()); - let is_cairo_plugin = matches!(cu, CompilationUnit::ProcMacro(_)); - is_cairo_plugin || (is_selected && is_included && !is_excluded) - }) - .sorted_by_key(|cu| { - if matches!(cu, CompilationUnit::ProcMacro(_)) { - 0 - } else { - 1 - } - }) - .collect::>(); + let compilation_units = ops::generate_compilation_units( + &resolve, + &opts.features, + opts.ignore_cairo_version, + true, + ws, + )? + .into_iter() + .filter(|cu| { + let is_excluded = opts + .exclude_target_kinds + .contains(&cu.main_component().target_kind()); + let is_included = opts.include_target_kinds.is_empty() + || opts + .include_target_kinds + .contains(&cu.main_component().target_kind()); + let is_included = is_included + && (opts.include_target_names.is_empty() + || cu + .main_component() + .targets + .iter() + .any(|t| opts.include_target_names.contains(&t.name))); + let is_selected = packages.contains(&cu.main_package_id()); + let is_cairo_plugin = matches!(cu, CompilationUnit::ProcMacro(_)); + is_cairo_plugin || (is_selected && is_included && !is_excluded) + }) + .sorted_by_key(|cu| { + if matches!(cu, CompilationUnit::ProcMacro(_)) { + 0 + } else { + 1 + } + }) + .collect::>(); operation(compilation_units, ws)?; @@ -194,13 +199,21 @@ pub fn compile_unit(unit: CompilationUnit, ws: &Workspace<'_>) -> Result<()> { fn compile_unit_inner(unit: CompilationUnit, ws: &Workspace<'_>) -> Result<()> { let package_name = unit.main_package_id().name.clone(); - ws.config() - .ui() - .print(Status::new("Compiling", &unit.name())); - let result = match unit { - CompilationUnit::ProcMacro(unit) => proc_macro::compile_unit(unit, ws), + CompilationUnit::ProcMacro(unit) => { + if unit.prebuilt.is_some() { + Ok(()) + } else { + ws.config() + .ui() + .print(Status::new("Compiling", &unit.name())); + proc_macro::compile_unit(unit, ws) + } + } CompilationUnit::Cairo(unit) => { + ws.config() + .ui() + .print(Status::new("Compiling", &unit.name())); let ScarbDatabase { mut db, proc_macro_host, diff --git a/scarb/src/ops/expand.rs b/scarb/src/ops/expand.rs index 417fa4b1b..0edb61da3 100644 --- a/scarb/src/ops/expand.rs +++ b/scarb/src/ops/expand.rs @@ -43,8 +43,13 @@ pub fn expand(package: Package, opts: ExpandOpts, ws: &Workspace<'_>) -> Result< let package_name = package.id.name.to_string(); let resolve = ops::resolve_workspace(ws)?; - let compilation_units = - ops::generate_compilation_units(&resolve, &opts.features, opts.ignore_cairo_version, ws)?; + let compilation_units = ops::generate_compilation_units( + &resolve, + &opts.features, + opts.ignore_cairo_version, + true, + ws, + )?; // Compile procedural macros. compilation_units diff --git a/scarb/src/ops/metadata.rs b/scarb/src/ops/metadata.rs index 52abdc57e..ad69030ed 100644 --- a/scarb/src/ops/metadata.rs +++ b/scarb/src/ops/metadata.rs @@ -48,6 +48,7 @@ pub fn collect_metadata(opts: &MetadataOptions, ws: &Workspace<'_>) -> Result, ) -> Result> { let mut units = Vec::with_capacity(ws.members().size_hint().0); @@ -204,7 +208,10 @@ pub fn generate_compilation_units( .collect_vec(); for plugin in cairo_plugins { - units.extend(generate_cairo_plugin_compilation_units(&plugin)?); + units.extend(generate_cairo_plugin_compilation_units( + &plugin, + load_prebuilts, + )?); } assert!( @@ -619,9 +626,13 @@ impl<'a> PackageSolutionCollector<'a> { // We can safely unwrap as all packages with `PackageClass::CairoPlugin` must define plugin target. let target = package.target(&TargetKind::CAIRO_PLUGIN).unwrap(); let props: CairoPluginProps = target.props()?; + let prebuilt = ProcMacroInstance::try_load_prebuilt(package.clone()) + .ok() + .map(Arc::new); Ok(CompilationUnitCairoPlugin::builder() .package(package) .builtin(props.builtin) + .prebuilt(prebuilt) .build()) }) .collect::>>()?; @@ -684,7 +695,17 @@ fn check_cairo_version_compatibility( Ok(()) } -fn generate_cairo_plugin_compilation_units(member: &Package) -> Result> { +pub fn generate_cairo_plugin_compilation_units( + member: &Package, + load_prebuilts: bool, +) -> Result> { + let prebuilt = if load_prebuilts && member.is_prebuilt() { + ProcMacroInstance::try_load_prebuilt(member.clone()) + .ok() + .map(Arc::new) + } else { + None + }; Ok(vec![CompilationUnit::ProcMacro(ProcMacroCompilationUnit { main_package_id: member.id, compiler_config: serde_json::Value::Null, @@ -697,6 +718,7 @@ fn generate_cairo_plugin_compilation_units(member: &Package) -> Result u32 { + let x = some!(42); + x + } + "#}) + .dep( + "proc_macro_example", + Dep.version("0.1.2").registry("https://scarbs.dev/"), + ) + .manifest_extra(indoc! {r#" + "#}) + .build(&t); + Scarb::quick_snapbox() + .arg("build") + // Disable Cargo and Rust compiler. + .env("CARGO", "/bin/false") + .env("RUSTC", "/bin/false") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + [..]Downloading proc_macro_example v0.1.2 ([..]) + [..]Compiling hello v1.0.0 ([..]Scarb.toml) + [..] Finished `dev` profile target(s) in [..] + "#}); +} + +#[test] +fn compile_with_invalid_prebuilt_plugins() { + let t = TempDir::new().unwrap(); + + ProjectBuilder::start() + .name("hello") + .lib_cairo(indoc! {r#" + fn main() -> u32 { + let x = some!(42); + x + } + "#}) + .dep( + "invalid_prebuilt_example", + Dep.version("0.1.0").registry("https://scarbs.dev/"), + ) + .manifest_extra(indoc! {r#" + "#}) + .build(&t); + Scarb::quick_snapbox() + .arg("build") + // Disable output from Cargo. + .env("CARGO_TERM_QUIET", "true") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + [..]Downloading invalid_prebuilt_example v0.1.0 ([..]) + [..]Compiling invalid_prebuilt_example v0.1.0 ([..]) + [..]Compiling hello v1.0.0 ([..]Scarb.toml) + [..] Finished `dev` profile target(s) in [..] + "#}); +} + #[test] fn can_emit_plugin_warning() { let temp = TempDir::new().unwrap(); diff --git a/scarb/tests/proc_macro_server.rs b/scarb/tests/proc_macro_server.rs index b01f865dc..b17790be8 100644 --- a/scarb/tests/proc_macro_server.rs +++ b/scarb/tests/proc_macro_server.rs @@ -12,7 +12,7 @@ use scarb_proc_macro_server_types::methods::expand::ExpandInlineMacroParams; use scarb_test_support::cairo_plugin_project_builder::CairoPluginProjectBuilder; use scarb_test_support::proc_macro_server::ProcMacroClient; use scarb_test_support::proc_macro_server::SIMPLE_MACROS; -use scarb_test_support::project_builder::ProjectBuilder; +use scarb_test_support::project_builder::{Dep, DepBuilder, ProjectBuilder}; #[test] fn defined_macros() { @@ -171,3 +171,32 @@ fn expand_inline() { TokenStream::new("struct A { field: 25 , other_field: macro_call!(12)}".to_string()) ); } + +#[test] +fn load_prebuilt_proc_macros() { + let t = TempDir::new().unwrap(); + + let project = t.child("test_package"); + + ProjectBuilder::start() + .name("test_package") + .version("1.0.0") + .lib_cairo("") + .dep( + "proc_macro_example", + Dep.version("0.1.2").registry("https://scarbs.dev/"), + ) + .build(&project); + + let mut proc_macro_server = ProcMacroClient::new_without_cargo(&project); + + let response = proc_macro_server + .request_and_wait::(ExpandInlineMacroParams { + name: "some".to_string(), + args: TokenStream::new("42".to_string()), + }) + .unwrap(); + + assert_eq!(response.diagnostics, vec![]); + assert_eq!(response.token_stream, TokenStream::new("42".to_string())); +} diff --git a/utils/scarb-test-support/src/proc_macro_server.rs b/utils/scarb-test-support/src/proc_macro_server.rs index 12e93176f..cf1fefab0 100644 --- a/utils/scarb-test-support/src/proc_macro_server.rs +++ b/utils/scarb-test-support/src/proc_macro_server.rs @@ -90,6 +90,31 @@ impl ProcMacroClient { responses: Default::default(), } } + pub fn new_without_cargo>(path: P) -> Self { + let mut server_process = Scarb::new() + .std() + .arg("--quiet") + .arg("proc-macro-server") + .env("CARGO", "/bin/false") + .env("RUSTC", "/bin/false") + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .stderr(Stdio::inherit()) + .current_dir(path) + .spawn() + .unwrap(); + + let requester = server_process.stdin.take().unwrap(); + let responder = BufReader::new(server_process.stdout.take().unwrap()).lines(); + + Self { + requester, + responder, + server_process, + id_counter: Default::default(), + responses: Default::default(), + } + } pub fn request(&mut self, params: M::Params) -> PendingRequest { let id = self.id_counter;