#!/usr/bin/env python3 # ------------------------------------------------------------------------------ # This file is part of solidity. # # solidity is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # solidity is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with solidity. If not, see # # (c) 2023 solidity contributors. # ------------------------------------------------------------------------------ import os import re import subprocess from shutil import which from textwrap import dedent from typing import Optional from runners.base import BaseRunner from test_helpers import SettingsPreset from test_helpers import settings_from_preset def run_forge_command(command: str, env: Optional[dict] = None): subprocess.run( command.split(), env=env if env is not None else os.environ.copy(), check=True ) class FoundryRunner(BaseRunner): """Configure and run Foundry-based projects""" FOUNDRY_CONFIG_FILE = "foundry.toml" def setup_environment(self): super().setup_environment() if which("forge") is None: raise RuntimeError("Forge not found.") @staticmethod def profile_name(preset: SettingsPreset): """Returns foundry profile name""" # Replace - or + by underscore to avoid invalid toml syntax return re.sub(r"(\-|\+)+", "_", preset.value) @staticmethod def profile_section(profile_fields: dict) -> str: return dedent("""\ [profile.{name}] gas_reports = ["*"] auto_detect_solc = false solc = "{solc}" evm_version = "{evm_version}" optimizer = {optimizer} via_ir = {via_ir} [profile.{name}.optimizer_details] yul = {yul} """).format(**profile_fields) @BaseRunner.enter_test_dir def configure(self): """Configure forge tests profiles""" profiles = [] for preset in self.presets: settings = settings_from_preset(preset, self.config.evm_version) profiles.append(self.profile_section({ "name": self.profile_name(preset), "solc": self.solc_binary_path, "evm_version": self.config.evm_version, "optimizer": str(settings["optimizer"]["enabled"]).lower(), "via_ir": str(settings["viaIR"]).lower(), "yul": str(settings["optimizer"]["details"]["yul"]).lower(), })) with open( file=self.test_dir / self.FOUNDRY_CONFIG_FILE, mode="a", encoding="utf-8", ) as f: for profile in profiles: f.write(profile) run_forge_command("forge install", self.env) @BaseRunner.enter_test_dir def compile(self, preset: SettingsPreset): """Compile project""" # Set the Foundry profile environment variable self.env.update({"FOUNDRY_PROFILE": self.profile_name(preset)}) run_forge_command("forge build", self.env) @BaseRunner.enter_test_dir def run_test(self): """Run project tests""" run_forge_command("forge test --gas-report", self.env)