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"
}
},
{
"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",
"repositoryURL": "https://github.com/apple/swift-log.git",
@ -145,13 +163,22 @@
"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",
"repositoryURL": "https://github.com/apple/swift-tools-support-core.git",
"state": {
"branch": null,
"revision": "243beea77d20db46647a3de4765c96e2c801c7c7",
"version": "0.1.12"
"branch": "release/5.4",
"revision": "bc7961701fd94104528b017e969f63ea40bdb22b",
"version": null
}
},
{
@ -180,6 +207,15 @@
"revision": "2b06a70dfcfa76a2e5079f60e3ae911511f09db0",
"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",
.upToNextMinor(from: "0.3.0")
),
.package(
name: "SwiftPM",
url: "https://github.com/apple/swift-package-manager.git",
.branch("release/5.4")
),
.package(
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/vapor/vapor.git", from: "4.29.3"),
@ -65,7 +70,6 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Crypto", package: "swift-crypto"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "Vapor", package: "vapor"),
"CartonHelpers",
openCombineProduct,
@ -76,7 +80,7 @@ let package = Package(
name: "SwiftToolchain",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "SwiftPMDataModel", package: "SwiftPM"),
"CartonHelpers",
openCombineProduct,
"WasmTransformer",
@ -86,7 +90,6 @@ let package = Package(
name: "CartonHelpers",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
openCombineProduct,
"Splash",
]
@ -107,7 +110,6 @@ let package = Package(
dependencies: [
"Carton",
"CartonHelpers",
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.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",
.upToNextMinor(from: "0.3.0")
),
.package(
name: "SwiftPM",
url: "https://github.com/apple/swift-package-manager.git",
.branch("release/5.4")
),
.package(
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/vapor/vapor.git", from: "4.29.3"),
@ -59,7 +64,6 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Crypto", package: "swift-crypto"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
.product(name: "Vapor", package: "vapor"),
"CartonHelpers",
"OpenCombine",
@ -70,6 +74,7 @@ let package = Package(
name: "SwiftToolchain",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftPMDataModel", package: "SwiftPM"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers",
"OpenCombine",
@ -92,7 +97,6 @@ let package = Package(
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers",
]
),

View File

@ -16,6 +16,7 @@ import ArgumentParser
import CartonHelpers
import CartonKit
import Crypto
import PackageModel
import SwiftToolchain
import TSCBasic
import WasmTransformer
@ -105,7 +106,7 @@ struct Bundle: ParsableCommand {
buildDirectory: AbsolutePath,
bundleDirectory: AbsolutePath,
toolchain: Toolchain,
product: Product
product: ProductDescription
) throws {
// 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)
@ -137,9 +138,9 @@ struct Bundle: ParsableCommand {
))
)
let package = try toolchain.package.get()
for target in package.targets where target.type == .regular && !target.resources.isEmpty {
let targetPath = package.resourcesPath(for: target)
let manifest = try toolchain.manifest.get()
for target in manifest.targets where target.type == .regular && !target.resources.isEmpty {
let targetPath = manifest.resourcesPath(for: target)
let resourcesPath = buildDirectory.appending(component: targetPath)
let targetDirectory = bundleDirectory.appending(component: targetPath)
@ -154,13 +155,13 @@ struct Bundle: ParsableCommand {
swiftlint:disable:next line_length
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)
}
guard let mainTarget = inferredMainTarget else { return }
let targetPath = package.resourcesPath(for: mainTarget)
let targetPath = manifest.resourcesPath(for: mainTarget)
let resourcesPath = buildDirectory.appending(component: targetPath)
for file in try localFileSystem.traverseRecursively(resourcesPath) {
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.")
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"
@Flag(name: .long, help: "Skip automatically opening app in system browser.")
@ -114,7 +117,7 @@ struct Dev: ParsableCommand {
host: host,
customIndexContent: HTML.readCustomIndexPage(at: customIndexPage, on: localFileSystem),
// swiftlint:disable:next force_try
package: try! toolchain.package.get(),
manifest: try! toolchain.manifest.get(),
product: inferredProduct,
entrypoint: Self.entrypoint
),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ extension StringProtocol {
func matches(regex: NSRegularExpression) -> String.SubSequence? {
let str = String(self)
guard let range = str.range(of: regex),
range.upperBound < str.endIndex
range.upperBound < str.endIndex
else { return nil }
return str[range.upperBound..<str.endIndex]
}
@ -29,7 +29,7 @@ extension StringProtocol {
let str = String(self)
let range = NSRange(location: 0, length: utf16.count)
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 {
return nil
}
@ -40,7 +40,7 @@ extension StringProtocol {
let str = String(self)
let range = NSRange(location: 0, length: utf16.count)
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 {
return nil
}
@ -50,7 +50,7 @@ extension StringProtocol {
func match(of regex: NSRegularExpression, named name: String) -> String.SubSequence? {
let str = String(self)
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 }
return str[range]
}

View File

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

View File

@ -69,7 +69,7 @@ public struct Entrypoint {
let client = HTTPClient(eventLoopGroupProvider: .createNew)
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)
}
try client.syncShutdown()
@ -91,7 +91,7 @@ public struct Entrypoint {
terminal.logLookup("Unpacking the archive: ", archiveFile)
try fileSystem.createDirectory(staticDir)
try await {
try tsc_await {
ZipArchiver().extract(from: archiveFile, to: staticDir, completion: $0)
}
}

View File

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

View File

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

View File

@ -92,7 +92,7 @@ public final class Builder {
}
public func runAndWaitUntilFinished() throws {
try await { completion in
try tsc_await { completion in
subscription = run()
.sink(
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 Foundation
import PackageModel
import TSCBasic
import TSCUtility
@ -67,16 +68,29 @@ enum ToolchainError: Error, CustomStringConvertible {
}
}
extension Package.Dependency.Requirement {
extension PackageDependencyDescription.Requirement {
var isJavaScriptKitCompatible: Bool {
if let upperBound = range?.first?.upperBound, let version = Version(string: upperBound) {
return version >= compatibleJSKitVersion
switch self {
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 {
revision?.first ?? range?.first?.lowerBound ?? ""
var versionDescription: String {
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 swiftPath: AbsolutePath
public let package: Result<Package, Error>
public let manifest: Result<Manifest, Error>
public init(
for versionSpec: String? = nil,
@ -99,7 +113,7 @@ public final class Toolchain {
self.version = version
self.fileSystem = fileSystem
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 {
@ -114,11 +128,11 @@ public final class Toolchain {
return AbsolutePath(binPath)
}
private func inferDevProduct(hint: String?) throws -> Product? {
let package = try self.package.get()
private func inferDevProduct(hint: String?) throws -> ProductDescription? {
let manifest = try self.manifest.get()
var candidateProducts = package.products
.filter { $0.type.library == nil }
var candidateProducts = manifest.products
.filter { $0.type == .executable }
if let productName = hint {
candidateProducts = candidateProducts.filter { $0.name == productName }
@ -152,7 +166,7 @@ public final class Toolchain {
}
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
}
@ -169,15 +183,15 @@ public final class Toolchain {
}
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 {
switch target.type {
case .regular:
return RelativePath("Sources").appending(component: target.name).pathString
case .test, .system:
case .test, .system, .executable, .binary:
return nil
}
}
@ -194,20 +208,20 @@ public final class Toolchain {
public func buildCurrentProject(
product: String?,
isRelease: Bool
) throws -> (builderArguments: [String], mainWasmPath: AbsolutePath, Product) {
) throws -> (builderArguments: [String], mainWasmPath: AbsolutePath, ProductDescription) {
guard let product = try inferDevProduct(hint: product)
else { throw ToolchainError.noExecutableProduct }
let package = try self.package.get()
if let jsKit = package.dependencies?.first(where: { $0.name == "JavaScriptKit" }),
!jsKit.requirement.isJavaScriptKitCompatible
let manifest = try self.manifest.get()
if let jsKit = manifest.dependencies.first(where: { $0.name == "JavaScriptKit" }),
!jsKit.requirement.isJavaScriptKitCompatible
{
let version = jsKit.requirement.version
let versionDescription = jsKit.requirement.versionDescription
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 \
\(compatibleJSKitVersion) in your `Package.swift`.\n
@ -246,9 +260,9 @@ public final class Toolchain {
isRelease: Bool,
_ environment: DestinationEnvironment
) throws -> AbsolutePath {
let package = try self.package.get()
let manifest = try self.manifest.get()
let binPath = try inferBinPath(isRelease: isRelease)
let testProductName = "\(package.name)PackageTests"
let testProductName = "\(manifest.name)PackageTests"
let testBundlePath = binPath.appending(component: "\(testProductName).wasm")
terminal.logLookup("- test bundle to run: ", testBundlePath.pathString)

View File

@ -49,7 +49,7 @@ extension ToolchainSystem {
var subscriptions = [AnyCancellable]()
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
subject.send(completion: .finished)
}
@ -115,7 +115,7 @@ extension ToolchainSystem {
path: path,
reportHead: {
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 {
subject.send(completion: .failure(ToolchainError.invalidResponseCode($0.status.code)))
return

View File

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

View File

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