diff --git a/.github/workflows/compute.yml b/.github/workflows/compute.yml index 9024a523..2d2fbe17 100644 --- a/.github/workflows/compute.yml +++ b/.github/workflows/compute.yml @@ -29,6 +29,54 @@ jobs: - name: Build in debug mode run: swift build -c debug -Xcc -Wno-elaborated-enum-base + compute_tuist_macos_build: + name: Build generated project with Compute (source) on macOS + permissions: + contents: read + id-token: write + strategy: + fail-fast: false + matrix: + os: [macos-15] + xcode-version: ["26.6"] + runs-on: + - ${{ matrix.os }} + - xcode-26.6 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Setup Xcode + uses: OpenSwiftUIProject/setup-xcode@v2 + with: + xcode-version: ${{ matrix.xcode-version }} + - name: Set up mise + uses: jdx/mise-action@v2 + with: + install: false + cache: false + - name: Set up Tuist cache + run: | + source Scripts/Tuist/common.sh + tuist_ci_setup + shell: bash + - name: Generate workspace with Compute + run: Scripts/setup.sh --compute + shell: bash + - name: Build OpenAttributeGraphShims with Compute + run: | + source Scripts/Tuist/common.sh + tuist_xcodebuild \ + "${RUNNER_TEMP:-/tmp}/openattributegraph-compute-tuist-macos-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-0}.xcresult" \ + build \ + -workspace OpenAttributeGraph.xcworkspace \ + -scheme OpenAttributeGraphShims \ + -configuration Debug \ + -destination "platform=macOS" \ + -derivedDataPath .build-tuist-compute \ + CODE_SIGNING_ALLOWED=NO + shell: bash + compute_macos_build: name: Build with Compute (binary) on macOS strategy: diff --git a/Configs/Common.xcconfig b/Configs/Common.xcconfig index 04e2fde1..b14b1f8c 100644 --- a/Configs/Common.xcconfig +++ b/Configs/Common.xcconfig @@ -13,6 +13,7 @@ SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx xros xrsimulator ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES TARGETED_DEVICE_FAMILY = 1,2,7 SDKROOT = auto +ONLY_ACTIVE_ARCH[config=Debug] = YES // MARK: - C/C++ Language diff --git a/Example/Project.swift b/Example/Project.swift index ce75b319..3c530b7a 100644 --- a/Example/Project.swift +++ b/Example/Project.swift @@ -1,5 +1,10 @@ import ProjectDescription +let openAttributeGraphModuleSearchSettings: SettingsDictionary = [ + "LD_RUNPATH_SEARCH_PATHS": "$(inherited) @loader_path", + "SWIFT_INCLUDE_PATHS": "$(inherited) $(SRCROOT)/../Sources/OpenAttributeGraphCxx/include", +] + let project = Project( name: "Example", targets: [ @@ -12,8 +17,9 @@ let project = Project( sources: ["Sources/**"], dependencies: [ .sdk(name: "c++", type: .library), - .external(name: "OpenAttributeGraph"), - ] + .project(target: "OpenAttributeGraph", path: ".."), + ], + settings: .settings(base: openAttributeGraphModuleSearchSettings) ), ] ) diff --git a/Example/Tuist.swift b/Example/Tuist.swift deleted file mode 100644 index 82f67208..00000000 --- a/Example/Tuist.swift +++ /dev/null @@ -1,18 +0,0 @@ -import ProjectDescription - -let tuist = Tuist( - fullHandle: "OpenSwiftUIProject/openattributegraph", - xcodeCache: .xcodeCache( - upload: Environment.isCI - ), - project: .tuist( - generationOptions: .options( - optionalAuthentication: true, - enableCaching: Environment.isCI, - manifestEnvironment: [ - "OPENSWIFTUI_*", - "OPENATTRIBUTEGRAPH_*", - ] - ) - ) -) diff --git a/Example/Tuist/Package.resolved b/Example/Tuist/Package.resolved deleted file mode 100644 index 45416df9..00000000 --- a/Example/Tuist/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "0e11c80665facc8857be461b32388524b07fef00c5f983d5bf4ac0cf97629dc3", - "pins" : [ - { - "identity" : "swift-numerics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-numerics", - "state" : { - "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", - "version" : "1.1.1" - } - } - ], - "version" : 3 -} diff --git a/Example/Tuist/Package.swift b/Example/Tuist/Package.swift deleted file mode 100644 index 694e5912..00000000 --- a/Example/Tuist/Package.swift +++ /dev/null @@ -1,17 +0,0 @@ -// swift-tools-version: 6.3 - -import PackageDescription - -#if TUIST -import ProjectDescription - -let packageSettings = PackageSettings() -#endif - -let package = Package( - name: "ExampleDependencies", - dependencies: [ - .package(path: "../../"), - .package(url: "https://github.com/apple/swift-numerics", from: "1.1.1"), - ] -) diff --git a/Example/mise.toml b/Example/mise.toml deleted file mode 100644 index fca2305c..00000000 --- a/Example/mise.toml +++ /dev/null @@ -1,2 +0,0 @@ -[tools] -tuist = "4.203.4" diff --git a/Example/setup.sh b/Example/setup.sh deleted file mode 100755 index 56e8dd6e..00000000 --- a/Example/setup.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" - -cd "$SCRIPT_DIR" -mise trust "$SCRIPT_DIR/mise.toml" -mise install -mise exec -- tuist install -mise exec -- tuist generate --no-open diff --git a/Project.swift b/Project.swift index b7065df8..57f59461 100644 --- a/Project.swift +++ b/Project.swift @@ -3,37 +3,110 @@ import ProjectDescription // MARK: - Constants let destinations: Destinations = [.iPhone, .iPad, .mac, .appleVision] +let useComputeBackend = Environment.openattributegraphCompute.getBoolean(default: false) +let platformTargetName = "OpenAttributeGraphPlatform" +let utilitiesTargetName = "OpenAttributeGraphUtilities" +let commonConfigurations: [Configuration] = [ + .debug(name: "Debug", xcconfig: "Configs/Common.xcconfig"), + .release(name: "Release", xcconfig: "Configs/Common.xcconfig"), +] +let nativeHeaderSearchSettings: SettingsDictionary = [ + "HEADER_SEARCH_PATHS": "$(inherited) $(SRCROOT)/Sources/Platform/include $(SRCROOT)/Sources/Utilities/include $(SRCROOT)/Sources/OpenAttributeGraphCxx/include", + "USER_HEADER_SEARCH_PATHS": "$(inherited) $(SRCROOT)/Sources/OpenAttributeGraphCxx", +] +let cxxInteropSettings: SettingsDictionary = [ + "GCC_PREPROCESSOR_DEFINITIONS": "$(inherited) SWIFT_TESTING=1", + "OTHER_SWIFT_FLAGS": "$(inherited) -cxx-interoperability-mode=default -Xcc -std=c++20", + "SWIFT_INCLUDE_PATHS": "$(inherited) $(SRCROOT)/Sources/Platform/include $(SRCROOT)/Sources/Utilities/include $(SRCROOT)/Sources/OpenAttributeGraphCxx/include", +] +let openAttributeGraphShimsDependencies: [TargetDependency] = useComputeBackend + ? [ + .external(name: "Compute"), + .sdk(name: "c++", type: .library), + .sdk(name: "Demangle", type: .swiftLibrary), + ] + : [ + .target(name: "OpenAttributeGraph"), + ] +let openAttributeGraphShimsSettings: SettingsDictionary = useComputeBackend + ? [ + "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "$(inherited) OPENATTRIBUTEGRAPH_COMPUTE", + ] + : [:] + +func commonSettings(base: SettingsDictionary = [:]) -> Settings { + .settings( + base: base, + configurations: commonConfigurations, + defaultSettings: .none + ) +} // MARK: - Project let project = Project( name: "OpenAttributeGraph", settings: .settings( - configurations: [ - .debug(name: "Debug", xcconfig: "Configs/Common.xcconfig"), - .release(name: "Release", xcconfig: "Configs/Common.xcconfig"), - ], + configurations: commonConfigurations, defaultSettings: .none ), targets: [ + .target( + name: platformTargetName, + destinations: destinations, + product: .staticLibrary, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.Platform", + sources: ["Sources/Platform/**/*.c"], + headers: .headers(public: ["Sources/Platform/include/platform/**"]), + settings: commonSettings(base: nativeHeaderSearchSettings) + ), + .target( + name: utilitiesTargetName, + destinations: destinations, + product: .staticLibrary, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.Utilities", + sources: ["Sources/Utilities/**/*.cpp"], + headers: .headers(public: ["Sources/Utilities/include/Utilities/**"]), + dependencies: [ + .target(name: platformTargetName), + ], + settings: commonSettings( + base: nativeHeaderSearchSettings.merging([ + "PRODUCT_MODULE_NAME": "Utilities", + ]) + ) + ), + .target( + name: "OpenAttributeGraphCxx", + destinations: destinations, + product: .staticLibrary, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.Cxx", + sources: [ + "Sources/OpenAttributeGraphCxx/**/*.c", + "Sources/OpenAttributeGraphCxx/**/*.cpp", + "Sources/OpenAttributeGraphCxx/**/*.mm", + ], + headers: .headers( + public: [ + "Sources/OpenAttributeGraphCxx/include/OpenAttributeGraph/**", + "Sources/OpenAttributeGraphCxx/include/OpenAttributeGraphCxx/**", + ] + ), + dependencies: [ + .target(name: platformTargetName), + .target(name: utilitiesTargetName), + .sdk(name: "z", type: .library), + ], + settings: commonSettings(base: nativeHeaderSearchSettings) + ), .target( name: "OpenAttributeGraph", destinations: destinations, product: .framework, bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph", sources: [ - "Sources/Platform/**", - "Sources/Utilities/**", - "Sources/OpenAttributeGraphCxx/**", "Sources/OpenAttributeGraph/**", ], - headers: .headers( - project: [ - "Sources/OpenAttributeGraphCxx/include/**", - "Sources/Platform/include/**", - "Sources/Utilities/include/**", - ] - ), scripts: [ .pre( path: "Scripts/Xcode/process_headers.sh", @@ -51,11 +124,13 @@ let project = Project( ), ], dependencies: [ - .sdk(name: "z", type: .library), + .target(name: "OpenAttributeGraphCxx"), + .sdk(name: "c++", type: .library), ], settings: .settings( base: [ "DEFINES_MODULE": "NO", + "OTHER_LDFLAGS": "$(inherited) -force_load $(BUILT_PRODUCTS_DIR)/libOpenAttributeGraphCxx.a", ], configurations: [ .debug(name: "Debug", xcconfig: "Configs/OpenAttributeGraph.xcconfig"), @@ -64,5 +139,60 @@ let project = Project( defaultSettings: .none ) ), + .target( + name: "OpenAttributeGraphShims", + destinations: destinations, + product: .staticFramework, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.Shims", + sources: ["Sources/OpenAttributeGraphShims/**"], + dependencies: openAttributeGraphShimsDependencies, + settings: commonSettings(base: openAttributeGraphShimsSettings) + ), + .target( + name: "UtilitiesTests", + destinations: destinations, + product: .unitTests, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.UtilitiesTests", + sources: ["Tests/UtilitiesTests/**/*.swift"], + dependencies: [ + .target(name: utilitiesTargetName), + ], + settings: commonSettings(base: cxxInteropSettings) + ), + .target( + name: "OpenAttributeGraphCxxTests", + destinations: destinations, + product: .unitTests, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.CxxTests", + sources: ["Tests/OpenAttributeGraphCxxTests/**/*.swift"], + dependencies: [ + .target(name: "OpenAttributeGraphCxx"), + ], + settings: commonSettings(base: cxxInteropSettings) + ), + .target( + name: "OpenAttributeGraphShimsTests", + destinations: destinations, + product: .unitTests, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.ShimsTests", + sources: ["Tests/OpenAttributeGraphShimsTests/**/*.swift"], + dependencies: [ + .target(name: "OpenAttributeGraphShims"), + ], + settings: commonSettings() + ), + .target( + name: "OpenAttributeGraphCompatibilityTests", + destinations: destinations, + product: .unitTests, + bundleId: "org.OpenSwiftUIProject.OpenAttributeGraph.CompatibilityTests", + sources: ["Tests/OpenAttributeGraphCompatibilityTests/**/*.swift"], + dependencies: [ + .target(name: "OpenAttributeGraph"), + .external(name: "Numerics"), + .external(name: "RealModule"), + ], + settings: commonSettings() + ), ] ) diff --git a/README.md b/README.md index 11e28401..ce73ea71 100644 --- a/README.md +++ b/README.md @@ -73,25 +73,31 @@ git submodule update --init ### Xcode (via Tuist) This project uses [Tuist](https://tuist.dev) to generate the Xcode project for framework builds. +The root workspace contains separate `OpenAttributeGraph` and `Example` projects. +The root `Package.swift` defines the public SwiftPM package, while `Tuist/Package.swift` defines +the external dependencies used only by the generated workspace. -1. Install Tuist (if not already installed): +1. Run the root setup script. It installs the pinned Tuist version from the root `mise.toml` and generates the workspace: ```shell - # via mise - mise install tuist - # or via Homebrew - brew install tuist + Scripts/setup.sh ``` -2. Generate the Xcode project: + By default, `OpenAttributeGraphShims` embeds the local `OpenAttributeGraph` implementation. + To use the source Compute package instead, select the environment from `mise.compute.toml`: ```shell - tuist generate + Scripts/setup.sh --compute ``` -3. Build using the generated workspace: +2. Build using the generated workspace: ```shell xcodebuild build -workspace OpenAttributeGraph.xcworkspace -scheme OpenAttributeGraph -destination 'generic/platform=iOS Simulator' ``` + To verify the Compute-backed shims generated by `--compute`: + ```shell + xcodebuild build -workspace OpenAttributeGraph.xcworkspace -scheme OpenAttributeGraphShims -destination 'platform=macOS' + ``` + ## License See LICENSE file - MIT diff --git a/Scripts/setup.sh b/Scripts/setup.sh new file mode 100755 index 00000000..e6377bd5 --- /dev/null +++ b/Scripts/setup.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +PROJECT_ROOT="$(dirname "$SCRIPTS_DIR")" +MISE_ARGS=() + +usage() { + printf '%s\n' \ + "Usage: $(basename "$0") [--compute]" \ + "" \ + "Options:" \ + " --compute Use mise.compute.toml while installing tools and generating the workspace." +} + +run_mise() { + if [[ ${#MISE_ARGS[@]} -gt 0 ]]; then + mise "${MISE_ARGS[@]}" "$@" + else + mise "$@" + fi +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --compute) + MISE_ARGS=(--env compute) + ;; + -h|--help) + usage + exit 0 + ;; + *) + usage >&2 + exit 2 + ;; + esac + shift +done + +cd "$PROJECT_ROOT" +mise trust "$PROJECT_ROOT/mise.toml" +if [[ ${#MISE_ARGS[@]} -gt 0 ]]; then + mise trust "$PROJECT_ROOT/mise.compute.toml" +fi +run_mise install +run_mise exec -- tuist install +run_mise exec -- tuist generate --no-open diff --git a/Tuist/Package.resolved b/Tuist/Package.resolved new file mode 100644 index 00000000..c79787c9 --- /dev/null +++ b/Tuist/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "6def8bc5c1bf57d9336d1ffa8a68d35124c72cee7f7d6d9db973886a0c8656ce", + "pins" : [ + { + "identity" : "compute", + "kind" : "remoteSourceControl", + "location" : "https://github.com/OpenSwiftUIProject/Compute", + "state" : { + "revision" : "a8387801fb54836919bec824f99049b28245d0b3", + "version" : "0.4.1-bugfix.2" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics", + "state" : { + "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", + "version" : "1.1.1" + } + } + ], + "version" : 3 +} diff --git a/Tuist/Package.swift b/Tuist/Package.swift index 582b73f4..5d3bd5fe 100644 --- a/Tuist/Package.swift +++ b/Tuist/Package.swift @@ -1,15 +1,61 @@ // swift-tools-version: 6.3 import PackageDescription +let environment = Context.environment +let useComputeBackend = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE"] == "1" +let useComputeBinary = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY"] == "1" + #if TUIST import ProjectDescription +var packageProductTypes: [String: ProjectDescription.Product] = [:] +if useComputeBackend && !useComputeBinary { + packageProductTypes["Compute"] = .staticFramework +} + let packageSettings = PackageSettings( - productTypes: [:] + productTypes: packageProductTypes ) #endif -let package = Package( +var dependencies: [PackageDescription.Package.Dependency] = [ + .package(url: "https://github.com/apple/swift-numerics", from: "1.1.1"), +] +var targets: [PackageDescription.Target] = [] + +if useComputeBackend { + if useComputeBinary { + let version = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY_VERSION"] ?? "0.4.1" + let repository = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY_REPO"] ?? "jcmosc/Compute" + let url = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY_URL"] + ?? "https://github.com/\(repository)/releases/download/\(version)/Compute.xcframework.zip" + let checksum = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY_CHECKSUM"] + ?? "322c72be5db233d723564a98c45150a45d3d5e9c0a4d4941d43edd1e2ea20bc4" + targets.append( + .binaryTarget( + name: "Compute", + url: url, + checksum: checksum + ) + ) + } else { + let repository = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_SOURCE_REPO"] + ?? "OpenSwiftUIProject/Compute" + let version = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_SOURCE_VERSION"] ?? "0.4.1" + if let branch = environment["OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_SOURCE_BRANCH"] { + dependencies.append( + .package(url: "https://github.com/\(repository)", branch: branch) + ) + } else { + dependencies.append( + .package(url: "https://github.com/\(repository)", exact: .init(stringLiteral: version)) + ) + } + } +} + +let package = PackageDescription.Package( name: "OpenAttributeGraphDeps", - dependencies: [] + dependencies: dependencies, + targets: targets ) diff --git a/Workspace.swift b/Workspace.swift new file mode 100644 index 00000000..978aa419 --- /dev/null +++ b/Workspace.swift @@ -0,0 +1,9 @@ +import ProjectDescription + +let workspace = Workspace( + name: "OpenAttributeGraph", + projects: [ + ".", + "Example", + ] +) diff --git a/mise.compute.toml b/mise.compute.toml new file mode 100644 index 00000000..0045476c --- /dev/null +++ b/mise.compute.toml @@ -0,0 +1,9 @@ +[tools] +tuist = "4.204.0" + +[env] +TUIST_OPENATTRIBUTEGRAPH_COMPUTE = "1" +OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE = "1" +OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY = "0" +OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_BINARY_VERSION = "0.4.1" +OPENATTRIBUTEGRAPH_OPENATTRIBUTESHIMS_COMPUTE_SOURCE_VERSION = "0.4.1-bugfix.2" diff --git a/mise.toml b/mise.toml index fca2305c..a0ef3ec6 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,2 @@ [tools] -tuist = "4.203.4" +tuist = "4.204.0"