Skip to content

Commit

Permalink
Refactoring XcodeBuild
Browse files Browse the repository at this point in the history
  • Loading branch information
swiftyfinch committed Feb 26, 2024
1 parent f4d28a0 commit ca86ef0
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Sources/RugbyFoundation/Core/Build/BuildManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ extension BuildManager: IInternalBuildManager {
processInterruptionTask.cancel()
cleanup()
}
try self.xcodeBuild.build(target: target.name, options: options, paths: paths)
try await xcodeBuild.build(target: target.name, options: options, paths: paths)
})
}
}
Expand Down
52 changes: 34 additions & 18 deletions Sources/RugbyFoundation/Core/Build/XcodeBuild/XcodeBuild.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Fish
protocol IXcodeBuild: AnyObject {
func build(target: String,
options: XcodeBuildOptions,
paths: XcodeBuildPaths) throws
paths: XcodeBuildPaths) async throws
}

/// Xcode build options.
Expand Down Expand Up @@ -81,31 +81,47 @@ final class XcodeBuild {
init(xcodeBuildExecutor: IXcodeBuildExecutor) {
self.xcodeBuildExecutor = xcodeBuildExecutor
}
}

extension XcodeBuild: IXcodeBuild {
func build(target: String,
options: XcodeBuildOptions,
paths: XcodeBuildPaths) throws {
private func run(
arguments: [String],
options: XcodeBuildOptions,
paths: XcodeBuildPaths
) async throws {
let command = "NSUnbufferedIO=YES xcodebuild"
var arguments = [
var arguments = arguments
arguments.append(contentsOf: [
"-project \(paths.project.shellFriendly)",
"-target \(target)",
"-sdk \(options.sdk.xcodebuild)",
"-config \(options.config.shellFriendly)",
"ARCHS=\(options.arch)",
"SYMROOT=\(paths.symroot.shellFriendly)",
"-parallelizeTargets"
]
"SYMROOT=\(paths.symroot.shellFriendly)"
])
options.resultBundlePath.map {
arguments.append("-resultBundlePath \($0.shellFriendly)")
}
arguments.append(contentsOf: options.xcargs)

try Folder.create(at: paths.symroot)
try xcodeBuildExecutor.run(command,
rawLogPath: paths.rawLog,
logPath: paths.beautifiedLog,
args: arguments)
try await xcodeBuildExecutor.run(command,
rawLogPath: paths.rawLog,
logPath: paths.beautifiedLog,
args: arguments)
}
}

// MARK: - IXcodeBuild

extension XcodeBuild: IXcodeBuild {
func build(target: String,
options: XcodeBuildOptions,
paths: XcodeBuildPaths) async throws {
try await run(
arguments: [
"-target \(target)",
"-sdk \(options.sdk.xcodebuild)",
"-config \(options.config.shellFriendly)",
"ARCHS=\(options.arch)",
"-parallelizeTargets"
],
options: options,
paths: paths
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,47 @@ protocol IXcodeBuildExecutor {
rawLogPath: String,
logPath: String,
args: Any...
) throws
) async throws
}

// MARK: - Implementation

final class XcodeBuildExecutor: IXcodeBuildExecutor {
final class XcodeBuildExecutor: IXcodeBuildExecutor, Loggable {
let logger: ILogger
private let shellExecutor: IShellExecutor
private let logFormatter: IBuildLogFormatter

init(shellExecutor: IShellExecutor,
init(logger: ILogger,
shellExecutor: IShellExecutor,
logFormatter: IBuildLogFormatter) {
self.logger = logger
self.shellExecutor = shellExecutor
self.logFormatter = logFormatter
}

func run(_ command: String, rawLogPath: String, logPath: String, args: Any...) throws {
func run(_ command: String, rawLogPath: String, logPath: String, args: Any...) async throws {
try Folder.create(at: URL(fileURLWithPath: rawLogPath).deletingLastPathComponent().path)
try shellExecutor.throwingShell(command, args: args, "| tee '\(rawLogPath)'")
if let errors = try? beautifyLog(rawLogPath: rawLogPath, logPath: logPath), errors.isNotEmpty {
if let errors = try? await beautifyLog(rawLogPath: rawLogPath, logPath: logPath), errors.isNotEmpty {
throw BuildError.buildFailed(errors: errors, buildLogPath: logPath, rawBuildLogPath: rawLogPath)
}
}

// MARK: - Private

private func beautifyLog(rawLogPath: String, logPath: String) throws -> [String] {
private func beautifyLog(rawLogPath: String, logPath: String) async throws -> [String] {
var tests: [String] = []
var errors: [String] = []
let log = try File.create(at: logPath)
let output: (String, OutputType) throws -> Void = { formattedLine, type in
if type == .error { errors.append(formattedLine) }
switch type {
case .error:
errors.append(formattedLine)
case .test, .testCase:
tests.append(formattedLine)
case .undefined, .task, .nonContextualError, .warning, .result:
break
}
try log.append("\(formattedLine)\n")
}

Expand All @@ -49,6 +60,9 @@ final class XcodeBuildExecutor: IXcodeBuildExecutor {
try logFormatter.format(line: line, output: output)
}
try logFormatter.finish(output: output)
for line in tests {
await logPlain(line)
}
return errors
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ extension Vault {
// MARK: - Internal

func internalBuildManager() -> IInternalBuildManager {
let logFormatter = BuildLogFormatter(workingDirectory: router.workingDirectory,
colored: Rainbow.enabled)
let xcodeBuildExecutor = XcodeBuildExecutor(
shellExecutor: shellExecutor,
logFormatter: logFormatter
)
let xcodeProject = xcode.project(projectPath: router.podsProjectPath)
let buildTargetsManager = BuildTargetsManager(xcodeProject: xcodeProject)
let useBinariesManager = useBinariesManager(xcodeProject: xcodeProject,
Expand All @@ -28,15 +22,14 @@ extension Vault {
localRugbyFolderPath: router.rugbyPath,
buildFolderPath: router.buildPath
)
let xcodeBuild = XcodeBuild(xcodeBuildExecutor: xcodeBuildExecutor)
return BuildManager(logger: logger,
buildTargetsManager: buildTargetsManager,
librariesPatcher: LibrariesPatcher(logger: logger),
xcodeProject: xcodeProject,
rugbyXcodeProject: RugbyXcodeProject(xcodeProject: xcodeProject),
backupManager: backupManager(),
processMonitor: processMonitor,
xcodeBuild: xcodeBuild,
xcodeBuild: xcodeBuild(),
binariesStorage: binariesStorage,
targetsHasher: targetsHasher(),
useBinariesManager: useBinariesManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import Fish
public extension Vault {
/// The manager to prebuild CocoaPods project.
func prebuildManager() -> IPrebuildManager {
PrebuildManager(
let xcodeProject = xcode.project(projectPath: router.podsProjectPath)
return PrebuildManager(
logger: logger,
xcodePhaseEditor: XcodePhaseEditor(),
buildManager: internalBuildManager(),
xcodeProject: xcode.project(projectPath: router.podsProjectPath),
xcodeProject: xcodeProject,
binariesStorage: binariesStorage
)
}
Expand Down
12 changes: 12 additions & 0 deletions Sources/RugbyFoundation/Vault/Vault.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Fish
import Rainbow

/// The main container of Rugby stuff.
public final class Vault {
Expand Down Expand Up @@ -135,6 +136,17 @@ public final class Vault {
logsRotator: logsRotator,
router: router
)

func xcodeBuild() -> XcodeBuild {
let logFormatter = BuildLogFormatter(workingDirectory: router.workingDirectory,
colored: Rainbow.enabled)
let xcodeBuildExecutor = XcodeBuildExecutor(
logger: logger,
shellExecutor: shellExecutor,
logFormatter: logFormatter
)
return XcodeBuild(xcodeBuildExecutor: xcodeBuildExecutor)
}
}

// MARK: - Keys
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import XCTest

final class XcodeBuildExecutorTests: XCTestCase {
private var sut: XcodeBuildExecutor!
private var logger: ILoggerMock!
private var shellExecutor: IShellExecutorMock!
private var buildLogFormatter: IBuildLogFormatterMock!
private var fishSharedStorage: IFilesManagerMock!
Expand All @@ -20,9 +21,11 @@ final class XcodeBuildExecutorTests: XCTestCase {
Fish.sharedStorage = backupFishSharedStorage
}

logger = ILoggerMock()
shellExecutor = IShellExecutorMock()
buildLogFormatter = IBuildLogFormatterMock()
sut = XcodeBuildExecutor(
logger: logger,
shellExecutor: shellExecutor,
logFormatter: buildLogFormatter
)
Expand All @@ -31,14 +34,15 @@ final class XcodeBuildExecutorTests: XCTestCase {
override func tearDown() {
super.tearDown()
sut = nil
logger = nil
shellExecutor = nil
buildLogFormatter = nil
fishSharedStorage = nil
}
}

extension XcodeBuildExecutorTests {
func test_taskInLog() throws {
func test_taskInLog() async throws {
let createFileMock = IFileMock()
fishSharedStorage.createFileAtContentsReturnValue = createFileMock
let createFolderMock = IFolderMock()
Expand All @@ -64,7 +68,7 @@ extension XcodeBuildExecutorTests {
}

// Act
try sut.run(
try await sut.run(
"test_command",
rawLogPath: test_rawLogPath,
logPath: test_logPath,
Expand Down Expand Up @@ -94,7 +98,7 @@ extension XcodeBuildExecutorTests {
XCTAssertEqual(createFileMock.appendReceivedInvocations, [expectedContent + "\n"])
}

func test_errorsInLog() throws {
func test_errorsInLog() async throws {
let createFileMock = IFileMock()
fishSharedStorage.createFileAtContentsReturnValue = createFileMock
let createFolderMock = IFolderMock()
Expand Down Expand Up @@ -125,14 +129,16 @@ extension XcodeBuildExecutorTests {

// Act
var resultError: Error?
try XCTAssertThrowsError(
sut.run(
do {
try await sut.run(
"test_command",
rawLogPath: test_rawLogPath,
logPath: test_logPath,
args: "arg0", "arg1"
)
) { resultError = $0 }
} catch {
resultError = error
}

// Assert
XCTAssertEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ final class XcodeBuildTests: XCTestCase {
}

extension XcodeBuildTests {
func test_general() throws {
func test_general() async throws {
fishSharedStorage.createFolderAtReturnValue = IFolderMock()
try sut.build(
try await sut.build(
target: "Rugby",
options: XcodeBuildOptions(
sdk: .ios,
Expand Down Expand Up @@ -61,13 +61,13 @@ extension XcodeBuildTests {
XCTAssertEqual(
arguments.args as? [[String]],
[[
"-project /Users/swiftyfinch/Developer/Repos/Rugby/Example/Pods/Pods.xcodeproj",
"-target Rugby",
"-sdk iphoneos",
"-config Debug",
"ARCHS=x86_64",
"SYMROOT=/Users/swiftyfinch/Developer/Repos/Rugby/Example/.rugby/build",
"-parallelizeTargets",
"-project /Users/swiftyfinch/Developer/Repos/Rugby/Example/Pods/Pods.xcodeproj",
"SYMROOT=/Users/swiftyfinch/Developer/Repos/Rugby/Example/.rugby/build",
"-resultBundlePath build.xcresult",
"COMPILER_INDEX_STORE_ENABLE=NO",
"SWIFT_COMPILATION_MODE=wholemodule",
Expand All @@ -79,9 +79,9 @@ extension XcodeBuildTests {
)
}

func test_spaceInPaths() throws {
func test_spaceInPaths() async throws {
fishSharedStorage.createFolderAtReturnValue = IFolderMock()
try sut.build(
try await sut.build(
target: "RugbyPods",
options: XcodeBuildOptions(
sdk: .sim,
Expand Down Expand Up @@ -111,13 +111,13 @@ extension XcodeBuildTests {
XCTAssertEqual(
arguments.args as? [[String]],
[[
"-project /Users/swiftyfinch/Developer/Repos/Rugby/Exa\\ mple/Pods/Pods.xcodeproj",
"-target RugbyPods",
"-sdk iphonesimulator",
"-config QA\\ Release",
"ARCHS=arm64",
"SYMROOT=/Users/swiftyfinch/Developer/Repos/Rugby/Exa\\ mple/.rugby/build",
"-parallelizeTargets",
"-project /Users/swiftyfinch/Developer/Repos/Rugby/Exa\\ mple/Pods/Pods.xcodeproj",
"SYMROOT=/Users/swiftyfinch/Developer/Repos/Rugby/Exa\\ mple/.rugby/build",
"COMPILER_INDEX_STORE_ENABLE=NO",
"SWIFT_COMPILATION_MODE=wholemodule"
]]
Expand Down
25 changes: 22 additions & 3 deletions Tests/FoundationTests/Mocks/IXcodeBuildMock.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,35 @@ final class IXcodeBuildMock: IXcodeBuild {
var buildTargetOptionsPathsCalled: Bool { buildTargetOptionsPathsCallsCount > 0 }
var buildTargetOptionsPathsReceivedArguments: (target: String, options: XcodeBuildOptions, paths: XcodeBuildPaths)?
var buildTargetOptionsPathsReceivedInvocations: [(target: String, options: XcodeBuildOptions, paths: XcodeBuildPaths)] = []
var buildTargetOptionsPathsClosure: ((String, XcodeBuildOptions, XcodeBuildPaths) throws -> Void)?
var buildTargetOptionsPathsClosure: ((String, XcodeBuildOptions, XcodeBuildPaths) async throws -> Void)?

func build(target: String, options: XcodeBuildOptions, paths: XcodeBuildPaths) throws {
func build(target: String, options: XcodeBuildOptions, paths: XcodeBuildPaths) async throws {
buildTargetOptionsPathsCallsCount += 1
buildTargetOptionsPathsReceivedArguments = (target: target, options: options, paths: paths)
buildTargetOptionsPathsReceivedInvocations.append((target: target, options: options, paths: paths))
if let error = buildTargetOptionsPathsThrowableError {
throw error
}
try buildTargetOptionsPathsClosure?(target, options, paths)
try await buildTargetOptionsPathsClosure?(target, options, paths)
}

// MARK: - test

var testSchemeTestPlanSimulatorNameOptionsPathsThrowableError: Error?
var testSchemeTestPlanSimulatorNameOptionsPathsCallsCount = 0
var testSchemeTestPlanSimulatorNameOptionsPathsCalled: Bool { testSchemeTestPlanSimulatorNameOptionsPathsCallsCount > 0 }
var testSchemeTestPlanSimulatorNameOptionsPathsReceivedArguments: (scheme: String, testPlan: String, simulatorName: String, options: XcodeBuildOptions, paths: XcodeBuildPaths)?
var testSchemeTestPlanSimulatorNameOptionsPathsReceivedInvocations: [(scheme: String, testPlan: String, simulatorName: String, options: XcodeBuildOptions, paths: XcodeBuildPaths)] = []
var testSchemeTestPlanSimulatorNameOptionsPathsClosure: ((String, String, String, XcodeBuildOptions, XcodeBuildPaths) async throws -> Void)?

func test(scheme: String, testPlan: String, simulatorName: String, options: XcodeBuildOptions, paths: XcodeBuildPaths) async throws {
if let error = testSchemeTestPlanSimulatorNameOptionsPathsThrowableError {
throw error
}
testSchemeTestPlanSimulatorNameOptionsPathsCallsCount += 1
testSchemeTestPlanSimulatorNameOptionsPathsReceivedArguments = (scheme: scheme, testPlan: testPlan, simulatorName: simulatorName, options: options, paths: paths)
testSchemeTestPlanSimulatorNameOptionsPathsReceivedInvocations.append((scheme: scheme, testPlan: testPlan, simulatorName: simulatorName, options: options, paths: paths))
try await testSchemeTestPlanSimulatorNameOptionsPathsClosure?(scheme, testPlan, simulatorName, options, paths)
}
}

Expand Down

0 comments on commit ca86ef0

Please sign in to comment.