From 0532f4d5a1a9caf3b731a2b032d8910dec31cb3c Mon Sep 17 00:00:00 2001 From: Maddy Adams Date: Wed, 3 Dec 2025 10:57:11 -0800 Subject: [PATCH] Set up CI for SwiftUsd Add check-documentation target to scripts/docc Make make-swift-package clean up the tmpDir when execution succeeds Add workaround for macOS apps with App Sandbox compiled in Release mode not finding Hydra Render Delegate plugins when launched from Xcode to GettingStarted.md --- .github/actions/build-swiftusd/action.yml | 86 ----- .github/actions/install-cmake/action.yml | 67 ---- .github/scripts/build-openusd.py | 70 ++++ .../compute-openusd-build-cache-key.py | 46 +++ .github/scripts/compute-test-artifact-name.py | 41 +++ .github/scripts/define-build-matrix.py | 46 +++ .github/scripts/define-test-matrix.py | 55 +++ .github/scripts/run-tests-helper.py | 201 +++++++++++ .../scripts/summarize-test-matrix-results.py | 143 ++++++++ .../scripts/swiftusd_ci_common/__init__.py | 26 ++ .../scripts/swiftusd_ci_common/environment.py | 51 +++ .../swiftusd_ci_common/markdown.py} | 19 +- .../swiftusd_ci_common/openusd_building.py | 55 +++ .../swiftusd_ci_common/subprocesses.py | 70 ++++ .../swiftusd_ci_common/test_matrix_results.py | 320 ++++++++++++++++++ .github/scripts/swiftusd_ci_common/write.py | 57 ++++ .github/workflows/build-swiftusd.yml | 177 ++++++++++ ...-request-doesnt-update-automated-files.yml | 10 +- ...ll-requests-for-documentation-warnings.yml | 29 +- .github/workflows/run-tests.yml | 193 ++++++++--- SwiftUsd.docc/Essentials/GettingStarted.md | 2 + openusd-patch.patch | 51 ++- scripts/docc/Package.swift | 2 + .../CheckDocumentation.swift | 34 ++ .../Sources/SwiftPackage.swift | 5 + 25 files changed, 1616 insertions(+), 240 deletions(-) delete mode 100644 .github/actions/build-swiftusd/action.yml delete mode 100644 .github/actions/install-cmake/action.yml create mode 100644 .github/scripts/build-openusd.py create mode 100644 .github/scripts/compute-openusd-build-cache-key.py create mode 100644 .github/scripts/compute-test-artifact-name.py create mode 100644 .github/scripts/define-build-matrix.py create mode 100644 .github/scripts/define-test-matrix.py create mode 100644 .github/scripts/run-tests-helper.py create mode 100644 .github/scripts/summarize-test-matrix-results.py create mode 100644 .github/scripts/swiftusd_ci_common/__init__.py create mode 100644 .github/scripts/swiftusd_ci_common/environment.py rename .github/{actions/build-symbol-graphs/action.yml => scripts/swiftusd_ci_common/markdown.py} (70%) create mode 100644 .github/scripts/swiftusd_ci_common/openusd_building.py create mode 100644 .github/scripts/swiftusd_ci_common/subprocesses.py create mode 100644 .github/scripts/swiftusd_ci_common/test_matrix_results.py create mode 100644 .github/scripts/swiftusd_ci_common/write.py create mode 100644 .github/workflows/build-swiftusd.yml create mode 100644 scripts/docc/Sources/check-documentation/CheckDocumentation.swift 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