Skip to content

Commit

Permalink
Add a --tests flag to forc build (and sway-core) in preparation f…
Browse files Browse the repository at this point in the history
…or building unit tests (FuelLabs#3150)

This strips out a couple commits from FuelLabs#2985 and aims to land them
independently!

Specifically, this enables calling `forc build --tests` which will (once
FuelLabs#2985 lands) build the current project along with all its tests.
Eventually we'll also want `forc check --tests` and `forc test` itself,
but landing this command early is particularly useful for testing the
new in-progress unit testing feature.

This PR also removes the old `AsmOrLib` and `BytecodeOrLib` types in
favour of new `CompiledAsm` and `CompiledBytecode` types. This is in
preparation for FuelLabs#2985 in which libraries will begin to generate bytecode
when tests are enabled.
  • Loading branch information
mitchmindtree authored Oct 26, 2022
1 parent d2a1428 commit 224e729
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 67 deletions.
3 changes: 3 additions & 0 deletions forc-pkg/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub struct BuildProfile {
pub print_intermediate_asm: bool,
pub terse: bool,
pub time_phases: bool,
pub include_tests: bool,
}

impl Dependency {
Expand Down Expand Up @@ -433,6 +434,7 @@ impl BuildProfile {
print_intermediate_asm: false,
terse: false,
time_phases: false,
include_tests: false,
}
}

Expand All @@ -444,6 +446,7 @@ impl BuildProfile {
print_intermediate_asm: false,
terse: false,
time_phases: false,
include_tests: false,
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use sway_core::{
},
semantic_analysis::namespace,
source_map::SourceMap,
BytecodeOrLib, CompileResult,
CompileResult, CompiledBytecode,
};
use sway_error::error::CompileError;
use sway_types::{Ident, JsonABIProgram, JsonTypeApplication, JsonTypeDeclaration};
Expand Down Expand Up @@ -1764,7 +1764,8 @@ pub fn sway_build_config(
)
.print_finalized_asm(build_profile.print_finalized_asm)
.print_intermediate_asm(build_profile.print_intermediate_asm)
.print_ir(build_profile.print_ir);
.print_ir(build_profile.print_ir)
.include_tests(build_profile.include_tests);
Ok(build_config)
}

Expand Down Expand Up @@ -2016,10 +2017,7 @@ pub fn compile(
);

match bc_res.value {
Some(BytecodeOrLib::Library) => {
unreachable!("compilation of library program types is handled above")
}
Some(BytecodeOrLib::Bytecode(bytes)) if bc_res.errors.is_empty() => {
Some(CompiledBytecode(bytes)) if bc_res.errors.is_empty() => {
print_on_success(terse_mode, &pkg.name, &bc_res.warnings, &tree_type);
let bytecode = bytes;
let compiled = Compiled {
Expand All @@ -2030,9 +2028,10 @@ pub fn compile(
};
Ok((compiled, None))
}
Some(BytecodeOrLib::Bytecode(_)) | None => fail(&bc_res.warnings, &bc_res.errors),
_ => fail(&bc_res.warnings, &bc_res.errors),
}
}

#[derive(Default)]
pub struct BuildOptions {
/// Path to the project, if not specified, current working directory will be used.
Expand Down Expand Up @@ -2081,6 +2080,8 @@ pub struct BuildOptions {
pub release: bool,
/// Output the time elapsed over each part of the compilation process.
pub time_phases: bool,
/// Include all test functions within the build.
pub tests: bool,
}

/// The suffix that helps identify the file which contains the hash of the binary file created when
Expand Down Expand Up @@ -2113,6 +2114,7 @@ pub fn build_with_options(build_options: BuildOptions) -> Result<Compiled> {
build_profile,
release,
time_phases,
tests,
} = build_options;

let mut selected_build_profile = key_debug;
Expand Down Expand Up @@ -2163,6 +2165,7 @@ pub fn build_with_options(build_options: BuildOptions) -> Result<Compiled> {
profile.print_intermediate_asm |= print_intermediate_asm;
profile.terse |= terse_mode;
profile.time_phases |= time_phases;
profile.include_tests |= tests;

// Build it!
let (compiled, source_map) = build(&plan, &profile)?;
Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-client/src/ops/deploy/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub async fn deploy(command: DeployCommand) -> Result<fuel_tx::ContractId> {
build_profile: command.build_profile,
release: command.release,
time_phases: command.time_phases,
tests: false,
};
let compiled = forc_pkg::build_with_options(build_options)?;

Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-client/src/ops/run/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub async fn run(command: RunCommand) -> Result<Vec<fuel_tx::Receipt>> {
build_profile: None,
release: false,
time_phases: command.time_phases,
tests: false,
};
let compiled = forc_pkg::build_with_options(build_options)?;

Expand Down
3 changes: 3 additions & 0 deletions forc/src/cli/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ pub struct Command {
/// Output the time elapsed over each part of the compilation process.
#[clap(long)]
pub time_phases: bool,
/// Also build all tests within the project.
#[clap(long)]
pub tests: bool,
}

pub(crate) fn exec(command: Command) -> Result<()> {
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub fn build(command: BuildCommand) -> Result<pkg::Compiled> {
release: command.release,
time_phases: command.time_phases,
print_intermediate_asm: command.print_intermediate_asm,
tests: command.tests,
};
let compiled = pkg::build_with_options(build_options)?;
Ok(compiled)
Expand Down
14 changes: 14 additions & 0 deletions sway-core/src/build_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct BuildConfig {
pub(crate) print_intermediate_asm: bool,
pub(crate) print_finalized_asm: bool,
pub(crate) print_ir: bool,
pub(crate) include_tests: bool,
}

impl BuildConfig {
Expand Down Expand Up @@ -46,6 +47,7 @@ impl BuildConfig {
print_intermediate_asm: false,
print_finalized_asm: false,
print_ir: false,
include_tests: false,
}
}

Expand All @@ -70,6 +72,18 @@ impl BuildConfig {
}
}

/// Whether or not to include test functions in parsing, type-checking and codegen.
///
/// This should be set to `true` by invocations like `forc test` or `forc check --tests`.
///
/// Default: `false`
pub fn include_tests(self, include_tests: bool) -> Self {
Self {
include_tests,
..self
}
}

pub fn canonical_root_module(&self) -> Arc<PathBuf> {
self.canonical_root_module.clone()
}
Expand Down
94 changes: 34 additions & 60 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,26 @@ use transform::to_parsed_lang;
/// # Panics
/// Panics if the parser panics.
pub fn parse(input: Arc<str>, config: Option<&BuildConfig>) -> CompileResult<parsed::ParseProgram> {
// Omit tests by default so that a test that fails to typecheck doesn't block the rest of the
// program (similar behaviour to rust). Later, we'll add a flag for including tests on
// invocations of `forc test` or `forc check --tests`. See #1832.
let include_test_fns = false;

CompileResult::with_handler(|h| match config {
None => parse_in_memory(h, input, include_test_fns),
None => parse_in_memory(h, input),
// When a `BuildConfig` is given,
// the module source may declare `dep`s that must be parsed from other files.
Some(config) => {
parse_module_tree(h, input, config.canonical_root_module(), include_test_fns)
.map(|(kind, root)| parsed::ParseProgram { kind, root })
}
Some(config) => parse_module_tree(
h,
input,
config.canonical_root_module(),
config.include_tests,
)
.map(|(kind, root)| parsed::ParseProgram { kind, root }),
})
}

/// When no `BuildConfig` is given, we're assumed to be parsing in-memory with no submodules.
fn parse_in_memory(
handler: &Handler,
src: Arc<str>,
include_test_fns: bool,
) -> Result<parsed::ParseProgram, ErrorEmitted> {
fn parse_in_memory(handler: &Handler, src: Arc<str>) -> Result<parsed::ParseProgram, ErrorEmitted> {
// Omit tests by default so that a test that fails to typecheck doesn't block the rest of the
// program (similar behaviour to rust). To include test functions, specify the `include_tests`
// flag in the `BuildConfig` passed to `parse`.
let include_test_fns = false;
let module = sway_parse::parse_file(handler, src, None)?;
let (kind, tree) = to_parsed_lang::convert_parse_tree(handler, module, include_test_fns)?;
let submodules = Default::default();
Expand Down Expand Up @@ -167,20 +165,10 @@ fn module_path(parent_module_dir: &Path, dep: &sway_ast::Dependency) -> PathBuf
.with_extension(sway_types::constants::DEFAULT_FILE_EXTENSION)
}

/// Either finalized ASM or a library.
pub enum AsmOrLib {
Asm(FinalizedAsm),
Library {
name: Ident,
namespace: Box<namespace::Root>,
},
}
pub struct CompiledAsm(pub FinalizedAsm);

/// Either compiled bytecode in byte form or a library.
pub enum BytecodeOrLib {
Bytecode(Vec<u8>),
Library,
}
/// The bytecode for a sway program.
pub struct CompiledBytecode(pub Vec<u8>);

pub fn parsed_to_ast(
parse_program: &parsed::ParseProgram,
Expand Down Expand Up @@ -298,49 +286,36 @@ pub fn compile_to_ast(
}

/// Given input Sway source code,
/// try compiling to a `AsmOrLib`,
/// try compiling to a `CompiledAsm`,
/// containing the asm in opcode form (not raw bytes/bytecode).
pub fn compile_to_asm(
input: Arc<str>,
initial_namespace: namespace::Module,
build_config: BuildConfig,
) -> CompileResult<AsmOrLib> {
) -> CompileResult<CompiledAsm> {
let ast_res = compile_to_ast(input, initial_namespace, Some(&build_config));
ast_to_asm(ast_res, &build_config)
}

/// Given an AST compilation result,
/// try compiling to a `AsmOrLib`,
/// try compiling to a `CompiledAsm`,
/// containing the asm in opcode form (not raw bytes/bytecode).
pub fn ast_to_asm(
ast_res: CompileResult<ty::TyProgram>,
build_config: &BuildConfig,
) -> CompileResult<AsmOrLib> {
) -> CompileResult<CompiledAsm> {
match ast_res.value {
None => err(ast_res.warnings, ast_res.errors),
Some(typed_program) => {
let mut errors = ast_res.errors;
let mut warnings = ast_res.warnings;

let tree_type = typed_program.kind.tree_type();
match tree_type {
parsed::TreeType::Contract
| parsed::TreeType::Script
| parsed::TreeType::Predicate => {
let asm = check!(
compile_ast_to_ir_to_asm(typed_program, build_config),
return deduped_err(warnings, errors),
warnings,
errors
);
ok(AsmOrLib::Asm(asm), warnings, errors)
}
parsed::TreeType::Library { name } => {
let namespace = Box::new(typed_program.root.namespace.into());
let lib = AsmOrLib::Library { name, namespace };
ok(lib, warnings, errors)
}
}
let asm = check!(
compile_ast_to_ir_to_asm(typed_program, build_config),
return deduped_err(warnings, errors),
warnings,
errors
);
ok(CompiledAsm(asm), warnings, errors)
}
}
}
Expand Down Expand Up @@ -584,39 +559,38 @@ fn simplify_cfg(
Ok(())
}

/// Given input Sway source code, compile to a [BytecodeOrLib], containing the asm in bytecode form.
/// Given input Sway source code, compile to [CompiledBytecode], containing the asm in bytecode form.
pub fn compile_to_bytecode(
input: Arc<str>,
initial_namespace: namespace::Module,
build_config: BuildConfig,
source_map: &mut SourceMap,
) -> CompileResult<BytecodeOrLib> {
) -> CompileResult<CompiledBytecode> {
let asm_res = compile_to_asm(input, initial_namespace, build_config);
let result = asm_to_bytecode(asm_res, source_map);
clear_lazy_statics();
result
}

/// Given the assembly (opcodes), compile to a [BytecodeOrLib], containing the asm in bytecode form.
/// Given the assembly (opcodes), compile to [CompiledBytecode], containing the asm in bytecode form.
pub fn asm_to_bytecode(
CompileResult {
value,
mut warnings,
mut errors,
}: CompileResult<AsmOrLib>,
}: CompileResult<CompiledAsm>,
source_map: &mut SourceMap,
) -> CompileResult<BytecodeOrLib> {
) -> CompileResult<CompiledBytecode> {
match value {
Some(AsmOrLib::Asm(mut asm)) => {
Some(CompiledAsm(mut asm)) => {
let bytes = check!(
asm.to_bytecode_mut(source_map),
return err(warnings, errors),
warnings,
errors,
);
ok(BytecodeOrLib::Bytecode(bytes), warnings, errors)
ok(CompiledBytecode(bytes), warnings, errors)
}
Some(AsmOrLib::Library { .. }) => ok(BytecodeOrLib::Library, warnings, errors),
None => err(warnings, errors),
}
}
Expand Down

0 comments on commit 224e729

Please sign in to comment.