From c5ca32ae729ea61918748b33417ced563aef071d Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 12 Feb 2022 09:49:18 +1100 Subject: [PATCH 01/14] Add `--output-directory` to `forc build` and related commands This is a start at addressing #765. Users can now specify a directory in which all compiler output artifacts will be placed by `forc` upon `forc build`. When unspecified, `--output-directory` defaults to `/out/`. More specifically, artifacts are placed in `/out/` where the `profile` subdirectory is in anticipation of support for multiple different build profiles. Currently "debug" is assumed. --- forc/src/cli/commands/build.rs | 10 +++++++++ forc/src/cli/commands/deploy.rs | 5 +++++ forc/src/cli/commands/run.rs | 6 ++++++ forc/src/ops/forc_build.rs | 37 ++++++++++++++++++++++++++------- forc/src/ops/forc_deploy.rs | 2 ++ forc/src/ops/forc_fmt.rs | 1 + forc/src/ops/forc_run.rs | 1 + 7 files changed, 54 insertions(+), 8 deletions(-) diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index 46906c8ecbd..f9bb8314374 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -2,6 +2,11 @@ use crate::ops::forc_build; use structopt::{self, StructOpt}; /// Compile the current or target project. +/// +/// The output produced will depend on the project's program type. Building script, predicate and +/// contract projects will produce their bytecode in binary format `.bin`. Building +/// contracts will *also* produce the contract's ABI in JSON format `-abi.json`. +/// Library projects will be type-checked, but no output is produced. #[derive(Debug, StructOpt)] pub struct Command { /// Path to the project, if not specified, current working directory will be used. @@ -32,6 +37,11 @@ pub struct Command { /// Silent mode. Don't output any warnings or errors to the command line. #[structopt(long = "silent", short = "s")] pub silent_mode: bool, + /// The directory in which the sway compiler output artifacts are placed. + /// + /// By default, this is `/out`. + #[structopt(long)] + pub output_directory: Option, } pub(crate) fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/deploy.rs b/forc/src/cli/commands/deploy.rs index 57e4d64c7c2..c4b29e373e6 100644 --- a/forc/src/cli/commands/deploy.rs +++ b/forc/src/cli/commands/deploy.rs @@ -33,6 +33,11 @@ pub struct Command { /// Silent mode. Don't output any warnings or errors to the command line. #[structopt(long = "silent", short = "s")] pub silent_mode: bool, + /// The directory in which the sway compiler output artifacts are placed. + /// + /// By default, this is `/out`. + #[structopt(long)] + pub output_directory: Option, } pub(crate) async fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/run.rs b/forc/src/cli/commands/run.rs index 59831c98409..3a6db73d112 100644 --- a/forc/src/cli/commands/run.rs +++ b/forc/src/cli/commands/run.rs @@ -61,6 +61,12 @@ pub struct Command { /// 32-byte contract ID that will be called during the transaction. #[structopt(long = "contract")] pub contract: Option>, + + /// The directory in which the sway compiler output artifacts are placed. + /// + /// By default, this is `/out`. + #[structopt(long)] + pub output_directory: Option, } pub(crate) async fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 1cedb3c598c..2204a81b1a7 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -22,15 +22,11 @@ use anyhow::Result; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; -pub fn build(command: BuildCommand) -> Result, String> { - // find manifest directory, even if in subdirectory - let this_dir = if let Some(ref path) = command.path { - PathBuf::from(path) - } else { - std::env::current_dir().map_err(|e| format!("{:?}", e))? - }; +const DEFAULT_OUTPUT_DIRECTORY: &str = "out"; +pub fn build(command: BuildCommand) -> Result, String> { let BuildCommand { + path, binary_outfile, use_ir, debug_outfile, @@ -39,8 +35,16 @@ pub fn build(command: BuildCommand) -> Result, String> { print_ir, offline_mode, silent_mode, - .. + output_directory, } = command; + + // find manifest directory, even if in subdirectory + let this_dir = if let Some(ref path) = path { + PathBuf::from(path) + } else { + std::env::current_dir().map_err(|e| format!("{:?}", e))? + }; + let manifest_dir = match find_manifest_dir(&this_dir) { Some(dir) => dir, None => { @@ -131,6 +135,23 @@ pub fn build(command: BuildCommand) -> Result, String> { .map_err(|e| e.to_string())?; } + // TODO: We may support custom build profiles in the future. + let profile = "debug"; + + // Create the output directory for build artifacts. + let output_dir = output_directory + .map(PathBuf::from) + .unwrap_or_else(|| manifest_dir.join(DEFAULT_OUTPUT_DIRECTORY).join(profile)); + if !output_dir.exists() { + fs::create_dir_all(&output_dir).map_err(|e| e.to_string())?; + } + + // Place build artifacts into the output directory. + let bin_path = output_dir + .join(&manifest.project.name) + .with_extension("bin"); + std::fs::write(&bin_path, main.as_slice()).map_err(|e| e.to_string())?; + println!(" Bytecode size is {} bytes.", main.len()); Ok(main) diff --git a/forc/src/ops/forc_deploy.rs b/forc/src/ops/forc_deploy.rs index 9b474938e16..5ffe0de230c 100644 --- a/forc/src/ops/forc_deploy.rs +++ b/forc/src/ops/forc_deploy.rs @@ -29,6 +29,7 @@ pub async fn deploy(command: DeployCommand) -> Result Result Result<(), FormatError> { debug_outfile: None, offline_mode: false, silent_mode: false, + output_directory: None, }; match forc_build::build(build_command) { diff --git a/forc/src/ops/forc_run.rs b/forc/src/ops/forc_run.rs index e42a4af68b4..c86f9e95af3 100644 --- a/forc/src/ops/forc_run.rs +++ b/forc/src/ops/forc_run.rs @@ -47,6 +47,7 @@ pub async fn run(command: RunCommand) -> Result<(), CliError> { debug_outfile: command.debug_outfile, offline_mode: false, silent_mode: command.silent_mode, + output_directory: command.output_directory, }; let compiled_script = forc_build::build(build_command)?; From c9bd34ad4933a7f06629464035c677891f127bd2 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 14 Feb 2022 23:22:57 +1100 Subject: [PATCH 02/14] Add `out` directory to Sway repo `.gitignore` for forc examples --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0d645a96019..218dac2eb0a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# Forc's build directory +out From 91911911f26d14fdd004a467e88578e077c44ad5 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 14 Feb 2022 23:42:34 +1100 Subject: [PATCH 03/14] Update e2e test harness for addition of `--output-directory` --- test/src/e2e_vm_tests/harness.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 1de31d4ecc6..52ea38c9b01 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -31,6 +31,7 @@ pub(crate) fn deploy_contract(file_name: &str) -> ContractId { debug_outfile: None, offline_mode: false, silent_mode: !verbose, + output_directory: None, })) .unwrap() } @@ -66,6 +67,7 @@ pub(crate) fn runs_on_node(file_name: &str, contract_ids: &[fuel_tx::ContractId] silent_mode: !verbose, pretty_print: false, contract: Some(contracts), + output_directory: None, }; tokio::runtime::Runtime::new() .unwrap() @@ -133,6 +135,7 @@ pub(crate) fn compile_to_bytes(file_name: &str) -> Result, String> { debug_outfile: None, offline_mode: false, silent_mode: !verbose, + output_directory: None, }) } From 507488cb770490cccd454841a2d3bc2db12007b2 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 18:42:58 +1100 Subject: [PATCH 04/14] sway-core: Expose inter-compilation steps for use in forc This refactors the bodies of the `compile_to_asm` and `compile_to_bytecode` functions into `ast_to_asm` and `asm_to_bytecode` respectively. This allows `forc` to compile the AST (for producing the ABI JSON) and then re-use the compiled AST to complete the rest of compilation to bytecode in the same pass. --- sway-core/src/lib.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index c67990cd484..89d20e858d7 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -369,7 +369,14 @@ pub fn compile_to_asm( build_config: BuildConfig, dependency_graph: &mut HashMap>, ) -> CompilationResult { - match compile_to_ast(input, initial_namespace, &build_config, dependency_graph) { + let ast_res = compile_to_ast(input, initial_namespace, &build_config, dependency_graph); + ast_to_asm(ast_res, &build_config) +} + +/// Given an AST compilation result, compile to a [CompilationResult] which contains the asm in +/// opcode form (not raw bytes/bytecode). +pub fn ast_to_asm(ast_res: CompileAstResult, build_config: &BuildConfig) -> CompilationResult { + match ast_res { CompileAstResult::Failure { warnings, errors } => { CompilationResult::Failure { warnings, errors } } @@ -383,9 +390,9 @@ pub fn compile_to_asm( TreeType::Contract | TreeType::Script | TreeType::Predicate => { let asm = check!( if build_config.use_ir { - compile_ast_to_ir_to_asm(*parse_tree, tree_type, &build_config) + compile_ast_to_ir_to_asm(*parse_tree, tree_type, build_config) } else { - compile_ast_to_asm(*parse_tree, &build_config) + compile_ast_to_asm(*parse_tree, build_config) }, return CompilationResult::Failure { errors, warnings }, warnings, @@ -510,7 +517,17 @@ pub fn compile_to_bytecode( dependency_graph: &mut HashMap>, source_map: &mut SourceMap, ) -> BytecodeCompilationResult { - match compile_to_asm(input, initial_namespace, build_config, dependency_graph) { + let asm_res = compile_to_asm(input, initial_namespace, build_config, dependency_graph); + asm_to_bytecode(asm_res, source_map) +} + +/// Given a [CompilationResult] containing the assembly (opcodes), compile to a +/// [BytecodeCompilationResult] which contains the asm in bytecode form. +pub fn asm_to_bytecode( + asm_res: CompilationResult, + source_map: &mut SourceMap, +) -> BytecodeCompilationResult { + match asm_res { CompilationResult::Success { mut asm, mut warnings, From 943356693e7f06a52e7f0340447b38bdfb5b9fdc Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 15:13:40 +1100 Subject: [PATCH 05/14] Add ABI JSON output to `forc build` This updates `forc build` and related commands to compile the JSON ABI and write it to a `-abi.json` file in the output directory. --- forc/src/ops/forc_abi_json.rs | 17 ++-- forc/src/ops/forc_build.rs | 150 +++++++++++++++++++++------------- forc/src/utils/helpers.rs | 12 +-- 3 files changed, 108 insertions(+), 71 deletions(-) diff --git a/forc/src/ops/forc_abi_json.rs b/forc/src/ops/forc_abi_json.rs index a49a9baa85f..97b5e301343 100644 --- a/forc/src/ops/forc_abi_json.rs +++ b/forc/src/ops/forc_abi_json.rs @@ -271,18 +271,23 @@ fn compile_library( let errors = vec![]; match tree_type { TreeType::Library { name } => { - print_on_success(silent_mode, proj_name, warnings, TreeType::Library { name }); + print_on_success( + silent_mode, + proj_name, + &warnings, + TreeType::Library { name }, + ); let json_abi = generate_json_abi(&Some(*parse_tree.clone())); Ok((parse_tree.get_namespace_ref(), json_abi)) } _ => { - print_on_failure(silent_mode, warnings, errors); + print_on_failure(silent_mode, &warnings, &errors); Err(format!("Failed to compile {}", proj_name)) } } } CompileAstResult::Failure { warnings, errors } => { - print_on_failure(silent_mode, warnings, errors); + print_on_failure(silent_mode, &warnings, &errors); Err(format!("Failed to compile {}", proj_name)) } } @@ -306,18 +311,18 @@ fn compile( let errors = vec![]; match tree_type { TreeType::Library { .. } => { - print_on_failure(silent_mode, warnings, errors); + print_on_failure(silent_mode, &warnings, &errors); Err(format!("Failed to compile {}", proj_name)) } typ => { - print_on_success(silent_mode, proj_name, warnings, typ); + print_on_success(silent_mode, proj_name, &warnings, typ); let json_abi = generate_json_abi(&Some(*parse_tree)); Ok(json_abi) } } } CompileAstResult::Failure { warnings, errors } => { - print_on_failure(silent_mode, warnings, errors); + print_on_failure(silent_mode, &warnings, &errors); Err(format!("Failed to compile {}", proj_name)) } } diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 2204a81b1a7..081c505aaf9 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -11,12 +11,13 @@ use std::fs::{self, File}; use std::io::Write; use std::sync::Arc; use sway_core::{FinalizedAsm, TreeType}; -use sway_utils::{constants, find_manifest_dir, MANIFEST_FILE_NAME}; +use sway_utils::{find_manifest_dir, MANIFEST_FILE_NAME}; use sway_core::{ create_module, source_map::SourceMap, BuildConfig, BytecodeCompilationResult, - CompilationResult, NamespaceRef, NamespaceWrapper, + CompilationResult, CompileAstResult, NamespaceRef, NamespaceWrapper, TypedParseTree, }; +use sway_types::JsonABI; use anyhow::Result; use std::collections::{HashMap, HashSet}; @@ -57,19 +58,8 @@ pub fn build(command: BuildCommand) -> Result, String> { }; let mut manifest = read_manifest(&manifest_dir)?; - - let main_path = { - let mut code_dir = manifest_dir.clone(); - code_dir.push(constants::SRC_DIR); - code_dir.push(&manifest.project.entry); - code_dir - }; - let mut file_path = manifest_dir.clone(); - file_path.pop(); - let file_name = match main_path.strip_prefix(file_path.clone()) { - Ok(o) => o, - Err(err) => return Err(err.to_string()), - }; + let main_path = find_main_path(&manifest_dir, &manifest); + let file_name = find_file_name(&manifest_dir, &main_path)?; let build_config = BuildConfig::root_from_file_name_and_manifest_path( file_name.to_path_buf(), @@ -84,10 +74,11 @@ pub fn build(command: BuildCommand) -> Result, String> { let namespace = create_module(); let mut source_map = SourceMap::new(); + let mut json_abi = vec![]; if let Some(ref mut deps) = manifest.dependencies { for (dependency_name, dependency_details) in deps.iter_mut() { - compile_dependency_lib( + let dep_json_abi = compile_dependency_lib( &this_dir, dependency_name, dependency_details, @@ -96,6 +87,7 @@ pub fn build(command: BuildCommand) -> Result, String> { silent_mode, offline_mode, )?; + json_abi.extend(dep_json_abi); source_map.insert_dependency(match dependency_details { Dependency::Simple(..) => { @@ -112,7 +104,7 @@ pub fn build(command: BuildCommand) -> Result, String> { // now, compile this program with all of its dependencies let main_file = get_main_file(&manifest, &manifest_dir)?; - let main = compile( + let (main, main_json_abi) = compile( main_file, &manifest.project.name, namespace, @@ -122,6 +114,8 @@ pub fn build(command: BuildCommand) -> Result, String> { silent_mode, )?; + json_abi.extend(main_json_abi); + if let Some(outfile) = binary_outfile { let mut file = File::create(outfile).map_err(|e| e.to_string())?; file.write_all(main.as_slice()).map_err(|e| e.to_string())?; @@ -151,14 +145,23 @@ pub fn build(command: BuildCommand) -> Result, String> { .join(&manifest.project.name) .with_extension("bin"); std::fs::write(&bin_path, main.as_slice()).map_err(|e| e.to_string())?; + if !json_abi.is_empty() { + let json_abi_stem = format!("{}-abi", manifest.project.name); + let json_abi_path = output_dir.join(&json_abi_stem).with_extension("json"); + let file = File::create(json_abi_path).map_err(|e| e.to_string())?; + serde_json::to_writer(&file, &json_abi).map_err(|e| e.to_string())?; + } println!(" Bytecode size is {} bytes.", main.len()); Ok(main) } -/// Takes a dependency and returns a namespace of exported things from that dependency -/// trait implementations are included as well +/// Takes a dependency and returns a namespace of exported things from that dependency including +/// trait implementations. +/// +/// Also returns the JSON ABI of the library. This may be empty in the case that no `abi` was +/// exposed. fn compile_dependency_lib<'manifest>( project_file_path: &Path, dependency_name: &'manifest str, @@ -167,7 +170,7 @@ fn compile_dependency_lib<'manifest>( dependency_graph: &mut HashMap>, silent_mode: bool, offline_mode: bool, -) -> Result<(), String> { +) -> Result { let mut details = match dependency_lib { Dependency::Simple(..) => { return Err( @@ -262,7 +265,7 @@ fn compile_dependency_lib<'manifest>( let main_file = get_main_file(&manifest_of_dep, &manifest_dir)?; - let compiled = compile_library( + let (compiled, json_abi) = compile_library( main_file, &manifest_of_dep.project.name, dep_namespace, @@ -273,8 +276,7 @@ fn compile_dependency_lib<'manifest>( namespace.insert_module_ref(dependency_name.to_string(), compiled); - // nothing is returned from this method since it mutates the hashmaps it was given - Ok(()) + Ok(json_abi) } fn compile_library( @@ -284,27 +286,32 @@ fn compile_library( build_config: BuildConfig, dependency_graph: &mut HashMap>, silent_mode: bool, -) -> Result { - let res = sway_core::compile_to_asm(source, namespace, build_config, dependency_graph); +) -> Result<(NamespaceRef, JsonABI), String> { + let res = sway_core::compile_to_ast(source, namespace, &build_config, dependency_graph); match res { - CompilationResult::Library { - namespace, + CompileAstResult::Success { + parse_tree, + tree_type, warnings, - .. } => { - print_on_success_library(silent_mode, proj_name, warnings); - Ok(namespace) + let errors = vec![]; + match tree_type { + TreeType::Library { .. } => { + print_on_success_library(silent_mode, proj_name, &warnings); + let json_abi = generate_json_abi(&*parse_tree); + let namespace = parse_tree.get_namespace_ref(); + Ok((namespace, json_abi)) + } + _ => { + print_on_failure(silent_mode, &warnings, &errors); + Err(format!("Failed to compile {}", proj_name)) + } + } } - CompilationResult::Failure { errors, warnings } => { - print_on_failure(silent_mode, warnings, errors); + CompileAstResult::Failure { warnings, errors } => { + print_on_failure(silent_mode, &warnings, &errors); Err(format!("Failed to compile {}", proj_name)) } - _ => { - return Err(format!( - "Project \"{}\" was included as a dependency but it is not a library.", - proj_name - )) - } } } @@ -316,29 +323,49 @@ fn compile( dependency_graph: &mut HashMap>, source_map: &mut SourceMap, silent_mode: bool, -) -> Result, String> { - let res = sway_core::compile_to_bytecode( - source, - namespace, - build_config, - dependency_graph, - source_map, - ); +) -> Result<(Vec, JsonABI), String> { + let ast_res = + sway_core::compile_to_ast(source.clone(), namespace, &build_config, dependency_graph); + let json_abi = match &ast_res { + CompileAstResult::Success { + parse_tree, + tree_type, + warnings, + } => match tree_type { + TreeType::Library { .. } => { + print_on_failure(silent_mode, warnings, &[]); + return Err(format!("Failed to compile {}", proj_name)); + } + typ => { + print_on_success(silent_mode, proj_name, warnings, typ.clone()); + let json_abi = generate_json_abi(&*parse_tree); + json_abi + } + }, + CompileAstResult::Failure { warnings, errors } => { + print_on_failure(silent_mode, warnings, errors); + return Err(format!("Failed to compile {}", proj_name)); + } + }; - match res { + let asm_res = sway_core::ast_to_asm(ast_res, &build_config); + let bc_res = sway_core::asm_to_bytecode(asm_res, source_map); + + let bytes = match bc_res { BytecodeCompilationResult::Success { bytes, warnings } => { - print_on_success(silent_mode, proj_name, warnings, TreeType::Script {}); - Ok(bytes) + print_on_success(silent_mode, proj_name, &warnings, TreeType::Script {}); + bytes } BytecodeCompilationResult::Library { warnings } => { - print_on_success_library(silent_mode, proj_name, warnings); - Ok(vec![]) + print_on_success_library(silent_mode, proj_name, &warnings); + vec![] } BytecodeCompilationResult::Failure { errors, warnings } => { - print_on_failure(silent_mode, warnings, errors); - Err(format!("Failed to compile {}", proj_name)) + print_on_failure(silent_mode, &warnings, &errors); + return Err(format!("Failed to compile {}", proj_name)); } - } + }; + Ok((bytes, json_abi)) } fn compile_to_asm( @@ -352,16 +379,25 @@ fn compile_to_asm( let res = sway_core::compile_to_asm(source, namespace, build_config, dependency_graph); match res { CompilationResult::Success { asm, warnings } => { - print_on_success(silent_mode, proj_name, warnings, TreeType::Script {}); + print_on_success(silent_mode, proj_name, &warnings, TreeType::Script {}); Ok(asm) } CompilationResult::Library { warnings, .. } => { - print_on_success_library(silent_mode, proj_name, warnings); + print_on_success_library(silent_mode, proj_name, &warnings); Ok(FinalizedAsm::Library) } CompilationResult::Failure { errors, warnings } => { - print_on_failure(silent_mode, warnings, errors); + print_on_failure(silent_mode, &warnings, &errors); return Err(format!("Failed to compile {}", proj_name)); } } } + +fn generate_json_abi(ast: &TypedParseTree) -> JsonABI { + match ast { + TypedParseTree::Contract { abi_entries, .. } => { + abi_entries.iter().map(|x| x.generate_json_abi()).collect() + } + _ => vec![], + } +} diff --git a/forc/src/utils/helpers.rs b/forc/src/utils/helpers.rs index 8271edf4e8f..e09396254cb 100644 --- a/forc/src/utils/helpers.rs +++ b/forc/src/utils/helpers.rs @@ -73,7 +73,7 @@ pub fn get_main_file(manifest_of_dep: &Manifest, manifest_dir: &Path) -> Result< pub fn print_on_success( silent_mode: bool, proj_name: &str, - warnings: Vec, + warnings: &[CompileWarning], tree_type: TreeType, ) { let type_str = match tree_type { @@ -104,7 +104,7 @@ pub fn print_on_success( } } -pub fn print_on_success_library(silent_mode: bool, proj_name: &str, warnings: Vec) { +pub fn print_on_success_library(silent_mode: bool, proj_name: &str, warnings: &[CompileWarning]) { if !silent_mode { warnings.iter().for_each(format_warning); } @@ -125,16 +125,12 @@ pub fn print_on_success_library(silent_mode: bool, proj_name: &str, warnings: Ve } } -pub fn print_on_failure( - silent_mode: bool, - warnings: Vec, - errors: Vec, -) { +pub fn print_on_failure(silent_mode: bool, warnings: &[CompileWarning], errors: &[CompileError]) { let e_len = errors.len(); if !silent_mode { warnings.iter().for_each(format_warning); - errors.into_iter().for_each(|error| format_err(&error)); + errors.iter().for_each(|error| format_err(&error)); } println_red_err(&format!( From 1c105873e62b5e5241ccd62b2cc73796ee9ea09c Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 19:14:26 +1100 Subject: [PATCH 06/14] Implement `forc json-abi` in terms of `forc build` under the hood We may want to consider removing the dedicated `forc json-abi` command altogether in favour of a `--print-json-abi` flag to `forc build` to make it clear that the output comes directly from `forc build`. --- forc/src/cli/commands/build.rs | 2 +- forc/src/ops/forc_abi_json.rs | 338 +------------------------------ forc/src/ops/forc_build.rs | 4 +- forc/src/ops/forc_deploy.rs | 2 +- forc/src/ops/forc_run.rs | 2 +- test/src/e2e_vm_tests/harness.rs | 1 + 6 files changed, 17 insertions(+), 332 deletions(-) diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index f9bb8314374..a36a981d9cb 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -7,7 +7,7 @@ use structopt::{self, StructOpt}; /// contract projects will produce their bytecode in binary format `.bin`. Building /// contracts will *also* produce the contract's ABI in JSON format `-abi.json`. /// Library projects will be type-checked, but no output is produced. -#[derive(Debug, StructOpt)] +#[derive(Debug, Default, StructOpt)] pub struct Command { /// Path to the project, if not specified, current working directory will be used. #[structopt(short, long)] diff --git a/forc/src/ops/forc_abi_json.rs b/forc/src/ops/forc_abi_json.rs index 97b5e301343..49f05268e62 100644 --- a/forc/src/ops/forc_abi_json.rs +++ b/forc/src/ops/forc_abi_json.rs @@ -1,338 +1,22 @@ -use crate::utils::dependency::{Dependency, DependencyDetails}; -use crate::{ - cli::JsonAbiCommand, - utils::dependency, - utils::helpers::{ - find_file_name, find_main_path, get_main_file, print_on_failure, print_on_success, - read_manifest, - }, -}; +use crate::cli::{BuildCommand, JsonAbiCommand}; -use sway_types::{Function, JsonABI}; -use sway_utils::{find_manifest_dir, MANIFEST_FILE_NAME}; - -use anyhow::Result; use serde_json::{json, Value}; -use std::collections::{HashMap, HashSet}; use std::fs::File; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use sway_core::{ - create_module, BuildConfig, CompileAstResult, NamespaceRef, NamespaceWrapper, TreeType, - TypedParseTree, -}; pub fn build(command: JsonAbiCommand) -> Result { - // find manifest directory, even if in subdirectory - let this_dir = if let Some(ref path) = command.path { - PathBuf::from(path) - } else { - std::env::current_dir().map_err(|e| format!("{:?}", e))? - }; - - let JsonAbiCommand { - json_outfile, - offline_mode, - silent_mode, - .. - } = command; - - let manifest_dir = match find_manifest_dir(&this_dir) { - Some(dir) => dir, - None => { - return Err(format!( - "could not find `{}` in `{}` or any parent directory", - MANIFEST_FILE_NAME, - this_dir.display() - )) - } + let build_command = BuildCommand { + path: command.path, + offline_mode: command.offline_mode, + silent_mode: command.silent_mode, + ..Default::default() }; - let mut manifest = read_manifest(&manifest_dir)?; - let main_path = find_main_path(&manifest_dir, &manifest); - let file_name = find_file_name(&manifest_dir, &main_path)?; - - let build_config = BuildConfig::root_from_file_name_and_manifest_path( - file_name.to_owned(), - manifest_dir.clone(), - ); - let mut dependency_graph = HashMap::new(); - let mut json_abi = vec![]; - - let namespace = create_module(); - if let Some(ref mut deps) = manifest.dependencies { - for (dependency_name, dependency_details) in deps.iter_mut() { - // Check if dependency is a git-based dependency. - let dep = match dependency_details { - Dependency::Simple(..) => { - return Err( - "Not yet implemented: Simple version-spec dependencies require a registry." - .into(), - ); - } - Dependency::Detailed(dep_details) => dep_details, - }; - - // Download a non-local dependency if the `git` property is set in this dependency. - if let Some(git) = &dep.git { - let fully_qualified_dep_name = format!("{}-{}", dependency_name, git); - let downloaded_dep_path = match dependency::download_github_dep( - &fully_qualified_dep_name, - git, - &dep.branch, - &dep.version, - offline_mode.into(), - ) { - Ok(path) => path, - Err(e) => { - return Err(format!( - "Couldn't download dependency ({:?}): {:?}", - dependency_name, e - )) - } - }; - - // Mutate this dependency's path to hold the newly downloaded dependency's path. - dep.path = Some(downloaded_dep_path); - } - - json_abi.append(&mut compile_dependency_lib( - &this_dir, - dependency_name, - dependency_details, - namespace, - &mut dependency_graph, - silent_mode, - offline_mode, - )?); - } - } - - // now, compile this program with all of its dependencies - let main_file = get_main_file(&manifest, &manifest_dir)?; - - let mut res = compile( - main_file, - &manifest.project.name, - namespace, - build_config, - &mut dependency_graph, - silent_mode, - )?; - json_abi.append(&mut res); - - let output_json = json!(json_abi); - - if let Some(outfile) = json_outfile { + let (_bytes, json_abi) = crate::ops::forc_build::build(build_command)?; + let json_abi = json!(json_abi); + if let Some(outfile) = command.json_outfile { let file = File::create(outfile).map_err(|e| e.to_string())?; - serde_json::to_writer(&file, &output_json).map_err(|e| e.to_string())?; + serde_json::to_writer(&file, &json_abi).map_err(|e| e.to_string())?; } else { - println!("{}", output_json); - } - - Ok(output_json) -} - -/// Takes a dependency and returns a namespace of exported things from that dependency -/// trait implementations are included as well -fn compile_dependency_lib<'manifest>( - project_file_path: &Path, - dependency_name: &'manifest str, - dependency_lib: &mut Dependency, - namespace: NamespaceRef, - dependency_graph: &mut HashMap>, - silent_mode: bool, - offline_mode: bool, -) -> Result, String> { - let mut details = match dependency_lib { - Dependency::Simple(..) => { - return Err( - "Not yet implemented: Simple version-spec dependencies require a registry.".into(), - ) - } - Dependency::Detailed(ref mut details) => details, - }; - // Download a non-local dependency if the `git` property is set in this dependency. - if let Some(ref git) = details.git { - // the qualified name of the dependency includes its source and some metadata to prevent - // conflating dependencies from different sources - let fully_qualified_dep_name = format!("{}-{}", dependency_name, git); - let downloaded_dep_path = match dependency::download_github_dep( - &fully_qualified_dep_name, - git, - &details.branch, - &details.version, - offline_mode.into(), - ) { - Ok(path) => path, - Err(e) => { - return Err(format!( - "Couldn't download dependency ({:?}): {:?}", - dependency_name, e - )) - } - }; - - // Mutate this dependency's path to hold the newly downloaded dependency's path. - details.path = Some(downloaded_dep_path); + println!("{}", json_abi); } - let dep_path = match dependency_lib { - Dependency::Simple(..) => { - return Err( - "Not yet implemented: Simple version-spec dependencies require a registry.".into(), - ) - } - Dependency::Detailed(DependencyDetails { path, .. }) => path, - }; - - let dep_path = - match dep_path { - Some(p) => p, - None => return Err( - "Only simple path imports are supported right now. Please supply a path relative \ - to the manifest file." - .into(), - ), - }; - - // dependency paths are relative to the path of the project being compiled - let mut project_path = PathBuf::from(project_file_path); - project_path.push(dep_path); - - // compile the dependencies of this dependency - // this should detect circular dependencies - let manifest_dir = match find_manifest_dir(&project_path) { - Some(o) => o, - None => { - return Err(format!( - "Manifest not found for dependency {:?}.", - project_path - )) - } - }; - let mut manifest_of_dep = read_manifest(&manifest_dir)?; - let main_path = find_main_path(&manifest_dir, &manifest_of_dep); - let file_name = find_file_name(&manifest_dir, &main_path)?; - - let build_config = BuildConfig::root_from_file_name_and_manifest_path( - file_name.to_owned(), - manifest_dir.clone(), - ); - - let dep_namespace = create_module(); - // The part below here is just a massive shortcut to get the standard library working - if let Some(ref mut deps) = manifest_of_dep.dependencies { - for ref mut dep in deps { - // to do this properly, iterate over list of dependencies make sure there are no - // circular dependencies - compile_dependency_lib( - &manifest_dir, - dep.0, - dep.1, - dep_namespace, - dependency_graph, - silent_mode, - offline_mode, - )?; - } - } - - let main_file = get_main_file(&manifest_of_dep, &manifest_dir)?; - - let (compiled, json_abi) = compile_library( - main_file, - &manifest_of_dep.project.name, - dep_namespace, - build_config, - dependency_graph, - silent_mode, - )?; - - namespace.insert_module_ref(dependency_name.to_string(), compiled); - - // nothing is returned from this method since it mutates the hashmaps it was given Ok(json_abi) } - -fn compile_library( - source: Arc, - proj_name: &str, - namespace: NamespaceRef, - build_config: BuildConfig, - dependency_graph: &mut HashMap>, - silent_mode: bool, -) -> Result<(NamespaceRef, Vec), String> { - let res = sway_core::compile_to_ast(source, namespace, &build_config, dependency_graph); - match res { - CompileAstResult::Success { - parse_tree, - tree_type, - warnings, - } => { - let errors = vec![]; - match tree_type { - TreeType::Library { name } => { - print_on_success( - silent_mode, - proj_name, - &warnings, - TreeType::Library { name }, - ); - let json_abi = generate_json_abi(&Some(*parse_tree.clone())); - Ok((parse_tree.get_namespace_ref(), json_abi)) - } - _ => { - print_on_failure(silent_mode, &warnings, &errors); - Err(format!("Failed to compile {}", proj_name)) - } - } - } - CompileAstResult::Failure { warnings, errors } => { - print_on_failure(silent_mode, &warnings, &errors); - Err(format!("Failed to compile {}", proj_name)) - } - } -} - -fn compile( - source: Arc, - proj_name: &str, - namespace: NamespaceRef, - build_config: BuildConfig, - dependency_graph: &mut HashMap>, - silent_mode: bool, -) -> Result, String> { - let res = sway_core::compile_to_ast(source, namespace, &build_config, dependency_graph); - match res { - CompileAstResult::Success { - parse_tree, - tree_type, - warnings, - } => { - let errors = vec![]; - match tree_type { - TreeType::Library { .. } => { - print_on_failure(silent_mode, &warnings, &errors); - Err(format!("Failed to compile {}", proj_name)) - } - typ => { - print_on_success(silent_mode, proj_name, &warnings, typ); - let json_abi = generate_json_abi(&Some(*parse_tree)); - Ok(json_abi) - } - } - } - CompileAstResult::Failure { warnings, errors } => { - print_on_failure(silent_mode, &warnings, &errors); - Err(format!("Failed to compile {}", proj_name)) - } - } -} - -fn generate_json_abi(ast: &Option) -> JsonABI { - match ast { - Some(TypedParseTree::Contract { abi_entries, .. }) => { - abi_entries.iter().map(|x| x.generate_json_abi()).collect() - } - _ => vec![], - } -} diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 081c505aaf9..4990902507b 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -25,7 +25,7 @@ use std::path::{Path, PathBuf}; const DEFAULT_OUTPUT_DIRECTORY: &str = "out"; -pub fn build(command: BuildCommand) -> Result, String> { +pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { let BuildCommand { path, binary_outfile, @@ -154,7 +154,7 @@ pub fn build(command: BuildCommand) -> Result, String> { println!(" Bytecode size is {} bytes.", main.len()); - Ok(main) + Ok((main, json_abi)) } /// Takes a dependency and returns a namespace of exported things from that dependency including diff --git a/forc/src/ops/forc_deploy.rs b/forc/src/ops/forc_deploy.rs index 5ffe0de230c..a23740cc5c7 100644 --- a/forc/src/ops/forc_deploy.rs +++ b/forc/src/ops/forc_deploy.rs @@ -56,7 +56,7 @@ pub async fn deploy(command: DeployCommand) -> Result::new(), diff --git a/forc/src/ops/forc_run.rs b/forc/src/ops/forc_run.rs index c86f9e95af3..e0e7d5fcc6f 100644 --- a/forc/src/ops/forc_run.rs +++ b/forc/src/ops/forc_run.rs @@ -50,7 +50,7 @@ pub async fn run(command: RunCommand) -> Result<(), CliError> { output_directory: command.output_directory, }; - let compiled_script = forc_build::build(build_command)?; + let (compiled_script, _json_abi) = forc_build::build(build_command)?; let contracts = command.contract.unwrap_or_default(); let (inputs, outputs) = get_tx_inputs_and_outputs(contracts); diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 52ea38c9b01..1cfaf7e17df 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -137,6 +137,7 @@ pub(crate) fn compile_to_bytes(file_name: &str) -> Result, String> { silent_mode: !verbose, output_directory: None, }) + .map(|(bytes, _json_abi)| bytes) } pub(crate) fn test_json_abi(file_name: &str) -> Result<(), String> { From 4f2622c9711c90ba3bfd9b85df8e0270dd204f98 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 19:27:44 +1100 Subject: [PATCH 07/14] Address clippy nits --- forc/src/ops/forc_build.rs | 6 ++---- forc/src/utils/helpers.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 4990902507b..9e4023e31c8 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -324,8 +324,7 @@ fn compile( source_map: &mut SourceMap, silent_mode: bool, ) -> Result<(Vec, JsonABI), String> { - let ast_res = - sway_core::compile_to_ast(source.clone(), namespace, &build_config, dependency_graph); + let ast_res = sway_core::compile_to_ast(source, namespace, &build_config, dependency_graph); let json_abi = match &ast_res { CompileAstResult::Success { parse_tree, @@ -338,8 +337,7 @@ fn compile( } typ => { print_on_success(silent_mode, proj_name, warnings, typ.clone()); - let json_abi = generate_json_abi(&*parse_tree); - json_abi + generate_json_abi(&*parse_tree) } }, CompileAstResult::Failure { warnings, errors } => { diff --git a/forc/src/utils/helpers.rs b/forc/src/utils/helpers.rs index e09396254cb..1044f43d3e2 100644 --- a/forc/src/utils/helpers.rs +++ b/forc/src/utils/helpers.rs @@ -130,7 +130,7 @@ pub fn print_on_failure(silent_mode: bool, warnings: &[CompileWarning], errors: if !silent_mode { warnings.iter().for_each(format_warning); - errors.iter().for_each(|error| format_err(&error)); + errors.iter().for_each(format_err); } println_red_err(&format!( From 7f4ab6ac56c575ed65776d31670a34e4653c3c6e Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 19:31:54 +1100 Subject: [PATCH 08/14] Always `forc build` at the beginning of `forc test` This ensures the project can build before attempting to run tests. This may also make it easier to assume a location for the contract JSON ABI in tests. --- forc/src/cli/commands/test.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/forc/src/cli/commands/test.rs b/forc/src/cli/commands/test.rs index 6db7baa8735..8167da7b54a 100644 --- a/forc/src/cli/commands/test.rs +++ b/forc/src/cli/commands/test.rs @@ -1,3 +1,4 @@ +use crate::ops::forc_build; use std::io::{BufRead, BufReader}; use std::process::Command as ProcessCommand; use std::process::Stdio; @@ -18,6 +19,9 @@ pub(crate) struct Command { } pub(crate) fn exec(command: Command) -> Result<(), String> { + // Ensure the project builds before running tests. + forc_build::build(Default::default())?; + // Cargo args setup let mut args: Vec = vec!["test".into()]; if let Some(name) = command.test_name { From 4b4d40a5d8d31154ec6ce9a352f41099568f05f1 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 19:42:49 +1100 Subject: [PATCH 09/14] Update `hello_world` contract example abi location to `out` directory Now that `cargo test` does `cargo build` first, it's safer to assume the contract JSON ABI is located in the `out` directory. --- examples/hello_world/my-contract-abi.json | 1 - examples/hello_world/tests/harness.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 examples/hello_world/my-contract-abi.json diff --git a/examples/hello_world/my-contract-abi.json b/examples/hello_world/my-contract-abi.json deleted file mode 100644 index 849d69e8308..00000000000 --- a/examples/hello_world/my-contract-abi.json +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[{"components":null,"name":"gas_","type":"u64"},{"components":null,"name":"amount_","type":"u64"},{"components":null,"name":"color_","type":"b256"},{"components":null,"name":"value","type":"u64"}],"name":"initialize_counter","outputs":[{"components":null,"name":"","type":"u64"}],"type":"function"},{"inputs":[{"components":null,"name":"gas_","type":"u64"},{"components":null,"name":"amount_","type":"u64"},{"components":null,"name":"color_","type":"b256"},{"components":null,"name":"amount","type":"u64"}],"name":"increment_counter","outputs":[{"components":null,"name":"","type":"u64"}],"type":"function"}] diff --git a/examples/hello_world/tests/harness.rs b/examples/hello_world/tests/harness.rs index de1e8b6a312..25385632ec0 100644 --- a/examples/hello_world/tests/harness.rs +++ b/examples/hello_world/tests/harness.rs @@ -6,7 +6,7 @@ use rand::{Rng, SeedableRng}; // Generate Rust bindings from our contract JSON ABI // FIXME: Incorrect path, see https://github.com/FuelLabs/fuels-rs/issues/94 -abigen!(MyContract, "./my-contract-abi.json"); +abigen!(MyContract, "./out/hello_world-abi.json"); #[tokio::test] async fn harness() { From fcd6a88681c6f7e9d50c4e276536899198fe0239 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 19:59:25 +1100 Subject: [PATCH 10/14] On `forc init` create `.gitignore` containing `out`, `target` dirs This ensures that forc and cargo's output artifact directories are discluded from git tracking by default. --- forc/src/ops/forc_init.rs | 6 ++++++ forc/src/utils/defaults.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/forc/src/ops/forc_init.rs b/forc/src/ops/forc_init.rs index b254718764f..0a622e39048 100644 --- a/forc/src/ops/forc_init.rs +++ b/forc/src/ops/forc_init.rs @@ -33,5 +33,11 @@ pub(crate) fn init_new_project(project_name: String) -> Result<(), Box String { r#"script; fn main() { - + } "# .into() @@ -69,3 +69,10 @@ async fn harness() { "# .into() } + +pub(crate) fn default_gitignore() -> String { + r#"out +target +"# + .into() +} From 1231e2030eb12bf7c58b3f5c9daccb6671fecb37 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 20:34:19 +1100 Subject: [PATCH 11/14] Add a `forc clean` command Removes the default forc compiler output directory (`/out`). Also runs `cargo clean` to remove the `target` directory that gets created when running `cargo test` via `forc test`. --- forc/src/cli/commands/clean.rs | 16 ++++++++++++++ forc/src/cli/commands/mod.rs | 1 + forc/src/cli/mod.rs | 7 ++++-- forc/src/ops/forc_build.rs | 7 +++--- forc/src/ops/forc_clean.rs | 40 ++++++++++++++++++++++++++++++++++ forc/src/ops/mod.rs | 1 + forc/src/utils/helpers.rs | 6 +++++ 7 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 forc/src/cli/commands/clean.rs create mode 100644 forc/src/ops/forc_clean.rs diff --git a/forc/src/cli/commands/clean.rs b/forc/src/cli/commands/clean.rs new file mode 100644 index 00000000000..3c8345c91ee --- /dev/null +++ b/forc/src/cli/commands/clean.rs @@ -0,0 +1,16 @@ +use crate::ops::forc_clean; +use structopt::{self, StructOpt}; + +/// Removes the default forc compiler output artifact directory, i.e. `/out`. Also +/// calls `cargo clean` which removes the `target` directory generated by `cargo` when running +/// tests. +#[derive(Debug, StructOpt)] +pub struct Command { + /// Path to the project, if not specified, current working directory will be used. + #[structopt(short, long)] + pub path: Option, +} + +pub fn exec(command: Command) -> Result<(), String> { + forc_clean::clean(command) +} diff --git a/forc/src/cli/commands/mod.rs b/forc/src/cli/commands/mod.rs index 6762ecacb2b..10bd3049f6e 100644 --- a/forc/src/cli/commands/mod.rs +++ b/forc/src/cli/commands/mod.rs @@ -1,5 +1,6 @@ pub mod addr2line; pub mod build; +pub mod clean; pub mod deploy; pub mod explorer; pub mod format; diff --git a/forc/src/cli/mod.rs b/forc/src/cli/mod.rs index b93d8513c42..52b83fee623 100644 --- a/forc/src/cli/mod.rs +++ b/forc/src/cli/mod.rs @@ -2,12 +2,13 @@ use structopt::StructOpt; mod commands; use self::commands::{ - addr2line, build, deploy, explorer, format, init, json_abi, lsp, parse_bytecode, run, test, - update, + addr2line, build, clean, deploy, explorer, format, init, json_abi, lsp, parse_bytecode, run, + test, update, }; use addr2line::Command as Addr2LineCommand; pub use build::Command as BuildCommand; +pub use clean::Command as CleanCommand; pub use deploy::Command as DeployCommand; pub use explorer::Command as ExplorerCommand; pub use format::Command as FormatCommand; @@ -32,6 +33,7 @@ enum Forc { #[structopt(name = "addr2line")] Addr2Line(Addr2LineCommand), Build(BuildCommand), + Clean(CleanCommand), Deploy(DeployCommand), Explorer(ExplorerCommand), #[structopt(name = "fmt")] @@ -50,6 +52,7 @@ pub(crate) async fn run_cli() -> Result<(), String> { match opt.command { Forc::Addr2Line(command) => addr2line::exec(command), Forc::Build(command) => build::exec(command), + Forc::Clean(command) => clean::exec(command), Forc::Deploy(command) => deploy::exec(command).await, Forc::Explorer(command) => explorer::exec(command).await, Forc::Format(command) => format::exec(command), diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 9e4023e31c8..5ed6974b263 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -4,7 +4,8 @@ use crate::{ cli::BuildCommand, utils::dependency, utils::helpers::{ - get_main_file, print_on_failure, print_on_success, print_on_success_library, read_manifest, + default_output_directory, get_main_file, print_on_failure, print_on_success, + print_on_success_library, read_manifest, }, }; use std::fs::{self, File}; @@ -23,8 +24,6 @@ use anyhow::Result; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; -const DEFAULT_OUTPUT_DIRECTORY: &str = "out"; - pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { let BuildCommand { path, @@ -135,7 +134,7 @@ pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { // Create the output directory for build artifacts. let output_dir = output_directory .map(PathBuf::from) - .unwrap_or_else(|| manifest_dir.join(DEFAULT_OUTPUT_DIRECTORY).join(profile)); + .unwrap_or_else(|| default_output_directory(&manifest_dir).join(profile)); if !output_dir.exists() { fs::create_dir_all(&output_dir).map_err(|e| e.to_string())?; } diff --git a/forc/src/ops/forc_clean.rs b/forc/src/ops/forc_clean.rs new file mode 100644 index 00000000000..0d6ab0cc8e6 --- /dev/null +++ b/forc/src/ops/forc_clean.rs @@ -0,0 +1,40 @@ +use crate::{cli::CleanCommand, utils::helpers::default_output_directory}; +use std::{path::PathBuf, process}; +use sway_utils::{find_manifest_dir, MANIFEST_FILE_NAME}; + +pub fn clean(command: CleanCommand) -> Result<(), String> { + let CleanCommand { path } = command; + + // find manifest directory, even if in subdirectory + let this_dir = if let Some(ref path) = path { + PathBuf::from(path) + } else { + std::env::current_dir().map_err(|e| format!("{:?}", e))? + }; + let manifest_dir = match find_manifest_dir(&this_dir) { + Some(dir) => dir, + None => { + return Err(format!( + "could not find `{}` in `{}` or any parent directory", + MANIFEST_FILE_NAME, + this_dir.display(), + )) + } + }; + + // Clear `/out` directory. + // Ignore I/O errors telling us `out_dir` isn't there. + let out_dir = default_output_directory(&manifest_dir); + let _ = std::fs::remove_dir_all(out_dir); + + // Run `cargo clean`, forwarding stdout and stderr (`cargo clean` doesn't appear to output + // anything as of writing this). + process::Command::new("cargo") + .arg("clean") + .stderr(process::Stdio::inherit()) + .stdout(process::Stdio::inherit()) + .output() + .map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/forc/src/ops/mod.rs b/forc/src/ops/mod.rs index 68ddaf27cb7..15e11339b71 100644 --- a/forc/src/ops/mod.rs +++ b/forc/src/ops/mod.rs @@ -1,5 +1,6 @@ pub mod forc_abi_json; pub mod forc_build; +pub mod forc_clean; pub mod forc_dep_check; pub mod forc_deploy; pub mod forc_explorer; diff --git a/forc/src/utils/helpers.rs b/forc/src/utils/helpers.rs index 1044f43d3e2..f7c72da6123 100644 --- a/forc/src/utils/helpers.rs +++ b/forc/src/utils/helpers.rs @@ -12,6 +12,8 @@ use sway_core::{error::LineCol, CompileError, CompileWarning, TreeType}; use sway_utils::constants; use termcolor::{self, Color as TermColor, ColorChoice, ColorSpec, StandardStream, WriteColor}; +pub const DEFAULT_OUTPUT_DIRECTORY: &str = "out"; + pub fn is_sway_file(file: &Path) -> bool { let res = file.extension(); Some(OsStr::new(constants::SWAY_EXTENSION)) == res @@ -70,6 +72,10 @@ pub fn get_main_file(manifest_of_dep: &Manifest, manifest_dir: &Path) -> Result< Ok(main_file) } +pub fn default_output_directory(manifest_dir: &Path) -> PathBuf { + manifest_dir.join(DEFAULT_OUTPUT_DIRECTORY) +} + pub fn print_on_success( silent_mode: bool, proj_name: &str, From 1f935ed61bfa5ec010701b44b89734a9d5510df3 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 15 Feb 2022 20:53:43 +1100 Subject: [PATCH 12/14] Fix doc comment to say JSON ABI is generated for both contract *and* lib --- forc/src/cli/commands/build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index a36a981d9cb..daaa31732be 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -5,8 +5,8 @@ use structopt::{self, StructOpt}; /// /// The output produced will depend on the project's program type. Building script, predicate and /// contract projects will produce their bytecode in binary format `.bin`. Building -/// contracts will *also* produce the contract's ABI in JSON format `-abi.json`. -/// Library projects will be type-checked, but no output is produced. +/// contracts and libraries will also produce the public ABI in JSON format +/// `-abi.json`. #[derive(Debug, Default, StructOpt)] pub struct Command { /// Path to the project, if not specified, current working directory will be used. From 9ad1e1859b1e9917676ad07574b7f7a404f9dca9 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 16 Feb 2022 00:33:25 +1100 Subject: [PATCH 13/14] Pretty print JSON ABI output by default Human-friendly formatting by default, useful for checking/debugging contract/library ABIs. This is applied to both the `forc build` JSON of the output directory, as well as the `forc json-abi` output. A `--minimize-json-abi` option is added in order to allow for achieving the original behaviour. --- forc/src/cli/commands/build.rs | 4 + forc/src/cli/commands/deploy.rs | 6 +- forc/src/cli/commands/run.rs | 7 +- forc/src/ops/forc_abi_json.rs | 2 +- forc/src/ops/forc_build.rs | 8 +- forc/src/ops/forc_deploy.rs | 2 + forc/src/ops/forc_fmt.rs | 13 +-- forc/src/ops/forc_run.rs | 1 + test/src/e2e_vm_tests/harness.rs | 27 +----- .../contract_abi_impl/json_abi_oracle.json | 91 ++++++++++++++++++- .../contract_abi_impl/json_abi_output.json | 91 ++++++++++++++++++- .../valid_impurity/json_abi_oracle.json | 36 +++++++- 12 files changed, 245 insertions(+), 43 deletions(-) diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index daaa31732be..a3b185ff1ae 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -42,6 +42,10 @@ pub struct Command { /// By default, this is `/out`. #[structopt(long)] pub output_directory: Option, + /// By default the JSON for ABIs is formatted for human readability. By using this option JSON + /// output will be "minimized", i.e. all on one line without whitespace. + #[structopt(long)] + pub minimize_json_abi: bool, } pub(crate) fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/deploy.rs b/forc/src/cli/commands/deploy.rs index c4b29e373e6..31351bc2858 100644 --- a/forc/src/cli/commands/deploy.rs +++ b/forc/src/cli/commands/deploy.rs @@ -3,7 +3,7 @@ use structopt::{self, StructOpt}; /// Deploy contract project. /// Crafts a contract deployment transaction then sends it to a running node. -#[derive(Debug, StructOpt)] +#[derive(Debug, Default, StructOpt)] pub struct Command { /// Path to the project, if not specified, current working directory will be used. #[structopt(short, long)] @@ -38,6 +38,10 @@ pub struct Command { /// By default, this is `/out`. #[structopt(long)] pub output_directory: Option, + /// By default the JSON for ABIs is formatted for human readability. By using this option JSON + /// output will be "minimized", i.e. all on one line without whitespace. + #[structopt(long)] + pub minimize_json_abi: bool, } pub(crate) async fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/run.rs b/forc/src/cli/commands/run.rs index 3a6db73d112..4f2ece313c4 100644 --- a/forc/src/cli/commands/run.rs +++ b/forc/src/cli/commands/run.rs @@ -3,7 +3,7 @@ use structopt::{self, StructOpt}; /// Run script project. /// Crafts a script transaction then sends it to a running node. -#[derive(Debug, StructOpt)] +#[derive(Debug, Default, StructOpt)] pub struct Command { /// Hex string of data to input to script. #[structopt(short, long)] @@ -67,6 +67,11 @@ pub struct Command { /// By default, this is `/out`. #[structopt(long)] pub output_directory: Option, + + /// By default the JSON for ABIs is formatted for human readability. By using this option JSON + /// output will be "minimized", i.e. all on one line without whitespace. + #[structopt(long)] + pub minimize_json_abi: bool, } pub(crate) async fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/ops/forc_abi_json.rs b/forc/src/ops/forc_abi_json.rs index 49f05268e62..788b4294ac0 100644 --- a/forc/src/ops/forc_abi_json.rs +++ b/forc/src/ops/forc_abi_json.rs @@ -14,7 +14,7 @@ pub fn build(command: JsonAbiCommand) -> Result { let json_abi = json!(json_abi); if let Some(outfile) = command.json_outfile { let file = File::create(outfile).map_err(|e| e.to_string())?; - serde_json::to_writer(&file, &json_abi).map_err(|e| e.to_string())?; + serde_json::to_writer_pretty(&file, &json_abi).map_err(|e| e.to_string())?; } else { println!("{}", json_abi); } diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 5ed6974b263..1e670c3cd53 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -36,6 +36,7 @@ pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { offline_mode, silent_mode, output_directory, + minimize_json_abi, } = command; // find manifest directory, even if in subdirectory @@ -148,7 +149,12 @@ pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { let json_abi_stem = format!("{}-abi", manifest.project.name); let json_abi_path = output_dir.join(&json_abi_stem).with_extension("json"); let file = File::create(json_abi_path).map_err(|e| e.to_string())?; - serde_json::to_writer(&file, &json_abi).map_err(|e| e.to_string())?; + let res = if minimize_json_abi { + serde_json::to_writer(&file, &json_abi) + } else { + serde_json::to_writer_pretty(&file, &json_abi) + }; + res.map_err(|e| e.to_string())?; } println!(" Bytecode size is {} bytes.", main.len()); diff --git a/forc/src/ops/forc_deploy.rs b/forc/src/ops/forc_deploy.rs index a23740cc5c7..b319754f7ec 100644 --- a/forc/src/ops/forc_deploy.rs +++ b/forc/src/ops/forc_deploy.rs @@ -30,6 +30,7 @@ pub async fn deploy(command: DeployCommand) -> Result Result Result<(), FormatError> { - let build_command = BuildCommand { - path: None, - use_ir: false, - print_finalized_asm: false, - print_intermediate_asm: false, - print_ir: false, - binary_outfile: None, - debug_outfile: None, - offline_mode: false, - silent_mode: false, - output_directory: None, - }; + let build_command = BuildCommand::default(); match forc_build::build(build_command) { // build is successful, continue to formatting diff --git a/forc/src/ops/forc_run.rs b/forc/src/ops/forc_run.rs index e0e7d5fcc6f..6176f122905 100644 --- a/forc/src/ops/forc_run.rs +++ b/forc/src/ops/forc_run.rs @@ -48,6 +48,7 @@ pub async fn run(command: RunCommand) -> Result<(), CliError> { offline_mode: false, silent_mode: command.silent_mode, output_directory: command.output_directory, + minimize_json_abi: command.minimize_json_abi, }; let (compiled_script, _json_abi) = forc_build::build(build_command)?; diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 1cfaf7e17df..ce6a6c96a35 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -24,14 +24,8 @@ pub(crate) fn deploy_contract(file_name: &str) -> ContractId { manifest_dir, file_name )), use_ir, - print_finalized_asm: false, - print_intermediate_asm: false, - print_ir: false, - binary_outfile: None, - debug_outfile: None, - offline_mode: false, silent_mode: !verbose, - output_directory: None, + ..Default::default() })) .unwrap() } @@ -50,24 +44,15 @@ pub(crate) fn runs_on_node(file_name: &str, contract_ids: &[fuel_tx::ContractId] let (verbose, use_ir) = get_test_config_from_env(); let command = RunCommand { - data: None, path: Some(format!( "{}/src/e2e_vm_tests/test_programs/{}", manifest_dir, file_name )), - dry_run: false, node_url: "127.0.0.1:4000".into(), - kill_node: false, use_ir, - binary_outfile: None, - debug_outfile: None, - print_finalized_asm: false, - print_intermediate_asm: false, - print_ir: false, silent_mode: !verbose, - pretty_print: false, contract: Some(contracts), - output_directory: None, + ..Default::default() }; tokio::runtime::Runtime::new() .unwrap() @@ -128,14 +113,8 @@ pub(crate) fn compile_to_bytes(file_name: &str) -> Result, String> { manifest_dir, file_name )), use_ir, - print_finalized_asm: false, - print_intermediate_asm: false, - print_ir: false, - binary_outfile: None, - debug_outfile: None, - offline_mode: false, silent_mode: !verbose, - output_directory: None, + ..Default::default() }) .map(|(bytes, _json_abi)| bytes) } diff --git a/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_oracle.json index 23376174411..0f7c76e0ceb 100644 --- a/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_oracle.json @@ -1 +1,90 @@ -[{"inputs":[{"components":null,"name":"gas","type":"u64"},{"components":null,"name":"coin","type":"u64"},{"components":null,"name":"asset_id","type":"b256"},{"components":[{"components":null,"name":"field_1","type":"bool"},{"components":null,"name":"field_2","type":"u64"}],"name":"input","type":"struct InputStruct"}],"name":"foo","outputs":[{"components":[{"components":null,"name":"field_1","type":"bool"},{"components":null,"name":"field_2","type":"u64"}],"name":"","type":"struct InputStruct"}],"type":"function"},{"inputs":[{"components":null,"name":"gas","type":"u64"},{"components":null,"name":"coin","type":"u64"},{"components":null,"name":"asset_id","type":"b256"},{"components":null,"name":"input","type":"bool"}],"name":"baz","outputs":[{"components":null,"name":"","type":"()"}],"type":"function"}] \ No newline at end of file +[ + { + "inputs": [ + { + "components": null, + "name": "gas", + "type": "u64" + }, + { + "components": null, + "name": "coin", + "type": "u64" + }, + { + "components": null, + "name": "asset_id", + "type": "b256" + }, + { + "components": [ + { + "components": null, + "name": "field_1", + "type": "bool" + }, + { + "components": null, + "name": "field_2", + "type": "u64" + } + ], + "name": "input", + "type": "struct InputStruct" + } + ], + "name": "foo", + "outputs": [ + { + "components": [ + { + "components": null, + "name": "field_1", + "type": "bool" + }, + { + "components": null, + "name": "field_2", + "type": "u64" + } + ], + "name": "", + "type": "struct InputStruct" + } + ], + "type": "function" + }, + { + "inputs": [ + { + "components": null, + "name": "gas", + "type": "u64" + }, + { + "components": null, + "name": "coin", + "type": "u64" + }, + { + "components": null, + "name": "asset_id", + "type": "b256" + }, + { + "components": null, + "name": "input", + "type": "bool" + } + ], + "name": "baz", + "outputs": [ + { + "components": null, + "name": "", + "type": "()" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_output.json b/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_output.json index 23376174411..0f7c76e0ceb 100644 --- a/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_output.json +++ b/test/src/e2e_vm_tests/test_programs/contract_abi_impl/json_abi_output.json @@ -1 +1,90 @@ -[{"inputs":[{"components":null,"name":"gas","type":"u64"},{"components":null,"name":"coin","type":"u64"},{"components":null,"name":"asset_id","type":"b256"},{"components":[{"components":null,"name":"field_1","type":"bool"},{"components":null,"name":"field_2","type":"u64"}],"name":"input","type":"struct InputStruct"}],"name":"foo","outputs":[{"components":[{"components":null,"name":"field_1","type":"bool"},{"components":null,"name":"field_2","type":"u64"}],"name":"","type":"struct InputStruct"}],"type":"function"},{"inputs":[{"components":null,"name":"gas","type":"u64"},{"components":null,"name":"coin","type":"u64"},{"components":null,"name":"asset_id","type":"b256"},{"components":null,"name":"input","type":"bool"}],"name":"baz","outputs":[{"components":null,"name":"","type":"()"}],"type":"function"}] \ No newline at end of file +[ + { + "inputs": [ + { + "components": null, + "name": "gas", + "type": "u64" + }, + { + "components": null, + "name": "coin", + "type": "u64" + }, + { + "components": null, + "name": "asset_id", + "type": "b256" + }, + { + "components": [ + { + "components": null, + "name": "field_1", + "type": "bool" + }, + { + "components": null, + "name": "field_2", + "type": "u64" + } + ], + "name": "input", + "type": "struct InputStruct" + } + ], + "name": "foo", + "outputs": [ + { + "components": [ + { + "components": null, + "name": "field_1", + "type": "bool" + }, + { + "components": null, + "name": "field_2", + "type": "u64" + } + ], + "name": "", + "type": "struct InputStruct" + } + ], + "type": "function" + }, + { + "inputs": [ + { + "components": null, + "name": "gas", + "type": "u64" + }, + { + "components": null, + "name": "coin", + "type": "u64" + }, + { + "components": null, + "name": "asset_id", + "type": "b256" + }, + { + "components": null, + "name": "input", + "type": "bool" + } + ], + "name": "baz", + "outputs": [ + { + "components": null, + "name": "", + "type": "()" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/valid_impurity/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/valid_impurity/json_abi_oracle.json index fc54d32d3ad..3f67e92368a 100644 --- a/test/src/e2e_vm_tests/test_programs/valid_impurity/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/valid_impurity/json_abi_oracle.json @@ -1 +1,35 @@ -[{"inputs":[{"components":null,"name":"gas","type":"u64"},{"components":null,"name":"coins","type":"u64"},{"components":null,"name":"asset_id","type":"b256"},{"components":null,"name":"input","type":"()"}],"name":"impure_func","outputs":[{"components":null,"name":"","type":"bool"}],"type":"function"}] \ No newline at end of file +[ + { + "inputs": [ + { + "components": null, + "name": "gas", + "type": "u64" + }, + { + "components": null, + "name": "coins", + "type": "u64" + }, + { + "components": null, + "name": "asset_id", + "type": "b256" + }, + { + "components": null, + "name": "input", + "type": "()" + } + ], + "name": "impure_func", + "outputs": [ + { + "components": null, + "name": "", + "type": "bool" + } + ], + "type": "function" + } +] \ No newline at end of file From f44e64b8677ee397972300229a284b0e3879e30e Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 16 Feb 2022 12:40:06 +1100 Subject: [PATCH 14/14] Rename minimize -> minify. Add `--minify` flag to `forc json-abi`. --- forc/src/cli/commands/build.rs | 4 ++-- forc/src/cli/commands/deploy.rs | 4 ++-- forc/src/cli/commands/json_abi.rs | 6 +++++- forc/src/cli/commands/run.rs | 4 ++-- forc/src/ops/forc_abi_json.rs | 12 ++++++++++-- forc/src/ops/forc_build.rs | 4 ++-- forc/src/ops/forc_deploy.rs | 4 ++-- forc/src/ops/forc_run.rs | 2 +- test/src/e2e_vm_tests/harness.rs | 2 +- 9 files changed, 27 insertions(+), 15 deletions(-) diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index a3b185ff1ae..bd233b0ba1c 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -43,9 +43,9 @@ pub struct Command { #[structopt(long)] pub output_directory: Option, /// By default the JSON for ABIs is formatted for human readability. By using this option JSON - /// output will be "minimized", i.e. all on one line without whitespace. + /// output will be "minified", i.e. all on one line without whitespace. #[structopt(long)] - pub minimize_json_abi: bool, + pub minify_json_abi: bool, } pub(crate) fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/deploy.rs b/forc/src/cli/commands/deploy.rs index 31351bc2858..4fbc0d92564 100644 --- a/forc/src/cli/commands/deploy.rs +++ b/forc/src/cli/commands/deploy.rs @@ -39,9 +39,9 @@ pub struct Command { #[structopt(long)] pub output_directory: Option, /// By default the JSON for ABIs is formatted for human readability. By using this option JSON - /// output will be "minimized", i.e. all on one line without whitespace. + /// output will be "minified", i.e. all on one line without whitespace. #[structopt(long)] - pub minimize_json_abi: bool, + pub minify_json_abi: bool, } pub(crate) async fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/json_abi.rs b/forc/src/cli/commands/json_abi.rs index c799020ba2b..a4804cc00d2 100644 --- a/forc/src/cli/commands/json_abi.rs +++ b/forc/src/cli/commands/json_abi.rs @@ -2,7 +2,7 @@ use crate::ops::forc_abi_json; use structopt::{self, StructOpt}; /// Output the JSON associated with the ABI. -#[derive(Debug, StructOpt)] +#[derive(Debug, Default, StructOpt)] pub struct Command { /// Path to the project, if not specified, current working directory will be used. #[structopt(short, long)] @@ -17,6 +17,10 @@ pub struct Command { /// Silent mode. Don't output any warnings or errors to the command line. #[structopt(long = "silent", short = "s")] pub silent_mode: bool, + /// By default the JSON for ABIs is formatted for human readability. By using this option JSON + /// output will be "minified", i.e. all on one line without whitespace. + #[structopt(long)] + pub minify: bool, } pub(crate) fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/cli/commands/run.rs b/forc/src/cli/commands/run.rs index 4f2ece313c4..629ec8b0ccf 100644 --- a/forc/src/cli/commands/run.rs +++ b/forc/src/cli/commands/run.rs @@ -69,9 +69,9 @@ pub struct Command { pub output_directory: Option, /// By default the JSON for ABIs is formatted for human readability. By using this option JSON - /// output will be "minimized", i.e. all on one line without whitespace. + /// output will be "minified", i.e. all on one line without whitespace. #[structopt(long)] - pub minimize_json_abi: bool, + pub minify_json_abi: bool, } pub(crate) async fn exec(command: Command) -> Result<(), String> { diff --git a/forc/src/ops/forc_abi_json.rs b/forc/src/ops/forc_abi_json.rs index 788b4294ac0..121012027f1 100644 --- a/forc/src/ops/forc_abi_json.rs +++ b/forc/src/ops/forc_abi_json.rs @@ -8,15 +8,23 @@ pub fn build(command: JsonAbiCommand) -> Result { path: command.path, offline_mode: command.offline_mode, silent_mode: command.silent_mode, + minify_json_abi: command.minify, ..Default::default() }; let (_bytes, json_abi) = crate::ops::forc_build::build(build_command)?; let json_abi = json!(json_abi); if let Some(outfile) = command.json_outfile { let file = File::create(outfile).map_err(|e| e.to_string())?; - serde_json::to_writer_pretty(&file, &json_abi).map_err(|e| e.to_string())?; - } else { + let res = if command.minify { + serde_json::to_writer(&file, &json_abi) + } else { + serde_json::to_writer_pretty(&file, &json_abi) + }; + res.map_err(|e| e.to_string())?; + } else if command.minify { println!("{}", json_abi); + } else { + println!("{:#}", json_abi); } Ok(json_abi) } diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 1e670c3cd53..679324e49a7 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -36,7 +36,7 @@ pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { offline_mode, silent_mode, output_directory, - minimize_json_abi, + minify_json_abi, } = command; // find manifest directory, even if in subdirectory @@ -149,7 +149,7 @@ pub fn build(command: BuildCommand) -> Result<(Vec, JsonABI), String> { let json_abi_stem = format!("{}-abi", manifest.project.name); let json_abi_path = output_dir.join(&json_abi_stem).with_extension("json"); let file = File::create(json_abi_path).map_err(|e| e.to_string())?; - let res = if minimize_json_abi { + let res = if minify_json_abi { serde_json::to_writer(&file, &json_abi) } else { serde_json::to_writer_pretty(&file, &json_abi) diff --git a/forc/src/ops/forc_deploy.rs b/forc/src/ops/forc_deploy.rs index b319754f7ec..453ad7e03b9 100644 --- a/forc/src/ops/forc_deploy.rs +++ b/forc/src/ops/forc_deploy.rs @@ -30,7 +30,7 @@ pub async fn deploy(command: DeployCommand) -> Result Result Result<(), CliError> { offline_mode: false, silent_mode: command.silent_mode, output_directory: command.output_directory, - minimize_json_abi: command.minimize_json_abi, + minify_json_abi: command.minify_json_abi, }; let (compiled_script, _json_abi) = forc_build::build(build_command)?; diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index ce6a6c96a35..6284d960bf5 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -158,8 +158,8 @@ fn compile_to_json_abi(file_name: &str) -> Result { "{}/src/e2e_vm_tests/test_programs/{}/{}", manifest_dir, file_name, "json_abi_output.json" )), - offline_mode: false, silent_mode: true, + ..Default::default() }) }