Repository navigation
[Mac OS] Support macOS arm64 (CPU-only) in the uv environment #8191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -224,6 +224,24 @@ Install ``uv``, clone Isaac Lab, and start a workflow: | |
| :: Play a policy | ||
| uv run isaaclab play --rl_library rsl_rl --task Isaac-Cartpole-Direct --viz newton | ||
|
|
||
| .. tab-item:: :icon:`fa-brands fa-apple` macOS (Apple silicon) | ||
| :sync: macos-arm64 | ||
|
|
||
| macOS support is experimental and CPU-only. It covers the Newton backend without Isaac Sim; | ||
| Isaac Sim, the OV PhysX and OV RTX backends, and the ``importers`` extra are unavailable. | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| curl -LsSf https://astral.sh/uv/install.sh | sh | ||
|
|
||
| .. isaaclab-clone-commands:: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| # Newton backend on the CPU | ||
| uv run isaaclab train --rl_library rsl_rl \ | ||
| --task Isaac-Cartpole-Direct --device cpu physics=newton_mjwarp | ||
|
Comment on lines
+242
to
+243
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this macOS example, |
||
|
|
||
| ``uv run`` installs the core dependencies automatically. The ``--extra <name>`` | ||
| option includes the selected optional integration in the command's environment. Place it | ||
| before ``isaaclab``; for example, ``--extra ov`` installs both ovphysx and ovrtx | ||
|
|
@@ -243,8 +261,8 @@ See :ref:`installation-optional-extras` for the available extras. | |
| ``uv run --extra <name> <command>`` syncs the selected extra into the project environment | ||
| and then runs the command. | ||
|
|
||
| The source checkout selects PyTorch's CUDA 13.0 build on Linux x86_64, Linux aarch64, and Windows. | ||
| No additional command flags are needed. | ||
| The source checkout selects PyTorch's CUDA 13.0 build on Linux x86_64, Linux aarch64, and Windows, | ||
| and the PyPI build on macOS. No additional command flags are needed. | ||
| The published wheel pins the PyTorch versions, but downstream uv projects must configure their own | ||
| PyTorch indexes because uv does not inherit a dependency project's ``tool.uv.sources`` settings. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,8 @@ dependencies = [ | |
| "pillow>=12.3.0", | ||
| "botocore", # omni.replicator.core S3 backend | ||
| "starlette>=1.3.1", # livestream; minimum patched for current security advisories | ||
| "omniverseclient==2.74.0", | ||
| # no macOS wheels; isaaclab.utils.assets reads HTTP(S) assets directly without it | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On macOS, the documented Cartpole task needs a remote USD file. This change excludes |
||
| "omniverseclient==2.74.0 ; sys_platform != 'darwin'", | ||
| "filelock", | ||
| "lazy_loader>=0.4", | ||
| # ----- project template generator ----- | ||
|
|
@@ -72,7 +73,9 @@ dependencies = [ | |
| # packages so another complete ``pxr`` runtime cannot co-install with it. Two providers in | ||
| # one environment overwrite each other's files, and removing either then breaks ``pxr``. | ||
| # usd-exchange 3.0.0 vendors USD 26.08; the isaacsim 6.1 importer pins are overridden below. | ||
| "usd-exchange==3.0.0", | ||
| "usd-exchange==3.0.0 ; sys_platform != 'darwin'", | ||
| # usd-exchange ships no macOS wheels, so macOS uses usd-core as its only pxr provider. | ||
| "usd-core ; sys_platform == 'darwin'", | ||
| # avoid broken hf-xet pre-release cached on NVIDIA Artifactory | ||
| "hf-xet>=1.4.1,<2.0.0 ; platform_machine == 'x86_64' or platform_machine == 'AMD64' or platform_machine == 'aarch64'", | ||
| # ----- tasks ----- | ||
|
|
@@ -117,7 +120,7 @@ tetrahedralization = ["pytetwild[all]>=0.3.0,<0.4"] | |
| video = ["moviepy>=1.0.3,<2.0.0.dev0"] | ||
|
|
||
| importers = [ | ||
| "isaacsim-asset-isolated==6.1.0.0", | ||
| "isaacsim-asset-isolated==6.1.0.0 ; sys_platform != 'darwin'", # depends on usd-optimize, which has no macOS wheels | ||
| "tinyobjloader==2.0.0rc13", | ||
| ] | ||
|
|
||
|
|
@@ -175,11 +178,17 @@ rerun = [ | |
| "pyarrow==23.0.1", | ||
| ] | ||
|
|
||
| isaacsim = ["isaacsim[all,extscache]==6.1.0.0"] | ||
| # Isaac Sim does not support macOS. | ||
| isaacsim = ["isaacsim[all,extscache]==6.1.0.0 ; sys_platform != 'darwin'"] | ||
|
|
||
| ov = ["ovphysx==0.6.3", "ovrtx==0.5.0.377615", "ovstage==0.2.0.377349"] | ||
| ovphysx = ["ovphysx==0.6.3", "ovstage==0.2.0.377349"] | ||
| ovrtx = ["ovrtx==0.5.0.377615", "ovstage==0.2.0.377349"] | ||
| # The OV runtimes ship no macOS wheels. | ||
| ov = [ | ||
| "ovphysx==0.6.3 ; sys_platform != 'darwin'", | ||
| "ovrtx==0.5.0.377615 ; sys_platform != 'darwin'", | ||
| "ovstage==0.2.0.377349 ; sys_platform != 'darwin'", | ||
| ] | ||
| ovphysx = ["ovphysx==0.6.3 ; sys_platform != 'darwin'", "ovstage==0.2.0.377349 ; sys_platform != 'darwin'"] | ||
| ovrtx = ["ovrtx==0.5.0.377615 ; sys_platform != 'darwin'", "ovstage==0.2.0.377349 ; sys_platform != 'darwin'"] | ||
|
|
||
| mimic = [ | ||
| "isaaclab-mimic", | ||
|
|
@@ -397,12 +406,13 @@ index-strategy = "unsafe-best-match" | |
| prerelease = "allow" | ||
| # Platforms Isaac Lab supports (docs/source/setup/installation/index.rst and the | ||
| # install CI matrix): Ubuntu 22.04 x86_64, Linux aarch64 (DGX Spark / Jetson), and | ||
| # Windows 11 x86_64. Restricting the resolution universe keeps macOS and other | ||
| # unsupported wheels out of uv.lock. | ||
| # Windows 11 x86_64, plus experimental CPU-only macOS on Apple silicon. Restricting the | ||
| # resolution universe keeps other unsupported wheels out of uv.lock. | ||
| environments = [ | ||
| "sys_platform == 'linux' and platform_machine == 'x86_64'", | ||
| "sys_platform == 'linux' and platform_machine == 'aarch64'", | ||
| "sys_platform == 'win32' and platform_machine == 'AMD64'", | ||
| "sys_platform == 'darwin' and platform_machine == 'arm64'", | ||
| ] | ||
| # Isaac Lab owns the Newton, MuJoCo, and torch versions. Overrides replace requirements | ||
| # for every requester, unlike constraints, which only intersect. The ``isaacsim`` and | ||
|
|
@@ -416,7 +426,8 @@ override-dependencies = [ | |
| # Force the Newton-matched schemas over isaacsim's ==0.2.0 pin. | ||
| "newton-usd-schemas>=0.5.0", | ||
| # isaacsim-asset-isolated and isaacsim-robot-schema 6.1 pin usd-exchange ==2.3.0. | ||
| "usd-exchange==3.0.0", | ||
| # Overrides replace markers too, so this one repeats the macOS exclusion. | ||
| "usd-exchange==3.0.0 ; sys_platform != 'darwin'", | ||
| "torch==2.12.0", | ||
| "torchvision==0.27.0", | ||
| # ovphysx caps packaging at <24 and isaacsim-core pins ==26.0; both caps are | ||
|
|
@@ -448,9 +459,19 @@ isaaclab = { path = "source/isaaclab", editable = true } | |
| "isaaclab-tasks-experimental" = { path = "source/isaaclab_tasks_experimental", editable = true } | ||
| "isaaclab-teleop" = { path = "source/isaaclab_teleop", editable = true } | ||
| "isaaclab-visualizers" = { path = "source/isaaclab_visualizers", editable = true } | ||
| torch = { index = "pytorch-cu130" } | ||
| torchaudio = { index = "pytorch-cu130" } | ||
| torchvision = { index = "pytorch-cu130" } | ||
| # macOS has no CUDA build; PyPI ships its CPU/MPS wheels. | ||
| torch = [ | ||
| { index = "pytorch-cu130", marker = "sys_platform != 'darwin'" }, | ||
| { index = "pypi-public", marker = "sys_platform == 'darwin'" }, | ||
| ] | ||
| torchaudio = [ | ||
| { index = "pytorch-cu130", marker = "sys_platform != 'darwin'" }, | ||
| { index = "pypi-public", marker = "sys_platform == 'darwin'" }, | ||
| ] | ||
| torchvision = [ | ||
| { index = "pytorch-cu130", marker = "sys_platform != 'darwin'" }, | ||
| { index = "pypi-public", marker = "sys_platform == 'darwin'" }, | ||
| ] | ||
|
|
||
| [tool.uv.pip] | ||
| index-strategy = "unsafe-best-match" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| Added | ||
| ^^^^^ | ||
|
|
||
| * Added experimental CPU-only macOS (Apple silicon) support to the source checkout's uv environment. | ||
| It installs PyPI's PyTorch build and ``usd-core``, and skips Isaac Sim, the OV runtimes, | ||
| ``omniverseclient``, and the ``importers`` extra, none of which ship macOS wheels. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,20 +12,46 @@ | |
|
|
||
| import pytest | ||
| import tomllib | ||
| from packaging.markers import Marker | ||
| from packaging.requirements import Requirement | ||
|
|
||
| pytestmark = pytest.mark.unit | ||
|
|
||
|
|
||
| _CUDA_ENVIRONMENTS = ( | ||
| {"sys_platform": "linux", "platform_machine": "x86_64"}, | ||
| {"sys_platform": "linux", "platform_machine": "aarch64"}, | ||
| {"sys_platform": "win32", "platform_machine": "AMD64"}, | ||
| ) | ||
| """Supported CUDA platforms from ``[tool.uv].environments``.""" | ||
|
|
||
| _MACOS_ENVIRONMENT = {"sys_platform": "darwin", "platform_machine": "arm64"} | ||
| """Supported macOS platform from ``[tool.uv].environments``, which runs on the CPU only.""" | ||
|
|
||
|
|
||
| def _applies(marker: str | None, environment: dict[str, str]) -> bool: | ||
| """Whether a requirement or lock marker applies in ``environment``.""" | ||
| return marker is None or Marker(marker).evaluate(environment) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("name", ["torch", "torchvision", "torchaudio"]) | ||
| def test_resolved_torch_stack_supports_blackwell(source_checkout_root: Path, name: str): | ||
| """All supported platforms need CUDA 13 wheels; PyTorch 2.12's cu126 excludes Blackwell.""" | ||
| """CUDA platforms need CUDA 13 wheels; PyTorch 2.12's cu126 excludes Blackwell. macOS uses PyPI's build.""" | ||
| with (source_checkout_root / "uv.lock").open("rb") as f: | ||
| lock = tomllib.load(f) | ||
|
|
||
| packages = [package for package in lock["package"] if package["name"] == name] | ||
| assert packages | ||
| assert all(package["version"].endswith("+cu130") for package in packages) | ||
| assert all(package["source"]["registry"] == "https://download.pytorch.org/whl/cu130" for package in packages) | ||
| cuda = [ | ||
| package | ||
| for package in packages | ||
| if any(_applies(m, env) for m in package["resolution-markers"] for env in _CUDA_ENVIRONMENTS) | ||
| ] | ||
| assert cuda | ||
| assert all(package["version"].endswith("+cu130") for package in cuda) | ||
| assert all(package["source"]["registry"] == "https://download.pytorch.org/whl/cu130" for package in cuda) | ||
| assert all( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Warning · Test Quality — macOS torch assertion passes vacuously The CUDA half is guarded by |
||
| package["source"]["registry"] == "https://pypi.org/simple" for package in packages if package not in cuda | ||
| ) | ||
|
Comment on lines
+52
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
|
|
||
| def _requirement_name(requirement: str) -> str: | ||
|
|
@@ -50,17 +76,31 @@ def test_resolved_environment_has_no_second_usd_provider(source_checkout_root: P | |
| ``newton[importers]``, ``mujoco[usd]`` and ``warp-lang[examples]`` all require it, so selecting | ||
| any of them would reinstate the overlap that the direct dependencies avoid. Checking the lock | ||
| catches that, where checking ``pyproject.toml`` alone would not. | ||
|
|
||
| ``usd-exchange`` ships no macOS wheels, so macOS installs ``usd-core`` as its only provider instead. | ||
| """ | ||
| with (source_checkout_root / "uv.lock").open("rb") as f: | ||
| lock = tomllib.load(f) | ||
|
|
||
| direct_names = [_requirement_name(dep) for dep in _root_project(source_checkout_root)["dependencies"]] | ||
| assert [name for name in direct_names if name in ("usd-core", "usd-exchange")] == ["usd-exchange"] | ||
|
|
||
| locked = {package["name"] for package in lock["package"]} | ||
|
|
||
| assert "usd-core" not in locked | ||
| assert "usd-exchange" in locked | ||
| providers = [ | ||
| Requirement(dep) | ||
| for dep in _root_project(source_checkout_root)["dependencies"] | ||
| if _requirement_name(dep) in ("usd-core", "usd-exchange") | ||
| ] | ||
| for environment in (*_CUDA_ENVIRONMENTS, _MACOS_ENVIRONMENT): | ||
| installed = [req.name for req in providers if _applies(str(req.marker) if req.marker else None, environment)] | ||
| assert installed == (["usd-core"] if environment is _MACOS_ENVIRONMENT else ["usd-exchange"]) | ||
|
|
||
| usd_core_edges = [ | ||
| dep | ||
| for package in lock["package"] | ||
| for deps in [package.get("dependencies", []), *package.get("optional-dependencies", {}).values()] | ||
| for dep in deps | ||
| if dep["name"] == "usd-core" | ||
| ] | ||
| assert usd_core_edges | ||
| assert not any(_applies(dep.get("marker"), env) for dep in usd_core_edges for env in _CUDA_ENVIRONMENTS) | ||
| assert "usd-exchange" in {package["name"] for package in lock["package"]} | ||
|
Comment on lines
+102
to
+103
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This test rules out |
||
|
|
||
|
|
||
| def test_standalone_importers_are_opt_in(source_checkout_root: Path): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| Changed | ||
| ^^^^^^^ | ||
|
|
||
| * Changed the ``ovstage`` dependency to skip macOS, which it ships no wheels for, so the package | ||
| installs in the macOS uv environment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔵 Suggestion · Style Consistency — Docs overstate importers extra removal
pyproject.tomlexcludes onlyisaacsim-asset-isolatedon darwin;tinyobjloaderstill resolves, souv run --extra importersinstalls on macOS. Saying the whole extra is unavailable contradicts the metadata, and the changelog fragment repeats the claim. Reword both to state that the standalone Isaac Sim asset importer is unavailable.