From 5047beb1e2d73f0e81bdc4fdabd03c8690ed7008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Thu, 2 Jan 2025 12:45:02 +0100 Subject: [PATCH] Allow using newer solc on macOS without Rosetta `solc` universal binaries are now available, starting from 0.8.24. Allow Macs without Rosetta to run those binaries. --- README.md | 10 ++++++---- solc_select/__main__.py | 2 +- solc_select/solc_select.py | 17 +++++++++++++---- solc_select/utils.py | 11 +++++++++++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 154dc73..2ce6233 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ To automatically install and use a version, run `solc-select use --alw ### Running on ARM (Mac M1/M2) -`solc` requires Rosetta to be installed. See the FAQ on [how to install Rosetta](#oserror-errno-86-bad-cpu-type-in-executable). +`solc` older than 0.8.24 requires Rosetta to be installed. See the FAQ on [how to install Rosetta](#oserror-errno-86-bad-cpu-type-in-executable). ## Usage @@ -85,10 +85,12 @@ Feel free to stop by our [Slack channel](https://empirehacking.slack.com/) for h ### OSError: [Errno 86] Bad CPU type in executable On newer `solc-select` versions, this might show as `solc binaries for macOS are -Intel-only. Please install Rosetta on your Mac to continue.` +Intel-only. Please install Rosetta on your Mac to continue.` or `solc binaries +previous to 0.8.24 for macOS are Intel-only. Please install Rosetta on your Mac +to continue.` -`solc` requires Rosetta to be installed. To see whether you have Rosetta -installed on your Mac, run +`solc` releases earlier than 0.8.24 require Rosetta to be installed. To see +whether you have Rosetta installed on your Mac, run ```bash pgrep -q oahd && echo Rosetta is installed || echo Rosetta is NOT installed diff --git a/solc_select/__main__.py b/solc_select/__main__.py index 9025920..5756917 100644 --- a/solc_select/__main__.py +++ b/solc_select/__main__.py @@ -94,7 +94,7 @@ def solc() -> None: (version, _) = res path = ARTIFACTS_DIR.joinpath(f"solc-{version}", f"solc-{version}") halt_old_architecture(path) - halt_incompatible_system() + halt_incompatible_system(path) try: subprocess.run( [str(path)] + sys.argv[1:], diff --git a/solc_select/solc_select.py b/solc_select/solc_select.py index 3aca587..814e258 100644 --- a/solc_select/solc_select.py +++ b/solc_select/solc_select.py @@ -20,7 +20,7 @@ CRYTIC_SOLC_ARTIFACTS, CRYTIC_SOLC_JSON, ) -from .utils import mac_can_run_intel_binaries +from .utils import mac_binary_is_universal, mac_can_run_intel_binaries Path.mkdir(ARTIFACTS_DIR, parents=True, exist_ok=True) @@ -32,10 +32,19 @@ def halt_old_architecture(path: Path) -> None: ) -def halt_incompatible_system() -> None: - if soliditylang_platform() == MACOSX_AMD64 and not mac_can_run_intel_binaries(): +def halt_incompatible_system(path: Path) -> None: + if soliditylang_platform() == MACOSX_AMD64: + # If Rosetta is available, we can run all solc versions + if mac_can_run_intel_binaries(): + return + + # If this is a newer universal solc (>=0.8.24) we can always run it + # https://github.com/ethereum/solidity/issues/12291#issuecomment-2223328961 + if mac_binary_is_universal(path): + return + raise argparse.ArgumentTypeError( - "solc binaries for macOS are Intel-only. Please install Rosetta on your Mac to continue. Refer to the solc-select README for instructions." + "solc binaries previous to 0.8.24 for macOS are Intel-only. Please install Rosetta on your Mac to continue. Refer to the solc-select README for instructions." ) # TODO: check for Linux aarch64 (e.g. RPi), presence of QEMU+binfmt diff --git a/solc_select/utils.py b/solc_select/utils.py index 98b7305..5b908e8 100644 --- a/solc_select/utils.py +++ b/solc_select/utils.py @@ -1,3 +1,4 @@ +from pathlib import Path import platform import subprocess import sys @@ -6,6 +7,16 @@ from packaging.version import Version +def mac_binary_is_universal(path: Path): + """Check if the Mac binary is Universal or not. Will throw an exception if run on non-macOS.""" + assert sys.platform == "darwin" + result = subprocess.run(["/usr/bin/file", str(path)], capture_output=True, check=False) + is_universal = all( + text in result.stdout.decode() for text in ("Mach-O universal binary", "x86_64", "arm64") + ) + return result.returncode == 0 and is_universal + + def mac_can_run_intel_binaries() -> bool: """Check if the Mac is Intel or M1 with available Rosetta. Will throw an exception if run on non-macOS.""" assert sys.platform == "darwin"