diff --git a/Package.resolved b/Package.resolved index 7beae5f..851b5c1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/apple/swift-argument-parser.git", "state": { "branch": null, - "revision": "83b23d940471b313427da226196661856f6ba3e0", - "version": "0.4.4" + "revision": "e394bf350e38cb100b6bc4172834770ede1b7232", + "version": "1.0.3" } }, { @@ -78,16 +78,16 @@ "repositoryURL": "https://github.com/apple/swift-crypto.git", "state": { "branch": null, - "revision": "ddb07e896a2a8af79512543b1c7eb9797f8898a5", - "version": "1.1.7" + "revision": "3bea268b223651c4ab7b7b9ad62ef9b2d4143eb6", + "version": "1.1.6" } }, { "package": "swift-driver", "repositoryURL": "https://github.com/apple/swift-driver.git", "state": { - "branch": "release/5.5", - "revision": "86c54dacd270e0c43374c0cb9b2ceb2924c9ea72", + "branch": "release/5.6", + "revision": "9982f32f96a2e0e597d1b4a0af4a7e997dc471be", "version": null } }, @@ -95,8 +95,8 @@ "package": "llbuild", "repositoryURL": "https://github.com/apple/swift-llbuild.git", "state": { - "branch": "release/5.5", - "revision": "83c4bcb8dfca48cc065325287b55d08ff7b26428", + "branch": "release/5.6", + "revision": "acd686530e56122d916acd49a166beb9198e9b87", "version": null } }, @@ -167,8 +167,8 @@ "package": "SwiftPM", "repositoryURL": "https://github.com/apple/swift-package-manager.git", "state": { - "branch": "release/5.5", - "revision": "a29154a4137747bdbfe83ca26db4b24f8c4fcd31", + "branch": "release/5.6", + "revision": "d53983abc7d1628a47ee26b24cbf35b06ac50f6e", "version": null } }, @@ -176,8 +176,8 @@ "package": "swift-tools-support-core", "repositoryURL": "https://github.com/apple/swift-tools-support-core.git", "state": { - "branch": "release/5.5", - "revision": "3b586ce12865db205081acdcea79fe5509b28152", + "branch": "release/5.6", + "revision": "107e570e3565920174d5a25bc3a0340b32d16042", "version": null } }, diff --git a/Package.swift b/Package.swift index 376adec..ff8bb3a 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "carton", - platforms: [.macOS(.v10_15)], + platforms: [.macOS("10.15.4")], products: [ .library(name: "SwiftToolchain", targets: ["SwiftToolchain"]), .library(name: "CartonHelpers", targets: ["CartonHelpers"]), @@ -21,17 +21,17 @@ let package = Package( ), .package( url: "https://github.com/apple/swift-argument-parser.git", - from: "0.4.3" + .upToNextMinor(from: "1.0.3") ), .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), .package( name: "SwiftPM", url: "https://github.com/apple/swift-package-manager.git", - .branch("release/5.5") + .branch("release/5.6") ), .package( url: "https://github.com/apple/swift-tools-support-core.git", - .branch("release/5.5") + .branch("release/5.6") ), .package(url: "https://github.com/vapor/vapor.git", from: "4.53.0"), .package(url: "https://github.com/apple/swift-crypto.git", from: "1.1.0"), diff --git a/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift b/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift index 823aebb..2f637ce 100644 --- a/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift +++ b/Sources/CartonHelpers/Parsers/DiagnosticsParser.swift @@ -157,7 +157,7 @@ public struct DiagnosticsParser: ProcessOutputParser { for (file, messages) in diagnostics.sorted(by: { $0.key < $1.key }) { guard messages.count > 0 else { continue } terminal.write("\(" \(file) ", color: "[1m", "[7m")") // bold, reversed - terminal.write(" \(messages.first!.file)\(messages.first!.line)\n\n", inColor: .grey) + terminal.write(" \(messages.first!.file)\(messages.first!.line)\n\n", inColor: .gray) // Group messages that occur on sequential lines to provie a more readable output var groupedMessages = [[CustomDiagnostic]]() for message in messages { diff --git a/Sources/CartonKit/Server/Server.swift b/Sources/CartonKit/Server/Server.swift index d844b76..d3e34d6 100644 --- a/Sources/CartonKit/Server/Server.swift +++ b/Sources/CartonKit/Server/Server.swift @@ -268,13 +268,13 @@ extension Server { terminal.write("\nAn error occurred, here's a stack trace for it:\n", inColor: .red) stackTrace.forEach { item in terminal.write(" \(item.symbol)", inColor: .cyan) - terminal.write(" at \(item.location ?? "")\n", inColor: .grey) + terminal.write(" at \(item.location ?? "")\n", inColor: .gray) } } else { terminal.write("\nAn error occurred, here's the raw stack trace for it:\n", inColor: .red) terminal.write(" Please create an issue or PR to the Carton repository\n" + " with your browser name and this raw stack trace so\n" + - " we can add support for it: https://github.com/swiftwasm/carton\n", inColor: .grey) + " we can add support for it: https://github.com/swiftwasm/carton\n", inColor: .gray) terminal.write(rawStackTrace + "\n") } diff --git a/Sources/SwiftToolchain/Manifest.swift b/Sources/SwiftToolchain/Manifest.swift index 1472e60..63d0af6 100644 --- a/Sources/SwiftToolchain/Manifest.swift +++ b/Sources/SwiftToolchain/Manifest.swift @@ -12,29 +12,44 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Basics import CartonHelpers -import Foundation import PackageModel +import PackageLoading import TSCBasic +import Workspace extension Manifest { - static func from(swiftPath: AbsolutePath, terminal: InteractiveWriter) throws -> Manifest { + static func from(path: AbsolutePath, swiftc: AbsolutePath, fileSystem: FileSystem, terminal: InteractiveWriter) async 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 + let toolchain = ToolchainConfiguration(swiftCompilerPath: swiftc) + let loader = ManifestLoader(toolchain: toolchain) + let observability = ObservabilitySystem { _, diagnostic in + terminal.write("\n\(diagnostic)") + } + let workspace = try Workspace(fileSystem: fileSystem, forRootPackage: path, customManifestLoader: loader) + let manifest = try await workspace.loadRootManifest( + at: path, + observabilityScope: observability.topScope ) - decoder.userInfo[DumpedManifest.unencodedKey] = unencodedValues - let dumpedManifest = try decoder.decode(DumpedManifest.self, from: output) - return dumpedManifest.manifest + return manifest } public func resourcesPath(for target: TargetDescription) -> String { - "\(name)_\(target.name).resources" + "\(displayName)_\(target.name).resources" + } +} + +extension Workspace { + func loadRootManifest( + at path: AbsolutePath, + observabilityScope: ObservabilityScope + ) async throws -> Manifest { + try await withCheckedThrowingContinuation { continuation in + loadRootManifest(at: path, observabilityScope: observabilityScope) { result in + continuation.resume(with: result) + } + } } } @@ -45,80 +60,3 @@ public enum PackageType: String { case systemModule = "system-module" case manifest } - -// MARK: Custom Decodable Wrappers - -/// 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 - ) - let dependencies = try container.decode( - [PackageDependencyDescription].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) - let packageKind = try container.decode(PackageReference.Kind.self, forKey: .packageKind) - - manifest = Manifest( - name: name, - path: unencoded.path, - packageKind: packageKind, - packageLocation: unencoded.path.parentDirectory.pathString, - platforms: platforms, - version: unencoded.version, - toolsVersion: toolsVersion, - pkgConfig: pkgConfig, - providers: providers, - cLanguageStandard: cLanguageStandard, - cxxLanguageStandard: cxxLanguageStandard, - swiftLanguageVersions: swiftLanguageVersions, - dependencies: dependencies, - products: products, - targets: targets - ) - } -} diff --git a/Sources/SwiftToolchain/Toolchain.swift b/Sources/SwiftToolchain/Toolchain.swift index e70eef7..5cf45bf 100644 --- a/Sources/SwiftToolchain/Toolchain.swift +++ b/Sources/SwiftToolchain/Toolchain.swift @@ -32,6 +32,7 @@ enum ToolchainError: Error, CustomStringConvertible { case invalidResponse(url: String, status: UInt) case unsupportedOperatingSystem case noInstallationDirectory(path: String) + case noWorkingDirectory var description: String { switch self { @@ -60,28 +61,45 @@ enum ToolchainError: Error, CustomStringConvertible { return """ Failed to infer toolchain installation directory. Please make sure that \(path) exists. """ + case .noWorkingDirectory: + return "Working directory cannot be inferred from file system" } } } -extension PackageDependencyDescription.Requirement { +extension PackageDependency { var isJavaScriptKitCompatible: Bool { + var exactVersion: Version? + var versionRange: Range? switch self { - case let .exact(version): - return version == compatibleJSKitVersion - case let .range(range): - return range.upperBound >= compatibleJSKitVersion - default: - return false + case let .sourceControl(sourceControl): + switch sourceControl.requirement { + case let .exact(version): exactVersion = version + case let .range(range): versionRange = range + default: break + } + case let .registry(registry): + switch registry.requirement { + case let .exact(version): exactVersion = version + case let .range(range): versionRange = range + } + default: break } + if let exactVersion = exactVersion { + return exactVersion == compatibleJSKitVersion + } + if let versionRange = versionRange { + return versionRange.upperBound >= compatibleJSKitVersion + } + return false } - var versionDescription: String { + var requirementDescription: String { switch self { - case let .exact(version): - return version.description - case let .range(range): - return range.lowerBound.description + case let .sourceControl(sourceControl): + return sourceControl.requirement.description + case let .registry(registry): + return registry.requirement.description default: return "(Unknown)" } @@ -107,7 +125,13 @@ public final class Toolchain { self.version = version self.fileSystem = fileSystem self.terminal = terminal - manifest = Result { try Manifest.from(swiftPath: swiftPath, terminal: terminal) } + if let workingDirectory = fileSystem.currentWorkingDirectory { + let swiftc = swiftPath.parentDirectory.appending(component: "swiftc") + manifest = await Result { try await Manifest.from(path: workingDirectory, swiftc: swiftc, fileSystem: fileSystem, terminal: terminal) + } + } else { + manifest = .failure(ToolchainError.noWorkingDirectory) + } } private func inferBinPath(isRelease: Bool) throws -> AbsolutePath { @@ -199,6 +223,43 @@ public final class Toolchain { } } + private func emitJSKitWarningIfNeeded() throws { + let manifest = try self.manifest.get() + guard let jsKit = manifest.dependencies.first(where: { + $0.nameForTargetDependencyResolutionOnly == "JavaScriptKit" + }) else { + return + } + + switch jsKit { + case .fileSystem: + terminal.write( + """ + + The local version of JavaScriptKit found in your dependency tree is not known to be compatible \ + with carton \(cartonVersion). Please specify a JavaScriptKit dependency of version \ + \(compatibleJSKitVersion) in your `Package.swift`.\n + + """, + inColor: .red + ) + + default: + guard !jsKit.isJavaScriptKitCompatible else { return } + terminal.write( + """ + + JavaScriptKit requirement \(jsKit + .requirementDescription), which is present in your dependency tree is not \ + known to be compatible with carton \(cartonVersion). Please specify a JavaScriptKit \ + dependency of version \(compatibleJSKitVersion) in your `Package.swift`.\n + + """, + inColor: .red + ) + } + } + public func buildCurrentProject( product: String?, flavor: BuildFlavor @@ -206,41 +267,7 @@ public final class Toolchain { guard let product = try inferDevProduct(hint: product) else { throw ToolchainError.noExecutableProduct } - let manifest = try self.manifest.get() - let jsKit = manifest.dependencies.first { - $0.nameForTargetDependencyResolutionOnly == "JavaScriptKit" - } - - switch jsKit { - case let .scm(jsKit) where !jsKit.requirement.isJavaScriptKitCompatible: - let versionDescription = jsKit.requirement.versionDescription - - terminal.write( - """ - - JavaScriptKit \(versionDescription), which is present in your dependency tree is not \ - known to be compatible with carton \(cartonVersion). Please specify a JavaScriptKit \ - dependency on version \(compatibleJSKitVersion) in your `Package.swift`.\n - - """, - inColor: .red - ) - - case .local: - terminal.write( - """ - - The version of JavaScriptKit found in your dependency tree is not known to be compatible \ - with carton \(cartonVersion). Please specify a JavaScriptKit dependency on version \ - \(compatibleJSKitVersion) in your `Package.swift`.\n - - """, - inColor: .red - ) - - case nil, .scm: - break - } + try emitJSKitWarningIfNeeded() let binPath = try inferBinPath(isRelease: flavor.isRelease) let mainWasmPath = binPath.appending(component: "\(product.name).wasm") @@ -294,7 +321,7 @@ public final class Toolchain { ) async throws -> AbsolutePath { let manifest = try self.manifest.get() let binPath = try inferBinPath(isRelease: flavor.isRelease) - let testProductName = "\(manifest.name)PackageTests" + let testProductName = "\(manifest.displayName)PackageTests" let testBundlePath = binPath.appending(component: "\(testProductName).wasm") terminal.logLookup("- test bundle to run: ", testBundlePath.pathString) @@ -360,3 +387,14 @@ public final class Toolchain { try await TSCBasic.Process.run(args, terminal) } } + +extension Result where Failure == Error { + init(catching body: () async throws -> Success) async { + do { + let value = try await body() + self = .success(value) + } catch { + self = .failure(error) + } + } +}