Use libSwiftPM instead of custom model types (#194)

* Use libSwiftPM instead of custom model types #120

* Update SwiftPM dependencies

* Add wrapper to handle discrepancy between encoded and decoded Manifest

* Try to fix 5.2 dependency resolving

* Try to fix 5.2 dependency resolving

Co-authored-by: Max Desiatov <max@desiatov.com>
This commit is contained in:
yonihemi 2021-01-20 23:31:16 +08:00 committed by GitHub
parent 9753432846
commit 4ef7b5339e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 315 additions and 204 deletions

View File

@ -82,6 +82,24 @@
"version": "1.1.3" "version": "1.1.3"
} }
}, },
{
"package": "swift-driver",
"repositoryURL": "https://github.com/apple/swift-driver.git",
"state": {
"branch": "release/5.4",
"revision": "b71abc6384dcf3e765854e2b28b83c618d3726a6",
"version": null
}
},
{
"package": "llbuild",
"repositoryURL": "https://github.com/apple/swift-llbuild.git",
"state": {
"branch": "release/5.4",
"revision": "eb56a00ed9dfd62c2ce4ec86183ff0bc0afda997",
"version": null
}
},
{ {
"package": "swift-log", "package": "swift-log",
"repositoryURL": "https://github.com/apple/swift-log.git", "repositoryURL": "https://github.com/apple/swift-log.git",
@ -145,13 +163,22 @@
"version": "1.9.1" "version": "1.9.1"
} }
}, },
{
"package": "SwiftPM",
"repositoryURL": "https://github.com/apple/swift-package-manager.git",
"state": {
"branch": "release/5.4",
"revision": "9c96dd770834d4b02c680cbf63949fab75fb6cca",
"version": null
}
},
{ {
"package": "swift-tools-support-core", "package": "swift-tools-support-core",
"repositoryURL": "https://github.com/apple/swift-tools-support-core.git", "repositoryURL": "https://github.com/apple/swift-tools-support-core.git",
"state": { "state": {
"branch": null, "branch": "release/5.4",
"revision": "243beea77d20db46647a3de4765c96e2c801c7c7", "revision": "bc7961701fd94104528b017e969f63ea40bdb22b",
"version": "0.1.12" "version": null
} }
}, },
{ {
@ -180,6 +207,15 @@
"revision": "2b06a70dfcfa76a2e5079f60e3ae911511f09db0", "revision": "2b06a70dfcfa76a2e5079f60e3ae911511f09db0",
"version": "2.1.2" "version": "2.1.2"
} }
},
{
"package": "Yams",
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "9003d51672e516cc59297b7e96bff1dfdedcb4ea",
"version": "4.0.4"
}
} }
] ]
}, },

View File

@ -26,9 +26,14 @@ let package = Package(
url: "https://github.com/apple/swift-argument-parser.git", url: "https://github.com/apple/swift-argument-parser.git",
.upToNextMinor(from: "0.3.0") .upToNextMinor(from: "0.3.0")
), ),
.package(
name: "SwiftPM",
url: "https://github.com/apple/swift-package-manager.git",
.branch("release/5.4")
),
.package( .package(
url: "https://github.com/apple/swift-tools-support-core.git", url: "https://github.com/apple/swift-tools-support-core.git",
.upToNextMinor(from: "0.1.10") .branch("release/5.4")
), ),
.package(url: "https://github.com/OpenCombine/OpenCombine.git", from: "0.10.0"), .package(url: "https://github.com/OpenCombine/OpenCombine.git", from: "0.10.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.29.3"), .package(url: "https://github.com/vapor/vapor.git", from: "4.29.3"),
@ -65,7 +70,6 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Crypto", package: "swift-crypto"), .product(name: "Crypto", package: "swift-crypto"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "Vapor", package: "vapor"), .product(name: "Vapor", package: "vapor"),
"CartonHelpers", "CartonHelpers",
openCombineProduct, openCombineProduct,
@ -76,7 +80,7 @@ let package = Package(
name: "SwiftToolchain", name: "SwiftToolchain",
dependencies: [ dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), .product(name: "SwiftPMDataModel", package: "SwiftPM"),
"CartonHelpers", "CartonHelpers",
openCombineProduct, openCombineProduct,
"WasmTransformer", "WasmTransformer",
@ -86,7 +90,6 @@ let package = Package(
name: "CartonHelpers", name: "CartonHelpers",
dependencies: [ dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
openCombineProduct, openCombineProduct,
"Splash", "Splash",
] ]
@ -107,7 +110,6 @@ let package = Package(
dependencies: [ dependencies: [
"Carton", "Carton",
"CartonHelpers", "CartonHelpers",
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
] ]
), ),

View File

@ -20,9 +20,14 @@ let package = Package(
url: "https://github.com/apple/swift-argument-parser.git", url: "https://github.com/apple/swift-argument-parser.git",
.upToNextMinor(from: "0.3.0") .upToNextMinor(from: "0.3.0")
), ),
.package(
name: "SwiftPM",
url: "https://github.com/apple/swift-package-manager.git",
.branch("release/5.4")
),
.package( .package(
url: "https://github.com/apple/swift-tools-support-core.git", url: "https://github.com/apple/swift-tools-support-core.git",
.upToNextMinor(from: "0.1.10") .branch("release/5.4")
), ),
.package(url: "https://github.com/OpenCombine/OpenCombine.git", from: "0.10.0"), .package(url: "https://github.com/OpenCombine/OpenCombine.git", from: "0.10.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.29.3"), .package(url: "https://github.com/vapor/vapor.git", from: "4.29.3"),
@ -59,7 +64,6 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Crypto", package: "swift-crypto"), .product(name: "Crypto", package: "swift-crypto"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "Vapor", package: "vapor"), .product(name: "Vapor", package: "vapor"),
"CartonHelpers", "CartonHelpers",
"OpenCombine", "OpenCombine",
@ -70,6 +74,7 @@ let package = Package(
name: "SwiftToolchain", name: "SwiftToolchain",
dependencies: [ dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftPMDataModel", package: "SwiftPM"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers", "CartonHelpers",
"OpenCombine", "OpenCombine",
@ -92,7 +97,6 @@ let package = Package(
dependencies: [ dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers", "CartonHelpers",
] ]
), ),

