diff --git a/.github/actions/build-swiftusd/action.yml b/.github/actions/build-swiftusd/action.yml
deleted file mode 100644
index 5151ff97fb..0000000000
--- a/.github/actions/build-swiftusd/action.yml
+++ /dev/null
@@ -1,86 +0,0 @@
-#===----------------------------------------------------------------------===#
-# This source file is part of github.com/apple/SwiftUsd
-#
-# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# SPDX-License-Identifier: Apache-2.0
-#===----------------------------------------------------------------------===#
-
-name: Build SwiftUsd
-
-inputs:
- openusd-ref:
- description: 'OpenUSD ref'
- required: true
- type: string
-
-runs:
- using: "composite"
- steps:
-
- - name: Install CMake
- uses: ./.github/actions/install-cmake
-
- - name: Clone OpenUSD
- uses: actions/checkout@v5
- with:
- repository: PixarAnimationStudios/OpenUSD
- ref: ${{ inputs.openusd-ref }}
- path: ./openusd-source
-
- - name: Apply patch to OpenUSD
- run: |
- cd ./openusd-source
- patch -p1 -i ../openusd-patch.patch
- shell: bash
-
- - name: Build OpenUSD for macOS
- run: |
- cd ./openusd-source
- python3 build_scripts/build_usd.py \
- --embree \
- --imageio \
- --alembic \
- --openvdb \
- --no-python \
- --ignore-homebrew \
- --build-target native \
- ../openusd-builds/macOS
- shell: bash
-
- - name: Build OpenUSD for iOS
- run: |
- cd ./openusd-source
- python3 build_scripts/build_usd.py \
- --imageio \
- --alembic \
- --no-python \
- --ignore-homebrew \
- --build-target iOS \
- ../openusd-builds/iOS
- shell: bash
-
- - name: Remove Package.swift and swift-package
- run: |
- rm ./Package.swift
- rm -rf ./swift-package
- shell: bash
-
- - name: Run make-swift-package
- run: |
- swift run --package-path ./scripts/make-swift-package make-swift-package \
- ./openusd-builds/iOS \
- ./openusd-builds/macOS
- shell: bash
diff --git a/.github/actions/install-cmake/action.yml b/.github/actions/install-cmake/action.yml
deleted file mode 100644
index 4d4fa9280e..0000000000
--- a/.github/actions/install-cmake/action.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-#===----------------------------------------------------------------------===#
-# This source file is part of github.com/apple/SwiftUsd
-#
-# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# SPDX-License-Identifier: Apache-2.0
-#===----------------------------------------------------------------------===#
-
-# Installs CMake 3.26.5 for macOS
-
-name: Install CMake
-
-runs:
- using: "composite"
- steps:
-
- - name: Download CMake
- run: |
- curl https://github.com/Kitware/CMake/releases/download/v3.26.5/cmake-3.26.5-macos-universal.dmg \
- --output CMake.dmg
- working-directory: ${{ runner.temp }}
- shell: bash
-
-
- - name: Verify SHA256 checksum
- run: |
- # Hard-coded checksum taken from https://github.com/Kitware/CMake/releases/download/v3.26.5/cmake-3.26.5-SHA-256.txt,
- # cmake-3.26.5-macos-universal.dmg
- echo '18fb759bbdd04309be6a8e12920a8183ebbb17b331a6b7fc6a4071a68e5c81fd *CMake.dmg' | shasum -a 256 -c
- working-directory: ${{ runner.temp }}
- shell: bash
-
-
- - name: Attach DMG
- run: hdiutil attach CMake.dmg
- working-directory: ${{ runner.temp }}
- shell: bash
-
- - name: Copy app to permanent location
- run: |
- cp /Volumes/CMake/CMake.app ../CMake.app
- hdiutil detach /Volumes/CMake
- working-directory: ${{ github.workspace }
- shell: bash
-
- - name: Add CMake to PATH
- run: |
- # Note: `export PATH=` doesn't work for GitHub actions, the environment variable
- # must be set by redirecting into GITHUB_ENV
- echo "PATH=$PATH:$GITHUB_WORKSPACE/../CMake.app/Contents/bin" >> $GITHUB_ENV
- shell: bash
-
- - name: Test CMake is in PATH
- run: which cmake
- shell: bash
\ No newline at end of file
diff --git a/.github/scripts/build-openusd.py b/.github/scripts/build-openusd.py
new file mode 100644
index 0000000000..bcdb7fd574
--- /dev/null
+++ b/.github/scripts/build-openusd.py
@@ -0,0 +1,70 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+import argparse
+import json
+import os
+import re
+import subprocess
+from swiftusd_ci_common import *
+
+def install_cmake():
+ print("Downloading CMake...")
+ run(["curl", "-L", "https://github.com/Kitware/CMake/releases/download/v3.28.6/cmake-3.28.6-macos-universal.dmg",
+ "--output", "CMake.dmg"],
+ cwd=Environment.Path.tmp_dir, logOutput=False)
+
+ print("Verifying SHA256 checksum...")
+ # Hard-coded checksum taken from https://github.com/Kitware/CMake/releases/download/v3.28.6/cmake-3.28.6-SHA-256.txt,
+ # cmake-3.28.6-macos-universal.dmg
+ run(["shasum", "-a", "256", "-c"],
+ input="d676ca7eb85be6d39c9c7595d858c3508b4076ed2ddb229a630c8ad0f22281dd *CMake.dmg",
+ cwd=Environment.Path.tmp_dir)
+
+ print("Attaching DMG...")
+ run(["hdiutil", "attach", "CMake.dmg"], cwd=Environment.Path.tmp_dir, logOutput=False)
+
+ print("Copying CMake.app to permanent location...")
+ copy_src = "/Volumes/cmake-3.28.6-macos-universal/CMake.app/."
+ copy_dest = Environment.Path.github_workspace / "../CMake.app"
+ run(["cp", "-R", copy_src, copy_dest],
+ cwd=Environment.Path.github_workspace, logOutput=False)
+
+ print("Detaching DMG...")
+ run(["hdiutil", "detach", "/Volumes/cmake-3.28.6-macos-universal"])
+
+ env = os.environ.copy()
+ env["PATH"] = str(copy_dest / "Contents" / "bin") + ":" + env["PATH"]
+
+ print("Testing CMake is in PATH...")
+ run(["which", "cmake"], env=env)
+
+ return env
+
+if __name__ == "__main__":
+ env = install_cmake()
+ clone_openusd(checkout=Environment.GitRef.openusd)
+ print("Patching OpenUSD...")
+ run(["git", "restore", "."], cwd=Environment.Path.openusd, logOutput=False)
+ run(["git", "clean", "-fd"], cwd=Environment.Path.openusd, logOutput=False)
+ run(["patch", "-p1", "-i", Environment.Path.swiftusd / "openusd-patch.patch"], cwd=Environment.Path.openusd, logOutput=False)
+ print("Building OpenUSD...")
+ run(["python3", "-u", "build_scripts/build_usd.py"] + get_openusd_build_flags(Environment.TestCombination.target_platform), env=env, cwd=Environment.Path.openusd)
+
diff --git a/.github/scripts/compute-openusd-build-cache-key.py b/.github/scripts/compute-openusd-build-cache-key.py
new file mode 100644
index 0000000000..9cd4c29af1
--- /dev/null
+++ b/.github/scripts/compute-openusd-build-cache-key.py
@@ -0,0 +1,46 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from swiftusd_ci_common import *
+import re
+
+if __name__ == "__main__":
+ openusd_patch_hash = run(["shasum", "-a", "256", "./openusd-patch.patch"], cwd=Environment.Path.swiftusd, logOutput=False).output[0]
+ # extract the hash, dropping the file name
+ openusd_patch_hash = re.search(r"([0-9a-f]+)", openusd_patch_hash).groups(1)[0]
+ compiler_version = run(["swift", "--version"], logOutput=False).output[0]
+ # extract `(swiftlang VERSION clang-VERSION)`, dropping the parentheses
+ compiler_version = re.search(r"\((swiftlang.*)\)", compiler_version).groups(1)[0]
+ host_version = run(["sw_vers", "--productVersion"], logOutput=False).output[0] + "(" + run(["sw_vers", "--buildVersion"], logOutput=False).output[0] + ")"
+ # Turn branch names like `dev` into a hash commit to avoid incorrect cache key matches
+ clone_openusd()
+ rev_parsed_ref = run(["git", "rev-parse", Environment.GitRef.openusd], cwd=Environment.Path.openusd, logOutput=False).output[0]
+ build_flags = "".join(get_openusd_build_flags(Environment.TestCombination.target_platform))
+
+ result = " ".join([
+ Environment.TestCombination.target_platform,
+ compiler_version,
+ Environment.TestCombination.target_platform + host_version,
+ build_flags,
+ rev_parsed_ref,
+ openusd_patch_hash
+ ])
+
+ printAndWrite(output=f"cache-key='{result}'")
\ No newline at end of file
diff --git a/.github/scripts/compute-test-artifact-name.py b/.github/scripts/compute-test-artifact-name.py
new file mode 100644
index 0000000000..86cce5de2d
--- /dev/null
+++ b/.github/scripts/compute-test-artifact-name.py
@@ -0,0 +1,41 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from swiftusd_ci_common import *
+
+
+if __name__ == "__main__":
+ # Artifact names cannot contain any of the following characters:
+ # `":<>|*?\r\n\/`
+ result = " ".join([
+ "SwiftUsd-Tests",
+ Environment.TestCombination.target_platform,
+ Environment.TestCombination.build_system,
+ Environment.TestCombination.config,
+ Environment.GitRef.swiftusd,
+ Environment.GitRef.openusd,
+ Environment.GitRef.swiftusd_tests,
+ Environment.TestCombination.github_run_id,
+ ])
+
+ for c in ":<>|*?\r\n/\\":
+ result = result.replace(c, "")
+
+ printAndWrite(output=f"artifact_name={result}")
\ No newline at end of file
diff --git a/.github/scripts/define-build-matrix.py b/.github/scripts/define-build-matrix.py
new file mode 100644
index 0000000000..4bcb324a6f
--- /dev/null
+++ b/.github/scripts/define-build-matrix.py
@@ -0,0 +1,46 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from swiftusd_ci_common import *
+import argparse
+import json
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--targets", required=True)
+ args = parser.parse_args()
+
+ all_targets = ["macOS", "iOS", "iOSSimulator", "visionOS", "visionOSSimulator"]
+
+ if args.targets == "ALL":
+ result = all_targets
+ else:
+ result = []
+ for x in args.targets.split(","):
+ x = x.strip()
+ if x in all_targets:
+ if x not in result:
+ result.append(x)
+ else:
+ raise ValueError(f"Unknown target '{x}'")
+
+ result = {"target" : result}
+
+ printAndWrite(output=f"matrix={json.dumps(result)}")
\ No newline at end of file
diff --git a/.github/scripts/define-test-matrix.py b/.github/scripts/define-test-matrix.py
new file mode 100644
index 0000000000..7c4aa3701c
--- /dev/null
+++ b/.github/scripts/define-test-matrix.py
@@ -0,0 +1,55 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from swiftusd_ci_common import *
+import random
+import json
+import math
+
+if __name__ == "__main__":
+ target_platforms = ["macOS", "iOSSimulator", "visionOSSimulator"]
+ configs = ["Debug", "Release"]
+ build_systems = ["xcodebuild-xcodeproj", "swiftbuild-SPM-Tests", "xcodebuild-SPM-Tests"]
+
+ all_combinations = []
+ for target_platform in target_platforms:
+ for config in configs:
+ for build_system in build_systems:
+
+ # if target_platform == "macOS" and build_system == "xcodebuild-xcodeproj":
+ # # Disabled for now
+ # continue
+
+ if build_system == "swiftbuild-SPM-Tests" and target_platform != "macOS":
+ # swiftbuild only supports macOS
+ continue
+
+ all_combinations.append({"target_platform" : target_platform, "config" : config, "build_system" : build_system})
+
+ random.shuffle(all_combinations)
+
+ write(output=f"matrix={json.dumps(all_combinations)}")
+ max_parallel = int(round(math.sqrt(len(all_combinations))))
+ write(output=f"max-parallel={max_parallel}")
+
+ printAndWrite(summary=collapsedSection(
+ title=f"Will run {len(all_combinations)} test combinations with max-parallel={max_parallel}:",
+ body=f"```\nmatrix={json.dumps(all_combinations, indent=2)}\n```"
+ ))
diff --git a/.github/scripts/run-tests-helper.py b/.github/scripts/run-tests-helper.py
new file mode 100644
index 0000000000..ad800bebe2
--- /dev/null
+++ b/.github/scripts/run-tests-helper.py
@@ -0,0 +1,201 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+import argparse
+import json
+import os
+import subprocess
+import re
+import pathlib
+import shutil
+import random
+import time
+from swiftusd_ci_common import *
+
+def build_or_run_test_suite(name, cleanCmd, buildCmd, testCmd, buildTestCommonArgs, cwd, action, env=None):
+ """Builds or runs the given test suite"""
+
+ test_matrix_result = TestMatrixResult.load()
+ test_matrix_result.action = action
+ test_matrix_result.name = name
+ test_matrix_result.returncode = -1
+ test_matrix_result.setExtractedLinesToTimeout(action)
+ test_matrix_result.write()
+
+ start = time.time()
+
+ def should_extract(l):
+ if action == "build":
+ return any([s in l for s in ["error:"]])
+ elif action == "test":
+ return any([s in l for s in ["failed:", "launchd"]])
+
+ def processRunResult(runResult):
+ end = time.time()
+ actionAsVerb = "Building" if action == "build" else "Testing"
+
+ test_matrix_result.returncode = runResult.returncode
+ if action == "build":
+ test_matrix_result.build_time = end - start
+ else:
+ test_matrix_result.test_time = end - start
+
+ test_matrix_result.extracted_lines = []
+
+ extractedLines = [l for l in runResult.output if should_extract(l)]
+ test_matrix_result.extracted_lines = extractedLines
+
+ test_matrix_result.write()
+
+ exit(runResult.returncode)
+
+ if action == "build":
+ print(f"Building {name} tests...")
+ run(cleanCmd, cwd=cwd)
+ processRunResult(run(buildCmd + buildTestCommonArgs, cwd=cwd, check=False, env=env))
+
+
+ elif action == "test":
+ print(f"Running {name} tests...")
+ processRunResult(run(testCmd + buildTestCommonArgs, cwd=cwd, check=False, env=env))
+
+def prepare_to_build_tests():
+ print("Preparing to build tests...")
+ run(["swift", "run", "--package-path", "ReconfigurePbxprojPackageDependency",
+ "ReconfigurePbxprojPackageDependency", "SwiftUsdTests.xcodeproj/project.pbxproj",
+ "--replace", "https://github.com/apple/SwiftUsd", "--with", "SwiftUsd"],
+ cwd=Environment.Path.swiftusd_tests)
+ if not (Environment.Path.swiftusd_tests / "SwiftUsd").exists():
+ (Environment.Path.swiftusd_tests / "SwiftUsd").symlink_to(Environment.Path.swiftusd)
+ run(["python3", "make-spm-tests.py", "--local", "--force"],
+ cwd=Environment.Path.swiftusd_tests)
+
+def get_xcodebuild_destination():
+ if Environment.TestCombination.target_platform == "macOS": return "platform=macOS,name=My Mac"
+ elif Environment.TestCombination.target_platform == "iOSSimulator": return "platform=iOS Simulator,name=iPhone 17 Pro"
+ elif Environment.TestCombination.target_platform == "visionOSSimulator": return "platform=visionOS Simulator,name=Apple Vision Pro (at 2732x2048)"
+ else:
+ print(f"Error: Unknown target '{Environment.TestCombination.target_platform}'")
+ exit(1)
+
+def do_xcodebuild_xcodeproj_tests(action):
+ build_or_run_test_suite(
+ name="xcodebuild-xcodeproj",
+ cleanCmd=["xcodebuild", "clean"],
+ buildCmd=["xcodebuild", "build-for-testing"],
+ testCmd=[
+ "xcodebuild", "test-without-building",
+ "-resultBundlePath", str(Environment.Path.result_bundle),
+ ],
+ buildTestCommonArgs=[
+ "-verbose", "-skipMacroValidation",
+ "-scheme", "UnitTests",
+ "-configuration", Environment.TestCombination.config,
+ "-destination", get_xcodebuild_destination(),
+ "OTHER_SWIFT_FLAGS=$(inherited) -DSWIFTUSD_TESTS_SUPPRESS_PERFORMANCE_FAILURES",
+ ],
+ cwd=Environment.Path.swiftusd_tests,
+ action=action,
+ )
+
+def do_swiftbuild_spm_tests(action):
+ env = os.environ.copy()
+ env["SWIFT_BACKTRACE"] = "interactive=no"
+
+ # Set DEVELOPER_DIR to work around
+ # error: cannot load module 'SwiftCompilerPlugin' built with SDK 'macosx26.0' when using SDK 'macosx26.2'
+ # https://github.com/apple/SwiftUsd/actions/runs/21256906902/job/61175337955
+ env["DEVELOPER_DIR"] = "/Applications/Xcode-latest.app"
+
+ build_or_run_test_suite(
+ name="swiftbuild-SPM-Tests",
+ cleanCmd=["swift", "package", "clean"],
+ buildCmd=["swift", "build", "--build-tests"],
+ testCmd=["swift", "test", "--skip-build"],
+ buildTestCommonArgs=[
+ "-v",
+ "-Xswiftc", "-DOPENUSD_SWIFT_BUILD_FROM_CLI", "-Xcxx", "-DOPENUSD_SWIFT_BUILD_FROM_CLI",
+ "--configuration", Environment.TestCombination.config.lower(),
+ "-Xswiftc", "-DSWIFTUSD_TESTS_SUPPRESS_PERFORMANCE_FAILURES",
+ ],
+ cwd=Environment.Path.swiftusd_tests / "SPM-Tests",
+ action=action,
+ env=env
+ )
+
+def do_xcodebuild_spm_tests(action):
+ build_or_run_test_suite(
+ name="xcodebuild-SPM-Tests",
+ cleanCmd=["swift", "package", "clean"],
+ buildCmd=["xcodebuild", "build-for-testing"],
+ testCmd=[
+ "xcodebuild", "test-without-building",
+ "-resultBundlePath", str(Environment.Path.result_bundle)
+ ],
+ buildTestCommonArgs=[
+ "-verbose", "-skipMacroValidation",
+ "-scheme", "SPM-Tests-Package",
+ "-config", Environment.TestCombination.config,
+ "-destination", get_xcodebuild_destination(),
+ "OTHER_SWIFT_FLAGS=$(inherited) -DSWIFTUSD_TESTS_SUPPRESS_PERFORMANCE_FAILURES",
+ ],
+ cwd=Environment.Path.swiftusd_tests / "SPM-Tests",
+ action=action,
+ )
+
+def build_tests(args, subparsers):
+ if subparsers is not None:
+ parser = subparsers.add_parser("build-tests")
+ parser.set_defaults(func=lambda args: build_tests(args=args, subparsers=None))
+ return
+
+ prepare_to_build_tests()
+ if Environment.TestCombination.build_system == "xcodebuild-xcodeproj": do_xcodebuild_xcodeproj_tests(action="build")
+ elif Environment.TestCombination.build_system == "swiftbuild-SPM-Tests": do_swiftbuild_spm_tests(action="build")
+ elif Environment.TestCombination.build_system == "xcodebuild-SPM-Tests": do_xcodebuild_spm_tests(action="build")
+ else:
+ print(f"Error: Unknown build system {Environment.TestCombination.build_system}")
+ exit(1)
+
+def run_tests(args, subparsers):
+ if subparsers is not None:
+ parser = subparsers.add_parser("run-tests")
+ parser.set_defaults(func=lambda args: run_tests(args=args, subparsers=None))
+ return
+
+ if Environment.TestCombination.build_system == "xcodebuild-xcodeproj": do_xcodebuild_xcodeproj_tests(action="test")
+ elif Environment.TestCombination.build_system == "swiftbuild-SPM-Tests": do_swiftbuild_spm_tests(action="test")
+ elif Environment.TestCombination.build_system == "xcodebuild-SPM-Tests": do_xcodebuild_spm_tests(action="test")
+ else:
+ print(f"Error: Unknown build system {Environment.TestCombination.build_system}")
+ exit(1)
+
+# MARK: Main
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(required=True)
+
+ build_tests(args=None, subparsers=subparsers)
+ run_tests(args=None, subparsers=subparsers)
+
+ args = parser.parse_args()
+
+ args.func(args)
\ No newline at end of file
diff --git a/.github/scripts/summarize-test-matrix-results.py b/.github/scripts/summarize-test-matrix-results.py
new file mode 100644
index 0000000000..8aabeb8d4e
--- /dev/null
+++ b/.github/scripts/summarize-test-matrix-results.py
@@ -0,0 +1,143 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from swiftusd_ci_common import *
+import argparse
+import json
+
+def explain_errors(axes, results, action):
+ if any([x for x in results if x.returncode != 0 and x.action == action]):
+ hypothesis = find_hypothesis(axes, results, action)
+ printAndWrite(summary=hypothesis)
+
+ else:
+ printAndWrite(summary=f"✅ No {action} errors occurred")
+
+def explain_times(axes, results, action):
+ timesAndInstances = []
+ for x in results:
+ t = x.build_time if action == "build" else x.test_time
+ if t <= 0: continue
+ timesAndInstances.append((t, x))
+
+ if not timesAndInstances:
+ printAndWrite(summary=f"No {action} times to summarize")
+ return
+
+ timesAndInstances.sort(key=lambda x: x[0])
+ nTimes = len(timesAndInstances)
+ minTime = timesAndInstances[0]
+ maxTime = timesAndInstances[-1]
+ medianTime = timesAndInstances[int(len(timesAndInstances) / 2)]
+ meanTime = sum([x[0] for x in timesAndInstances]) / len(timesAndInstances)
+
+ title = f"{action[0].upper() + action[1:]} times ({nTimes} instances, median {medianTime[0]:.1f}s, {minTime[0]:.1f}s - {maxTime[0]:.1f}s)"
+ body = [
+ "```",
+ f"action: {action}",
+ f"instances: {nTimes}",
+ f"average: {meanTime}",
+ f"median: {medianTime[0]}, {medianTime[1].summary(axes)}",
+ f"min: {minTime[0]}, {minTime[1].summary(axes)}",
+ f"max: {maxTime[0]}, {maxTime[1].summary(axes)}",
+ ]
+ for x in timesAndInstances:
+ body.append(f"{x[1].summary(axes)}: {x[0]}")
+ body.append("```")
+ body = "\n".join(body)
+
+ printAndWrite(summary=collapsedSection(title=title, body=body))
+
+def makeFakeData():
+ """Make fake test matrix result data, for testing summarization"""
+ print("Making fake data...")
+ result = []
+ for target_platform in ["macOS", "iOSSimulator", "visionOSSimulator"]:
+ for config in ["Debug", "Release"]:
+ for build_system in ["xcodebuild-xcodeproj", "swiftbuild-SPM-Tests", "xcodebuild-SPM-Tests"]:
+ isFailure = False
+ action = "test"
+
+ if target_platform == "macOS" and config == "Release":
+ isFailure = True
+
+ if build_system == "swiftbuild-SPM-Tests":
+ isFailure = True
+
+ if target_platform == "macOS" and build_system == "xcodebuild-xcodeproj":
+ continue
+
+ if build_system == "swiftbuild-SPM-Tests" and target_platform != "macOS":
+ continue
+
+ if target_platform == "iOSSimulator" and config == "Debug" and build_system == "xcodebuild-xcodeproj":
+ isFailure = True
+ action = "build"
+
+ x = TestMatrixResult()
+ x.action = action
+ x.returncode = 1 if isFailure else 0
+ x.target_platform = target_platform
+ x.config = config
+ x.build_system = build_system
+ x.extracted_lines = [
+ "err 3",
+ "err 4"
+ ]
+ x.build_time = 301.2
+ x.test_time = 24.5
+
+ result.append(x)
+ return result
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--makeFakeData", action="store_true")
+ args = parser.parse_args()
+
+ results = TestMatrixResult.load_all()
+
+ if args.makeFakeData:
+ results = makeFakeData()
+
+ axes = Axes.fromTestMatrixResults(results)
+ explain_errors(axes, results, "build")
+ printAndWrite(summary="")
+ explain_errors(axes, results, "test")
+ printAndWrite(summary="")
+
+ title = "All extracted lines"
+ body = formExtractedLinesFromResults(axes, results)
+ printAndWrite(summary=collapsedSection(title=title, body=body))
+ printAndWrite(summary="")
+
+ explain_times(axes, results, "build")
+ printAndWrite(summary="")
+ explain_times(axes, results, "test")
+
+ # Important: If any test instances fail, we want
+ # the overall test workflow to be marked as failed.
+ # But since we let test instances continue after failure,
+ # GitHub won't mark it as failed unless this job fails
+ if any([x for x in results if x.returncode != 0]):
+ exit(1)
+ else:
+ exit(0)
\ No newline at end of file
diff --git a/.github/scripts/swiftusd_ci_common/__init__.py b/.github/scripts/swiftusd_ci_common/__init__.py
new file mode 100644
index 0000000000..ab12989980
--- /dev/null
+++ b/.github/scripts/swiftusd_ci_common/__init__.py
@@ -0,0 +1,26 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from .environment import *
+from .markdown import *
+from .openusd_building import *
+from .subprocesses import *
+from .test_matrix_results import *
+from .write import *
diff --git a/.github/scripts/swiftusd_ci_common/environment.py b/.github/scripts/swiftusd_ci_common/environment.py
new file mode 100644
index 0000000000..cb67933938
--- /dev/null
+++ b/.github/scripts/swiftusd_ci_common/environment.py
@@ -0,0 +1,51 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+import os
+import pathlib
+
+def _getenvpath(k):
+ result = os.getenv(k)
+ if result is not None: result = pathlib.Path(result)
+ return result
+
+class Environment:
+ class GitRef:
+ swiftusd = os.getenv("SWIFTUSD_REF")
+ openusd = os.getenv("OPENUSD_REF")
+ swiftusd_tests = os.getenv("SWIFTUSD_TESTS_REF")
+
+ class TestCombination:
+ target_platform = os.getenv("TARGET_PLATFORM")
+ config = os.getenv("CONFIG")
+ build_system = os.getenv("BUILD_SYSTEM")
+ github_run_id = os.getenv("GITHUB_RUN_ID")
+
+ class Path:
+ swiftusd = _getenvpath("SWIFTUSD_PATH")
+ swiftusd_tests = _getenvpath("SWIFTUSD_TESTS_PATH")
+ openusd = _getenvpath("OPENUSD_PATH")
+ result_bundle = _getenvpath("RESULT_BUNDLE_PATH")
+ github_output = _getenvpath("GITHUB_OUTPUT")
+ github_step_summary = _getenvpath("GITHUB_STEP_SUMMARY")
+ tmp_dir = _getenvpath("RUNNER_TEMP")
+ github_workspace = _getenvpath("GITHUB_WORKSPACE")
+ matrix_result = _getenvpath("MATRIX_RESULT_PATH")
+ matrix_results = _getenvpath("MATRIX_RESULTS_PATH")
\ No newline at end of file
diff --git a/.github/actions/build-symbol-graphs/action.yml b/.github/scripts/swiftusd_ci_common/markdown.py
similarity index 70%
rename from .github/actions/build-symbol-graphs/action.yml
rename to .github/scripts/swiftusd_ci_common/markdown.py
index 186669153f..59e7f20d17 100644
--- a/.github/actions/build-symbol-graphs/action.yml
+++ b/.github/scripts/swiftusd_ci_common/markdown.py
@@ -18,18 +18,11 @@
# SPDX-License-Identifier: Apache-2.0
#===----------------------------------------------------------------------===#
-name: Build symbol graphs
+def collapsedSection(title, body, openByDefault=False):
+ openString = " open" if openByDefault else ""
+ return f"""
+{title}
-runs:
- using: "composite"
- steps:
+{body}
- - name: Build symbol graphs
- run: swift run --package-path ./scripts/docc build-documentation
- shell: bash
-
- - name: Check documentation for warnings
- run: |
- xcrun docc convert --additional-symbol-graph-dir ./.symbol-graphs ./SwiftUsd.docc \
- --warnings-as-errors
- shell: bash
\ No newline at end of file
+ """
\ No newline at end of file
diff --git a/.github/scripts/swiftusd_ci_common/openusd_building.py b/.github/scripts/swiftusd_ci_common/openusd_building.py
new file mode 100644
index 0000000000..71e8a5ddb5
--- /dev/null
+++ b/.github/scripts/swiftusd_ci_common/openusd_building.py
@@ -0,0 +1,55 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from .environment import *
+from .subprocesses import *
+
+def openusd_build_dir(target): return str(Environment.Path.github_workspace / "openusd-builds" / target)
+
+def clone_openusd(checkout=None):
+ if not Environment.Path.openusd.exists():
+ print("Cloning OpenUSD...")
+ run(["git", "clone", "https://github.com/PixarAnimationStudios/OpenUSD.git", Environment.Path.openusd], logOutput=False)
+ if checkout is not None:
+ print(f"Checking out {checkout}")
+ run(["git", "checkout", checkout], cwd=Environment.Path.openusd, logOutput=False)
+
+def get_openusd_build_flags(target):
+ if target == "macOS":
+ return ["--embree", "--imageio", "--alembic", "--openvdb", "--no-python",
+ "--ignore-homebrew", "--build-target", "native", openusd_build_dir("macOS")]
+
+ if target == "iOS":
+ return ["--imageio", "--alembic", "--no-python", "--ignore-homebrew",
+ "--build-target", "iOS", openusd_build_dir("iOS")]
+
+ if target == "iOSSimulator":
+ return ["--imageio", "--alembic", "--no-python", "--ignore-homebrew",
+ "--build-target", "iOSSimulator", openusd_build_dir("iOSSimulator")]
+
+ if target == "visionOS":
+ return ["--imageio", "--alembic", "--no-python", "--ignore-homebrew",
+ "--build-target", "visionOS", openusd_build_dir("visionOS")]
+
+ if target == "visionOSSimulator":
+ return ["--imageio", "--alembic", "--no-python", "--ignore-homebrew",
+ "--build-target", "visionOSSimulator", openusd_build_dir("visionOSSimulator")]
+
+ raise ValueError(f"Unknown target {target}")
\ No newline at end of file
diff --git a/.github/scripts/swiftusd_ci_common/subprocesses.py b/.github/scripts/swiftusd_ci_common/subprocesses.py
new file mode 100644
index 0000000000..0fccbfcabf
--- /dev/null
+++ b/.github/scripts/swiftusd_ci_common/subprocesses.py
@@ -0,0 +1,70 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+import subprocess
+
+class RunResult:
+ def __init__(self, returncode, output):
+ self.returncode = returncode
+ self.output = output
+
+def run(args, cwd=None, env=None, input=None, logCmd=True, logOutput=True, check=True):
+ """Runs the given command in a subprocess"""
+ cmd_as_string = " ".join([str(x) for x in args])
+ if input is not None:
+ cmd_as_string = f"{cmd_as_string} <<< \"{input}\""
+
+ if logCmd:
+ print(cmd_as_string)
+ p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, cwd=cwd, env=env)
+
+ result = RunResult(None, [])
+
+ if input is not None:
+ (stdout_data, stderr_data) = p.communicate(input=input.encode("utf-8"))
+ if stderr_data is not None:
+ result.output.append(stderr_data.decode("utf-8"))
+ if logOutput and result.output[-1]: print(result.output[-1], end="", flush=True)
+ if stdout_data is not None:
+ result.output.append(stdout_data.decode("utf-8"))
+ if logOutput and result.output[-1]: print(result.output[-1], end="", flush=True)
+
+ else:
+ while True:
+ l = p.stdout.readline().decode("utf-8")
+ result.output.append(l)
+ if l:
+ if logOutput:
+ print(l, end="", flush=True)
+ elif p.poll() is not None:
+ break
+
+ result.returncode = p.returncode
+ result.output = [x for l in result.output for x in l.splitlines() ]
+
+ if check and result.returncode != 0:
+ print(f"'{cmd_as_string}' exited with returncode {p.returncode}:")
+ if not logOutput:
+ print("\n".join(result.output))
+
+ exit(result.returncode)
+
+ return result
\ No newline at end of file
diff --git a/.github/scripts/swiftusd_ci_common/test_matrix_results.py b/.github/scripts/swiftusd_ci_common/test_matrix_results.py
new file mode 100644
index 0000000000..a163319b1f
--- /dev/null
+++ b/.github/scripts/swiftusd_ci_common/test_matrix_results.py
@@ -0,0 +1,320 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from .environment import *
+from .markdown import *
+from .subprocesses import *
+import json
+import os
+import random
+import pprint
+import time
+import copy
+import re
+
+# MARK: TestMatrixResult
+
+class TestMatrixResult:
+ def __init__(self):
+ raw_swift_version = "\n".join(run(["swift", "--version"]).output)
+ short_swift_version = re.search(r"version (.*) \(", raw_swift_version).group(1)
+ raw_sw_vers = "\n".join(run(["sw_vers"]).output)
+ short_sw_vers = run(["sw_vers", "--productVersion"]).output[0] + " (" + run(["sw_vers", "--buildVersion"]).output[0] + ")"
+ raw_xcodebuild_version = "\n".join(run(["xcodebuild", "-version"]).output)
+ short_xcodebuild_version = re.search(r"Xcode (.*)", raw_xcodebuild_version).group(1) + " (" + re.search(r"Build version (.*)", raw_xcodebuild_version).group(1) + ")"
+ if Environment.GitRef.swiftusd and Environment.Path.swiftusd:
+ swiftusd_ref = run(["git", "rev-parse", Environment.GitRef.swiftusd], cwd=Environment.Path.swiftusd).output[0]
+ else:
+ swiftusd_ref = "null"
+
+ if Environment.GitRef.swiftusd_tests and Environment.Path.swiftusd_tests:
+ swiftusd_tests_ref = run(["git", "rev-parse", Environment.GitRef.swiftusd_tests], cwd=Environment.Path.swiftusd_tests).output[0]
+ else:
+ swiftusd_tests_ref = "null"
+
+ self.data = {
+ "action": "null",
+ "matrix_instance" : {
+ "target_platform": Environment.TestCombination.target_platform,
+ "config": Environment.TestCombination.config,
+ "build_system": Environment.TestCombination.build_system,
+ },
+ "output" : {
+ "returncode": -2,
+ "build_time": -1.0,
+ "test_time": -1.0,
+ "extracted_lines": [],
+ },
+ "extra_info" : {
+ "raw_swift_version": raw_swift_version,
+ "short_swift_version": short_swift_version,
+ "raw_sw_vers": raw_sw_vers,
+ "short_sw_vers": short_sw_vers,
+ "raw_xcodebuild_version": raw_xcodebuild_version,
+ "short_xcodebuild_version": short_xcodebuild_version,
+ "rev_parse_swiftusd_ref": swiftusd_ref,
+ "rev_parse_swiftusd_tests_ref": swiftusd_tests_ref,
+ },
+ }
+
+ def matrixInstanceSummary(self):
+ return " ".join([v for v in self.data["matrix_instance"].values()])
+
+ def setExtractedLinesToTimeout(self, action):
+ self.extracted_lines = [f"{action} timeout: {self.matrixInstanceSummary()}"]
+
+ def niceAxisName(self, k):
+ if k == "target_platform": return "target platform"
+ if k == "config": return "config"
+ if k == "build_system": return "build system"
+ if k == "raw_swift_version": return "swift version"
+ if k == "short_swift_version": return "swift version"
+ if k == "raw_sw_vers": return "host OS"
+ if k == "short_sw_vers": return "host OS"
+ if k == "raw_xcodebuild_version": return "Xcode version"
+ if k == "short_xcodebuild_version": return "Xcode version"
+ if k == "rev_parse_swiftusd_ref": return "SwiftUsd ref"
+ if k == "rev_parse_swiftusd_tests_ref": return "SwiftUsd-Tests ref"
+
+ return k
+
+ @property
+ def action(self): return self.data["action"]
+ @action.setter
+ def action(self, value): self.data["action"] = value
+
+ @property
+ def returncode(self): return self.data["output"]["returncode"]
+ @returncode.setter
+ def returncode(self, value): self.data["output"]["returncode"] = value
+
+ @property
+ def extracted_lines(self): return self.data["output"]["extracted_lines"]
+ @extracted_lines.setter
+ def extracted_lines(self, value): self.data["output"]["extracted_lines"] = value
+
+ @property
+ def build_time(self): return self.data["output"]["build_time"]
+ @build_time.setter
+ def build_time(self, value): self.data["output"]["build_time"] = value
+
+ @property
+ def test_time(self): return self.data["output"]["test_time"]
+ @test_time.setter
+ def test_time(self, value): self.data["output"]["test_time"] = value
+
+ @property
+ def target_platform(self): return self.data["matrix_instance"]["target_platform"]
+ @target_platform.setter
+ def target_platform(self, value): self.data["matrix_instance"]["target_platform"] = value
+
+ @property
+ def config(self): return self.data["matrix_instance"]["config"]
+ @config.setter
+ def config(self, value): self.data["matrix_instance"]["config"] = value
+
+ @property
+ def build_system(self): return self.data["matrix_instance"]["build_system"]
+ @build_system.setter
+ def build_system(self, value): self.data["matrix_instance"]["build_system"] = value
+
+ @property
+ def isFailure(self): return self.returncode != 0
+
+ def write(self):
+ with open(Environment.Path.matrix_result, "w") as f:
+ json.dump(self.data, f)
+
+ @staticmethod
+ def load(p=None):
+ if p is None: p = Environment.Path.matrix_result
+ if not p.exists():
+ return TestMatrixResult()
+
+ with open(p, "r") as f:
+ x = TestMatrixResult()
+ x.data = json.load(f)
+ return x
+
+ @staticmethod
+ def load_all():
+ result = []
+ for (dirpath, dirnames, filenames) in os.walk(Environment.Path.matrix_results):
+ for f in filenames:
+ if f.endswith(".json"):
+ result.append(TestMatrixResult.load(pathlib.Path(dirpath) / f))
+ return result
+
+ @property
+ def axisValues(self):
+ result = []
+
+ for k, v in self.data["matrix_instance"].items():
+ result.append(AxisValue(k, v))
+
+ for k, v in self.data["extra_info"].items():
+ result.append(AxisValue(k, v))
+
+ return result
+
+ def summary(self, axes):
+ x = ", ".join([f"{self.niceAxisName(k)} = {self.valueForAxis(k)}" for k in axes.get_all_axes_names()])
+ return x[0].upper() + x[1:]
+
+ def valueForAxis(self, axis):
+ for a in self.axisValues:
+ if a.axis == axis: return a.value
+
+# MARK: Hypothesis discovery
+
+class Axes:
+ def __init__(self, data):
+ self._data = copy.deepcopy(data)
+
+ def get_all_axes_names(self):
+ return list(self._data.keys())
+
+ def get_all_values_for_axis(self, k):
+ return self._data[k]
+
+ def get_number_of_axes(self):
+ return len(self._data)
+
+ def get_cartesian_product(self):
+ result = [[]]
+ for axis in self.get_all_axes_names():
+ result = [x + [AxisValue(axis, value)] for value in self.get_all_values_for_axis(axis) for x in result]
+ return result
+
+ def get_all_axes_values(self):
+ result = []
+ for k in self.get_all_axes_names():
+ for v in self.get_all_values_for_axis(k):
+ result.append(AxisValue(k, v))
+ return result
+
+ @staticmethod
+ def fromTestMatrixResults(testMatrixResults):
+ d = {}
+ for testMatrixResult in testMatrixResults:
+ for axisValue in testMatrixResult.axisValues:
+ if axisValue.axis not in d:
+ d[axisValue.axis] = []
+ if axisValue.value not in d[axisValue.axis]:
+ d[axisValue.axis].append(axisValue.value)
+
+ # If there's at most one value for a field, it isn't an axis
+ # of variability, but including it as an axis could
+ # lead to combinatorial explosion when trying to find
+ # hypotheses
+ d = {k : v for k, v in d.items() if len(v) >= 2}
+
+ return Axes(d)
+
+ def __str__(self):
+ return str(self._data)
+
+ def __repr__(self): return str(self)
+
+
+class AxisValue:
+ def __init__(self, axis, value):
+ self.axis = axis
+ self.value = value
+
+ def __str__(self):
+ return f"{self.axis} = {self.value}"
+
+ def __repr__(self): return str(self)
+
+ def __eq__(self, other):
+ return self.axis == other.axis and self.value == other.value
+
+class Hyperplane:
+ def __init__(self, axes_values):
+ self.axes_values = axes_values
+
+ def appendingAxisValue(self, axisValue):
+ return Hyperplane(self.axes_values + [axisValue])
+
+ def getTestCaseIntersection(self, test_cases):
+ result = []
+ for tc in test_cases:
+ if all([tc.valueForAxis(av.axis) == av.value for av in self.axes_values]):
+ result.append(tc)
+
+ return result
+
+ def __str__(self):
+ if self.axes_values:
+ return ", ".join([str(x) for x in self.axes_values])
+ else:
+ return "All instances"
+
+ def __repr__(self): return str(self)
+
+def formExtractedLinesFromResults(axes, results):
+ ans = []
+ for x in results:
+ if len(x.extracted_lines) > 0:
+ ans += [x.summary(axes) + ":", "```"] + x.extracted_lines + ["```"]
+ else:
+ ans += [x.summary(axes) + ": (none) "]
+
+ return "\n".join(ans)
+
+def find_hypothesis(axes, test_cases, action):
+ print(f"Finding {action} hypothesis with axes:")
+ pprint.pprint(axes)
+ print("and test cases:")
+ print([x.summary(axes) for x in test_cases])
+ print("")
+
+ hyperplanes = [Hyperplane([])]
+
+ capitalizedAction = action[0].upper() + action[1:]
+ result = [
+ f"❌ {capitalizedAction} failures occurred in these combinations:",
+ ]
+
+ test_cases = copy.deepcopy(test_cases)
+
+ while hyperplanes:
+ h = hyperplanes.pop(0)
+ intersection = h.getTestCaseIntersection(test_cases)
+ if all([not x.isFailure for x in intersection]):
+ continue
+ if all([x.isFailure and x.action == action for x in intersection]):
+ title = f"{h} ({len(intersection)} instances)"
+ body = formExtractedLinesFromResults(axes, intersection)
+
+ result.append(collapsedSection(title=title, body=body))
+
+ for i, tc in reversed(list(enumerate(test_cases))):
+ if tc in intersection:
+ del test_cases[i]
+ continue
+
+ for av in axes.get_all_axes_values():
+ if av not in h.axes_values:
+ hyperplanes.append(h.appendingAxisValue(av))
+
+ return "\n".join(result)
+
diff --git a/.github/scripts/swiftusd_ci_common/write.py b/.github/scripts/swiftusd_ci_common/write.py
new file mode 100644
index 0000000000..9a40855146
--- /dev/null
+++ b/.github/scripts/swiftusd_ci_common/write.py
@@ -0,0 +1,57 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+from .environment import *
+
+def write(output=None, summary=None):
+ """Writes to $GITHUB_OUTPUT or $GITHUB_STEP_SUMMARY"""
+ if output is not None:
+ with open(Environment.Path.github_output, "a") as f:
+ f.write(output + "\n")
+
+ if summary is not None:
+ with open(Environment.Path.github_step_summary, "a") as f:
+ f.write(summary + "\n")
+
+def printAndWrite(output=None, summary=None, matrixResult=None):
+ """Prints and writes to $GITHUB_OUTPUT or $GITHUB_STEP_SUMMARY"""
+ if output is not None: print(output)
+ if summary is not None: print(summary)
+ write(output=output, summary=summary)
+
+def annotate(notice=None, warning=None, error=None, title=None):
+ """Emits a notice, warning, or error annotation"""
+ if notice is not None:
+ cmd = "notice"
+ msg = notice
+ elif warning is not None:
+ cmd = "warning"
+ msg = warning
+ elif error is not None:
+ cmd = "error"
+ msg = error
+
+ # URL-encode newlines to try to get GH to support multi-line annotations
+ msg = msg.replace("\n", "%0A")
+
+ if title is not None:
+ print(f"::{cmd} title={title}::{msg}")
+ else:
+ print(f"::{cmd}::{msg}")
\ No newline at end of file
diff --git a/.github/workflows/build-swiftusd.yml b/.github/workflows/build-swiftusd.yml
new file mode 100644
index 0000000000..f46041aaad
--- /dev/null
+++ b/.github/workflows/build-swiftusd.yml
@@ -0,0 +1,177 @@
+#===----------------------------------------------------------------------===#
+# This source file is part of github.com/apple/SwiftUsd
+#
+# Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#===----------------------------------------------------------------------===#
+
+name: Build SwiftUsd
+run-name: Building SwiftUsd ${{ inputs.swiftusd-ref }} on OpenUSD ${{ inputs.openusd-ref }}
+permissions:
+ contents: read
+
+on:
+ workflow_call:
+ inputs:
+ swiftusd-ref:
+ description: 'SwiftUsd ref'
+ required: true
+ type: string
+
+ openusd-ref:
+ description: 'OpenUSD ref'
+ required: true
+ type: string
+ default: 'v25.08'
+
+ build-targets:
+ description: 'Build target list, or ALL'
+ required: true
+ type: string
+ default: 'ALL'
+
+ allow-caching-openusd-build:
+ description: 'Whether to restore to/save from the cache. Default: true'
+ required: false
+ type: boolean
+ default: true
+
+jobs:
+ Define-Build-Matrix:
+ name: Define build matrix
+ runs-on: [self-hosted, macos]
+
+ outputs:
+ matrix: ${{ steps.matrix.outputs.matrix }}
+
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ inputs.swiftusd-ref }}
+ sparse-checkout: .github/scripts
+ sparse-checkout-cone-mode: false
+
+ - name: Define matrix
+ id: matrix
+ run: |
+ python3 ./.github/scripts/define-build-matrix.py \
+ --targets $BUILD_TARGETS
+ env:
+ BUILD_TARGETS: ${{ inputs.build-targets }}
+
+ Build-OpenUSD:
+ runs-on: [self-hosted, macos]
+ needs: [Define-Build-Matrix]
+ strategy:
+ matrix: ${{ fromJson(needs.Define-Build-Matrix.outputs.matrix) }}
+ name: Build OpenUSD (${{ matrix.target }})
+
+ env:
+ OPENUSD_REF: ${{ inputs.openusd-ref }}
+ TARGET_PLATFORM: ${{ matrix.target }}
+ OPENUSD_PATH: ${{ github.workspace }}/openusd-source
+ SWIFTUSD_PATH: ${{ github.workspace }}/SwiftUsd
+
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ inputs.swiftusd-ref }}
+ path: SwiftUsd
+ sparse-checkout: |
+ .github/scripts/
+ openusd-patch.patch
+ sparse-checkout-cone-mode: false
+
+ - name: Compute cache key
+ id: compute-cache-key
+ run: |
+ python3 SwiftUsd/.github/scripts/compute-openusd-build-cache-key.py
+
+ - name: Cache OpenUSD build
+ if: ${{ inputs.allow-caching-openusd-build }}
+ id: cache-openusd-build
+ uses: actions/cache@v4
+ with:
+ path: openusd-builds/${{ matrix.target }}
+ key: ${{ steps.compute-cache-key.outputs.cache-key }}
+
+ - name: Build OpenUSD on cache miss
+ if: ${{ steps.cache-openusd-build.outputs.cache-hit != 'true' }}
+ run: |
+ python3 SwiftUsd/.github/scripts/build-openusd.py
+
+ - name: Upload OpenUSD build artifact
+ # Upload the build artifact even if building fails so it can be
+ # inspected/debugged
+ if: ${{ always() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: openusd-builds-${{ github.run_id }}-${{ matrix.target }}
+ path: openusd-builds/${{ matrix.target }}
+ if-no-files-found: 'error'
+
+ Make-Swift-Package:
+ runs-on: [self-hosted, macos]
+ needs: [Build-OpenUSD]
+ permissions:
+ contents: write
+ name: Make Swift Package
+
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ inputs.swiftusd-ref }}
+
+ - name: Download OpenUSD build artifacts
+ uses: actions/download-artifact@v5
+ with:
+ path: ../openusd-builds
+ pattern: openusd-builds-${{ github.run_id }}-*
+
+ - name: Run make-swift-package
+ run: |
+ swift run --package-path ./scripts/make-swift-package \
+ make-swift-package ../openusd-builds/* --force
+ echo "Trimming down artiface size more..."
+ rm -rf ./docs
+ rm -rf ./SwiftUsd.doccarchive
+ swift package --package-path ./scripts/make-swift-package clean
+
+ - name: Tar package artifact before uploading
+ # `/SwiftUsd.docc/SymbolExtensions/Bool.init(_:TfWeakPtrProtocol).md` is an invalid
+ # file path for GH artifacts because it contains a colon, so tar things
+ # first to avoid that issue
+ run: |
+ tar -czf ../SwiftUsd.tar.gz .
+
+ - name: Move .tar.gz to workaround https://github.com/actions/upload-artifact/issues/176
+ # The documentation says that relative paths are supported, but in practice
+ # paths starting with '.' or '..' cause errors. So, create the .tar.gz not in the SwiftUsd directory
+ # (because creating an archive of a directory inside that directory is a bad idea),
+ # then move the archive into the SwiftUsd directory after archiving finishes to make upload-artifact happy
+ run: |
+ mv ../SwiftUsd.tar.gz ./SwiftUsd.tar.gz
+
+ - name: Upload package artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: SwiftUsd-package-${{ github.run_id }}.tar.gz
+ path: SwiftUsd.tar.gz
+ if-no-files-found: 'error'
+
diff --git a/.github/workflows/check-pull-request-doesnt-update-automated-files.yml b/.github/workflows/check-pull-request-doesnt-update-automated-files.yml
index 1028bda54c..4c86cd7d64 100644
--- a/.github/workflows/check-pull-request-doesnt-update-automated-files.yml
+++ b/.github/workflows/check-pull-request-doesnt-update-automated-files.yml
@@ -18,9 +18,7 @@
# SPDX-License-Identifier: Apache-2.0
#===----------------------------------------------------------------------===#
-name: Check that pull requests don't update automated files
-run-name: Checking that pull request doesn't update automated files
-
+name: Check that pull requests don't update generated files
permissions:
contents: read
@@ -34,6 +32,8 @@ on:
jobs:
Fail:
- runs-on: self-hosted
+ runs-on: ubuntu-latest
steps:
- - run: exit 1
\ No newline at end of file
+ - run: exit 1
+
+
\ No newline at end of file
diff --git a/.github/workflows/check-pull-requests-for-documentation-warnings.yml b/.github/workflows/check-pull-requests-for-documentation-warnings.yml
index 6e256e3736..a998852e6e 100644
--- a/.github/workflows/check-pull-requests-for-documentation-warnings.yml
+++ b/.github/workflows/check-pull-requests-for-documentation-warnings.yml
@@ -19,16 +19,29 @@
#===----------------------------------------------------------------------===#
name: Check Pull Requests for Documentation Warnings
-run-name: Checking Pull Request for Documentation Warnings
permissions:
contents: read
+
on: pull_request
jobs:
- Check:
- runs-on: self-hosted
- steps:
- - name: Check out repository code
- uses: actions/checkout@v5
+ Build-SwiftUsd:
+ uses: ./.github/workflows/build-swiftusd.yml
+ with:
+ swiftusd-ref: ${{ github.ref }}
- - name: Check for documentation warnings
- uses: ./.github/actions/build-symbol-graphs
\ No newline at end of file
+ Check-Documentation-Warnings:
+ runs-on: [self-hosted, macos]
+ needs: [Build-SwiftUsd]
+ steps:
+ - name: Download package artifact
+ uses: actions/download-artifact@v5
+ with:
+ path: .
+ name: SwiftUsd-package-${{ github.run_id }}
+
+ - name: Check for documentation warnings
+ run: |
+ swift run --package-path ./scripts/docc build-documentation
+ swift run --package-path ./scripts/docc check-documentation
+
+
\ No newline at end of file
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 9431be6b46..a2e14e6bd5 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -19,54 +19,153 @@
#===----------------------------------------------------------------------===#
name: Run tests
-run-name: Running tests
+run-name: Testing ${{ inputs.swiftusd-tests-ref }} on SwiftUsd ${{ inputs.swiftusd-ref }}, OpenUSD ${{ inputs.openusd-ref }}
permissions:
contents: read
+
on:
- workflow_dispatch:
- inputs:
- swiftusd-ref:
- description: 'SwiftUsd ref'
- required: true
- type: string
- openusd-ref:
- description: 'OpenUSD ref'
- required: true
- type: string
- swiftusd-tests-ref:
- description: 'SwiftUsd-Tests ref'
- required: true
- type: string
-
+ workflow_dispatch:
+ inputs:
+ swiftusd-ref:
+ description: 'SwiftUsd ref'
+ required: true
+ type: string
+ openusd-ref:
+ description: 'OpenUSD ref'
+ required: true
+ type: string
+ swiftusd-tests-ref:
+ description: 'SwiftUsd-Tests ref'
+ required: true
+ type: string
+
jobs:
- Run-Tests:
- name: Run tests
- strategy:
- matrix:
- configuration: [Debug, Release]
-
- runs-on: self-hosted
- steps:
- - name: Check out repository code
- uses: actions/checkout@v5
- with:
- ref: ${{ inputs.swiftusd-ref }}
-
- - name: Build SwiftUsd
- uses: ./.github/actions/build-swiftusd
- with:
- swiftusd-ref: ${{ inputs.swiftusd-ref }}
- openusd-ref: ${{ inputs.openusd-ref }}
-
- - name: Check out unit tests
- uses: actions/checkout@v5
- with:
- repository: apple/SwiftUsd-Tests
- ref: ${{ inputs.swiftusd-tests-ref }}
- path: ./swiftusd-tests
-
- - name: Run unit tests
- run: |
- cd ./swiftusd-tests
- ln -s .. SwiftUsd
- xcodebuild test -scheme SwiftUsdTests -skipMacroValidation -configuration ${{ matrix.configuration }}
\ No newline at end of file
+ Build-SwiftUsd:
+ name: Build SwiftUsd
+ permissions:
+ contents: write
+ uses: ./.github/workflows/build-swiftusd.yml
+ with:
+ swiftusd-ref: ${{ inputs.swiftusd-ref }}
+ openusd-ref: ${{ inputs.openusd-ref }}
+ build-targets: ALL
+
+ Define-Test-Matrix:
+ name: Define Test Matrix
+ runs-on: [self-hosted, macos]
+ outputs:
+ matrix: ${{ steps.matrix.outputs.matrix }}
+ max-parallel: ${{ steps.matrix.outputs.max-parallel }}
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ inputs.swiftusd-ref }}
+ sparse-checkout: .github/scripts/
+ sparse-checkout-cone-mode: false
+
+ - name: Define matrix
+ id: matrix
+ run: |
+ python3 ./.github/scripts/define-test-matrix.py
+
+ Run-Tests-Once:
+ runs-on: [self-hosted, macos]
+ needs: [Build-SwiftUsd, Define-Test-Matrix]
+ continue-on-error: true
+ name: Run tests (${{ matrix.target_platform }}, ${{ matrix.config }}, ${{ matrix.build_system }})
+ strategy:
+ fail-fast: false
+ max-parallel: ${{ fromJson(needs.Define-Test-Matrix.outputs.max-parallel) }}
+ matrix:
+ include: ${{ fromJson(needs.Define-Test-Matrix.outputs.matrix) }}
+
+ env:
+ TARGET_PLATFORM: ${{ matrix.target_platform }}
+ BUILD_SYSTEM: ${{ matrix.build_system }}
+ CONFIG: ${{ matrix.config }}
+ SWIFTUSD_REF: ${{ inputs.swiftusd-ref }}
+ OPENUSD_REF: ${{ inputs.openusd-ref }}
+ SWIFTUSD_TESTS_REF: ${{ inputs.swiftusd-tests-ref }}
+ GITHUB_RUN_ID: ${{ github.run_id }}
+ SWIFTUSD_TESTS_PATH: ${{ github.workspace }}/SwiftUsd-Tests
+ SWIFTUSD_PATH: ${{ github.workspace }}/SwiftUsd
+ RESULT_BUNDLE_PATH: ${{ github.workspace }}/SwiftUsd-Tests.xcresult
+ MATRIX_RESULT_PATH: ${{ github.workspace }}/matrix-result.json
+
+ steps:
+ - name: Download package artifact
+ uses: actions/download-artifact@v5
+ with:
+ path: SwiftUsd.tar.gz
+ name: SwiftUsd-package-${{ github.run_id }}.tar.gz
+
+ - name: Untar package artifact
+ run: |
+ mkdir ./SwiftUsd
+ tar -xzf ./SwiftUsd.tar.gz/SwiftUsd.tar.gz -C ./SwiftUsd
+
+ - name: Check out unit tests
+ uses: actions/checkout@v5
+ with:
+ repository: apple/SwiftUsd-Tests
+ ref: ${{ inputs.swiftusd-tests-ref }}
+ path: ./SwiftUsd-Tests
+
+ - name: Compute artifact name
+ id: compute-artifact-name
+ run: |
+ python3 ./SwiftUsd/.github/scripts/compute-test-artifact-name.py
+
+ - name: Build tests
+ id: build-tests
+ timeout-minutes: 15
+ run: |
+ python3 ./SwiftUsd/.github/scripts/run-tests-helper.py build-tests
+
+ - name: Run tests
+ id: run-tests
+ timeout-minutes: 15
+ run: |
+ python3 ./SwiftUsd/.github/scripts/run-tests-helper.py run-tests
+
+ - name: Upload xcresult artifact
+ if: ${{ !cancelled() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.compute-artifact-name.outputs.artifact_name }}
+ path: ./SwiftUsd-Tests.xcresult
+ if-no-files-found: 'warn'
+
+ - name: Upload matrix result artifact
+ if: ${{ !cancelled() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: matrix-result-${{ github.run_id }}-${{ steps.compute-artifact-name.outputs.artifact_name }}
+ path: ./matrix-result.json
+ if-no-files-found: 'error'
+
+ Summarize-Test-Results:
+ if: ${{ !cancelled() }}
+ runs-on: [self-hosted, macos]
+ needs: [Run-Tests-Once]
+ name: Summarize Test Results
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v5
+ with:
+ ref: ${{ inputs.swiftusd-ref }}
+ sparse-checkout: .github/scripts
+ sparse-checkout-cone-mode: false
+
+ - name: Download matrix result artifacts
+ uses: actions/download-artifact@v5
+ with:
+ path: ../matrix-results
+ pattern: matrix-result-${{ github.run_id }}-*
+
+ - name: Summarize matrix results
+ run: |
+ python3 ./.github/scripts/summarize-test-matrix-results.py
+ env:
+ MATRIX_RESULTS_PATH: ${{ github.workspace }}/../matrix-results
diff --git a/SwiftUsd.docc/Essentials/GettingStarted.md b/SwiftUsd.docc/Essentials/GettingStarted.md
index 2986f52f95..d07aeab66d 100644
--- a/SwiftUsd.docc/Essentials/GettingStarted.md
+++ b/SwiftUsd.docc/Essentials/GettingStarted.md
@@ -78,3 +78,5 @@ print(makeHelloWorldString())
**Solution**: In the Build Settings for your target, make sure that `Runpath Search Paths` is set to `@executable_path/Frameworks`. (`LD_RUNPATH_SEARCH_PATHS=@executable_path/Frameworks` for xcconfig files.)
Note: This should only affect command line executable targets.
+- macOS apps with App Sandbox compiled in Release mode can't find Hydra Render Delegate plugins when launched from Xcode
+**Solution**: In the Run, Arguments, Environment Variables section of your Scheme, add `DYLD_FRAMEWORK_PATH=$CONFIGURATION_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH` as a key-value pair
\ No newline at end of file
diff --git a/openusd-patch.patch b/openusd-patch.patch
index 46a4ea12e1..e860b7c3d2 100644
--- a/openusd-patch.patch
+++ b/openusd-patch.patch
@@ -133,9 +133,32 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
+
+ return target_config_patches, clang_config_patches
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/build_scripts/build_usd.py ./build_scripts/build_usd.py
---- /Users/maddyadams/OpenUSD/build_scripts/build_usd.py 2025-11-17 14:21:56
-+++ ./build_scripts/build_usd.py 2025-11-17 14:56:52
-@@ -1035,30 +1035,21 @@
+--- /Users/maddyadams/OpenUSD/build_scripts/build_usd.py 2026-01-07 13:16:06
++++ ./build_scripts/build_usd.py 2026-01-07 15:00:08
+@@ -276,13 +276,20 @@
+ p.wait()
+
+ if p.returncode != 0:
++ with codecs.open("log.txt", "a", "utf-8") as logfile:
++ logfile.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
++ logfile.write("\n")
++ logfile.write("{cmd} exited with returncode {returncode}"
++ .format(cmd=cmd, returncode=p.returncode))
++ logfile.write("\n")
++
+ # If verbosity >= 3, we'll have already been printing out command output
+ # so no reason to print the log file again.
+ if verbosity < 3:
+ with open("log.txt", "r") as logfile:
+ Print(logfile.read())
+- raise RuntimeError("Failed to run '{cmd}' in {path}.\nSee {log} for more details."
+- .format(cmd=cmd, path=os.getcwd(), log=os.path.abspath("log.txt")))
++ raise RuntimeError("Failed to run '{cmd}' in {path} (exited with returncode {returncode}).\nSee {log} for more details."
++ .format(cmd=cmd, path=os.getcwd(), returncode=p.returncode, log=os.path.abspath("log.txt")))
+
+ @contextlib.contextmanager
+ def CurrentWorkingDirectory(dir):
+@@ -1035,30 +1042,21 @@
"ifeq ($(arch),$(filter $(arch),armv7 armv7s {0}))"
.format(apple_utils.GetTargetArmArch()))])
@@ -174,7 +197,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
(primaryArch, secondaryArch) = apple_utils.GetTargetArchPair(context)
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/base/arch/fileSystem.h ./pxr/base/arch/fileSystem.h
---- /Users/maddyadams/OpenUSD/pxr/base/arch/fileSystem.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/base/arch/fileSystem.h 2026-01-07 13:16:06
+++ ./pxr/base/arch/fileSystem.h 2025-11-17 14:57:18
@@ -38,8 +38,6 @@
#include
@@ -195,7 +218,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
#if defined(ARCH_OS_WINDOWS)
#define ARCH_GLOB_NOCHECK 1
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/base/tf/CMakeLists.txt ./pxr/base/tf/CMakeLists.txt
---- /Users/maddyadams/OpenUSD/pxr/base/tf/CMakeLists.txt 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/base/tf/CMakeLists.txt 2026-01-07 13:16:06
+++ ./pxr/base/tf/CMakeLists.txt 2025-11-17 14:57:34
@@ -237,6 +237,7 @@
pxrTslRobinMap/robin_map.h
@@ -225,7 +248,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
Tf_ProxyReferenceReverseIterator() = default;
explicit Tf_ProxyReferenceReverseIterator(UnderlyingIterator it) :
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/base/tf/notice.h ./pxr/base/tf/notice.h
---- /Users/maddyadams/OpenUSD/pxr/base/tf/notice.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/base/tf/notice.h 2026-01-07 13:16:06
+++ ./pxr/base/tf/notice.h 2025-11-17 14:58:09
@@ -263,6 +263,7 @@
@@ -457,7 +480,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
}
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/base/vt/dictionary.h ./pxr/base/vt/dictionary.h
---- /Users/maddyadams/OpenUSD/pxr/base/vt/dictionary.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/base/vt/dictionary.h 2026-01-07 13:16:06
+++ ./pxr/base/vt/dictionary.h 2025-11-17 14:59:10
@@ -439,8 +439,8 @@
{
@@ -471,7 +494,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
return i->second.Get();
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/exec/exec/CMakeLists.txt ./pxr/exec/exec/CMakeLists.txt
---- /Users/maddyadams/OpenUSD/pxr/exec/exec/CMakeLists.txt 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/exec/exec/CMakeLists.txt 2026-01-07 13:16:06
+++ ./pxr/exec/exec/CMakeLists.txt 2025-11-17 14:59:28
@@ -35,6 +35,7 @@
systemChangeProcessor
@@ -490,7 +513,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
RESOURCE_FILES
plugInfo.json
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/exec/vdf/parallelExecutorEngineBase.h ./pxr/exec/vdf/parallelExecutorEngineBase.h
---- /Users/maddyadams/OpenUSD/pxr/exec/vdf/parallelExecutorEngineBase.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/exec/vdf/parallelExecutorEngineBase.h 2026-01-07 13:16:06
+++ ./pxr/exec/vdf/parallelExecutorEngineBase.h 2025-11-17 14:59:55
@@ -1885,9 +1885,9 @@
if (!privateBuffer->GetExecutorCache()) {
@@ -519,7 +542,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
private:
friend class VdfPoolChainIndexer;
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/exec/vdf/pullBasedExecutorEngine.h ./pxr/exec/vdf/pullBasedExecutorEngine.h
---- /Users/maddyadams/OpenUSD/pxr/exec/vdf/pullBasedExecutorEngine.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/exec/vdf/pullBasedExecutorEngine.h 2026-01-07 13:16:06
+++ ./pxr/exec/vdf/pullBasedExecutorEngine.h 2025-11-17 15:00:15
@@ -1035,9 +1035,9 @@
// requested because otherwise, it wouldn't be in the schedule,
@@ -683,7 +706,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
// A map from pool chain index to prioritized output, used to ensure that we
// process outputs in their order in the pool chain.
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/imaging/hd/dependencySchema.h ./pxr/imaging/hd/dependencySchema.h
---- /Users/maddyadams/OpenUSD/pxr/imaging/hd/dependencySchema.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/imaging/hd/dependencySchema.h 2026-01-07 13:16:06
+++ ./pxr/imaging/hd/dependencySchema.h 2025-11-17 15:00:38
@@ -52,6 +52,9 @@
HdDependencySchema(HdContainerDataSourceHandle container)
@@ -703,7 +726,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
\ No newline at end of file
+#endif
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/imaging/hd/mergingSceneIndex.h ./pxr/imaging/hd/mergingSceneIndex.h
---- /Users/maddyadams/OpenUSD/pxr/imaging/hd/mergingSceneIndex.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/imaging/hd/mergingSceneIndex.h 2026-01-07 13:16:06
+++ ./pxr/imaging/hd/mergingSceneIndex.h 2025-11-17 15:00:40
@@ -153,6 +153,9 @@
, sceneRoot(sceneRoot)
@@ -716,7 +739,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
// We observe that most merging scene indexes have few inputs, such as 2.
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/imaging/hd/schema.h ./pxr/imaging/hd/schema.h
---- /Users/maddyadams/OpenUSD/pxr/imaging/hd/schema.h 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/imaging/hd/schema.h 2026-01-07 13:16:06
+++ ./pxr/imaging/hd/schema.h 2025-11-17 15:00:42
@@ -28,6 +28,8 @@
HdSchema(HdContainerDataSourceHandle container)
@@ -860,7 +883,7 @@ diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /
using _PrimMap = std::map;
diff -Nura --exclude .git --exclude *.orig --exclude .DS_Store --exclude *.pyc /Users/maddyadams/OpenUSD/pxr/imaging/hdx/taskControllerSceneIndex.cpp ./pxr/imaging/hdx/taskControllerSceneIndex.cpp
---- /Users/maddyadams/OpenUSD/pxr/imaging/hdx/taskControllerSceneIndex.cpp 2025-11-17 14:21:56
+--- /Users/maddyadams/OpenUSD/pxr/imaging/hdx/taskControllerSceneIndex.cpp 2026-01-07 13:16:06
+++ ./pxr/imaging/hdx/taskControllerSceneIndex.cpp 2025-11-17 15:01:09
@@ -1466,7 +1466,7 @@
if (_aovNames == aovNames) {
diff --git a/scripts/docc/Package.swift b/scripts/docc/Package.swift
index 68479bb811..0a14ab9226 100644
--- a/scripts/docc/Package.swift
+++ b/scripts/docc/Package.swift
@@ -42,6 +42,8 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser")
]
),
+ .executableTarget(name: "check-documentation",
+ dependencies: ["SwiftUsdDoccUtil"]),
.executableTarget(name: "preview-documentation",
dependencies: ["SwiftUsdDoccUtil"]),
.executableTarget(name: "update-documentation",
diff --git a/scripts/docc/Sources/check-documentation/CheckDocumentation.swift b/scripts/docc/Sources/check-documentation/CheckDocumentation.swift
new file mode 100644
index 0000000000..3f1568ed54
--- /dev/null
+++ b/scripts/docc/Sources/check-documentation/CheckDocumentation.swift
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+// This source file is part of github.com/apple/SwiftUsd
+//
+// Copyright © 2025 Apple Inc. and the SwiftUsd project authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// SPDX-License-Identifier: Apache-2.0
+//===----------------------------------------------------------------------===//
+
+import Foundation
+import SwiftUsdDoccUtil
+
+@main
+struct CheckDocumentation {
+ static func main() async throws {
+ try await ShellUtil.runCommandAndWait(arguments: [
+ "xcrun", "docc", "convert",
+ Driver.shared.doccCatalogURL,
+ "--additional-symbol-graph-dir", Driver.shared.symbolGraphsURL,
+ "--warnings-as-errors"
+ ])
+ }
+}
\ No newline at end of file
diff --git a/scripts/make-swift-package/Sources/SwiftPackage.swift b/scripts/make-swift-package/Sources/SwiftPackage.swift
index 3bd99a09c5..49557f2b3d 100644
--- a/scripts/make-swift-package/Sources/SwiftPackage.swift
+++ b/scripts/make-swift-package/Sources/SwiftPackage.swift
@@ -58,6 +58,7 @@ struct SwiftPackage {
try await swiftPackage.writeModulemap()
try await swiftPackage.writeCppDefinesFile()
try await swiftPackage.writeExtraArgsFile()
+ try await swiftPackage.removeTemporaryFiles()
print("")
print("Success! To use \(path: swiftPackage.fsInfo.swiftUsdPackage.generatedSwiftPackageDir) from the command line: ")
@@ -83,6 +84,10 @@ struct SwiftPackage {
try! fm.createDirectory(at: fsInfo.swiftUsdPackage.sources_OpenUSD_SwiftBindingHelpers, withIntermediateDirectories: true)
try! fm.createDirectory(at: fsInfo.swiftUsdPackage.sourcesInclude, withIntermediateDirectories: true)
}
+
+ private func removeTemporaryFiles() async throws {
+ try! fm.removeItem(at: fsInfo.swiftUsdPackage.tmpDir)
+ }
private mutating func makeXCFrameworks() async throws {
// Group framework variants for different platforms by their name