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:
yonihemi 2022-03-28 18:13:23 +08:00 committed by GitHub
parent 4ca0cdf939
commit b89d7c79dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 158 deletions

View File

@ -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
}
},

View File

@ -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"),

View File

@ -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 {

View File

@ -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")
}

View File

@ -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
)
}
}

View File

@ -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:
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)
}
}
}