From fef2da94533b0c09dc3c3e4dbeef1370b86c85e3 Mon Sep 17 00:00:00 2001 From: Marek Kaput Date: Wed, 17 May 2023 16:54:28 +0200 Subject: [PATCH] Parse `[cairo-plugin]` in `Scarb.toml` commit-id:289e98b3 --- scarb/src/core/manifest/toml_manifest.rs | 84 +++++++++++----- scarb/src/ops/resolve.rs | 18 +++- scarb/tests/e2e/build_cairo_plugin.rs | 118 +++++++++++++++++++++++ scarb/tests/e2e/main.rs | 1 + 4 files changed, 195 insertions(+), 26 deletions(-) create mode 100644 scarb/tests/e2e/build_cairo_plugin.rs diff --git a/scarb/src/core/manifest/toml_manifest.rs b/scarb/src/core/manifest/toml_manifest.rs index 350e9700c..e5d0d32bd 100644 --- a/scarb/src/core/manifest/toml_manifest.rs +++ b/scarb/src/core/manifest/toml_manifest.rs @@ -32,6 +32,7 @@ pub struct TomlManifest { pub package: Option>, pub dependencies: Option>, pub lib: Option>, + pub cairo_plugin: Option>, pub target: Option>>>, pub cairo: Option, pub tool: Option, @@ -134,6 +135,12 @@ pub struct TomlLibTargetParams { pub casm: Option, } +#[derive(Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct TomlCairoPluginTargetParams { + pub builtin: Option, +} + pub type TomlExternalTargetParams = BTreeMap; #[derive(Debug, Default, Deserialize, Serialize, Clone)] @@ -272,20 +279,19 @@ impl TomlManifest { let mut targets = Vec::new(); - if let Some(lib_toml) = &self.lib { - let name = lib_toml - .name - .clone() - .unwrap_or_else(|| package_name.clone()); - - let target = Target::try_from_structured_params( - Target::LIB, - name, - default_source_path.clone(), - &lib_toml.params, - )?; - targets.push(target); - } + targets.extend(Self::collect_target( + Target::LIB, + self.lib.as_ref(), + &package_name, + &default_source_path, + )?); + + targets.extend(Self::collect_target( + Target::CAIRO_PLUGIN, + self.cairo_plugin.as_ref(), + &package_name, + &default_source_path, + )?); for (kind_toml, ext_toml) in self .target @@ -293,20 +299,15 @@ impl TomlManifest { .flatten() .flat_map(|(k, vs)| vs.iter().map(|v| (k.clone(), v))) { - let name = ext_toml - .name - .clone() - .unwrap_or_else(|| package_name.clone()); - - let target = Target::try_from_structured_params( + targets.extend(Self::collect_target( kind_toml, - name, - default_source_path.clone(), - &ext_toml.params, - )?; - targets.push(target); + Some(ext_toml), + &package_name, + &default_source_path, + )?); } + Self::check_cairo_plugin_target_is_exclusive(&targets)?; Self::check_unique_targets(&targets, &package_name)?; if targets.is_empty() { @@ -318,6 +319,39 @@ impl TomlManifest { Ok(targets) } + fn collect_target( + kind: impl Into, + target: Option<&TomlTarget>, + default_name: &SmolStr, + default_source_path: &Utf8Path, + ) -> Result> { + let Some(target) = target else { + return Ok(None); + }; + + let name = target.name.clone().unwrap_or_else(|| default_name.clone()); + + let target = Target::try_from_structured_params( + kind, + name, + default_source_path.to_path_buf(), + &target.params, + )?; + + Ok(Some(target)) + } + + fn check_cairo_plugin_target_is_exclusive(targets: &[Target]) -> Result<()> { + if targets.iter().any(Target::is_cairo_plugin) { + ensure!( + targets.len() == 1, + "target `{}` cannot be mixed with other targets", + Target::CAIRO_PLUGIN, + ); + } + Ok(()) + } + fn check_unique_targets(targets: &[Target], package_name: &str) -> Result<()> { let mut used = HashSet::with_capacity(targets.len()); for target in targets { diff --git a/scarb/src/ops/resolve.rs b/scarb/src/ops/resolve.rs index f1ce944bf..5751cdee1 100644 --- a/scarb/src/ops/resolve.rs +++ b/scarb/src/ops/resolve.rs @@ -109,7 +109,7 @@ 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() { - todo!("Compiling Cairo plugins is not implemented yet.") + generate_cairo_plugin_compilation_units(&member)? } else { generate_cairo_compilation_units(&member, resolve, ws)? }); @@ -240,3 +240,19 @@ fn check_cairo_version_compatibility(packages: &[Package], ws: &Workspace<'_>) - } Ok(()) } + +fn generate_cairo_plugin_compilation_units(member: &Package) -> Result> { + let target = member.fetch_target(Target::CAIRO_PLUGIN)?; + + // If this is a built-in plugin package, then compiling it boils down to doing nothing. + if target + .params + .as_table() + .map(|t| t.contains_key("builtin")) + .unwrap() + { + return Ok(vec![]); + } + + bail!("compiling Cairo plugin packages is possible yet") +} diff --git a/scarb/tests/e2e/build_cairo_plugin.rs b/scarb/tests/e2e/build_cairo_plugin.rs new file mode 100644 index 000000000..48621aa84 --- /dev/null +++ b/scarb/tests/e2e/build_cairo_plugin.rs @@ -0,0 +1,118 @@ +use assert_fs::TempDir; +use indoc::indoc; + +use crate::support::command::Scarb; +use crate::support::project_builder::ProjectBuilder; + +#[test] +fn compile_cairo_plugin() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .version("1.0.0") + .manifest_extra(r#"[cairo-plugin]"#) + .build(&t); + + Scarb::quick_snapbox() + .arg("build") + .current_dir(&t) + .assert() + .failure() + .stdout_matches(indoc! {r#" + error: compiling Cairo plugin packages is possible yet + "#}); +} + +#[test] +fn compile_known_builtin() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .version("1.0.0") + .manifest_extra(indoc! {r#" + [cairo-plugin] + builtin = "starknet" + "#}) + .build(&t); + + Scarb::quick_snapbox() + .arg("build") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + [..] Finished release target(s) in [..] + "#}); +} + +#[test] +fn compile_unknown_builtin() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .version("1.0.0") + .manifest_extra(indoc! {r#" + [cairo-plugin] + builtin = "i_definitely_do_not_exist" + "#}) + .build(&t); + + Scarb::quick_snapbox() + .arg("build") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + [..] Finished release target(s) in [..] + "#}); +} + +#[test] +fn compile_cairo_plugin_with_lib_target() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .version("1.0.0") + .manifest_extra(indoc! {r#" + [lib] + [cairo-plugin] + "#}) + .build(&t); + + Scarb::quick_snapbox() + .arg("build") + .current_dir(&t) + .assert() + .failure() + .stdout_matches(indoc! {r#" + error: failed to parse manifest at `[..]/Scarb.toml` + + Caused by: + target `cairo-plugin` cannot be mixed with other targets + "#}); +} + +#[test] +fn compile_cairo_plugin_with_other_target() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .version("1.0.0") + .manifest_extra(indoc! {r#" + [cairo-plugin] + [[target.starknet-contract]] + "#}) + .build(&t); + + Scarb::quick_snapbox() + .arg("build") + .current_dir(&t) + .assert() + .failure() + .stdout_matches(indoc! {r#" + error: failed to parse manifest at `[..]/Scarb.toml` + + Caused by: + target `cairo-plugin` cannot be mixed with other targets + "#}); +} diff --git a/scarb/tests/e2e/main.rs b/scarb/tests/e2e/main.rs index f58d660f3..df9b7a7d4 100644 --- a/scarb/tests/e2e/main.rs +++ b/scarb/tests/e2e/main.rs @@ -1,5 +1,6 @@ mod add; mod build; +mod build_cairo_plugin; mod build_starknet_contract; mod build_targets; mod clean;