Add ReactiveTask (#109)

This commit is contained in:
Pedro Piñera Buendía 2018-08-26 20:59:52 +02:00 committed by GitHub
parent 0ccf28e8d6
commit dc198b42b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 27 deletions

View File

@ -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"]),

View File

@ -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)
}
}
}

View File

@ -5,7 +5,7 @@ enum Platform: String {
case macOS
case watchOS
case tvOS
init?(string: String) {
switch string.lowercased() {
case "ios":

View File

@ -90,13 +90,13 @@ class InfoPlistProvisioner: InfoPlistProvisioning {
"UIInterfaceOrientationLandscapeRight",
]
}
// tvOS application
if product == .app && platform == .tvOS {
base["LSRequiresIPhoneOS"] = true
base["UIRequiredDeviceCapabilities"] = ["arm64"]
}
return base
}
}

View File

@ -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)

View File

@ -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)