diff --git a/Sources/GenIR/GenIR.swift b/Sources/GenIR/GenIR.swift index c3926b3..ea0d194 100644 --- a/Sources/GenIR/GenIR.swift +++ b/Sources/GenIR/GenIR.swift @@ -112,42 +112,21 @@ struct IREmitterCommand: ParsableCommand { // swiftlint:disable function_parameter_count mutating func run(project: URL, log: String, archive: URL, output: URL, level: Logger.Level, dryRun: Bool) throws { - // let project = try ProjectParser(path: project, logLevel: level) - // var targets = Targets(for: project) - - // let log = try logParser(for: log) - // try log.parse(&targets) - - // let buildCacheManipulator = try BuildCacheManipulator( - // buildCachePath: log.buildCachePath, - // buildSettings: log.settings, - // archive: archive - // ) - - // let runner = CompilerCommandRunner( - // output: output, - // buildCacheManipulator: buildCacheManipulator, - // dryRun: dryRun - // ) - // try runner.run(targets: targets) - - // let postprocessor = try OutputPostprocessor(archive: archive, output: output) - // try postprocessor.process(targets: &targets) // TODO: We can't use async parsable commands here as SPM relies on swift-driver which, until Swift 5.9 uses Argument Parser 1.0.3. // Remove these declarations etc when we can use Swift 5.9 - guard let projectPath = projectPath else { - throw ValidationError("Failed to infer project path. Please rerun providing --project-path") - } - let logPath = logPath - let quieter = quieter - let outputPath = outputPath + let project = project + let log = log + let archive = archive + let output = output + let level = level + let quieter = dryRun let semaphore = DispatchSemaphore(value: 0) Task { - let project = try await ProjectParser(path: projectPath, logLevel: logger.logLevel) + let project = try await ProjectParser(path: project, logLevel: logger.logLevel) var targets = Targets(for: project) - let log = try IREmitterCommand.logParser(for: logPath, quieter) + let log = try IREmitterCommand.logParser(for: log, quieter) try log.parse(&targets) let buildCacheManipulator = try BuildCacheManipulator( @@ -157,7 +136,7 @@ struct IREmitterCommand: ParsableCommand { ) let runner = CompilerCommandRunner( - output: outputPath, + output: output, buildCacheManipulator: buildCacheManipulator, dryRun: dryRun ) diff --git a/Tests/GenIRTests/MultipleAppTests.swift b/Tests/GenIRTests/MultipleAppTests.swift index 87ddbaf..9872903 100644 --- a/Tests/GenIRTests/MultipleAppTests.swift +++ b/Tests/GenIRTests/MultipleAppTests.swift @@ -9,11 +9,11 @@ final class MultipleAppTests: XCTestCase { .appendingPathComponent("MultipleApp.xcodeproj") }() - func testExpectedTargetLookup() throws { + func testExpectedTargetLookup() async throws { let context = try TestContext() let result = try context.build(test: Self.testPath, scheme: "MultipleApp") - let project: ProjectParser = try ProjectParser(path: Self.testPath, logLevel: .debug) + let project: ProjectParser = try await ProjectParser(path: Self.testPath, logLevel: .debug) var targets = Targets(for: project) let logContents = try String(contentsOf: context.buildLog).components(separatedBy: .newlines) diff --git a/Tests/GenIRTests/TestContext.swift b/Tests/GenIRTests/TestContext.swift index 2049559..4b626a4 100644 --- a/Tests/GenIRTests/TestContext.swift +++ b/Tests/GenIRTests/TestContext.swift @@ -18,7 +18,9 @@ class TestContext { func clean(test path: URL) throws -> Process.ReturnValue { return try Process.runShell( "/usr/bin/xcodebuild", - arguments: ["clean"], + arguments: [ + "clean" + ], runInDirectory: path.deletingLastPathComponent(), joinPipes: true ) @@ -29,6 +31,14 @@ class TestContext { scheme: String, additionalArguments: [String] = [] ) throws -> Process.ReturnValue { + if shouldDeployPodsBuildSystemHack { + if let returnValue = try deployPodsBuildSystemHack(path) { + guard returnValue.code == 0 else { + throw Error.commandFailed(returnValue) + } + } + } + let clean = try clean(test: path) guard clean.code == 0 else { @@ -70,10 +80,37 @@ class TestContext { let archive: URL let buildLog: URL let temporaryDirectory: URL + let shouldDeployPodsBuildSystemHack: Bool - init() throws { + init(podsBuildSystemHack: Bool = false) throws { temporaryDirectory = try FileManager.default.temporaryDirectory(named: "gen-ir-tests-\(UUID().uuidString)") archive = temporaryDirectory.appendingPathComponent("x.xcarchive") buildLog = temporaryDirectory.appendingPathComponent("build.log") + shouldDeployPodsBuildSystemHack = podsBuildSystemHack + } +} + +extension TestContext { + // TODO: we should look into how this will effect users and whether we should do this on their behalf.... (probably yes) + /// Pods has a weird default for legacy build systems where they set SYMROOT to ${SRCROOT}../build + /// Then, Apple has this stupid bug where they don't mark this folder as being generated by the build system + /// This dates back until at _least_ iOS 12 - with no fix. Because Apple is Apple and they hate developers + /// This function will mark the build folder as created by the build system so `xcodebuild clean` works. + private func deployPodsBuildSystemHack(_ path: URL) throws -> Process.ReturnValue? { + let buildPath = path.deletingLastPathComponent().appendingPath(component: "build") + + if !FileManager.default.directoryExists(at: buildPath.appendingPath(component: "Pods.build")) { + return nil + } + + return try Process.runShell( + "/usr/bin/xattr", + arguments: [ + "-w", + "com.apple.xcode.CreatedByBuildSystem", + "true", + buildPath.filePath + ] + ) } } diff --git a/Tests/GenIRTests/UmbrellaTests.swift b/Tests/GenIRTests/UmbrellaTests.swift index fe9df60..2bf13ab 100644 --- a/Tests/GenIRTests/UmbrellaTests.swift +++ b/Tests/GenIRTests/UmbrellaTests.swift @@ -18,12 +18,12 @@ final class UmbrellaTests: XCTestCase { "Umbrella.framework": ["GetOrg.bc", "Umbrella_vers.bc"].sorted() ] - func testUmbrellaTargets() throws { + func testUmbrellaTargets() async throws { let context = try TestContext() let process = try context.build(test: Self.testPath, scheme: Self.scheme) XCTAssertEqual(process.code, 0, "Failed to build test case") - let projectParser = try ProjectParser(path: Self.testPath, logLevel: .info) + let projectParser = try await ProjectParser(path: Self.testPath, logLevel: .info) let targets = Targets(for: projectParser) XCTAssertEqual(targets.count, 4, "Expected 4 targets, got \(targets.count)") @@ -69,7 +69,7 @@ final class UmbrellaTests: XCTestCase { } func testCustomDerivedDataAndSkipInstallNo() throws { - let context = try TestContext() + let context = try TestContext(podsBuildSystemHack: true) defer { try? FileManager.default.removeItem(at: context.temporaryDirectory) } _ = try context.build(test: Self.testPath, scheme: Self.scheme, additionalArguments: ["SKIP_INSTALL=NO", "-derivedDataPath", "_build"]) diff --git a/Tests/GenIRTests/gen_irTests.swift b/Tests/GenIRTests/gen_irTests.swift index a8710fa..37bb6e1 100644 --- a/Tests/GenIRTests/gen_irTests.swift +++ b/Tests/GenIRTests/gen_irTests.swift @@ -3,12 +3,12 @@ import XCTest import PBXProjParser final class GenIRTests: XCTestCase { - func testManyTargetTestTargets() throws { + func testManyTargetTestTargets() async throws { let projectPath = TestContext.baseTestingPath .appendingPathComponent("TestAssets") .appendingPathComponent("ManyTargetTest") .appendingPathComponent("ManyTargetTest.xcodeproj") - let project = try ProjectParser(path: projectPath, logLevel: logger.logLevel) + let project = try await ProjectParser(path: projectPath, logLevel: logger.logLevel) let targets = Targets(for: project) XCTAssert(targets.count == 3, "Targets count expected to be 3, was \(targets.count)")