diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index 29ffd0927..f185a5840 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -146,6 +146,7 @@ def prepare_compiler_input( # pylint: disable=too-many-arguments force_no_optimize_yul: bool, interface: CompilerInterface, smt_use: SMTUse, + metadata_option_supported: bool, ) -> Tuple[List[str], str]: if interface == CompilerInterface.STANDARD_JSON: @@ -168,7 +169,9 @@ def prepare_compiler_input( # pylint: disable=too-many-arguments else: assert interface == CompilerInterface.CLI - compiler_options = [str(source_file_name), '--bin', '--metadata'] + compiler_options = [str(source_file_name), '--bin'] + if metadata_option_supported: + compiler_options.append('--metadata') if optimize: compiler_options.append('--optimize') elif force_no_optimize_yul: @@ -182,6 +185,29 @@ def prepare_compiler_input( # pylint: disable=too-many-arguments return (command_line, compiler_input) +def detect_metadata_cli_option_support(compiler_path: Path): + process = subprocess.run( + [str(compiler_path.absolute()), '--metadata', '-'], + input="contract C {}", + encoding='utf8', + capture_output=True, + check=False, + ) + + negative_response = "unrecognised option '--metadata'".strip() + if (process.returncode == 0) != (process.stderr.strip() != negative_response): + # If the error is other than expected or there's an error message but no error, don't try + # to guess. Just fail. + print( + f"Compiler exit code: {process.returncode}\n" + f"Compiler output:\n{process.stderr}\n", + file=sys.stderr + ) + raise Exception("Failed to determine if the compiler supports the --metadata option.") + + return process.returncode == 0 + + def run_compiler( # pylint: disable=too-many-arguments compiler_path: Path, source_file_name: Path, @@ -189,6 +215,7 @@ def run_compiler( # pylint: disable=too-many-arguments force_no_optimize_yul: bool, interface: CompilerInterface, smt_use: SMTUse, + metadata_option_supported: bool, tmp_dir: Path, ) -> FileReport: @@ -200,6 +227,7 @@ def run_compiler( # pylint: disable=too-many-arguments force_no_optimize_yul, interface, smt_use, + metadata_option_supported, ) process = subprocess.run( @@ -222,6 +250,7 @@ def run_compiler( # pylint: disable=too-many-arguments force_no_optimize_yul, interface, smt_use, + metadata_option_supported, ) # Create a copy that we can use directly with the CLI interface @@ -249,6 +278,8 @@ def generate_report( smt_use: SMTUse, force_no_optimize_yul: bool ): + metadata_option_supported = detect_metadata_cli_option_support(compiler_path) + with open('report.txt', mode='w', encoding='utf8', newline='\n') as report_file: for optimize in [False, True]: with TemporaryDirectory(prefix='prepare_report-') as tmp_dir: @@ -261,6 +292,7 @@ def generate_report( force_no_optimize_yul, interface, smt_use, + metadata_option_supported, Path(tmp_dir), ) report_file.write(report.format_report()) diff --git a/test/scripts/test_bytecodecompare_prepare_report.py b/test/scripts/test_bytecodecompare_prepare_report.py index 7992347f3..41e45de66 100644 --- a/test/scripts/test_bytecodecompare_prepare_report.py +++ b/test/scripts/test_bytecodecompare_prepare_report.py @@ -176,6 +176,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase): force_no_optimize_yul=False, interface=CompilerInterface.STANDARD_JSON, smt_use=SMTUse.DISABLE, + metadata_option_supported=True, ) self.assertEqual(command_line, ['solc', '--standard-json']) @@ -189,6 +190,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase): force_no_optimize_yul=False, interface=CompilerInterface.CLI, smt_use=SMTUse.DISABLE, + metadata_option_supported=True, ) self.assertEqual( @@ -223,6 +225,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase): force_no_optimize_yul=False, interface=CompilerInterface.STANDARD_JSON, smt_use=SMTUse.DISABLE, + metadata_option_supported=True, ) self.assertEqual(command_line, ['solc', '--standard-json']) @@ -236,6 +239,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase): force_no_optimize_yul=True, interface=CompilerInterface.CLI, smt_use=SMTUse.DISABLE, + metadata_option_supported=True, ) self.assertEqual(compiler_input, SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_CODE) @@ -248,6 +252,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase): force_no_optimize_yul=True, interface=CompilerInterface.CLI, smt_use=SMTUse.DISABLE, + metadata_option_supported=True, ) self.assertEqual( @@ -256,6 +261,23 @@ class TestPrepareCompilerInput(PrepareReportTestBase): ) self.assertEqual(compiler_input, SMT_SMOKE_TEST_SOL_CODE) + def test_prepare_compiler_input_for_cli_should_not_use_metadata_option_if_not_supported(self): + (command_line, compiler_input) = prepare_compiler_input( + Path('solc'), + SMT_SMOKE_TEST_SOL_PATH, + optimize=True, + force_no_optimize_yul=False, + interface=CompilerInterface.CLI, + smt_use=SMTUse.PRESERVE, + metadata_option_supported=False, + ) + + self.assertEqual( + command_line, + ['solc', str(SMT_SMOKE_TEST_SOL_PATH), '--bin', '--optimize'], + ) + self.assertEqual(compiler_input, SMT_SMOKE_TEST_SOL_CODE) + class TestParseStandardJSONOutput(PrepareReportTestBase): def test_parse_standard_json_output(self):