Add ReactiveTask (#109)
This commit is contained in:
parent
0ccf28e8d6
commit
dc198b42b8
|
@ -14,11 +14,12 @@ let package = Package(
|
|||
dependencies: [
|
||||
.package(url: "https://github.com/tuist/xcodeproj.git", .revision("549d67686d90ef8e45fccdca147682f185af2ad0")),
|
||||
.package(url: "https://github.com/apple/swift-package-manager.git", .revision("3e71e57db41ebb32ccec1841a7e26c428a9c08c5")),
|
||||
.package(url: "https://github.com/Carthage/ReactiveTask.git", .revision("57d221b82270b05380d66117e07ac4069b78a4e9")),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "TuistCore",
|
||||
dependencies: ["Utility"]),
|
||||
dependencies: ["Utility", "ReactiveTask"]),
|
||||
.target(
|
||||
name: "TuistCoreTesting",
|
||||
dependencies: ["TuistCore"]),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import ReactiveSwift
|
||||
import ReactiveTask
|
||||
|
||||
public protocol Systeming {
|
||||
func capture(_ args: [String], verbose: Bool) throws -> SystemResult
|
||||
|
@ -31,15 +33,6 @@ public struct SystemError: FatalError, Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
extension ProcessResult.ExitStatus {
|
||||
var exitcode: Int32 {
|
||||
switch self {
|
||||
case let .signalled(exitcode): return exitcode
|
||||
case let .terminated(exitcode): return exitcode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SystemResult {
|
||||
public let stdout: String
|
||||
public let stderror: String
|
||||
|
@ -80,8 +73,11 @@ public final class System: Systeming {
|
|||
public func capture(_ args: [String], verbose _: Bool = false) throws -> SystemResult {
|
||||
precondition(args.count >= 1, "Invalid number of argumentss")
|
||||
let arguments = ["/bin/bash", "-c", "\(args.map({ $0.shellEscaped() }).joined(separator: " "))"]
|
||||
let result = try Process.popen(arguments: arguments)
|
||||
return try SystemResult(stdout: result.utf8Output(), stderror: result.utf8stderrOutput(), exitcode: result.exitStatus.exitcode)
|
||||
if let output = task(arguments).single() {
|
||||
return try output.dematerialize()
|
||||
} else {
|
||||
throw SystemError(stderror: "Error running command: \(args.joined(separator: " "))", exitcode: 1)
|
||||
}
|
||||
}
|
||||
|
||||
public func popen(_ args: String..., verbose: Bool = false) throws {
|
||||
|
@ -91,8 +87,37 @@ public final class System: Systeming {
|
|||
public func popen(_ args: [String], verbose _: Bool = false) throws {
|
||||
precondition(args.count >= 1, "Invalid number of arguments")
|
||||
let arguments = ["/bin/bash", "-c", "\(args.map({ $0.shellEscaped() }).joined(separator: " "))"]
|
||||
let process = Process(arguments: arguments, redirectOutput: false)
|
||||
try process.launch()
|
||||
try process.waitUntilExit()
|
||||
_ = task(arguments, print: true).wait()
|
||||
}
|
||||
|
||||
// MARK: - Fileprivate
|
||||
|
||||
fileprivate func task(_ args: [String], print: Bool = false) -> SignalProducer<SystemResult, SystemError> {
|
||||
let task = Task(args.first!, arguments: Array(args.dropFirst()), workingDirectoryPath: nil, environment: nil)
|
||||
return task.launch()
|
||||
.on(value: {
|
||||
if !print { return }
|
||||
switch $0 {
|
||||
case let .standardError(error):
|
||||
FileHandle.standardError.write(error)
|
||||
case let .standardOutput(output):
|
||||
FileHandle.standardOutput.write(output)
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
.ignoreTaskData()
|
||||
.mapError { (error: TaskError) -> SystemError in
|
||||
switch error {
|
||||
case let TaskError.posixError(code):
|
||||
return SystemError(stderror: nil, exitcode: code)
|
||||
case let TaskError.shellTaskFailed(_, code, standardError):
|
||||
return SystemError(stderror: standardError, exitcode: code)
|
||||
}
|
||||
}
|
||||
.map { data in
|
||||
let stdout = String(data: data, encoding: .utf8)!
|
||||
return SystemResult(stdout: stdout, stderror: "", exitcode: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ enum Platform: String {
|
|||
case macOS
|
||||
case watchOS
|
||||
case tvOS
|
||||
|
||||
|
||||
init?(string: String) {
|
||||
switch string.lowercased() {
|
||||
case "ios":
|
||||
|
|
|
@ -90,13 +90,13 @@ class InfoPlistProvisioner: InfoPlistProvisioning {
|
|||
"UIInterfaceOrientationLandscapeRight",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// tvOS application
|
||||
if product == .app && platform == .tvOS {
|
||||
base["LSRequiresIPhoneOS"] = true
|
||||
base["UIRequiredDeviceCapabilities"] = ["arm64"]
|
||||
}
|
||||
|
||||
|
||||
return base
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ final class InitCommandTests: XCTestCase {
|
|||
XCTAssertEqual(playgroundGenerator.generateArgs.first?.2, .iOS)
|
||||
XCTAssertEqual(playgroundGenerator.generateArgs.first?.3, PlaygroundGenerator.defaultContent())
|
||||
}
|
||||
|
||||
|
||||
func test_run_when_tvos_application() throws {
|
||||
let result = try parser.parse(["init", "--product", "application", "--platform", "tvos"])
|
||||
try subject.run(with: result)
|
||||
|
@ -118,7 +118,7 @@ final class InitCommandTests: XCTestCase {
|
|||
XCTAssertTrue(fileHandler.exists(fileHandler.currentPath.appending(RelativePath("Sources/AppDelegate.swift"))))
|
||||
XCTAssertTrue(fileHandler.exists(fileHandler.currentPath.appending(RelativePath("Tests/\(name)Tests.swift"))))
|
||||
XCTAssertEqual(printer.printSuccessArgs.first, "Project generated at path \(fileHandler.currentPath.asString).")
|
||||
|
||||
|
||||
let playgroundsPath = fileHandler.currentPath.appending(component: "Playgrounds")
|
||||
XCTAssertTrue(fileHandler.exists(playgroundsPath))
|
||||
XCTAssertEqual(playgroundGenerator.generateCallCount, 1)
|
||||
|
@ -167,7 +167,7 @@ final class InitCommandTests: XCTestCase {
|
|||
XCTAssertEqual(playgroundGenerator.generateArgs.first?.2, .iOS)
|
||||
XCTAssertEqual(playgroundGenerator.generateArgs.first?.3, PlaygroundGenerator.defaultContent())
|
||||
}
|
||||
|
||||
|
||||
func test_run_when_tvos_framework() throws {
|
||||
let result = try parser.parse(["init", "--product", "framework", "--platform", "tvos"])
|
||||
try subject.run(with: result)
|
||||
|
@ -178,7 +178,7 @@ final class InitCommandTests: XCTestCase {
|
|||
XCTAssertTrue(fileHandler.exists(fileHandler.currentPath.appending(RelativePath("Sources/\(name).swift"))))
|
||||
XCTAssertTrue(fileHandler.exists(fileHandler.currentPath.appending(RelativePath("Tests/\(name)Tests.swift"))))
|
||||
XCTAssertEqual(printer.printSuccessArgs.first, "Project generated at path \(fileHandler.currentPath.asString).")
|
||||
|
||||
|
||||
let playgroundsPath = fileHandler.currentPath.appending(component: "Playgrounds")
|
||||
XCTAssertTrue(fileHandler.exists(playgroundsPath))
|
||||
XCTAssertEqual(playgroundGenerator.generateCallCount, 1)
|
||||
|
|
|
@ -44,7 +44,7 @@ final class InfoPlistProvisionerTests: XCTestCase {
|
|||
XCTAssertEqual(NSDictionary(dictionary: got),
|
||||
NSDictionary(dictionary: expected))
|
||||
}
|
||||
|
||||
|
||||
func test_generate_when_tvos_app() throws {
|
||||
let got = try provision(platform: .tvOS, product: .app)
|
||||
let expected: [String: Any] = [
|
||||
|
@ -97,7 +97,7 @@ final class InfoPlistProvisionerTests: XCTestCase {
|
|||
"CFBundleVersion": "$(CURRENT_PROJECT_VERSION)",
|
||||
"CFBundlePackageType": "FMWK",
|
||||
"NSPrincipalClass": "",
|
||||
]
|
||||
]
|
||||
XCTAssertEqual(NSDictionary(dictionary: got),
|
||||
NSDictionary(dictionary: expected))
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ final class InfoPlistProvisionerTests: XCTestCase {
|
|||
"CFBundleVersion": "$(CURRENT_PROJECT_VERSION)",
|
||||
"CFBundlePackageType": "FMWK",
|
||||
"NSPrincipalClass": "",
|
||||
]
|
||||
]
|
||||
XCTAssertEqual(NSDictionary(dictionary: got),
|
||||
NSDictionary(dictionary: expected))
|
||||
}
|
||||
|
@ -133,11 +133,11 @@ final class InfoPlistProvisionerTests: XCTestCase {
|
|||
"CFBundleVersion": "$(CURRENT_PROJECT_VERSION)",
|
||||
"CFBundlePackageType": "FMWK",
|
||||
"NSPrincipalClass": "",
|
||||
]
|
||||
]
|
||||
XCTAssertEqual(NSDictionary(dictionary: got),
|
||||
NSDictionary(dictionary: expected))
|
||||
}
|
||||
|
||||
|
||||
func provision(platform: Platform, product: Product) throws -> [String: AnyHashable] {
|
||||
try subject.generate(path: path, platform: platform, product: product)
|
||||
let data = try Data(contentsOf: path.url)
|
||||
|
|
Loading…
Reference in New Issue