Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
56ecfd7
Use usd-exchange as the only standalone USD provider
hujc7 Aug 7, 2026
54940e4
Add an importers extra for the standalone URDF/MJCF importers
hujc7 Aug 7, 2026
79b38c7
Select a physics variant on converted assets
hujc7 Aug 7, 2026
ed60409
Run the converter tests in the kit-less image
hujc7 Aug 7, 2026
f97e1b2
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 7, 2026
d5e4cb9
Consolidate the changelog fragments
hujc7 Aug 8, 2026
2f6bf12
Address review findings on the importers extra
hujc7 Aug 8, 2026
153bdaf
Reject importers against an environment that already has Isaac Sim
hujc7 Aug 8, 2026
8817a8d
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 8, 2026
ecdc05f
Drop the aarch64-only usd-exchange override
hujc7 Aug 8, 2026
d8d4996
Update the install contracts for the importers feature
hujc7 Aug 8, 2026
021f83b
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 8, 2026
bd283f2
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 10, 2026
480c68d
Address review: keep public APIs and the smoke helper unchanged
hujc7 Aug 10, 2026
e1fcd0b
Document only the flag the wheel install actually needs
hujc7 Aug 10, 2026
a78748f
Select a physics backend for converter previews
hujc7 Aug 10, 2026
820b8b7
Fork only the importers extra in uv conflicts
hujc7 Aug 11, 2026
cf4b624
Document the importers extra in the installation guide
hujc7 Aug 11, 2026
f3652bf
Document the importers extra with verified commands
hujc7 Aug 11, 2026
25b7726
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 11, 2026
adb68ef
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 11, 2026
4ba8c8e
Sync uv.lock with the bumped package versions
hujc7 Aug 11, 2026
f947ebb
Trim the kit-less collection comment
hujc7 Aug 11, 2026
3b89155
Let the importers extra install beside Isaac Sim
hujc7 Aug 12, 2026
b4c27af
Drop the Isaac Sim incompatibility from the importers docs
hujc7 Aug 12, 2026
27cb826
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 12, 2026
33fbc2d
Reject a variant selection the set does not offer
hujc7 Aug 12, 2026
3a4cea8
Restore enum members inside lists and tuples
hujc7 Aug 12, 2026
eff5d53
Record the standalone USD provider in benchmark metadata
hujc7 Aug 12, 2026
f38ef4b
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 12, 2026
e7cc57d
Sync uv.lock with the bumped package versions
hujc7 Aug 12, 2026
574bd9f
Pin Newton after the extra features install
hujc7 Aug 12, 2026
60b37dd
Simplify converter comments and test fixtures
hujc7 Aug 12, 2026
b48a6c1
Install the standalone importers by default
hujc7 Aug 12, 2026
d7d1adf
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 12, 2026
77e6abd
Write converter test output under tmp_path
hujc7 Aug 12, 2026
223ea16
Merge remote-tracking branch 'upstream/develop' into jichuanh/usd-exc…
hujc7 Aug 12, 2026
2cd1a8f
Choose the importer backend by availability, not installation
hujc7 Aug 12, 2026
36c5514
Trim the importer backend comments to the rationale's one home
hujc7 Aug 12, 2026
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
16 changes: 8 additions & 8 deletions .github/actions/validate-kitless-image/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,20 @@ runs:
print("ovrtx renderer initialized")
'

# The Newton training probe is the shared one architecture CI also runs, so
# the command and its assertions live in one place. pytest goes only into
# this disposable layer; reinstalling the workspace would fail against the
# read-only source mount.
# Files are discovered by grep, not by pointing pytest at a directory: this image has no

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review — comment hygiene: this five-line narrative is longer than the behavior it protects and includes agentic maintenance instructions (“Adding coverage is … nothing here”). Reduce it to the functional constraint, e.g. “Collect marked files explicitly because importing the full source tree requires Isaac Sim; install pytest only in this disposable container.”

# Isaac Sim, and collecting a directory imports every module in it to read its markers,
# which most of the suite cannot survive. Adding coverage is a `kitless` marker, nothing
# here. pytest goes only into this disposable layer; reinstalling the workspace would fail
# against the read-only source mount.
docker run --rm --gpus all --network host \
"${mount_args[@]}" \
"$IMAGE_TAG" \
bash -lc '
set -euo pipefail
uv pip install --python "$VIRTUAL_ENV/bin/python" pytest
python -m pytest \
source/isaaclab/test/install_ci/misc/cartpole_training_smoke.py::test_train_cartpole_state_completes \
source/isaaclab_newton/test/test_mock_interfaces \
-q -p no:cacheprovider
mapfile -t suite < <(grep -rl --include="*.py" "pytest\.mark\.kitless" source/)
[ "${#suite[@]}" -gt 0 ] || { echo "::error::no tests carry the kitless marker"; exit 1; }
python -m pytest -m kitless "${suite[@]}" -q -p no:cacheprovider
'

