Add default values to compiler arguments (#451)

## Description

Adds default values to the compiler arguments below when parsed by
`clap`. This does not change previous user-facing behavior as these
defaults were implemented using different logic, but improves the UX and
the visibility of the exact defaults used.

**Example output from `resolc --help`:**
(Only affected arguments are shown here, and some of their descriptions
have been omitted here.)
```
Options:

  -O, --optimization <OPTIMIZATION>
          Set the optimization parameter -O[0 | 1 | 2 | 3 | s | z]. Use `3` for best performance and `z` for minimal size
          
          [default: z]

      --metadata-hash <METADATA_HASH>
          Set the metadata hash type. Available types: `none`, `ipfs`, `keccak256`
          
          [default: keccak256]

      --heap-size <HEAP_SIZE>
          The emulated EVM linear heap memory static buffer size in bytes.
          ...
          
          [default: 131072]

      --stack-size <STACK_SIZE>
          The contracts total stack size in bytes.
          ...
          
          [default: 131072]
```

## Resolved Issues

Closes #446
This commit is contained in:
LJ authored and GitHub committed 2026-01-14 19:22:03 +01:00
1 parent 3bcb62ac0b
commit f5fe3faa71
4 files changed
+166 -65

No files matched your search

@@ -64,14 +64,7 @@ impl Settings {
/// Creates settings from a CLI optimization parameter.
pub fn try_from_cli(value: char) -> anyhow::Result<Self> {
Ok(match value {
'0' => Self::new(
// The middle-end optimization level.
inkwell::OptimizationLevel::None,
// The middle-end size optimization level.
SizeLevel::Zero,
// The back-end optimization level.
inkwell::OptimizationLevel::None,
),
'0' => Self::none(),
'1' => Self::new(
inkwell::OptimizationLevel::Less,
SizeLevel::Zero,
@@ -84,23 +77,14 @@ impl Settings {
// The back-end does not currently distinguish between O1, O2, and O3.
inkwell::OptimizationLevel::Default,
),
'3' => Self::new(
inkwell::OptimizationLevel::Aggressive,
SizeLevel::Zero,
inkwell::OptimizationLevel::Aggressive,
),
'3' => Self::cycles(),
's' => Self::new(
// The middle-end optimization level is ignored when SizeLevel is set.
inkwell::OptimizationLevel::Default,
SizeLevel::S,
inkwell::OptimizationLevel::Aggressive,
),
'z' => Self::new(
// The middle-end optimization level is ignored when SizeLevel is set.
inkwell::OptimizationLevel::Default,
SizeLevel::Z,
inkwell::OptimizationLevel::Aggressive,
),
'z' => Self::size(),
char => anyhow::bail!("Unexpected optimization option '{}'", char),
})
}
@@ -126,6 +110,7 @@ impl Settings {
/// Returns the settings for the optimal size.
pub fn size() -> Self {
Self::new(
// The middle-end optimization level is ignored when SizeLevel is set.
inkwell::OptimizationLevel::Default,
SizeLevel::Z,
inkwell::OptimizationLevel::Aggressive,
+42 -23
View File
@@ -4,10 +4,12 @@ use std::collections::BTreeSet;
use std::path::Path;
use std::path::PathBuf;
use clap::Parser;
use clap::{parser::ValueSource, ArgMatches, CommandFactory, Parser};
use path_slash::PathExt;
use revive_common::MetadataHash;
use revive_solc_json_interface::SolcStandardJsonOutputError;
use revive_solc_json_interface::{
PolkaVMDefaultHeapMemorySize, PolkaVMDefaultStackMemorySize, SolcStandardJsonOutputError,
};
/// Compiles the provided Solidity input files (or use the standard input if no files
/// are given or "-" is specified as a file name). Outputs the components based on the
@@ -57,8 +59,8 @@ pub struct Arguments {
/// Set the optimization parameter -O[0 | 1 | 2 | 3 | s | z].
/// Use `3` for best performance and `z` for minimal size.
#[arg(short = 'O', long = "optimization")]
pub optimization: Option<char>,
#[arg(short = 'O', long = "optimization", default_value = "z")]
pub optimization: char,
/// Disable the `solc` optimizer.
/// Use it if your project uses the `MSIZE` instruction, or in other cases.
@@ -116,9 +118,8 @@ pub struct Arguments {
/// Set the metadata hash type.
/// Available types: `none`, `ipfs`, `keccak256`.
/// The default is `keccak256`.
#[arg(long)]
pub metadata_hash: Option<String>,
#[arg(long, default_value_t = MetadataHash::Keccak256)]
pub metadata_hash: MetadataHash,
/// Output PolkaVM assembly of the contracts.
#[arg(long = "asm")]
@@ -185,9 +186,9 @@ pub struct Arguments {
/// You are incentiviced to keep this value as small as possible:
/// 1.Increasing the heap size will increase startup costs.
/// 2.The heap size contributes to the total memory size a contract can use,
/// which includes the contracts code size
#[arg(long = "heap-size")]
pub heap_size: Option<u32>,
/// which includes the contracts code size.
#[arg(long = "heap-size", default_value_t = PolkaVMDefaultHeapMemorySize)]
pub heap_size: u32,
/// The contracts total stack size in bytes.
///
@@ -198,16 +199,17 @@ pub struct Arguments {
/// eventually revert execution at runtime!
///
/// You are incentiviced to keep this value as small as possible:
/// 1.Increasing the heap size will increase startup costs.
/// 1.Increasing the stack size will increase startup costs.
/// 2.The stack size contributes to the total memory size a contract can use,
/// which includes the contracts code size
#[arg(long = "stack-size")]
pub stack_size: Option<u32>,
/// which includes the contracts code size.
#[arg(long = "stack-size", default_value_t = PolkaVMDefaultStackMemorySize)]
pub stack_size: u32,
}
impl Arguments {
/// Validates the arguments.
pub fn validate(&self) -> Vec<SolcStandardJsonOutputError> {
let argument_matches = Self::command().get_matches();
let mut messages = Vec::new();
if self.version && std::env::args().count() > 2 {
@@ -226,9 +228,13 @@ impl Arguments {
));
}
if self.metadata_hash == Some(MetadataHash::IPFS.to_string()) {
if self.metadata_hash == MetadataHash::IPFS {
messages.push(SolcStandardJsonOutputError::new_error(
"`IPFS` metadata hash type is not supported. Please use `keccak256` instead.",
format!(
"`{}` metadata hash type is not supported. Please use `{}` instead.",
MetadataHash::IPFS,
MetadataHash::Keccak256,
),
None,
None,
));
@@ -350,34 +356,34 @@ impl Arguments {
}
if self.disable_solc_optimizer {
messages.push(SolcStandardJsonOutputError::new_error(
"Disabling the solc optimizer must specified in standard JSON input settings.",
"Disabling the solc optimizer must be specified in standard JSON input settings.",
None,
None,
));
}
if self.optimization.is_some() {
if Self::is_argument("optimization", &argument_matches) {
messages.push(SolcStandardJsonOutputError::new_error(
"LLVM optimizations must specified in standard JSON input settings.",
"LLVM optimizations must be specified in standard JSON input settings.",
None,
None,
));
}
if self.metadata_hash.is_some() {
if Self::is_argument("metadata_hash", &argument_matches) {
messages.push(SolcStandardJsonOutputError::new_error(
"Metadata hash mode must specified in standard JSON input settings.",
"Metadata hash mode must be specified in standard JSON input settings.",
None,
None,
));
}
if self.heap_size.is_some() {
if Self::is_argument("heap_size", &argument_matches) {
messages.push(SolcStandardJsonOutputError::new_error(
"Heap size must be specified in standard JSON input polkavm memory settings.",
None,
None,
));
}
if self.stack_size.is_some() {
if Self::is_argument("stack_size", &argument_matches) {
messages.push(SolcStandardJsonOutputError::new_error(
"Stack size must be specified in standard JSON input polkavm memory settings.",
None,
@@ -403,6 +409,19 @@ impl Arguments {
messages
}
/// Checks whether the value from the argument with the given `argument_id`
/// came from the command line.
///
/// This can be used for determining if a user has explicitly provided an
/// argument, even if it is the same value as the default one.
///
/// Panics if the `id` is not a valid argument id.
fn is_argument(argument_id: &str, argument_matches: &ArgMatches) -> bool {
argument_matches
.value_source(argument_id)
.is_some_and(|source| source == ValueSource::CommandLine)
}
/// Returns remappings from input paths.
pub fn split_input_files_and_remappings(
&self,
+11 -20
View File
@@ -1,13 +1,10 @@
//! Solidity to PolkaVM compiler binary.
use std::str::FromStr;
use std::{io::Write, path::PathBuf};
use clap::error::ErrorKind;
use resolc::Process;
use revive_common::{
deserialize_from_str, EVMVersion, MetadataHash, EXIT_CODE_FAILURE, EXIT_CODE_SUCCESS,
};
use revive_common::{deserialize_from_str, EVMVersion, EXIT_CODE_FAILURE, EXIT_CODE_SUCCESS};
use revive_llvm_context::{initialize_llvm, DebugConfig, OptimizerSettings, PolkaVMTarget};
use revive_solc_json_interface::{
ResolcWarning, SolcStandardJsonInputSettingsPolkaVMMemory,
@@ -32,7 +29,7 @@ fn main() -> anyhow::Result<()> {
let is_standard_json = arguments.standard_json.is_some();
let mut messages = arguments.validate();
if messages.iter().all(|error| error.severity != "error") {
if messages.iter().all(|error| !error.is_error()) {
if !is_standard_json {
std::io::stderr()
.write_all(
@@ -170,27 +167,21 @@ fn main_inner(
None => None,
};
let mut optimizer_settings = match arguments.optimization {
Some(mode) => OptimizerSettings::try_from_cli(mode)?,
None => OptimizerSettings::size(),
};
let mut optimizer_settings = OptimizerSettings::try_from_cli(arguments.optimization)?;
optimizer_settings.is_verify_each_enabled = arguments.llvm_verify_each;
optimizer_settings.is_debug_logging_enabled = arguments.llvm_debug_logging;
let metadata_hash = match arguments.metadata_hash {
Some(ref hash_type) => MetadataHash::from_str(hash_type.as_str())?,
None => MetadataHash::Keccak256,
};
let memory_config =
SolcStandardJsonInputSettingsPolkaVMMemory::new(arguments.heap_size, arguments.stack_size);
let memory_config = SolcStandardJsonInputSettingsPolkaVMMemory::new(
Some(arguments.heap_size),
Some(arguments.stack_size),
);
let build = if arguments.yul {
resolc::yul(
&solc,
input_files.as_slice(),
arguments.libraries.as_slice(),
metadata_hash,
arguments.metadata_hash,
messages,
optimizer_settings,
debug_config,
@@ -200,7 +191,7 @@ fn main_inner(
} else if let Some(standard_json) = arguments.standard_json {
resolc::standard_json(
&solc,
metadata_hash,
arguments.metadata_hash,
messages,
standard_json.map(PathBuf::from),
arguments.base_path,
@@ -215,7 +206,7 @@ fn main_inner(
&solc,
input_files.as_slice(),
arguments.libraries.as_slice(),
metadata_hash,
arguments.metadata_hash,
messages,
evm_version,
format,
@@ -240,7 +231,7 @@ fn main_inner(
&solc,
input_files.as_slice(),
arguments.libraries.as_slice(),
metadata_hash,
arguments.metadata_hash,
messages,
evm_version,
!arguments.disable_solc_optimizer,
+109 -3
View File
@@ -1,6 +1,8 @@
//! The tests for running resolc with standard JSON option.
use revive_solc_json_interface::SolcStandardJsonOutput;
use revive_solc_json_interface::{
PolkaVMDefaultHeapMemorySize, PolkaVMDefaultStackMemorySize, SolcStandardJsonOutput,
};
use crate::cli_utils::{
assert_command_success, assert_equal_exit_codes, execute_resolc_with_stdin_input,
@@ -10,12 +12,31 @@ use crate::cli_utils::{
const JSON_OPTION: &str = "--standard-json";
/// Asserts that the standard JSON output has at least one error with the given `error_message`.
fn assert_standard_json_errors_contain(output: &SolcStandardJsonOutput, error_message: &str) {
assert!(
output
.errors
.iter()
.any(|error| error.is_error() && error.message.contains(error_message)),
"the standard JSON output should contain the error message `{error_message}`"
);
}
/// Converts valid JSON text to `SolcStandardJsonOutput`.
fn to_solc_standard_json_output(json_text: &str) -> SolcStandardJsonOutput {
serde_json::from_str(json_text).unwrap()
}
#[test]
fn runs_with_valid_input_file() {
let arguments = &[JSON_OPTION];
let resolc_result = execute_resolc_with_stdin_input(arguments, STANDARD_JSON_CONTRACTS_PATH);
assert_command_success(&resolc_result, "Providing a valid input file to stdin");
let resolc_output = to_solc_standard_json_output(&resolc_result.stdout);
assert!(!resolc_output.errors.iter().any(|error| error.is_error()));
assert!(
resolc_result.stdout.contains("contracts"),
"Expected the output to contain a `contracts` field."
@@ -30,7 +51,7 @@ fn no_evm_codegen_requested() {
let result = execute_resolc_with_stdin_input(&[JSON_OPTION], STANDARD_JSON_NO_EVM_CODEGEN_PATH);
assert_command_success(&result, "EVM codegen std json input fixture should build");
let output: SolcStandardJsonOutput = serde_json::from_str(&result.stdout).unwrap();
let output = to_solc_standard_json_output(&result.stdout);
assert!(!output.errors.iter().any(|msg| msg.severity == "error"))
}
@@ -49,6 +70,91 @@ fn no_evm_codegen_requested_complex() {
"the EVM codegen std json complext input fixture should build fine",
);
let output: SolcStandardJsonOutput = serde_json::from_str(&result.stdout).unwrap();
let output = to_solc_standard_json_output(&result.stdout);
assert!(!output.errors.iter().any(|msg| msg.severity == "error"))
}
#[test]
fn invalid_extra_arguments() {
struct TestCase<'a> {
arguments: Vec<&'a str>,
error_message: &'static str,
}
let default_heap_size_string = PolkaVMDefaultHeapMemorySize.to_string();
let default_stack_size_string = PolkaVMDefaultStackMemorySize.to_string();
let cases = vec![
TestCase {
arguments: vec![JSON_OPTION, "--heap-size", &default_heap_size_string],
error_message:
"Heap size must be specified in standard JSON input polkavm memory settings",
},
TestCase {
arguments: vec![JSON_OPTION, "--stack-size", &default_stack_size_string],
error_message:
"Stack size must be specified in standard JSON input polkavm memory settings",
},
TestCase {
arguments: vec![JSON_OPTION, "--optimization", "z"],
error_message: "LLVM optimizations must be specified in standard JSON input settings",
},
TestCase {
arguments: vec![JSON_OPTION, "-Oz"],
error_message: "LLVM optimizations must be specified in standard JSON input settings",
},
TestCase {
arguments: vec![JSON_OPTION, "--metadata-hash", "ipfs"],
error_message:
"`ipfs` metadata hash type is not supported. Please use `keccak256` instead",
},
TestCase {
arguments: vec![JSON_OPTION, "--metadata-hash", "keccak256"],
error_message: "Metadata hash mode must be specified in standard JSON input settings",
},
TestCase {
arguments: vec![JSON_OPTION, "--asm"],
error_message: "Cannot output assembly or binary outside of JSON in standard JSON mode",
},
TestCase {
arguments: vec![JSON_OPTION, "--bin"],
error_message: "Cannot output assembly or binary outside of JSON in standard JSON mode",
},
TestCase {
arguments: vec![JSON_OPTION, "--libraries", "myLib=0x0000"],
error_message: "Libraries must be passed via standard JSON input",
},
TestCase {
arguments: vec![JSON_OPTION, "--evm-version", "osaka"],
error_message: "EVM version must be passed via standard JSON input",
},
TestCase {
arguments: vec![JSON_OPTION, "--output-dir", "tmp"],
error_message: "Output directory cannot be used in standard JSON mode",
},
TestCase {
arguments: vec![JSON_OPTION, "--overwrite"],
error_message: "Overwriting flag cannot be used in standard JSON mode",
},
TestCase {
arguments: vec![JSON_OPTION, "--disable-solc-optimizer"],
error_message:
"Disabling the solc optimizer must be specified in standard JSON input settings",
},
TestCase {
arguments: vec![JSON_OPTION, "-g"],
error_message: "Debug info must be requested in standard JSON input polkavm settings",
},
TestCase {
arguments: vec![JSON_OPTION, "--llvm-arg=-riscv"],
error_message:
"LLVM arguments must be configured in standard JSON input polkavm settings",
},
];
for case in cases {
let result = execute_resolc_with_stdin_input(&case.arguments, STANDARD_JSON_CONTRACTS_PATH);
let output = to_solc_standard_json_output(&result.stdout);
assert_standard_json_errors_contain(&output, case.error_message);
}
}