diff --git a/Cargo.lock b/Cargo.lock index 1b7a70b2063..f513136caf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1982,12 +1982,12 @@ dependencies = [ "noirc_abi", "noirc_driver", "noirc_errors", + "noirc_frontend", "regex", "rustc_version", "serde", "serde_json", "thiserror", - "toml", ] [[package]] diff --git a/crates/nargo/Cargo.toml b/crates/nargo/Cargo.toml index afbafdff931..3039268281c 100644 --- a/crates/nargo/Cargo.toml +++ b/crates/nargo/Cargo.toml @@ -14,8 +14,8 @@ rustc_version = "0.4.0" acvm.workspace = true noirc_abi.workspace = true noirc_driver.workspace = true +noirc_frontend.workspace = true iter-extended.workspace = true -toml.workspace = true serde.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/crates/nargo_cli/src/constants.rs b/crates/nargo/src/constants.rs similarity index 55% rename from crates/nargo_cli/src/constants.rs rename to crates/nargo/src/constants.rs index d3e6b7f28e1..5e448277694 100644 --- a/crates/nargo_cli/src/constants.rs +++ b/crates/nargo/src/constants.rs @@ -1,23 +1,23 @@ // Directories /// The directory for the `nargo contract` command output -pub(crate) const CONTRACT_DIR: &str = "contract"; +pub const CONTRACT_DIR: &str = "contract"; /// The directory to store serialized circuit proofs. -pub(crate) const PROOFS_DIR: &str = "proofs"; +pub const PROOFS_DIR: &str = "proofs"; /// The directory to store Noir source files -pub(crate) const SRC_DIR: &str = "src"; +pub const SRC_DIR: &str = "src"; /// The directory to store circuits' serialized ACIR representations. -pub(crate) const TARGET_DIR: &str = "target"; +pub const TARGET_DIR: &str = "target"; // Files /// The file from which Nargo pulls prover inputs -pub(crate) const PROVER_INPUT_FILE: &str = "Prover"; +pub const PROVER_INPUT_FILE: &str = "Prover"; /// The file from which Nargo pulls verifier inputs -pub(crate) const VERIFIER_INPUT_FILE: &str = "Verifier"; +pub const VERIFIER_INPUT_FILE: &str = "Verifier"; /// The package definition file for a Noir project. -pub(crate) const PKG_FILE: &str = "Nargo.toml"; +pub const PKG_FILE: &str = "Nargo.toml"; // Extensions /// The extension for files containing circuit proofs. -pub(crate) const PROOF_EXT: &str = "proof"; +pub const PROOF_EXT: &str = "proof"; /// The extension for files containing proof witnesses. -pub(crate) const WITNESS_EXT: &str = "tr"; +pub const WITNESS_EXT: &str = "tr"; diff --git a/crates/nargo/src/lib.rs b/crates/nargo/src/lib.rs index 24605de7849..fda02cf98c2 100644 --- a/crates/nargo/src/lib.rs +++ b/crates/nargo/src/lib.rs @@ -8,8 +8,10 @@ //! Noir Package Manager abbreviated is npm, which is already taken. pub mod artifacts; +pub mod constants; mod errors; -pub mod manifest; pub mod ops; +pub mod package; +pub mod workspace; pub use self::errors::NargoError; diff --git a/crates/nargo/src/manifest/errors.rs b/crates/nargo/src/manifest/errors.rs deleted file mode 100644 index 250211de6fb..00000000000 --- a/crates/nargo/src/manifest/errors.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::path::PathBuf; -use thiserror::Error; - -/// Errors covering situations where a package is either missing or malformed. -#[derive(Debug, Error)] -pub enum InvalidPackageError { - /// Package doesn't have a manifest file - #[error("cannot find a Nargo.toml in {}", .0.display())] - MissingManifestFile(PathBuf), - - /// Package manifest is unreadable. - #[error("Nargo.toml is badly formed, could not parse.\n\n {0}")] - MalformedManifestFile(#[from] toml::de::Error), - - /// Package does not contain Noir source files. - #[error("cannot find src directory in path {}", .0.display())] - NoSourceDir(PathBuf), - - /// Package has neither of `main.nr` and `lib.nr`. - #[error("package must contain either a `lib.nr`(Library) or a `main.nr`(Binary).")] - ContainsZeroCrates, - - /// Package has both a `main.nr` (for binaries) and `lib.nr` (for libraries) - #[error("package cannot contain both a `lib.nr` and a `main.nr`")] - ContainsMultipleCrates, -} diff --git a/crates/nargo/src/manifest/mod.rs b/crates/nargo/src/manifest/mod.rs deleted file mode 100644 index f5a247cf72a..00000000000 --- a/crates/nargo/src/manifest/mod.rs +++ /dev/null @@ -1,147 +0,0 @@ -use serde::Deserialize; -use std::{collections::BTreeMap, path::PathBuf}; - -mod errors; -pub use self::errors::InvalidPackageError; - -#[derive(Debug, Deserialize, Clone)] -pub struct PackageManifest { - pub package: PackageMetadata, - pub dependencies: BTreeMap, -} - -/// Contains all the information about a package, as loaded from a `Nargo.toml`. -/// Represents a manifest, which can be either a package manifest or a workspace manifest. -#[derive(Debug, Deserialize, Clone)] -#[serde(untagged)] -pub enum Manifest { - /// Represents a package manifest. - Package(PackageManifest), - /// Represents a workspace manifest. - Workspace(Workspace), -} - -impl Manifest { - pub fn from_toml_str(toml_as_string: &str) -> Result { - let manifest = toml::from_str(toml_as_string)?; - Ok(manifest) - } - - pub fn to_package(self) -> Option { - match self { - Self::Package(v) => Some(v), - _ => None, - } - } -} - -impl PackageManifest { - /// Returns whether the package has a local dependency. - // Local paths are usually relative and are discouraged when sharing libraries - // It is better to separate these into different packages. - pub fn has_local_dependency(&self) -> bool { - self.dependencies.values().any(|dep| matches!(dep, Dependency::Path { .. })) - } -} - -/// Configuration of a workspace in a manifest. -/// Indicates that `[workspace]` was present and the members were specified as well. -#[derive(Debug, Deserialize, Clone)] -pub struct Workspace { - #[serde(rename = "workspace")] - pub config: WorkspaceConfig, -} - -#[derive(Default, Debug, Deserialize, Clone)] -#[serde(rename_all = "kebab-case")] -pub struct WorkspaceConfig { - /// List of members in this workspace. - pub members: Vec, - /// Specifies the default crate to interact with in the context (similarly to how we have nargo as the default crate in this repository). - pub default_member: Option, -} - -#[allow(dead_code)] -#[derive(Default, Debug, Deserialize, Clone)] -pub struct PackageMetadata { - #[serde(default = "panic_missing_name")] - pub name: String, - description: Option, - authors: Vec, - // If not compiler version is supplied, the latest is used - // For now, we state that all packages must be compiled under the same - // compiler version. - // We also state that ACIR and the compiler will upgrade in lockstep. - // so you will not need to supply an ACIR and compiler version - compiler_version: Option, - backend: Option, - license: Option, -} - -// TODO: Remove this after a couple of breaking releases (added in 0.10.0) -fn panic_missing_name() -> String { - panic!( - r#" - -Failed to parse `Nargo.toml`. - -`Nargo.toml` now requires a "name" field for Noir packages. - -```toml -[package] -name = "package_name" -``` - -Modify your `Nargo.toml` similarly to above and rerun the command. - -"# - ) -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(untagged)] -/// Enum representing the different types of ways to -/// supply a source for the dependency -pub enum Dependency { - Github { git: String, tag: String }, - Path { path: String }, -} - -#[test] -fn parse_standard_toml() { - let src = r#" - - [package] - name = "test" - authors = ["kev", "foo"] - compiler_version = "0.1" - - [dependencies] - rand = { tag = "next", git = "https://github.com/rust-lang-nursery/rand"} - cool = { tag = "next", git = "https://github.com/rust-lang-nursery/rand"} - hello = {path = "./noir_driver"} - "#; - - assert!(Manifest::from_toml_str(src).is_ok()); -} - -#[test] -fn parse_workspace_toml() { - let src = r#" - [workspace] - members = ["a", "b"] - "#; - - assert!(Manifest::from_toml_str(src).is_ok()); -} - -#[test] -fn parse_workspace_default_member_toml() { - let src = r#" - [workspace] - members = ["a", "b"] - default-member = "a" - "#; - - assert!(Manifest::from_toml_str(src).is_ok()); -} diff --git a/crates/nargo/src/package.rs b/crates/nargo/src/package.rs new file mode 100644 index 00000000000..20c662b69f4 --- /dev/null +++ b/crates/nargo/src/package.rs @@ -0,0 +1,33 @@ +use std::{collections::BTreeMap, path::PathBuf}; + +use noirc_frontend::graph::{CrateName, CrateType}; + +use crate::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; + +#[derive(Clone)] +pub enum Dependency { + Local { package: Package }, + Remote { package: Package }, +} + +#[derive(Clone)] +pub struct Package { + pub root_dir: PathBuf, + pub crate_type: CrateType, + pub entry_path: PathBuf, + pub name: CrateName, + pub dependencies: BTreeMap, +} + +impl Package { + pub fn prover_input_path(&self) -> PathBuf { + // TODO: This should be configurable, such as if we are looking for .json or .toml or custom paths + // For now it is hard-coded to be toml. + self.root_dir.join(format!("{PROVER_INPUT_FILE}.toml")) + } + pub fn verifier_input_path(&self) -> PathBuf { + // TODO: This should be configurable, such as if we are looking for .json or .toml or custom paths + // For now it is hard-coded to be toml. + self.root_dir.join(format!("{VERIFIER_INPUT_FILE}.toml")) + } +} diff --git a/crates/nargo/src/workspace.rs b/crates/nargo/src/workspace.rs index 0954b4eb143..5df13350683 100644 --- a/crates/nargo/src/workspace.rs +++ b/crates/nargo/src/workspace.rs @@ -2,3 +2,77 @@ // Then we use workspace to allow more than one. In the future, do not allow there to be // both a binary and a library. // - library will be default + +use std::{ + iter::{once, Once}, + path::PathBuf, + slice, +}; + +use crate::{ + constants::{CONTRACT_DIR, PROOFS_DIR, TARGET_DIR}, + package::Package, +}; + +#[derive(Clone)] +pub struct Workspace { + pub root_dir: PathBuf, + pub members: Vec, + // If `Some()`, the `selected_package_index` is used to select the only `Package` when iterating a Workspace + pub selected_package_index: Option, +} + +impl Workspace { + pub fn package_build_path(&self, package: &Package) -> PathBuf { + let name: String = package.name.clone().into(); + self.target_directory_path().join(name) + } + + pub fn contracts_directory_path(&self, package: &Package) -> PathBuf { + let name: String = package.name.clone().into(); + self.root_dir.join(CONTRACT_DIR).join(name) + } + + pub fn proofs_directory_path(&self) -> PathBuf { + self.root_dir.join(PROOFS_DIR) + } + + pub fn target_directory_path(&self) -> PathBuf { + self.root_dir.join(TARGET_DIR) + } +} + +pub enum IntoIter<'a, T> { + Only(Once<&'a T>), + All(slice::Iter<'a, T>), +} + +impl<'a> IntoIterator for &'a Workspace { + type Item = &'a Package; + type IntoIter = IntoIter<'a, Package>; + + fn into_iter(self) -> Self::IntoIter { + if let Some(index) = self.selected_package_index { + // Precondition: The selected_package_index was verified to be in-bounds before constructing workspace + let member = self + .members + .get(index) + .expect("Workspace constructed with invalid selected_package_index"); + + IntoIter::Only(once(member)) + } else { + IntoIter::All(self.members.iter()) + } + } +} + +impl<'a> Iterator for IntoIter<'a, Package> { + type Item = &'a Package; + + fn next(&mut self) -> Option { + match self { + Self::Only(iter) => iter.next(), + Self::All(iter) => iter.next(), + } + } +} diff --git a/crates/nargo_cli/build.rs b/crates/nargo_cli/build.rs index d889ba6856c..f3493148a7f 100644 --- a/crates/nargo_cli/build.rs +++ b/crates/nargo_cli/build.rs @@ -84,7 +84,6 @@ fn generate_tests(test_file: &mut File) { if config_data["exclude"].contains(&test_name) { "#[ignore]" } else { "" }; let should_fail = config_data["fail"].contains(&test_name); - let is_workspace = test_dir.to_str().map_or(false, |s| s.contains("workspace")); write!( test_file, @@ -96,8 +95,7 @@ fn execute_{test_sub_dir}_{test_name}() {{ let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg(if {is_workspace} {{ "test" }} else {{ "execute" }}); - + cmd.arg("execute"); if {should_fail} {{ cmd.assert().failure(); diff --git a/crates/nargo_cli/src/cli/check_cmd.rs b/crates/nargo_cli/src/cli/check_cmd.rs index 9a0a2f77e7c..8f2e23ed750 100644 --- a/crates/nargo_cli/src/cli/check_cmd.rs +++ b/crates/nargo_cli/src/cli/check_cmd.rs @@ -1,53 +1,58 @@ -use crate::{errors::CliError, resolver::resolve_root_manifest}; +use crate::{ + errors::CliError, find_package_manifest, manifest::resolve_workspace_from_toml, prepare_package, +}; use acvm::Backend; use clap::Args; use iter_extended::btree_map; +use nargo::package::Package; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; use noirc_driver::{check_crate, compute_function_signature, CompileOptions}; use noirc_errors::reporter::ReportedErrors; -use noirc_frontend::{graph::CrateId, hir::Context}; -use std::path::{Path, PathBuf}; +use noirc_frontend::{ + graph::{CrateId, CrateName}, + hir::Context, +}; use super::fs::write_to_file; use super::NargoConfig; -use crate::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; /// Checks the constraint system for errors #[derive(Debug, Clone, Args)] pub(crate) struct CheckCommand { + /// The name of the package to check + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, } pub(crate) fn run( - backend: &B, + _backend: &B, args: CheckCommand, config: NargoConfig, ) -> Result<(), CliError> { - check_from_path(backend, &config.program_dir, &args.compile_options)?; - println!("Constraint system successfully built!"); + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + + for package in &workspace { + check_package(package, &args.compile_options)?; + println!("[{}] Constraint system successfully built!", package.name); + } Ok(()) } -fn check_from_path( - // Backend isn't used but keeping it in the signature allows for better type inference - // TODO: This function doesn't need to exist but requires a little more refactoring - _backend: &B, - program_dir: &Path, +fn check_package( + package: &Package, compile_options: &CompileOptions, -) -> Result<(), CliError> { - let (mut context, crate_id) = resolve_root_manifest(program_dir, None)?; +) -> Result<(), ReportedErrors> { + let (mut context, crate_id) = prepare_package(package); check_crate_and_report_errors(&mut context, crate_id, compile_options.deny_warnings)?; // XXX: We can have a --overwrite flag to determine if you want to overwrite the Prover/Verifier.toml files if let Some((parameters, return_type)) = compute_function_signature(&context, &crate_id) { - // XXX: The root config should return an enum to determine if we are looking for .json or .toml - // For now it is hard-coded to be toml. - // - // Check for input.toml and verifier.toml - let path_to_root = PathBuf::from(program_dir); - let path_to_prover_input = path_to_root.join(format!("{PROVER_INPUT_FILE}.toml")); - let path_to_verifier_input = path_to_root.join(format!("{VERIFIER_INPUT_FILE}.toml")); + let path_to_prover_input = package.prover_input_path(); + let path_to_verifier_input = package.verifier_input_path(); // If they are not available, then create them and populate them based on the ABI if !path_to_prover_input.exists() { @@ -108,6 +113,8 @@ mod tests { use noirc_abi::{AbiParameter, AbiType, AbiVisibility, Sign}; use noirc_driver::CompileOptions; + use crate::{find_package_manifest, manifest::resolve_workspace_from_toml}; + use super::create_input_toml_template; const TEST_DATA_DIR: &str = "tests/target_tests_data"; @@ -157,16 +164,15 @@ d2 = ["", "", ""] let pass_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("{TEST_DATA_DIR}/pass")); - let backend = crate::backends::ConcreteBackend::default(); let config = CompileOptions::default(); let paths = std::fs::read_dir(pass_dir).unwrap(); for path in paths.flatten() { let path = path.path(); - assert!( - super::check_from_path(&backend, &path, &config).is_ok(), - "path: {}", - path.display() - ); + let toml_path = find_package_manifest(&path).unwrap(); + let workspace = resolve_workspace_from_toml(&toml_path, None).unwrap(); + for package in &workspace { + assert!(super::check_package(package, &config).is_ok(), "path: {}", path.display()); + } } } @@ -176,16 +182,19 @@ d2 = ["", "", ""] let fail_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("{TEST_DATA_DIR}/fail")); - let backend = crate::backends::ConcreteBackend::default(); let config = CompileOptions::default(); let paths = std::fs::read_dir(fail_dir).unwrap(); for path in paths.flatten() { let path = path.path(); - assert!( - super::check_from_path(&backend, &path, &config).is_err(), - "path: {}", - path.display() - ); + let toml_path = find_package_manifest(&path).unwrap(); + let workspace = resolve_workspace_from_toml(&toml_path, None).unwrap(); + for package in &workspace { + assert!( + super::check_package(package, &config).is_err(), + "path: {}", + path.display() + ); + } } } @@ -194,17 +203,16 @@ d2 = ["", "", ""] let pass_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join(format!("{TEST_DATA_DIR}/pass_dev_mode")); - let backend = crate::backends::ConcreteBackend::default(); let config = CompileOptions { deny_warnings: false, ..Default::default() }; let paths = std::fs::read_dir(pass_dir).unwrap(); for path in paths.flatten() { let path = path.path(); - assert!( - super::check_from_path(&backend, &path, &config).is_ok(), - "path: {}", - path.display() - ); + let toml_path = find_package_manifest(&path).unwrap(); + let workspace = resolve_workspace_from_toml(&toml_path, None).unwrap(); + for package in &workspace { + assert!(super::check_package(package, &config).is_ok(), "path: {}", path.display()); + } } } } diff --git a/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs b/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs index cedf558bcb8..0c01f8d5dc8 100644 --- a/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use super::fs::{ common_reference_string::{ read_cached_common_reference_string, update_common_reference_string, @@ -8,20 +10,23 @@ use super::fs::{ write_to_file, }; use super::NargoConfig; -use crate::{ - cli::compile_cmd::compile_circuit, constants::CONTRACT_DIR, constants::TARGET_DIR, - errors::CliError, -}; +use crate::{cli::compile_cmd::compile_circuit, errors::CliError}; +use crate::{find_package_manifest, manifest::resolve_workspace_from_toml, prepare_package}; use acvm::Backend; use clap::Args; -use nargo::ops::{codegen_verifier, preprocess_program}; +use nargo::{ + ops::{codegen_verifier, preprocess_program}, + package::Package, +}; use noirc_driver::CompileOptions; +use noirc_frontend::graph::CrateName; /// Generates a Solidity verifier smart contract for the program #[derive(Debug, Clone, Args)] pub(crate) struct CodegenVerifierCommand { - /// The name of the circuit build files (ACIR, proving and verification keys) - circuit_name: Option, + /// The name of the package to codegen + #[clap(long)] + package: Option, #[clap(flatten)] compile_options: CompileOptions, @@ -32,34 +37,52 @@ pub(crate) fn run( args: CodegenVerifierCommand, config: NargoConfig, ) -> Result<(), CliError> { - // TODO(#1201): Should this be a utility function? - let circuit_build_path = args - .circuit_name - .map(|circuit_name| config.program_dir.join(TARGET_DIR).join(circuit_name)); + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; - let common_reference_string = read_cached_common_reference_string(); + for package in &workspace { + let circuit_build_path = workspace.package_build_path(package); + + let smart_contract_string = smart_contract_for_package( + backend, + package, + circuit_build_path, + &args.compile_options, + )?; + + let contract_dir = workspace.contracts_directory_path(package); + create_named_dir(&contract_dir, "contract"); + let contract_path = contract_dir.join("plonk_vk").with_extension("sol"); + + let path = write_to_file(smart_contract_string.as_bytes(), &contract_path); + println!("[{}] Contract successfully created and located at {path}", package.name); + } - let (common_reference_string, preprocessed_program) = match circuit_build_path { - Some(circuit_build_path) => { - let program = read_program_from_file(circuit_build_path)?; - let common_reference_string = update_common_reference_string( - backend, - &common_reference_string, - &program.bytecode, - ) - .map_err(CliError::CommonReferenceStringError)?; - (common_reference_string, program) - } - None => { - let (program, _) = - compile_circuit(backend, None, config.program_dir.as_ref(), &args.compile_options)?; - let common_reference_string = - update_common_reference_string(backend, &common_reference_string, &program.circuit) - .map_err(CliError::CommonReferenceStringError)?; - let (program, _) = preprocess_program(backend, true, &common_reference_string, program) - .map_err(CliError::ProofSystemCompilerError)?; - (common_reference_string, program) - } + Ok(()) +} + +fn smart_contract_for_package( + backend: &B, + package: &Package, + circuit_build_path: PathBuf, + compile_options: &CompileOptions, +) -> Result> { + let common_reference_string = read_cached_common_reference_string(); + let (common_reference_string, preprocessed_program) = if circuit_build_path.exists() { + let program = read_program_from_file(circuit_build_path)?; + let common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.bytecode) + .map_err(CliError::CommonReferenceStringError)?; + (common_reference_string, program) + } else { + let (mut context, crate_id) = prepare_package(package); + let program = compile_circuit(backend, &mut context, crate_id, compile_options)?; + let common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.circuit) + .map_err(CliError::CommonReferenceStringError)?; + let (program, _) = preprocess_program(backend, true, &common_reference_string, program) + .map_err(CliError::ProofSystemCompilerError)?; + (common_reference_string, program) }; let verification_key = preprocessed_program @@ -75,11 +98,5 @@ pub(crate) fn run( write_cached_common_reference_string(&common_reference_string); - let contract_dir = config.program_dir.join(CONTRACT_DIR); - create_named_dir(&contract_dir, "contract"); - let contract_path = contract_dir.join("plonk_vk").with_extension("sol"); - - let path = write_to_file(smart_contract_string.as_bytes(), &contract_path); - println!("Contract successfully created and located at {path}"); - Ok(()) + Ok(smart_contract_string) } diff --git a/crates/nargo_cli/src/cli/compile_cmd.rs b/crates/nargo_cli/src/cli/compile_cmd.rs index fbaecb606a1..2d59667e7ff 100644 --- a/crates/nargo_cli/src/cli/compile_cmd.rs +++ b/crates/nargo_cli/src/cli/compile_cmd.rs @@ -7,14 +7,16 @@ use noirc_driver::{ compile_contracts, compile_main, CompileOptions, CompiledProgram, ErrorsAndWarnings, Warnings, }; use noirc_errors::reporter::ReportedErrors; +use noirc_frontend::graph::{CrateId, CrateName}; use noirc_frontend::hir::Context; -use std::path::Path; use clap::Args; use nargo::ops::{preprocess_contract_function, preprocess_program}; -use crate::{constants::TARGET_DIR, errors::CliError, resolver::resolve_root_manifest}; +use crate::errors::CliError; +use crate::manifest::resolve_workspace_from_toml; +use crate::{find_package_manifest, prepare_package}; use super::fs::{ common_reference_string::{ @@ -31,9 +33,6 @@ const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg"; /// Compile the program and its secret execution trace into ACIR format #[derive(Debug, Clone, Args)] pub(crate) struct CompileCommand { - /// The name of the ACIR file - circuit_name: String, - /// Include Proving and Verification keys in the build artifacts. #[arg(long)] include_keys: bool, @@ -42,6 +41,10 @@ pub(crate) struct CompileCommand { #[arg(short, long)] contracts: bool, + /// The name of the package to compile + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, } @@ -51,66 +54,72 @@ pub(crate) fn run( args: CompileCommand, config: NargoConfig, ) -> Result<(), CliError> { - let circuit_dir = config.program_dir.join(TARGET_DIR); + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let circuit_dir = workspace.target_directory_path(); let mut common_reference_string = read_cached_common_reference_string(); // If contracts is set we're compiling every function in a 'contract' rather than just 'main'. if args.contracts { - let (mut context, crate_id) = resolve_root_manifest(&config.program_dir, None)?; - - let result = compile_contracts(&mut context, crate_id, &args.compile_options); - let contracts = report_errors(result, &context, args.compile_options.deny_warnings)?; - - // TODO(#1389): I wonder if it is incorrect for nargo-core to know anything about contracts. - // As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms) - // are compiled via nargo-core and then the PreprocessedContract is constructed here. - // This is due to EACH function needing it's own CRS, PKey, and VKey from the backend. - let preprocessed_contracts: Result, CliError> = - try_vecmap(contracts, |contract| { - let preprocessed_contract_functions = - try_vecmap(contract.functions, |mut func| { - func.bytecode = optimize_circuit(backend, func.bytecode)?.0; - common_reference_string = update_common_reference_string( - backend, - &common_reference_string, - &func.bytecode, - ) - .map_err(CliError::CommonReferenceStringError)?; - - preprocess_contract_function( - backend, - args.include_keys, - &common_reference_string, - func, - ) - .map_err(CliError::ProofSystemCompilerError) - })?; - - Ok(PreprocessedContract { - name: contract.name, - backend: String::from(BACKEND_IDENTIFIER), - functions: preprocessed_contract_functions, - }) - }); - for contract in preprocessed_contracts? { - save_contract_to_file( - &contract, - &format!("{}-{}", &args.circuit_name, contract.name), - &circuit_dir, - ); + for package in &workspace { + let (mut context, crate_id) = prepare_package(package); + let result = compile_contracts(&mut context, crate_id, &args.compile_options); + let contracts = report_errors(result, &context, args.compile_options.deny_warnings)?; + + // TODO(#1389): I wonder if it is incorrect for nargo-core to know anything about contracts. + // As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms) + // are compiled via nargo-core and then the PreprocessedContract is constructed here. + // This is due to EACH function needing it's own CRS, PKey, and VKey from the backend. + let preprocessed_contracts: Result, CliError> = + try_vecmap(contracts, |contract| { + let preprocessed_contract_functions = + try_vecmap(contract.functions, |mut func| { + func.bytecode = optimize_circuit(backend, func.bytecode)?.0; + common_reference_string = update_common_reference_string( + backend, + &common_reference_string, + &func.bytecode, + ) + .map_err(CliError::CommonReferenceStringError)?; + + preprocess_contract_function( + backend, + args.include_keys, + &common_reference_string, + func, + ) + .map_err(CliError::ProofSystemCompilerError) + })?; + + Ok(PreprocessedContract { + name: contract.name, + backend: String::from(BACKEND_IDENTIFIER), + functions: preprocessed_contract_functions, + }) + }); + for contract in preprocessed_contracts? { + save_contract_to_file( + &contract, + &format!("{}-{}", package.name, contract.name), + &circuit_dir, + ); + } } } else { - let (program, _) = - compile_circuit(backend, None, &config.program_dir, &args.compile_options)?; - common_reference_string = - update_common_reference_string(backend, &common_reference_string, &program.circuit) - .map_err(CliError::CommonReferenceStringError)?; - - let (preprocessed_program, _) = - preprocess_program(backend, args.include_keys, &common_reference_string, program) - .map_err(CliError::ProofSystemCompilerError)?; - save_program_to_file(&preprocessed_program, &args.circuit_name, circuit_dir); + for package in &workspace { + let (mut context, crate_id) = prepare_package(package); + let program = compile_circuit(backend, &mut context, crate_id, &args.compile_options)?; + + common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.circuit) + .map_err(CliError::CommonReferenceStringError)?; + + let (preprocessed_program, _) = + preprocess_program(backend, args.include_keys, &common_reference_string, program) + .map_err(CliError::ProofSystemCompilerError)?; + save_program_to_file(&preprocessed_program, &package.name, &circuit_dir); + } } write_cached_common_reference_string(&common_reference_string); @@ -120,18 +129,18 @@ pub(crate) fn run( pub(crate) fn compile_circuit( backend: &B, - package: Option, - program_dir: &Path, + context: &mut Context, + crate_id: CrateId, compile_options: &CompileOptions, -) -> Result<(CompiledProgram, Context), CliError> { - let (mut context, crate_id) = resolve_root_manifest(program_dir, package)?; - let result = compile_main(&mut context, crate_id, compile_options); - let mut program = report_errors(result, &context, compile_options.deny_warnings)?; - +) -> Result { + let result = compile_main(context, crate_id, compile_options); + let mut program = report_errors(result, context, compile_options.deny_warnings)?; // Apply backend specific optimizations. let (optimized_circuit, opcode_labels) = optimize_circuit(backend, program.circuit) .expect("Backend does not support an opcode that is in the IR"); + // TODO(#2110): Why does this set `program.circuit` to `optimized_circuit` instead of the function taking ownership + // and requiring we use `optimized_circuit` everywhere after program.circuit = optimized_circuit; let opcode_ids = vecmap(opcode_labels, |label| match label { OpcodeLabel::Unresolved => { @@ -141,7 +150,7 @@ pub(crate) fn compile_circuit( }); program.debug.update_acir(opcode_ids); - Ok((program, context)) + Ok(program) } pub(super) fn optimize_circuit( diff --git a/crates/nargo_cli/src/cli/execute_cmd.rs b/crates/nargo_cli/src/cli/execute_cmd.rs index eaaea6d4ab3..ca5c18585ab 100644 --- a/crates/nargo_cli/src/cli/execute_cmd.rs +++ b/crates/nargo_cli/src/cli/execute_cmd.rs @@ -1,23 +1,23 @@ -use std::path::Path; - use acvm::acir::circuit::OpcodeLabel; use acvm::acir::{circuit::Circuit, native_types::WitnessMap}; use acvm::Backend; use clap::Args; +use nargo::constants::PROVER_INPUT_FILE; +use nargo::package::Package; use nargo::NargoError; use noirc_abi::input_parser::{Format, InputValue}; use noirc_abi::{Abi, InputMap}; use noirc_driver::{CompileOptions, CompiledProgram}; use noirc_errors::{debug_info::DebugInfo, CustomDiagnostic}; +use noirc_frontend::graph::CrateName; use noirc_frontend::hir::Context; +use super::compile_cmd::compile_circuit; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; -use crate::{ - cli::compile_cmd::compile_circuit, - constants::{PROVER_INPUT_FILE, TARGET_DIR}, - errors::CliError, -}; +use crate::errors::CliError; +use crate::manifest::resolve_workspace_from_toml; +use crate::{find_package_manifest, prepare_package}; /// Executes a circuit to calculate its return value #[derive(Debug, Clone, Args)] @@ -29,6 +29,10 @@ pub(crate) struct ExecuteCommand { #[clap(long, short, default_value = PROVER_INPUT_FILE)] prover_name: String, + /// The name of the package to execute + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, } @@ -38,35 +42,40 @@ pub(crate) fn run( args: ExecuteCommand, config: NargoConfig, ) -> Result<(), CliError> { - let (return_value, solved_witness) = - execute_with_path(backend, &config.program_dir, args.prover_name, &args.compile_options)?; + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let witness_dir = &workspace.target_directory_path(); - println!("Circuit witness successfully solved"); - if let Some(return_value) = return_value { - println!("Circuit output: {return_value:?}"); - } - if let Some(witness_name) = args.witness_name { - let witness_dir = config.program_dir.join(TARGET_DIR); + for package in &workspace { + let (return_value, solved_witness) = + execute_package(backend, package, &args.prover_name, &args.compile_options)?; - let witness_path = save_witness_to_dir(solved_witness, &witness_name, witness_dir)?; + println!("[{}] Circuit witness successfully solved", package.name); + if let Some(return_value) = return_value { + println!("[{}] Circuit output: {return_value:?}", package.name); + } + if let Some(witness_name) = &args.witness_name { + let witness_path = save_witness_to_dir(solved_witness, witness_name, witness_dir)?; - println!("Witness saved to {}", witness_path.display()); + println!("[{}] Witness saved to {}", package.name, witness_path.display()); + } } Ok(()) } -fn execute_with_path( +fn execute_package( backend: &B, - program_dir: &Path, - prover_name: String, + package: &Package, + prover_name: &str, compile_options: &CompileOptions, ) -> Result<(Option, WitnessMap), CliError> { - let (compiled_program, context) = compile_circuit(backend, None, program_dir, compile_options)?; + let (mut context, crate_id) = prepare_package(package); + let compiled_program = compile_circuit(backend, &mut context, crate_id, compile_options)?; let CompiledProgram { abi, circuit, debug } = compiled_program; // Parse the initial witness values from Prover.toml let (inputs_map, _) = - read_inputs_from_file(program_dir, prover_name.as_str(), Format::Toml, &abi)?; + read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &abi)?; let solved_witness = execute_program(backend, circuit, &abi, &inputs_map, Some((debug, context)))?; diff --git a/crates/nargo_cli/src/cli/fs/inputs.rs b/crates/nargo_cli/src/cli/fs/inputs.rs index bd55e4b0abd..fd2afdefa12 100644 --- a/crates/nargo_cli/src/cli/fs/inputs.rs +++ b/crates/nargo_cli/src/cli/fs/inputs.rs @@ -70,6 +70,7 @@ mod tests { use std::{collections::BTreeMap, vec}; use acvm::FieldElement; + use nargo::constants::VERIFIER_INPUT_FILE; use noirc_abi::{ input_parser::{Format, InputValue}, Abi, AbiParameter, AbiType, AbiVisibility, @@ -77,7 +78,6 @@ mod tests { use tempdir::TempDir; use super::{read_inputs_from_file, write_inputs_to_file}; - use crate::constants::VERIFIER_INPUT_FILE; #[test] fn write_and_read_recovers_inputs_and_return_value() { diff --git a/crates/nargo_cli/src/cli/fs/program.rs b/crates/nargo_cli/src/cli/fs/program.rs index 871a6023837..311923a6686 100644 --- a/crates/nargo_cli/src/cli/fs/program.rs +++ b/crates/nargo_cli/src/cli/fs/program.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; use nargo::artifacts::{contract::PreprocessedContract, program::PreprocessedProgram}; +use noirc_frontend::graph::CrateName; use crate::errors::FilesystemError; @@ -8,10 +9,11 @@ use super::{create_named_dir, write_to_file}; pub(crate) fn save_program_to_file>( compiled_program: &PreprocessedProgram, - circuit_name: &str, + crate_name: &CrateName, circuit_dir: P, ) -> PathBuf { - save_build_artifact_to_file(compiled_program, circuit_name, circuit_dir) + let circuit_name: String = crate_name.into(); + save_build_artifact_to_file(compiled_program, &circuit_name, circuit_dir) } pub(crate) fn save_contract_to_file>( compiled_contract: &PreprocessedContract, diff --git a/crates/nargo_cli/src/cli/fs/proof.rs b/crates/nargo_cli/src/cli/fs/proof.rs index 3a54aa908f8..d2b3050708b 100644 --- a/crates/nargo_cli/src/cli/fs/proof.rs +++ b/crates/nargo_cli/src/cli/fs/proof.rs @@ -1,6 +1,8 @@ use std::path::{Path, PathBuf}; -use crate::{constants::PROOF_EXT, errors::FilesystemError}; +use nargo::constants::PROOF_EXT; + +use crate::errors::FilesystemError; use super::{create_named_dir, write_to_file}; diff --git a/crates/nargo_cli/src/cli/fs/witness.rs b/crates/nargo_cli/src/cli/fs/witness.rs index 7ecafb1615b..edfb1aa63d6 100644 --- a/crates/nargo_cli/src/cli/fs/witness.rs +++ b/crates/nargo_cli/src/cli/fs/witness.rs @@ -1,9 +1,10 @@ use std::path::{Path, PathBuf}; use acvm::acir::native_types::WitnessMap; +use nargo::constants::WITNESS_EXT; use super::{create_named_dir, write_to_file}; -use crate::{constants::WITNESS_EXT, errors::FilesystemError}; +use crate::errors::FilesystemError; pub(crate) fn save_witness_to_dir>( witnesses: WitnessMap, diff --git a/crates/nargo_cli/src/cli/info_cmd.rs b/crates/nargo_cli/src/cli/info_cmd.rs index 7ad0a2caf8c..12a70f7b13e 100644 --- a/crates/nargo_cli/src/cli/info_cmd.rs +++ b/crates/nargo_cli/src/cli/info_cmd.rs @@ -1,19 +1,26 @@ use acvm::Backend; use clap::Args; +use nargo::package::Package; use noirc_driver::CompileOptions; -use std::path::Path; +use noirc_frontend::graph::CrateName; -use crate::cli::compile_cmd::compile_circuit; -use crate::errors::CliError; +use crate::{ + cli::compile_cmd::compile_circuit, errors::CliError, find_package_manifest, + manifest::resolve_workspace_from_toml, prepare_package, +}; use super::NargoConfig; -/// Provides detailed informaton on a circuit +/// Provides detailed information on a circuit /// Current information provided: /// 1. The number of ACIR opcodes /// 2. Counts the final number gates in the circuit used by a backend #[derive(Debug, Clone, Args)] pub(crate) struct InfoCommand { + /// The name of the package to detail + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, } @@ -23,20 +30,29 @@ pub(crate) fn run( args: InfoCommand, config: NargoConfig, ) -> Result<(), CliError> { - count_opcodes_and_gates_with_path(backend, config.program_dir, &args.compile_options) + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + + for package in &workspace { + count_opcodes_and_gates_in_package(backend, package, &args.compile_options)?; + } + + Ok(()) } -fn count_opcodes_and_gates_with_path>( +fn count_opcodes_and_gates_in_package( backend: &B, - program_dir: P, + package: &Package, compile_options: &CompileOptions, ) -> Result<(), CliError> { - let (compiled_program, _) = - compile_circuit(backend, None, program_dir.as_ref(), compile_options)?; + let (mut context, crate_id) = prepare_package(package); + let compiled_program = compile_circuit(backend, &mut context, crate_id, compile_options)?; + let num_opcodes = compiled_program.circuit.opcodes.len(); println!( - "Total ACIR opcodes generated for language {:?}: {}", + "[{}] Total ACIR opcodes generated for language {:?}: {}", + package.name, backend.np_language(), num_opcodes ); @@ -44,7 +60,7 @@ fn count_opcodes_and_gates_with_path>( let exact_circuit_size = backend .get_exact_circuit_size(&compiled_program.circuit) .map_err(CliError::ProofSystemCompilerError)?; - println!("Backend circuit size: {exact_circuit_size}"); + println!("[{}] Backend circuit size: {exact_circuit_size}", package.name); Ok(()) } diff --git a/crates/nargo_cli/src/cli/init_cmd.rs b/crates/nargo_cli/src/cli/init_cmd.rs index 77613611343..576690b7fab 100644 --- a/crates/nargo_cli/src/cli/init_cmd.rs +++ b/crates/nargo_cli/src/cli/init_cmd.rs @@ -1,12 +1,10 @@ -use crate::{ - constants::{PKG_FILE, SRC_DIR}, - errors::CliError, -}; +use crate::errors::CliError; use super::fs::{create_named_dir, write_to_file}; use super::{NargoConfig, CARGO_PKG_VERSION}; use acvm::Backend; use clap::Args; +use nargo::constants::{PKG_FILE, SRC_DIR}; use std::path::PathBuf; /// Create a Noir project in the current directory. diff --git a/crates/nargo_cli/src/cli/prove_cmd.rs b/crates/nargo_cli/src/cli/prove_cmd.rs index 92e9599cd8b..cdf83f9759b 100644 --- a/crates/nargo_cli/src/cli/prove_cmd.rs +++ b/crates/nargo_cli/src/cli/prove_cmd.rs @@ -3,38 +3,31 @@ use std::path::{Path, PathBuf}; use acvm::Backend; use clap::Args; use nargo::artifacts::program::PreprocessedProgram; +use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::ops::{preprocess_program, prove_execution, verify_proof}; +use nargo::package::Package; use noirc_abi::input_parser::Format; use noirc_driver::CompileOptions; +use noirc_frontend::graph::CrateName; -use super::NargoConfig; -use super::{ - compile_cmd::compile_circuit, - fs::{ - common_reference_string::{ - read_cached_common_reference_string, update_common_reference_string, - write_cached_common_reference_string, - }, - inputs::{read_inputs_from_file, write_inputs_to_file}, - program::read_program_from_file, - proof::save_proof_to_dir, +use super::compile_cmd::compile_circuit; +use super::fs::{ + common_reference_string::{ + read_cached_common_reference_string, update_common_reference_string, + write_cached_common_reference_string, }, + inputs::{read_inputs_from_file, write_inputs_to_file}, + program::read_program_from_file, + proof::save_proof_to_dir, }; -use crate::{ - cli::execute_cmd::execute_program, - constants::{PROOFS_DIR, PROVER_INPUT_FILE, TARGET_DIR, VERIFIER_INPUT_FILE}, - errors::CliError, -}; +use super::NargoConfig; +use crate::manifest::resolve_workspace_from_toml; +use crate::{cli::execute_cmd::execute_program, errors::CliError}; +use crate::{find_package_manifest, prepare_package}; /// Create proof for this program. The proof is returned as a hex encoded string. #[derive(Debug, Clone, Args)] pub(crate) struct ProveCommand { - /// The name of the proof - proof_name: Option, - - /// The name of the circuit build files (ACIR, proving and verification keys) - circuit_name: Option, - /// The name of the toml file which contains the inputs for the prover #[clap(long, short, default_value = PROVER_INPUT_FILE)] prover_name: String, @@ -47,11 +40,12 @@ pub(crate) struct ProveCommand { #[arg(long)] verify: bool, + /// The name of the package to prove + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, - - #[clap(long)] - package: Option, } pub(crate) fn run( @@ -59,65 +53,57 @@ pub(crate) fn run( args: ProveCommand, config: NargoConfig, ) -> Result<(), CliError> { - let proof_dir = config.program_dir.join(PROOFS_DIR); - - let circuit_build_path = args - .circuit_name - .map(|circuit_name| config.program_dir.join(TARGET_DIR).join(circuit_name)); - - prove_with_path( - backend, - args.proof_name, - args.prover_name, - args.verifier_name, - args.package, - config.program_dir, - proof_dir, - circuit_build_path, - args.verify, - &args.compile_options, - )?; + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let proof_dir = workspace.proofs_directory_path(); + + for package in &workspace { + let circuit_build_path = workspace.package_build_path(package); + + prove_package( + backend, + package, + &args.prover_name, + &args.verifier_name, + &proof_dir, + circuit_build_path, + args.verify, + &args.compile_options, + )?; + } Ok(()) } #[allow(clippy::too_many_arguments)] -pub(crate) fn prove_with_path>( +pub(crate) fn prove_package( backend: &B, - proof_name: Option, - prover_name: String, - verifier_name: String, - package: Option, - program_dir: P, - proof_dir: P, - circuit_build_path: Option, + package: &Package, + prover_name: &str, + verifier_name: &str, + proof_dir: &Path, + circuit_build_path: PathBuf, check_proof: bool, compile_options: &CompileOptions, -) -> Result, CliError> { +) -> Result<(), CliError> { let common_reference_string = read_cached_common_reference_string(); - let (common_reference_string, preprocessed_program, debug_data) = match circuit_build_path { - Some(circuit_build_path) => { - let program = read_program_from_file(circuit_build_path)?; - let common_reference_string = update_common_reference_string( - backend, - &common_reference_string, - &program.bytecode, - ) - .map_err(CliError::CommonReferenceStringError)?; - (common_reference_string, program, None) - } - None => { - let (program, context) = - compile_circuit(backend, package, program_dir.as_ref(), compile_options)?; - let common_reference_string = - update_common_reference_string(backend, &common_reference_string, &program.circuit) - .map_err(CliError::CommonReferenceStringError)?; - let (program, debug) = - preprocess_program(backend, true, &common_reference_string, program) - .map_err(CliError::ProofSystemCompilerError)?; - (common_reference_string, program, Some((debug, context))) - } + let (common_reference_string, preprocessed_program, debug_data) = if circuit_build_path.exists() + { + let program = read_program_from_file(circuit_build_path)?; + let common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.bytecode) + .map_err(CliError::CommonReferenceStringError)?; + (common_reference_string, program, None) + } else { + let (mut context, crate_id) = prepare_package(package); + let program = compile_circuit(backend, &mut context, crate_id, compile_options)?; + let common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.circuit) + .map_err(CliError::CommonReferenceStringError)?; + let (program, debug) = preprocess_program(backend, true, &common_reference_string, program) + .map_err(CliError::ProofSystemCompilerError)?; + (common_reference_string, program, Some((debug, context))) }; write_cached_common_reference_string(&common_reference_string); @@ -127,7 +113,7 @@ pub(crate) fn prove_with_path>( // Parse the initial witness values from Prover.toml let (inputs_map, _) = - read_inputs_from_file(&program_dir, prover_name.as_str(), Format::Toml, &abi)?; + read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &abi)?; let solved_witness = execute_program(backend, bytecode.clone(), &abi, &inputs_map, debug_data)?; @@ -139,8 +125,8 @@ pub(crate) fn prove_with_path>( &public_inputs, &return_value, &public_abi, - &program_dir, - verifier_name.as_str(), + &package.root_dir, + verifier_name, Format::Toml, )?; @@ -170,12 +156,7 @@ pub(crate) fn prove_with_path>( } } - let proof_path = if let Some(proof_name) = proof_name { - Some(save_proof_to_dir(&proof, &proof_name, proof_dir)?) - } else { - println!("{}", hex::encode(&proof)); - None - }; + save_proof_to_dir(&proof, &String::from(&package.name), proof_dir)?; - Ok(proof_path) + Ok(()) } diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index c1aa359e724..7eb1c9bff74 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -1,15 +1,15 @@ -use std::{io::Write, path::Path}; +use std::io::Write; use acvm::{acir::native_types::WitnessMap, Backend}; use clap::Args; -use nargo::ops::execute_circuit; +use nargo::{ops::execute_circuit, package::Package}; use noirc_driver::{compile_no_check, CompileOptions}; -use noirc_frontend::{hir::Context, node_interner::FuncId}; +use noirc_frontend::{graph::CrateName, hir::Context, node_interner::FuncId}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use crate::{ - cli::check_cmd::check_crate_and_report_errors, errors::CliError, - resolver::resolve_root_manifest, + cli::check_cmd::check_crate_and_report_errors, errors::CliError, find_package_manifest, + manifest::resolve_workspace_from_toml, prepare_package, }; use super::{compile_cmd::optimize_circuit, NargoConfig}; @@ -24,6 +24,10 @@ pub(crate) struct TestCommand { #[arg(long)] show_output: bool, + /// The name of the package to test + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, } @@ -35,56 +39,62 @@ pub(crate) fn run( ) -> Result<(), CliError> { let test_name: String = args.test_name.unwrap_or_else(|| "".to_owned()); - run_tests(backend, &config.program_dir, &test_name, args.show_output, &args.compile_options) + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + + for package in &workspace { + run_tests(backend, package, &test_name, args.show_output, &args.compile_options)?; + } + + Ok(()) } fn run_tests( backend: &B, - program_dir: &Path, + package: &Package, test_name: &str, show_output: bool, compile_options: &CompileOptions, ) -> Result<(), CliError> { - let (mut context, crate_id) = resolve_root_manifest(program_dir, None)?; + let (mut context, crate_id) = prepare_package(package); check_crate_and_report_errors(&mut context, crate_id, compile_options.deny_warnings)?; - let test_functions = match context.crate_graph.crate_type(crate_id) { - noirc_frontend::graph::CrateType::Workspace => { - context.get_all_test_functions_in_workspace_matching(test_name) - } - _ => context.get_all_test_functions_in_crate_matching(&crate_id, test_name), - }; + let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, test_name); - println!("Running {} test functions...", test_functions.len()); + println!("[{}] Running {} test functions", package.name, test_functions.len()); let mut failing = 0; let writer = StandardStream::stderr(ColorChoice::Always); let mut writer = writer.lock(); for (test_name, test_function) in test_functions { - writeln!(writer, "Testing {test_name}...").expect("Failed to write to stdout"); - writer.flush().ok(); + write!(writer, "[{}] Testing {test_name}... ", package.name) + .expect("Failed to write to stdout"); + writer.flush().expect("Failed to flush writer"); match run_test(backend, &test_name, test_function, &context, show_output, compile_options) { Ok(_) => { - writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).ok(); - writeln!(writer, "ok").ok(); + writer + .set_color(ColorSpec::new().set_fg(Some(Color::Green))) + .expect("Failed to set color"); + writeln!(writer, "ok").expect("Failed to write to stdout"); } // Assume an error was already printed to stdout Err(_) => failing += 1, } - writer.reset().ok(); + writer.reset().expect("Failed to reset writer"); } if failing == 0 { - writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).unwrap(); - writeln!(writer, "All tests passed").ok(); + write!(writer, "[{}] ", package.name).expect("Failed to write to stdout"); + writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).expect("Failed to set color"); + writeln!(writer, "All tests passed").expect("Failed to write to stdout"); } else { let plural = if failing == 1 { "" } else { "s" }; - return Err(CliError::Generic(format!("{failing} test{plural} failed"))); + return Err(CliError::Generic(format!("[{}] {failing} test{plural} failed", package.name))); } - writer.reset().ok(); + writer.reset().expect("Failed to reset writer"); Ok(()) } diff --git a/crates/nargo_cli/src/cli/verify_cmd.rs b/crates/nargo_cli/src/cli/verify_cmd.rs index f9068c66c9c..78b23a0612d 100644 --- a/crates/nargo_cli/src/cli/verify_cmd.rs +++ b/crates/nargo_cli/src/cli/verify_cmd.rs @@ -9,32 +9,31 @@ use super::fs::{ program::read_program_from_file, }; use super::NargoConfig; -use crate::{ - constants::{PROOFS_DIR, PROOF_EXT, TARGET_DIR, VERIFIER_INPUT_FILE}, - errors::CliError, -}; +use crate::errors::CliError; +use crate::manifest::resolve_workspace_from_toml; +use crate::{find_package_manifest, prepare_package}; use acvm::Backend; use clap::Args; -use nargo::artifacts::program::PreprocessedProgram; +use nargo::constants::{PROOF_EXT, VERIFIER_INPUT_FILE}; use nargo::ops::{preprocess_program, verify_proof}; +use nargo::{artifacts::program::PreprocessedProgram, package::Package}; use noirc_abi::input_parser::Format; use noirc_driver::CompileOptions; +use noirc_frontend::graph::CrateName; use std::path::{Path, PathBuf}; /// Given a proof and a program, verify whether the proof is valid #[derive(Debug, Clone, Args)] pub(crate) struct VerifyCommand { - /// The proof to verify - proof: String, - - /// The name of the circuit build files (ACIR, proving and verification keys) - circuit_name: Option, - /// The name of the toml file which contains the inputs for the verifier #[clap(long, short, default_value = VERIFIER_INPUT_FILE)] verifier_name: String, + /// The name of the package verify + #[clap(long)] + package: Option, + #[clap(flatten)] compile_options: CompileOptions, } @@ -44,54 +43,53 @@ pub(crate) fn run( args: VerifyCommand, config: NargoConfig, ) -> Result<(), CliError> { - let proof_path = - config.program_dir.join(PROOFS_DIR).join(&args.proof).with_extension(PROOF_EXT); - - let circuit_build_path = args - .circuit_name - .map(|circuit_name| config.program_dir.join(TARGET_DIR).join(circuit_name)); + let toml_path = find_package_manifest(&config.program_dir)?; + let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let proofs_dir = workspace.proofs_directory_path(); + + for package in &workspace { + let circuit_build_path = workspace.package_build_path(package); + + let proof_path = proofs_dir.join(String::from(&package.name)).with_extension(PROOF_EXT); + + verify_package( + backend, + package, + &proof_path, + circuit_build_path, + &args.verifier_name, + &args.compile_options, + )?; + } - verify_with_path( - backend, - &config.program_dir, - proof_path, - circuit_build_path.as_ref(), - args.verifier_name, - &args.compile_options, - ) + Ok(()) } -fn verify_with_path>( +fn verify_package( backend: &B, - program_dir: P, - proof_path: PathBuf, - circuit_build_path: Option

