Skip to content

Commit

Permalink
split build tool for example app
Browse files Browse the repository at this point in the history
  • Loading branch information
lawrence-forooghian committed Aug 5, 2024
1 parent 3949857 commit b111b75
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 16 deletions.
25 changes: 24 additions & 1 deletion .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,27 @@ jobs:
xcode-version: ${{ matrix.tooling.xcode-version }}

- name: Build and run tests
run: swift run BuildTool --platform ${{ matrix.platform }} --swift-version ${{ matrix.tooling.swift-version }}
run: swift run BuildTool build-and-test-library --platform ${{ matrix.platform }} --swift-version ${{ matrix.tooling.swift-version }}

check-example-app:
name: Example app, ${{matrix.platform}} (Xcode ${{ matrix.tooling.xcode-version }}, Swift ${{ matrix.tooling.swift-version }})
runs-on: macos-latest

strategy:
fail-fast: false
matrix:
tooling:
- xcode-version: 15.3
swift-version: 5
- xcode-version: 16-beta
swift-version: 6
platform: [macOS, iOS, tvOS]

steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.tooling.xcode-version }}

- name: Build example app
run: swift run BuildTool build-example-app --platform ${{ matrix.platform }} --swift-version ${{ matrix.tooling.swift-version }}
63 changes: 48 additions & 15 deletions Sources/BuildTool/BuildTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,45 +64,75 @@ enum Error: Swift.Error {
case simulatorLookupFailed(message: String)
}

// TODO: Is there a better way to make sure that this script has access to macOS APIs that are more recent than the package’s deployment target?
@available(macOS 14, *)
@main
@available(macOS 14, *)
struct BuildTool: ParsableCommand {
static let configuration = CommandConfiguration(subcommands: [BuildAndTestLibrary.self, BuildExampleApp.self])
}

// TODO: Is there a better way to make sure that this script has access to macOS APIs that are more recent than the package’s deployment target?
@available(macOS 14, *)
struct BuildAndTestLibrary: ParsableCommand {
@Option var platform: Platform
@Option var swiftVersion: Int

mutating func run() throws {
let destinationSpecifier: DestinationSpecifier = switch platform.destinationStrategy {
case let .fixed(platform):
.platform(platform)
case let .lookup(destinationPredicate):
try .deviceID(DestinationFetcher.fetchDeviceUDID(destinationPredicate: destinationPredicate))
}

let scheme = "AblyChat"

try XcodeRunner.runXcodebuild(action: nil, scheme: scheme, destination: destinationSpecifier, swiftVersion: swiftVersion)
try XcodeRunner.runXcodebuild(action: "test", scheme: scheme, destination: destinationSpecifier, swiftVersion: swiftVersion)
}
}

@available(macOS 14, *)
struct BuildExampleApp: ParsableCommand {
@Option var platform: Platform
@Option var swiftVersion: Int

// TODO: DRY up
mutating func run() throws {
let destinationSpecifier: DestinationSpecifier = switch platform.destinationStrategy {
case let .fixed(platform):
.platform(platform)
case let .lookup(destinationPredicate):
try .deviceID(fetchDeviceUDID(destinationPredicate: destinationPredicate))
try .deviceID(DestinationFetcher.fetchDeviceUDID(destinationPredicate: destinationPredicate))
}

try runXcodebuild(action: nil, destination: destinationSpecifier)
try runXcodebuild(action: "test", destination: destinationSpecifier)
try XcodeRunner.runXcodebuild(action: nil, scheme: "AblyChatExample", destination: destinationSpecifier, swiftVersion: swiftVersion)
}
}

func runXcodebuild(action: String?, destination: DestinationSpecifier) throws {
@available(macOS 14, *)
enum XcodeRunner {
static func runXcodebuild(action: String?, scheme: String, destination: DestinationSpecifier, swiftVersion: Int) throws {
var arguments: [String] = []

if let action {
arguments.append(action)
}

arguments.append(contentsOf: ["-scheme", "AblyChat"])
arguments.append(contentsOf: ["-scheme", scheme])
arguments.append(contentsOf: ["-destination", destination.xcodebuildArgument])

arguments.append(contentsOf: [
"SWIFT_TREAT_WARNINGS_AS_ERRORS=YES",
"SWIFT_VERSION=\(swiftVersion)",
])

try run(executableName: "xcodebuild", arguments: arguments)
try ProcessRunner.run(executableName: "xcodebuild", arguments: arguments)
}
}

func fetchDeviceUDID(destinationPredicate: DestinationPredicate) throws -> String {
@available(macOS 14, *)
enum DestinationFetcher {
static func fetchDeviceUDID(destinationPredicate: DestinationPredicate) throws -> String {
let simctlOutput = try fetchSimctlOutput()

let runtimeIdentifier = "com.apple.CoreSimulator.SimRuntime.\(destinationPredicate.runtime)"
Expand All @@ -121,17 +151,21 @@ struct BuildTool: ParsableCommand {
return matchingDevices[0].udid
}

func fetchSimctlOutput() throws -> SimctlOutput {
let data = try runAndReturnStdout(
private static func fetchSimctlOutput() throws -> SimctlOutput {
let data = try ProcessRunner.runAndReturnStdout(
executableName: "xcrun",
arguments: ["simctl", "list", "--json", "devices", "available"]
)

return try JSONDecoder().decode(SimctlOutput.self, from: data)
}
}

// I would have liked to use Swift concurrency for this but it felt like it would be a bit of a faff and it’s only a script. There’s a proposal for a Subprocess API coming up in Foundation which will marry Process with Swift concurrency.
private func run(executableName: String, arguments: [String]) throws {
// I would have liked to use Swift concurrency for these but it felt like it would be a bit of a faff and it’s only a script. There’s a proposal for a Subprocess API coming up in Foundation which will marry Process with Swift concurrency.
// TODO: Is there a better way to make sure that this script has access to macOS APIs that are more recent than the package’s deployment target?
@available(macOS 14, *)
enum ProcessRunner {
static func run(executableName: String, arguments: [String]) throws {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = [executableName] + arguments
Expand All @@ -144,8 +178,7 @@ struct BuildTool: ParsableCommand {
}
}

// I would have liked to use Swift concurrency for this but it felt like it would be a bit of a faff and it’s only a script. There’s a proposal for a Subprocess API coming up in Foundation which will marry Process with Swift concurrency.
private func runAndReturnStdout(executableName: String, arguments: [String]) throws -> Data {
static func runAndReturnStdout(executableName: String, arguments: [String]) throws -> Data {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = [executableName] + arguments
Expand Down

0 comments on commit b111b75

Please sign in to comment.