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