, - verifier_name: String, + package: &Package, + proof_path: &Path, + circuit_build_path: PathBuf, + verifier_name: &str, compile_options: &CompileOptions, ) -> Result<(), CliError> { let common_reference_string = read_cached_common_reference_string(); - let (common_reference_string, preprocessed_program) = match circuit_build_path { - Some(circuit_build_path) => { - let program = read_program_from_file(circuit_build_path)?; - let common_reference_string = update_common_reference_string( - backend, - &common_reference_string, - &program.bytecode, - ) - .map_err(CliError::CommonReferenceStringError)?; - (common_reference_string, program) - } - None => { - let (program, _) = - compile_circuit(backend, None, program_dir.as_ref(), compile_options)?; - let common_reference_string = - update_common_reference_string(backend, &common_reference_string, &program.circuit) - .map_err(CliError::CommonReferenceStringError)?; - let (program, _) = preprocess_program(backend, true, &common_reference_string, program) - .map_err(CliError::ProofSystemCompilerError)?; - (common_reference_string, program) - } + let (common_reference_string, preprocessed_program) = if circuit_build_path.exists() { + let program = read_program_from_file(circuit_build_path)?; + let common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.bytecode) + .map_err(CliError::CommonReferenceStringError)?; + (common_reference_string, program) + } else { + let (mut context, crate_id) = prepare_package(package); + let program = compile_circuit(backend, &mut context, crate_id, compile_options)?; + let common_reference_string = + update_common_reference_string(backend, &common_reference_string, &program.circuit) + .map_err(CliError::CommonReferenceStringError)?; + let (program, _) = preprocess_program(backend, true, &common_reference_string, program) + .map_err(CliError::ProofSystemCompilerError)?; + (common_reference_string, program) }; write_cached_common_reference_string(&common_reference_string); @@ -101,10 +99,10 @@ fn verify_with_path>( // Load public inputs (if any) from `verifier_name`. let public_abi = abi.public_abi(); let (public_inputs_map, return_value) = - read_inputs_from_file(program_dir, verifier_name.as_str(), Format::Toml, &public_abi)?; + read_inputs_from_file(&package.root_dir, verifier_name, Format::Toml, &public_abi)?; let public_inputs = public_abi.encode(&public_inputs_map, return_value)?; - let proof = load_hex_data(&proof_path)?; + let proof = load_hex_data(proof_path)?; let verification_key = verification_key .expect("Verification key should exist as `true` is passed to `preprocess_program`"); @@ -121,6 +119,6 @@ fn verify_with_path>( if valid_proof { Ok(()) } else { - Err(CliError::InvalidProof(proof_path)) + Err(CliError::InvalidProof(proof_path.to_path_buf())) } } diff --git a/crates/nargo_cli/src/errors.rs b/crates/nargo_cli/src/errors.rs index f9220d55b1c..00a84ff2964 100644 --- a/crates/nargo_cli/src/errors.rs +++ b/crates/nargo_cli/src/errors.rs @@ -9,8 +9,6 @@ use noirc_errors::reporter::ReportedErrors; use std::path::PathBuf; use thiserror::Error; -use crate::resolver::DependencyResolutionError; - #[derive(Debug, Error)] pub(crate) enum FilesystemError { #[error("Error: {} is not a valid path\nRun either `nargo compile` to generate missing build artifacts or `nargo prove` to construct a proof", .0.display())] @@ -41,9 +39,6 @@ pub(crate) enum CliError { #[error("Failed to verify proof {}", .0.display())] InvalidProof(PathBuf), - #[error(transparent)] - ResolutionError(#[from] DependencyResolutionError), - /// Errors encountered while compiling the noir program. /// These errors are already written to stderr. #[error("Aborting due to {} previous error{}", .0.error_count, if .0.error_count == 1 { "" } else { "s" })] @@ -64,6 +59,10 @@ pub(crate) enum CliError { #[error(transparent)] NargoError(#[from] NargoError), + /// Error from Manifest + #[error(transparent)] + ManifestError(#[from] ManifestError), + /// Backend error caused by a function on the SmartContract trait #[error(transparent)] SmartContractError(::Error), // Unfortunately, Rust won't let us `impl From` over an Associated Type on a generic @@ -82,3 +81,50 @@ impl From for CliError { Self::ReportedErrors(errors) } } + +/// Errors covering situations where a package is either missing or malformed. +#[derive(Debug, Error)] +pub(crate) enum ManifestError { + /// Package doesn't have a manifest file + #[error("cannot find a Nargo.toml in {}", .0.display())] + MissingFile(PathBuf), + + #[error("Cannot read file {0}. Does it exist?")] + ReadFailed(PathBuf), + + #[error("Nargo.toml is missing a parent directory")] + MissingParent, + + /// Package manifest is unreadable. + #[error("Nargo.toml is badly formed, could not parse.\n\n {0}")] + MalformedFile(#[from] toml::de::Error), + + #[error("Unxpected workspace definition found in {0}")] + UnexpectedWorkspace(PathBuf), + + /// Package does not contain Noir source files. + #[error("cannot find src directory in path {0}")] + NoSourceDir(PathBuf), + + /// Package has neither of `main.nr` and `lib.nr`. + #[error("package must contain either a `lib.nr`(Library) or a `main.nr`(Binary).")] + ContainsZeroCrates, + + /// Package has both a `main.nr` (for binaries) and `lib.nr` (for libraries) + #[error("package cannot contain both a `lib.nr` and a `main.nr`")] + ContainsMultipleCrates, + + /// Invalid character `-` in package name + #[error("invalid character `-` in package name")] + InvalidPackageName, + + /// Encountered error while downloading git repository. + #[error("{0}")] + GitError(String), + + #[error("Selected package ({0}) was not found")] + MissingSelectedPackage(String), + + #[error("Default package was not found. Does {0} exist in your workspace?")] + MissingDefaultPackage(PathBuf), +} diff --git a/crates/nargo_cli/src/git.rs b/crates/nargo_cli/src/git.rs index 7f103e21b38..850657a8af1 100644 --- a/crates/nargo_cli/src/git.rs +++ b/crates/nargo_cli/src/git.rs @@ -1,7 +1,16 @@ use std::path::PathBuf; +/// Creates a unique folder name for a GitHub repo +/// by using its URL and tag +fn resolve_folder_name(base: &url::Url, tag: &str) -> String { + let mut folder_name = base.domain().unwrap().to_owned(); + folder_name.push_str(base.path()); + folder_name.push_str(tag); + folder_name +} + pub(crate) fn git_dep_location(base: &url::Url, tag: &str) -> PathBuf { - let folder_name = super::resolver::resolve_folder_name(base, tag); + let folder_name = resolve_folder_name(base, tag); super::nargo_crates().join(folder_name) } diff --git a/crates/nargo_cli/src/lib.rs b/crates/nargo_cli/src/lib.rs index 9426decf194..b456d31c0ca 100644 --- a/crates/nargo_cli/src/lib.rs +++ b/crates/nargo_cli/src/lib.rs @@ -7,21 +7,26 @@ //! This name was used because it sounds like `cargo` and //! Noir Package Manager abbreviated is npm, which is already taken. -use noirc_frontend::graph::CrateType; +use fm::FileManager; +use nargo::package::{Dependency, Package}; +use noirc_driver::{add_dep, create_local_crate, create_non_local_crate}; +use noirc_frontend::{ + graph::{CrateGraph, CrateId, CrateName, CrateType}, + hir::Context, +}; use std::{ + collections::BTreeMap, fs::ReadDir, path::{Path, PathBuf}, }; +use errors::ManifestError; + mod backends; pub mod cli; -mod constants; mod errors; mod git; mod manifest; -mod resolver; - -use nargo::manifest::InvalidPackageError; fn nargo_crates() -> PathBuf { dirs::home_dir().unwrap().join("nargo") @@ -30,7 +35,7 @@ fn nargo_crates() -> PathBuf { /// Returns the path of the root directory of the package containing `current_path`. /// /// Returns a `CliError` if no parent directories of `current_path` contain a manifest file. -fn find_package_root(current_path: &Path) -> Result { +fn find_package_root(current_path: &Path) -> Result { let manifest_path = find_package_manifest(current_path)?; let package_root = @@ -42,27 +47,27 @@ fn find_package_root(current_path: &Path) -> Result Result { +fn find_package_manifest(current_path: &Path) -> Result { current_path .ancestors() .find_map(|dir| find_file(dir, "Nargo", "toml")) - .ok_or_else(|| InvalidPackageError::MissingManifestFile(current_path.to_path_buf())) + .ok_or_else(|| ManifestError::MissingFile(current_path.to_path_buf())) } -fn lib_or_bin(current_path: impl AsRef) -> Result<(PathBuf, CrateType), InvalidPackageError> { - let current_path = current_path.as_ref(); +fn lib_or_bin(root_dir: impl AsRef) -> Result<(PathBuf, CrateType), ManifestError> { + let current_path = root_dir.as_ref(); // A library has a lib.nr and a binary has a main.nr // You cannot have both. let src_path = find_dir(current_path, "src") - .ok_or_else(|| InvalidPackageError::NoSourceDir(current_path.to_path_buf()))?; + .ok_or_else(|| ManifestError::NoSourceDir(current_path.to_path_buf()))?; let lib_nr_path = find_file(&src_path, "lib", "nr"); let bin_nr_path = find_file(&src_path, "main", "nr"); match (lib_nr_path, bin_nr_path) { - (Some(_), Some(_)) => Err(InvalidPackageError::ContainsMultipleCrates), + (Some(_), Some(_)) => Err(ManifestError::ContainsMultipleCrates), (None, Some(path)) => Ok((path, CrateType::Binary)), (Some(path), None) => Ok((path, CrateType::Library)), - (None, None) => Err(InvalidPackageError::ContainsZeroCrates), + (None, None) => Err(ManifestError::ContainsZeroCrates), } } @@ -93,3 +98,32 @@ fn find_artifact(entries: ReadDir, artifact_name: &str) -> Option { fn list_files_and_folders_in>(path: P) -> Option { std::fs::read_dir(path).ok() } + +fn prepare_dependencies( + context: &mut Context, + parent_crate: CrateId, + dependencies: BTreeMap, +) { + for (dep_name, dep) in dependencies.into_iter() { + match dep { + Dependency::Remote { package } | Dependency::Local { package } => { + let crate_id = + create_non_local_crate(context, &package.entry_path, package.crate_type); + add_dep(context, parent_crate, crate_id, dep_name); + prepare_dependencies(context, crate_id, package.dependencies.to_owned()); + } + } + } +} + +fn prepare_package(package: &Package) -> (Context, CrateId) { + let fm = FileManager::new(&package.root_dir); + let graph = CrateGraph::default(); + let mut context = Context::new(fm, graph); + + let crate_id = create_local_crate(&mut context, &package.entry_path, package.crate_type); + + prepare_dependencies(&mut context, crate_id, package.dependencies.to_owned()); + + (context, crate_id) +} diff --git a/crates/nargo_cli/src/manifest.rs b/crates/nargo_cli/src/manifest.rs index 2660fd8c1cb..e1da57c0c2b 100644 --- a/crates/nargo_cli/src/manifest.rs +++ b/crates/nargo_cli/src/manifest.rs @@ -1,13 +1,284 @@ -use std::path::Path; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; -use nargo::manifest::{InvalidPackageError, Manifest}; +use nargo::{ + package::{Dependency, Package}, + workspace::Workspace, +}; +use noirc_frontend::graph::CrateName; +use serde::Deserialize; -/// Parses a Nargo.toml file from it's path -/// The path to the toml file must be present. -/// Calling this function without this guarantee is an ICE. -pub(crate) fn parse>(path_to_toml: P) -> Result { - let toml_as_string = - std::fs::read_to_string(&path_to_toml).expect("ice: path given for toml file is invalid"); +use crate::{errors::ManifestError, git::clone_git_repo}; - Manifest::from_toml_str(&toml_as_string) +#[derive(Debug, Deserialize, Clone)] +struct PackageConfig { + package: PackageMetadata, + dependencies: BTreeMap, +} + +impl PackageConfig { + fn resolve_to_package(&self, root_dir: &Path) -> Result { + let name = self.package.name.parse().map_err(|_| ManifestError::InvalidPackageName)?; + + let mut dependencies: BTreeMap = BTreeMap::new(); + for (name, dep_config) in self.dependencies.iter() { + let name = name.parse().map_err(|_| ManifestError::InvalidPackageName)?; + let resolved_dep = dep_config.resolve_to_dependency(root_dir)?; + + dependencies.insert(name, resolved_dep); + } + + let (entry_path, crate_type) = crate::lib_or_bin(root_dir)?; + + Ok(Package { root_dir: root_dir.to_path_buf(), entry_path, crate_type, name, dependencies }) + } +} + +/// Contains all the information about a package, as loaded from a `Nargo.toml`. +#[derive(Debug, Deserialize, Clone)] +#[serde(untagged)] +enum Config { + /// Represents a `Nargo.toml` with package fields. + Package { + #[serde(flatten)] + package_config: PackageConfig, + }, + /// Represents a `Nargo.toml` with workspace fields. + Workspace { + #[serde(alias = "workspace")] + workspace_config: WorkspaceConfig, + }, +} + +impl TryFrom for Config { + type Error = toml::de::Error; + + fn try_from(toml: String) -> Result { + toml::from_str(&toml) + } +} + +impl TryFrom<&str> for Config { + type Error = toml::de::Error; + + fn try_from(toml: &str) -> Result { + toml::from_str(toml) + } +} + +/// Tracks the root_dir of a `Nargo.toml` and the contents inside the file. +struct NargoToml { + root_dir: PathBuf, + config: Config, +} + +#[derive(Default, Debug, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] +struct WorkspaceConfig { + /// List of members in this workspace. + members: Vec, + /// Specifies the default crate to interact with in the context (similarly to how we have nargo as the default crate in this repository). + default_member: Option, +} + +#[allow(dead_code)] +#[derive(Default, Debug, Deserialize, Clone)] +struct PackageMetadata { + #[serde(default = "panic_missing_name")] + name: String, + description: Option, + authors: Option>, + // If not compiler version is supplied, the latest is used + // For now, we state that all packages must be compiled under the same + // compiler version. + // We also state that ACIR and the compiler will upgrade in lockstep. + // so you will not need to supply an ACIR and compiler version + compiler_version: Option, + backend: Option, + license: Option, +} + +// TODO: Remove this after a couple of breaking releases (added in 0.10.0) +fn panic_missing_name() -> String { + panic!( + r#" + +Failed to parse `Nargo.toml`. + +`Nargo.toml` now requires a "name" field for Noir packages. + +```toml +[package] +name = "package_name" +``` + +Modify your `Nargo.toml` similarly to above and rerun the command. + +"# + ) +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(untagged)] +/// Enum representing the different types of ways to +/// supply a source for the dependency +enum DependencyConfig { + Github { git: String, tag: String }, + Path { path: String }, +} + +impl DependencyConfig { + fn resolve_to_dependency(&self, pkg_root: &Path) -> Result { + match self { + Self::Github { git, tag } => { + let dir_path = clone_git_repo(git, tag).map_err(ManifestError::GitError)?; + let toml_path = dir_path.join("Nargo.toml"); + let package = resolve_package_from_toml(&toml_path)?; + Ok(Dependency::Remote { package }) + } + Self::Path { path } => { + let dir_path = pkg_root.join(path); + let toml_path = dir_path.join("Nargo.toml"); + let package = resolve_package_from_toml(&toml_path)?; + Ok(Dependency::Local { package }) + } + } + } +} + +fn toml_to_workspace( + nargo_toml: NargoToml, + selected_package: Option, +) -> Result { + let workspace = match nargo_toml.config { + Config::Package { package_config } => { + let member = package_config.resolve_to_package(&nargo_toml.root_dir)?; + if selected_package.is_none() || Some(&member.name) == selected_package.as_ref() { + Workspace { + root_dir: nargo_toml.root_dir, + selected_package_index: Some(0), + members: vec![member], + } + } else { + return Err(ManifestError::MissingSelectedPackage(member.name.into())); + } + } + Config::Workspace { workspace_config } => { + let mut members = Vec::new(); + let mut selected_package_index = None; + for (index, member_path) in workspace_config.members.into_iter().enumerate() { + let package_root_dir = nargo_toml.root_dir.join(&member_path); + let package_toml_path = package_root_dir.join("Nargo.toml"); + let member = resolve_package_from_toml(&package_toml_path)?; + + match selected_package.as_ref() { + Some(selected_name) => { + if &member.name == selected_name { + selected_package_index = Some(index); + } + } + None => { + if Some(&member_path) == workspace_config.default_member.as_ref() { + selected_package_index = Some(index); + } + } + } + + members.push(member); + } + + // If the selected_package_index is still `None` but we have see a default_member or selected package, + // we want to present an error to users + if selected_package_index.is_none() { + if let Some(selected_name) = selected_package { + return Err(ManifestError::MissingSelectedPackage(selected_name.into())); + } + if let Some(default_path) = workspace_config.default_member { + return Err(ManifestError::MissingDefaultPackage(default_path)); + } + } + + Workspace { root_dir: nargo_toml.root_dir, members, selected_package_index } + } + }; + + Ok(workspace) +} + +fn read_toml(toml_path: &Path) -> Result { + let toml_as_string = std::fs::read_to_string(toml_path) + .map_err(|_| ManifestError::ReadFailed(toml_path.to_path_buf()))?; + let root_dir = toml_path.parent().ok_or(ManifestError::MissingParent)?; + let nargo_toml = + NargoToml { root_dir: root_dir.to_path_buf(), config: toml_as_string.try_into()? }; + + Ok(nargo_toml) +} + +/// Resolves a Nargo.toml file into a `Package` struct as defined by our `nargo` core. +fn resolve_package_from_toml(toml_path: &Path) -> Result { + let nargo_toml = read_toml(toml_path)?; + + match nargo_toml.config { + Config::Package { package_config } => { + package_config.resolve_to_package(&nargo_toml.root_dir) + } + Config::Workspace { .. } => { + Err(ManifestError::UnexpectedWorkspace(toml_path.to_path_buf())) + } + } +} + +/// Resolves a Nargo.toml file into a `Workspace` struct as defined by our `nargo` core. +pub(crate) fn resolve_workspace_from_toml( + toml_path: &Path, + selected_package: Option, +) -> Result { + let nargo_toml = read_toml(toml_path)?; + + toml_to_workspace(nargo_toml, selected_package) +} + +#[test] +fn parse_standard_toml() { + let src = r#" + + [package] + name = "test" + authors = ["kev", "foo"] + compiler_version = "0.1" + + [dependencies] + rand = { tag = "next", git = "https://github.com/rust-lang-nursery/rand"} + cool = { tag = "next", git = "https://github.com/rust-lang-nursery/rand"} + hello = {path = "./noir_driver"} + "#; + + assert!(Config::try_from(String::from(src)).is_ok()); + assert!(Config::try_from(src).is_ok()); +} + +#[test] +fn parse_workspace_toml() { + let src = r#" + [workspace] + members = ["a", "b"] + "#; + + assert!(Config::try_from(String::from(src)).is_ok()); + assert!(Config::try_from(src).is_ok()); +} + +#[test] +fn parse_workspace_default_member_toml() { + let src = r#" + [workspace] + members = ["a", "b"] + default-member = "a" + "#; + + assert!(Config::try_from(String::from(src)).is_ok()); + assert!(Config::try_from(src).is_ok()); } diff --git a/crates/nargo_cli/src/resolver.rs b/crates/nargo_cli/src/resolver.rs deleted file mode 100644 index 5c4e8225ee4..00000000000 --- a/crates/nargo_cli/src/resolver.rs +++ /dev/null @@ -1,265 +0,0 @@ -use std::{ - collections::HashMap, - path::{Path, PathBuf}, -}; - -use fm::FileManager; -use nargo::manifest::{Dependency, Manifest, PackageManifest, WorkspaceConfig}; -use noirc_driver::{add_dep, create_local_crate, create_non_local_crate}; -use noirc_frontend::{ - graph::{CrateGraph, CrateId, CrateName, CrateType}, - hir::Context, -}; -use thiserror::Error; - -use crate::{git::clone_git_repo, InvalidPackageError}; - -/// Creates a unique folder name for a GitHub repo -/// by using it's URL and tag -pub(crate) fn resolve_folder_name(base: &url::Url, tag: &str) -> String { - let mut folder_name = base.domain().unwrap().to_owned(); - folder_name.push_str(base.path()); - folder_name.push_str(tag); - folder_name -} - -/// Errors covering situations where a crate's dependency tree cannot be resolved. -#[derive(Debug, Error)] -pub(crate) enum DependencyResolutionError { - /// Encountered error while downloading git repository. - #[error("{0}")] - GitError(String), - - /// Attempted to depend on a binary crate. - #[error("dependency {dep_pkg_name} is a binary package and so it cannot be depended upon.")] - BinaryDependency { dep_pkg_name: String }, - - /// Attempted to depend on remote crate which has a local dependency. - /// We have no guarantees that this local dependency will be available so must error. - #[error("remote(git) dependency has a local dependency.\ndependency located at {}", dependency_path.display())] - RemoteDepWithLocalDep { dependency_path: PathBuf }, - - /// Dependency is not a valid crate - #[error(transparent)] - MalformedDependency(#[from] InvalidPackageError), - - /// Workspace does not contain packages - #[error("manifest path `{}` contains no packages", path.display())] - EmptyWorkspace { path: PathBuf }, - - /// Use workspace as a dependency is not currently supported - #[error("use workspace as a dependency is not currently supported")] - WorkspaceDependency, - - /// Multiple workspace roots found in the same workspace - #[error("multiple workspace roots found in the same workspace:\n{}\n{}", root.display(), member.display())] - MultipleWorkspace { root: PathBuf, member: PathBuf }, - - /// Invalid character `-` in package name - #[error("invalid character `-` in package name")] - InvalidPackageName, - - #[error("package specification `{0}` did not match any packages")] - PackageNotFound(String), - - #[error("two packages named `{0}` in this workspace")] - PackageCollision(String), -} - -#[derive(Debug, Clone)] -struct CachedDep { - entry_path: PathBuf, - crate_type: CrateType, - manifest: PackageManifest, - // Whether the dependency came from - // a remote dependency - remote: bool, -} - -/// Resolves a toml file by either downloading the necessary git repo -/// or it uses the repo on the cache. -/// Downloading will be recursive, so if a package contains packages -/// We need to download those too - -/// Returns the Driver and the backend to use -/// Note that the backend is ignored in the dependencies. -/// Since Noir is backend agnostic, this is okay to do. -/// XXX: Need to handle when a local package changes! -pub(crate) fn resolve_root_manifest( - dir_path: &std::path::Path, - package: Option, -) -> Result<(Context, CrateId), DependencyResolutionError> { - let fm = FileManager::new(dir_path); - let graph = CrateGraph::default(); - let mut context = Context::new(fm, graph); - - let manifest_path = super::find_package_manifest(dir_path)?; - let manifest = super::manifest::parse(&manifest_path)?; - - let crate_id = match manifest { - Manifest::Package(package) => { - let (entry_path, crate_type) = super::lib_or_bin(dir_path)?; - - let crate_id = create_local_crate(&mut context, &entry_path, crate_type); - let pkg_root = manifest_path.parent().expect("Every manifest path has a parent."); - - resolve_package_manifest(&mut context, crate_id, package, pkg_root)?; - - crate_id - } - Manifest::Workspace(workspace) => resolve_workspace_manifest( - &mut context, - package, - manifest_path, - dir_path, - workspace.config, - )?, - }; - - Ok((context, crate_id)) -} - -// Resolves a config file by recursively resolving the dependencies in the config -// Need to solve the case of a project trying to use itself as a dep -// -// We do not need to add stdlib, as it's implicitly -// imported. However, it may be helpful to have the stdlib imported by the -// package manager. -fn resolve_package_manifest( - context: &mut Context, - parent_crate: CrateId, - manifest: PackageManifest, - pkg_root: &Path, -) -> Result<(), DependencyResolutionError> { - let mut cached_packages: HashMap = HashMap::new(); - - // First download and add these top level dependencies crates to the Driver - for (dep_pkg_name, pkg_src) in manifest.dependencies.iter() { - let (dir_path, dep_meta) = cache_dep(pkg_src, pkg_root)?; - - let (entry_path, crate_type) = (&dep_meta.entry_path, &dep_meta.crate_type); - - if crate_type == &CrateType::Binary { - return Err(DependencyResolutionError::BinaryDependency { - dep_pkg_name: dep_pkg_name.to_string(), - }); - } - - let crate_id = create_non_local_crate(context, entry_path, *crate_type); - add_dep(context, parent_crate, crate_id, dep_pkg_name); - - cached_packages.insert(dir_path, (crate_id, dep_meta)); - } - - // Resolve all transitive dependencies - for (dependency_path, (crate_id, dep_meta)) in cached_packages { - if dep_meta.remote && dep_meta.manifest.has_local_dependency() { - return Err(DependencyResolutionError::RemoteDepWithLocalDep { dependency_path }); - } - // TODO: Why did it create a new resolver? - resolve_package_manifest(context, crate_id, dep_meta.manifest, &dependency_path)?; - } - Ok(()) -} - -fn resolve_workspace_manifest( - context: &mut Context, - mut local_package: Option, - manifest_path: PathBuf, - dir_path: &Path, - workspace: WorkspaceConfig, -) -> Result { - let members = workspace.members; - let mut packages = HashMap::new(); - - if members.is_empty() { - return Err(DependencyResolutionError::EmptyWorkspace { path: manifest_path }); - } - - for member in &members { - let member_path: PathBuf = dir_path.join(member); - let member_member_path = super::find_package_manifest(&member_path)?; - let member_manifest = super::manifest::parse(&member_member_path)?; - - match member_manifest { - Manifest::Package(inner) => { - let name: CrateName = inner - .package - .name - .parse() - .map_err(|_name| DependencyResolutionError::InvalidPackageName)?; - - if packages.insert(name.clone(), member_path).is_some() { - return Err(DependencyResolutionError::PackageCollision(name.into())); - } - - if local_package.is_none() && workspace.default_member.as_ref() == Some(member) { - local_package = Some(name.into()); - } - } - Manifest::Workspace(_) => { - return Err(DependencyResolutionError::MultipleWorkspace { - root: manifest_path, - member: member_member_path, - }) - } - } - } - - let local_package = match local_package { - Some(local_package) => { - local_package.parse().map_err(|_| DependencyResolutionError::InvalidPackageName)? - } - None => packages.keys().last().expect("non-empty packages").clone(), - }; - - let local_crate = packages - .remove(&local_package) - .ok_or_else(|| DependencyResolutionError::PackageNotFound(local_package.into()))?; - - let (entry_path, _crate_type) = super::lib_or_bin(local_crate)?; - let crate_id = create_local_crate(context, &entry_path, CrateType::Workspace); - - for (_, package_path) in packages.drain() { - let (entry_path, crate_type) = super::lib_or_bin(package_path)?; - create_non_local_crate(context, &entry_path, crate_type); - } - - Ok(crate_id) -} - -/// If the dependency is remote, download the dependency -/// and return the directory path along with the metadata -/// Needed to fill the CachedDep struct -/// -/// If it's a local path, the same applies, however it will not -/// be downloaded -fn cache_dep( - dep: &Dependency, - pkg_root: &Path, -) -> Result<(PathBuf, CachedDep), DependencyResolutionError> { - fn retrieve_meta( - dir_path: &Path, - remote: bool, - ) -> Result { - let (entry_path, crate_type) = super::lib_or_bin(dir_path)?; - let manifest_path = super::find_package_manifest(dir_path)?; - let manifest = super::manifest::parse(manifest_path)? - .to_package() - .ok_or(DependencyResolutionError::WorkspaceDependency)?; - Ok(CachedDep { entry_path, crate_type, manifest, remote }) - } - - match dep { - Dependency::Github { git, tag } => { - let dir_path = clone_git_repo(git, tag).map_err(DependencyResolutionError::GitError)?; - let meta = retrieve_meta(&dir_path, true)?; - Ok((dir_path, meta)) - } - Dependency::Path { path } => { - let dir_path = pkg_root.join(path); - let meta = retrieve_meta(&dir_path, false)?; - Ok((dir_path, meta)) - } - } -} diff --git a/crates/nargo_cli/tests/codegen-verifier.rs b/crates/nargo_cli/tests/codegen-verifier.rs index 3e4dc1dc745..f991f72b108 100644 --- a/crates/nargo_cli/tests/codegen-verifier.rs +++ b/crates/nargo_cli/tests/codegen-verifier.rs @@ -29,5 +29,9 @@ fn simple_verifier_codegen() { .success() .stdout(predicate::str::contains("Contract successfully created and located at")); - project_dir.child("contract").child("plonk_vk.sol").assert(predicate::path::is_file()); + project_dir + .child("contract") + .child("hello_world") + .child("plonk_vk.sol") + .assert(predicate::path::is_file()); } diff --git a/crates/nargo_cli/tests/hello_world.rs b/crates/nargo_cli/tests/hello_world.rs index 121f09f0ece..bc7022d1567 100644 --- a/crates/nargo_cli/tests/hello_world.rs +++ b/crates/nargo_cli/tests/hello_world.rs @@ -36,21 +36,20 @@ fn hello_world_example() { project_dir.child("Prover.toml").assert(predicate::path::is_file()); project_dir.child("Verifier.toml").assert(predicate::path::is_file()); - // `nargo prove p` - let proof_name = "p"; + // `nargo prove` project_dir.child("Prover.toml").write_str("x = 1\ny = 2").unwrap(); let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("prove").arg(proof_name); + cmd.arg("prove"); cmd.assert().success(); project_dir .child("proofs") - .child(format!("{proof_name}.proof")) + .child(format!("{project_name}.proof")) .assert(predicate::path::is_file()); // `nargo verify p` let mut cmd = Command::cargo_bin("nargo").unwrap(); - cmd.arg("verify").arg(proof_name); + cmd.arg("verify"); cmd.assert().success(); } diff --git a/crates/nargo_cli/tests/test_data/config.toml b/crates/nargo_cli/tests/test_data/config.toml index 88776ed03d2..6fe6c7897e1 100644 --- a/crates/nargo_cli/tests/test_data/config.toml +++ b/crates/nargo_cli/tests/test_data/config.toml @@ -2,4 +2,4 @@ exclude = [] # List of tests (as their directory name) expecting to fail: if the test pass, we report an error. -fail = ["brillig_assert_fail", "dep_impl_primitive"] +fail = ["brillig_assert_fail", "dep_impl_primitive", "workspace_fail", "workspace_missing_toml"] diff --git a/crates/nargo_cli/tests/test_data/workspace/crates/a/Prover.toml b/crates/nargo_cli/tests/test_data/workspace/crates/a/Prover.toml new file mode 100644 index 00000000000..465ef562de4 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace/crates/a/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "1" diff --git a/crates/nargo_cli/tests/test_data/workspace/crates/a/src/main.nr b/crates/nargo_cli/tests/test_data/workspace/crates/a/src/main.nr index 81847a9031d..550e5034a7b 100644 --- a/crates/nargo_cli/tests/test_data/workspace/crates/a/src/main.nr +++ b/crates/nargo_cli/tests/test_data/workspace/crates/a/src/main.nr @@ -1,11 +1,3 @@ fn main(x : Field, y : pub Field) { - assert(x != y); -} - -#[test] -fn a() { - main(1, 2); - - // Uncomment to make test fail - // main(1, 1); + assert(x == y); } diff --git a/crates/nargo_cli/tests/test_data/workspace/crates/b/Prover.toml b/crates/nargo_cli/tests/test_data/workspace/crates/b/Prover.toml new file mode 100644 index 00000000000..a0397e89477 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace/crates/b/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "0" diff --git a/crates/nargo_cli/tests/test_data/workspace/crates/b/src/main.nr b/crates/nargo_cli/tests/test_data/workspace/crates/b/src/main.nr index 512f99feeca..6e170de75fc 100644 --- a/crates/nargo_cli/tests/test_data/workspace/crates/b/src/main.nr +++ b/crates/nargo_cli/tests/test_data/workspace/crates/b/src/main.nr @@ -1,11 +1,3 @@ fn main(x : Field, y : pub Field) { assert(x != y); } - -#[test] -fn b() { - main(1, 2); - - // Uncomment to make test fail - // main(1, 1); -} diff --git a/crates/nargo_cli/tests/test_data/workspace_default_member/a/Prover.toml b/crates/nargo_cli/tests/test_data/workspace_default_member/a/Prover.toml new file mode 100644 index 00000000000..465ef562de4 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_default_member/a/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "1" diff --git a/crates/nargo_cli/tests/test_data/workspace_default_member/a/src/main.nr b/crates/nargo_cli/tests/test_data/workspace_default_member/a/src/main.nr index 206dc46d57a..550e5034a7b 100644 --- a/crates/nargo_cli/tests/test_data/workspace_default_member/a/src/main.nr +++ b/crates/nargo_cli/tests/test_data/workspace_default_member/a/src/main.nr @@ -1,11 +1,3 @@ fn main(x : Field, y : pub Field) { - assert(x != y); -} - -#[test] -fn test_main() { - main(1, 2); - - // Uncomment to make test fail - // main(1, 1); + assert(x == y); } diff --git a/crates/nargo_cli/tests/test_data/workspace_default_member/b/Nargo.toml b/crates/nargo_cli/tests/test_data/workspace_default_member/b/Nargo.toml new file mode 100644 index 00000000000..85c6119c62c --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_default_member/b/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "b" +authors = [""] +compiler_version = "0.8.0" + +[dependencies] diff --git a/crates/nargo_cli/tests/test_data/workspace_default_member/b/Prover.toml b/crates/nargo_cli/tests/test_data/workspace_default_member/b/Prover.toml new file mode 100644 index 00000000000..83fcd8678e7 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_default_member/b/Prover.toml @@ -0,0 +1,3 @@ +# Deliberately setting these to fail to prove this is NOT executed since a default is specified +x = "1" +y = "1" diff --git a/crates/nargo_cli/tests/test_data/workspace_default_member/b/src/main.nr b/crates/nargo_cli/tests/test_data/workspace_default_member/b/src/main.nr new file mode 100644 index 00000000000..6e170de75fc --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_default_member/b/src/main.nr @@ -0,0 +1,3 @@ +fn main(x : Field, y : pub Field) { + assert(x != y); +} diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/Nargo.toml b/crates/nargo_cli/tests/test_data/workspace_fail/Nargo.toml new file mode 100644 index 00000000000..36db098686f --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/Nargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["crates/a", "crates/b"] diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/Nargo.toml b/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/Nargo.toml new file mode 100644 index 00000000000..5ff1a743e3d --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "a" +authors = [""] +compiler_version = "0.8.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/Prover.toml b/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/Prover.toml new file mode 100644 index 00000000000..b76c88bf536 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/Prover.toml @@ -0,0 +1,3 @@ +# Deliberately setting these to fail to prove this is being executed +x = "1" +y = "2" diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/src/main.nr b/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/src/main.nr new file mode 100644 index 00000000000..550e5034a7b --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/crates/a/src/main.nr @@ -0,0 +1,3 @@ +fn main(x : Field, y : pub Field) { + assert(x == y); +} diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/Nargo.toml b/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/Nargo.toml new file mode 100644 index 00000000000..8ae69a781eb --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "b" +authors = [""] +compiler_version = "0.8.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/Prover.toml b/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/Prover.toml new file mode 100644 index 00000000000..a0397e89477 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "0" diff --git a/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/src/main.nr b/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/src/main.nr new file mode 100644 index 00000000000..6e170de75fc --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_fail/crates/b/src/main.nr @@ -0,0 +1,3 @@ +fn main(x : Field, y : pub Field) { + assert(x != y); +} diff --git a/crates/nargo_cli/tests/test_data/workspace_missing_toml/Nargo.toml b/crates/nargo_cli/tests/test_data/workspace_missing_toml/Nargo.toml new file mode 100644 index 00000000000..36db098686f --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_missing_toml/Nargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["crates/a", "crates/b"] diff --git a/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/a/Prover.toml b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/a/Prover.toml new file mode 100644 index 00000000000..465ef562de4 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/a/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "1" diff --git a/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/a/src/main.nr b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/a/src/main.nr new file mode 100644 index 00000000000..550e5034a7b --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/a/src/main.nr @@ -0,0 +1,3 @@ +fn main(x : Field, y : pub Field) { + assert(x == y); +} diff --git a/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/Nargo.toml b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/Nargo.toml new file mode 100644 index 00000000000..8ae69a781eb --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "b" +authors = [""] +compiler_version = "0.8.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/Prover.toml b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/Prover.toml new file mode 100644 index 00000000000..a0397e89477 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "0" diff --git a/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/src/main.nr b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/src/main.nr new file mode 100644 index 00000000000..6e170de75fc --- /dev/null +++ b/crates/nargo_cli/tests/test_data/workspace_missing_toml/crates/b/src/main.nr @@ -0,0 +1,3 @@ +fn main(x : Field, y : pub Field) { + assert(x != y); +} diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index f2537bb88fe..c0957313f69 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -87,10 +87,12 @@ pub fn create_non_local_crate( } /// Adds a edge in the crate graph for two crates -pub fn add_dep(context: &mut Context, this_crate: CrateId, depends_on: CrateId, crate_name: &str) { - let crate_name = - crate_name.parse().expect("crate name contains blacklisted characters, please remove"); - +pub fn add_dep( + context: &mut Context, + this_crate: CrateId, + depends_on: CrateId, + crate_name: CrateName, +) { // Cannot depend on a binary if context.crate_graph.crate_type(depends_on) == CrateType::Binary { panic!("crates cannot depend on binaries. {crate_name:?} is a binary crate") @@ -142,15 +144,7 @@ pub fn check_crate( propagate_dep(context, std_crate, &std_crate_name.parse().unwrap()); let mut errors = vec![]; - match context.crate_graph.crate_type(crate_id) { - CrateType::Workspace => { - let keys: Vec<_> = context.crate_graph.iter_keys().collect(); // avoid borrow checker - for crate_id in keys { - CrateDefMap::collect_defs(crate_id, context, &mut errors); - } - } - _ => CrateDefMap::collect_defs(crate_id, context, &mut errors), - } + CrateDefMap::collect_defs(crate_id, context, &mut errors); if has_errors(&errors, deny_warnings) { Err(errors) diff --git a/crates/noirc_frontend/src/graph/mod.rs b/crates/noirc_frontend/src/graph/mod.rs index 7ebfbae4817..af9216071e6 100644 --- a/crates/noirc_frontend/src/graph/mod.rs +++ b/crates/noirc_frontend/src/graph/mod.rs @@ -4,7 +4,7 @@ // This version is also simpler due to not having macro_defs or proc_macros // XXX: Edition may be reintroduced or some sort of versioning -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; use fm::FileId; use rustc_hash::{FxHashMap, FxHashSet}; @@ -26,14 +26,25 @@ impl CrateId { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct CrateName(SmolStr); +impl Display for CrateName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + impl From for String { fn from(crate_name: CrateName) -> Self { crate_name.0.into() } } +impl From<&CrateName> for String { + fn from(crate_name: &CrateName) -> Self { + crate_name.0.clone().into() + } +} /// Creates a new CrateName rejecting any crate name that /// has a character on the blacklist. @@ -66,7 +77,6 @@ pub const CHARACTER_BLACK_LIST: [char; 1] = ['-']; pub enum CrateType { Library, Binary, - Workspace, } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index 5937f57a8c7..d6f98e112af 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -69,10 +69,7 @@ impl Context { // Check the crate type // We don't panic here to allow users to `evaluate` libraries which will do nothing - if matches!( - self.crate_graph[*crate_id].crate_type, - CrateType::Binary | CrateType::Workspace - ) { + if matches!(self.crate_graph[*crate_id].crate_type, CrateType::Binary) { // All Binaries should have a main function local_crate.main_function() } else { @@ -112,19 +109,6 @@ impl Context { .collect() } - pub fn get_all_test_functions_in_workspace_matching( - &self, - pattern: &str, - ) -> Vec<(String, FuncId)> { - let mut tests = Vec::new(); - - for crate_id in self.crate_graph.iter_keys() { - tests.extend(self.get_all_test_functions_in_crate_matching(&crate_id, pattern)); - } - - tests - } - /// Return a Vec of all `contract` declarations in the source code and the functions they contain pub fn get_all_contracts(&self, crate_id: &CrateId) -> Vec { self.def_map(crate_id)