runtime_identity="$(docker run --rm --entrypoint bash "$IMAGE_TAG" \
Expand Down
11 changes: 6 additions & 5 deletions docker/Dockerfile.kitless
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ COPY pyproject.toml uv.lock VERSION LICENSE LICENSE-mimic README.md ./
COPY source/ source/
COPY isaaclab.sh ./

# Same entry point as Dockerfile.base. The selector is explicit because OVRTX
# lives in the `ov` feature a bare --install excludes; OVPhysX stays out until
# its OmniClient pin is compatible. The venv pins python3.12 to match the
# runtime stage's libpython3.12, and isaaclab.sh resolves VIRTUAL_ENV first.
# Same entry point as Dockerfile.base. Selectors are explicit: OVRTX lives in the `ov`
# feature a bare --install excludes, and `importers` carries the standalone URDF/MJCF
# importers that replace the Isaac Sim ones. OVPhysX stays out until its OmniClient pin
# is compatible. The venv pins python3.12 to match the runtime stage's libpython3.12,
# and isaaclab.sh resolves VIRTUAL_ENV first.
RUN uv venv --python /usr/bin/python3.12 --seed --no-managed-python "${VIRTUAL_ENV}" \
&& chmod +x "${ISAACLAB_PATH}/isaaclab.sh" \
&& "${ISAACLAB_PATH}/isaaclab.sh" --install newton,rl[all],ov[ovrtx] \
&& "${ISAACLAB_PATH}/isaaclab.sh" --install newton,rl[all],ov[ovrtx],importers \
&& python -c "import importlib.metadata as m; \
names = {d.metadata['Name'].lower() for d in m.distributions()}; \
assert 'isaacsim' not in names; \
Expand Down
6 changes: 3 additions & 3 deletions docs/source/api/lab/isaaclab.sim.converters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Mesh Converter
:members:
:inherited-members:
:show-inheritance:
:exclude-members: __init__
:exclude-members: __init__, PhysicsVariant


URDF Converter
Expand All @@ -53,7 +53,7 @@ URDF Converter
:members:
:inherited-members:
:show-inheritance:
:exclude-members: __init__
:exclude-members: __init__, PhysicsVariant

MJCF Converter
--------------
Expand All @@ -67,4 +67,4 @@ MJCF Converter
:members:
:inherited-members:
:show-inheritance:
:exclude-members: __init__
:exclude-members: __init__, PhysicsVariant
25 changes: 15 additions & 10 deletions docs/source/how-to/import_new_asset.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ use the Omniverse Kit to edit the asset and export it to other file formats. Isa
these importers by default. They can also be enabled manually in Omniverse Kit.

Isaac Lab's URDF and MJCF converter utilities first use the importer APIs from
Isaac Sim when the full runtime is installed. In kit-less environments, install
the standalone importer wheel as described in
Isaac Sim when the full runtime is installed. In kit-less environments they use the
standalone importers that ship with Isaac Lab, as described in
:ref:`installation-standalone-importers` below.
The Kit visualizer and GUI import dialogs still require an Omniverse Kit runtime.

