diff --git a/Package.resolved b/Package.resolved index 820b4709..ab9db949 100644 --- a/Package.resolved +++ b/Package.resolved @@ -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" + } } ] }, diff --git a/Package.swift b/Package.swift index 34798057..190f1e5b 100644 --- a/Package.swift +++ b/Package.swift @@ -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"), ] ), diff --git a/Package@swift-5.2.swift b/Package@swift-5.2.swift index 4c1cbc67..45a1decf 100644 --- a/Package@swift-5.2.swift +++ b/Package@swift-5.2.swift @@ -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", ] ), diff --git a/Sources/CartonCLI/Commands/Bundle.swift b/Sources/CartonCLI/Commands/Bundle.swift index 897950cf..f050831b 100644 --- a/Sources/CartonCLI/Commands/Bundle.swift +++ b/Sources/CartonCLI/Commands/Bundle.swift @@ -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) diff --git a/Sources/CartonCLI/Commands/Dev.swift b/Sources/CartonCLI/Commands/Dev.swift index 5db6fa36..acbf85bc 100644 --- a/Sources/CartonCLI/Commands/Dev.swift +++ b/Sources/CartonCLI/Commands/Dev.swift @@ -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 ), diff --git a/Sources/CartonCLI/Commands/Test.swift b/Sources/CartonCLI/Commands/Test.swift index 5ebd7b64..d71ef056 100644 --- a/Sources/CartonCLI/Commands/Test.swift +++ b/Sources/CartonCLI/Commands/Test.swift @@ -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 ), diff --git a/Sources/CartonHelpers/Parsers/ChromeStackTrace.swift b/Sources/CartonHelpers/Parsers/ChromeStackTrace.swift index b4667f94..1104b7ee 100644 --- a/Sources/CartonHelpers/Parsers/ChromeStackTrace.swift +++ b/Sources/CartonHelpers/Parsers/ChromeStackTrace.swift @@ -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 diff --git a/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift b/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift index 823aebb9..f28126b1 100644 --- a/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift +++ b/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift @@ -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 { diff --git a/Sources/CartonHelpers/Parsers/FirefoxStackTrace.swift b/Sources/CartonHelpers/Parsers/FirefoxStackTrace.swift index b569c272..62faffa4 100644 --- a/Sources/CartonHelpers/Parsers/FirefoxStackTrace.swift +++ b/Sources/CartonHelpers/Parsers/FirefoxStackTrace.swift @@ -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 diff --git a/Sources/CartonHelpers/Parsers/SafariStackTrace.swift b/Sources/CartonHelpers/Parsers/SafariStackTrace.swift index 6654370f..63dadf23 100644 --- a/Sources/CartonHelpers/Parsers/SafariStackTrace.swift +++ b/Sources/CartonHelpers/Parsers/SafariStackTrace.swift @@ -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), diff --git a/Sources/CartonHelpers/Parsers/String+Regex.swift b/Sources/CartonHelpers/Parsers/String+Regex.swift index 7540c789..52f2de0f 100644 --- a/Sources/CartonHelpers/Parsers/String+Regex.swift +++ b/Sources/CartonHelpers/Parsers/String+Regex.swift @@ -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.. 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] } diff --git a/Sources/CartonHelpers/Parsers/TestsParser.swift b/Sources/CartonHelpers/Parsers/TestsParser.swift index 6a361c66..b98ea167 100644 --- a/Sources/CartonHelpers/Parsers/TestsParser.swift +++ b/Sources/CartonHelpers/Parsers/TestsParser.swift @@ -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 { diff --git a/Sources/CartonHelpers/ProcessRunner.swift b/Sources/CartonHelpers/ProcessRunner.swift index 7aa3b788..0d3ab2ab 100644 --- a/Sources/CartonHelpers/ProcessRunner.swift +++ b/Sources/CartonHelpers/ProcessRunner.swift @@ -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) }, diff --git a/Sources/CartonKit/Model/Entrypoint.swift b/Sources/CartonKit/Model/Entrypoint.swift index 3bbc02cd..9ba6b249 100644 --- a/Sources/CartonKit/Model/Entrypoint.swift +++ b/Sources/CartonKit/Model/Entrypoint.swift @@ -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) } } diff --git a/Sources/CartonKit/Server/Application.swift b/Sources/CartonKit/Server/Application.swift index 2f637ae4..e83e99c9 100644 --- a/Sources/CartonKit/Server/Application.swift +++ b/Sources/CartonKit/Server/Application.swift @@ -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), diff --git a/Sources/CartonKit/Server/Server.swift b/Sources/CartonKit/Server/Server.swift index 792877cb..a7ba7172 100644 --- a/Sources/CartonKit/Server/Server.swift +++ b/Sources/CartonKit/Server/Server.swift @@ -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 diff --git a/Sources/SwiftToolchain/Builder.swift b/Sources/SwiftToolchain/Builder.swift index 8f6b1dcd..de9a44ab 100644 --- a/Sources/SwiftToolchain/Builder.swift +++ b/Sources/SwiftToolchain/Builder.swift @@ -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) }, diff --git a/Sources/SwiftToolchain/Manifest.swift b/Sources/SwiftToolchain/Manifest.swift new file mode 100644 index 00000000..a2fd775f --- /dev/null +++ b/Sources/SwiftToolchain/Manifest.swift @@ -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 + ) + } +} diff --git a/Sources/SwiftToolchain/Package.swift b/Sources/SwiftToolchain/Package.swift deleted file mode 100644 index 840e750a..00000000 --- a/Sources/SwiftToolchain/Package.swift +++ /dev/null @@ -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 -} diff --git a/Sources/SwiftToolchain/Toolchain.swift b/Sources/SwiftToolchain/Toolchain.swift index 6d20da08..ff2a3bd3 100644 --- a/Sources/SwiftToolchain/Toolchain.swift +++ b/Sources/SwiftToolchain/Toolchain.swift @@ -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 + public let manifest: Result 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) diff --git a/Sources/SwiftToolchain/ToolchainInstallation.swift b/Sources/SwiftToolchain/ToolchainInstallation.swift index 075d2ff4..4c829cdf 100644 --- a/Sources/SwiftToolchain/ToolchainInstallation.swift +++ b/Sources/SwiftToolchain/ToolchainInstallation.swift @@ -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 diff --git a/Sources/SwiftToolchain/ToolchainManagement.swift b/Sources/SwiftToolchain/ToolchainManagement.swift index 2f11f059..741579ab 100644 --- a/Sources/SwiftToolchain/ToolchainManagement.swift +++ b/Sources/SwiftToolchain/ToolchainManagement.swift @@ -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 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 diff --git a/Sources/carton-release/Formula.swift b/Sources/carton-release/Formula.swift index d2e48a6e..df953edb 100644 --- a/Sources/carton-release/Formula.swift +++ b/Sources/carton-release/Formula.swift @@ -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()