Change the XCFrameworkBuilder to use XcodeBuildController
This commit is contained in:
parent
22a5e70d82
commit
a25861b0a5
|
@ -1,74 +1,97 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
import XcbeautifyLib
|
||||
|
||||
protocol XcodeBuildControlling {
|
||||
/// Returns an observable to build the given project using xcodebuild.
|
||||
/// - Parameters:
|
||||
/// - target: The project or workspace to be built.
|
||||
/// - scheme: The scheme of the project that should be built.
|
||||
/// - clean: True if xcodebuild should clean the project before building.
|
||||
func build(_ target: XcodeBuildTarget, scheme: String, clean: Bool) -> Observable<SystemEvent<String>>
|
||||
}
|
||||
|
||||
public enum XcodeBuildTarget {
|
||||
/// The target is an Xcode project.
|
||||
case project(AbsolutePath)
|
||||
|
||||
/// The target is an Xcode workspace.
|
||||
case workspace(AbsolutePath)
|
||||
|
||||
/// Returns the arguments that need to be passed to xcodebuild to build this target.
|
||||
var xcodebuildArguments: [String] {
|
||||
switch self {
|
||||
case let .project(path):
|
||||
return ["-project", path.pathString]
|
||||
case let .workspace(path):
|
||||
return ["-workspace", path.pathString]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class XcodeBuildController: XcodeBuildControlling {
|
||||
// MARK: - Attributes
|
||||
|
||||
/// Instance to format xcodebuild output.
|
||||
private let parser: Parsing
|
||||
|
||||
public convenience init() {
|
||||
self.init(parser: Parser())
|
||||
}
|
||||
|
||||
init(parser: Parsing) {
|
||||
self.parser = parser
|
||||
}
|
||||
|
||||
func build(_ target: XcodeBuildTarget, scheme: String, clean: Bool = false) -> Observable<SystemEvent<String>> {
|
||||
var command = ["/usr/bin/xcrun", "xcodebuild", "-scheme", scheme]
|
||||
public func build(_ target: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
clean: Bool = false,
|
||||
arguments: XcodeBuildArgument...) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
var command = ["/usr/bin/xcrun", "xcodebuild"]
|
||||
|
||||
// Action
|
||||
if clean {
|
||||
command.append("clean")
|
||||
}
|
||||
command.append("build")
|
||||
|
||||
// Scheme
|
||||
command.append(contentsOf: ["-scheme", scheme])
|
||||
|
||||
// Target
|
||||
command.append(contentsOf: target.xcodebuildArguments)
|
||||
|
||||
// Arguments
|
||||
command.append(contentsOf: arguments.flatMap { $0.arguments })
|
||||
|
||||
return run(command: command)
|
||||
}
|
||||
|
||||
public func archive(_ target: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
clean: Bool,
|
||||
archivePath: AbsolutePath,
|
||||
arguments: XcodeBuildArgument...) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
var command = ["/usr/bin/xcrun", "xcodebuild"]
|
||||
|
||||
// Action
|
||||
command.append("build")
|
||||
if clean {
|
||||
command.append("clean")
|
||||
}
|
||||
command.append("archive")
|
||||
|
||||
// Scheme
|
||||
command.append(contentsOf: ["-scheme", scheme])
|
||||
|
||||
// Target
|
||||
command.append(contentsOf: target.xcodebuildArguments)
|
||||
|
||||
// Archive path
|
||||
command.append(contentsOf: ["-archivePath", archivePath.pathString])
|
||||
|
||||
// Arguments
|
||||
command.append(contentsOf: arguments.flatMap { $0.arguments })
|
||||
|
||||
return run(command: command)
|
||||
}
|
||||
|
||||
public func createXCFramework(frameworks: [AbsolutePath], output: AbsolutePath) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
var command = ["/usr/bin/xcrun", "xcodebuild", "-create-xcframework"]
|
||||
command.append(contentsOf: frameworks.flatMap { ["-framework", $0.pathString] })
|
||||
command.append(contentsOf: ["-output", output.pathString])
|
||||
return run(command: command)
|
||||
}
|
||||
|
||||
fileprivate func run(command: [String]) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
let colored = Environment.shared.shouldOutputBeColoured
|
||||
return System.shared.observable(command, verbose: true)
|
||||
.compactMap { event -> SystemEvent<String>? in
|
||||
return System.shared.observable(command, verbose: false)
|
||||
.compactMap { event -> SystemEvent<XcodeBuildOutput>? in
|
||||
switch event {
|
||||
case let .standardError(errorData):
|
||||
guard let line = String(data: errorData, encoding: .utf8) else { return nil }
|
||||
guard let formatedOutput = self.parser.parse(line: line, colored: colored) else { return nil }
|
||||
return .standardError(formatedOutput)
|
||||
let formatedOutput = self.parser.parse(line: line, colored: colored)
|
||||
return .standardError(XcodeBuildOutput(raw: line, formatted: formatedOutput))
|
||||
case let .standardOutput(outputData):
|
||||
guard let line = String(data: outputData, encoding: .utf8) else { return nil }
|
||||
guard let formatedOutput = self.parser.parse(line: line, colored: colored) else { return nil }
|
||||
return .standardOutput(formatedOutput)
|
||||
let formatedOutput = self.parser.parse(line: line, colored: colored)
|
||||
return .standardOutput(XcodeBuildOutput(raw: line, formatted: formatedOutput))
|
||||
}
|
||||
}
|
||||
.do(onNext: { event in
|
||||
Printer.shared.print("\(event.value)")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
|
||||
enum XCFrameworkBuilderError: FatalError {
|
||||
case nonFrameworkTarget(String)
|
||||
|
||||
/// Error type.
|
||||
var type: ErrorType {
|
||||
switch self {
|
||||
case .nonFrameworkTarget: return .abort
|
||||
}
|
||||
}
|
||||
|
||||
/// Error description.
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .nonFrameworkTarget(name):
|
||||
return "Can't generate an .xcframework from the target '\(name)' because it's not a framework target"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol XCFrameworkBuilding {
|
||||
/// It builds an xcframework for the given target.
|
||||
/// The target must have framework as product.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - workspacePath: Path to the generated .xcworkspace that contains the given target.
|
||||
/// - target: Target whose .xcframework will be generated.
|
||||
/// - Returns: Path to the compiled .xcframework.
|
||||
func build(workspacePath: AbsolutePath, target: Target) throws -> AbsolutePath
|
||||
|
||||
/// It builds an xcframework for the given target.
|
||||
/// The target must have framework as product.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - projectPath: Path to the generated .xcodeproj that contains the given target.
|
||||
/// - target: Target whose .xcframework will be generated.
|
||||
/// - Returns: Path to the compiled .xcframework.
|
||||
func build(projectPath: AbsolutePath, target: Target) throws -> AbsolutePath
|
||||
}
|
||||
|
||||
public final class XCFrameworkBuilder: XCFrameworkBuilding {
|
||||
// MARK: - Attributes
|
||||
|
||||
/// When true the builder outputs the output from xcodebuild.
|
||||
private let printOutput: Bool
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
/// Initializes the builder.
|
||||
/// - Parameter printOutput: When true the builder outputs the output from xcodebuild.
|
||||
public init(printOutput: Bool = true) {
|
||||
self.printOutput = printOutput
|
||||
}
|
||||
|
||||
// MARK: - XCFrameworkBuilding
|
||||
|
||||
public func build(workspacePath: AbsolutePath, target: Target) throws -> AbsolutePath {
|
||||
try build(arguments: ["-workspace", workspacePath.pathString], target: target)
|
||||
}
|
||||
|
||||
public func build(projectPath: AbsolutePath, target: Target) throws -> AbsolutePath {
|
||||
try build(arguments: ["-project", projectPath.pathString], target: target)
|
||||
}
|
||||
|
||||
// MARK: - Fileprivate
|
||||
|
||||
fileprivate func build(arguments: [String], target: Target) throws -> AbsolutePath {
|
||||
if target.product != .framework {
|
||||
throw XCFrameworkBuilderError.nonFrameworkTarget(target.name)
|
||||
}
|
||||
|
||||
// Create temporary directories
|
||||
let outputDirectory = try TemporaryDirectory(removeTreeOnDeinit: false)
|
||||
let derivedDataPath = try TemporaryDirectory(removeTreeOnDeinit: true)
|
||||
|
||||
Printer.shared.print(section: "Building .xcframework for \(target.name)")
|
||||
|
||||
// Build for the device
|
||||
let deviceArchivePath = derivedDataPath.path.appending(component: "device.xcarchive")
|
||||
var deviceArguments = xcodebuildCommand(scheme: target.name,
|
||||
destination: deviceDestination(platform: target.platform),
|
||||
sdk: target.platform.xcodeDeviceSDK,
|
||||
derivedDataPath: derivedDataPath.path)
|
||||
deviceArguments.append(contentsOf: ["-archivePath", deviceArchivePath.pathString])
|
||||
deviceArguments.append(contentsOf: arguments)
|
||||
Printer.shared.print(subsection: "Building \(target.name) for device")
|
||||
try runCommand(deviceArguments)
|
||||
|
||||
// Build for the simulator
|
||||
var simulatorArchivePath: AbsolutePath?
|
||||
if target.platform.hasSimulators {
|
||||
simulatorArchivePath = derivedDataPath.path.appending(component: "simulator.xcarchive")
|
||||
var simulatorArguments = xcodebuildCommand(scheme: target.name,
|
||||
destination: target.platform.xcodeSimulatorDestination!,
|
||||
sdk: target.platform.xcodeSimulatorSDK!,
|
||||
derivedDataPath: derivedDataPath.path)
|
||||
simulatorArguments.append(contentsOf: ["-archivePath", simulatorArchivePath!.pathString])
|
||||
simulatorArguments.append(contentsOf: arguments)
|
||||
Printer.shared.print(subsection: "Building \(target.name) for simulator")
|
||||
try runCommand(simulatorArguments)
|
||||
}
|
||||
|
||||
// Build the xcframework
|
||||
Printer.shared.print(subsection: "Exporting xcframework for \(target.name)")
|
||||
let xcframeworkPath = outputDirectory.path.appending(component: "\(target.productName).xcframework")
|
||||
let xcframeworkArguments = xcodebuildXcframeworkCommand(deviceArchivePath: deviceArchivePath,
|
||||
simulatorArchivePath: simulatorArchivePath,
|
||||
productName: target.productName,
|
||||
xcframeworkPath: xcframeworkPath)
|
||||
try runCommand(xcframeworkArguments)
|
||||
|
||||
return xcframeworkPath
|
||||
}
|
||||
|
||||
/// Runs the given command.
|
||||
/// - Parameter arguments: Command arguments.
|
||||
fileprivate func runCommand(_ arguments: [String]) throws {
|
||||
if printOutput {
|
||||
try System.shared.runAndPrint(arguments)
|
||||
} else {
|
||||
try System.shared.run(arguments)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the arguments that should be passed to xcodebuild to compile for a device on the given platform.
|
||||
/// - Parameter platform: Platform we are compiling for.
|
||||
fileprivate func deviceDestination(platform: Platform) -> String {
|
||||
switch platform {
|
||||
case .macOS: return "osx"
|
||||
default: return "generic/platform=\(platform.caseValue)"
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the xcodebuild command to generate the .xcframework from the device
|
||||
/// and the simulator frameworks.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - deviceArchivePath: Path to the archive that contains the framework for the device.
|
||||
/// - simulatorArchivePath: Path to the archive that contains the framework for the simulator.
|
||||
/// - productName: Name of the product.
|
||||
/// - xcframeworkPath: Path where the .xcframework should be exported to (e.g. /path/to/MyFeature.xcframework).
|
||||
fileprivate func xcodebuildXcframeworkCommand(deviceArchivePath: AbsolutePath,
|
||||
simulatorArchivePath: AbsolutePath?,
|
||||
productName: String,
|
||||
xcframeworkPath: AbsolutePath) -> [String] {
|
||||
var command = ["xcrun", "xcodebuild", "-create-xcframework"]
|
||||
command.append(contentsOf: ["-framework", deviceArchivePath.appending(RelativePath("Products/Library/Frameworks/\(productName).framework")).pathString])
|
||||
if let simulatorArchivePath = simulatorArchivePath {
|
||||
command.append(contentsOf: ["-framework", simulatorArchivePath.appending(RelativePath("Products/Library/Frameworks/\(productName).framework")).pathString])
|
||||
}
|
||||
command.append(contentsOf: ["-output", xcframeworkPath.pathString])
|
||||
return command
|
||||
}
|
||||
|
||||
/// It returns the xcodebuild command to archive the .framework.
|
||||
/// - Parameters:
|
||||
/// - scheme: Name of the scheme that archives the framework.
|
||||
/// - destination: Compilation destination.
|
||||
/// - sdk: Compilation SDK.
|
||||
/// - derivedDataPath: Derived data directory.
|
||||
fileprivate func xcodebuildCommand(scheme: String, destination: String, sdk: String, derivedDataPath: AbsolutePath) -> [String] {
|
||||
var command = ["xcrun", "xcodebuild", "archive"]
|
||||
command.append(contentsOf: ["-scheme", scheme.spm_shellEscaped()])
|
||||
command.append(contentsOf: ["-sdk", sdk])
|
||||
command.append(contentsOf: ["-destination='\(destination)'"])
|
||||
command.append(contentsOf: ["-derivedDataPath", derivedDataPath.pathString])
|
||||
// Without the BUILD_LIBRARY_FOR_DISTRIBUTION argument xcodebuild doesn't generate the .swiftinterface file
|
||||
command.append(contentsOf: ["SKIP_INSTALL=NO", "BUILD_LIBRARY_FOR_DISTRIBUTION=YES"])
|
||||
return command
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
|
||||
enum XCFrameworkBuilderError: FatalError {
|
||||
case nonFrameworkTarget(String)
|
||||
|
||||
/// Error type.
|
||||
var type: ErrorType {
|
||||
switch self {
|
||||
case .nonFrameworkTarget: return .abort
|
||||
}
|
||||
}
|
||||
|
||||
/// Error description.
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .nonFrameworkTarget(name):
|
||||
return "Can't generate an .xcframework from the target '\(name)' because it's not a framework target"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol XCFrameworkBuilding {
|
||||
/// Returns an observable build an xcframework for the given target.
|
||||
/// The target must have framework as product.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - workspacePath: Path to the generated .xcworkspace that contains the given target.
|
||||
/// - target: Target whose .xcframework will be generated.
|
||||
/// - Returns: Path to the compiled .xcframework.
|
||||
func build(workspacePath: AbsolutePath, target: Target) throws -> Observable<AbsolutePath>
|
||||
|
||||
/// Returns an observable to build an xcframework for the given target.
|
||||
/// The target must have framework as product.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - projectPath: Path to the generated .xcodeproj that contains the given target.
|
||||
/// - target: Target whose .xcframework will be generated.
|
||||
/// - Returns: Path to the compiled .xcframework.
|
||||
func build(projectPath: AbsolutePath, target: Target) throws -> Observable<AbsolutePath>
|
||||
}
|
||||
|
||||
public final class XCFrameworkBuilder: XCFrameworkBuilding {
|
||||
// MARK: - Attributes
|
||||
|
||||
/// Xcode build controller instance to run xcodebuild commands.
|
||||
private let xcodeBuildController: XcodeBuildControlling
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
/// Initializes the builder.
|
||||
/// - Parameter xcodeBuildController: Xcode build controller instance to run xcodebuild commands.
|
||||
public init(xcodeBuildController: XcodeBuildControlling) {
|
||||
self.xcodeBuildController = xcodeBuildController
|
||||
}
|
||||
|
||||
// MARK: - XCFrameworkBuilding
|
||||
|
||||
public func build(workspacePath: AbsolutePath, target: Target) throws -> Observable<AbsolutePath> {
|
||||
try build(.workspace(workspacePath), target: target)
|
||||
}
|
||||
|
||||
public func build(projectPath: AbsolutePath, target: Target) throws -> Observable<AbsolutePath> {
|
||||
try build(.project(projectPath), target: target)
|
||||
}
|
||||
|
||||
// MARK: - Fileprivate
|
||||
|
||||
fileprivate func build(_ projectTarget: XcodeBuildTarget, target: Target) throws -> Observable<AbsolutePath> {
|
||||
if target.product != .framework {
|
||||
throw XCFrameworkBuilderError.nonFrameworkTarget(target.name)
|
||||
}
|
||||
let scheme = target.name.spm_shellEscaped()
|
||||
|
||||
// Create temporary directories
|
||||
let outputDirectory = try TemporaryDirectory(removeTreeOnDeinit: false)
|
||||
let temporaryPath = try TemporaryDirectory(removeTreeOnDeinit: false)
|
||||
|
||||
Printer.shared.print(section: "Building .xcframework for \(target.name)")
|
||||
|
||||
// Build for the device
|
||||
// Without the BUILD_LIBRARY_FOR_DISTRIBUTION argument xcodebuild doesn't generate the .swiftinterface file
|
||||
let deviceArchivePath = temporaryPath.path.appending(component: "device.xcarchive")
|
||||
let deviceArchiveObservable = xcodeBuildController.archive(projectTarget,
|
||||
scheme: scheme,
|
||||
clean: true,
|
||||
archivePath: deviceArchivePath,
|
||||
arguments:
|
||||
.sdk(target.platform.xcodeDeviceSDK),
|
||||
.derivedDataPath(temporaryPath.path),
|
||||
.buildSetting("SKIP_INSTALL", "NO"),
|
||||
.buildSetting("BUILD_LIBRARY_FOR_DISTRIBUTION", "YES"))
|
||||
.do(onSubscribed: {
|
||||
Printer.shared.print(subsection: "Building \(target.name) for device")
|
||||
})
|
||||
|
||||
// Build for the simulator
|
||||
var simulatorArchiveObservable: Observable<SystemEvent<XcodeBuildOutput>>?
|
||||
var simulatorArchivePath: AbsolutePath?
|
||||
if target.platform.hasSimulators {
|
||||
simulatorArchivePath = temporaryPath.path.appending(component: "simulator.xcarchive")
|
||||
simulatorArchiveObservable = xcodeBuildController.archive(projectTarget,
|
||||
scheme: scheme,
|
||||
clean: false,
|
||||
archivePath: simulatorArchivePath!,
|
||||
arguments:
|
||||
.sdk(target.platform.xcodeSimulatorSDK!),
|
||||
.derivedDataPath(temporaryPath.path),
|
||||
.buildSetting("SKIP_INSTALL", "NO"),
|
||||
.buildSetting("BUILD_LIBRARY_FOR_DISTRIBUTION", "YES"))
|
||||
.do(onSubscribed: {
|
||||
Printer.shared.print(subsection: "Building \(target.name) for simulator")
|
||||
})
|
||||
}
|
||||
|
||||
// Build the xcframework
|
||||
var frameworkpaths = [frameworkPath(fromArchivePath: deviceArchivePath, productName: target.productName)]
|
||||
if let simulatorArchivePath = simulatorArchivePath {
|
||||
frameworkpaths.append(frameworkPath(fromArchivePath: simulatorArchivePath, productName: target.productName))
|
||||
}
|
||||
let xcframeworkPath = outputDirectory.path.appending(component: "\(target.productName).xcframework")
|
||||
let xcframeworkObservable = xcodeBuildController.createXCFramework(frameworks: frameworkpaths, output: xcframeworkPath)
|
||||
.do(onSubscribed: {
|
||||
Printer.shared.print(subsection: "Exporting xcframework for \(target.platform.caseValue)")
|
||||
})
|
||||
|
||||
return deviceArchiveObservable
|
||||
.concat(simulatorArchiveObservable ?? Observable.empty())
|
||||
.concat(xcframeworkObservable)
|
||||
.ignoreElements()
|
||||
.andThen(Observable.just(xcframeworkPath))
|
||||
.do(afterCompleted: {
|
||||
try FileHandler.shared.delete(temporaryPath.path)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the path to the framework inside the archive.
|
||||
/// - Parameters:
|
||||
/// - archivePath: Path to the .xcarchive.
|
||||
/// - productName: Product name.
|
||||
fileprivate func frameworkPath(fromArchivePath archivePath: AbsolutePath, productName: String) -> AbsolutePath {
|
||||
archivePath.appending(RelativePath("Products/Library/Frameworks/\(productName).framework"))
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import TuistCache
|
||||
import TuistCore
|
||||
import TuistGalaxy
|
||||
|
||||
public final class MockCacheStorage: CacheStoraging {
|
||||
var existsStub: ((String) -> Bool)?
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import TuistCache
|
||||
import TuistCore
|
||||
import TuistGalaxy
|
||||
|
||||
public final class MockXCFrameworkBuilder: XCFrameworkBuilding {
|
||||
var buildProjectArgs: [(projectPath: AbsolutePath, target: Target)] = []
|
||||
|
@ -9,21 +10,21 @@ public final class MockXCFrameworkBuilder: XCFrameworkBuilding {
|
|||
var buildProjectStub: AbsolutePath?
|
||||
var buildWorkspaceStub: AbsolutePath?
|
||||
|
||||
public func build(projectPath: AbsolutePath, target: Target) throws -> AbsolutePath {
|
||||
public func build(projectPath: AbsolutePath, target: Target) throws -> Observable<AbsolutePath> {
|
||||
buildProjectArgs.append((projectPath: projectPath, target: target))
|
||||
if let buildProjectStub = buildProjectStub {
|
||||
return buildProjectStub
|
||||
return Observable.just(buildProjectStub)
|
||||
} else {
|
||||
return AbsolutePath.root
|
||||
return Observable.just(AbsolutePath.root)
|
||||
}
|
||||
}
|
||||
|
||||
public func build(workspacePath: AbsolutePath, target: Target) throws -> AbsolutePath {
|
||||
public func build(workspacePath: AbsolutePath, target: Target) throws -> Observable<AbsolutePath> {
|
||||
buildWorkspaceArgs.append((workspacePath: workspacePath, target: target))
|
||||
if let buildWorkspaceStub = buildWorkspaceStub {
|
||||
return buildWorkspaceStub
|
||||
return Observable.just(buildWorkspaceStub)
|
||||
} else {
|
||||
return AbsolutePath.root
|
||||
return Observable.just(AbsolutePath.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import Foundation
|
||||
import RxSwift
|
||||
import TuistSupport
|
||||
|
||||
public extension Observable where Element == SystemEvent<XcodeBuildOutput> {
|
||||
func printFormattedOutput() -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
self.do(onNext: { event in
|
||||
switch event {
|
||||
case let .standardError(error):
|
||||
Printer.shared.print(errorMessage: "\(error.formatted ?? error.raw)")
|
||||
case let .standardOutput(output):
|
||||
Printer.shared.print("\(output.formatted ?? output.raw)")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
|
||||
/// It represents arguments that can be passed to the xcodebuild command.
|
||||
public enum XcodeBuildArgument: Equatable, CustomStringConvertible {
|
||||
/// Use SDK as the name or path of the base SDK when building the project
|
||||
case sdk(String)
|
||||
|
||||
/// Use the destination described by DESTINATIONSPECIFIER (a comma-separated set of key=value pairs describing the destination to use)
|
||||
case destination(String)
|
||||
|
||||
/// Specifies the directory where build products and other derived data will go.
|
||||
case derivedDataPath(AbsolutePath)
|
||||
|
||||
/// To override build settings.
|
||||
case buildSetting(String, String)
|
||||
|
||||
/// It returns the bash arguments that represent this xcodebuild argument.
|
||||
public var arguments: [String] {
|
||||
switch self {
|
||||
case let .sdk(sdk):
|
||||
return ["-sdk", sdk]
|
||||
case let .destination(destination):
|
||||
return ["-destination", "\(destination)"]
|
||||
case let .derivedDataPath(path):
|
||||
return ["-derivedDataPath", path.pathString]
|
||||
case let .buildSetting(key, value):
|
||||
return ["\(key)=\(value.spm_shellEscaped())"]
|
||||
}
|
||||
}
|
||||
|
||||
/// The argument's description.
|
||||
public var description: String {
|
||||
switch self {
|
||||
case let .sdk(sdk):
|
||||
return "Xcodebuild's SDK argument: \(sdk)"
|
||||
case let .destination(destination):
|
||||
return "Xcodebuild's destination argument: \(destination)"
|
||||
case let .derivedDataPath(path):
|
||||
return "Xcodebuild's derivedDataPath argument: \(path.pathString)"
|
||||
case let .buildSetting(key, value):
|
||||
return "Xcodebuild's additional build setting: \(key)=\(value)"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import TuistSupport
|
||||
|
||||
public protocol XcodeBuildControlling {
|
||||
/// Returns an observable to build the given project using xcodebuild.
|
||||
/// - Parameters:
|
||||
/// - target: The project or workspace to be built.
|
||||
/// - scheme: The scheme of the project that should be built.
|
||||
/// - clean: True if xcodebuild should clean the project before building.
|
||||
/// - arguments: Extra xcodebuild arguments.
|
||||
func build(_ target: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
clean: Bool,
|
||||
arguments: XcodeBuildArgument...) -> Observable<SystemEvent<XcodeBuildOutput>>
|
||||
|
||||
/// Returns an observable that archives the given project using xcodebuild.
|
||||
/// - Parameters:
|
||||
/// - target: The project or workspace to be archived.
|
||||
/// - scheme: The scheme of the project that should be archived.
|
||||
/// - clean: True if xcodebuild should clean the project before archiving.
|
||||
/// - archivePath: Path where the archive will be exported (with extension .xcarchive)
|
||||
/// - arguments: Extra xcodebuild arguments.
|
||||
func archive(_ target: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
clean: Bool,
|
||||
archivePath: AbsolutePath,
|
||||
arguments: XcodeBuildArgument...) -> Observable<SystemEvent<XcodeBuildOutput>>
|
||||
|
||||
/// Creates an .xcframework combining the list of given frameworks.
|
||||
/// - Parameters:
|
||||
/// - frameworks: Frameworks to be combined.
|
||||
/// - output: Path to the output .xcframework.
|
||||
func createXCFramework(frameworks: [AbsolutePath], output: AbsolutePath) -> Observable<SystemEvent<XcodeBuildOutput>>
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import Foundation
|
||||
|
||||
/// It represents an output from the xcodebuild command.
|
||||
public struct XcodeBuildOutput: Equatable {
|
||||
/// Output as xcodebuild returns it.
|
||||
let raw: String
|
||||
|
||||
/// Beautified version of the raw output.
|
||||
let formatted: String?
|
||||
|
||||
/// Initializes the output with its arguments.
|
||||
/// - Parameters:
|
||||
/// - raw: Output as xcodebuild returns it.
|
||||
/// - formatted: Beautified version of the raw output.
|
||||
public init(raw: String, formatted: String?) {
|
||||
self.raw = raw
|
||||
self.formatted = formatted
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
|
||||
public enum XcodeBuildTarget {
|
||||
/// The target is an Xcode project.
|
||||
case project(AbsolutePath)
|
||||
|
||||
/// The target is an Xcode workspace.
|
||||
case workspace(AbsolutePath)
|
||||
|
||||
/// Returns the arguments that need to be passed to xcodebuild to build this target.
|
||||
public var xcodebuildArguments: [String] {
|
||||
switch self {
|
||||
case let .project(path):
|
||||
return ["-project", path.pathString]
|
||||
case let .workspace(path):
|
||||
return ["-workspace", path.pathString]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ extension Platform {
|
|||
public var xcodeSimulatorDestination: String? {
|
||||
switch self {
|
||||
case .macOS: return nil
|
||||
default: return "\(caseValue) Simulator"
|
||||
default: return "platform=\(caseValue) Simulator"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
@testable import TuistSupportTesting
|
||||
|
||||
final class MockXcodeBuildController: XcodeBuildControlling {
|
||||
var buildStub: ((XcodeBuildTarget, String, Bool, [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>)?
|
||||
|
||||
func build(_ target: XcodeBuildTarget, scheme: String, clean: Bool, arguments: XcodeBuildArgument...) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
if let buildStub = buildStub {
|
||||
return buildStub(target, scheme, clean, arguments)
|
||||
} else {
|
||||
return Observable.error(TestError("\(String(describing: MockXcodeBuildController.self)) received an unexpected call to build"))
|
||||
}
|
||||
}
|
||||
|
||||
var archiveStub: ((XcodeBuildTarget, String, Bool, AbsolutePath, [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>)?
|
||||
func archive(_ target: XcodeBuildTarget, scheme: String, clean: Bool, archivePath: AbsolutePath, arguments: XcodeBuildArgument...) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
if let archiveStub = archiveStub {
|
||||
return archiveStub(target, scheme, clean, archivePath, arguments)
|
||||
} else {
|
||||
return Observable.error(TestError("\(String(describing: MockXcodeBuildController.self)) received an unexpected call to archive"))
|
||||
}
|
||||
}
|
||||
|
||||
var createXCFrameworkStub: (([AbsolutePath], AbsolutePath) -> Observable<SystemEvent<XcodeBuildOutput>>)?
|
||||
func createXCFramework(frameworks: [AbsolutePath], output: AbsolutePath) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||
if let createXCFrameworkStub = createXCFrameworkStub {
|
||||
return createXCFrameworkStub(frameworks, output)
|
||||
} else {
|
||||
return Observable.error(TestError("\(String(describing: MockXcodeBuildController.self)) received an unexpected call to createXCFramework"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,9 @@ import Basic
|
|||
import Foundation
|
||||
import RxBlocking
|
||||
import RxSwift
|
||||
import TuistAutomation
|
||||
import TuistCache
|
||||
import TuistCore
|
||||
import TuistGalaxy
|
||||
import TuistGenerator
|
||||
import TuistLoader
|
||||
import TuistSupport
|
||||
|
@ -32,7 +33,7 @@ final class CacheController: CacheControlling {
|
|||
|
||||
init(generator: Generating = Generator(),
|
||||
manifestLoader: ManifestLoading = ManifestLoader(),
|
||||
xcframeworkBuilder: XCFrameworkBuilding = XCFrameworkBuilder(printOutput: false),
|
||||
xcframeworkBuilder: XCFrameworkBuilding = XCFrameworkBuilder(xcodeBuildController: XcodeBuildController()),
|
||||
cache: CacheStoraging = Cache(),
|
||||
graphContentHasher: GraphContentHashing = GraphContentHasher()) {
|
||||
self.generator = generator
|
||||
|
@ -50,21 +51,21 @@ final class CacheController: CacheControlling {
|
|||
Printer.shared.print(section: "Hashing cacheable frameworks")
|
||||
let targets: [TargetNode: String] = try graphContentHasher.contentHashes(for: graph)
|
||||
.filter { target, hash in
|
||||
if let exists = try self.cache.exists(hash: hash).toBlocking().first(), exists {
|
||||
Printer.shared.print("The target \(.bold(.raw(target.name))) with hash \(.bold(.raw(hash))) is already in the cache. Skipping...")
|
||||
return false
|
||||
if let exists = try self.cache.exists(hash: hash).toBlocking().first(), exists {
|
||||
Printer.shared.print("The target \(.bold(.raw(target.name))) with hash \(.bold(.raw(hash))) is already in the cache. Skipping...")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var completables: [Completable] = []
|
||||
try targets.forEach { target, hash in
|
||||
// Build targets sequentially
|
||||
let xcframeworkPath: AbsolutePath!
|
||||
if path.extension == "xcworkspace" {
|
||||
xcframeworkPath = try self.xcframeworkBuilder.build(workspacePath: path, target: target.target)
|
||||
xcframeworkPath = try self.xcframeworkBuilder.build(workspacePath: path, target: target.target).toBlocking().single()
|
||||
} else {
|
||||
xcframeworkPath = try self.xcframeworkBuilder.build(projectPath: path, target: target.target)
|
||||
xcframeworkPath = try self.xcframeworkBuilder.build(projectPath: path, target: target.target).toBlocking().single()
|
||||
}
|
||||
|
||||
// Create tasks to cache and delete the xcframeworks asynchronously
|
||||
|
|
|
@ -404,6 +404,7 @@ public final class System: Systeming {
|
|||
exitStatus: result.exitStatus,
|
||||
output: result.output,
|
||||
stderrOutput: result.stderrOutput.map { _ in errorData })
|
||||
|
||||
try result.throwIfErrored()
|
||||
observer.onCompleted()
|
||||
} catch {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Foundation
|
||||
import RxSwift
|
||||
import TuistSupport
|
||||
@testable import TuistEnvKit
|
||||
|
||||
public final class MockURLSessionScheduler: TuistSupport.URLSessionScheduling {
|
||||
private var stubs: [URLRequest: (error: URLError?, data: Data?)] = [:]
|
||||
|
|
|
@ -478,6 +478,7 @@
|
|||
B9023E0B239BCDA200666BE6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
|
@ -493,7 +494,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.iOS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SKIP_INSTALL = NO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
@ -503,6 +504,7 @@
|
|||
B9023E0C239BCDA200666BE6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
|
@ -518,7 +520,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.iOS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SKIP_INSTALL = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
@ -635,6 +637,7 @@
|
|||
B9023E38239BCE2D00666BE6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
|
@ -653,7 +656,6 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.macOS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
|
@ -662,6 +664,7 @@
|
|||
B9023E39239BCE2D00666BE6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
|
@ -680,7 +683,6 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = io.tuist.macOS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -1,40 +1,13 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxBlocking
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
import XCTest
|
||||
|
||||
@testable import TuistAutomation
|
||||
@testable import TuistSupportTesting
|
||||
|
||||
final class XcodeBuildTargetTests: TuistUnitTestCase {
|
||||
func test_xcodebuildArguments_returns_the_right_arguments_when_project() throws {
|
||||
// Given
|
||||
let path = try temporaryPath()
|
||||
let xcodeprojPath = path.appending(component: "Project.xcodeproj")
|
||||
let subject = XcodeBuildTarget.project(xcodeprojPath)
|
||||
|
||||
// When
|
||||
let got = subject.xcodebuildArguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-project", xcodeprojPath.pathString])
|
||||
}
|
||||
|
||||
func test_xcodebuildArguments_returns_the_right_arguments_when_workspace() throws {
|
||||
// Given
|
||||
let path = try temporaryPath()
|
||||
let xcworkspacePath = path.appending(component: "Project.xcworkspace")
|
||||
let subject = XcodeBuildTarget.workspace(xcworkspacePath)
|
||||
|
||||
// When
|
||||
let got = subject.xcodebuildArguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-workspace", xcworkspacePath.pathString])
|
||||
}
|
||||
}
|
||||
|
||||
private final class MockParser: Parsing {
|
||||
var parseStub: ((String, Bool) -> String?)?
|
||||
|
||||
|
@ -70,7 +43,7 @@ final class XcodeBuildControllerTests: TuistUnitTestCase {
|
|||
|
||||
var command = ["/usr/bin/xcrun", "xcodebuild", "-scheme", scheme]
|
||||
command.append(contentsOf: target.xcodebuildArguments)
|
||||
command.append(contentsOf: ["build", "clean"])
|
||||
command.append(contentsOf: ["clean", "build"])
|
||||
|
||||
system.succeedCommand(command, output: "output")
|
||||
var parseCalls: [(String, Bool)] = []
|
||||
|
@ -91,7 +64,7 @@ final class XcodeBuildControllerTests: TuistUnitTestCase {
|
|||
|
||||
switch events {
|
||||
case let .completed(output):
|
||||
XCTAssertEqual(output, [.standardOutput("formated-output")])
|
||||
XCTAssertEqual(output, [.standardOutput(XcodeBuildOutput(raw: "output", formatted: "formated-output"))])
|
||||
case .failed:
|
||||
XCTFail("The command was not expected to fail")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import XCTest
|
||||
@testable import TuistCache
|
||||
@testable import TuistSupportTesting
|
||||
|
||||
final class XCFrameworkBuilderErrorTests: TuistUnitTestCase {
|
||||
func test_type_when_nonFrameworkTarget() {
|
||||
// Given
|
||||
let subject = XCFrameworkBuilderError.nonFrameworkTarget("App")
|
||||
|
||||
// When
|
||||
let got = subject.type
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, .abort)
|
||||
}
|
||||
|
||||
func test_description_when_nonFrameworkTarget() {
|
||||
// Given
|
||||
let subject = XCFrameworkBuilderError.nonFrameworkTarget("App")
|
||||
|
||||
// When
|
||||
let got = subject.description
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, "Can't generate an .xcframework from the target 'App' because it's not a framework target")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import XCTest
|
||||
@testable import TuistCore
|
||||
@testable import TuistSupportTesting
|
||||
|
||||
final class XcodeBuildArgumentTests: TuistUnitTestCase {
|
||||
func test_arguments_returns_the_right_value_when_sdk() {
|
||||
// Given
|
||||
let subject = XcodeBuildArgument.sdk("sdk")
|
||||
|
||||
// When
|
||||
let got = subject.arguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-sdk", "sdk"])
|
||||
}
|
||||
|
||||
func test_arguments_returns_the_right_value_when_destination() {
|
||||
// Given
|
||||
let subject = XcodeBuildArgument.destination("destination")
|
||||
|
||||
// When
|
||||
let got = subject.arguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-destination", "destination"])
|
||||
}
|
||||
|
||||
func test_arguments_returns_the_right_value_when_derivedDataPath() {
|
||||
// Given
|
||||
let path = AbsolutePath.root
|
||||
let subject = XcodeBuildArgument.derivedDataPath(path)
|
||||
|
||||
// When
|
||||
let got = subject.arguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-derivedDataPath", path.pathString])
|
||||
}
|
||||
|
||||
func test_arguments_returns_the_right_value_when_buildSetting() {
|
||||
// Given
|
||||
let subject = XcodeBuildArgument.buildSetting("key", "value")
|
||||
|
||||
// When
|
||||
let got = subject.arguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["key=value"])
|
||||
}
|
||||
|
||||
func test_arguments_returns_the_right_value_when_buildSetting_with_spaces() {
|
||||
// Given
|
||||
let subject = XcodeBuildArgument.buildSetting("key", "value with spaces")
|
||||
|
||||
// When
|
||||
let got = subject.arguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["key=\'value with spaces\'"])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import RxBlocking
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
import XCTest
|
||||
|
||||
@testable import TuistAutomation
|
||||
@testable import TuistSupportTesting
|
||||
|
||||
final class XcodeBuildTargetTests: TuistUnitTestCase {
|
||||
func test_xcodebuildArguments_returns_the_right_arguments_when_project() throws {
|
||||
// Given
|
||||
let path = try temporaryPath()
|
||||
let xcodeprojPath = path.appending(component: "Project.xcodeproj")
|
||||
let subject = XcodeBuildTarget.project(xcodeprojPath)
|
||||
|
||||
// When
|
||||
let got = subject.xcodebuildArguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-project", xcodeprojPath.pathString])
|
||||
}
|
||||
|
||||
func test_xcodebuildArguments_returns_the_right_arguments_when_workspace() throws {
|
||||
// Given
|
||||
let path = try temporaryPath()
|
||||
let xcworkspacePath = path.appending(component: "Project.xcworkspace")
|
||||
let subject = XcodeBuildTarget.workspace(xcworkspacePath)
|
||||
|
||||
// When
|
||||
let got = subject.xcodebuildArguments
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(got, ["-workspace", xcworkspacePath.pathString])
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import SPMUtility
|
||||
import TuistAutomation
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
import XCTest
|
||||
|
@ -15,7 +16,7 @@ final class XCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
override func setUp() {
|
||||
super.setUp()
|
||||
plistDecoder = PropertyListDecoder()
|
||||
subject = XCFrameworkBuilder(printOutput: false)
|
||||
subject = XCFrameworkBuilder(xcodeBuildController: XcodeBuildController())
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
@ -31,7 +32,7 @@ final class XCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let target = Target.test(name: "iOS", platform: .iOS, product: .framework, productName: "iOS")
|
||||
|
||||
// When
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target)
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target).toBlocking().single()
|
||||
let infoPlist = try self.infoPlist(xcframeworkPath: xcframeworkPath)
|
||||
|
||||
// Then
|
||||
|
@ -55,7 +56,7 @@ final class XCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let target = Target.test(name: "macOS", platform: .macOS, product: .framework, productName: "macOS")
|
||||
|
||||
// When
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target)
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target).toBlocking().single()
|
||||
let infoPlist = try self.infoPlist(xcframeworkPath: xcframeworkPath)
|
||||
|
||||
// Then
|
||||
|
@ -77,7 +78,7 @@ final class XCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let target = Target.test(name: "tvOS", platform: .tvOS, product: .framework, productName: "tvOS")
|
||||
|
||||
// When
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target)
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target).toBlocking().single()
|
||||
let infoPlist = try self.infoPlist(xcframeworkPath: xcframeworkPath)
|
||||
|
||||
// Then
|
||||
|
@ -101,7 +102,7 @@ final class XCFrameworkBuilderIntegrationTests: TuistTestCase {
|
|||
let target = Target.test(name: "watchOS", platform: .watchOS, product: .framework, productName: "watchOS")
|
||||
|
||||
// When
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target)
|
||||
let xcframeworkPath = try subject.build(projectPath: projectPath, target: target).toBlocking().single()
|
||||
let infoPlist = try self.infoPlist(xcframeworkPath: xcframeworkPath)
|
||||
|
||||
// Then
|
|
@ -1,15 +1,14 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
import TuistSupport
|
||||
import TuistCacheTesting
|
||||
import TuistCore
|
||||
import TuistSupportTesting
|
||||
import TuistCoreTesting
|
||||
import TuistGalaxyTesting
|
||||
import TuistSupport
|
||||
import TuistSupportTesting
|
||||
import XCTest
|
||||
|
||||
@testable import TuistKit
|
||||
|
||||
final class CacheControllerTests: XCTestCase {
|
||||
|
||||
var generator: MockGenerator!
|
||||
var xcframeworkBuilder: MockXCFrameworkBuilder!
|
||||
var cache: MockCacheStorage!
|
||||
|
|
Loading…
Reference in New Issue