Expand All @@ -43,18 +43,23 @@ For more details on instanceable assets, please check the Isaac Sim `documentati
Standalone URDF/MJCF importers
------------------------------

The URDF and MJCF converter scripts can run without Isaac Sim when the standalone
``isaacsim-asset-isolated`` wheel is installed in the active environment. The wheel is not
published on PyPI, so replace ``PACKAGE_INDEX_URL`` with the package index that hosts it:
The URDF and MJCF converter scripts run without Isaac Sim, using the standalone
``isaacsim-asset-isolated`` importers. They ship in the ``importers`` extra, which is not
installed by default because it cannot share an environment with Isaac Sim -- both provide the
same ``isaacsim.asset`` importers. Install it from a source checkout:

.. code-block:: bash

uv pip install "isaacsim-asset-isolated>=6.0,<6.1" \
--extra-index-url "PACKAGE_INDEX_URL"
./isaaclab.sh --install 'newton,rl[rsl-rl],importers'

After installing the wheel, run conversion in the kit-less environment. Optionally pass
``--viz newton`` (or ``rerun`` / ``viser``) to preview the converted asset in a kit-less
Isaac Lab visualizer:
or into an environment built from the published wheel:

.. code-block:: bash

uv pip install 'isaaclab[importers]'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review — documentation correctness: this published-wheel flow runs outside the source checkout, so it does not inherit the repository [tool.uv] index strategy or prerelease settings. The PR description itself notes that this needs --index-strategy unsafe-best-match --prerelease allow, and the isolated wheel is on the NVIDIA index, so document the complete executable command including --extra-index-url https://pypi.nvidia.com. Also qualify the nearby “uv refuses” claim: uv sync sees [tool.uv].conflicts, but uv pip install from a wheel does not.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. The published-wheel command is gone: it cannot run today anyway, since the released isaaclab wheel carries no importers extra (No solution found: ... no wheels with a matching Python ABI tag).

The section now leads with uv sync --inexact --extra importers, matching how tetrahedralization, leapp and rlinf are documented, and attaches --extra to the conversion commands so they work without a separate install step. Every command in the section has been executed on this branch.


Run conversion in the kit-less environment. Optionally pass ``--viz newton`` (or ``rerun`` /
``viser``) to preview the converted asset in a kit-less Isaac Lab visualizer:

.. code-block:: bash

Expand Down
45 changes: 36 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ dependencies = [
"pin ; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'aarch64')",
"pin-pink==3.3.0 ; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'aarch64')",
"daqp==0.8.5 ; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'aarch64')",
# OpenUSD (kit-less mode). Both packages vendor a complete ``pxr`` runtime, so
# they must never co-install: usd-core supplies pxr where it has wheels (x86_64),
# usd-exchange supplies it on aarch64 (where usd-core has no wheel).
"usd-core>=25.11,<26.0 ; platform_machine == 'x86_64' or platform_machine == 'AMD64'",
"usd-exchange>=2.2 ; platform_machine == 'aarch64'",
# OpenUSD (kit-less mode). Use the same provider required by the URDF and MJCF importer
# 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 2.3.0 vendors USD 25.5, matching the Isaac Sim 6.0 wheel stack.
"usd-exchange==2.3.0",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're removing core completely in favor of exchange. Is there anything we're loosing? Is 2.3.0 OV libraries friendly?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be no? There's a commit to update usdex to 26.08. I checked with author and it's expected this month if everything goes well.

# 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 -----
Expand All @@ -88,9 +88,19 @@ dependencies = [
# ----- newton (default physics engine) -----
# Loose bound so the wheel co-resolves with isaacsim's newton[sim]==1.2.0 pin; the
# exact git commit is forced via [tool.uv].override-dependencies (uv sync only).
# The importers extra carries the mesh-processing deps (coacd, fast-simplification,
# ...) that honoring USD-authored ``physics:approximation`` requires.
"newton[sim,importers]>=1.2.0",
"newton[sim]>=1.2.0",
# Import and mesh-processing packages used by Newton, including the ones that honoring
# USD-authored ``physics:approximation`` requires. Keep these explicit instead of selecting
# newton[importers], whose standalone USD dependency would overlap with usd-exchange.
"requests>=2.25.0",
"scipy>=1.11.0",
"coacd>=1.0.7",
"fast-simplification>=0.1.11",
"alphashape>=1.3.1",
"meshio>=5.3.5",
"pycollada>=0.9",
"resolve-robotics-uri-py>=0.4.0",
"open3d>=0.19.0 ; python_version < '3.13' and (sys_platform != 'linux' or platform_machine != 'aarch64')",
# Loose bound co-resolves with isaacsim's ==0.2.0; the override below forces >=0.4.1.
"newton-usd-schemas>=0.2.0",
"PyOpenGL-accelerate>=3.1.0",
Expand Down Expand Up @@ -150,6 +160,19 @@ rerun = [

isaacsim = ["isaacsim[all,extscache]==6.0.1.0"]

# Packages that cannot share an environment with Isaac Sim, because they provide the same
# import paths it does. This is not "everything a kit-less setup installs" -- Newton, the RL
# frameworks and the visualizers work with and without Isaac Sim and stay in the base set.
# Membership rule: it belongs here only when co-installing it with Isaac Sim breaks imports.
#
# * isaacsim-asset-isolated -- standalone URDF/MJCF importers. Contributes to the
# ``isaacsim`` namespace, so alongside a real Isaac Sim it displaces the Kit extension
# serving the same import and conversion fails with ``No module named 'isaacsim.asset'``.
#
# Runtime-only incompatibilities (ovrtx, ovphysx) do NOT belong here: they install fine
# beside Isaac Sim and are rejected at launch by ``sim_launcher._validate_runtime``.
importers = ["isaacsim-asset-isolated>=6.0,<6.1"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-generated review — comment hygiene: the 12-line preamble repeats the user guide, changelog, and PR rationale. Keep only the durable operational invariant next to the extra, for example: “Standalone URDF/MJCF importers; conflicts with Isaac Sim because both provide isaacsim.asset.” Historical provider details and failure narratives belong in the documentation or PR.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed to the two lines you suggested.


ov = ["ovphysx==0.5.9", "ovrtx>=0.4.0,<0.5.0", "ovstage==0.1.0.346039"]
ovphysx = ["ovphysx==0.5.9", "ovstage==0.1.0.346039"]
ovrtx = ["ovrtx>=0.4.0,<0.5.0", "ovstage==0.1.0.346039"]
Expand Down Expand Up @@ -200,6 +223,7 @@ leapp = [
# ``test_version_single_source`` fails CI if they drift.
[tool.isaaclab.versions]
isaacsim = "6.0.1.0"
usd_exchange = "2.3.0"
torch = "2.11.0"
torchvision = "0.26.0"
torchaudio = "2.11.0"
Expand Down Expand Up @@ -343,6 +367,7 @@ markers = [
"benchmark: test covers the Isaac Lab benchmark framework and infrastructure",
"rendering: test exercises the rendering / camera / visualizer pipeline",
"smoke: tests for core installation, task, and RL functionality",
"importers: test must pass inside the Kit-less container, which has no Isaac Sim runtime",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Register the kitless marker

The new converter tests and container selection use pytest.mark.kitless, but this configuration registers importers instead. This produces unknown-marker warnings and makes collection fail under --strict-markers.

Suggested change
"importers: test must pass inside the Kit-less container, which has no Isaac Sim runtime",
"kitless: test must pass inside the Kit-less container, which has no Isaac Sim runtime",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2f6bf12. The marker is now registered as kitless, matching the six test files and the -m kitless selection in validate-kitless-image. It had been renamed along with the extra by a bulk rename that should only have touched the extra name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning · Implementation — Registered marker name does not match tests

The tests and .github/actions/validate-kitless-image/action.yml use pytest.mark.kitless (grep for pytest\.mark\.kitless, then pytest -m kitless), but the marker registered here is named importers. So kitless stays unregistered and raises PytestUnknownMarkWarning on every marked module (failing under --strict-markers), while the registered importers marker selects nothing. Rename the registration to kitless.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2f6bf12 — registered as kitless. Caused by a bulk rename of the extra (kitlessimporters) that also caught the marker string, leaving all six marked modules with an unregistered marker.

]

# Add pypi.nvidia.com so that `uv pip install isaaclab[isaacsim]` works without --extra-index-url.
Expand Down Expand Up @@ -388,7 +413,7 @@ override-dependencies = [
"numpy>=2",
"mujoco~=3.11.0",
"mujoco-warp~=3.11.0",
"newton[sim,importers] @ git+https://github.com/newton-physics/newton.git@release-1.5",
"newton[sim] @ git+https://github.com/newton-physics/newton.git@release-1.5",
# Force the Newton-matched schemas over isaacsim's ==0.2.0 pin.
"newton-usd-schemas>=0.4.1",
"torch==2.11.0",
Expand All @@ -408,6 +433,8 @@ conflicts = [
[{ extra = "teleop" }, { extra = "ovphysx" }],
[{ extra = "isaacsim" }, { extra = "ov" }],
[{ extra = "isaacsim" }, { extra = "ovphysx" }],
# both supply the ``isaacsim.asset`` importers; together the wheel shadows the Kit extension
[{ extra = "isaacsim" }, { extra = "importers" }],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning · Design Architecture — teleop does not inherit the importers conflict

The teleop extra installs isaacsim[all,extscache] directly, which is why the table already mirrors (isaacsim, ov) with (teleop, ov). Only (isaacsim, importers) was added, so uv sync --extra teleop --extra importers resolves and ./isaaclab.sh -i teleop,importers passes the new check, co-installing isaacsim-asset-isolated beside a real Isaac Sim — exactly the shadowing this extra exists to prevent. Add (teleop, importers).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2f6bf12: added [{ extra = "teleop" }, { extra = "importers" }]. Correct — teleop lists isaacsim[all,extscache]==6.0.1.0 directly, so it inherits the incompatibility, and the table already mirrored every isaacsim pair with a teleop one.

Note that after merging develop the ovphysx pairs were removed upstream, so the table is now teleop×ov, isaacsim×ov, isaacsim×importers, teleop×importers.

]
python-preference = "only-managed"
package = false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Added
^^^^^

* Added an ``importers`` extra carrying the standalone URDF and MJCF importers, so conversion
works without Isaac Sim. It cannot be combined with the ``isaacsim`` extra, which ships its own
copies of the same importers.

* Added :attr:`~isaaclab.sim.converters.AssetConverterBaseCfg.physics_variant` to choose which
``"Physics"`` variant the URDF and MJCF converters select on the generated USD file.

Fixed
^^^^^

* Fixed URDF and MJCF conversion producing assets with no joints, articulation roots, or mass
properties.

* Fixed MJCF conversion failing with ``Cannot find a valid schema for 'MjcSceneAPI'`` when another
package queried a USD schema first.

* Fixed :meth:`~isaaclab.utils.dict.class_to_dict` expanding enum values into their internal
members, which wrote unusable entries into serialized configurations.

Changed
^^^^^^^

* Changed :func:`~isaaclab.sim.utils.select_usd_variants` to raise for a variant set listed in
:obj:`~isaaclab.sim.utils.REQUIRED_VARIANT_SETS` that is absent or does not offer the requested
variant. Other variant sets still log a warning and continue.

* Changed ``./isaaclab.sh --install`` to reject extras that the root ``pyproject.toml`` declares
conflicting, instead of installing a combination ``uv sync`` refuses.
6 changes: 6 additions & 0 deletions source/isaaclab/changelog.d/kellyguo11-use-usd-exchange.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fixed
^^^^^

* Fixed installation failures caused by overlapping standalone USD providers by using
``usd-exchange`` on all supported platforms and installing required Newton mesh-processing
packages directly.
27 changes: 27 additions & 0 deletions source/isaaclab/isaaclab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""Package containing the core framework."""

import importlib.metadata
import importlib.util
import os
import sys

Expand Down Expand Up @@ -79,7 +80,33 @@ def _should_demote(path: str) -> bool:
os.environ["PYTHONPATH"] = os.pathsep.join(env_clean + env_demoted)


def _expose_mujoco_usd_schemas():
"""Put the MuJoCo USD schemas on OpenUSD's plugin search path.

``mujoco-usd-converter`` ships them as a codeless schema plugin that it registers when the
package is imported. OpenUSD builds its schema registry once, on the first schema query, and
ignores plugins registered after that -- so MJCF conversion fails with "Cannot find a valid
schema for ``MjcSceneAPI``" whenever anything touched a schema first. Adding the plugin to the
search path lets OpenUSD find it while building the registry, so importing the converter late
no longer matters.

OpenUSD reads the search path while building the registry, so this only helps while the
registry is still unbuilt. A host that queries a schema before importing Isaac Lab has to put
the plugin directory on ``PXR_PLUGINPATH_NAME`` itself.
"""
spec = importlib.util.find_spec("mujoco_usd_converter")
if spec is None or spec.origin is None:
return
plugins = os.path.join(os.path.dirname(spec.origin), "plugins")
if not os.path.isdir(plugins):
return
search_path = os.environ.get("PXR_PLUGINPATH_NAME", "")
if plugins not in search_path.split(os.pathsep):
os.environ["PXR_PLUGINPATH_NAME"] = os.pathsep.join(filter(None, (search_path, plugins)))


_deprioritize_prebundle_paths()
_expose_mujoco_usd_schemas()


try:
Expand Down
55 changes: 53 additions & 2 deletions source/isaaclab/isaaclab/cli/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,15 +674,17 @@ def _install_isaacsim() -> None:
# core set.
VALID_EXTRA_FEATURES: set[str] = {
"contrib",
"importers",
"newton",
"ov",
"rl",
"tetrahedralization",
"visualizer",
}

# Extra features excluded from the automatic ``-i all`` / ``-i`` install.
MANUAL_EXTRA_FEATURES: set[str] = {"contrib", "ov", "tetrahedralization"}
# Extra features excluded from the automatic ``-i all`` / ``-i`` install. ``importers`` is
# here because it cannot coexist with Isaac Sim, so it must be asked for deliberately.
MANUAL_EXTRA_FEATURES: set[str] = {"contrib", "importers", "ov", "tetrahedralization"}


def split_install_items(install_type: str) -> list[str]:
Expand Down Expand Up @@ -817,6 +819,11 @@ def _install_extra_feature(feature_name: str, selector: str = "") -> None:
"""
if feature_name == "contrib":
_install_contrib_extra_dependencies(selector)
elif feature_name == "importers":
if selector:
print_warning(f"'importers' does not support selectors (got '{selector}').")
print_info("Installing the kit-less stand-ins for Isaac Sim (URDF/MJCF importers)...")
_install_root_extra("importers")
elif feature_name == "newton":
if selector:
print_warning(f"'newton' does not support selectors (got '{selector}').")
Expand Down Expand Up @@ -1097,6 +1104,48 @@ def _repoint_prebundle_packages() -> None:
)


def _requested_root_extras(
install_isaacsim: bool, optional_submodules: list[str], extra_features: list[tuple[str, str]]
) -> set[str]:
"""Return the root extras this install would apply.

Tokens are not extras: ``ov[ovrtx]`` installs ``ovrtx``, not ``ov``.
"""
extras = set(optional_submodules)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Suggestion · Implementation — Submodule tokens are not mapped to root extras

set(optional_submodules) stores the CLI token, but OPTIONAL_SUBMODULE_ROOT_EXTRAS maps mimic to the teleop and mimic extras. So -i mimic,ov[ovphysx] installs the Isaac-Sim-bearing teleop extra while the declared {teleop, ovphysx} conflict never matches, leaving one of the pairs this function claims to enforce unenforced. Expand submodule tokens through OPTIONAL_SUBMODULE_ROOT_EXTRAS first.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2f6bf12: optional submodule tokens are expanded through OPTIONAL_SUBMODULE_ROOT_EXTRAS before the conflict check, so -i mimic contributes both mimic and teleop.

Verified at the time: mimic + ov[ovphysx] was rejected. After merging develop the ovphysx conflict pairs no longer exist upstream, so that specific pair is legal again — the check reads [tool.uv].conflicts rather than hardcoding, so it tracked the change.

if install_isaacsim:
extras.add("isaacsim")
for feature, selector in extra_features:
if feature == "ov":
chosen = {item.strip().lower() for item in selector.split(",") if item.strip()}
extras |= {"ovphysx", "ovrtx"} if "all" in chosen else chosen
else:
extras.add(feature)
return extras


def _reject_conflicting_extras(requested: set[str]) -> None:
"""Reject extras that ``[tool.uv].conflicts`` declares incompatible.

That table only binds a resolver, and each feature here installs in its own pip pass, so
nothing else would catch the combination.

Args:
requested: Names of the extras this install would apply.

Raises:
SystemExit: When the request covers every extra of a declared conflict.
"""
conflicts = _load_root_pyproject().get("tool", {}).get("uv", {}).get("conflicts", [])
for conflict in conflicts:
extras = {entry["extra"] for entry in conflict if "extra" in entry}
if len(extras) > 1 and extras <= requested:
names = ", ".join(f"'{extra}'" for extra in sorted(extras))
raise SystemExit(
f"error: {names} cannot be installed together; the root pyproject.toml declares"
" them conflicting. Drop one of the tokens."
)


def command_install(install_type: str = "all") -> None:
"""Install Isaac Lab extensions and optional extras.

Expand Down Expand Up @@ -1195,6 +1244,8 @@ def append_submodules_once(package_dirs: tuple[str, ...]) -> None:
valid = sorted(OPTIONAL_ISAACLAB_SUBMODULES) + sorted(VALID_EXTRA_FEATURES) + ["isaacsim"]
print_warning(f"Unknown install token '{name}'. Valid values: {', '.join(valid)}. Skipping.")

_reject_conflicting_extras(_requested_root_extras(install_isaacsim, requested_optional_submodules, extra_features))

# Configure extra package indexes for NVIDIA and MuJoCo wheels.
os.environ.setdefault("UV_EXTRA_INDEX_URL", "https://pypi.nvidia.com")
os.environ.setdefault("PIP_EXTRA_INDEX_URL", "https://pypi.nvidia.com")
Expand Down
Loading
Loading