diff --git a/scarb/src/core/manifest/compiler_config.rs b/scarb/src/core/manifest/compiler_config.rs new file mode 100644 index 000000000..a9fa8e0be --- /dev/null +++ b/scarb/src/core/manifest/compiler_config.rs @@ -0,0 +1,34 @@ +use serde::{Deserialize, Serialize}; + +use crate::compiler::{DefaultForProfile, Profile}; +use crate::core::TomlCairo; + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub struct ManifestCompilerConfig { + /// Replace all names in generated Sierra code with dummy counterparts, representing the + /// expanded information about the named items. + /// + /// For libfuncs and types that would be recursively opening their generic arguments. + /// For functions, that would be their original name in Cairo. + /// For example, while the Sierra name be `[6]`, with this flag turned on it might be: + /// - For libfuncs: `felt252_const<2>` or `unbox>>`. + /// - For types: `felt252` or `Box>`. + /// - For user functions: `test::foo`. + pub sierra_replace_ids: bool, +} + +impl DefaultForProfile for ManifestCompilerConfig { + fn default_for_profile(profile: &Profile) -> Self { + Self { + sierra_replace_ids: profile.is_dev(), + } + } +} + +impl From for TomlCairo { + fn from(config: ManifestCompilerConfig) -> Self { + Self { + sierra_replace_ids: Some(config.sierra_replace_ids), + } + } +} diff --git a/scarb/src/core/manifest/dependency.rs b/scarb/src/core/manifest/dependency.rs new file mode 100644 index 000000000..05c853445 --- /dev/null +++ b/scarb/src/core/manifest/dependency.rs @@ -0,0 +1,40 @@ +use std::fmt; + +use semver::VersionReq; + +use crate::core::{PackageId, PackageName, SourceId, Summary}; + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct ManifestDependency { + pub name: PackageName, + pub version_req: VersionReq, + pub source_id: SourceId, +} + +impl ManifestDependency { + pub fn matches_summary(&self, summary: &Summary) -> bool { + self.matches_package_id(summary.package_id) + } + + pub fn matches_package_id(&self, package_id: PackageId) -> bool { + package_id.name == self.name && self.version_req.matches(&package_id.version) + } +} + +impl fmt::Display for ManifestDependency { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {}", self.name, self.version_req)?; + + if !self.source_id.is_default_registry() { + write!(f, " ({})", self.source_id)?; + } + + Ok(()) + } +} + +impl fmt::Debug for ManifestDependency { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ManifestDependency({self})") + } +} diff --git a/scarb/src/core/manifest/mod.rs b/scarb/src/core/manifest/mod.rs index a414d5082..9ef979e03 100644 --- a/scarb/src/core/manifest/mod.rs +++ b/scarb/src/core/manifest/mod.rs @@ -1,24 +1,23 @@ use std::collections::BTreeMap; -use std::fmt; -use std::fmt::Debug; -use std::ops::Deref; -use std::sync::Arc; -use once_cell::sync::Lazy; use semver::VersionReq; use serde::{Deserialize, Serialize}; use smol_str::SmolStr; use toml::Value; -use crate::compiler::{DefaultForProfile, Profile}; +pub use compiler_config::*; +pub use dependency::*; pub use scripts::*; +pub use summary::*; pub use target::*; pub use toml_manifest::*; -use crate::core::package::{PackageId, PackageName}; -use crate::core::source::SourceId; +use crate::compiler::Profile; +mod compiler_config; +mod dependency; mod scripts; +mod summary; mod target; mod toml_manifest; @@ -36,103 +35,6 @@ pub struct Manifest { pub profiles: Vec, } -/// Subset of a [`Manifest`] that contains only the most important information about a package. -/// See [`SummaryInner`] for public fields reference. -#[derive(Clone, Debug)] -pub struct Summary(Arc); - -#[derive(Debug)] -#[non_exhaustive] -pub struct SummaryInner { - pub package_id: PackageId, - pub dependencies: Vec, - pub no_core: bool, -} - -impl Deref for Summary { - type Target = SummaryInner; - - fn deref(&self) -> &Self::Target { - self.0.deref() - } -} - -impl Summary { - pub fn build(package_id: PackageId) -> SummaryBuilder { - SummaryBuilder::new(package_id) - } - - pub fn minimal(package_id: PackageId, dependencies: Vec) -> Self { - Self::build(package_id) - .with_dependencies(dependencies) - .finish() - } - - fn new(data: SummaryInner) -> Self { - Self(Arc::new(data)) - } - - pub fn full_dependencies(&self) -> impl Iterator { - self.dependencies.iter().chain(self.implicit_dependencies()) - } - - pub fn implicit_dependencies(&self) -> impl Iterator { - static CORE_DEPENDENCY: Lazy = Lazy::new(|| { - // NOTE: Pin `core` to exact version, because we know that's the only one we have. - let cairo_version = crate::version::get().cairo.version; - let version_req = VersionReq::parse(&format!("={cairo_version}")).unwrap(); - ManifestDependency { - name: PackageName::CORE, - version_req, - source_id: SourceId::for_std(), - } - }); - - let mut deps: Vec<&ManifestDependency> = Vec::new(); - - if !self.no_core { - deps.push(&CORE_DEPENDENCY); - } - - deps.into_iter() - } -} - -#[derive(Debug)] -pub struct SummaryBuilder { - package_id: PackageId, - dependencies: Vec, - no_core: bool, -} - -impl SummaryBuilder { - fn new(package_id: PackageId) -> Self { - Self { - package_id, - dependencies: Vec::new(), - no_core: false, - } - } - - pub fn with_dependencies(mut self, dependencies: Vec) -> Self { - self.dependencies = dependencies; - self - } - - pub fn no_core(mut self, no_core: bool) -> Self { - self.no_core = no_core; - self - } - - pub fn finish(self) -> Summary { - Summary::new(SummaryInner { - package_id: self.package_id, - dependencies: self.dependencies, - no_core: self.no_core, - }) - } -} - /// Subset of a [`Manifest`] that contains package metadata. #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] pub struct ManifestMetadata { @@ -150,68 +52,3 @@ pub struct ManifestMetadata { pub tool_metadata: Option>, pub cairo_version: Option, } - -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] -pub struct ManifestCompilerConfig { - /// Replace all names in generated Sierra code with dummy counterparts, representing the - /// expanded information about the named items. - /// - /// For libfuncs and types that would be recursively opening their generic arguments. - /// For functions, that would be their original name in Cairo. - /// For example, while the Sierra name be `[6]`, with this flag turned on it might be: - /// - For libfuncs: `felt252_const<2>` or `unbox>>`. - /// - For types: `felt252` or `Box>`. - /// - For user functions: `test::foo`. - pub sierra_replace_ids: bool, -} - -impl DefaultForProfile for ManifestCompilerConfig { - fn default_for_profile(profile: &Profile) -> Self { - Self { - sierra_replace_ids: profile.is_dev(), - } - } -} - -impl From for TomlCairo { - fn from(config: ManifestCompilerConfig) -> Self { - Self { - sierra_replace_ids: Some(config.sierra_replace_ids), - } - } -} - -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct ManifestDependency { - pub name: PackageName, - pub version_req: VersionReq, - pub source_id: SourceId, -} - -impl ManifestDependency { - pub fn matches_summary(&self, summary: &Summary) -> bool { - self.matches_package_id(summary.package_id) - } - - pub fn matches_package_id(&self, package_id: PackageId) -> bool { - package_id.name == self.name && self.version_req.matches(&package_id.version) - } -} - -impl fmt::Display for ManifestDependency { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} {}", self.name, self.version_req)?; - - if !self.source_id.is_default_registry() { - write!(f, " ({})", self.source_id)?; - } - - Ok(()) - } -} - -impl fmt::Debug for ManifestDependency { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ManifestDependency({self})") - } -} diff --git a/scarb/src/core/manifest/summary.rs b/scarb/src/core/manifest/summary.rs new file mode 100644 index 000000000..8ebbd08ae --- /dev/null +++ b/scarb/src/core/manifest/summary.rs @@ -0,0 +1,102 @@ +use crate::core::{ManifestDependency, PackageId, PackageName, SourceId}; +use once_cell::sync::Lazy; +use semver::VersionReq; +use std::ops::Deref; +use std::sync::Arc; + +/// Subset of a [`Manifest`] that contains only the most important information about a package. +/// See [`SummaryInner`] for public fields reference. +#[derive(Clone, Debug)] +pub struct Summary(Arc); + +#[derive(Debug)] +#[non_exhaustive] +pub struct SummaryInner { + pub package_id: PackageId, + pub dependencies: Vec, + pub no_core: bool, +} + +impl Deref for Summary { + type Target = SummaryInner; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl Summary { + pub fn build(package_id: PackageId) -> SummaryBuilder { + SummaryBuilder::new(package_id) + } + + pub fn minimal(package_id: PackageId, dependencies: Vec) -> Self { + Self::build(package_id) + .with_dependencies(dependencies) + .finish() + } + + fn new(data: SummaryInner) -> Self { + Self(Arc::new(data)) + } + + pub fn full_dependencies(&self) -> impl Iterator { + self.dependencies.iter().chain(self.implicit_dependencies()) + } + + pub fn implicit_dependencies(&self) -> impl Iterator { + static CORE_DEPENDENCY: Lazy = Lazy::new(|| { + // NOTE: Pin `core` to exact version, because we know that's the only one we have. + let cairo_version = crate::version::get().cairo.version; + let version_req = VersionReq::parse(&format!("={cairo_version}")).unwrap(); + ManifestDependency { + name: PackageName::CORE, + version_req, + source_id: SourceId::default(), + } + }); + + let mut deps: Vec<&ManifestDependency> = Vec::new(); + + if !self.no_core { + deps.push(&CORE_DEPENDENCY); + } + + deps.into_iter() + } +} + +#[derive(Debug)] +pub struct SummaryBuilder { + package_id: PackageId, + dependencies: Vec, + no_core: bool, +} + +impl SummaryBuilder { + fn new(package_id: PackageId) -> Self { + Self { + package_id, + dependencies: Vec::new(), + no_core: false, + } + } + + pub fn with_dependencies(mut self, dependencies: Vec) -> Self { + self.dependencies = dependencies; + self + } + + pub fn no_core(mut self, no_core: bool) -> Self { + self.no_core = no_core; + self + } + + pub fn finish(self) -> Summary { + Summary::new(SummaryInner { + package_id: self.package_id, + dependencies: self.dependencies, + no_core: self.no_core, + }) + } +} 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/core/registry/mod.rs b/scarb/src/core/registry/mod.rs index 01602f560..5267f4780 100644 --- a/scarb/src/core/registry/mod.rs +++ b/scarb/src/core/registry/mod.rs @@ -4,6 +4,8 @@ use async_trait::async_trait; use crate::core::{ManifestDependency, Package, PackageId, Summary}; pub mod cache; +pub mod patch_map; +pub mod patcher; pub mod source_map; #[async_trait(?Send)] @@ -25,6 +27,7 @@ pub(crate) mod mock { use camino::Utf8PathBuf; use itertools::Itertools; + use crate::compiler::{DefaultForProfile, Profile}; use crate::core::package::PackageName; use crate::core::registry::Registry; use crate::core::{ @@ -45,7 +48,7 @@ pub(crate) mod mock { PackageId::new( PackageName::CORE, crate::version::get().cairo.version.parse().unwrap(), - SourceId::for_std(), + SourceId::default(), ), Vec::new(), ); @@ -202,6 +205,5 @@ pub(crate) mod mock { ); } - use crate::compiler::{DefaultForProfile, Profile}; pub(crate) use pkgs; } diff --git a/scarb/src/core/registry/patch_map.rs b/scarb/src/core/registry/patch_map.rs new file mode 100644 index 000000000..b1053f5f9 --- /dev/null +++ b/scarb/src/core/registry/patch_map.rs @@ -0,0 +1,34 @@ +use std::collections::HashMap; + +use crate::core::{ManifestDependency, PackageName}; +use crate::sources::canonical_url::CanonicalUrl; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct PatchMap(HashMap>); + +impl PatchMap { + pub fn new() -> Self { + Default::default() + } + + /// Lookup the `dependency` in this patch map and return patched dependency if found, + /// or return `dependency` back otherwise. + pub fn lookup<'a>(&'a self, dependency: &'a ManifestDependency) -> &'a ManifestDependency { + self.0 + .get(&dependency.source_id.canonical_url) + .and_then(|patches| patches.get(&dependency.name)) + .unwrap_or(dependency) + } + + pub fn insert( + &mut self, + source_pattern: CanonicalUrl, + dependencies: impl IntoIterator, + ) { + self.0.entry(source_pattern).or_default().extend( + dependencies + .into_iter() + .map(|dependency| (dependency.name.clone(), dependency)), + ); + } +} diff --git a/scarb/src/core/registry/patcher.rs b/scarb/src/core/registry/patcher.rs new file mode 100644 index 000000000..07e706dab --- /dev/null +++ b/scarb/src/core/registry/patcher.rs @@ -0,0 +1,40 @@ +use anyhow::Result; +use async_trait::async_trait; +use tracing::debug; + +use crate::core::registry::patch_map::PatchMap; +use crate::core::registry::Registry; +use crate::core::{ManifestDependency, Package, PackageId, Summary}; + +/// Intercepts [`Registry::query`] operations to follow patches set by user. +pub struct RegistryPatcher<'a> { + registry: &'a dyn Registry, + patch_map: &'a PatchMap, +} + +impl<'a> RegistryPatcher<'a> { + pub fn new(registry: &'a dyn Registry, patch_map: &'a PatchMap) -> Self { + Self { + registry, + patch_map, + } + } +} + +#[async_trait(?Send)] +impl<'a> Registry for RegistryPatcher<'a> { + #[tracing::instrument(skip_all)] + async fn query(&self, dependency: &ManifestDependency) -> Result> { + let patch = self.patch_map.lookup(dependency); + + if patch != dependency { + debug!(%dependency, %patch); + } + + self.registry.query(patch).await + } + + async fn download(&self, package_id: PackageId) -> Result { + self.registry.download(package_id).await + } +} diff --git a/scarb/src/ops/resolve.rs b/scarb/src/ops/resolve.rs index 50a446e9f..5751cdee1 100644 --- a/scarb/src/ops/resolve.rs +++ b/scarb/src/ops/resolve.rs @@ -4,15 +4,18 @@ use anyhow::{bail, Result}; use cairo_lang_filesystem::cfg::{Cfg, CfgSet}; use futures::TryFutureExt; use itertools::Itertools; +use semver::VersionReq; use crate::compiler::{CompilationUnit, CompilationUnitCairoPlugin, CompilationUnitComponent}; use crate::core::package::{Package, PackageClass, PackageId}; use crate::core::registry::cache::RegistryCache; +use crate::core::registry::patch_map::PatchMap; +use crate::core::registry::patcher::RegistryPatcher; use crate::core::registry::source_map::SourceMap; use crate::core::registry::Registry; use crate::core::resolver::Resolve; use crate::core::workspace::Workspace; -use crate::core::Target; +use crate::core::{ManifestDependency, PackageName, SourceId, Target}; use crate::internal::to_version::ToVersion; use crate::resolver; @@ -47,17 +50,31 @@ impl WorkspaceResolve { pub fn resolve_workspace(ws: &Workspace<'_>) -> Result { ws.config().tokio_handle().block_on( async { + let mut patch_map = PatchMap::new(); + + let cairo_version = crate::version::get().cairo.version; + let version_req = VersionReq::parse(&format!("={cairo_version}")).unwrap(); + patch_map.insert( + SourceId::default().canonical_url.clone(), + [ManifestDependency { + name: PackageName::CORE, + version_req, + source_id: SourceId::for_std(), + }], + ); + let source_map = SourceMap::preloaded(ws.members(), ws.config()); - let registry_cache = RegistryCache::new(&source_map); + let cached = RegistryCache::new(&source_map); + let patched = RegistryPatcher::new(&cached, &patch_map); let members_summaries = ws .members() .map(|pkg| pkg.manifest.summary.clone()) .collect::>(); - let resolve = resolver::resolve(&members_summaries, ®istry_cache).await?; + let resolve = resolver::resolve(&members_summaries, &patched).await?; - let packages = collect_packages_from_resolve_graph(&resolve, ®istry_cache).await?; + let packages = collect_packages_from_resolve_graph(&resolve, &patched).await?; Ok(WorkspaceResolve { resolve, packages }) } @@ -73,7 +90,7 @@ pub fn resolve_workspace(ws: &Workspace<'_>) -> Result { #[tracing::instrument(level = "trace", skip_all)] async fn collect_packages_from_resolve_graph( resolve: &Resolve, - registry: &RegistryCache<'_>, + registry: &dyn Registry, ) -> Result> { let mut packages = HashMap::with_capacity(resolve.package_ids().size_hint().0); // TODO(#6): Parallelize this loop. @@ -92,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)? }); @@ -223,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/src/resolver/mod.rs b/scarb/src/resolver/mod.rs index 07ff95fec..02a7de498 100644 --- a/scarb/src/resolver/mod.rs +++ b/scarb/src/resolver/mod.rs @@ -45,36 +45,36 @@ pub async fn resolve(summaries: &[Summary], registry: &dyn Registry) -> Result= 1.7.0")]), + ("baz v1.7.0", []), + ("baz v1.7.1", []), ("baz v1.8.0", []), ("baz v2.1.0", []), ], 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; diff --git a/scarb/tests/e2e/resolver_with_git.rs b/scarb/tests/e2e/resolver_with_git.rs index 84a92ef57..88aea3ac2 100644 --- a/scarb/tests/e2e/resolver_with_git.rs +++ b/scarb/tests/e2e/resolver_with_git.rs @@ -87,6 +87,7 @@ fn two_revs_of_same_dep() { .assert() .failure() .stdout_matches(indoc! {r#" + [..] Updating git repository file://[..]/culprit [..] Updating git repository file://[..]/culprit error: found dependencies on the same package `culprit` coming from incompatible sources: source 1: git+file://[..]/culprit @@ -147,6 +148,7 @@ fn two_revs_of_same_dep_diamond() { [..] Updating git repository file://[..]/dep1 [..] Updating git repository file://[..]/dep2 [..] Updating git repository file://[..]/culprit + [..] Updating git repository file://[..]/culprit error: found dependencies on the same package `culprit` coming from incompatible sources: source 1: git+file://[..]/culprit source 2: git+file://[..]/culprit?branch=branchy