From 9fab36c85abd7d2acbdb4803d027a7dc61343911 Mon Sep 17 00:00:00 2001 From: snkmcb Date: Thu, 24 Sep 2026 01:09:36 +0900 Subject: [PATCH 1/2] Adopt one CMake dependency contract for plain CMake and ost Inside the repository an edge is the in-tree target when it exists and the installed package otherwise; outside it, always an installed package, found by the component that links it. The adapters now declare motionRetarget and motionCore themselves, and the root lists no external package but OpenUSD, which it still resolves once as the boundary the pure libraries are added before. The shared CMake is one module per concern, and hides no add_library(), target_link_libraries() or dependency declaration: - UsdMmdProject: USDMMD_VERSION from VERSION, and usdmmd_component() for the C++ standard, the single-config Release default and the tests option. The per-component version fallbacks are gone; check_docs.py now holds every project() to ${USDMMD_VERSION} instead. - UsdMmdSanitizers: USDMMD_SANITIZERS and USDMMD_BUILD_FUZZERS replace the five per-component option pairs, applied per target by usdmmd_target_defaults() rather than by directory-scoped flags. - UsdMmdPackage: usdmmd_install_library(), one layout, one SameMinorVersion policy and one export for every plain library. - UsdMmdTesting: test executables, boundary tests, the test interpreter, and OpenUSD on PATH for the adapter tests, which load its DLLs through the motion packages and failed outside an ost session. - UsdMmdOpenUsd: usdmmd_find_openusd() runs pxrConfig.cmake only where OpenUSD's targets are not yet visible, as usd-motion-plugins' configs do, and checks the pin at every entry point. CMakePresets.json no longer reads USD_INSTALL_ROOT; the caller passes the dependency prefix as CMAKE_PREFIX_PATH. The installed-consumer lane runs its adapter probe with OpenUSD on PATH for the same reason as the tests. Staging the plugin and tools out of the source tree is not part of this: ost 0.23.3 reads a bundle's plugInfo.json and lib/ from its source directory. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/parser-sanitizers.yml | 29 +++-- CMakeLists.txt | 83 +++++-------- CMakePresets.json | 9 +- cmake/UsdMmdOpenUsd.cmake | 106 +++++++++------- cmake/UsdMmdPackage.cmake | 59 +++++++++ cmake/UsdMmdProject.cmake | 74 +++++++++++ cmake/UsdMmdSanitizers.cmake | 54 ++++++++ cmake/UsdMmdTargets.cmake | 17 ++- cmake/UsdMmdTesting.cmake | 116 ++++++++++++++++++ libs/mmdControl/CMakeLists.txt | 73 ++--------- libs/mmdControl/tests/CMakeLists.txt | 44 ++----- libs/mmdModel/CMakeLists.txt | 79 ++---------- libs/mmdModel/tests/CMakeLists.txt | 45 ++----- libs/mmdMotionAdapter/CMakeLists.txt | 76 +++++------- libs/mmdMotionAdapter/tests/CMakeLists.txt | 56 ++++----- libs/mmdMotionBinding/CMakeLists.txt | 73 ++--------- libs/mmdMotionBinding/tests/CMakeLists.txt | 41 ++----- libs/mmdPmx/CMakeLists.txt | 101 +++------------ libs/mmdPmx/fuzz/CMakeLists.txt | 6 +- libs/mmdPmx/tests/CMakeLists.txt | 37 ++---- libs/mmdSkeletonAdapter/CMakeLists.txt | 76 +++++------- libs/mmdSkeletonAdapter/tests/CMakeLists.txt | 39 ++---- libs/motionVmd/CMakeLists.txt | 91 ++------------ libs/motionVmd/fuzz/CMakeLists.txt | 4 +- libs/motionVmd/tests/CMakeLists.txt | 43 ++----- plugins/usdMmdFileFormat/CMakeLists.txt | 49 ++------ .../cmake/WriteBuildInfo.cmake | 4 +- scripts/check_docs.py | 19 ++- scripts/check_installed_consumer.py | 13 +- tests/CMakeLists.txt | 9 +- tools/mmdInspect/CMakeLists.txt | 38 +----- tools/mmdInspect/tests/CMakeLists.txt | 22 +--- tools/vmdInspect/CMakeLists.txt | 37 +----- tools/vmdInspect/tests/CMakeLists.txt | 26 ++-- 34 files changed, 689 insertions(+), 959 deletions(-) create mode 100644 cmake/UsdMmdPackage.cmake create mode 100644 cmake/UsdMmdProject.cmake create mode 100644 cmake/UsdMmdSanitizers.cmake create mode 100644 cmake/UsdMmdTesting.cmake diff --git a/.github/workflows/parser-sanitizers.yml b/.github/workflows/parser-sanitizers.yml index 936e112..2f5c29b 100644 --- a/.github/workflows/parser-sanitizers.yml +++ b/.github/workflows/parser-sanitizers.yml @@ -8,7 +8,8 @@ # lane needs no runtime and no `ost`: it configures libs/mmdPmx and # libs/motionVmd on their own, as `ost library build` does, installs each # library instrumented, and configures the ones that depend on them against -# that prefix, each with the sanitizer options its CMakeLists.txt declares. `ost ci generate` has no +# that prefix, each with the USDMMD_SANITIZERS option +# cmake/UsdMmdSanitizers.cmake declares. `ost ci generate` has no # way to express a sanitizer build, which is why this workflow exists beside # the generated one rather than inside it. name: parser-sanitizers @@ -23,7 +24,11 @@ on: - "libs/mmdControl/**" - "tests/fixtures/generate_fixtures.py" - "tests/fixtures/generate_vmd_fixtures.py" + - "cmake/UsdMmdProject.cmake" - "cmake/UsdMmdTargets.cmake" + - "cmake/UsdMmdSanitizers.cmake" + - "cmake/UsdMmdPackage.cmake" + - "cmake/UsdMmdTesting.cmake" - "scripts/check_library_boundaries.py" - ".github/workflows/parser-sanitizers.yml" push: @@ -36,7 +41,11 @@ on: - "libs/mmdControl/**" - "tests/fixtures/generate_fixtures.py" - "tests/fixtures/generate_vmd_fixtures.py" + - "cmake/UsdMmdProject.cmake" - "cmake/UsdMmdTargets.cmake" + - "cmake/UsdMmdSanitizers.cmake" + - "cmake/UsdMmdPackage.cmake" + - "cmake/UsdMmdTesting.cmake" - "scripts/check_library_boundaries.py" - ".github/workflows/parser-sanitizers.yml" workflow_dispatch: @@ -71,8 +80,8 @@ jobs: -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMMDPMX_BUILD_TESTS=ON - -DMMDPMX_SANITIZERS="address;undefined" - -DMMDPMX_BUILD_FUZZER=ON + -DUSDMMD_SANITIZERS="address;undefined" + -DUSDMMD_BUILD_FUZZERS=ON - name: Build run: cmake --build build/sanitize @@ -89,7 +98,7 @@ jobs: -DCMAKE_CXX_COMPILER=clang++-18 \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DMMDPMX_BUILD_TESTS=OFF \ - -DMMDPMX_SANITIZERS="address;undefined" + -DUSDMMD_SANITIZERS="address;undefined" cmake --build build/sanitize-pmx cmake --install build/sanitize-pmx --prefix build/sanitize-prefix @@ -100,7 +109,7 @@ jobs: -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_PREFIX_PATH="$PWD/build/sanitize-prefix" \ -DMMDMODEL_BUILD_TESTS=ON \ - -DMMDMODEL_SANITIZERS="address;undefined" + -DUSDMMD_SANITIZERS="address;undefined" cmake --build build/sanitize-model - name: mmdModel unit, robustness and boundary tests under the sanitizers @@ -112,8 +121,8 @@ jobs: -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMOTIONVMD_BUILD_TESTS=ON - -DMOTIONVMD_SANITIZERS="address;undefined" - -DMOTIONVMD_BUILD_FUZZER=ON + -DUSDMMD_SANITIZERS="address;undefined" + -DUSDMMD_BUILD_FUZZERS=ON - name: Build motionVmd run: cmake --build build/sanitize-vmd @@ -129,7 +138,7 @@ jobs: -DCMAKE_CXX_COMPILER=clang++-18 \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DMOTIONVMD_BUILD_TESTS=OFF \ - -DMOTIONVMD_SANITIZERS="address;undefined" + -DUSDMMD_SANITIZERS="address;undefined" cmake --build build/sanitize-vmd-lib cmake --install build/sanitize-vmd-lib --prefix build/sanitize-prefix cmake --install build/sanitize-model --prefix build/sanitize-prefix @@ -141,7 +150,7 @@ jobs: -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_PREFIX_PATH="$PWD/build/sanitize-prefix" \ -DMMDMOTIONBINDING_BUILD_TESTS=ON \ - -DMMDMOTIONBINDING_SANITIZERS="address;undefined" + -DUSDMMD_SANITIZERS="address;undefined" cmake --build build/sanitize-binding - name: mmdMotionBinding unit and boundary tests under the sanitizers @@ -159,7 +168,7 @@ jobs: -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_PREFIX_PATH="$PWD/build/sanitize-prefix" \ -DMMDCONTROL_BUILD_TESTS=ON \ - -DMMDCONTROL_SANITIZERS="address;undefined" + -DUSDMMD_SANITIZERS="address;undefined" cmake --build build/sanitize-control - name: mmdControl unit, robustness and boundary tests under the sanitizers diff --git a/CMakeLists.txt b/CMakeLists.txt index ec17a1c..24d01ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,48 +1,35 @@ # SPDX-License-Identifier: Apache-2.0 # -# Top-level project for usd-mmd-plugins. +# Top-level project for usd-mmd-plugins: the dependency graph of the +# workspace, and nothing else. Each component declares its own dependencies +# and its own install; this file says in which order they are composed, and +# where OpenUSD enters. # -# The workspace builds two ways, and both are kept working -# (docs/architecture/WORKSPACE.md §5): -# * OpenStrata: `ost plugin build|test plugins/usdMmdFileFormat` drives the -# bundle's own CMakeLists.txt against the activated runtime's toolchain, and -# `ost plugin test --workspace` configures this file. -# * Plain CMake: configure this file with CMAKE_PREFIX_PATH pointing at an -# OpenUSD 26.08 install, or use CMakePresets.json: -# cmake -S . -B build -DCMAKE_PREFIX_PATH= +# One CMake contract, three ways to drive it (docs/architecture/WORKSPACE.md +# §5): +# * Plain CMake, this file: CMAKE_PREFIX_PATH names the dependency prefix -- +# OpenUSD 26.08 and usd-motion-plugins' packages, installed: +# cmake -S . -B build -DCMAKE_PREFIX_PATH= # cmake --build build --config Release # ctest --test-dir build -C Release +# * Plain CMake, one component: `cmake -S libs/` against the same +# prefix plus the installed packages of its in-repository edges. +# * OpenStrata: `ost build`, `ost plugin build plugins/usdMmdFileFormat`, +# `ost library build libs/` compose that prefix and configure +# these same files; nothing here knows it. # -# Every component is a self-contained CMake project (so `ost` can build it -# standalone) that is also add_subdirectory()-able from here. +# Inside this repository an edge is the in-tree target when it exists and the +# installed package otherwise (`if(NOT TARGET ...) find_package(...)`); +# outside it, always an installed package. No sibling checkout is ever +# add_subdirectory()'d. cmake_minimum_required(VERSION 3.22) -# The single product version: the repository-root VERSION file. CHANGELOG.md, -# the git tag and every manifest mirror it; nothing restates the number here. -file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" _mmd_project_version LIMIT_COUNT 1) -string(STRIP "${_mmd_project_version}" _mmd_project_version) - +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/UsdMmdProject.cmake") project(UsdMmdPlugins - VERSION ${_mmd_project_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "OpenUSD plugins for MikuMikuDance assets" LANGUAGES CXX) - -# C++20: the parser's public API takes std::span (DEPENDENCIES.md §2). -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -# Single-config generators (Ninja) default to an empty build type. The OpenUSD -# installs this builds against are Release-only, so default to Release rather -# than pull debug-only imported dependencies. -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -option(USDMMD_BUILD_TESTS "Build usd-mmd-plugins tests" ${PROJECT_IS_TOP_LEVEL}) +usdmmd_component(TESTS_OPTION USDMMD_BUILD_TESTS) if(USDMMD_BUILD_TESTS) # At the top level, so tests registered by components are discoverable by @@ -50,10 +37,11 @@ if(USDMMD_BUILD_TESTS) enable_testing() endif() -# The PMX syntax parser, the canonical model, the VMD reader and the tools that -# report on a PMX and a VMD link no OpenUSD (WORKSPACE.md §2), so they are -# added before the one workspace-wide OpenUSD resolution below. Their position -# is the statement: nothing they configure can see pxr. +# Pure MMD libraries: the PMX syntax parser, the canonical model, the VMD +# reader, motion binding and control evaluation, and the tools that report on +# a PMX and a VMD. They link no OpenUSD and nothing outside this repository +# (WORKSPACE.md §2), so they are added before OpenUSD is resolved. Their +# position is the statement: nothing they configure can see pxr. add_subdirectory("libs/mmdPmx") add_subdirectory("libs/mmdModel") add_subdirectory("libs/motionVmd") @@ -62,18 +50,9 @@ add_subdirectory("libs/mmdControl") add_subdirectory("tools/mmdInspect") add_subdirectory("tools/vmdInspect") -# Resolve OpenUSD once for the whole project; bundles reuse the result. -find_package(pxr REQUIRED CONFIG) +# OpenUSD, resolved once for everything below and held to the pin. include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/UsdMmdOpenUsd.cmake") - -# The two Phase 9 adapters are the only libraries that cross into the shared -# motion packages. Resolve them after OpenUSD so motionCore/motionRetarget can -# reuse the already-defined foundation targets, then add the adapters in their -# dependency order. -find_package(motionCore 0.5 CONFIG REQUIRED) -find_package(motionRetarget 0.5 CONFIG REQUIRED) -add_subdirectory("libs/mmdSkeletonAdapter") -add_subdirectory("libs/mmdMotionAdapter") +usdmmd_find_openusd() # The interpreter the integration tests import OpenUSD's Python bindings into. # It must be the Python OpenUSD was built against, and pxrConfig.cmake names @@ -88,6 +67,12 @@ if(USDMMD_BUILD_TESTS) endif() endif() +# Ecosystem adapters: the only libraries that cross into usd-motion-plugins +# (WORKSPACE.md §2.4). Each finds its motion package itself; they follow +# OpenUSD so those packages reuse the foundation targets resolved above. +add_subdirectory("libs/mmdSkeletonAdapter") +add_subdirectory("libs/mmdMotionAdapter") + # Every plugin bundle: an immediate plugins// carrying both an # openstrata.plugin.yaml and a CMakeLists.txt. Named rather than globbed, so # adding a bundle is a visible edit here, in openstrata.toml, and in diff --git a/CMakePresets.json b/CMakePresets.json index b34f351..83b285b 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -13,14 +13,13 @@ "installDir": "${sourceDir}/build/${presetName}-install", "cacheVariables": { "CMAKE_CXX_STANDARD": "20", - "USDMMD_BUILD_TESTS": "ON", - "CMAKE_PREFIX_PATH": "$env{USD_INSTALL_ROOT}" + "USDMMD_BUILD_TESTS": "ON" } }, { "name": "windows-msvc", "displayName": "Windows / Visual Studio (plain CMake)", - "description": "Set USD_INSTALL_ROOT to an OpenUSD 26.08 install. No generator is named, so CMake picks the newest Visual Studio installed.", + "description": "Pass the dependency prefix -- OpenUSD 26.08, and usd-motion-plugins' motionCore, motionRetarget and motionUsd 0.5 -- with -DCMAKE_PREFIX_PATH or the CMAKE_PREFIX_PATH environment variable. No generator is named, so CMake picks the newest Visual Studio installed.", "inherits": "base", "architecture": { "value": "x64", @@ -35,7 +34,7 @@ { "name": "macos-ninja", "displayName": "macOS / Ninja (plain CMake)", - "description": "Set USD_INSTALL_ROOT to an OpenUSD 26.08 install.", + "description": "Pass the dependency prefix -- OpenUSD 26.08, and usd-motion-plugins' motionCore, motionRetarget and motionUsd 0.5 -- with -DCMAKE_PREFIX_PATH or the CMAKE_PREFIX_PATH environment variable.", "inherits": "base", "generator": "Ninja Multi-Config", "condition": { @@ -47,7 +46,7 @@ { "name": "linux-ninja", "displayName": "Linux / Ninja (plain CMake)", - "description": "Set USD_INSTALL_ROOT to an OpenUSD 26.08 install.", + "description": "Pass the dependency prefix -- OpenUSD 26.08, and usd-motion-plugins' motionCore, motionRetarget and motionUsd 0.5 -- with -DCMAKE_PREFIX_PATH or the CMAKE_PREFIX_PATH environment variable.", "inherits": "base", "generator": "Ninja Multi-Config", "condition": { diff --git a/cmake/UsdMmdOpenUsd.cmake b/cmake/UsdMmdOpenUsd.cmake index 926caf3..72d45ea 100644 --- a/cmake/UsdMmdOpenUsd.cmake +++ b/cmake/UsdMmdOpenUsd.cmake @@ -1,6 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # -# UsdMmdOpenUsd.cmake -- the workspace's OpenUSD pin, enforced in one place. +# UsdMmdOpenUsd.cmake -- how the workspace resolves OpenUSD, and its pin, +# enforced in one place. # # OpenUSD is 26.08 and nothing else (docs/architecture/DEPENDENCIES.md §1). A # plugin built against one OpenUSD release is not loadable in another, and @@ -8,12 +9,17 @@ # process, so this is the release the rest of the ecosystem pins # (usd-vrm-plugins' cmake/UsdVrmOpenUsd.cmake). # -# Every entry point that resolves OpenUSD includes this immediately after its -# `find_package(pxr ...)`: the root project and each bundle under plugins/. A -# bundle built standalone by `ost plugin build` never composes the root -# project, so the pin travels with the find_package call, not with the root. -# The plain libraries (libs/mmdPmx) never include it: they resolve no OpenUSD -# at all (docs/architecture/WORKSPACE.md §2). +# Every entry point that needs OpenUSD calls usdmmd_find_openusd(): the root +# project, once, before it adds the components that may link it, and each +# bundle under plugins/. A bundle built standalone by `ost plugin build` never +# composes the root project, so the pin travels with the resolution, not with +# the root. The plain libraries (libs/mmdPmx) never call it: they resolve no +# OpenUSD at all (docs/architecture/WORKSPACE.md §2). +# +# pxrConfig.cmake defines its imported targets unconditionally, so it is run +# only where they are not yet visible -- the check usd-motion-plugins' package +# configs make before their own find_dependency(pxr), so the ecosystem +# resolves OpenUSD once per scope whoever asks first. # # WHY NOT `find_package(pxr 26.08 EXACT ...)`: OpenUSD installs no # pxrConfigVersion.cmake, so any version argument makes find_package fail with @@ -23,7 +29,7 @@ # Unlike usd-vrm-plugins' module there is no OpenExec probe: nothing here # evaluates anything (DEPENDENCIES.md §1, "Not used"). # -# Sets, for callers that report build metadata: +# usdmmd_find_openusd() sets, for callers that report build metadata: # USDMMD_OPENUSD_RELEASE - "26.08" include_guard(GLOBAL) @@ -34,42 +40,56 @@ include_guard(GLOBAL) set(USDMMD_OPENUSD_REQUIRED_PXR_VERSION 2608) set(USDMMD_OPENUSD_REQUIRED_RELEASE "26.08") -if(NOT pxr_FOUND) - message(FATAL_ERROR - "UsdMmdOpenUsd.cmake was included before OpenUSD was resolved. " - "Include it after find_package(pxr REQUIRED CONFIG).") -endif() +# usdmmd_find_openusd() +# +# A macro, so pxrConfig.cmake's variables (PXR_VERSION, PXR_INCLUDE_DIRS, the +# Python it names) land in the caller's scope, as a find_package() call there +# would leave them. +macro(usdmmd_find_openusd) + if(NOT TARGET usd AND NOT TARGET pxr::usd AND NOT TARGET usd_ms) + find_package(pxr REQUIRED CONFIG) + endif() + _usdmmd_check_openusd_pin() +endmacro() -if(NOT DEFINED PXR_VERSION) - message(FATAL_ERROR - "This OpenUSD install publishes no PXR_VERSION, so its version cannot " - "be verified. usd-mmd-plugins requires OpenUSD " - "${USDMMD_OPENUSD_REQUIRED_RELEASE} exactly.\n" - " pxrConfig.cmake: ${pxr_DIR}") -endif() +# The pin, against the PXR_* variables of the calling scope. A macro for the +# same reason; it sets USDMMD_OPENUSD_RELEASE there. +macro(_usdmmd_check_openusd_pin) + if(NOT DEFINED PXR_VERSION) + message(FATAL_ERROR + "This OpenUSD install publishes no PXR_VERSION, so its version cannot " + "be verified. usd-mmd-plugins requires OpenUSD " + "${USDMMD_OPENUSD_REQUIRED_RELEASE} exactly.\n" + " pxrConfig.cmake: ${pxr_DIR}") + endif() -if(DEFINED PXR_MINOR_VERSION AND DEFINED PXR_PATCH_VERSION) - # 26 + 8 -> "26.08"; OpenUSD zero-pads the month in every name it uses. - string(REGEX REPLACE "^([0-9])$" "0\\1" _usdmmd_patch "${PXR_PATCH_VERSION}") - set(USDMMD_OPENUSD_RELEASE "${PXR_MINOR_VERSION}.${_usdmmd_patch}") - unset(_usdmmd_patch) -else() - set(USDMMD_OPENUSD_RELEASE "${PXR_VERSION}") -endif() + if(DEFINED PXR_MINOR_VERSION AND DEFINED PXR_PATCH_VERSION) + # 26 + 8 -> "26.08"; OpenUSD zero-pads the month in every name it uses. + string(REGEX REPLACE "^([0-9])$" "0\\1" _usdmmd_patch "${PXR_PATCH_VERSION}") + set(USDMMD_OPENUSD_RELEASE "${PXR_MINOR_VERSION}.${_usdmmd_patch}") + unset(_usdmmd_patch) + else() + set(USDMMD_OPENUSD_RELEASE "${PXR_VERSION}") + endif() -if(NOT PXR_VERSION EQUAL USDMMD_OPENUSD_REQUIRED_PXR_VERSION) - message(FATAL_ERROR - "Unsupported OpenUSD: found ${USDMMD_OPENUSD_RELEASE} " - "(PXR_VERSION ${PXR_VERSION}), require " - "${USDMMD_OPENUSD_REQUIRED_RELEASE} " - "(PXR_VERSION ${USDMMD_OPENUSD_REQUIRED_PXR_VERSION}) exactly.\n" - " pxrConfig.cmake: ${pxr_DIR}\n" - "OpenUSD guarantees no ABI stability across releases, so a plugin " - "built against another release could not be loaded beside the rest " - "of the ecosystem. See docs/architecture/DEPENDENCIES.md.") -endif() + if(NOT PXR_VERSION EQUAL USDMMD_OPENUSD_REQUIRED_PXR_VERSION) + message(FATAL_ERROR + "Unsupported OpenUSD: found ${USDMMD_OPENUSD_RELEASE} " + "(PXR_VERSION ${PXR_VERSION}), require " + "${USDMMD_OPENUSD_REQUIRED_RELEASE} " + "(PXR_VERSION ${USDMMD_OPENUSD_REQUIRED_PXR_VERSION}) exactly.\n" + " pxrConfig.cmake: ${pxr_DIR}\n" + "OpenUSD guarantees no ABI stability across releases, so a plugin " + "built against another release could not be loaded beside the rest " + "of the ecosystem. See docs/architecture/DEPENDENCIES.md.") + endif() -# include_guard(GLOBAL) makes this the only time the line is printed per -# configure, however many members include the module. -message(STATUS - "OpenUSD ${USDMMD_OPENUSD_RELEASE} (PXR_VERSION ${PXR_VERSION})") + # Once per configure, however many entry points resolve OpenUSD. + get_property(_usdmmd_reported GLOBAL PROPERTY USDMMD_OPENUSD_REPORTED) + if(NOT _usdmmd_reported) + set_property(GLOBAL PROPERTY USDMMD_OPENUSD_REPORTED TRUE) + message(STATUS + "OpenUSD ${USDMMD_OPENUSD_RELEASE} (PXR_VERSION ${PXR_VERSION})") + endif() + unset(_usdmmd_reported) +endmacro() diff --git a/cmake/UsdMmdPackage.cmake b/cmake/UsdMmdPackage.cmake new file mode 100644 index 0000000..24646d1 --- /dev/null +++ b/cmake/UsdMmdPackage.cmake @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# UsdMmdPackage.cmake -- how a plain library of this workspace installs as a +# CMake package (docs/architecture/PACKAGE_CONTRACT.md). One layout, one +# version policy and one export for every library, so no package can differ +# from the contract by omission. +# +# Plugin bundles install differently -- plugInfo.json, buildInfo.json, the +# bundle's lib/ beside plugin/resources/ -- and never through this helper. +include_guard(GLOBAL) + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# usdmmd_install_library() +# +# Installs as the package of the same name, with the imported target +# ::: +# +# ${CMAKE_INSTALL_LIBDIR}/ the static archive +# ${CMAKE_INSTALL_INCLUDEDIR}/ the component's include/ tree +# ${CMAKE_INSTALL_LIBDIR}/cmake// Config.cmake, +# ConfigVersion.cmake, +# Targets.cmake +# +# The package config is generated from the component's own +# cmake/Config.cmake.in. That template is where the package declares +# the packages its public link interface needs -- each behind a target check, +# with find_dependency() -- so a consumer never lists a transitive dependency +# itself (PACKAGE_CONTRACT.md). +# +# Pre-1.0, a minor version may break the API, so a consumer's request is +# compatible only within the same major.minor: SameMinorVersion. +function(usdmmd_install_library target) + set(_package_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${target}") + set(_config "${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake") + set(_version "${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake") + + install(TARGETS ${target} + EXPORT ${target}Targets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + + configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${target}Config.cmake.in" + "${_config}" + INSTALL_DESTINATION "${_package_dir}") + write_basic_package_version_file("${_version}" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMinorVersion) + + install(EXPORT ${target}Targets + FILE ${target}Targets.cmake + NAMESPACE ${target}:: + DESTINATION "${_package_dir}") + install(FILES "${_config}" "${_version}" DESTINATION "${_package_dir}") +endfunction() diff --git a/cmake/UsdMmdProject.cmake b/cmake/UsdMmdProject.cmake new file mode 100644 index 0000000..f315471 --- /dev/null +++ b/cmake/UsdMmdProject.cmake @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# UsdMmdProject.cmake -- what every project in the workspace declares the same +# way: its version, its language level, its build type and its tests option. +# +# Included by the root project and by each component BEFORE project(), since +# the version is an argument of project(): +# +# include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") +# project(mmdModel VERSION ${USDMMD_VERSION} ... LANGUAGES CXX) +# usdmmd_component(TESTS_OPTION MMDMODEL_BUILD_TESTS) +# +# project() itself stays in each CMakeLists.txt -- CMake requires the call to +# be literal there -- and so do add_library(), target_link_libraries() and +# every dependency a component declares (docs/architecture/WORKSPACE.md §5). +# Nothing here resolves a dependency. + +# The single product version: the repository-root VERSION file +# (WORKSPACE.md §4). CHANGELOG.md, the git tag and every manifest mirror it; +# no CMakeLists.txt restates the number. Recomputed on every include, so a +# component reads it whether or not a parent already did. +file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/../VERSION" USDMMD_VERSION LIMIT_COUNT 1) +string(STRIP "${USDMMD_VERSION}" USDMMD_VERSION) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${CMAKE_CURRENT_LIST_DIR}/../VERSION") + +include_guard(GLOBAL) + +# usdmmd_component([TESTS_OPTION ]) +# +# Called right after project(). A macro, so what it sets lands in the calling +# project's scope. +# +# * C++20, unless the caller already chose a standard: the parser's public +# API takes std::span (DEPENDENCIES.md §2). Each library also states +# `cxx_std_20` as a usage requirement, which is what a consumer sees. +# * Release when a single-config generator (Ninja) is given no build type: +# the OpenUSD installs this builds against are Release-only, and an empty +# build type would pull debug-only imported dependencies. +# * TESTS_OPTION declares the component's own tests switch. It defaults to +# USDMMD_BUILD_TESTS when the component is composed by the root project +# (or when the caller passes it), else to whether the component is the +# top-level project -- so a standalone configure builds its tests, and a +# consumer that add_subdirectory()s it does not. +macro(usdmmd_component) + cmake_parse_arguments(_usdmmd_component "" "TESTS_OPTION" "" ${ARGN}) + + if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 20) + endif() + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + + get_property(_usdmmd_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(NOT _usdmmd_multi_config AND NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + endif() + + if(_usdmmd_component_TESTS_OPTION) + if(DEFINED USDMMD_BUILD_TESTS) + set(_usdmmd_tests_default "${USDMMD_BUILD_TESTS}") + else() + set(_usdmmd_tests_default "${PROJECT_IS_TOP_LEVEL}") + endif() + option(${_usdmmd_component_TESTS_OPTION} + "Build ${PROJECT_NAME} tests" ${_usdmmd_tests_default}) + unset(_usdmmd_tests_default) + endif() + + unset(_usdmmd_multi_config) + unset(_usdmmd_component_TESTS_OPTION) + unset(_usdmmd_component_UNPARSED_ARGUMENTS) + unset(_usdmmd_component_KEYWORDS_MISSING_VALUES) +endmacro() diff --git a/cmake/UsdMmdSanitizers.cmake b/cmake/UsdMmdSanitizers.cmake new file mode 100644 index 0000000..dd9bc34 --- /dev/null +++ b/cmake/UsdMmdSanitizers.cmake @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# UsdMmdSanitizers.cmake -- AddressSanitizer, UndefinedBehaviorSanitizer and +# libFuzzer instrumentation (docs/design/DESIGN_POLICY.md §13), for the lane +# that builds the plain libraries on their own: +# .github/workflows/parser-sanitizers.yml. Nothing shipped is built with them +# (docs/architecture/DEPENDENCIES.md §2). +# +# Two cache options, the same in every component: +# +# USDMMD_SANITIZERS e.g. "address;undefined" (Clang or GCC) +# USDMMD_BUILD_FUZZERS ON builds a component's libFuzzer target, where it +# has one (Clang only; needs USDMMD_SANITIZERS) +# +# They are applied per target, by usdmmd_target_defaults() +# (UsdMmdTargets.cmake): every target a component creates -- library, tests, +# fuzz harness -- is instrumented, and nothing else in a composed build is. +# Nothing is added to a target's link line, so the boundary checks see +# exactly the edges they did before. +include_guard(GLOBAL) + +set(USDMMD_SANITIZERS "" CACHE STRING + "Sanitizers for every target of the configured components, e.g. \"address;undefined\" (Clang or GCC)") +option(USDMMD_BUILD_FUZZERS + "Build the libFuzzer targets of the configured components (Clang only)" OFF) + +if(USDMMD_BUILD_FUZZERS) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "USDMMD_BUILD_FUZZERS needs Clang's libFuzzer") + endif() + # The coverage hooks fuzzer-no-link compiles into every target are defined + # by the sanitizer runtime; without one the tests do not link. + if(NOT USDMMD_SANITIZERS) + message(FATAL_ERROR + "USDMMD_BUILD_FUZZERS needs USDMMD_SANITIZERS (at least \"address\")") + endif() +endif() + +# usdmmd_target_sanitizers() +# +# PRIVATE compile and link options: a static library passes neither to its +# consumers, so an installed instrumented archive is linked by a consumer that +# instruments itself (as the sanitizer lane's dependent libraries do). +function(usdmmd_target_sanitizers target) + if(USDMMD_SANITIZERS) + list(JOIN USDMMD_SANITIZERS "," _sanitize) + target_compile_options(${target} PRIVATE + -fsanitize=${_sanitize} -fno-sanitize-recover=all -fno-omit-frame-pointer) + target_link_options(${target} PRIVATE -fsanitize=${_sanitize}) + endif() + if(USDMMD_BUILD_FUZZERS) + target_compile_options(${target} PRIVATE -fsanitize=fuzzer-no-link) + endif() +endfunction() diff --git a/cmake/UsdMmdTargets.cmake b/cmake/UsdMmdTargets.cmake index e2e6bc6..2959752 100644 --- a/cmake/UsdMmdTargets.cmake +++ b/cmake/UsdMmdTargets.cmake @@ -2,14 +2,21 @@ # # Per-target settings every component of the workspace applies to its own # targets. Included by the root project and by each component, so a component -# built standalone (`ost plugin build`, `ost library build`) compiles exactly -# as it does in the composed tree. +# built standalone (`ost plugin build`, `ost library build`, or plain CMake on +# its directory) compiles exactly as it does in the composed tree. +# +# Every setting is a PRIVATE property of the target it is applied to: nothing +# here is directory-scoped (add_compile_options, add_link_options), so a +# composed build -- this workspace, or a consumer that add_subdirectory()s a +# component -- sees no flag it did not ask for. # # Nothing here resolves a dependency. The workspace has no third-party # dependency (docs/architecture/DEPENDENCIES.md), and OpenUSD is resolved by -# the components that may link it, next to cmake/UsdMmdOpenUsd.cmake. +# the components that may link it, through cmake/UsdMmdOpenUsd.cmake. include_guard(GLOBAL) +include("${CMAKE_CURRENT_LIST_DIR}/UsdMmdSanitizers.cmake") + # usdmmd_target_defaults() # # Windows: `/utf-8`, so source literals and narrow strings are UTF-8 whatever @@ -25,6 +32,9 @@ include_guard(GLOBAL) # the same bytes would then author a different stage on macOS than on Linux, # and one golden could not serve both (DESIGN_POLICY.md §2.5). MSVC's default # /fp:precise does not contract. +# +# And the sanitizer instrumentation USDMMD_SANITIZERS and USDMMD_BUILD_FUZZERS +# ask for (UsdMmdSanitizers.cmake); none by default. function(usdmmd_target_defaults target) if(MSVC) target_compile_options(${target} PRIVATE /utf-8) @@ -32,6 +42,7 @@ function(usdmmd_target_defaults target) elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(${target} PRIVATE -ffp-contract=off) endif() + usdmmd_target_sanitizers(${target}) endfunction() # usdmmd_use_utf8_code_page() diff --git a/cmake/UsdMmdTesting.cmake b/cmake/UsdMmdTesting.cmake new file mode 100644 index 0000000..a75d26c --- /dev/null +++ b/cmake/UsdMmdTesting.cmake @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# UsdMmdTesting.cmake -- how a component registers its tests +# (docs/architecture/WORKSPACE.md §6). Included by a component's tests/ +# directory; add_executable(), target_link_libraries() and add_test() stay in +# the component, so what a suite links is read where it is declared. +include_guard(GLOBAL) + +include("${CMAKE_CURRENT_LIST_DIR}/UsdMmdTargets.cmake") + +# usdmmd_test_python() +# +# Sets USDMMD_TEST_PYTHON in the caller's scope, unless it is already defined: +# the root project defines it after OpenUSD is resolved, to the interpreter +# pxrConfig.cmake names, and a component configured on its own finds any +# Python 3. Left undefined when there is none, and the caller then registers +# no Python-driven test. +macro(usdmmd_test_python) + if(NOT DEFINED USDMMD_TEST_PYTHON) + find_package(Python3 COMPONENTS Interpreter QUIET) + if(Python3_Interpreter_FOUND) + set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") + endif() + endif() +endmacro() + +# usdmmd_test_executable( ...) +# +# A suite: a plain executable that checks with `assert()`, which NDEBUG +# compiles away -- and Release is the default build type -- so NDEBUG is +# undefined for it. It compiles as the component's own targets do +# (usdmmd_target_defaults). The caller links it. +function(usdmmd_test_executable target) + add_executable(${target} ${ARGN}) + usdmmd_target_defaults(${target}) + target_compile_options(${target} + PRIVATE $,/UNDEBUG,-UNDEBUG>) +endfunction() + +# usdmmd_openusd_root() +# +# The OpenUSD install root, from where pxrConfig.cmake was found: an +# install's root holds pxrConfig.cmake itself, a relocated runtime's holds it +# under lib/cmake/pxr. Empty when OpenUSD was not resolved. +function(usdmmd_openusd_root out_var) + set(_root "") + if(pxr_DIR AND EXISTS "${pxr_DIR}/bin") + set(_root "${pxr_DIR}") + elseif(pxr_DIR) + get_filename_component(_root "${pxr_DIR}/../../.." ABSOLUTE) + endif() + set(${out_var} "${_root}" PARENT_SCOPE) +endfunction() + +# usdmmd_openusd_test_environment(...) +# +# Puts OpenUSD's shared libraries on PATH for tests whose executables load +# them -- the adapters reach the foundation libraries through the shared +# motion packages. An `ost` session sets this itself; a plain-CMake build +# does not, and on Windows a DLL is found through PATH alone. +function(usdmmd_openusd_test_environment) + usdmmd_openusd_root(_root) + if(_root) + set_property(TEST ${ARGN} APPEND PROPERTY ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:${_root}/bin" + "PATH=path_list_prepend:${_root}/lib") + endif() +endfunction() + +# usdmmd_add_boundary_test( +# TARGET the library or executable whose edges are gated +# BINARY an executable that links it and nothing else +# [ARGS ...]) --allow, --forbid-include, --allow-openusd-foundation +# +# Registers _boundaries: scripts/check_library_boundaries.py over the +# component's sources (the parent of the calling tests/ directory), over +# 's link line exactly as CMake resolved it, and over what BINARY +# imports (WORKSPACE.md §2.3). The link line is LINK_LIBRARIES, and for a +# library also INTERFACE_LINK_LIBRARIES. +function(usdmmd_add_boundary_test name) + cmake_parse_arguments(PARSE_ARGV 1 _arg "" "TARGET;BINARY" "ARGS") + foreach(_required TARGET BINARY) + if(NOT _arg_${_required}) + message(FATAL_ERROR "usdmmd_add_boundary_test(${name}): ${_required} is required") + endif() + endforeach() + + usdmmd_test_python() + if(NOT USDMMD_TEST_PYTHON) + message(WARNING + "${name}: no Python 3 interpreter; the boundary check is not registered") + return() + endif() + + set(_link_file "${CMAKE_CURRENT_BINARY_DIR}/${name}_link_$.txt") + get_target_property(_type ${_arg_TARGET} TYPE) + if(_type STREQUAL "EXECUTABLE") + file(GENERATE OUTPUT "${_link_file}" + CONTENT "LINK_LIBRARIES=$ +") + else() + file(GENERATE OUTPUT "${_link_file}" + CONTENT "LINK_LIBRARIES=$ +INTERFACE_LINK_LIBRARIES=$ +") + endif() + + add_test(NAME ${name}_boundaries + COMMAND "${USDMMD_TEST_PYTHON}" + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../scripts/check_library_boundaries.py" + --name ${name} + --source "${CMAKE_CURRENT_SOURCE_DIR}/.." + --link-file "${_link_file}" + --binary "$" + ${_arg_ARGS}) +endfunction() diff --git a/libs/mmdControl/CMakeLists.txt b/libs/mmdControl/CMakeLists.txt index 86b7135..54f5b95 100644 --- a/libs/mmdControl/CMakeLists.txt +++ b/libs/mmdControl/CMakeLists.txt @@ -8,54 +8,20 @@ # which has NO OpenUSD dependency and nothing of usd-motion-plugins # (docs/architecture/WORKSPACE.md §2). # -# Self-contained, so `ost library build libs/mmdControl` can configure it -# against installed packages; also add_subdirectory()'d by the repository root. +# Self-contained, so `ost library build libs/mmdControl` or plain CMake can +# configure it against installed packages; also add_subdirectory()'d by the +# repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_control_version LIMIT_COUNT 1) - string(STRIP "${_mmd_control_version}" _mmd_control_version) -else() - set(_mmd_control_version "0.1.0") # mirrors openstrata.library.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(mmdControl - VERSION ${_mmd_control_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "MMD control evaluation for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED MMDCONTROL_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_control_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_control_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDCONTROL_BUILD_TESTS "Build mmdControl tests" - ${_mmd_control_tests_default}) -endif() +usdmmd_component(TESTS_OPTION MMDCONTROL_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") - -set(MMDCONTROL_SANITIZERS "" CACHE STRING - "Sanitizers for mmdControl and its tests, e.g. \"address;undefined\" (Clang or GCC)") -if(MMDCONTROL_SANITIZERS) - list(JOIN MMDCONTROL_SANITIZERS "," _mmd_control_sanitize) - add_compile_options(-fsanitize=${_mmd_control_sanitize} -fno-sanitize-recover=all - -fno-omit-frame-pointer) - add_link_options(-fsanitize=${_mmd_control_sanitize}) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") # The declared edges (WORKSPACE.md §2.1). A composed build already has the # targets; a standalone configure resolves the installed packages. @@ -85,30 +51,7 @@ target_include_directories(mmdControl target_link_libraries(mmdControl PUBLIC mmdMotionBinding::mmdMotionBinding mmdModel::mmdModel) usdmmd_target_defaults(mmdControl) -include(GNUInstallDirs) -install(TARGETS mmdControl - EXPORT mmdControlTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mmdControlConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mmdControlConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdControl") -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/mmdControlConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMinorVersion) -install(EXPORT mmdControlTargets - FILE mmdControlTargets.cmake - NAMESPACE mmdControl:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdControl") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mmdControlConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mmdControlConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdControl") +usdmmd_install_library(mmdControl) if(MMDCONTROL_BUILD_TESTS) enable_testing() diff --git a/libs/mmdControl/tests/CMakeLists.txt b/libs/mmdControl/tests/CMakeLists.txt index 63cc588..530e209 100644 --- a/libs/mmdControl/tests/CMakeLists.txt +++ b/libs/mmdControl/tests/CMakeLists.txt @@ -1,16 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 -# Each suite checks with `assert()`, which NDEBUG compiles away -- and Release -# is the default build type -- so NDEBUG is undefined for these targets only. +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + # Models are PMX documents stated as data and canonicalized, and motions are # bound data: no PMX or VMD bytes are involved. function(_mmd_control_test target) - add_executable(${target} ${ARGN}) + usdmmd_test_executable(${target} ${ARGN}) target_link_libraries(${target} PRIVATE mmdControl::mmdControl) target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") - usdmmd_target_defaults(${target}) - target_compile_options(${target} - PRIVATE $,/UNDEBUG,-UNDEBUG>) endfunction() # Synthetic rigs with known answers (docs/design/MOTION_CONTRACT.md §11). @@ -21,32 +18,9 @@ add_test(NAME mmdControl_unit COMMAND mmdControl_tests) _mmd_control_test(mmdControl_robustness_tests test_robustness.cpp) add_test(NAME mmdControl_robustness COMMAND mmdControl_robustness_tests) -# The link line exactly as CMake resolved it: WORKSPACE.md §2.1 allows -# mmdControl two edges, mmdMotionBinding and mmdModel. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmdControl_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") - -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() -if(USDMMD_TEST_PYTHON) - add_test(NAME mmdControl_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name mmdControl - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmdControl_link_$.txt" - --binary "$" - --allow mmdMotionBinding::mmdMotionBinding - --allow mmdModel::mmdModel - --forbid-include motionCore/) -else() - message(WARNING - "mmdControl: no Python 3 interpreter; the boundary check is not registered") -endif() +# WORKSPACE.md §2.1 allows mmdControl two edges, mmdMotionBinding and +# mmdModel, and nothing of usd-motion-plugins. +usdmmd_add_boundary_test(mmdControl TARGET mmdControl BINARY mmdControl_tests + ARGS --allow mmdMotionBinding::mmdMotionBinding + --allow mmdModel::mmdModel + --forbid-include motionCore/) diff --git a/libs/mmdModel/CMakeLists.txt b/libs/mmdModel/CMakeLists.txt index f724459..a42f192 100644 --- a/libs/mmdModel/CMakeLists.txt +++ b/libs/mmdModel/CMakeLists.txt @@ -8,58 +8,20 @@ # and the tests below fail if anything it links, or anything linking it # inherits, reaches OpenUSD. # -# Self-contained, so `ost library build libs/mmdModel` can configure it -# against an installed mmdPmx; also add_subdirectory()'d by the repository -# root, which already has the in-tree mmdPmx target. +# Self-contained, so `ost library build libs/mmdModel` or plain CMake can +# configure it against an installed mmdPmx; also add_subdirectory()'d by the +# repository root, which already has the in-tree mmdPmx target. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_model_version LIMIT_COUNT 1) - string(STRIP "${_mmd_model_version}" _mmd_model_version) -else() - set(_mmd_model_version "0.1.0") # mirrors openstrata.library.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(mmdModel - VERSION ${_mmd_model_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "Canonical MMD semantics for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED MMDMODEL_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_model_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_model_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDMODEL_BUILD_TESTS "Build mmdModel tests" ${_mmd_model_tests_default}) -endif() +usdmmd_component(TESTS_OPTION MMDMODEL_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") - -# Sanitizers, for the lane that builds this library on its own against an -# instrumented mmdPmx: .github/workflows/parser-sanitizers.yml. Canonicalization -# runs on every document the parser accepts, malformed-but-recoverable ones -# included, so it is held to the parser's bar (DESIGN_POLICY.md §13). -set(MMDMODEL_SANITIZERS "" CACHE STRING - "Sanitizers for mmdModel and its tests, e.g. \"address;undefined\" (Clang or GCC)") -if(MMDMODEL_SANITIZERS) - list(JOIN MMDMODEL_SANITIZERS "," _mmd_model_sanitize) - add_compile_options(-fsanitize=${_mmd_model_sanitize} -fno-sanitize-recover=all - -fno-omit-frame-pointer) - add_link_options(-fsanitize=${_mmd_model_sanitize}) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") # The declared mmdModel -> mmdPmx edge (WORKSPACE.md §2.1). In the composed # workspace build mmdPmx is already an in-tree target; a standalone configure @@ -92,32 +54,7 @@ target_include_directories(mmdModel target_link_libraries(mmdModel PUBLIC mmdPmx::mmdPmx) usdmmd_target_defaults(mmdModel) -include(GNUInstallDirs) -install(TARGETS mmdModel - EXPORT mmdModelTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mmdModelConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mmdModelConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdModel") -# Pre-1.0, a minor version may break the API, so a consumer's request is -# compatible only within the same major.minor. -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/mmdModelConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMinorVersion) -install(EXPORT mmdModelTargets - FILE mmdModelTargets.cmake - NAMESPACE mmdModel:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdModel") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mmdModelConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mmdModelConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdModel") +usdmmd_install_library(mmdModel) if(MMDMODEL_BUILD_TESTS) enable_testing() diff --git a/libs/mmdModel/tests/CMakeLists.txt b/libs/mmdModel/tests/CMakeLists.txt index ed7dd4d..9a3c8c9 100644 --- a/libs/mmdModel/tests/CMakeLists.txt +++ b/libs/mmdModel/tests/CMakeLists.txt @@ -1,19 +1,16 @@ # SPDX-License-Identifier: Apache-2.0 -# Each suite checks with `assert()`, which NDEBUG compiles away -- and Release -# is the default build type -- so NDEBUG is undefined for these targets only. -# They see the library's private headers, to test identifiers, texture paths -# and the joint order directly. Canonicalization takes a pmx::Document, so the -# tests state their documents as data: no PMX bytes are involved. +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +# The suites see the library's private headers, to test identifiers, texture +# paths and the joint order directly. Canonicalization takes a pmx::Document, +# so the tests state their documents as data: no PMX bytes are involved. function(_mmd_model_test target) - add_executable(${target} ${ARGN}) + usdmmd_test_executable(${target} ${ARGN}) target_link_libraries(${target} PRIVATE mmdModel::mmdModel) target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_SOURCE_DIR}") - usdmmd_target_defaults(${target}) - target_compile_options(${target} - PRIVATE $,/UNDEBUG,-UNDEBUG>) endfunction() _mmd_model_test(mmdModel_tests @@ -31,30 +28,6 @@ add_test(NAME mmdModel_unit COMMAND mmdModel_tests) _mmd_model_test(mmdModel_robustness_tests test_robustness.cpp) add_test(NAME mmdModel_robustness COMMAND mmdModel_robustness_tests) -# The link line exactly as CMake resolved it: WORKSPACE.md §2.1 allows -# mmdModel one edge, mmdPmx. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmdModel_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") - -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() -if(USDMMD_TEST_PYTHON) - add_test(NAME mmdModel_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name mmdModel - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmdModel_link_$.txt" - --binary "$" - --allow mmdPmx::mmdPmx) -else() - message(WARNING - "mmdModel: no Python 3 interpreter; the boundary check is not registered") -endif() +# WORKSPACE.md §2.1 allows mmdModel one edge, mmdPmx. +usdmmd_add_boundary_test(mmdModel TARGET mmdModel BINARY mmdModel_tests + ARGS --allow mmdPmx::mmdPmx) diff --git a/libs/mmdMotionAdapter/CMakeLists.txt b/libs/mmdMotionAdapter/CMakeLists.txt index 4b62dca..93e1959 100644 --- a/libs/mmdMotionAdapter/CMakeLists.txt +++ b/libs/mmdMotionAdapter/CMakeLists.txt @@ -1,35 +1,31 @@ # SPDX-License-Identifier: Apache-2.0 +# +# mmdMotionAdapter -- fully evaluated mmdControl output as usd-motion-plugins' +# MotionClip (docs/design/MOTION_CONTRACT.md §10), with mmdSkeletonAdapter's +# roles and source rest. +# +# The other narrow adapter that crosses into usd-motion-plugins +# (docs/architecture/WORKSPACE.md §2.4): its edges are mmdControl, mmdModel, +# mmdSkeletonAdapter and the released motionCore package. +# +# Self-contained, so `ost library build libs/mmdMotionAdapter` or plain CMake +# can configure it against installed packages; also add_subdirectory()'d by +# the repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_motion_adapter_version LIMIT_COUNT 1) - string(STRIP "${_mmd_motion_adapter_version}" _mmd_motion_adapter_version) -else() - set(_mmd_motion_adapter_version "0.1.0") -endif() - -project(mmdMotionAdapter VERSION ${_mmd_motion_adapter_version} - DESCRIPTION "Evaluated MMD motion adapter for usd-motion-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -if(NOT DEFINED MMDMOTIONADAPTER_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_motion_adapter_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_motion_adapter_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDMOTIONADAPTER_BUILD_TESTS "Build mmdMotionAdapter tests" - ${_mmd_motion_adapter_tests_default}) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") +project(mmdMotionAdapter + VERSION ${USDMMD_VERSION} + DESCRIPTION "Evaluated MMD motion adapter for usd-motion-plugins" + LANGUAGES CXX) +usdmmd_component(TESTS_OPTION MMDMOTIONADAPTER_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") +# The declared edges (WORKSPACE.md §2.1). The three repository libraries are +# in-tree targets in the composed build; motionCore is always an installed +# package, never a sibling checkout (WORKSPACE.md §2.4). if(NOT TARGET mmdModel::mmdModel) find_package(mmdModel CONFIG REQUIRED) endif() @@ -46,11 +42,15 @@ endif() add_library(mmdMotionAdapter STATIC src/Adapter.cpp) add_library(mmdMotionAdapter::mmdMotionAdapter ALIAS mmdMotionAdapter) set_target_properties(mmdMotionAdapter PROPERTIES - EXPORT_NAME mmdMotionAdapter POSITION_INDEPENDENT_CODE ON) + EXPORT_NAME mmdMotionAdapter + POSITION_INDEPENDENT_CODE ON) + target_compile_features(mmdMotionAdapter PUBLIC cxx_std_20) target_include_directories(mmdMotionAdapter PUBLIC "$" "$") +# PUBLIC: Adapter.h takes mmdControl's and mmdSkeletonAdapter's types and +# returns motionCore's. target_link_libraries(mmdMotionAdapter PUBLIC mmdControl::mmdControl mmdModel::mmdModel @@ -58,27 +58,7 @@ target_link_libraries(mmdMotionAdapter PUBLIC motionCore::motionCore) usdmmd_target_defaults(mmdMotionAdapter) -include(GNUInstallDirs) -install(TARGETS mmdMotionAdapter EXPORT mmdMotionAdapterTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mmdMotionAdapterConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionAdapterConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdMotionAdapter") -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionAdapterConfigVersion.cmake" - VERSION ${PROJECT_VERSION} COMPATIBILITY SameMinorVersion) -install(EXPORT mmdMotionAdapterTargets FILE mmdMotionAdapterTargets.cmake - NAMESPACE mmdMotionAdapter:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdMotionAdapter") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionAdapterConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionAdapterConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdMotionAdapter") +usdmmd_install_library(mmdMotionAdapter) if(MMDMOTIONADAPTER_BUILD_TESTS) enable_testing() diff --git a/libs/mmdMotionAdapter/tests/CMakeLists.txt b/libs/mmdMotionAdapter/tests/CMakeLists.txt index 204d63e..44c7421 100644 --- a/libs/mmdMotionAdapter/tests/CMakeLists.txt +++ b/libs/mmdMotionAdapter/tests/CMakeLists.txt @@ -1,51 +1,39 @@ # SPDX-License-Identifier: Apache-2.0 -add_executable(mmdMotionAdapter_tests test_adapter.cpp) +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +usdmmd_test_executable(mmdMotionAdapter_tests test_adapter.cpp) target_link_libraries(mmdMotionAdapter_tests PRIVATE mmdMotionAdapter::mmdMotionAdapter) -usdmmd_target_defaults(mmdMotionAdapter_tests) -target_compile_options(mmdMotionAdapter_tests - PRIVATE $,/UNDEBUG,-UNDEBUG>) add_test(NAME mmdMotionAdapter_unit COMMAND mmdMotionAdapter_tests) +# The Phase 9 acceptance executable is the one place motionRetarget and the +# stage-level motionUsd are linked directly (WORKSPACE.md §2.1): it declares +# both itself, as installed packages. Neither is in the adapter's link +# interface, which the boundary test below holds to. +if(NOT TARGET motionRetarget::motionRetarget) + find_package(motionRetarget 0.5 CONFIG REQUIRED) +endif() if(NOT TARGET motionUsd::motionUsd) find_package(motionUsd 0.5 CONFIG REQUIRED) endif() -add_executable(mmdMotionAdapter_acceptance +usdmmd_test_executable(mmdMotionAdapter_acceptance test_acceptance.cpp generic_retarget.cpp) target_link_libraries(mmdMotionAdapter_acceptance PRIVATE mmdMotionAdapter::mmdMotionAdapter motionRetarget::motionRetarget motionUsd::motionUsd) -usdmmd_target_defaults(mmdMotionAdapter_acceptance) -target_compile_options(mmdMotionAdapter_acceptance - PRIVATE $,/UNDEBUG,-UNDEBUG>) add_test(NAME mmdMotionAdapter_acceptance COMMAND mmdMotionAdapter_acceptance) +usdmmd_openusd_test_environment(mmdMotionAdapter_unit mmdMotionAdapter_acceptance) -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionAdapter_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") - -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() -if(USDMMD_TEST_PYTHON) - add_test(NAME mmdMotionAdapter_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name mmdMotionAdapter - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionAdapter_link_$.txt" - --binary "$" - --allow mmdControl::mmdControl - --allow mmdModel::mmdModel - --allow mmdSkeletonAdapter::mmdSkeletonAdapter - --allow motionCore::motionCore - --allow-openusd-foundation) -endif() +# WORKSPACE.md §2.1 allows mmdMotionAdapter mmdControl, mmdModel, +# mmdSkeletonAdapter and motionCore, and through it the OpenUSD foundation +# closure only. +usdmmd_add_boundary_test(mmdMotionAdapter + TARGET mmdMotionAdapter BINARY mmdMotionAdapter_tests + ARGS --allow mmdControl::mmdControl + --allow mmdModel::mmdModel + --allow mmdSkeletonAdapter::mmdSkeletonAdapter + --allow motionCore::motionCore + --allow-openusd-foundation) diff --git a/libs/mmdMotionBinding/CMakeLists.txt b/libs/mmdMotionBinding/CMakeLists.txt index b27a743..a27e36c 100644 --- a/libs/mmdMotionBinding/CMakeLists.txt +++ b/libs/mmdMotionBinding/CMakeLists.txt @@ -8,54 +8,20 @@ # WORKSPACE.md §2). It is the one place a motion and a model meet, so neither # motionVmd nor mmdModel has to know the other. # -# Self-contained, so `ost library build libs/mmdMotionBinding` can configure it -# against installed packages; also add_subdirectory()'d by the repository root. +# Self-contained, so `ost library build libs/mmdMotionBinding` or plain CMake +# can configure it against installed packages; also add_subdirectory()'d by +# the repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_binding_version LIMIT_COUNT 1) - string(STRIP "${_mmd_binding_version}" _mmd_binding_version) -else() - set(_mmd_binding_version "0.1.0") # mirrors openstrata.library.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(mmdMotionBinding - VERSION ${_mmd_binding_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "VMD-to-PMX motion binding for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED MMDMOTIONBINDING_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_binding_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_binding_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDMOTIONBINDING_BUILD_TESTS "Build mmdMotionBinding tests" - ${_mmd_binding_tests_default}) -endif() +usdmmd_component(TESTS_OPTION MMDMOTIONBINDING_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") - -set(MMDMOTIONBINDING_SANITIZERS "" CACHE STRING - "Sanitizers for mmdMotionBinding and its tests, e.g. \"address;undefined\" (Clang or GCC)") -if(MMDMOTIONBINDING_SANITIZERS) - list(JOIN MMDMOTIONBINDING_SANITIZERS "," _mmd_binding_sanitize) - add_compile_options(-fsanitize=${_mmd_binding_sanitize} -fno-sanitize-recover=all - -fno-omit-frame-pointer) - add_link_options(-fsanitize=${_mmd_binding_sanitize}) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") # The declared edges (WORKSPACE.md §2.1). A composed build already has the # targets; a standalone configure resolves the installed packages. @@ -84,30 +50,7 @@ target_include_directories(mmdMotionBinding target_link_libraries(mmdMotionBinding PUBLIC mmdModel::mmdModel motionVmd::motionVmd) usdmmd_target_defaults(mmdMotionBinding) -include(GNUInstallDirs) -install(TARGETS mmdMotionBinding - EXPORT mmdMotionBindingTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mmdMotionBindingConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionBindingConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdMotionBinding") -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionBindingConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMinorVersion) -install(EXPORT mmdMotionBindingTargets - FILE mmdMotionBindingTargets.cmake - NAMESPACE mmdMotionBinding:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdMotionBinding") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionBindingConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionBindingConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdMotionBinding") +usdmmd_install_library(mmdMotionBinding) if(MMDMOTIONBINDING_BUILD_TESTS) enable_testing() diff --git a/libs/mmdMotionBinding/tests/CMakeLists.txt b/libs/mmdMotionBinding/tests/CMakeLists.txt index 03c32a7..1bf8b04 100644 --- a/libs/mmdMotionBinding/tests/CMakeLists.txt +++ b/libs/mmdMotionBinding/tests/CMakeLists.txt @@ -1,38 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 -# `assert()` checks, so NDEBUG is undefined for the test target only. -add_executable(mmdMotionBinding_tests test_bind.cpp) +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +usdmmd_test_executable(mmdMotionBinding_tests test_bind.cpp) target_link_libraries(mmdMotionBinding_tests PRIVATE mmdMotionBinding::mmdMotionBinding) -usdmmd_target_defaults(mmdMotionBinding_tests) -target_compile_options(mmdMotionBinding_tests - PRIVATE $,/UNDEBUG,-UNDEBUG>) add_test(NAME mmdMotionBinding_unit COMMAND mmdMotionBinding_tests) -# The link line exactly as CMake resolved it: WORKSPACE.md §2.1 allows -# mmdMotionBinding two edges, mmdModel and motionVmd. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionBinding_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") - -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() -if(USDMMD_TEST_PYTHON) - add_test(NAME mmdMotionBinding_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name mmdMotionBinding - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmdMotionBinding_link_$.txt" - --binary "$" - --allow mmdModel::mmdModel - --allow motionVmd::motionVmd) -else() - message(WARNING - "mmdMotionBinding: no Python 3 interpreter; the boundary check is not registered") -endif() +# WORKSPACE.md §2.1 allows mmdMotionBinding two edges, mmdModel and motionVmd. +usdmmd_add_boundary_test(mmdMotionBinding + TARGET mmdMotionBinding BINARY mmdMotionBinding_tests + ARGS --allow mmdModel::mmdModel + --allow motionVmd::motionVmd) diff --git a/libs/mmdPmx/CMakeLists.txt b/libs/mmdPmx/CMakeLists.txt index 935afa7..e61434b 100644 --- a/libs/mmdPmx/CMakeLists.txt +++ b/libs/mmdPmx/CMakeLists.txt @@ -2,74 +2,24 @@ # # mmdPmx -- the PMX syntax parser (docs/design/DESIGN_POLICY.md §5.1). # -# A plain static library with NO OpenUSD dependency (docs/architecture/ -# WORKSPACE.md §2): this file never resolves pxr, and the tests below fail if -# anything it links, or anything linking it inherits, reaches OpenUSD. +# A plain static library with NO dependency at all -- no OpenUSD, nothing in +# this repository (docs/architecture/WORKSPACE.md §2): this file never +# resolves pxr, and the tests below fail if anything it links, or anything +# linking it inherits, reaches OpenUSD. # -# Self-contained, so `ost library build libs/mmdPmx` can configure it directly; -# also add_subdirectory()'d by the repository root. +# Self-contained, so `ost library build libs/mmdPmx` or plain CMake can +# configure it directly; also add_subdirectory()'d by the repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_pmx_version LIMIT_COUNT 1) - string(STRIP "${_mmd_pmx_version}" _mmd_pmx_version) -else() - set(_mmd_pmx_version "0.1.0") # mirrors openstrata.library.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(mmdPmx - VERSION ${_mmd_pmx_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "PMX syntax parser for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED MMDPMX_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_pmx_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_pmx_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDPMX_BUILD_TESTS "Build mmdPmx tests" ${_mmd_pmx_tests_default}) -endif() +usdmmd_component(TESTS_OPTION MMDPMX_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") - -# Sanitizers and the fuzz target (DESIGN_POLICY.md §13), for the lane that -# builds this library on its own: .github/workflows/parser-sanitizers.yml. -# Directory-scoped, so they instrument this library, its tests and its fuzz -# target and nothing a composed build adds elsewhere. -set(MMDPMX_SANITIZERS "" CACHE STRING - "Sanitizers for mmdPmx and its tests, e.g. \"address;undefined\" (Clang or GCC)") -option(MMDPMX_BUILD_FUZZER "Build the mmdPmx_fuzz libFuzzer target (Clang only)" OFF) -if(MMDPMX_SANITIZERS) - list(JOIN MMDPMX_SANITIZERS "," _mmd_pmx_sanitize) - add_compile_options(-fsanitize=${_mmd_pmx_sanitize} -fno-sanitize-recover=all - -fno-omit-frame-pointer) - add_link_options(-fsanitize=${_mmd_pmx_sanitize}) -endif() -if(MMDPMX_BUILD_FUZZER) - if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") - message(FATAL_ERROR "MMDPMX_BUILD_FUZZER needs Clang's libFuzzer") - endif() - # The coverage hooks fuzzer-no-link compiles into every target here are - # defined by the sanitizer runtime; without one the tests do not link. - if(NOT MMDPMX_SANITIZERS) - message(FATAL_ERROR - "MMDPMX_BUILD_FUZZER needs MMDPMX_SANITIZERS (at least \"address\")") - endif() - add_compile_options(-fsanitize=fuzzer-no-link) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") add_library(mmdPmx STATIC src/Diagnostic.cpp @@ -91,37 +41,14 @@ target_include_directories(mmdPmx "${CMAKE_CURRENT_SOURCE_DIR}/src") usdmmd_target_defaults(mmdPmx) -include(GNUInstallDirs) -install(TARGETS mmdPmx - EXPORT mmdPmxTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mmdPmxConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mmdPmxConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdPmx") -# Pre-1.0, a minor version may break the API, so a consumer's request is -# compatible only within the same major.minor. -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/mmdPmxConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMinorVersion) -install(EXPORT mmdPmxTargets - FILE mmdPmxTargets.cmake - NAMESPACE mmdPmx:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdPmx") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mmdPmxConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mmdPmxConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdPmx") +usdmmd_install_library(mmdPmx) if(MMDPMX_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() -if(MMDPMX_BUILD_FUZZER) +# The libFuzzer target (DESIGN_POLICY.md §13), for the sanitizer lane: +# .github/workflows/parser-sanitizers.yml. +if(USDMMD_BUILD_FUZZERS) add_subdirectory(fuzz) endif() diff --git a/libs/mmdPmx/fuzz/CMakeLists.txt b/libs/mmdPmx/fuzz/CMakeLists.txt index e0bca8d..c63b8da 100644 --- a/libs/mmdPmx/fuzz/CMakeLists.txt +++ b/libs/mmdPmx/fuzz/CMakeLists.txt @@ -1,13 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 # # mmdPmx_fuzz: the libFuzzer target (fuzz_read.cpp). Clang only; the library -# itself is instrumented with -fsanitize=fuzzer-no-link by the parent file, so -# the fuzzer sees coverage inside the parser and not only in this harness. +# itself is instrumented with -fsanitize=fuzzer-no-link by +# usdmmd_target_defaults() (cmake/UsdMmdSanitizers.cmake), so the fuzzer sees +# coverage inside the parser and not only in this harness. add_executable(mmdPmx_fuzz fuzz_read.cpp ../tests/PmxSamples.cpp) target_link_libraries(mmdPmx_fuzz PRIVATE mmdPmx::mmdPmx) target_include_directories(mmdPmx_fuzz PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../tests") +usdmmd_target_defaults(mmdPmx_fuzz) target_compile_options(mmdPmx_fuzz PRIVATE -fsanitize=fuzzer) target_link_options(mmdPmx_fuzz PRIVATE -fsanitize=fuzzer) diff --git a/libs/mmdPmx/tests/CMakeLists.txt b/libs/mmdPmx/tests/CMakeLists.txt index a1596b4..2ee7772 100644 --- a/libs/mmdPmx/tests/CMakeLists.txt +++ b/libs/mmdPmx/tests/CMakeLists.txt @@ -1,18 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 -# Each suite checks with `assert()`, which NDEBUG compiles away -- and Release -# is the default build type -- so NDEBUG is undefined for these targets only. -# They see the library's private headers, to test the decoders and the +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +# The suites see the library's private headers, to test the decoders and the # diagnostic list directly. function(_mmd_pmx_test target) - add_executable(${target} ${ARGN}) + usdmmd_test_executable(${target} ${ARGN}) target_link_libraries(${target} PRIVATE mmdPmx::mmdPmx) target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_SOURCE_DIR}") - usdmmd_target_defaults(${target}) - target_compile_options(${target} - PRIVATE $,/UNDEBUG,-UNDEBUG>) endfunction() _mmd_pmx_test(mmdPmx_tests @@ -31,34 +28,14 @@ _mmd_pmx_test(mmdPmx_robustness_tests test_robustness_main.cpp) add_test(NAME mmdPmx_robustness COMMAND mmdPmx_robustness_tests) -# The link line exactly as CMake resolved it, for the boundary check below: # WORKSPACE.md §2.1 allows mmdPmx no edge at all. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmdPmx_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") +usdmmd_add_boundary_test(mmdPmx TARGET mmdPmx BINARY mmdPmx_tests) -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() +# The check's rules against cases they must reject. +usdmmd_test_python() if(USDMMD_TEST_PYTHON) - add_test(NAME mmdPmx_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name mmdPmx - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmdPmx_link_$.txt" - --binary "$") - # The check's rules against cases they must reject. add_test(NAME mmdPmx_boundaries_selftest COMMAND "${USDMMD_TEST_PYTHON}" "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" --selftest) -else() - message(WARNING - "mmdPmx: no Python 3 interpreter; the boundary check is not registered") endif() diff --git a/libs/mmdSkeletonAdapter/CMakeLists.txt b/libs/mmdSkeletonAdapter/CMakeLists.txt index 3d28e06..c3f6874 100644 --- a/libs/mmdSkeletonAdapter/CMakeLists.txt +++ b/libs/mmdSkeletonAdapter/CMakeLists.txt @@ -1,35 +1,32 @@ # SPDX-License-Identifier: Apache-2.0 +# +# mmdSkeletonAdapter -- a canonical PMX skeleton as usd-motion-plugins' +# SkeletonDescriptor, RetargetMap and SourceRestPose +# (docs/design/MOTION_CONTRACT.md §10.4). +# +# One of the two narrow adapters that cross into usd-motion-plugins +# (docs/architecture/WORKSPACE.md §2.4): its edges are mmdModel and the +# released motionRetarget package, which brings motionCore and the OpenUSD +# foundation types with it through its own package config. +# +# Self-contained, so `ost library build libs/mmdSkeletonAdapter` or plain +# CMake can configure it against installed packages; also +# add_subdirectory()'d by the repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_skeleton_adapter_version LIMIT_COUNT 1) - string(STRIP "${_mmd_skeleton_adapter_version}" _mmd_skeleton_adapter_version) -else() - set(_mmd_skeleton_adapter_version "0.1.0") -endif() - -project(mmdSkeletonAdapter VERSION ${_mmd_skeleton_adapter_version} - DESCRIPTION "MMD skeleton adapter for usd-motion-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -if(NOT DEFINED MMDSKELETONADAPTER_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_skeleton_adapter_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_skeleton_adapter_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDSKELETONADAPTER_BUILD_TESTS "Build mmdSkeletonAdapter tests" - ${_mmd_skeleton_adapter_tests_default}) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") +project(mmdSkeletonAdapter + VERSION ${USDMMD_VERSION} + DESCRIPTION "MMD skeleton adapter for usd-motion-plugins" + LANGUAGES CXX) +usdmmd_component(TESTS_OPTION MMDSKELETONADAPTER_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") +# The declared edges (WORKSPACE.md §2.1). mmdModel is this repository's: the +# composed build already has the target. motionRetarget is always an +# installed package, never a sibling checkout (WORKSPACE.md §2.4). if(NOT TARGET mmdModel::mmdModel) find_package(mmdModel CONFIG REQUIRED) endif() @@ -40,36 +37,19 @@ endif() add_library(mmdSkeletonAdapter STATIC src/Adapter.cpp) add_library(mmdSkeletonAdapter::mmdSkeletonAdapter ALIAS mmdSkeletonAdapter) set_target_properties(mmdSkeletonAdapter PROPERTIES - EXPORT_NAME mmdSkeletonAdapter POSITION_INDEPENDENT_CODE ON) + EXPORT_NAME mmdSkeletonAdapter + POSITION_INDEPENDENT_CODE ON) + target_compile_features(mmdSkeletonAdapter PUBLIC cxx_std_20) target_include_directories(mmdSkeletonAdapter PUBLIC "$" "$") +# PUBLIC: Adapter.h returns motionRetarget's types for mmdModel's. target_link_libraries(mmdSkeletonAdapter PUBLIC mmdModel::mmdModel motionRetarget::motionRetarget) usdmmd_target_defaults(mmdSkeletonAdapter) -include(GNUInstallDirs) -install(TARGETS mmdSkeletonAdapter EXPORT mmdSkeletonAdapterTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mmdSkeletonAdapterConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/mmdSkeletonAdapterConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdSkeletonAdapter") -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/mmdSkeletonAdapterConfigVersion.cmake" - VERSION ${PROJECT_VERSION} COMPATIBILITY SameMinorVersion) -install(EXPORT mmdSkeletonAdapterTargets FILE mmdSkeletonAdapterTargets.cmake - NAMESPACE mmdSkeletonAdapter:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdSkeletonAdapter") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/mmdSkeletonAdapterConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/mmdSkeletonAdapterConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mmdSkeletonAdapter") +usdmmd_install_library(mmdSkeletonAdapter) if(MMDSKELETONADAPTER_BUILD_TESTS) enable_testing() diff --git a/libs/mmdSkeletonAdapter/tests/CMakeLists.txt b/libs/mmdSkeletonAdapter/tests/CMakeLists.txt index 8eb5898..c66e4ac 100644 --- a/libs/mmdSkeletonAdapter/tests/CMakeLists.txt +++ b/libs/mmdSkeletonAdapter/tests/CMakeLists.txt @@ -1,34 +1,17 @@ # SPDX-License-Identifier: Apache-2.0 -add_executable(mmdSkeletonAdapter_tests test_adapter.cpp) +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +usdmmd_test_executable(mmdSkeletonAdapter_tests test_adapter.cpp) target_link_libraries(mmdSkeletonAdapter_tests PRIVATE mmdSkeletonAdapter::mmdSkeletonAdapter) -usdmmd_target_defaults(mmdSkeletonAdapter_tests) -target_compile_options(mmdSkeletonAdapter_tests - PRIVATE $,/UNDEBUG,-UNDEBUG>) add_test(NAME mmdSkeletonAdapter_unit COMMAND mmdSkeletonAdapter_tests) +usdmmd_openusd_test_environment(mmdSkeletonAdapter_unit) -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmdSkeletonAdapter_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") - -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() -if(USDMMD_TEST_PYTHON) - add_test(NAME mmdSkeletonAdapter_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name mmdSkeletonAdapter - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmdSkeletonAdapter_link_$.txt" - --binary "$" - --allow mmdModel::mmdModel - --allow motionRetarget::motionRetarget - --allow-openusd-foundation) -endif() +# WORKSPACE.md §2.1 allows mmdSkeletonAdapter mmdModel and motionRetarget, +# and through it the OpenUSD foundation closure only. +usdmmd_add_boundary_test(mmdSkeletonAdapter + TARGET mmdSkeletonAdapter BINARY mmdSkeletonAdapter_tests + ARGS --allow mmdModel::mmdModel + --allow motionRetarget::motionRetarget + --allow-openusd-foundation) diff --git a/libs/motionVmd/CMakeLists.txt b/libs/motionVmd/CMakeLists.txt index 1e7c2ff..6903a1c 100644 --- a/libs/motionVmd/CMakeLists.txt +++ b/libs/motionVmd/CMakeLists.txt @@ -9,67 +9,19 @@ # may not reach mmdPmx or mmdModel either. The boundary test below fails if # its sources include either, or its link line names anything. # -# Self-contained, so `ost library build libs/motionVmd` can configure it -# directly; also add_subdirectory()'d by the repository root. +# Self-contained, so `ost library build libs/motionVmd` or plain CMake can +# configure it directly; also add_subdirectory()'d by the repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_vmd_version LIMIT_COUNT 1) - string(STRIP "${_mmd_vmd_version}" _mmd_vmd_version) -else() - set(_mmd_vmd_version "0.1.0") # mirrors openstrata.library.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(motionVmd - VERSION ${_mmd_vmd_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "VMD motion reader for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED MOTIONVMD_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_vmd_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_vmd_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MOTIONVMD_BUILD_TESTS "Build motionVmd tests" ${_mmd_vmd_tests_default}) -endif() +usdmmd_component(TESTS_OPTION MOTIONVMD_BUILD_TESTS) include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") - -# Sanitizers and the fuzz target, for the lane that builds this library on its -# own (.github/workflows/parser-sanitizers.yml), exactly as mmdPmx declares -# them: a VMD reader takes untrusted bytes too. -set(MOTIONVMD_SANITIZERS "" CACHE STRING - "Sanitizers for motionVmd and its tests, e.g. \"address;undefined\" (Clang or GCC)") -option(MOTIONVMD_BUILD_FUZZER "Build the motionVmd_fuzz libFuzzer target (Clang only)" OFF) -if(MOTIONVMD_SANITIZERS) - list(JOIN MOTIONVMD_SANITIZERS "," _mmd_vmd_sanitize) - add_compile_options(-fsanitize=${_mmd_vmd_sanitize} -fno-sanitize-recover=all - -fno-omit-frame-pointer) - add_link_options(-fsanitize=${_mmd_vmd_sanitize}) -endif() -if(MOTIONVMD_BUILD_FUZZER) - if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") - message(FATAL_ERROR "MOTIONVMD_BUILD_FUZZER needs Clang's libFuzzer") - endif() - if(NOT MOTIONVMD_SANITIZERS) - message(FATAL_ERROR - "MOTIONVMD_BUILD_FUZZER needs MOTIONVMD_SANITIZERS (at least \"address\")") - endif() - add_compile_options(-fsanitize=fuzzer-no-link) -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdPackage.cmake") add_library(motionVmd STATIC src/Cp932.cpp @@ -90,37 +42,14 @@ target_include_directories(motionVmd "${CMAKE_CURRENT_SOURCE_DIR}/src") usdmmd_target_defaults(motionVmd) -include(GNUInstallDirs) -install(TARGETS motionVmd - EXPORT motionVmdTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -include(CMakePackageConfigHelpers) -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/motionVmdConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/motionVmdConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/motionVmd") -# Pre-1.0, a minor version may break the API, so a consumer's request is -# compatible only within the same major.minor. -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/motionVmdConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMinorVersion) -install(EXPORT motionVmdTargets - FILE motionVmdTargets.cmake - NAMESPACE motionVmd:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/motionVmd") -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/motionVmdConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/motionVmdConfigVersion.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/motionVmd") +usdmmd_install_library(motionVmd) if(MOTIONVMD_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() -if(MOTIONVMD_BUILD_FUZZER) +# The libFuzzer target, as mmdPmx has one: a VMD reader takes untrusted bytes +# too. +if(USDMMD_BUILD_FUZZERS) add_subdirectory(fuzz) endif() diff --git a/libs/motionVmd/fuzz/CMakeLists.txt b/libs/motionVmd/fuzz/CMakeLists.txt index ae2dd13..b57cf0f 100644 --- a/libs/motionVmd/fuzz/CMakeLists.txt +++ b/libs/motionVmd/fuzz/CMakeLists.txt @@ -1,10 +1,12 @@ # SPDX-License-Identifier: Apache-2.0 # # motionVmd_fuzz: the libFuzzer target (fuzz_read.cpp). Clang only; the library -# itself is instrumented with -fsanitize=fuzzer-no-link by the parent file. +# itself is instrumented with -fsanitize=fuzzer-no-link by +# usdmmd_target_defaults() (cmake/UsdMmdSanitizers.cmake). add_executable(motionVmd_fuzz fuzz_read.cpp) target_link_libraries(motionVmd_fuzz PRIVATE motionVmd::motionVmd) target_include_directories(motionVmd_fuzz PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../tests") +usdmmd_target_defaults(motionVmd_fuzz) target_compile_options(motionVmd_fuzz PRIVATE -fsanitize=fuzzer) target_link_options(motionVmd_fuzz PRIVATE -fsanitize=fuzzer) diff --git a/libs/motionVmd/tests/CMakeLists.txt b/libs/motionVmd/tests/CMakeLists.txt index 9695948..a1ef951 100644 --- a/libs/motionVmd/tests/CMakeLists.txt +++ b/libs/motionVmd/tests/CMakeLists.txt @@ -1,16 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 -# Each suite checks with `assert()`, which NDEBUG compiles away -- and Release -# is the default build type -- so NDEBUG is undefined for these targets only. +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + function(_mmd_vmd_test target) - add_executable(${target} ${ARGN}) + usdmmd_test_executable(${target} ${ARGN}) target_link_libraries(${target} PRIVATE motionVmd::motionVmd) target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_SOURCE_DIR}") - usdmmd_target_defaults(${target}) - target_compile_options(${target} - PRIVATE $,/UNDEBUG,-UNDEBUG>) endfunction() _mmd_vmd_test(motionVmd_tests @@ -24,37 +21,19 @@ add_test(NAME motionVmd_unit COMMAND motionVmd_tests) _mmd_vmd_test(motionVmd_robustness_tests test_robustness.cpp) add_test(NAME motionVmd_robustness COMMAND motionVmd_robustness_tests) -# The link line exactly as CMake resolved it: WORKSPACE.md §2.1 allows -# motionVmd no edge at all. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/motionVmd_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -INTERFACE_LINK_LIBRARIES=$ -") +# WORKSPACE.md §2.1 allows motionVmd no edge at all, and no header of the +# model libraries: motionVmd is extraction-ready (WORKSPACE.md §2.2). +usdmmd_add_boundary_test(motionVmd TARGET motionVmd BINARY motionVmd_tests + ARGS --forbid-include mmdPmx/ + --forbid-include mmdModel/) -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() +# The committed CP932 table is exactly what its generator writes. +usdmmd_test_python() if(USDMMD_TEST_PYTHON) - # No OpenUSD, and no header of the model libraries: motionVmd is - # extraction-ready (WORKSPACE.md §2.2). - add_test(NAME motionVmd_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../scripts/check_library_boundaries.py" - --name motionVmd - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/motionVmd_link_$.txt" - --binary "$" - --forbid-include mmdPmx/ - --forbid-include mmdModel/) - # The committed CP932 table is exactly what its generator writes. add_test(NAME motionVmd_cp932_table COMMAND "${USDMMD_TEST_PYTHON}" "${CMAKE_CURRENT_SOURCE_DIR}/../tools/generate_cp932_table.py" --check) else() message(WARNING - "motionVmd: no Python 3 interpreter; the boundary and table checks are not registered") + "motionVmd: no Python 3 interpreter; the table check is not registered") endif() diff --git a/plugins/usdMmdFileFormat/CMakeLists.txt b/plugins/usdMmdFileFormat/CMakeLists.txt index 6247c9d..13241f3 100644 --- a/plugins/usdMmdFileFormat/CMakeLists.txt +++ b/plugins/usdMmdFileFormat/CMakeLists.txt @@ -3,46 +3,26 @@ # usdMmdFileFormat -- the OpenUSD SdfFileFormat bundle that imports .pmx # (docs/design/DESIGN_POLICY.md §5.4). # -# Self-contained, so `ost plugin build plugins/usdMmdFileFormat` can configure -# it directly; also add_subdirectory()'d by the repository root for the -# plain-CMake workflow. +# Self-contained, so `ost plugin build plugins/usdMmdFileFormat` or plain +# CMake can configure it directly against installed packages; also +# add_subdirectory()'d by the repository root. cmake_minimum_required(VERSION 3.22) -# The repository-root VERSION file is the single source. A standalone export -# without the repository root falls back to the manifest's version. -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_plugin_version LIMIT_COUNT 1) - string(STRIP "${_mmd_plugin_version}" _mmd_plugin_version) -else() - set(_mmd_plugin_version "0.1.0") # mirrors openstrata.plugin.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(UsdMmdFileFormatPlugin - VERSION ${_mmd_plugin_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "OpenUSD pmx file-format plugin" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() +usdmmd_component() include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdTargets.cmake") - -# Resolved from the runtime OpenStrata activates, or from CMAKE_PREFIX_PATH in -# the plain-CMake flow. Idempotent when the root project already found it. -if(NOT pxr_FOUND) - find_package(pxr REQUIRED CONFIG) -endif() include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdOpenUsd.cmake") +# OpenUSD, from the runtime OpenStrata activates or from CMAKE_PREFIX_PATH in +# the plain-CMake flow. The composed root build has already resolved it, and +# this reuses those targets; either way the pin is checked here. +usdmmd_find_openusd() + # mmdPmx and mmdModel are plain libraries, not bundles. ost executes the # manifest's requires.libraries closure -- it builds and installs them into # the workspace prefix before configuring this bundle -- and the composed root @@ -135,11 +115,8 @@ if(NOT _mmd_stage_contract_version) "usdMmdFileFormat: could not read kStageContractVersion from " "src/usd/UsdMmdAuthorer.h") endif() +# VERSION is one already: cmake/UsdMmdProject.cmake reads it. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_mmd_authorer_header}") -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") -endif() find_package(Git QUIET) add_custom_target(UsdMmdFileFormat_buildInfo ALL @@ -148,7 +125,7 @@ add_custom_target(UsdMmdFileFormat_buildInfo ALL "-DOUTPUT=${_mmd_resources}/buildInfo.json" "-DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}" "-DGIT_EXECUTABLE=${GIT_EXECUTABLE}" - "-D_mmd_plugin_version=${_mmd_plugin_version}" + "-D_mmd_plugin_version=${PROJECT_VERSION}" "-D_mmd_stage_contract_version=${_mmd_stage_contract_version}" "-DUSDMMD_OPENUSD_RELEASE=${USDMMD_OPENUSD_RELEASE}" "-DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}" diff --git a/plugins/usdMmdFileFormat/cmake/WriteBuildInfo.cmake b/plugins/usdMmdFileFormat/cmake/WriteBuildInfo.cmake index 3caa7c4..907ccbb 100644 --- a/plugins/usdMmdFileFormat/cmake/WriteBuildInfo.cmake +++ b/plugins/usdMmdFileFormat/cmake/WriteBuildInfo.cmake @@ -11,8 +11,8 @@ # untouched. # # Every other value is fixed at configure time and passed in with -D; the -# bundle's CMakeLists.txt makes the files they come from (VERSION, the -# authorer's header) configure dependencies. +# bundle's CMakeLists.txt makes the files they come from (VERSION, through +# cmake/UsdMmdProject.cmake, and the authorer's header) configure dependencies. # # Inputs: TEMPLATE, OUTPUT, SOURCE_DIR, GIT_EXECUTABLE (may be empty), and the # @-variables TEMPLATE names. diff --git a/scripts/check_docs.py b/scripts/check_docs.py index 5320e32..dc8058f 100644 --- a/scripts/check_docs.py +++ b/scripts/check_docs.py @@ -16,8 +16,9 @@ External links (`http:`, `https:`, `mailto:`) are not fetched. Mirrors -- docs/architecture/WORKSPACE.md §4: the repository-root VERSION is -the single product version, and openstrata.toml, every component manifest and -every CMake fallback mirror it, every range a manifest requires a sibling in +the single product version, and openstrata.toml and every component manifest +mirror it, every CMake project() takes it from cmake/UsdMmdProject.cmake +rather than restating it, every range a manifest requires a sibling in admits it, and so does every version the installed-consumer lane asks `find_package` for; cmake/UsdMmdOpenUsd.cmake's pin and every bundle manifest's `runtime.openusd` name the same OpenUSD release. @@ -175,6 +176,10 @@ def check_ranges(root: pathlib.Path, manifest: pathlib.Path, version: str, if dependency in sibling_ids and not in_range(version, lower, upper)] +# `project(mmdPmx VERSION ${USDMMD_VERSION} ...)`: the argument after VERSION. +PROJECT_VERSION = re.compile(r"^project\(\w+\s+VERSION\s+(\S+)", re.MULTILINE) + + def check_mirrors(root: pathlib.Path) -> list[str]: errors: list[str] = [] version = (root / "VERSION").read_text(encoding="utf-8").strip() @@ -194,10 +199,12 @@ def expect(path: pathlib.Path, pattern: str, want: str, what: str) -> None: for manifest in manifests: expect(manifest, r"^\s+version:\s*([0-9][^\s#]*)", version, "version") errors.extend(check_ranges(root, manifest, version, sibling_ids)) - for cmake in sorted(root.glob("*/*/CMakeLists.txt")): - if "../../VERSION" in cmake.read_text(encoding="utf-8"): - expect(cmake, r'set\(_mmd_\w+_version "([^"]+)"\)', version, - "standalone fallback version") + for cmake in [root / "CMakeLists.txt", *sorted(root.glob("*/*/CMakeLists.txt"))]: + project = PROJECT_VERSION.search(cmake.read_text(encoding="utf-8")) + if project and project.group(1) != "${USDMMD_VERSION}": + errors.append(f"{cmake.relative_to(root).as_posix()}: project() " + f"VERSION is {project.group(1)}, expected " + "${USDMMD_VERSION} (cmake/UsdMmdProject.cmake)") consumer = root / "tests" / "installed_consumer" / "CMakeLists.txt" for package, requested in FIND_PACKAGE_VERSION.findall( diff --git a/scripts/check_installed_consumer.py b/scripts/check_installed_consumer.py index 2cc586b..82c3a5e 100644 --- a/scripts/check_installed_consumer.py +++ b/scripts/check_installed_consumer.py @@ -304,10 +304,17 @@ def main() -> int: if not adapter_probes: print("the consumer built no adapter_probe", file=sys.stderr) return 1 + # The adapters link OpenUSD's foundation libraries through the shared + # motion packages, so the probe loads them: on Windows from PATH, which + # an `ost` session sets and a plain-CMake build does not. + runtime_env = dict(os.environ) + runtime_env["PATH"] = os.pathsep.join( + [str(args.usd_root / "bin"), str(args.usd_root / "lib"), + runtime_env.get("PATH", "")]) adapted = subprocess.run( [str(adapter_probes[0]), str(vmd_fixtures / "sample.vmd"), str(fixtures / model)], text=True, encoding="utf-8", - stdout=subprocess.PIPE) + stdout=subprocess.PIPE, env=runtime_env) adapter_lines = adapted.stdout.splitlines() if (adapted.returncode != 0 or len(adapter_lines) != 1 or not adapter_lines[0].startswith("samples=") @@ -319,10 +326,8 @@ def main() -> int: f"{adapter_lines[0]}") # The Python host, with only the prefix on the plugin path. - env = dict(os.environ) + env = dict(runtime_env) env["PXR_PLUGINPATH_NAME"] = str(prefix / PLUGIN_RESOURCES) - paths = [str(args.usd_root / "bin"), str(args.usd_root / "lib")] - env["PATH"] = os.pathsep.join(paths + [env.get("PATH", "")]) env["PYTHONPATH"] = os.pathsep.join( [str(args.usd_root / "lib" / "python"), env.get("PYTHONPATH", "")]) run([sys.executable, source / "open_stage.py", prefix, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c4ece33..1465d81 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,13 +30,10 @@ add_test(NAME workspace_docs_selftest COMMAND "${USDMMD_TEST_PYTHON}" "${PROJECT_SOURCE_DIR}/scripts/check_docs.py" --selftest) +include("${PROJECT_SOURCE_DIR}/cmake/UsdMmdTesting.cmake") + # The OpenUSD install root, from where pxrConfig.cmake was found. -set(_usd_root "") -if(pxr_DIR AND EXISTS "${pxr_DIR}/bin") - set(_usd_root "${pxr_DIR}") -elseif(pxr_DIR) - get_filename_component(_usd_root "${pxr_DIR}/../../.." ABSOLUTE) -endif() +usdmmd_openusd_root(_usd_root) # The plugin as a host finds it: through the bundle's own plugInfo.json, whose # relative LibraryPath points at the library staged into the bundle's lib/. diff --git a/tools/mmdInspect/CMakeLists.txt b/tools/mmdInspect/CMakeLists.txt index e63679f..963eae8 100644 --- a/tools/mmdInspect/CMakeLists.txt +++ b/tools/mmdInspect/CMakeLists.txt @@ -4,43 +4,17 @@ # §5.5). It links mmdPmx and nothing else: WORKSPACE.md §2.1 allows it no # OpenUSD, and its link line is the cheapest place to keep that true. # -# Self-contained, so a standalone configure resolves mmdPmx as an installed -# package; also add_subdirectory()'d by the repository root, which already has -# the in-tree target. +# Self-contained, so a standalone configure -- plain CMake or `ost` -- +# resolves mmdPmx as an installed package; also add_subdirectory()'d by the +# repository root, which already has the in-tree target. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_inspect_version LIMIT_COUNT 1) - string(STRIP "${_mmd_inspect_version}" _mmd_inspect_version) -else() - set(_mmd_inspect_version "0.1.0") # mirrors openstrata.tool.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(mmdInspect - VERSION ${_mmd_inspect_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "PMX inspection CLI for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED MMDINSPECT_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_mmd_inspect_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_mmd_inspect_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(MMDINSPECT_BUILD_TESTS "Build mmd_inspect tests" ${_mmd_inspect_tests_default}) -endif() +usdmmd_component(TESTS_OPTION MMDINSPECT_BUILD_TESTS) if(NOT TARGET mmdPmx::mmdPmx) find_package(mmdPmx CONFIG REQUIRED) diff --git a/tools/mmdInspect/tests/CMakeLists.txt b/tools/mmdInspect/tests/CMakeLists.txt index 03fd16e..4efc9cc 100644 --- a/tools/mmdInspect/tests/CMakeLists.txt +++ b/tools/mmdInspect/tests/CMakeLists.txt @@ -1,11 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +usdmmd_test_python() if(NOT USDMMD_TEST_PYTHON) message(WARNING "mmd_inspect: no Python 3 interpreter; its tests are not registered") return() @@ -21,14 +18,5 @@ add_test(NAME mmd_inspect_fixtures --generator "${_repo}/tests/fixtures/generate_fixtures.py") # WORKSPACE.md §2.1 allows mmd_inspect mmdPmx and nothing else -- no OpenUSD. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mmd_inspect_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -") -add_test(NAME mmd_inspect_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" "${_repo}/scripts/check_library_boundaries.py" - --name mmd_inspect - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/mmd_inspect_link_$.txt" - --binary "$" - --allow mmdPmx::mmdPmx) +usdmmd_add_boundary_test(mmd_inspect TARGET mmd_inspect BINARY mmd_inspect + ARGS --allow mmdPmx::mmdPmx) diff --git a/tools/vmdInspect/CMakeLists.txt b/tools/vmdInspect/CMakeLists.txt index 894d240..573f75b 100644 --- a/tools/vmdInspect/CMakeLists.txt +++ b/tools/vmdInspect/CMakeLists.txt @@ -4,42 +4,17 @@ # (docs/design/MOTION_CONTRACT.md §2). It links motionVmd and nothing else: # WORKSPACE.md §2.1 allows it no OpenUSD and no model library. # -# Self-contained, so a standalone configure resolves motionVmd as an installed -# package; also add_subdirectory()'d by the repository root. +# Self-contained, so a standalone configure -- plain CMake or `ost` -- +# resolves motionVmd as an installed package; also add_subdirectory()'d by the +# repository root. cmake_minimum_required(VERSION 3.22) -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION") - file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/../../VERSION" - _mmd_vmd_inspect_version LIMIT_COUNT 1) - string(STRIP "${_mmd_vmd_inspect_version}" _mmd_vmd_inspect_version) -else() - set(_mmd_vmd_inspect_version "0.1.0") # mirrors openstrata.tool.yaml -endif() - +include("${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/UsdMmdProject.cmake") project(vmdInspect - VERSION ${_mmd_vmd_inspect_version} + VERSION ${USDMMD_VERSION} DESCRIPTION "VMD inspection CLI for usd-mmd-plugins" LANGUAGES CXX) - -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) -endif() - -if(NOT DEFINED VMDINSPECT_BUILD_TESTS) - if(DEFINED USDMMD_BUILD_TESTS) - set(_vmd_inspect_tests_default "${USDMMD_BUILD_TESTS}") - else() - set(_vmd_inspect_tests_default "${PROJECT_IS_TOP_LEVEL}") - endif() - option(VMDINSPECT_BUILD_TESTS "Build vmd_inspect tests" ${_vmd_inspect_tests_default}) -endif() +usdmmd_component(TESTS_OPTION VMDINSPECT_BUILD_TESTS) if(NOT TARGET motionVmd::motionVmd) find_package(motionVmd CONFIG REQUIRED) diff --git a/tools/vmdInspect/tests/CMakeLists.txt b/tools/vmdInspect/tests/CMakeLists.txt index a450350..436da01 100644 --- a/tools/vmdInspect/tests/CMakeLists.txt +++ b/tools/vmdInspect/tests/CMakeLists.txt @@ -1,11 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 -if(NOT DEFINED USDMMD_TEST_PYTHON) - find_package(Python3 COMPONENTS Interpreter QUIET) - if(Python3_Interpreter_FOUND) - set(USDMMD_TEST_PYTHON "${Python3_EXECUTABLE}") - endif() -endif() +include("${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/UsdMmdTesting.cmake") + +usdmmd_test_python() if(NOT USDMMD_TEST_PYTHON) message(WARNING "vmd_inspect: no Python 3 interpreter; its tests are not registered") return() @@ -21,16 +18,7 @@ add_test(NAME vmd_inspect_fixtures --generator "${_repo}/tests/fixtures/generate_vmd_fixtures.py") # WORKSPACE.md §2.1 allows vmd_inspect motionVmd and nothing else. -file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/vmd_inspect_link_$.txt" - CONTENT "LINK_LIBRARIES=$ -") -add_test(NAME vmd_inspect_boundaries - COMMAND "${USDMMD_TEST_PYTHON}" "${_repo}/scripts/check_library_boundaries.py" - --name vmd_inspect - --source "${CMAKE_CURRENT_SOURCE_DIR}/.." - --link-file "${CMAKE_CURRENT_BINARY_DIR}/vmd_inspect_link_$.txt" - --binary "$" - --allow motionVmd::motionVmd - --forbid-include mmdPmx/ - --forbid-include mmdModel/) +usdmmd_add_boundary_test(vmd_inspect TARGET vmd_inspect BINARY vmd_inspect + ARGS --allow motionVmd::motionVmd + --forbid-include mmdPmx/ + --forbid-include mmdModel/) From 050f21709acf0dd290810f1c8e7e5ce731d8b076 Mon Sep 17 00:00:00 2001 From: snkmcb Date: Thu, 24 Sep 2026 01:09:36 +0900 Subject: [PATCH 2/2] Document the CMake contract, and why staging stays in the source tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WORKSPACE.md §5 now states the three build modes as one contract -- who declares an edge, how installed packages carry transitive edges, how OpenUSD is resolved, what the cmake/ modules provide -- and records that the plugin and tools are staged in their source directories because ost 0.23.3 reads them there. building.md gains the single-component plain-CMake build and the new sanitizer options, and every plain-CMake command in it was run again on 2026-09-24. docs/reports/ost/ starts with report 01: the contract needs nothing from ost, and staging the bundle into its build tree builds but fails `ost plugin test` from L0 to L5, measured; it asks for verification and packaging from a staged layout. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 18 +++ README.md | 7 +- docs/README.md | 2 +- docs/architecture/DEPENDENCIES.md | 4 +- docs/architecture/WORKSPACE.md | 77 +++++++--- docs/guides/building.md | 83 +++++++--- docs/releases/README.md | 4 +- ...3-a-bundle-is-staged-in-its-source-tree.md | 142 ++++++++++++++++++ docs/reports/ost/README.md | 22 +++ 9 files changed, 310 insertions(+), 49 deletions(-) create mode 100644 docs/reports/ost/01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md create mode 100644 docs/reports/ost/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d80fc8..bd3cc47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,24 @@ Stage-contract version: **1**, authored since the Phase 0 importer. ### Changed +- **One CMake dependency contract for plain CMake, a single component and + `ost`.** Inside the repository an edge is the in-tree target when it + exists and the installed package otherwise; outside it, always an + installed package found by the component that links it — the adapters now + find `motionRetarget` and `motionCore` themselves, and the root lists no + external package but OpenUSD. `CMakePresets.json` no longer reads + `USD_INSTALL_ROOT`: the caller passes the dependency prefix as + `CMAKE_PREFIX_PATH`. The shared CMake is one module per concern + (`UsdMmdProject`, `UsdMmdTargets`, `UsdMmdSanitizers`, `UsdMmdPackage`, + `UsdMmdTesting`, `UsdMmdOpenUsd`): every library installs through + `usdmmd_install_library()`, OpenUSD resolves through + `usdmmd_find_openusd()` only where its targets are not yet visible, and + build settings are per target. The per-component sanitizer options became + `USDMMD_SANITIZERS` and `USDMMD_BUILD_FUZZERS`. The adapters' tests put + OpenUSD on `PATH` themselves, so they pass outside an `ost` session. + Staging the plugin and tools out of the source tree waits on OpenStrata + (`WORKSPACE.md` §5; ost report 01). + - **The `ost` pin is 0.23.3.** 0.23.3 adds a tool's own library edges to `ost library pull`, the root build and the graph check, and discards a build tree whose cache was configured against another runtime diff --git a/README.md b/README.md index 4832b39..5154141 100644 --- a/README.md +++ b/README.md @@ -112,18 +112,19 @@ paths, and Japanese texture filenames resolve. The full contract is The workspace builds both with [OpenStrata](https://github.com/animu-sphere/open-strata) (`ost`) and with -plain CMake, against OpenUSD 26.08 exactly +plain CMake, against OpenUSD 26.08 exactly and `usd-motion-plugins`' released +packages, all found as installed packages on `CMAKE_PREFIX_PATH` ([WORKSPACE.md §5](docs/architecture/WORKSPACE.md#5-build-modes)): ```powershell -$env:USD_INSTALL_ROOT = "" +$env:CMAKE_PREFIX_PATH = ";;;" cmake --preset windows-msvc cmake --build --preset windows-release ctest --preset windows-release ``` or `ost build` and `ost test`. [docs/guides/building.md](docs/guides/building.md) -has the full set — the bundle on its own, packaging, and the +has the full set — one component on its own, packaging, and the installed-consumer lane — every command in it run. ## Documentation diff --git a/docs/README.md b/docs/README.md index f93897e..d9baea4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -35,7 +35,7 @@ place that says what is implemented. | [roadmap/](roadmap/) | What is planned next (incomplete work only), and which release carries it. | [README.md](roadmap/README.md) · [current.md](roadmap/current.md) | | [contributing/](contributing/) | How to maintain these documents. | [documentation.md](contributing/documentation.md) | | [releases/](releases/) | Immutable per-version release records, and how a release is cut. | [README.md](releases/README.md) · [v0.1.0.md](releases/v0.1.0.md) | -| [reports/](reports/) | Dated evidence from real runs; append-only. | [2026-09-15-phase2-local-models.md](reports/2026-09-15-phase2-local-models.md) · [2026-09-16-phase4-local-models.md](reports/2026-09-16-phase4-local-models.md) · [2026-09-16-phase5-local-models.md](reports/2026-09-16-phase5-local-models.md) · [2026-09-17-phase6-local-models.md](reports/2026-09-17-phase6-local-models.md) · [2026-09-17-phase7-local-motions.md](reports/2026-09-17-phase7-local-motions.md) · [2026-09-22-phase9-motion-acceptance.md](reports/2026-09-22-phase9-motion-acceptance.md) | +| [reports/](reports/) | Dated evidence from real runs; append-only. | [2026-09-15-phase2-local-models.md](reports/2026-09-15-phase2-local-models.md) · [2026-09-16-phase4-local-models.md](reports/2026-09-16-phase4-local-models.md) · [2026-09-16-phase5-local-models.md](reports/2026-09-16-phase5-local-models.md) · [2026-09-17-phase6-local-models.md](reports/2026-09-17-phase6-local-models.md) · [2026-09-17-phase7-local-motions.md](reports/2026-09-17-phase7-local-motions.md) · [2026-09-22-phase9-motion-acceptance.md](reports/2026-09-22-phase9-motion-acceptance.md) · [ost/](reports/ost/README.md), the OpenStrata dogfooding reports | ## Canonical documents diff --git a/docs/architecture/DEPENDENCIES.md b/docs/architecture/DEPENDENCIES.md index fc21728..032afe7 100644 --- a/docs/architecture/DEPENDENCIES.md +++ b/docs/architecture/DEPENDENCIES.md @@ -34,13 +34,13 @@ calls the MaterialX library. The MaterialX document version it declares | | | | --- | --- | | Language | C++20 (the public parser API takes `std::span`) | -| Build | CMake **3.22** or later, the minimum `usd-vrm-plugins` declares; `CMakePresets.json` for the plain-CMake path | +| Build | CMake **3.22** or later, the minimum `usd-vrm-plugins` declares; `CMakePresets.json` for the plain-CMake path, which takes every dependency from `CMAKE_PREFIX_PATH` as an installed package ([WORKSPACE.md §5](WORKSPACE.md#5-build-modes)) | | Compilers | MSVC on Windows, Clang on macOS (arm64), GCC on Linux — the three hosted lanes `usd-vrm-plugins` runs | | Windows flags | `/utf-8`, `NOMINMAX`, applied by `usdmmd_target_defaults()` in [cmake/UsdMmdTargets.cmake](../../cmake/UsdMmdTargets.cmake) ([WORKSPACE.md §5](WORKSPACE.md#5-build-modes)) | | Python | the Python OpenUSD was built against — 3.13 for the 26.08 runtimes — for stage tests and tooling. The root project finds the interpreter *after* OpenUSD, so it inherits the one `pxrConfig.cmake` names | | OpenStrata | `ost` **0.23.3**, pinned in `openstrata.ci.yaml`; required for digest-pinned external-library artifacts in root builds | | Unit-test framework | **none**, as in `usd-vrm-plugins`: each suite is a plain executable that checks with `assert()`, compiled with `NDEBUG` undefined so Release builds still check, and registered with CTest | -| Sanitizers and fuzzing | Clang 18's AddressSanitizer, UndefinedBehaviorSanitizer and libFuzzer, from Ubuntu 24.04's packages, in [parser-sanitizers.yml](../../.github/workflows/parser-sanitizers.yml) only; `mmdPmx`'s `MMDPMX_SANITIZERS` and `MMDPMX_BUILD_FUZZER` options, and the matching `MMDMODEL_`, `MOTIONVMD_` and `MMDMOTIONBINDING_` ones, switch them on, and nothing shipped is built with them. Toolchain runtimes, not dependencies: no code is vendored and nothing links them outside that lane | +| Sanitizers and fuzzing | Clang 18's AddressSanitizer, UndefinedBehaviorSanitizer and libFuzzer, from Ubuntu 24.04's packages, in [parser-sanitizers.yml](../../.github/workflows/parser-sanitizers.yml) only; the `USDMMD_SANITIZERS` and `USDMMD_BUILD_FUZZERS` options of [cmake/UsdMmdSanitizers.cmake](../../cmake/UsdMmdSanitizers.cmake) switch them on, and nothing shipped is built with them. Toolchain runtimes, not dependencies: no code is vendored and nothing links them outside that lane | ## 3. Refused dependencies diff --git a/docs/architecture/WORKSPACE.md b/docs/architecture/WORKSPACE.md index ac2fcb0..07db53f 100644 --- a/docs/architecture/WORKSPACE.md +++ b/docs/architecture/WORKSPACE.md @@ -24,7 +24,7 @@ edge on 2026-09-21 (§2.4, [DESIGN_POLICY.md §20](../design/DESIGN_POLICY.md#20-alignment-with-the-usd-motion-plugins-design-policy)). The shape follows `usd-vrm-plugins`' workspace contract on purpose — the same -plugin/library split, the same manifests, the same two build modes — so that a +plugin/library split, the same manifests, the same build modes — so that a contributor, and `usd-avatar-runtime`, can treat the two repositories alike. It does not copy VRM-specific identities that have no MMD reason to exist, such as a package resolver. @@ -227,8 +227,8 @@ runtime consumer makes them concrete usd-mmd-plugins/ ├─ .github/workflows/ ost-source-ci.yml (generated from openstrata.ci.yaml); hand-written: │ docs-check.yml, parser-sanitizers.yml, release.yml -├─ cmake/ UsdMmdOpenUsd.cmake (the OpenUSD pin), UsdMmdTargets.cmake (per-target -│ compile flags, the UTF-8 code-page manifest helper), utf8-code-page.manifest +├─ cmake/ UsdMmdProject, UsdMmdTargets, UsdMmdSanitizers, UsdMmdPackage, +│ UsdMmdTesting, UsdMmdOpenUsd (.cmake: §5), utf8-code-page.manifest ├─ docs/ see docs/README.md ├─ libs/ │ ├─ mmdPmx/ include/ src/ tests/ fuzz/ cmake/ CMakeLists.txt openstrata.library.yaml @@ -314,34 +314,68 @@ bundle's copies. ## 5. Build modes -The workspace builds two ways, and both are kept working: +One CMake contract, driven three ways. The difference between them is only +who prepares the **dependency prefix** — the installed OpenUSD 26.08 and the +installed `usd-motion-plugins` packages — never what the CMake does with it: ```sh -# OpenStrata -ost plugin build plugins/usdMmdFileFormat -ost plugin test plugins/usdMmdFileFormat -ost plugin package plugins/usdMmdFileFormat - -# Plain CMake, against any supported OpenUSD install -cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/openusd +# Plain CMake, the whole repository: the prefix is the caller's +cmake -S . -B build -DCMAKE_PREFIX_PATH= cmake --build build --config Release ctest --test-dir build -C Release + +# Plain CMake, one component: the same prefix, plus the installed packages +# of the component's in-repository edges +cmake -S libs/mmdMotionAdapter -B build/mmdMotionAdapter -DCMAKE_PREFIX_PATH=";" + +# OpenStrata: `ost` composes the prefix and configures the same files +ost build +ost plugin build plugins/usdMmdFileFormat +ost library build libs/mmdMotionAdapter ``` -Both have been run; [guides/building.md](../guides/building.md) records the -exact commands, including the presets in `CMakePresets.json`. +All three have been run; [guides/building.md](../guides/building.md) records +the exact commands, including the presets in `CMakePresets.json`. + +The contract every `CMakeLists.txt` keeps: + +| Rule | Detail | +| --- | --- | +| In-repository edges | the in-tree target when it exists, the installed package otherwise: `if(NOT TARGET mmdPmx::mmdPmx) find_package(mmdPmx CONFIG REQUIRED)`. So the same file configures composed by the root and on its own. | +| Edges out of the repository | always `find_package( CONFIG REQUIRED)` on an installed package. Never a sibling checkout: no `add_subdirectory()` of a path outside this repository, and no probing for one (`if(EXISTS ../usd-motion-plugins)`), so the checkout layout is never an input to the graph. | +| Who declares an edge | the component that links it, next to its target. The root `CMakeLists.txt` names components and their order, and resolves OpenUSD once as the boundary the pure libraries are added before; it lists no other external package. | +| Transitive edges | a package's `Config.cmake` finds what its public link interface needs with `find_dependency()`, each behind a target check, so a consumer never lists a transitive dependency ([PACKAGE_CONTRACT.md](PACKAGE_CONTRACT.md)). | +| OpenUSD | resolved through `usdmmd_find_openusd()` ([cmake/UsdMmdOpenUsd.cmake](../../cmake/UsdMmdOpenUsd.cmake)), which runs `pxrConfig.cmake` only where OpenUSD's targets are not yet visible — the check `usd-motion-plugins`' package configs make — and holds every entry point to the pin. | +| OpenStrata | nothing in CMake knows it. `ost` writes a toolchain whose `CMAKE_PREFIX_PATH` holds the runtime, the workspace prefix and the digest-pinned external artifacts; a plain-CMake caller passes the same prefixes. | +| Build settings | per target, never per directory: `usdmmd_target_defaults()` applies the compile flags and the sanitizer instrumentation as `PRIVATE` properties, so a composed build sees no flag it did not ask for. | + +The root adds the five OpenUSD-free libraries and both tools before OpenUSD is +resolved, so nothing they configure can see pxr; then the two adapters, whose +motion packages reuse the OpenUSD targets resolved there; then the bundle. + +The shared CMake lives in `cmake/`, one module per concern, and hides no +`add_library()`, `target_link_libraries()` or dependency declaration: + +| Module | Provides | +| --- | --- | +| `UsdMmdProject.cmake` | `USDMMD_VERSION` from `VERSION`, before `project()`; `usdmmd_component()` — C++20, the single-config Release default, the component's tests option | +| `UsdMmdTargets.cmake` | `usdmmd_target_defaults()` (below), `usdmmd_use_utf8_code_page()` | +| `UsdMmdSanitizers.cmake` | `USDMMD_SANITIZERS`, `USDMMD_BUILD_FUZZERS` | +| `UsdMmdPackage.cmake` | `usdmmd_install_library()` — one package layout, one `SameMinorVersion` policy, one export for every plain library. A bundle installs its own way. | +| `UsdMmdTesting.cmake` | `usdmmd_test_executable()`, `usdmmd_add_boundary_test()` (§2.3), the test interpreter and the OpenUSD runtime environment | +| `UsdMmdOpenUsd.cmake` | `usdmmd_find_openusd()` and the pin | -- The root `CMakeLists.txt` composes every component for development. It - adds `mmdPmx` before resolving OpenUSD, so nothing the library configures - can see pxr. -- Once a component is packaged, each bundle also builds **standalone** - against the installed packages of its dependencies - (`find_package(mmdPmx CONFIG REQUIRED)`). No consumer reaches into a - sibling's source tree with an ad-hoc `add_subdirectory()`. - **Windows:** every target compiles with `/utf-8` and `NOMINMAX`; the plugin follows `usd-vrm-plugins`' DLL import/export discipline; executables embed a UTF-8 `activeCodePage` manifest ([TEXT_ENCODING_POLICY.md §4](../design/TEXT_ENCODING_POLICY.md#4-no-locale-anywhere)). +- **Staging:** the build writes the plugin library into the bundle's own + `lib/`, its `plugInfo.json` and `buildInfo.json` into + `plugin/resources/usdMmdFileFormat/`, and each tool into its `bin/` — inside + the source tree, and ignored by git. `ost` 0.23.3 reads a bundle's + registration and library, and a tool's declared directories, from the + component directory itself, so staging into the build tree waits on + OpenStrata ([ost report 01](../reports/ost/01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md)). What each installed package promises a consumer — its `find_package` name, target, header root and required packages — is @@ -380,7 +414,8 @@ first. motion evaluation at import. 4. The same bytes produce the same stage. 5. The authored stage does not change meaning without a stage-contract bump. -6. Both build modes work, and every bundle builds against installed siblings. +6. Every build mode of §5 works, and every component builds against installed + siblings. 7. No component keeps a private copy of a facility another component owns. One exception, and its reason: `motionVmd` declares its own diagnostic record, `Result` and diagnostic list, the same shape as `mmdPmx`'s, diff --git a/docs/guides/building.md b/docs/guides/building.md index 1209077..f7562be 100644 --- a/docs/guides/building.md +++ b/docs/guides/building.md @@ -2,9 +2,9 @@ How to build the workspace, run its tests, and package the plugin. Every command on this page has been run, on Windows 11 with Visual Studio 18 -(MSVC 19.51), CMake 4.4, Python 3.13 and OpenUSD 26.08, on 2026-09-15 — except -the sanitizer build, which was run on Ubuntu (WSL) with GCC 15, as that -section says. macOS and Linux run the same `ost` commands in CI +(MSVC 19.51), CMake 4.4, Python 3.13 and OpenUSD 26.08, on 2026-09-15, and the +plain-CMake sections again on 2026-09-24 — except the sanitizer build, which +was run on Ubuntu (WSL) with GCC, as that section says. macOS and Linux run the same `ost` commands in CI ([openstrata.ci.yaml](../../openstrata.ci.yaml)); their plain-CMake presets exist but have not been run by hand, so they are not documented here yet. @@ -23,21 +23,38 @@ Commands are PowerShell, run from the repository root. ## Plain CMake -`CMakePresets.json` reads the OpenUSD install from `USD_INSTALL_ROOT`: +Plain CMake is given one thing: the **dependency prefix**, as +`CMAKE_PREFIX_PATH` — OpenUSD 26.08, and `usd-motion-plugins`' `motionCore`, +`motionRetarget` and `motionUsd` 0.5 packages, installed +([WORKSPACE.md §5](../architecture/WORKSPACE.md#5-build-modes)). Nothing looks +for a sibling checkout. The presets in `CMakePresets.json` set the generator, +the architecture and the tests, and leave the prefix to the caller, either on +the command line or in CMake's own environment variable: ```powershell -$env:USD_INSTALL_ROOT = "" +$env:CMAKE_PREFIX_PATH = ";;;" cmake --preset windows-msvc cmake --build --preset windows-release ctest --preset windows-release ``` +`cmake --preset windows-msvc -DCMAKE_PREFIX_PATH="..."` does the same. The +motion packages are the digest-pinned artifacts the manifests name; after an +`ost library pull` they are under +`.strata/external-libraries////`, and that is where +the prefix above was taken from on 2026-09-24. An existing build tree keeps +the `CMAKE_PREFIX_PATH` it was first given in its cache and searches it before +the environment's, so configure a new tree (`-B `, or `--fresh`) when the +prefix changes. + The Windows preset names no generator, so CMake picks the newest Visual Studio installed. The build tree is `build/windows-msvc/`. The plugin library is staged into the bundle itself, `plugins/usdMmdFileFormat/lib/`, where the bundle's `plugInfo.json` expects it, `mmd_inspect` into `tools/mmdInspect/bin/`, and `vmd_inspect` into `tools/vmdInspect/bin/` -([inspecting.md](inspecting.md) says how to use them). +([inspecting.md](inspecting.md) says how to use them). That is where `ost` +reads them too, which is why they are not in the build tree +([ost report 01](../reports/ost/01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md)). `ctest` runs every test in the workspace: @@ -79,6 +96,29 @@ labelled and can be left out: ctest --preset windows-release -LE installed-consumer ``` +## One component, plain CMake + +Every component under `libs/`, `tools/` and `plugins/` configures on its own, +against the installed packages of its edges. Install a repository build, then +point a component at that install and the dependency prefix — for example the +motion adapter, which finds `mmdControl`, `mmdModel`, `mmdSkeletonAdapter` and +`motionCore` there, and `motionRetarget` and `motionUsd` for its acceptance +test: + +```powershell +cmake --install build/windows-msvc --prefix build/usdmmd-install --config Release +$env:CMAKE_PREFIX_PATH = "/build/usdmmd-install;;;;" +cmake -S libs/mmdMotionAdapter -B build/mmdMotionAdapter -A x64 +cmake --build build/mmdMotionAdapter --config Release +ctest --test-dir build/mmdMotionAdapter -C Release +``` + +Run on 2026-09-24 for `mmdPmx`, `mmdModel`, `mmdControl`, +`mmdSkeletonAdapter`, `mmdMotionAdapter` and `vmd_inspect`, whose own suites +all pass that way, and for the bundle, which configures and builds. A +component's tests are on when it is the top-level project +(`_BUILD_TESTS`, e.g. `MMDMOTIONADAPTER_BUILD_TESTS`). + ## OpenStrata The dependency graph, before anything builds: @@ -176,13 +216,16 @@ built. ## Sanitizers -`mmdPmx` builds on its own, and its unit, robustness and boundary tests run -under AddressSanitizer and UndefinedBehaviorSanitizer with two cache options. -Run on Ubuntu 24.04 under WSL, with GCC 15, CMake 4.2 and Ninja, from the -repository root: +The plain libraries build on their own, and their unit, robustness and +boundary tests run under AddressSanitizer and UndefinedBehaviorSanitizer with +one cache option, the same in every component: +`USDMMD_SANITIZERS` ([cmake/UsdMmdSanitizers.cmake](../../cmake/UsdMmdSanitizers.cmake)). +It instruments every target the configured component creates — library, +tests, fuzz harness — and nothing else. Run on Ubuntu 24.04 under WSL, with +GCC, CMake 4.2 and Ninja, from the repository root: ```sh -cmake -S libs/mmdPmx -B ~/mmd-sanitize -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMMDPMX_SANITIZERS="address;undefined" -DMMDPMX_BUILD_TESTS=ON +cmake -S libs/mmdPmx -B ~/mmd-sanitize -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSDMMD_SANITIZERS="address;undefined" -DMMDPMX_BUILD_TESTS=ON cmake --build ~/mmd-sanitize ctest --test-dir ~/mmd-sanitize --output-on-failure ``` @@ -192,20 +235,20 @@ instrumented parser is installed into a prefix first: ```sh cmake --install ~/mmd-sanitize --prefix ~/mmd-san-prefix -cmake -S libs/mmdModel -B ~/mmd-san-model -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMMDMODEL_SANITIZERS="address;undefined" -DMMDMODEL_BUILD_TESTS=ON -DCMAKE_PREFIX_PATH=~/mmd-san-prefix +cmake -S libs/mmdModel -B ~/mmd-san-model -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSDMMD_SANITIZERS="address;undefined" -DMMDMODEL_BUILD_TESTS=ON -DCMAKE_PREFIX_PATH=~/mmd-san-prefix cmake --build ~/mmd-san-model ctest --test-dir ~/mmd-san-model --output-on-failure ``` -`motionVmd`, `mmdMotionBinding` and `mmdControl` declare the same options -(`MOTIONVMD_SANITIZERS`, `MOTIONVMD_BUILD_FUZZER`, -`MMDMOTIONBINDING_SANITIZERS`, `MMDCONTROL_SANITIZERS`); instrumented, they -have run only in CI. +`motionVmd`, `mmdMotionBinding` and `mmdControl` follow the same way, each +against the prefix its edges were installed into; on 2026-09-24 the whole +chain of five passed instrumented, as +[parser-sanitizers.yml](../../.github/workflows/parser-sanitizers.yml) runs +it. -The fuzz targets need Clang's libFuzzer: `-DMMDPMX_BUILD_FUZZER=ON` or -`-DMOTIONVMD_BUILD_FUZZER=ON` with `clang++`, and the sanitizers on. They have -run only in CI; -[parser-sanitizers.yml](../../.github/workflows/parser-sanitizers.yml) is the +The fuzz targets need Clang's libFuzzer: `-DUSDMMD_BUILD_FUZZERS=ON` with +`clang++` and the sanitizers on builds `mmdPmx_fuzz` or `motionVmd_fuzz` in +the component that has one. They have run only in CI; the workflow is the record of its commands, including how the corpus is seeded from the generated fixtures. diff --git a/docs/releases/README.md b/docs/releases/README.md index b6a3ced..998f855 100644 --- a/docs/releases/README.md +++ b/docs/releases/README.md @@ -14,8 +14,8 @@ stated only in its ## How a release is cut -1. On a branch: set `VERSION`, and every manifest and CMake fallback that - mirrors it, including the sibling ranges the manifests require — +1. On a branch: set `VERSION`, and every manifest that mirrors it (CMake + reads it directly), including the sibling ranges the manifests require — `scripts/check_docs.py` fails until they agree. Move the changelog's `[Unreleased]` entries under `## [X.Y.Z] - YYYY-MM-DD`, write the record here, and give the Phases it carries their release in the diff --git a/docs/reports/ost/01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md b/docs/reports/ost/01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md new file mode 100644 index 0000000..5fc3377 --- /dev/null +++ b/docs/reports/ost/01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md @@ -0,0 +1,142 @@ +# A bundle is staged in its source tree + +- Date: 2026-09-24 +- Version exercised: `ost 0.23.3`, the published `x86_64-pc-windows-msvc` + asset, on this workstation (`cy2026-windows-x86_64-py313-usd`), in the + default `OST_HOME` +- Occasion: the CMake cleanup that fixes this repository's dependency + contract — every external edge an installed package found by the component + that links it, no sibling checkout, per-target build settings, one install + helper — and makes plain CMake a supported build mode beside `ost` + ([WORKSPACE.md §5](../../architecture/WORKSPACE.md#5-build-modes)) +- Prior report: none here; the ecosystem's series is `usd-vrm-plugins`' + ([report 45](https://github.com/animu-sphere/usd-vrm-plugins/blob/main/docs/reports/ost/45-2026-09-23-v0.23.3-a-tool-edge-arrives-and-a-bundle-tree-keeps-its-runtime.md) + is its newest) +- Next ask target: `ost 0.23.4` + +## TL;DR + +**The dependency contract needs nothing from `ost`. The one step of the +cleanup `ost` blocks is staging the build out of the source tree.** + +1. **Worked: `ost` already composes exactly the prefix plain CMake is given.** + The toolchain `ost` writes prepends the runtime, the workspace prefix and + each digest-pinned external artifact to `CMAKE_PREFIX_PATH`, which is the + whole of what the cleaned CMake asks for. Moving `motionCore` and + `motionRetarget` out of the root `CMakeLists.txt` into the adapters that + link them changed no `ost` result (§2). +2. **P2: a bundle's library and `plugInfo.json`, and a tool's declared + directories, are read from the component's source directory.** Staged + into the build tree instead, the bundle builds and then fails L0 through + L5 (§3). So the build writes into the source tree, and a plain-CMake build + and an `ost` build of the same checkout write the same files (§4). + +## 1. What was being done + +The cleanup follows the ecosystem's CMake contract: inside the repository an +edge is the in-tree target when it exists and the installed package +otherwise; outside it, always `find_package(... CONFIG)` on an installed +package; `ost` prepares that prefix and nothing in CMake knows it. It was +verified four ways on this workstation, all from one tree: + +| Mode | Command | Result | +| --- | --- | --- | +| plain CMake, repository | `cmake --preset windows-msvc -B build/verify-plain`, with `CMAKE_PREFIX_PATH` naming OpenUSD 26.08 and the three motion artifacts | 32 of 32 tests | +| plain CMake, one component | `cmake -S libs/` against the install of the first, for `mmdPmx`, `mmdModel`, `mmdControl`, `mmdSkeletonAdapter`, `mmdMotionAdapter`, `vmd_inspect`; the bundle configured and built alone | every suite passes | +| `ost`, workspace | `ost build`, `ost test` | 32 of 32 tests | +| `ost`, one component | `ost plugin build`/`test plugins/usdMmdFileFormat`; `ost library build`/`test libs/mmdMotionAdapter`, `libs/mmdPmx`; `ost library verify-consumer libs/mmdControl` | 12 pass, 0 fail, 4 skip; 3 of 3; 4 of 4; closure of 5 | + +## 2. What worked: the prefix is the contract + +`.strata/targets/cy2026-windows-x86_64-py313-usd/toolchain.cmake` prepends the +runtime prefix, and then the three pinned artifacts: + +```text +list(PREPEND CMAKE_PREFIX_PATH ".../.strata/external-libraries/.../motionCore/f06fef79…" + ".../motionRetarget/d6f83599…" ".../motionUsd/f1154553…") +``` + +A plain-CMake caller who passes the same four directories gets the same +graph: the adapters' own `find_package(motionRetarget 0.5 CONFIG REQUIRED)` +and `find_package(motionCore 0.5 CONFIG REQUIRED)` resolve them, and the +packages' configs reuse the OpenUSD targets the root resolved. `ost` needed no +knowledge of where a component declares its edges, and the cleanup needed no +OpenStrata variable. + +One difference is the session, not the prefix: an `ost` test session puts the +runtime's `bin/` and `lib/` on `PATH`, and a plain-CMake `ctest` does not. On +Windows the adapters' test executables then fail to start (`0xc0000135`), since +they load OpenUSD's foundation DLLs through the motion packages. That is this +repository's to state, and its tests now set it themselves. + +## 3. A bundle cannot be staged out of its source tree + +`crates/ost-plugin/src/bundle.rs` resolves both halves of a bundle against its +root: `plug_info()` joins the manifest's `usd.plug_info` to it, and +`lib_dir()` is `path("lib")`. Packaging copies from the same places +(`stage_plugin_bundle`, `crates/ost-cli/src/commands/plugin.rs`), and a tool's +`built_directories()` tests `root.join(directory)` +(`crates/ost-plugin/src/tool.rs`). Nothing reads a build or install tree. + +Measured by staging the bundle into its build tree for one run — the library +into `${CMAKE_CURRENT_BINARY_DIR}/bundle/lib/`, `plugInfo.json` and +`buildInfo.json` into `bundle/plugin/resources/usdMmdFileFormat/` — with the +source-tree copies moved aside: + +- `ost plugin build plugins/usdMmdFileFormat` succeeded and linked + `bundle\lib\libUsdMmdFileFormat.dll`, but still reported + `lib: C:\dev\usd-mmd-plugins\plugins\usdMmdFileFormat\lib` and + `provenance: 2 managed output(s)`, where the source-tree staging records 5. +- `ost plugin test plugins/usdMmdFileFormat` then failed: + + ```text + [FAIL] L0 bundle.plug_info plugInfo.json not found at 'plugin/resources/usdMmdFileFormat/plugInfo.json' + [FAIL] L0 plugin.shared_library no shared library (.so/.dll/.dylib) in lib/ + [FAIL] L2 plugin.discovery USD does not recognize '.pmx' (discovery failed) + [FAIL] L3 usdcat.read … Cannot determine file format … + [FAIL] L4 python.stage_open Usd.Stage.Open() failed … + [FAIL] L5 golden.roundtrip 2 of 2 roundtrip fixtures failed … + Result: FAILED (5 pass, 6 fail, 4 skip) + ``` + +Restoring the source-tree staging returned `Result: OK (12 pass, 0 fail, 4 +skip)`. The `usd-vrm-plugins` and `usd-motion-plugins` bundles stage the same +way, for the same reason. + +## 4. What source-tree staging costs + +Every configured tree of the checkout writes one `plugins/usdMmdFileFormat/lib/`, +one `plugInfo.json` and one `buildInfo.json`: + +- A plain-CMake build and `ost build` overwrite each other's library. + [building.md](../../guides/building.md#openstrata) already records the two + consequences met so far: `ost plugin package` refuses with + `PLUGIN_PACKAGE_OUTPUT_MISMATCH` after a root build rewrote `buildInfo.json`, + and a stale object in one tree once linked into the library every tree's + tests load. +- After the plain-CMake build of §1, the next `ost build` warned + `did not attribute 6 unchanged workspace output(s) without matching prior + root-build evidence: plugins/usdMmdFileFormat/plugin/resources/usdMmdFileFormat/buildInfo.json, …`: + files in the bundle's source directory that the other tree had last + written. (The list also names `buildInfo.json.in`, which is committed + source, not an output.) +- Two configurations, or two build trees, cannot hold two libraries: the + second build replaces the first. + +The generated files are ignored by git, so a checkout stays clean; the cost is +that the source tree is a build output shared by every tree. + +## 5. Asks + +- **P2: let a bundle, and a tool, be verified and packaged from a staged + layout outside the source tree.** The bundle's install rules already produce + the layout — `lib/` beside `plugin/resources//`, which this + repository's installed-consumer lane opens a stage through — so one shape + that needs no new CMake variable is: `ost plugin build` installs the bundle + into a per-target stage (`.strata/targets//bundle-stage/`) and + `plugin test`, `plugin run` and `plugin package` resolve `usd.plug_info` + and `lib/` there; `ost` tool packaging likewise reads a tool's + `directories` from its install. A manifest field naming a build-tree + directory would also do, but would put a build-layout fact in the manifest. + Until then this repository keeps staging in the source tree + ([WORKSPACE.md §5](../../architecture/WORKSPACE.md#5-build-modes)). diff --git a/docs/reports/ost/README.md b/docs/reports/ost/README.md new file mode 100644 index 0000000..d8df9ee --- /dev/null +++ b/docs/reports/ost/README.md @@ -0,0 +1,22 @@ +# OST dogfooding reports + +This repository is built with [OpenStrata](https://github.com/animu-sphere/open-strata) +(`ost`), and these are the dated records of what that was like, measured in +this repository's own tree. They are upstream feedback first and our own +status trail second, and follow the series `usd-vrm-plugins` keeps +([its reports](https://github.com/animu-sphere/usd-vrm-plugins/tree/main/docs/reports/ost)): +the ecosystem's running ask list is there, and a report here records what only +this repository measured. + +**They are append-only historical evidence.** A report is never rewritten to +match what later turned out to be true. When a newer `ost` resolves an item, a +new report re-verifies it and the superseded report gets a one-line +forward-note at the top. + +## Reading order + +The newest report carries the current asks. + +| Report | `ost` | Subject | +| --- | --- | --- | +| [01](01-2026-09-24-v0.23.3-a-bundle-is-staged-in-its-source-tree.md) | 0.23.3 | The CMake dependency contract needs nothing from `ost`; a bundle's library and registration, and a tool's directories, can only be staged in the source tree |