Skip to content

Resolve each dependency where it is linked, and prove plain CMake without ost - #230

Merged
snkmcb merged 3 commits into
mainfrom
cmake/consumer-owned-dependencies
Sep 24, 2026
Merged

snkmcb merged 3 commits into
mainfrom
cmake/consumer-owned-dependencies

Conversation

@snkmcb

@snkmcb snkmcb commented Sep 24, 2026

Copy link
Copy Markdown
Member

This PR makes plain CMake a proven build contract. usd-motion-plugins is consumed only as an installed package, each member declares exactly the packages it uses, and a CI lane with no ost in it builds and tests the workspace.

Dependency boundary

  • The root resolves no consumed package. It used to find_package all five motion packages, so every configure required every package, including motionRecording, which nothing here includes. Each member now resolves what it links through usdvrm_consume_package() (cmake/UsdVrmConsumedPackage.cmake). The function runs find_package in function scope, so pxrConfig's Python3 clobbering does not leak, and it promotes the targets it created to IMPORTED_GLOBAL, so later members and root tests reuse one definition.
  • Unused edges are removed. execVrm linked motionSampling and motionRecording, and its descriptor pinned them, from MIG-1 on, but no source includes either. motion_retarget included motionCore/ headers without linking motionCore. motionRecording is no longer consumed at all. The graph drops from 15 to 13 library edges.
  • usdvrm_baseline asks USDVRM_COMPOSED_BUILD. It used to ask whether motionCore's target was visible, and once the root stopped resolving packages that test would have unregistered the gate silently.
Member Consumed packages
vrmContainer, vrmSchema, usdVrmFileFormat, usdVrmPackageResolver none
vrmRig, usdVrmaFileFormat motionCore
execVrm motionCore, motionRetarget
motion_retarget motionCore, motionRetarget, motionSampling, motionUsd

Enforcement: scripts/check_cmake_boundaries.py

The script is static and needs no build. It is registered as workspace_cmake_boundaries plus a self-test (18 cases). It fails on:

  • a source tree of another repository: add_subdirectory leaving the repo, FetchContent, or ExternalProject
  • a usd-motion-plugins identity built here
  • the root resolving a consumed package, including through a foreach loop
  • a member reaching a package that WORKSPACE.md §2 does not allow. The VRM importer may reach no motion package, and the .vrma importer may reach neither vrmRig nor motionRetarget.
  • a member whose resolved, linked and included packages are not the same set
  • a descriptor whose requires.libraries disagrees with its CMake
  • given an installed motion include/: any code there that knows VRM

Run against main, it reports 17 violations.

CI: .github/workflows/plain-cmake.yml

The lane runs OpenUSD install + cmake --install of usd-motion-plugins → cmake → build → ctest on ubuntu-24.04, and fails if ost is on PATH. It copies no pin. scripts/plain_cmake_inputs.py supplies the inputs:

  • It fetches the Linux workspace cell's runtime archive by digest over plain HTTPS. The archive is an ordinary OpenUSD prefix.
  • It builds usd-motion-plugins from the commit that the pinned packages' SLSA provenance names. It checks that every pin agrees on that commit, which today is v0.5.1 9256f93.
  • It fetches execMotion from execVrm's pin.

Fix found by the new lane

