diff --git a/scarb/src/bin/scarb/args.rs b/scarb/src/bin/scarb/args.rs index 4b007d799..40dbe944e 100644 --- a/scarb/src/bin/scarb/args.rs +++ b/scarb/src/bin/scarb/args.rs @@ -228,6 +228,14 @@ pub struct ExpandArgs { #[command(flatten)] pub features: FeaturesSpec, + /// Specify the target to expand by target kind. + #[arg(long)] + pub target_kind: Option, + + /// Specify the target to expand by target name. + #[arg(long)] + pub target_name: Option, + /// Do not attempt formatting. #[arg(long, default_value_t = false)] pub ugly: bool, diff --git a/scarb/src/bin/scarb/commands/expand.rs b/scarb/src/bin/scarb/commands/expand.rs index 67939ab71..e6c018962 100644 --- a/scarb/src/bin/scarb/commands/expand.rs +++ b/scarb/src/bin/scarb/commands/expand.rs @@ -1,7 +1,8 @@ use anyhow::Result; +use smol_str::ToSmolStr; use crate::args::ExpandArgs; -use scarb::core::Config; +use scarb::core::{Config, TargetKind}; use scarb::ops; use scarb::ops::ExpandOpts; @@ -12,6 +13,8 @@ pub fn run(args: ExpandArgs, config: &Config) -> Result<()> { let opts = ExpandOpts { features: args.features.try_into()?, ugly: args.ugly, + target_name: args.target_name.map(|n| n.to_smolstr()), + target_kind: args.target_kind.map(TargetKind::try_new).transpose()?, }; ops::expand(package, opts, &ws) } diff --git a/scarb/src/ops/compile.rs b/scarb/src/ops/compile.rs index 63252fbfe..e894f2326 100644 --- a/scarb/src/ops/compile.rs +++ b/scarb/src/ops/compile.rs @@ -17,6 +17,7 @@ use crate::core::{ FeatureName, PackageId, PackageName, TargetKind, Utf8PathWorkspaceExt, Workspace, }; use crate::ops; +use crate::ops::get_test_package_ids; #[derive(Debug, Clone)] pub enum FeaturesSelector { @@ -81,26 +82,7 @@ where let resolve = ops::resolve_workspace(ws)?; // Add test compilation units to build - let packages = packages - .into_iter() - .flat_map(|package_id| { - let package = ws.members().find(|p| p.id == package_id).unwrap(); - let mut result: Vec = package - .manifest - .targets - .iter() - .filter(|t| t.is_test()) - .map(|t| { - package - .id - .for_test_target(t.group_id.clone().unwrap_or(t.name.clone())) - }) - .collect(); - result.push(package_id); - result - }) - .collect::>(); - + let packages = get_test_package_ids(packages, ws); let compilation_units = ops::generate_compilation_units(&resolve, &opts.features, ws)? .into_iter() .filter(|cu| { diff --git a/scarb/src/ops/expand.rs b/scarb/src/ops/expand.rs index 7ee79a999..78b88614c 100644 --- a/scarb/src/ops/expand.rs +++ b/scarb/src/ops/expand.rs @@ -3,7 +3,7 @@ use crate::compiler::helpers::{build_compiler_config, write_string}; use crate::compiler::{CairoCompilationUnit, CompilationUnit, CompilationUnitAttributes}; use crate::core::{Package, TargetKind, Workspace}; use crate::ops; -use crate::ops::FeaturesOpts; +use crate::ops::{get_test_package_ids, FeaturesOpts}; use anyhow::{anyhow, bail, Context, Result}; use cairo_lang_compiler::db::RootDatabase; use cairo_lang_compiler::diagnostics::DiagnosticsError; @@ -18,11 +18,14 @@ use cairo_lang_parser::db::ParserGroup; use cairo_lang_syntax::node::helpers::UsePathEx; use cairo_lang_syntax::node::{ast, TypedStablePtr, TypedSyntaxNode}; use cairo_lang_utils::Upcast; +use smol_str::SmolStr; use std::collections::HashSet; #[derive(Debug)] pub struct ExpandOpts { pub features: FeaturesOpts, + pub target_kind: Option, + pub target_name: Option, pub ugly: bool, } @@ -38,10 +41,37 @@ pub fn expand(package: Package, opts: ExpandOpts, ws: &Workspace<'_>) -> Result< .map(|unit| ops::compile::compile_unit(unit.clone(), ws)) .collect::>>()?; - let Some(compilation_unit) = compilation_units.into_iter().find(|unit| { - unit.main_package_id() == package.id - && unit.main_component().target_kind() == TargetKind::LIB - }) else { + let Some(compilation_unit) = compilation_units + .into_iter() + // We rewrite group compilation units to single source paths ones. We value simplicity over + // performance here, as expand output will be read by people rather than tooling. + .flat_map(|unit| match unit { + CompilationUnit::Cairo(unit) => unit + .rewrite_to_single_source_paths() + .into_iter() + .map(CompilationUnit::Cairo) + .collect::>(), + // We include non-cairo compilation units here, so we can show better error msg later. + _ => vec![unit], + }) + .find(|unit| { + let target_kind = if opts.target_name.is_none() && opts.target_kind.is_none() { + // If no target specifier is used - default to lib. + Some(TargetKind::LIB) + } else { + opts.target_kind.clone() + }; + // Includes test package ids. + get_test_package_ids(vec![package.id], ws).contains(&unit.main_package_id()) + // We can use main_component below, as targets are not grouped. + && target_kind.as_ref() + .map_or(true, |kind| unit.main_component().target_kind() == *kind) + && opts + .target_name + .as_ref() + .map_or(true, |name| unit.main_component().first_target().name == *name) + }) + else { bail!("compilation unit not found for `{package_name}`") }; let CompilationUnit::Cairo(compilation_unit) = compilation_unit else { diff --git a/scarb/src/ops/resolve.rs b/scarb/src/ops/resolve.rs index cb64f8dcc..e780a3174 100644 --- a/scarb/src/ops/resolve.rs +++ b/scarb/src/ops/resolve.rs @@ -571,3 +571,29 @@ fn generate_cairo_plugin_compilation_units(member: &Package) -> Result, ws: &Workspace<'_>) -> Vec { + packages + .into_iter() + .flat_map(|package_id| { + let Some(package) = ws.members().find(|p| p.id == package_id) else { + return Vec::new(); + }; + let mut result: Vec = package + .manifest + .targets + .iter() + .filter(|t| t.is_test()) + .map(|t| { + package + .id + .for_test_target(t.group_id.clone().unwrap_or(t.name.clone())) + }) + .collect(); + result.push(package_id); + result + }) + .collect::>() +}