View File

@ -16,6 +16,7 @@ import ArgumentParser
import CartonHelpers import CartonHelpers
import CartonKit import CartonKit
import Crypto import Crypto
import PackageModel
import SwiftToolchain import SwiftToolchain
import TSCBasic import TSCBasic
import WasmTransformer import WasmTransformer
@ -105,7 +106,7 @@ struct Bundle: ParsableCommand {
buildDirectory: AbsolutePath, buildDirectory: AbsolutePath,
bundleDirectory: AbsolutePath, bundleDirectory: AbsolutePath,
toolchain: Toolchain, toolchain: Toolchain,
product: Product product: ProductDescription
) throws { ) throws {
// Rename the final binary to use a part of its hash to bust browsers and CDN caches. // Rename the final binary to use a part of its hash to bust browsers and CDN caches.
let optimizedHash = try localFileSystem.readFileContents(optimizedPath).hexSHA256.prefix(16) let optimizedHash = try localFileSystem.readFileContents(optimizedPath).hexSHA256.prefix(16)
@ -137,9 +138,9 @@ struct Bundle: ParsableCommand {
)) ))
) )
let package = try toolchain.package.get() let manifest = try toolchain.manifest.get()
for target in package.targets where target.type == .regular && !target.resources.isEmpty { for target in manifest.targets where target.type == .regular && !target.resources.isEmpty {
let targetPath = package.resourcesPath(for: target) let targetPath = manifest.resourcesPath(for: target)
let resourcesPath = buildDirectory.appending(component: targetPath) let resourcesPath = buildDirectory.appending(component: targetPath)
let targetDirectory = bundleDirectory.appending(component: targetPath) let targetDirectory = bundleDirectory.appending(component: targetPath)
@ -154,13 +155,13 @@ struct Bundle: ParsableCommand {
swiftlint:disable:next line_length swiftlint:disable:next line_length
https://forums.swift.org/t/pitch-ability-to-declare-executable-targets-in-swiftpm-manifests-to-support-main/41968 https://forums.swift.org/t/pitch-ability-to-declare-executable-targets-in-swiftpm-manifests-to-support-main/41968
*/ */
let inferredMainTarget = package.targets.first { let inferredMainTarget = manifest.targets.first {
product.targets.contains($0.name) product.targets.contains($0.name)
} }
guard let mainTarget = inferredMainTarget else { return } guard let mainTarget = inferredMainTarget else { return }
let targetPath = package.resourcesPath(for: mainTarget) let targetPath = manifest.resourcesPath(for: mainTarget)
let resourcesPath = buildDirectory.appending(component: targetPath) let resourcesPath = buildDirectory.appending(component: targetPath)
for file in try localFileSystem.traverseRecursively(resourcesPath) { for file in try localFileSystem.traverseRecursively(resourcesPath) {
let targetPath = bundleDirectory.appending(component: file.basename) let targetPath = bundleDirectory.appending(component: file.basename)

View File

@ -47,7 +47,10 @@ struct Dev: ParsableCommand {
@Option(name: .shortAndLong, help: "Set the HTTP port the development server will run on.") @Option(name: .shortAndLong, help: "Set the HTTP port the development server will run on.")
var port = 8080 var port = 8080
@Option(name: .shortAndLong, help: "Set the location where the development server will run. Default is `127.0.0.1`.") @Option(
name: .shortAndLong,
help: "Set the location where the development server will run. Default is `127.0.0.1`."
)
var host = "127.0.0.1" var host = "127.0.0.1"
@Flag(name: .long, help: "Skip automatically opening app in system browser.") @Flag(name: .long, help: "Skip automatically opening app in system browser.")
@ -114,7 +117,7 @@ struct Dev: ParsableCommand {
host: host, host: host,
customIndexContent: HTML.readCustomIndexPage(at: customIndexPage, on: localFileSystem), customIndexContent: HTML.readCustomIndexPage(at: customIndexPage, on: localFileSystem),
// swiftlint:disable:next force_try // swiftlint:disable:next force_try
package: try! toolchain.package.get(), manifest: try! toolchain.manifest.get(),
product: inferredProduct, product: inferredProduct,
entrypoint: Self.entrypoint entrypoint: Self.entrypoint
), ),

View File

@ -96,7 +96,7 @@ struct Test: ParsableCommand {
host: host, host: host,
customIndexContent: nil, customIndexContent: nil,
// swiftlint:disable:next force_try // swiftlint:disable:next force_try
package: try! toolchain.package.get(), manifest: try! toolchain.manifest.get(),
product: nil, product: nil,
entrypoint: Self.entrypoint entrypoint: Self.entrypoint
), ),

View File

@ -26,8 +26,8 @@ public extension StringProtocol {
var chromeStackTrace: [StackTraceItem] { var chromeStackTrace: [StackTraceItem] {
split(separator: "\n").dropFirst().compactMap { split(separator: "\n").dropFirst().compactMap {
if let webpackMatch = webpackRegex.matchGroups(in: String($0)).first, if let webpackMatch = webpackRegex.matchGroups(in: String($0)).first,
let symbol = webpackMatch.first, let symbol = webpackMatch.first,
let location = webpackMatch.last let location = webpackMatch.last
{ {
return StackTraceItem(symbol: symbol, location: location, kind: .javaScript) return StackTraceItem(symbol: symbol, location: location, kind: .javaScript)
} else if } else if

View File

@ -162,9 +162,9 @@ public struct DiagnosticsParser: ProcessOutputParser {
var groupedMessages = [[CustomDiagnostic]]() var groupedMessages = [[CustomDiagnostic]]()
for message in messages { for message in messages {
if let lastLineStr = groupedMessages.last?.last?.line, if let lastLineStr = groupedMessages.last?.last?.line,
let lastLine = Int(lastLineStr), let lastLine = Int(lastLineStr),
let line = Int(message.line), let line = Int(message.line),
lastLine == line - 1 || lastLine == line lastLine == line - 1 || lastLine == line
{ {
groupedMessages[groupedMessages.count - 1].append(message) groupedMessages[groupedMessages.count - 1].append(message)
} else { } else {

View File

@ -26,8 +26,8 @@ public extension StringProtocol {
var firefoxStackTrace: [StackTraceItem] { var firefoxStackTrace: [StackTraceItem] {
split(separator: "\n").compactMap { split(separator: "\n").compactMap {
if let webpackMatch = webpackRegex.matchGroups(in: String($0)).first, if let webpackMatch = webpackRegex.matchGroups(in: String($0)).first,
let symbol = webpackMatch.first, let symbol = webpackMatch.first,
let location = webpackMatch.last let location = webpackMatch.last
{ {
return StackTraceItem(symbol: symbol, location: location, kind: .javaScript) return StackTraceItem(symbol: symbol, location: location, kind: .javaScript)
} else if } else if

View File

@ -26,7 +26,7 @@ public extension StringProtocol {
var safariStackTrace: [StackTraceItem] { var safariStackTrace: [StackTraceItem] {
split(separator: "\n").compactMap { split(separator: "\n").compactMap {
if let wasmMatch = wasmRegex.matchGroups(in: String($0)).first, if let wasmMatch = wasmRegex.matchGroups(in: String($0)).first,
let symbol = wasmMatch.first let symbol = wasmMatch.first
{ {
return StackTraceItem( return StackTraceItem(
symbol: demangle(symbol), symbol: demangle(symbol),

View File

@ -19,7 +19,7 @@ extension StringProtocol {
func matches(regex: NSRegularExpression) -> String.SubSequence? { func matches(regex: NSRegularExpression) -> String.SubSequence? {
let str = String(self) let str = String(self)
guard let range = str.range(of: regex), guard let range = str.range(of: regex),
range.upperBound < str.endIndex range.upperBound < str.endIndex
else { return nil } else { return nil }
return str[range.upperBound..<str.endIndex] return str[range.upperBound..<str.endIndex]
} }
@ -29,7 +29,7 @@ extension StringProtocol {
let str = String(self) let str = String(self)
let range = NSRange(location: 0, length: utf16.count) let range = NSRange(location: 0, length: utf16.count)
guard let match = regex.firstMatch(in: str, options: [], range: range), guard let match = regex.firstMatch(in: str, options: [], range: range),
let matchRange = Range(match.range, in: str) let matchRange = Range(match.range, in: str)
else { else {
return nil return nil
} }
@ -40,7 +40,7 @@ extension StringProtocol {
let str = String(self) let str = String(self)
let range = NSRange(location: 0, length: utf16.count) let range = NSRange(location: 0, length: utf16.count)
guard let matches = regex.matches(in: str, options: [], range: range).first, guard let matches = regex.matches(in: str, options: [], range: range).first,
let matchRange = Range(matches.range(withName: name), in: str) let matchRange = Range(matches.range(withName: name), in: str)
else { else {
return nil return nil
} }
@ -50,7 +50,7 @@ extension StringProtocol {
func match(of regex: NSRegularExpression, named name: String) -> String.SubSequence? { func match(of regex: NSRegularExpression, named name: String) -> String.SubSequence? {
let str = String(self) let str = String(self)
guard let range = str.range(of: regex, named: name), guard let range = str.range(of: regex, named: name),
range.upperBound < str.endIndex && range.lowerBound >= str.startIndex range.upperBound < str.endIndex && range.lowerBound >= str.startIndex
else { return nil } else { return nil }
return str[range] return str[range]
} }

View File

@ -41,7 +41,7 @@ private extension StringProtocol {
_ labelB: TestsParser.Regex.Label _ labelB: TestsParser.Regex.Label
) -> (String.SubSequence, String.SubSequence)? { ) -> (String.SubSequence, String.SubSequence)? {
guard let a = match(of: regex, named: labelA.rawValue), guard let a = match(of: regex, named: labelA.rawValue),
let b = match(of: regex, named: labelB.rawValue) let b = match(of: regex, named: labelB.rawValue)
else { else {
return nil return nil
} }
@ -93,10 +93,10 @@ public struct TestsParser: ProcessOutputParser {
enum Assertion: String, CaseIterable { enum Assertion: String, CaseIterable {
case equal = "Equal", case equal = "Equal",
greaterThan = "GreaterThan", greaterThan = "GreaterThan",
lessThan = "LessThan", lessThan = "LessThan",
greaterThanOrEqual = "GreaterThanOrEqual", greaterThanOrEqual = "GreaterThanOrEqual",
lessThanOrEqual = "LessThanOrEqual" lessThanOrEqual = "LessThanOrEqual"
var funcName: String { var funcName: String {
"XCTAssert\(rawValue)" "XCTAssert\(rawValue)"
@ -178,20 +178,20 @@ public struct TestsParser: ProcessOutputParser {
if let suite = line.match(of: Regex.suiteStarted, labelled: .suite) { if let suite = line.match(of: Regex.suiteStarted, labelled: .suite) {
suites.append(.init(name: suite, cases: [])) suites.append(.init(name: suite, cases: []))
} else if let testCase = line.match(of: Regex.caseFinished, labelled: .testCase), } else if let testCase = line.match(of: Regex.caseFinished, labelled: .testCase),
let suite = line.match(of: Regex.caseFinished, labelled: .suite), let suite = line.match(of: Regex.caseFinished, labelled: .suite),
let suiteIdx = suites.firstIndex(where: { $0.name == suite }), let suiteIdx = suites.firstIndex(where: { $0.name == suite }),
let status = line.match(of: Regex.caseFinished, labelled: .status), let status = line.match(of: Regex.caseFinished, labelled: .status),
let duration = line.match(of: Regex.caseFinished, labelled: .duration) let duration = line.match(of: Regex.caseFinished, labelled: .duration)
{ {
suites[suiteIdx].cases.append( suites[suiteIdx].cases.append(
.init(name: testCase, passed: status == "passed", duration: duration, problems: []) .init(name: testCase, passed: status == "passed", duration: duration, problems: [])
) )
} else if let problem = line.matches(regex: Regex.problem), } else if let problem = line.matches(regex: Regex.problem),
let path = line.match(of: Regex.problem, labelled: .path), let path = line.match(of: Regex.problem, labelled: .path),
let lineNum = line.match(of: Regex.problem, labelled: .line), let lineNum = line.match(of: Regex.problem, labelled: .line),
let status = line.match(of: Regex.problem, labelled: .status), let status = line.match(of: Regex.problem, labelled: .status),
let suite = line.match(of: Regex.problem, labelled: .suite), let suite = line.match(of: Regex.problem, labelled: .suite),
let testCase = line.match(of: Regex.problem, labelled: .testCase) let testCase = line.match(of: Regex.problem, labelled: .testCase)
{ {
let diag = DiagnosticsParser.CustomDiagnostic( let diag = DiagnosticsParser.CustomDiagnostic(
kind: DiagnosticsParser.CustomDiagnostic.Kind(rawValue: String(status)) ?? .note, kind: DiagnosticsParser.CustomDiagnostic.Kind(rawValue: String(status)) ?? .note,
@ -202,7 +202,7 @@ public struct TestsParser: ProcessOutputParser {
message: String(problem) message: String(problem)
) )
if let suiteIdx = suites.firstIndex(where: { $0.name == suite }), if let suiteIdx = suites.firstIndex(where: { $0.name == suite }),
let caseIdx = suites[suiteIdx].cases.firstIndex(where: { $0.name == testCase }) let caseIdx = suites[suiteIdx].cases.firstIndex(where: { $0.name == testCase })
{ {
suites[suiteIdx].cases[caseIdx].problems.append(diag) suites[suiteIdx].cases[caseIdx].problems.append(diag)
} else { } else {
@ -212,7 +212,7 @@ public struct TestsParser: ProcessOutputParser {
} }
for problem in unmappedProblems { for problem in unmappedProblems {
if let suiteIdx = suites.firstIndex(where: { $0.name == problem.suite }), if let suiteIdx = suites.firstIndex(where: { $0.name == problem.suite }),
let caseIdx = suites[suiteIdx].cases.firstIndex(where: { $0.name == problem.testCase }) let caseIdx = suites[suiteIdx].cases.firstIndex(where: { $0.name == problem.testCase })
{ {
suites[suiteIdx].cases[caseIdx].problems.append(problem.problem) suites[suiteIdx].cases[caseIdx].problems.append(problem.problem)
} }
@ -269,7 +269,7 @@ public struct TestsParser: ProcessOutputParser {
} }
// Get the line of code from the file and output it for context. // Get the line of code from the file and output it for context.
if let lineNum = Int(problem.line), if let lineNum = Int(problem.line),
lineNum > 0 lineNum > 0
{ {
var fileContents: String? var fileContents: String?
if let fileBuf = fileBufs.first(where: { $0.path == problem.file })?.contents { if let fileBuf = fileBufs.first(where: { $0.path == problem.file })?.contents {

View File

@ -146,7 +146,7 @@ public final class ProcessRunner {
} }
public func waitUntilFinished() throws { public func waitUntilFinished() throws {
try await { completion in try tsc_await { completion in
subscription = publisher subscription = publisher
.sink( .sink(
receiveCompletion: { completion($0.result) }, receiveCompletion: { completion($0.result) },

View File

@ -69,7 +69,7 @@ public struct Entrypoint {
let client = HTTPClient(eventLoopGroupProvider: .createNew) let client = HTTPClient(eventLoopGroupProvider: .createNew)
let request = try HTTPClient.Request.get(url: staticArchiveURL) let request = try HTTPClient.Request.get(url: staticArchiveURL)
let response: HTTPClient.Response = try await { let response: HTTPClient.Response = try tsc_await {
client.execute(request: request).whenComplete($0) client.execute(request: request).whenComplete($0)
} }
try client.syncShutdown() try client.syncShutdown()
@ -91,7 +91,7 @@ public struct Entrypoint {
terminal.logLookup("Unpacking the archive: ", archiveFile) terminal.logLookup("Unpacking the archive: ", archiveFile)
try fileSystem.createDirectory(staticDir) try fileSystem.createDirectory(staticDir)
try await { try tsc_await {
ZipArchiver().extract(from: archiveFile, to: staticDir, completion: $0) ZipArchiver().extract(from: archiveFile, to: staticDir, completion: $0)
} }
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
import Foundation import Foundation
import PackageModel
import SwiftToolchain import SwiftToolchain
import TSCBasic import TSCBasic
import Vapor import Vapor
@ -23,8 +24,8 @@ extension Application {
let host: String let host: String
let mainWasmPath: AbsolutePath let mainWasmPath: AbsolutePath
let customIndexContent: String? let customIndexContent: String?
let package: SwiftToolchain.Package let manifest: Manifest
let product: Product? let product: ProductDescription?
let entrypoint: Entrypoint let entrypoint: Entrypoint
let onWebSocketOpen: (WebSocket, DestinationEnvironment) -> () let onWebSocketOpen: (WebSocket, DestinationEnvironment) -> ()
let onWebSocketClose: (WebSocket) -> () let onWebSocketClose: (WebSocket) -> ()
@ -63,10 +64,10 @@ extension Application {
} }
let buildDirectory = configuration.mainWasmPath.parentDirectory let buildDirectory = configuration.mainWasmPath.parentDirectory
for target in configuration.package.targets for target in configuration.manifest.targets
where target.type == .regular && !target.resources.isEmpty where target.type == .regular && !target.resources.isEmpty
{ {
let resourcesPath = configuration.package.resourcesPath(for: target) let resourcesPath = configuration.manifest.resourcesPath(for: target)
get(.constant(resourcesPath), "**") { get(.constant(resourcesPath), "**") {
$0.eventLoop.makeSucceededFuture($0.fileio.streamFile(at: AbsolutePath( $0.eventLoop.makeSucceededFuture($0.fileio.streamFile(at: AbsolutePath(
buildDirectory.appending(component: resourcesPath), buildDirectory.appending(component: resourcesPath),
@ -75,13 +76,13 @@ extension Application {
} }
} }
let inferredMainTarget = configuration.package.targets.first { let inferredMainTarget = configuration.manifest.targets.first {
configuration.product?.targets.contains($0.name) == true configuration.product?.targets.contains($0.name) == true
} }
guard let mainTarget = inferredMainTarget else { return } guard let mainTarget = inferredMainTarget else { return }
let resourcesPath = configuration.package.resourcesPath(for: mainTarget) let resourcesPath = configuration.manifest.resourcesPath(for: mainTarget)
get("**") { get("**") {
$0.eventLoop.makeSucceededFuture($0.fileio.streamFile(at: AbsolutePath( $0.eventLoop.makeSucceededFuture($0.fileio.streamFile(at: AbsolutePath(
buildDirectory.appending(component: resourcesPath), buildDirectory.appending(component: resourcesPath),

View File

@ -18,6 +18,7 @@ import Combine
#else #else
import OpenCombine import OpenCombine
#endif #endif
import PackageModel
import SwiftToolchain import SwiftToolchain
import TSCBasic import TSCBasic
import Vapor import Vapor
@ -83,8 +84,8 @@ public final class Server {
let port: Int let port: Int
let host: String let host: String
let customIndexContent: String? let customIndexContent: String?
let package: SwiftToolchain.Package let manifest: Manifest
let product: Product? let product: ProductDescription?
let entrypoint: Entrypoint let entrypoint: Entrypoint
public init( public init(
@ -95,8 +96,8 @@ public final class Server {
port: Int, port: Int,
host: String, host: String,
customIndexContent: String?, customIndexContent: String?,
package: SwiftToolchain.Package, manifest: Manifest,
product: Product?, product: ProductDescription?,
entrypoint: Entrypoint entrypoint: Entrypoint
) { ) {
self.builder = builder self.builder = builder
@ -106,7 +107,7 @@ public final class Server {
self.port = port self.port = port
self.host = host self.host = host
self.customIndexContent = customIndexContent self.customIndexContent = customIndexContent
self.package = package self.manifest = manifest
self.product = product self.product = product
self.entrypoint = entrypoint self.entrypoint = entrypoint
} }
@ -137,7 +138,7 @@ public final class Server {
host: configuration.host, host: configuration.host,
mainWasmPath: configuration.mainWasmPath, mainWasmPath: configuration.mainWasmPath,
customIndexContent: configuration.customIndexContent, customIndexContent: configuration.customIndexContent,
package: configuration.package, manifest: configuration.manifest,
product: configuration.product, product: configuration.product,
entrypoint: configuration.entrypoint, entrypoint: configuration.entrypoint,
onWebSocketOpen: { [weak self] ws, environment in onWebSocketOpen: { [weak self] ws, environment in

View File

@ -92,7 +92,7 @@ public final class Builder {
} }
public func runAndWaitUntilFinished() throws { public func runAndWaitUntilFinished() throws {
try await { completion in try tsc_await { completion in
subscription = run() subscription = run()
.sink( .sink(
receiveCompletion: { completion($0.result) }, receiveCompletion: { completion($0.result) },

View File

@ -0,0 +1,154 @@
// Copyright 2020 Carton contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import CartonHelpers
import Foundation
import PackageModel
import TSCBasic
extension Manifest {
static func from(swiftPath: AbsolutePath, terminal: InteractiveWriter) throws -> Manifest {
terminal.write("\nParsing package manifest: ", inColor: .yellow)
terminal.write("\(swiftPath) package dump-package\n")
let output = try Data(processDataOutput([swiftPath.pathString, "package", "dump-package"]))
let decoder = JSONDecoder()
let unencodedValues = DumpedManifest.Unencoded(
path: swiftPath,
url: swiftPath.asURL.absoluteString,
version: nil
)
decoder.userInfo[DumpedManifest.unencodedKey] = unencodedValues
let dumpedManifest = try decoder.decode(DumpedManifest.self, from: output)
return dumpedManifest.manifest
}
public func resourcesPath(for target: TargetDescription) -> String {
"\(name)_\(target.name).resources"
}
}
public enum PackageType: String {
case empty
case library
case executable
case systemModule = "system-module"
case manifest
}
// MARK: Custom Decodable Wrappers
// TODO: Remove this struct when we move to `Swift 5.4`
/// A temprary wrapper for decoding `PackageDependencyDescription` since
/// `productFilter` is not available in dumped JSON with pre-5.4 compilers.
struct DumpedPackageDependencyDescription: Decodable {
var dependendy: PackageDependencyDescription
private enum CodingKeys: CodingKey {
case name, url, requirement, productFilter
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let name = try container.decode(String.self, forKey: .name)
let url = try container.decode(String.self, forKey: .url)
let requirement = try container.decode(
PackageDependencyDescription.Requirement.self,
forKey: .requirement
)
let productFilter = try? container.decode(ProductFilter.self, forKey: .productFilter)
dependendy = PackageDependencyDescription(
name: name,
url: url,
requirement: requirement,
productFilter: productFilter ?? .nothing
)
}
}
/// A wrapper around `Manifest` needed for decoding from `dump-package` output,
/// since when encoding several (required for initialization) keys are skipped.
/// When decoding this wrapper, callers must provide an `unencodedKey` in the
/// decoder's `userInfo`.
struct DumpedManifest: Decodable {
var manifest: Manifest
static let unencodedKey = CodingUserInfoKey(rawValue: "unencoded")!
/// The skipped keys during `dump-package` encoding
struct Unencoded {
let path: AbsolutePath
let url: String
let version: Version?
}
private enum CodingKeys: CodingKey {
case name, toolsVersion,
pkgConfig, providers, cLanguageStandard, cxxLanguageStandard, swiftLanguageVersions,
dependencies, products, targets, platforms, packageKind, revision,
defaultLocalization
}
init(from decoder: Decoder) throws {
guard let unencoded = decoder.userInfo[DumpedManifest.unencodedKey] as? Unencoded else {
let context = DecodingError.Context(
codingPath: [],
debugDescription: "Unencoded values are missing from Decoder's userInfo"
)
throw DecodingError.dataCorrupted(context)
}
let container = try decoder.container(keyedBy: CodingKeys.self)
let name = try container.decode(String.self, forKey: .name)
let toolsVersion = try container.decode(ToolsVersion.self, forKey: .toolsVersion)
let pkgConfig = try container.decode(String?.self, forKey: .pkgConfig)
let providers = try container.decode(
[SystemPackageProviderDescription]?.self,
forKey: .providers
)
let cLanguageStandard = try container.decode(String?.self, forKey: .cLanguageStandard)
let cxxLanguageStandard = try container.decode(String?.self, forKey: .cxxLanguageStandard)
let swiftLanguageVersions = try container.decode(
[SwiftLanguageVersion]?.self,
forKey: .swiftLanguageVersions
)
// TODO: Change to `PackageDependencyDescription` when we move to `Swift 5.4`
let dependencies = try container.decode(
[DumpedPackageDependencyDescription].self,
forKey: .dependencies
)
let products = try container.decode([ProductDescription].self, forKey: .products)
let targets = try container.decode([TargetDescription].self, forKey: .targets)
let platforms = try container.decode([PlatformDescription].self, forKey: .platforms)
// TODO: Change to non-optional when we move to `Swift 5.4`
// `packageKind` is not available in dumped JSON with pre-5.4 compilers.
let packageKind = try? container.decode(PackageReference.Kind.self, forKey: .packageKind)
manifest = Manifest(
name: name,
platforms: platforms,
path: unencoded.path,
url: unencoded.url,
version: unencoded.version,
toolsVersion: toolsVersion,
packageKind: packageKind ?? .root,
pkgConfig: pkgConfig,
providers: providers,
cLanguageStandard: cLanguageStandard,
cxxLanguageStandard: cxxLanguageStandard,
swiftLanguageVersions: swiftLanguageVersions,
dependencies: dependencies.map(\.dependendy),
products: products,
targets: targets
)
}
}

View File

@ -1,105 +0,0 @@
// Copyright 2020 Carton contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import CartonHelpers
import Foundation
import TSCBasic
/**
Simple Package structure from package dump
*/
public struct Package: Codable {
public let name: String
public let products: [Product]
public let targets: [Target]
public let dependencies: [Dependency]?
public struct Dependency: Codable {
let name: String
let requirement: Requirement
struct Requirement: Codable {
let range: [Range]?
let branch: [String]?
let revision: [String]?
let exact: [String]?
struct Range: Codable {
let lowerBound: String
let upperBound: String
}
}
}
init(with swiftPath: AbsolutePath, _ terminal: InteractiveWriter) throws {
terminal.write("\nParsing package manifest: ", inColor: .yellow)
terminal.write("\(swiftPath) package dump-package\n")
let output = try Data(processDataOutput([swiftPath.pathString, "package", "dump-package"]))
self = try JSONDecoder().decode(Package.self, from: output)
}
public func resourcesPath(for target: Target) -> String {
"\(name)_\(target.name).resources"
}
}
struct ProductType: Codable {
let executable: String?
let library: [String]?
}
/**
Simple Product structure from package dump
*/
public struct Product: Codable {
let name: String
let type: ProductType
/** List of names of targets that this product is composed of. Can be used to infer actual
target descriptions used in this product.
*/
public let targets: [String]
}
public enum TargetType: String, Codable {
case regular
case test
case system
}
public struct Target: Codable {
public let name: String
public let type: TargetType
public let path: String?
public let resources: [Resource]
}
public struct Resource: Codable {
public let path: String
public let rule: Rule
public enum Rule: String, Codable {
case copy
case process
}
}
public enum PackageType: String {
case empty
case library
case executable
case systemModule = "system-module"
case manifest
}

View File

@ -14,6 +14,7 @@
import CartonHelpers import CartonHelpers
import Foundation import Foundation
import PackageModel
import TSCBasic import TSCBasic
import TSCUtility import TSCUtility
@ -67,16 +68,29 @@ enum ToolchainError: Error, CustomStringConvertible {
} }
} }
extension Package.Dependency.Requirement { extension PackageDependencyDescription.Requirement {
var isJavaScriptKitCompatible: Bool { var isJavaScriptKitCompatible: Bool {
if let upperBound = range?.first?.upperBound, let version = Version(string: upperBound) { switch self {
return version >= compatibleJSKitVersion case let .exact(version):
return version == compatibleJSKitVersion
case let .range(range):
return range.upperBound >= compatibleJSKitVersion
case .localPackage:
return true
default:
return false
} }
return exact?.compactMap { Version(string: $0) } == [compatibleJSKitVersion]
} }
var version: String { var versionDescription: String {
revision?.first ?? range?.first?.lowerBound ?? "" switch self {
case let .exact(version):
return version.description
case let .range(range):
return range.lowerBound.description
default:
return "(Unknown)"
}
} }
} }
@ -86,7 +100,7 @@ public final class Toolchain {
private let version: String private let version: String
private let swiftPath: AbsolutePath private let swiftPath: AbsolutePath
public let package: Result<Package, Error> public let manifest: Result<Manifest, Error>
public init( public init(
for versionSpec: String? = nil, for versionSpec: String? = nil,
@ -99,7 +113,7 @@ public final class Toolchain {
self.version = version self.version = version
self.fileSystem = fileSystem self.fileSystem = fileSystem
self.terminal = terminal self.terminal = terminal
package = Result { try Package(with: swiftPath, terminal) } manifest = Result { try Manifest.from(swiftPath: swiftPath, terminal: terminal) }
} }
private func inferBinPath(isRelease: Bool) throws -> AbsolutePath { private func inferBinPath(isRelease: Bool) throws -> AbsolutePath {
@ -114,11 +128,11 @@ public final class Toolchain {
return AbsolutePath(binPath) return AbsolutePath(binPath)
} }
private func inferDevProduct(hint: String?) throws -> Product? { private func inferDevProduct(hint: String?) throws -> ProductDescription? {
let package = try self.package.get() let manifest = try self.manifest.get()
var candidateProducts = package.products var candidateProducts = manifest.products
.filter { $0.type.library == nil } .filter { $0.type == .executable }
if let productName = hint { if let productName = hint {
candidateProducts = candidateProducts.filter { $0.name == productName } candidateProducts = candidateProducts.filter { $0.name == productName }
@ -152,7 +166,7 @@ public final class Toolchain {
} }
private func inferManifestDirectory() throws -> AbsolutePath { private func inferManifestDirectory() throws -> AbsolutePath {
guard (try? package.get()) != nil, var cwd = fileSystem.currentWorkingDirectory else { guard (try? manifest.get()) != nil, var cwd = fileSystem.currentWorkingDirectory else {
throw ToolchainError.missingPackageManifest throw ToolchainError.missingPackageManifest
} }
@ -169,15 +183,15 @@ public final class Toolchain {
} }
public func inferSourcesPaths() throws -> [AbsolutePath] { public func inferSourcesPaths() throws -> [AbsolutePath] {
let package = try self.package.get() let manifest = try self.manifest.get()
let targetPaths = package.targets.compactMap { target -> String? in let targetPaths = manifest.targets.compactMap { target -> String? in
guard let path = target.path else { guard let path = target.path else {
switch target.type { switch target.type {
case .regular: case .regular:
return RelativePath("Sources").appending(component: target.name).pathString return RelativePath("Sources").appending(component: target.name).pathString
case .test, .system: case .test, .system, .executable, .binary:
return nil return nil
} }
} }
@ -194,20 +208,20 @@ public final class Toolchain {
public func buildCurrentProject( public func buildCurrentProject(
product: String?, product: String?,
isRelease: Bool isRelease: Bool
) throws -> (builderArguments: [String], mainWasmPath: AbsolutePath, Product) { ) throws -> (builderArguments: [String], mainWasmPath: AbsolutePath, ProductDescription) {
guard let product = try inferDevProduct(hint: product) guard let product = try inferDevProduct(hint: product)
else { throw ToolchainError.noExecutableProduct } else { throw ToolchainError.noExecutableProduct }
let package = try self.package.get() let manifest = try self.manifest.get()
if let jsKit = package.dependencies?.first(where: { $0.name == "JavaScriptKit" }), if let jsKit = manifest.dependencies.first(where: { $0.name == "JavaScriptKit" }),
!jsKit.requirement.isJavaScriptKitCompatible !jsKit.requirement.isJavaScriptKitCompatible
{ {
let version = jsKit.requirement.version let versionDescription = jsKit.requirement.versionDescription
terminal.write( terminal.write(
""" """
This version of JavaScriptKit \(version) is not known to be compatible with \ This version of JavaScriptKit \(versionDescription) is not known to be compatible with \
carton \(cartonVersion). Please specify a JavaScriptKit dependency on version \ carton \(cartonVersion). Please specify a JavaScriptKit dependency on version \
\(compatibleJSKitVersion) in your `Package.swift`.\n \(compatibleJSKitVersion) in your `Package.swift`.\n
@ -246,9 +260,9 @@ public final class Toolchain {
isRelease: Bool, isRelease: Bool,
_ environment: DestinationEnvironment _ environment: DestinationEnvironment
) throws -> AbsolutePath { ) throws -> AbsolutePath {
let package = try self.package.get() let manifest = try self.manifest.get()
let binPath = try inferBinPath(isRelease: isRelease) let binPath = try inferBinPath(isRelease: isRelease)
let testProductName = "\(package.name)PackageTests" let testProductName = "\(manifest.name)PackageTests"
let testBundlePath = binPath.appending(component: "\(testProductName).wasm") let testBundlePath = binPath.appending(component: "\(testProductName).wasm")
terminal.logLookup("- test bundle to run: ", testBundlePath.pathString) terminal.logLookup("- test bundle to run: ", testBundlePath.pathString)

View File

@ -49,7 +49,7 @@ extension ToolchainSystem {
var subscriptions = [AnyCancellable]() var subscriptions = [AnyCancellable]()
let request = try HTTPClient.Request.get(url: url) let request = try HTTPClient.Request.get(url: url)
_ = try await { (completion: @escaping (Result<(), Error>) -> ()) in _ = try tsc_await { (completion: @escaping (Result<(), Error>) -> ()) in
client.execute(request: request, delegate: delegate).futureResult.whenComplete { _ in client.execute(request: request, delegate: delegate).futureResult.whenComplete { _ in
subject.send(completion: .finished) subject.send(completion: .finished)
} }
@ -115,7 +115,7 @@ extension ToolchainSystem {
path: path, path: path,
reportHead: { reportHead: {
guard $0.status == .ok, guard $0.status == .ok,
let totalBytes = $0.headers.first(name: "Content-Length").flatMap(Int.init) let totalBytes = $0.headers.first(name: "Content-Length").flatMap(Int.init)
else { else {
subject.send(completion: .failure(ToolchainError.invalidResponseCode($0.status.code))) subject.send(completion: .failure(ToolchainError.invalidResponseCode($0.status.code)))
return return

View File

@ -104,8 +104,8 @@ public class ToolchainSystem {
) throws -> String { ) throws -> String {
if let versionSpec = versionSpec { if let versionSpec = versionSpec {
if let url = URL(string: versionSpec), if let url = URL(string: versionSpec),
let filename = url.pathComponents.last, let filename = url.pathComponents.last,
let match = versionRegEx.matchGroups(in: filename).first?.first let match = versionRegEx.matchGroups(in: filename).first?.first
{ {
terminal.logLookup("Inferred swift version: ", match) terminal.logLookup("Inferred swift version: ", match)
return match return match
@ -152,7 +152,7 @@ public class ToolchainSystem {
terminal.logLookup("Fetching release assets from ", releaseURL) terminal.logLookup("Fetching release assets from ", releaseURL)
let decoder = JSONDecoder() let decoder = JSONDecoder()
let request = try HTTPClient.Request.get(url: releaseURL) let request = try HTTPClient.Request.get(url: releaseURL)
let release = try await { let release = try tsc_await {
client.execute(request: request).flatMapResult { response -> Result<Release, Error> in client.execute(request: request).flatMapResult { response -> Result<Release, Error> in
guard (200..<300).contains(response.status.code), let body = response.body else { guard (200..<300).contains(response.status.code), let body = response.body else {
return .failure(ToolchainError.invalidResponse( return .failure(ToolchainError.invalidResponse(
@ -267,10 +267,10 @@ public class ToolchainSystem {
public func fetchLocalSwiftVersion() throws -> String? { public func fetchLocalSwiftVersion() throws -> String? {
guard fileSystem.isFile(swiftVersionPath), guard fileSystem.isFile(swiftVersionPath),
let version = try fileSystem.readFileContents(swiftVersionPath) let version = try fileSystem.readFileContents(swiftVersionPath)
.validDescription? .validDescription?
// get the first line of the file // get the first line of the file
.components(separatedBy: CharacterSet.newlines).first .components(separatedBy: CharacterSet.newlines).first
else { return nil } else { return nil }
return version return version

View File

@ -23,7 +23,7 @@ struct Formula: ParsableCommand {
let archiveURL = "https://github.com/swiftwasm/carton/archive/\(version).tar.gz" let archiveURL = "https://github.com/swiftwasm/carton/archive/\(version).tar.gz"
let client = HTTPClient(eventLoopGroupProvider: .createNew) let client = HTTPClient(eventLoopGroupProvider: .createNew)
let response: HTTPClient.Response = try await { let response: HTTPClient.Response = try tsc_await {
client.get(url: archiveURL).whenComplete($0) client.get(url: archiveURL).whenComplete($0)
} }
try client.syncShutdown() try client.syncShutdown()