Skip to content

Commit

Permalink
Check if Rosetta 2 is enabled in the Conda and Docker runner checks
Browse files Browse the repository at this point in the history
When on macOS on aarch64 hardware, the Conda runtime currently requires
Rosetta.  The Docker runtime does not require it, but can benefit from it
because our aarch64 image isn't totally free of x86_64 code (yet?),
so emulation is required and Rosetta is faster than QEMU.

The standalone installer still checks for Rosetta as we previously
didn't produce aarch64 standalone binaries¹, but that check will be made
conditional on Nextstrain CLI version in a subsequent commit² after the
first release of aarch64 support and these new Rosetta checks.

¹ <#357>
² <#358>
  • Loading branch information
tsibley committed Feb 2, 2024
1 parent f2d51d1 commit 8856915
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 5 deletions.
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ development source code and as such may not be routinely kept up to date.
([#357](https://github.com/nextstrain/cli/pull/357),
[#358](https://github.com/nextstrain/cli/pull/358))

* The Conda and Docker runtime checks performed by `nextstrain setup` and
`nextstrain check-setup` now test if Rosetta 2 is enabled for macOS on
aarch64 (aka arm64, Apple Silicon, M1/M2) hardware. Rosetta is required for
the Conda runtime and optional, but recommended, for the Docker runtime.
Previously only the standalone installer checked for Rosetta, but starting
with this release it will not.
([#361](https://github.com/nextstrain/cli/pull/361),
[#358](https://github.com/nextstrain/cli/pull/358))

* `nextstrain build` now errors if a [development overlay option][] such as
`--augur` or `--auspice` is given when using a runtime without support for
those (anything but Docker or Singularity). Previously, it would silently
Expand Down
5 changes: 3 additions & 2 deletions nextstrain/cli/runner/conda.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
from ..errors import InternalError
from ..paths import RUNTIMES
from ..types import Env, RunnerSetupStatus, RunnerTestResults, RunnerUpdateStatus
from ..util import capture_output, colored, exec_or_return, runner_tests_ok, warn
from ..util import capture_output, colored, exec_or_return, runner_tests_ok, test_rosetta_enabled, warn


RUNTIME_ROOT = RUNTIMES / "conda/"
Expand Down Expand Up @@ -467,11 +467,12 @@ def supported_os() -> bool:
else:
return False


return [
('operating system is supported',
supported_os()),

*test_rosetta_enabled(),

("runtime data dir doesn't have spaces",
" " not in str(RUNTIME_ROOT)),
]
Expand Down
9 changes: 7 additions & 2 deletions nextstrain/cli/runner/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
from .. import config, env
from ..errors import UserError
from ..types import Env, RunnerSetupStatus, RunnerTestResults, RunnerTestResultStatus, RunnerUpdateStatus
from ..util import warn, colored, capture_output, exec_or_return, split_image_name
from ..util import warn, colored, capture_output, exec_or_return, split_image_name, test_rosetta_enabled
from ..volume import store_volume, NamedVolume
from ..__version__ import __version__

Expand Down Expand Up @@ -387,7 +387,12 @@ def test_image_version():
('docker run works',
test_run()),
*test_memory_limit(),
*test_image_version()
*test_image_version(),

# Rosetta 2 is optional, so convert False (fail) → None (warning)
*[(msg, None if status is False else status)
for msg, status
in test_rosetta_enabled("Rosetta 2 is enabled for faster execution (optional)")],
]


Expand Down
43 changes: 42 additions & 1 deletion nextstrain/cli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from wcmatch.glob import globmatch, GLOBSTAR, EXTGLOB, BRACE, MATCHBASE, NEGATE
from .__version__ import __version__
from .debug import debug
from .types import RunnerModule, RunnerTestResults
from .types import RunnerModule, RunnerTestResults, RunnerTestResultStatus


def warn(*args):
Expand Down Expand Up @@ -603,6 +603,47 @@ def print_runner_tests(tests: RunnerTestResults):
print(status.get(result, str(result)) + ":", formatted_description)


def test_rosetta_enabled(msg: str = "Rosetta 2 is enabled") -> RunnerTestResults:
"""
Check if Rosetta 2 is enabled (installed and active) on macOS aarch64
systems.
"""
if (platform.system(), platform.machine()) != ("Darwin", "arm64"):
return []

status: RunnerTestResultStatus = ... # unknown

try:
subprocess.run(
["pgrep", "-qU", "_oahd"],
stdin = subprocess.DEVNULL,
stdout = subprocess.DEVNULL,
stderr = subprocess.DEVNULL,
check = True)

except OSError:
status = ... # unknown; something's maybe wrong with our check

except subprocess.CalledProcessError as err:
if err.returncode > 0:
status = False
msg += dedent("""
To enable Rosetta, please run:
softwareupdate --install-rosetta
and then try your setup or check-setup command again.\
""")
else:
status = None # warning; something's weird about the system
msg += " (unable to check!)"
else:
status = True

return [(msg, status)]


# Copied without modification from lib/id3c/api/utils/__init__.py in the ID3C
# project¹, which is licensed under the MIT license. See the LICENSE.id3c file
# distributed alongside this project's own LICENSE file.
Expand Down

0 comments on commit 8856915

Please sign in to comment.