libExecMotion.so as published carries its producer's CI checkout as its RUNPATH, so it opens only when LD_LIBRARY_PATH names OpenUSD. ost test sets that variable. The suites only prepended PATH, so on Linux outside ost, execVrm_diagnostics and workspace_exec_driver failed with the plugin never loaded. The failure was bisected with LD_DEBUG, and a C++20 build and a build against the published motion libraries were both ruled out. USDVRM_EXEC_MOTION_ENV now carries the platform's loader variable, and every suite that composes the bundle uses it. The ost side is filed as a P3 in report 48 (#229).

Verification (local)

  • Plain CMake, Windows (VS 18, no ost): 40/40
  • Plain CMake, Ubuntu 24.04 container mirroring the workflow step by step: 40/40. Before the loader fix it was 38/40.
  • Standalone configure and build of all 8 members, each against a prefix holding only its allowed packages: all ok. main's execVrm fails that test on motionSampling.
  • ost build + ost test (0.23.6): 40/40. ost plugin test --workspace --graph-only: valid. Package-consumer lane: 3/3 PASS.
  • check_docs.py: OK

The new lane itself only runs on this PR.

🤖 Generated with Claude Code

snkmcb and others added 3 commits September 24, 2026 11:48
…hout ost

The root CMakeLists.txt resolved all five usd-motion-plugins packages up front,
so every configure required every package -- motionRecording included, which
nothing here includes. Each member now resolves what it links through
usdvrm_consume_package() (cmake/UsdVrmConsumedPackage.cmake), which promotes
the targets it creates to IMPORTED_GLOBAL so a later member and a
root-registered test reuse one definition. The root orchestrates and resolves
nothing on a member's behalf.

EDGES THAT WERE NEVER USED ARE GONE. execVrm linked, and its descriptor
pinned, motionSampling and motionRecording since MIG-1 split motionRuntime,
with no source including either. motion_retarget included motionCore/ headers
through motionRetarget's link line and now links it by name. Measured: every
member configures and builds standalone against a prefix holding only its own
packages; usdVrmFileFormat, usdVrmPackageResolver, vrmSchema and vrmContainer
need no motion package at all, and main's execVrm fails the same test on
motionSampling.

usdvrm_baseline asked whether motionCore's target was visible as a stand-in for
"is this the root build", which would have silently unregistered the gate once
the root stopped resolving packages. It asks USDVRM_COMPOSED_BUILD now.

A CMAKE DEPENDENCY AUDIT, scripts/check_cmake_boundaries.py
(workspace_cmake_boundaries + selftest): no source tree of another repository,
no usd-motion-plugins identity built here, the root resolving nothing, each
member's reachable packages within WORKSPACE.md section 2 (the VRM importer
none; the .vrma importer neither vrmRig nor motionRetarget), resolved = linked
= included per member, and descriptor = CMake. Given an installed motion
include/ it also fails on code there that knows VRM. On main it reports 17
violations.

A PLAIN-CMAKE LANE, .github/workflows/plain-cmake.yml: OpenUSD install +
cmake --install of usd-motion-plugins -> cmake -> build -> ctest on Linux,
refusing to run with ost on PATH. It copies no pin: scripts/plain_cmake_inputs.py
fetches the Linux workspace cell's runtime archive and execVrm's execMotion
bundle by digest, and builds usd-motion-plugins from the commit the pinned
packages' SLSA provenance names.

ITS FIRST RUN FOUND A DEFECT that ost's activation hid. The published
libExecMotion.so carries the producer's CI checkout as its RUNPATH, so it opens
only when LD_LIBRARY_PATH names OpenUSD; the suites only prepended PATH.
Outside `ost test`, execVrm_diagnostics and workspace_exec_driver failed on
Linux with the plugin never loaded. USDVRM_EXEC_MOTION_ENV
(cmake/UsdVrmExecMotion.cmake) now carries the platform loader path, used by
every suite that composes the bundle.

Local: plain CMake 40/40 on Windows (VS 18, no ost) and 40/40 in an Ubuntu
24.04 container mirroring the workflow; `ost build` + `ost test` 40/40 on
0.23.6; graph valid, 13 library edges; package-consumer lane 3/3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pxrConfig.cmake find_dependency's Python3 Development, and on the hosted
runner FindPython3 looked for the headers and libpython under /usr instead of
setup-python's interpreter, failing the usd-motion-plugins configure. The local
container had one Python 3.13 and never saw it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second hosted run failed where the first did, with Python3_EXECUTABLE
passed: an OpenUSD install's CMake package names its build Python by absolute
path. pxrConfig.cmake defaults Python3_LIBRARY/_INCLUDE_DIR to it, and
pxrTargets.cmake bakes /usr/include/python3.13 into gf's, usd's and others'
INTERFACE_INCLUDE_DIRECTORIES, which no Python3_* hint overrides -- the
configure then fails at generate time. `ost` rewrites these paths when it
materializes the runtime; the local container passed only because the builder
image has that Python at those paths.

So the lane installs deadsnakes' python3.13-dev on the runner instead of
setup-python -- the Python the pinned runtime was built against, where it
expects it, as someone who built OpenUSD on their own host has it. Measured in
a clean ubuntu:24.04 container: 40/40. SUPPORTED_CONFIGURATIONS and the README
state the constraint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@snkmcb
snkmcb merged commit bbac925 into main Sep 24, 2026
14 checks passed
@snkmcb
snkmcb deleted the cmake/consumer-owned-dependencies branch September 24, 2026 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant