Skip to content

Commit

Permalink
is it something about the script?
Browse files Browse the repository at this point in the history
  • Loading branch information
lawrence-forooghian committed Aug 2, 2024
1 parent 2062aeb commit badf115
Showing 1 changed file with 2 additions and 158 deletions.
160 changes: 2 additions & 158 deletions Sources/BuildTool/BuildTool.swift
Original file line number Diff line number Diff line change
@@ -1,171 +1,15 @@
import ArgumentParser
import Foundation

enum DestinationSpecifier {
case platform(String)
case deviceID(String)

var xcodebuildArgument: String {
switch self {
case let .platform(platform):
"platform=\(platform)"
case let .deviceID(deviceID):
"id=\(deviceID)"
}
}
}

enum DestinationStrategy {
case fixed(platform: String)
case lookup(destinationPredicate: DestinationPredicate)
}

struct DestinationPredicate {
// TODO: document
var runtime: String
var deviceType: String
}

enum Platform: String, CaseIterable {
case macOS
case iOS
case tvOS

var destinationStrategy: DestinationStrategy {
// TODO: why is xcodebuild giving locally with iOS "--- xcodebuild: WARNING: Using the first of multiple matching destinations:"
switch self {
case .macOS:
.fixed(platform: "macOS")
case .iOS:
.lookup(destinationPredicate: .init(runtime: "iOS-17-5", deviceType: "iPhone-15"))
case .tvOS:
.lookup(destinationPredicate: .init(runtime: "tvOS-17-5", deviceType: "Apple TV"))
}
}
}

extension Platform: ExpressibleByArgument {
init?(argument: String) {
self.init(rawValue: argument)
}
}

struct SimctlOutput: Codable {
var devices: [String: [Device]]

struct Device: Codable {
var udid: String
var deviceTypeIdentifier: String
}
}

enum Error: Swift.Error {
case terminatedWithExitCode(Int32)
}

// 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
struct BuildTool: ParsableCommand {
@Option var platform: Platform
@Option var platform: String

@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(fetchDeviceUDID(destinationPredicate: destinationPredicate))
}

try runXcodebuild(action: nil, destination: destinationSpecifier)
try runXcodebuild(action: "test", destination: destinationSpecifier)
}

func runXcodebuild(action: String?, destination: DestinationSpecifier) throws {
var arguments: [String] = []

if let action {
arguments.append(action)
}

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

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

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

func fetchDeviceUDID(destinationPredicate: DestinationPredicate) throws -> String {
let simctlOutput = try fetchSimctlOutput()

let runtimeIdentifier = "com.apple.CoreSimulator.SimRuntime.\(destinationPredicate.runtime)"
let deviceTypeIdentifier = "com.apple.CoreSimulator.SimDeviceType.\(destinationPredicate.deviceType)"
guard let matchingDevices = simctlOutput.devices[runtimeIdentifier]?.filter({ $0.deviceTypeIdentifier == deviceTypeIdentifier }) else {
fatalError("Couldn’t find a simulator with runtime \(runtimeIdentifier) and device type \(deviceTypeIdentifier); available devices are \(simctlOutput.devices)")
}

if matchingDevices.count > 1 {
fatalError("Found multiple simulators with runtime \(runtimeIdentifier) and device type \(deviceTypeIdentifier); matching devices are \(matchingDevices)")
}

return matchingDevices[0].udid
}

func fetchSimctlOutput() throws -> SimctlOutput {
let data = try 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 {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = [executableName] + arguments

try process.run()
process.waitUntilExit()

if process.terminationStatus != 0 {
throw Error.terminatedWithExitCode(process.terminationStatus)
}
}

// 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 {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = [executableName] + arguments

let standardOutput = Pipe()
process.standardOutput = standardOutput

try process.run()

var stdoutData = Data()
while true {
if let data = try standardOutput.fileHandleForReading.readToEnd() {
stdoutData.append(data)
} else {
break
}
}

process.waitUntilExit()

if process.terminationStatus != 0 {
throw Error.terminatedWithExitCode(process.terminationStatus)
}

return stdoutData
print("Here is run")
}
}

0 comments on commit badf115

Please sign in to comment.