diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35b122c..1ac917a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,3 @@ -# NOTE: every job here fails on a clean checkout until PureSwift/AndroidBluetooth#4 is merged. -# AndroidBluetooth requests the `AndroidManifest` product from `Android`, but it moved to -# `swift-android-native` (PureSwift/Android#40), and SwiftPM validates the whole package graph even -# for dependencies that are conditional on `.android`: -# error: product 'AndroidManifest' required by package 'androidbluetooth' ... not found -# That is a one-line manifest fix upstream, not a problem with this repo. name: CI on: @@ -19,13 +13,16 @@ concurrency: jobs: test: name: Test - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 45 steps: - uses: actions/checkout@v4 + # Pinned, not latest-stable: WasmKit 0.3.x declares swift-tools-version 6.3, so it needs + # Swift 6.3+. Xcode 26.5 (the macos-26 default) ships Swift 6.2.x and fails with + # "package 'wasmkit' is using Swift tools version 6.3.0 but the installed version is 6.2.4". - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '26.6' - name: Build run: swift build --build-tests - name: Test @@ -33,19 +30,17 @@ jobs: ios-archive: name: iOS Archive - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 60 steps: - uses: actions/checkout@v4 + # Pinned, not latest-stable: WasmKit 0.3.x declares swift-tools-version 6.3, so it needs + # Swift 6.3+. Xcode 26.5 (the macos-26 default) ships Swift 6.2.x and fails with + # "package 'wasmkit' is using Swift tools version 6.3.0 but the installed version is 6.2.4". - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '26.6' - # WasmKit declares `.treatAllWarnings(as: .error)` for Apple platforms, which conflicts with - # the `-suppress-warnings` Xcode passes to package dependencies: - # error: Conflicting options '-warnings-as-errors' and '-suppress-warnings' - # The setting only reaches package targets from the command line, so it cannot live in the - # xcconfig or the project. - name: Archive working-directory: Darwin run: | @@ -56,7 +51,6 @@ jobs: -archivePath "$RUNNER_TEMP/BluetoothExplorer.xcarchive" \ -skipPackagePluginValidation \ -skipMacroValidation \ - SWIFT_SUPPRESS_WARNINGS=NO \ CODE_SIGNING_ALLOWED=NO - name: Verify archive contents @@ -72,26 +66,3 @@ jobs: name: ios-archive path: ${{ runner.temp }}/BluetoothExplorer.xcarchive retention-days: 14 - - android-package: - name: Android Swift Package - runs-on: macos-15 - timeout-minutes: 60 - steps: - - uses: actions/checkout@v4 - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: latest-stable - - - name: Install Swift Android SDK - run: | - brew install skiptools/skip/skip || (brew update && brew install skiptools/skip/skip) - skip android sdk install --version 6.3.3 - - # Only the plugin engine is built here. The full app does not yet cross-compile for Android: - # AndroidBluetooth's `@JavaImplementation` macro generates invalid code for its scan callback, - # and WasmKit's SystemExtras target does not compile against the Android sysroot - # (`st_mode` is UInt32 there while swift-system's CInterop.Mode is UInt16). Neither is - # reachable from this target. See Documentation/AndroidSwiftUIMigration.md. - - name: Build plugin engine for Android - run: swift build --swift-sdk swift-6.3.3-RELEASE_android --target BluetoothExplorerPluginEngine diff --git a/.gitignore b/.gitignore index eb5ceef..f5c416d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,10 @@ playground.xcworkspace # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins -*Package.resolved +# NOTE: Package.resolved is deliberately committed. This is an app, not a library, and several +# dependencies track moving branches (GATT master, AndroidSwiftUI master), so without it every +# clean checkout — including CI — resolves a different graph than the one that was tested. +Darwin/**/Package.resolved .build/ .swiftpm diff --git a/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj b/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj index 5fba681..219b15f 100644 --- a/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj +++ b/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj @@ -105,7 +105,6 @@ 499CD43A2AC5B799001AE8D8 /* Sources */, 499CD43C2AC5B799001AE8D8 /* Frameworks */, 499CD43E2AC5B799001AE8D8 /* Resources */, - 499CD4452AC5B869001AE8D8 /* Run skip gradle */, 499CD44A2AC5B9C6001AE8D8 /* Embed Frameworks */, ); buildRules = ( @@ -166,25 +165,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 499CD4452AC5B869001AE8D8 /* Run skip gradle */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run skip gradle"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = "/bin/sh -e"; - shellScript = "if [ \"${SKIP_ZERO}\" != \"\" ]; then\n echo \"note: skipping skip due to SKIP_ZERO\"\n exit 0\nelif [ \"${ENABLE_PREVIEWS}\" = \"YES\" ]; then\n echo \"note: skipping skip due to ENABLE_PREVIEWS\"\n exit 0\nelif [ \"${ACTION}\" = \"install\" ]; then\n echo \"note: skipping skip due to archive install\"\n exit 0\nelif [ \"${SKIP_ACTION}\" = \"none\" ]; then\n echo \"note: skipping skip due to SKIP_ACTION none\"\n exit 0\nelse\n SKIP_ACTION=\"${SKIP_ACTION:-launch}\"\nfi\nPATH=${BUILD_ROOT}/Release:${BUILD_ROOT}/Debug:${BUILD_ROOT}/../../SourcePackages/artifacts/skip/skip/skip.artifactbundle/macos:${PATH}:${HOMEBREW_PREFIX:-/opt/homebrew}/bin\necho \"note: running gradle build with: $(which skip) gradle -p ${PWD}/../Android ${SKIP_ACTION:-launch}${CONFIGURATION:-Debug}\"\nskip gradle -p ../Android ${SKIP_ACTION:-launch}${CONFIGURATION:-Debug}\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/Documentation/AndroidSwiftUIMigration.md b/Documentation/AndroidSwiftUIMigration.md index dc0a87c..4ced352 100644 --- a/Documentation/AndroidSwiftUIMigration.md +++ b/Documentation/AndroidSwiftUIMigration.md @@ -88,7 +88,19 @@ device or emulator. Notable limitations, each documented in the source: ## Platform status -- **Apple platforms** — the package resolves and every target builds with no Skip involvement. +- **Apple platforms** — working end to end. A clean checkout resolves, builds, tests and archives + with **no mirrors, no environment variables and no extra xcodebuild flags**, and the app runs in + the simulator: the device list populates from `MockCentral`, and all 13 bundled plugins install + into `Documents/Plugins/` on first launch and appear in the Plugins tab. + + Two things were needed to get there beyond removing Skip: + - The `AndroidBluetooth` package dependency is temporarily not declared. SwiftPM validates the + whole graph even for `.android`-conditional dependencies, so its `AndroidManifest` bug broke + Apple builds. Restore it with PureSwift/AndroidBluetooth#4. + - WasmKit is taken from a fork that drops `.treatAllWarnings(as: .error)`. Upstream's setting + collides with the `-suppress-warnings` Xcode passes to package dependencies, which made the app + unbuildable in Xcode; the override only works from the command line, so it could not be fixed + from the xcconfig or project. - **Android** — the dependency wiring is in place, but the Android app build has not been verified end to end. It additionally needs the two PureSwift PRs above, and the Kotlin JNI peers under `Sources/BluetoothExplorer/Skip/` (`ScanCallback.kt`, `BluetoothGattCallback.kt`) rehomed into the diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..51a2888 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,176 @@ +{ + "originHash" : "8b32b28011bb149a00e00863969b6c918972017fd6cea9b44aace44913d40d43", + "pins" : [ + { + "identity" : "android", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/Android.git", + "state" : { + "branch" : "master", + "revision" : "815159f37488510d3deac61da6025022dc378fab" + } + }, + { + "identity" : "androidswiftui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/AndroidSwiftUI.git", + "state" : { + "branch" : "master", + "revision" : "ff646f4886d441264c1d05bbb90ffee01ab3aa82" + } + }, + { + "identity" : "bluetooth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/Bluetooth.git", + "state" : { + "revision" : "7cd6513aeaf0bc669c06bb346bcd5c9cc7c51cc3", + "version" : "7.6.0" + } + }, + { + "identity" : "gatt", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/GATT.git", + "state" : { + "branch" : "master", + "revision" : "ba4b36aed4a901ccf583e7365b661bc02727ea5a" + } + }, + { + "identity" : "javalang", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/JavaLang.git", + "state" : { + "branch" : "master", + "revision" : "1b7320afd8d40a669422ee61d5335ae6d647dce5" + } + }, + { + "identity" : "kotlin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/Kotlin.git", + "state" : { + "branch" : "master", + "revision" : "88a9bb94d156ef95d177bc6a65580dfacdda4b13" + } + }, + { + "identity" : "opencombine", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/OpenCombine.git", + "state" : { + "branch" : "feature/android", + "revision" : "5ea4600557999c7d160a3d211684a565fcb31bf8" + } + }, + { + "identity" : "swift-android-native", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-android-sdk/swift-android-native.git", + "state" : { + "branch" : "main", + "revision" : "5f71c1584b3cbe3842d335ebb4d7b545205d9945" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", + "version" : "1.8.2" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34", + "version" : "1.3.1" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", + "version" : "1.6.0" + } + }, + { + "identity" : "swift-java", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-java.git", + "state" : { + "branch" : "main", + "revision" : "d3092ee208d0704a1349e2d87ab8599306fc98f1" + } + }, + { + "identity" : "swift-java-jni-core", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-java-jni-core", + "state" : { + "branch" : "main", + "revision" : "b440a0be98381550cf189c903782e817619ffa05" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log", + "state" : { + "revision" : "a878e7f8f46cfc0e1125e565b5c08e7d5272dc9a", + "version" : "1.14.0" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio", + "state" : { + "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b", + "version" : "2.101.3" + } + }, + { + "identity" : "swift-subprocess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-subprocess.git", + "state" : { + "revision" : "13d087685b95d64d6aac9b94500d347bbe84c39b", + "version" : "0.4.0" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "79e4b74a295b6eb74a8b585e3a39d29e70c1dbd1", + "version" : "603.0.2" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system", + "state" : { + "revision" : "b5544ba79a70a0cb3563e75bf26dc198d6b40ed3", + "version" : "1.7.4" + } + }, + { + "identity" : "wasmkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/MillerTechnologyPeru/WasmKit.git", + "state" : { + "revision" : "ba06b7c64b5bc692c19c301ba2ef843c8d0f37c2" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift index 7a00432..fc5f88f 100644 --- a/Package.swift +++ b/Package.swift @@ -10,12 +10,27 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/PureSwift/GATT.git", branch: "master"), - .package(url: "https://github.com/PureSwift/AndroidBluetooth.git", branch: "master"), + // AndroidBluetooth is temporarily not declared. It requests the `AndroidManifest` product + // from `Android`, but that product moved to `swift-android-native` + // (PureSwift/Android#40), and SwiftPM validates the whole package graph even for + // dependencies conditional on `.android` — so simply declaring it breaks Apple-platform + // builds. Restore this together with the `AndroidBluetooth` target dependency below once + // PureSwift/AndroidBluetooth#4 is merged. The Swift sources still guard their use of it + // with `#if os(Android)`, so nothing else has to change. .package(url: "https://github.com/PureSwift/Bluetooth.git", from: "7.2.0"), // SwiftUI for Android. Apple platforms use the system SwiftUI instead, so this is only // linked for .android — see the conditional target dependencies below. .package(url: "https://github.com/PureSwift/AndroidSwiftUI.git", branch: "master"), - .package(url: "https://github.com/swiftwasm/WasmKit.git", .upToNextMinor(from: "0.3.1")) + // Fork of WasmKit 0.3.1 with one change: upstream declares + // `.treatAllWarnings(as: .error)` for Apple platforms, which collides with the + // `-suppress-warnings` Xcode passes to package dependencies and makes the app fail to build + // in Xcode ("Conflicting options '-warnings-as-errors' and '-suppress-warnings'"). That + // setting only reaches package targets from the command line, so it cannot be overridden + // from the xcconfig or the project. Track upstream and drop the fork once it is fixed there. + .package( + url: "https://github.com/MillerTechnologyPeru/WasmKit.git", + revision: "ba06b7c64b5bc692c19c301ba2ef843c8d0f37c2" + ) ], targets: [ .target( @@ -64,11 +79,6 @@ let package = Package( package: "GATT", condition: .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS]) ), - .product( - name: "AndroidBluetooth", - package: "AndroidBluetooth", - condition: .when(platforms: [.android]) - ), .product( name: "AndroidSwiftUI", package: "AndroidSwiftUI",