Use libSwiftPM 5.6 to parse manifests (#302)
* Upgrade SwiftPM to 5.6. Use libSwiftPM to parse manifests. * Use SwiftWasm's swiftc
This commit is contained in:
parent
4ca0cdf939
commit
b89d7c79dd
|
@ -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
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 ?? "<unknown>")\n", inColor: .grey)
|
||||
terminal.write(" at \(item.location ?? "<unknown>")\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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Version>?
|
||||
switch self {
|
||||
case let .exact(version):
|
||||
return version == compatibleJSKitVersion
|
||||
case let .range(range):
|
||||
return range.upperBound >= compatibleJSKitVersion
|
||||
default:
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue