Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Procedural macro compilation #1110

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scarb/src/compiler/compilation_unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ impl CompilationUnit {
ws.target_dir().child(self.profile.as_str())
}

pub fn is_cairo_plugin(&self) -> bool {
mkaput marked this conversation as resolved.
Show resolved Hide resolved
self.target().is_cairo_plugin()
}

pub fn is_sole_for_package(&self) -> bool {
self.main_component()
.package
Expand Down
48 changes: 47 additions & 1 deletion scarb/src/compiler/plugin/proc_macro/compilation.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::compiler::plugin::proc_macro::PROC_MACRO_BUILD_PROFILE;
use crate::core::{Config, Package};
use crate::compiler::CompilationUnit;
use crate::core::{Config, Package, Workspace};
use crate::flock::Filesystem;
use crate::process::exec_piping;
use anyhow::Result;
use camino::Utf8PathBuf;
use libloading::library_filename;
use std::process::Command;
use tracing::trace_span;

/// This trait is used to define the shared library path for a package.
pub trait SharedLibraryProvider {
Expand Down Expand Up @@ -38,3 +43,44 @@ impl SharedLibraryProvider for Package {
.join(lib_name)
}
}

pub fn compile_unit(unit: CompilationUnit, ws: &Workspace<'_>) -> Result<()> {
let main_package = unit.components.first().unwrap().package.clone();
let cmd = CargoCommand {
current_dir: main_package.root().to_path_buf(),
target_dir: main_package
.target_path(ws.config())
.path_unchecked()
.to_path_buf(),
};
{
let _ = trace_span!("compile_proc_macro").enter();
exec(&mut cmd.into(), ws.config())?;
}
Ok(())
}

struct CargoCommand {
current_dir: Utf8PathBuf,
target_dir: Utf8PathBuf,
}

impl From<CargoCommand> for Command {
fn from(args: CargoCommand) -> Self {
let mut cmd = Command::new("cargo");
cmd.current_dir(args.current_dir);
cmd.args(["build", "--release"]);
cmd.arg("--target-dir");
cmd.arg(args.target_dir);
cmd
}
}

fn exec(cmd: &mut Command, config: &Config) -> Result<()> {
exec_piping(
cmd,
config,
|line: &str| config.ui().print(line),
|line: &str| config.ui().print(line),
)
}
1 change: 1 addition & 0 deletions scarb/src/compiler/plugin/proc_macro/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ pub mod compilation;
mod ffi;
mod host;

pub use compilation::compile_unit;
pub use ffi::*;
pub use host::*;
45 changes: 28 additions & 17 deletions scarb/src/ops/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use anyhow::{anyhow, Result};
use cairo_lang_compiler::db::RootDatabase;
use cairo_lang_compiler::diagnostics::DiagnosticsError;
use indoc::formatdoc;
use itertools::Itertools;

use scarb_ui::components::Status;
use scarb_ui::HumanDuration;

use crate::compiler::db::{build_scarb_root_database, has_starknet_plugin};
use crate::compiler::helpers::build_compiler_config;
use crate::compiler::plugin::proc_macro;
use crate::compiler::CompilationUnit;
use crate::core::{PackageId, PackageName, TargetKind, Utf8PathWorkspaceExt, Workspace};
use crate::ops;
Expand Down Expand Up @@ -59,11 +61,21 @@ where

let compilation_units = ops::generate_compilation_units(&resolve, ws)?
.into_iter()
.filter(|cu| !opts.exclude_targets.contains(&cu.target().kind))
.filter(|cu| {
opts.include_targets.is_empty() || opts.include_targets.contains(&cu.target().kind)
let is_excluded = opts.exclude_targets.contains(&cu.target().kind);
let is_included =
opts.include_targets.is_empty() || opts.include_targets.contains(&cu.target().kind);
let is_selected = packages.contains(&cu.main_package_id);
let is_cairo_plugin = cu.components.first().unwrap().target.is_cairo_plugin();
is_cairo_plugin || (is_selected && is_included && !is_excluded)
})
.sorted_by_key(|cu| {
if cu.components.first().unwrap().target.is_cairo_plugin() {
0
} else {
1
}
})
.filter(|cu| packages.contains(&cu.main_package_id))
.collect::<Vec<_>>();

for unit in compilation_units {
Expand All @@ -89,22 +101,21 @@ fn compile_unit(unit: CompilationUnit, ws: &Workspace<'_>) -> Result<()> {
.ui()
.print(Status::new("Compiling", &unit.name()));

let mut db = build_scarb_root_database(&unit, ws)?;

check_starknet_dependency(&unit, ws, &db, &package_name);

ws.config()
.compilers()
.compile(unit, &mut db, ws)
.map_err(|err| {
if !suppress_error(&err) {
ws.config().ui().anyhow(&err);
}
let result = if unit.is_cairo_plugin() {
proc_macro::compile_unit(unit, ws)
} else {
let mut db = build_scarb_root_database(&unit, ws)?;
check_starknet_dependency(&unit, ws, &db, &package_name);
ws.config().compilers().compile(unit, &mut db, ws)
};

anyhow!("could not compile `{package_name}` due to previous error")
})?;
result.map_err(|err| {
if !suppress_error(&err) {
ws.config().ui().anyhow(&err);
}

Ok(())
anyhow!("could not compile `{package_name}` due to previous error")
})
}

fn check_unit(unit: CompilationUnit, ws: &Workspace<'_>) -> Result<()> {
Expand Down
36 changes: 33 additions & 3 deletions scarb/src/ops/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,24 @@ pub fn generate_compilation_units(
let mut units = Vec::with_capacity(ws.members().size_hint().0);
for member in ws.members() {
units.extend(if member.is_cairo_plugin() {
generate_cairo_plugin_compilation_units()?
generate_cairo_plugin_compilation_units(&member, ws)?
} else {
generate_cairo_compilation_units(&member, resolve, ws)?
});
}

let cairo_plugins = units
.iter()
.flat_map(|unit| unit.cairo_plugins.clone())
.filter(|plugin| !plugin.builtin)
.map(|plugin| plugin.package.clone())
.unique_by(|plugin| plugin.id)
.collect_vec();

for plugin in cairo_plugins {
units.extend(generate_cairo_plugin_compilation_units(&plugin, ws)?);
}

assert!(
units.iter().map(CompilationUnit::id).all_unique(),
"All generated compilation units must have unique IDs."
Expand Down Expand Up @@ -425,6 +437,24 @@ fn check_cairo_version_compatibility(packages: &[Package], ws: &Workspace<'_>) -
Ok(())
}

fn generate_cairo_plugin_compilation_units() -> Result<Vec<CompilationUnit>> {
bail!("compiling Cairo plugin packages is not possible yet")
fn generate_cairo_plugin_compilation_units(
member: &Package,
ws: &Workspace<'_>,
) -> Result<Vec<CompilationUnit>> {
Ok(vec![CompilationUnit {
main_package_id: member.id,
components: vec![CompilationUnitComponent {
package: member.clone(),
cfg_set: None,
target: member
.fetch_target(&TargetKind::CAIRO_PLUGIN)
.cloned()
// Safe to unwrap, as member.is_cairo_plugin() has been ensured before.
.expect("main component of procedural macro must define `cairo-plugin` target"),
}],
cairo_plugins: Vec::new(),
profile: ws.current_profile()?,
compiler_config: member.manifest.compiler_config.clone(),
cfg_set: Default::default(),
}])
}
29 changes: 24 additions & 5 deletions scarb/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,28 @@ mod imp {
}
}

/// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
#[tracing::instrument(level = "trace", skip_all)]
pub fn exec(cmd: &mut Command, config: &Config) -> Result<()> {
exec_piping(
cmd,
config,
|line: &str| {
debug!("{line}");
},
|line: &str| {
debug!("{line}");
},
)
}

/// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
#[tracing::instrument(level = "trace", skip_all)]
pub fn exec_piping(
cmd: &mut Command,
config: &Config,
stdout_callback: impl Fn(&str) + Send,
stderr_callback: impl Fn(&str) + Send,
) -> Result<()> {
let cmd_str = shlex_join(cmd);

config.ui().verbose(Status::new("Running", &cmd_str));
Expand All @@ -112,7 +131,7 @@ pub fn exec(cmd: &mut Command, config: &Config) -> Result<()> {
let span = debug_span!("out");
move || {
let mut stdout = stdout;
pipe_to_logs(&span, &mut stdout);
pipe(&span, &mut stdout, stdout_callback);
}
});

Expand All @@ -121,7 +140,7 @@ pub fn exec(cmd: &mut Command, config: &Config) -> Result<()> {
let span = debug_span!("err");
move || {
let mut stderr = stderr;
pipe_to_logs(&span, &mut stderr);
pipe(&span, &mut stderr, stderr_callback);
}
});

Expand All @@ -135,12 +154,12 @@ pub fn exec(cmd: &mut Command, config: &Config) -> Result<()> {
}
});

fn pipe_to_logs(span: &Span, stream: &mut dyn Read) {
fn pipe(span: &Span, stream: &mut dyn Read, callback: impl Fn(&str)) {
let _enter = span.enter();
let stream = BufReader::with_capacity(128, stream);
for line in stream.lines() {
match line {
Ok(line) => debug!("{line}"),
Ok(line) => callback(line.as_str()),
Err(err) => warn!("{err:?}"),
}
}
Expand Down
1 change: 1 addition & 0 deletions scarb/tests/build_cairo_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use scarb_test_support::command::Scarb;
use scarb_test_support::project_builder::ProjectBuilder;

#[test]
#[ignore = "TODO(maciektr): Remove when proc-macros are implemented."]
fn compile_cairo_plugin() {
let t = TempDir::new().unwrap();
ProjectBuilder::start()
Expand Down
Loading