Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion crytic_compile/platform/solc_standard_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ def run_solc_standard_json(
compiler_version: CompilerVersion,
solc_disable_warnings: bool = False,
working_dir: str | None = None,
solc: str | None = None,
solc_env: dict | None = None,
) -> dict:
"""Run the solc standard json compilation.
Ensure that crytic_compile.compiler_version is set prior calling _run_solc
Expand All @@ -149,6 +151,10 @@ def run_solc_standard_json(
compiler_version (CompilerVersion): info regarding the compiler
solc_disable_warnings (bool): True to not print the solc warnings. Defaults to False.
working_dir (Optional[str], optional): Working directory to run solc. Defaults to None.
solc (Optional[str], optional): Path or name of the solc binary to invoke. Defaults to
``compiler_version.compiler`` for backward compatibility.
solc_env (Optional[Dict], optional): Extra environment variables to set when running solc.
Defaults to None.

Raises:
InvalidCompilation: If the compilation failed
Expand All @@ -157,12 +163,15 @@ def run_solc_standard_json(
Dict: Solc json output
"""
working_dir_resolved = Path(working_dir if working_dir else ".").resolve()
cmd = [compiler_version.compiler, "--standard-json", "--allow-paths", str(working_dir_resolved)]
solc_binary = solc if solc else compiler_version.compiler
cmd = [solc_binary, "--standard-json", "--allow-paths", str(working_dir_resolved)]
cwd: str | None = working_dir if working_dir else None

env = dict(os.environ)
if compiler_version.version:
env["SOLC_VERSION"] = compiler_version.version
if solc_env:
env.update(solc_env)

stderr = ""
LOGGER.info(
Expand Down Expand Up @@ -494,6 +503,8 @@ def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None:
compilation_unit.compiler_version,
solc_disable_warnings=solc_disable_warnings,
working_dir=solc_working_dir,
solc=solc,
solc_env=solc_env,
)

parse_standard_json_output(
Expand Down
61 changes: 61 additions & 0 deletions tests/test_solc_standard_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
Tests for the SolcStandardJson platform.
"""

import json
from unittest import mock

from crytic_compile.compiler.compiler import CompilerVersion
from crytic_compile.platform.solc_standard_json import run_solc_standard_json


def _fake_popen(stdout: bytes):
"""Build a mock subprocess.Popen replacement returning the given stdout."""
fake_proc = mock.MagicMock()
fake_proc.communicate.return_value = (stdout, b"")
fake_ctx = mock.MagicMock()
fake_ctx.__enter__.return_value = fake_proc
fake_ctx.__exit__.return_value = False
return mock.MagicMock(return_value=fake_ctx)


def test_run_solc_standard_json_uses_default_compiler() -> None:
"""When no `solc` kwarg is passed, the binary from CompilerVersion is used."""
compiler_version = CompilerVersion(compiler="solc", version="0.8.0", optimized=False)
fake_popen = _fake_popen(json.dumps({"contracts": {}}).encode("utf-8"))

with mock.patch("crytic_compile.platform.solc_standard_json.subprocess.Popen", fake_popen):
run_solc_standard_json({"language": "Solidity"}, compiler_version)

cmd = fake_popen.call_args.args[0]
assert cmd[0] == "solc"


def test_run_solc_standard_json_respects_custom_solc_path() -> None:
"""A custom `solc` path is used as the executable instead of `compiler_version.compiler`."""
compiler_version = CompilerVersion(compiler="solc", version="0.8.33", optimized=False)
custom_path = "/custom/path/to/solc-0.8.33"
fake_popen = _fake_popen(json.dumps({"contracts": {}}).encode("utf-8"))

with mock.patch("crytic_compile.platform.solc_standard_json.subprocess.Popen", fake_popen):
run_solc_standard_json({"language": "Solidity"}, compiler_version, solc=custom_path)

cmd = fake_popen.call_args.args[0]
assert cmd[0] == custom_path


def test_run_solc_standard_json_merges_solc_env() -> None:
"""Extra env vars from `solc_env` are merged into the subprocess environment."""
compiler_version = CompilerVersion(compiler="solc", version="0.8.0", optimized=False)
fake_popen = _fake_popen(json.dumps({"contracts": {}}).encode("utf-8"))

with mock.patch("crytic_compile.platform.solc_standard_json.subprocess.Popen", fake_popen):
run_solc_standard_json(
{"language": "Solidity"},
compiler_version,
solc_env={"FOO": "bar"},
)

env = fake_popen.call_args.kwargs["env"]
assert env["FOO"] == "bar"
assert env["SOLC_VERSION"] == "0.8.0"