From c4cfd4939d1481c0ac659d2dbe12c6cf69e82844 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 16 Nov 2022 10:56:06 +0000 Subject: [PATCH 01/15] Commands: add `experimental-destination` command Also adds `experimental-destination list` subcommand, which prints a list of available destinations. `ArtifactsArchiveMetadata` schema version validation is implemented to accept new 1.1 schema version with the new destinations bundle type. --- Sources/Basics/FileSystem+Extensions.swift | 47 +++++++- Sources/Commands/CMakeLists.txt | 2 + .../DestinationTools/DestinationCommand.swift | 26 ++++ .../DestinationTools/ListDestinations.swift | 114 ++++++++++++++++++ .../PackageTools/SwiftPackageTool.swift | 2 + Sources/CoreCommands/Options.swift | 2 +- Sources/CoreCommands/SwiftTool.swift | 21 +--- .../ArtifactsArchiveMetadata.swift | 20 ++- 8 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 Sources/Commands/DestinationTools/DestinationCommand.swift create mode 100644 Sources/Commands/DestinationTools/ListDestinations.swift diff --git a/Sources/Basics/FileSystem+Extensions.swift b/Sources/Basics/FileSystem+Extensions.swift index 60ca928f8ea..de65e74b427 100644 --- a/Sources/Basics/FileSystem+Extensions.swift +++ b/Sources/Basics/FileSystem+Extensions.swift @@ -224,7 +224,7 @@ extension FileSystem { } } -// MARK: - cross-compilation SDKs +// MARK: - cross-compilation destinations private let crossCompilationDestinationsDirectoryName = "destinations" @@ -232,18 +232,57 @@ extension FileSystem { /// SwiftPM cross-compilation destinations directory (if exists) public var swiftPMCrossCompilationDestinationsDirectory: AbsolutePath { get throws { - if let path = try self.idiomaticSwiftPMDirectory { + if let path = try idiomaticSwiftPMDirectory { return path.appending(component: crossCompilationDestinationsDirectoryName) } else { - return try self.dotSwiftPMCrossCompilationDestinationsDirectory + return try dotSwiftPMCrossCompilationDestinationsDirectory } } } fileprivate var dotSwiftPMCrossCompilationDestinationsDirectory: AbsolutePath { get throws { - return try self.dotSwiftPM.appending(component: crossCompilationDestinationsDirectoryName) + return try dotSwiftPM.appending(component: crossCompilationDestinationsDirectoryName) + } + } + + public func getSharedCrossCompilationDestinationsDirectory( + explicitDirectory: AbsolutePath? + ) throws -> AbsolutePath? { + if let explicitDestinationsDirectory = explicitDirectory { + // Create the explicit SDKs path if necessary + if !exists(explicitDestinationsDirectory) { + try createDirectory(explicitDestinationsDirectory, recursive: true) + } + return explicitDestinationsDirectory + } else { + return try swiftPMCrossCompilationDestinationsDirectory + } + } + + public func getOrCreateSwiftPMCrossCompilationDestinationsDirectory() throws -> AbsolutePath { + let idiomaticDestinationsDirectory = try swiftPMCrossCompilationDestinationsDirectory + + // Create idiomatic if necessary + if !exists(idiomaticDestinationsDirectory) { + try createDirectory(idiomaticDestinationsDirectory, recursive: true) + } + // Create ~/.swiftpm if necessary + if !exists(try dotSwiftPM) { + try createDirectory(dotSwiftPM, recursive: true) + } + // Create ~/.swiftpm/destinations symlink if necessary + // locking ~/.swiftpm to protect from concurrent access + try withLock(on: dotSwiftPM, type: .exclusive) { + if !exists(try dotSwiftPMCrossCompilationDestinationsDirectory, followSymlink: false) { + try createSymbolicLink( + dotSwiftPMCrossCompilationDestinationsDirectory, + pointingAt: idiomaticDestinationsDirectory, + relative: false + ) + } } + return idiomaticDestinationsDirectory } } diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index 0c6cc70a4ab..4f84e503ce0 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -7,6 +7,8 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_library(Commands + DestinationTools/DestinationCommand.swift + DestinationTools/ListDestinations.swift PackageTools/APIDiff.swift PackageTools/ArchiveSource.swift PackageTools/CompletionTool.swift diff --git a/Sources/Commands/DestinationTools/DestinationCommand.swift b/Sources/Commands/DestinationTools/DestinationCommand.swift new file mode 100644 index 00000000000..de353232b70 --- /dev/null +++ b/Sources/Commands/DestinationTools/DestinationCommand.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ArgumentParser + +public struct DestinationCommand: ParsableCommand { + public static var configuration = CommandConfiguration( + commandName: "experimental-destination", + _superCommandName: "package", + abstract: "Perform operations on Swift cross-compilation destinations.", + subcommands: [ + ListDestinations.self, + ], + helpNames: [.short, .long, .customLong("help", withSingleDash: true)]) + + public init() {} +} diff --git a/Sources/Commands/DestinationTools/ListDestinations.swift b/Sources/Commands/DestinationTools/ListDestinations.swift new file mode 100644 index 00000000000..ff75ac69d28 --- /dev/null +++ b/Sources/Commands/DestinationTools/ListDestinations.swift @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Basics +import CoreCommands +import SPMBuildCore +import PackageModel +import TSCBasic + +extension DestinationCommand { + struct ListDestinations: ParsableCommand { + static let configuration = CommandConfiguration( + commandName: "list", + abstract: + """ + Print a list of IDs of available cross-compilation destinations available on the filesystem. + """ + ) + + @OptionGroup() + public var locations: LocationOptions + + func run() throws { + let fileSystem = localFileSystem + let observabilitySystem = ObservabilitySystem( + SwiftToolObservabilityHandler(outputStream: stdoutStream, logLevel: .warning) + ) + let observabilityScope = observabilitySystem.topScope + + guard var destinationsDirectory = try fileSystem.getSharedCrossCompilationDestinationsDirectory( + explicitDirectory: locations.crossCompilationDestinationsDirectory + ) else { + let expectedPath = try fileSystem.swiftPMCrossCompilationDestinationsDirectory + throw StringError( + "Couldn't find or create a directory where cross-compilation destinations are stored: \(expectedPath)" + ) + } + + if !fileSystem.exists(destinationsDirectory) { + destinationsDirectory = try fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory() + } + + // Get absolute paths to available destination bundles. + let destinationBundles = try fileSystem.getDirectoryContents(destinationsDirectory).filter { + $0.hasSuffix(BinaryTarget.Kind.artifactsArchive.fileExtension) + }.map { + destinationsDirectory.appending(components: [$0]) + } + + // Enumerate available bundles and parse manifests for each of them, then validate supplied destinations. + for bundlePath in destinationBundles { + do { + let parsedManifest = try ArtifactsArchiveMetadata.parse( + fileSystem: fileSystem, + rootPath: bundlePath + ) + + for (artifactID, artifactMetadata) in parsedManifest.artifacts + where artifactMetadata.type == .crossCompilationDestination { + for variant in artifactMetadata.variants { + let destinationJSONPath = try bundlePath + .appending(RelativePath(validating: variant.path)) + .appending(component: "destination.json") + + guard fileSystem.exists(destinationJSONPath) else { + observabilityScope.emit( + .warning( + """ + Destination metadata file not found at \( + destinationJSONPath + ) for a variant of artifact \(artifactID) + """ + ) + ) + + continue + } + + do { + _ = try Destination( + fromFile: destinationJSONPath, fileSystem: fileSystem + ) + } catch { + observabilityScope.emit( + .warning( + "Couldn't parse destination metadata at \(destinationJSONPath): \(error)" + ) + ) + } + } + + print(artifactID) + } + } catch { + observabilityScope.emit( + .warning( + "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" + ) + ) + } + } + } + } +} diff --git a/Sources/Commands/PackageTools/SwiftPackageTool.swift b/Sources/Commands/PackageTools/SwiftPackageTool.swift index 6ea1c236afd..61b294965ea 100644 --- a/Sources/Commands/PackageTools/SwiftPackageTool.swift +++ b/Sources/Commands/PackageTools/SwiftPackageTool.swift @@ -56,6 +56,8 @@ public struct SwiftPackageTool: ParsableCommand { Resolve.self, Fetch.self, + DestinationCommand.self, + ShowDependencies.self, ToolsVersionCommand.self, ComputeChecksum.self, diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 36aa9b03ea3..d2350192e80 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -85,7 +85,7 @@ public struct LocationOptions: ParsableArguments { public var customCompileDestination: AbsolutePath? @Option(name: .customLong("experimental-destinations-path"), help: .hidden, completion: .directory) - var crossCompilationDestinationsDirectory: AbsolutePath? + public var crossCompilationDestinationsDirectory: AbsolutePath? } public struct CachingOptions: ParsableArguments { diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index 45c93ccfb0d..e8eff8e4627 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -161,7 +161,7 @@ public final class SwiftTool { /// Path to the shared configuration directory public let sharedConfigurationDirectory: AbsolutePath? - /// Path to the cross-compilation SDK directory. + /// Path to the cross-compilation destinations directory. public let sharedCrossCompilationDestinationsDirectory: AbsolutePath? /// Cancellator to handle cancellation of outstanding work when handling SIGINT @@ -310,7 +310,9 @@ public final class SwiftTool { self.sharedSecurityDirectory = try getSharedSecurityDirectory(options: self.options, fileSystem: fileSystem) self.sharedConfigurationDirectory = try getSharedConfigurationDirectory(options: self.options, fileSystem: fileSystem) self.sharedCacheDirectory = try getSharedCacheDirectory(options: self.options, fileSystem: fileSystem) - self.sharedCrossCompilationDestinationsDirectory = try getSharedCrossCompilationDestinationsDirectory(options: self.options, fileSystem: fileSystem) + self.sharedCrossCompilationDestinationsDirectory = try fileSystem.getSharedCrossCompilationDestinationsDirectory( + explicitDirectory: self.options.locations.crossCompilationDestinationsDirectory + ) // set global process logging handler Process.loggingHandler = { self.observabilityScope.emit(debug: $0) } @@ -783,21 +785,6 @@ private func getSharedCacheDirectory(options: GlobalOptions, fileSystem: FileSys } } -private func getSharedCrossCompilationDestinationsDirectory( - options: GlobalOptions, - fileSystem: FileSystem -) throws -> AbsolutePath? { - if let explicitDestinationsDirectory = options.locations.crossCompilationDestinationsDirectory { - // Create the explicit destinations path if necessary - if !fileSystem.exists(explicitDestinationsDirectory) { - try fileSystem.createDirectory(explicitDestinationsDirectory, recursive: true) - } - return explicitDestinationsDirectory - } else { - return try fileSystem.swiftPMCrossCompilationDestinationsDirectory - } -} - extension Basics.Diagnostic { static func unsupportedFlag(_ flag: String) -> Self { .warning("\(flag) is an *unsupported* option which can be removed at any time; do not rely on it") diff --git a/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift b/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift index c363ef256f7..c63cfb61822 100644 --- a/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift +++ b/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift @@ -15,6 +15,7 @@ import PackageModel import TSCBasic import struct TSCUtility.Triple +import struct TSCUtility.Version public struct ArtifactsArchiveMetadata: Equatable { public let schemaVersion: String @@ -26,9 +27,9 @@ public struct ArtifactsArchiveMetadata: Equatable { } public struct Artifact: Equatable { - let type: ArtifactType + public let type: ArtifactType let version: String - let variants: [Variant] + public let variants: [Variant] public init(type: ArtifactsArchiveMetadata.ArtifactType, version: String, variants: [Variant]) { self.type = type @@ -42,10 +43,11 @@ public struct ArtifactsArchiveMetadata: Equatable { // This can also support resource-only artifacts as well. For example, 3d models along with associated textures, or fonts, etc. public enum ArtifactType: String, RawRepresentable, Decodable { case executable + case crossCompilationDestination } public struct Variant: Equatable { - let path: String + public let path: String let supportedTriples: [Triple] public init(path: String, supportedTriples: [Triple]) { @@ -65,7 +67,17 @@ extension ArtifactsArchiveMetadata { do { let data: Data = try fileSystem.readFileContents(path) let decoder = JSONDecoder.makeWithDefaults() - return try decoder.decode(ArtifactsArchiveMetadata.self, from: data) + let decodedMetadata = try decoder.decode(ArtifactsArchiveMetadata.self, from: data) + let version = try Version( + versionString: decodedMetadata.schemaVersion, + usesLenientParsing: true + ) + + guard version <= Version(1, 1, 0) else { + throw StringError("invalid `schemaVersion` of bundle manifest at `\(path)`: \(decodedMetadata.schemaVersion)") + } + + return decodedMetadata } catch { throw StringError("failed parsing ArtifactsArchive info.json at '\(path)': \(error)") } From acfd9eba58b73208abcfb6ccb06cb2b08fd2ed01 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 17 Nov 2022 14:34:48 +0000 Subject: [PATCH 02/15] Refactor `DestinationCommand.ListDestinations` --- .../DestinationTools/ListDestinations.swift | 80 +++++++++++-------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/Sources/Commands/DestinationTools/ListDestinations.swift b/Sources/Commands/DestinationTools/ListDestinations.swift index ff75ac69d28..1c8ef6fcccb 100644 --- a/Sources/Commands/DestinationTools/ListDestinations.swift +++ b/Sources/Commands/DestinationTools/ListDestinations.swift @@ -65,50 +65,64 @@ extension DestinationCommand { rootPath: bundlePath ) - for (artifactID, artifactMetadata) in parsedManifest.artifacts - where artifactMetadata.type == .crossCompilationDestination { - for variant in artifactMetadata.variants { - let destinationJSONPath = try bundlePath - .appending(RelativePath(validating: variant.path)) - .appending(component: "destination.json") + try parsedManifest.printValidatedArtifactIDs( + bundlePath: bundlePath, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + } catch { + observabilityScope.emit( + .warning( + "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" + ) + ) + } + } + } + } +} - guard fileSystem.exists(destinationJSONPath) else { - observabilityScope.emit( - .warning( - """ - Destination metadata file not found at \( - destinationJSONPath - ) for a variant of artifact \(artifactID) - """ - ) - ) +private extension ArtifactsArchiveMetadata { + func printValidatedArtifactIDs( + bundlePath: AbsolutePath, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope + ) throws { + for (artifactID, artifactMetadata) in artifacts + where artifactMetadata.type == .crossCompilationDestination { + for variant in artifactMetadata.variants { + let destinationJSONPath = try bundlePath + .appending(RelativePath(validating: variant.path)) + .appending(component: "destination.json") - continue - } + guard fileSystem.exists(destinationJSONPath) else { + observabilityScope.emit( + .warning( + """ + Destination metadata file not found at \( + destinationJSONPath + ) for a variant of artifact \(artifactID) + """ + ) + ) - do { - _ = try Destination( - fromFile: destinationJSONPath, fileSystem: fileSystem - ) - } catch { - observabilityScope.emit( - .warning( - "Couldn't parse destination metadata at \(destinationJSONPath): \(error)" - ) - ) - } - } + continue + } - print(artifactID) - } + do { + _ = try Destination( + fromFile: destinationJSONPath, fileSystem: fileSystem + ) } catch { observabilityScope.emit( .warning( - "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" + "Couldn't parse destination metadata at \(destinationJSONPath): \(error)" ) ) } } + + print(artifactID) } } } From 4fdd3adb194d749114cc2491f80e098fe0ac76f5 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 22 Nov 2022 00:07:45 +0000 Subject: [PATCH 03/15] Introduce `DestinationsBundle` to `SPMBuildCore` --- .../DestinationTools/ListDestinations.swift | 49 +---------- .../ArtifactsArchiveMetadata.swift | 12 +-- Sources/SPMBuildCore/CMakeLists.txt | 1 + Sources/SPMBuildCore/DestinationsBundle.swift | 81 +++++++++++++++++++ 4 files changed, 92 insertions(+), 51 deletions(-) create mode 100644 Sources/SPMBuildCore/DestinationsBundle.swift diff --git a/Sources/Commands/DestinationTools/ListDestinations.swift b/Sources/Commands/DestinationTools/ListDestinations.swift index 1c8ef6fcccb..4eb8d33517e 100644 --- a/Sources/Commands/DestinationTools/ListDestinations.swift +++ b/Sources/Commands/DestinationTools/ListDestinations.swift @@ -65,64 +65,21 @@ extension DestinationCommand { rootPath: bundlePath ) - try parsedManifest.printValidatedArtifactIDs( + let destinationsBundle = try parsedManifest.validateDestinationsBundle( bundlePath: bundlePath, fileSystem: fileSystem, observabilityScope: observabilityScope ) - } catch { - observabilityScope.emit( - .warning( - "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" - ) - ) - } - } - } - } -} - -private extension ArtifactsArchiveMetadata { - func printValidatedArtifactIDs( - bundlePath: AbsolutePath, - fileSystem: FileSystem, - observabilityScope: ObservabilityScope - ) throws { - for (artifactID, artifactMetadata) in artifacts - where artifactMetadata.type == .crossCompilationDestination { - for variant in artifactMetadata.variants { - let destinationJSONPath = try bundlePath - .appending(RelativePath(validating: variant.path)) - .appending(component: "destination.json") - guard fileSystem.exists(destinationJSONPath) else { - observabilityScope.emit( - .warning( - """ - Destination metadata file not found at \( - destinationJSONPath - ) for a variant of artifact \(artifactID) - """ - ) - ) - - continue - } - - do { - _ = try Destination( - fromFile: destinationJSONPath, fileSystem: fileSystem - ) + destinationsBundle.artifacts.keys.forEach { print($0) } } catch { observabilityScope.emit( .warning( - "Couldn't parse destination metadata at \(destinationJSONPath): \(error)" + "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" ) ) } } - - print(artifactID) } } } diff --git a/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift b/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift index c63cfb61822..a584596e5ca 100644 --- a/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift +++ b/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift @@ -27,9 +27,9 @@ public struct ArtifactsArchiveMetadata: Equatable { } public struct Artifact: Equatable { - public let type: ArtifactType + let type: ArtifactType let version: String - public let variants: [Variant] + let variants: [Variant] public init(type: ArtifactsArchiveMetadata.ArtifactType, version: String, variants: [Variant]) { self.type = type @@ -47,7 +47,7 @@ public struct ArtifactsArchiveMetadata: Equatable { } public struct Variant: Equatable { - public let path: String + let path: String let supportedTriples: [Triple] public init(path: String, supportedTriples: [Triple]) { @@ -73,11 +73,13 @@ extension ArtifactsArchiveMetadata { usesLenientParsing: true ) - guard version <= Version(1, 1, 0) else { + switch (version.major, version.minor) { + case (1, 1), (1, 0): + return decodedMetadata + default: throw StringError("invalid `schemaVersion` of bundle manifest at `\(path)`: \(decodedMetadata.schemaVersion)") } - return decodedMetadata } catch { throw StringError("failed parsing ArtifactsArchive info.json at '\(path)': \(error)") } diff --git a/Sources/SPMBuildCore/CMakeLists.txt b/Sources/SPMBuildCore/CMakeLists.txt index b5872c46afc..75179ca76f1 100644 --- a/Sources/SPMBuildCore/CMakeLists.txt +++ b/Sources/SPMBuildCore/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(SPMBuildCore BuildSystemCommand.swift BuildSystemDelegate.swift BuiltTestProduct.swift + CrossCompilationDestinations.swift PluginContextSerializer.swift PluginInvocation.swift PluginMessages.swift diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift new file mode 100644 index 00000000000..5fb6e8c3be3 --- /dev/null +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import TSCBasic + +/// Represents an `.artifactbundle` on the filesystem that contains cross-compilation destinations. +public struct DestinationsBundle { + public struct Variant { + let metadata: ArtifactsArchiveMetadata.Variant + let destination: Destination + } + + let path: AbsolutePath + + /// Mapping of artifact IDs to variants available for a corresponding artifact. + public fileprivate(set) var artifacts = [String: [Variant]]() +} + +extension ArtifactsArchiveMetadata { + public func validateDestinationsBundle( + bundlePath: AbsolutePath, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope + ) throws -> DestinationsBundle { + var result = DestinationsBundle(path: bundlePath) + + for (artifactID, artifactMetadata) in artifacts + where artifactMetadata.type == .crossCompilationDestination { + var variants = [DestinationsBundle.Variant]() + + for variant in artifactMetadata.variants { + let destinationJSONPath = try bundlePath + .appending(RelativePath(validating: variant.path)) + .appending(component: "destination.json") + + guard fileSystem.exists(destinationJSONPath) else { + observabilityScope.emit( + .warning( + """ + Destination metadata file not found at \( + destinationJSONPath + ) for a variant of artifact \(artifactID) + """ + ) + ) + + continue + } + + do { + let destination = try Destination( + fromFile: destinationJSONPath, fileSystem: fileSystem + ) + + variants.append(.init(metadata: variant, destination: destination)) + } catch { + observabilityScope.emit( + .warning( + "Couldn't parse destination metadata at \(destinationJSONPath): \(error)" + ) + ) + } + } + + result.artifacts[artifactID] = variants + } + + return result + } +} From ecd74b4dcaf9d6dcb89db669a3dc03a4e674fc3c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 22 Nov 2022 00:15:34 +0000 Subject: [PATCH 04/15] Move more parsing code to `DestinationsBundle.swift` --- .../DestinationTools/ListDestinations.swift | 7 +---- Sources/SPMBuildCore/DestinationsBundle.swift | 29 +++++++++++++++++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Sources/Commands/DestinationTools/ListDestinations.swift b/Sources/Commands/DestinationTools/ListDestinations.swift index 4eb8d33517e..eaf919f5fe8 100644 --- a/Sources/Commands/DestinationTools/ListDestinations.swift +++ b/Sources/Commands/DestinationTools/ListDestinations.swift @@ -60,12 +60,7 @@ extension DestinationCommand { // Enumerate available bundles and parse manifests for each of them, then validate supplied destinations. for bundlePath in destinationBundles { do { - let parsedManifest = try ArtifactsArchiveMetadata.parse( - fileSystem: fileSystem, - rootPath: bundlePath - ) - - let destinationsBundle = try parsedManifest.validateDestinationsBundle( + let destinationsBundle = try DestinationsBundle.parseAndValidate( bundlePath: bundlePath, fileSystem: fileSystem, observabilityScope: observabilityScope diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift index 5fb6e8c3be3..f524308edfa 100644 --- a/Sources/SPMBuildCore/DestinationsBundle.swift +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -25,10 +25,35 @@ public struct DestinationsBundle { /// Mapping of artifact IDs to variants available for a corresponding artifact. public fileprivate(set) var artifacts = [String: [Variant]]() + + /// Parses metadata of an `.artifactbundle` and validates it as a bundle containing + /// cross-compilation destinations. + /// - Parameters: + /// - bundlePath: path to the bundle root directory. + /// - fileSystem: filesystem containing the bundle. + /// - observabilityScope: observability scope to log validation warnings. + /// - Returns: Validated `DestinationsBundle` containing validated `Destination` values for + /// each artifact and its variants. + public static func parseAndValidate( + bundlePath: AbsolutePath, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope + ) throws -> Self { + let parsedManifest = try ArtifactsArchiveMetadata.parse( + fileSystem: fileSystem, + rootPath: bundlePath + ) + + return try parsedManifest.validateDestinationsBundle( + bundlePath: bundlePath, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + } } -extension ArtifactsArchiveMetadata { - public func validateDestinationsBundle( +private extension ArtifactsArchiveMetadata { + func validateDestinationsBundle( bundlePath: AbsolutePath, fileSystem: FileSystem, observabilityScope: ObservabilityScope From 13ec06e2e30e8a6a82bc4b572270f83c39132a0d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 22 Nov 2022 10:51:38 +0000 Subject: [PATCH 05/15] Clean up variable naming, fix CMake filename mismatch --- Sources/SPMBuildCore/CMakeLists.txt | 2 +- Sources/SPMBuildCore/DestinationsBundle.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/SPMBuildCore/CMakeLists.txt b/Sources/SPMBuildCore/CMakeLists.txt index 75179ca76f1..e213f8b91bf 100644 --- a/Sources/SPMBuildCore/CMakeLists.txt +++ b/Sources/SPMBuildCore/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(SPMBuildCore BuildSystemCommand.swift BuildSystemDelegate.swift BuiltTestProduct.swift - CrossCompilationDestinations.swift + DestinationsBundle.swift PluginContextSerializer.swift PluginInvocation.swift PluginMessages.swift diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift index f524308edfa..68c6c3972bc 100644 --- a/Sources/SPMBuildCore/DestinationsBundle.swift +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -64,9 +64,9 @@ private extension ArtifactsArchiveMetadata { where artifactMetadata.type == .crossCompilationDestination { var variants = [DestinationsBundle.Variant]() - for variant in artifactMetadata.variants { + for variantMetadata in artifactMetadata.variants { let destinationJSONPath = try bundlePath - .appending(RelativePath(validating: variant.path)) + .appending(RelativePath(validating: variantMetadata.path)) .appending(component: "destination.json") guard fileSystem.exists(destinationJSONPath) else { @@ -88,7 +88,7 @@ private extension ArtifactsArchiveMetadata { fromFile: destinationJSONPath, fileSystem: fileSystem ) - variants.append(.init(metadata: variant, destination: destination)) + variants.append(.init(metadata: variantMetadata, destination: destination)) } catch { observabilityScope.emit( .warning( From ccb6c39e6618b84dbfeb6296174c875483bf208d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 22 Nov 2022 16:22:10 +0000 Subject: [PATCH 06/15] SPMBuildCore: allow selecting destinations by query --- .../DestinationTools/ListDestinations.swift | 30 ++--- Sources/PackageModel/Manifest.swift | 6 +- .../ArtifactsArchiveMetadata.swift | 7 +- Sources/SPMBuildCore/DestinationsBundle.swift | 105 ++++++++++++++++- Sources/SPMTestSupport/Observability.swift | 2 +- .../PackageModelTests/DestinationTests.swift | 106 +++++++++++++++--- 6 files changed, 210 insertions(+), 46 deletions(-) diff --git a/Sources/Commands/DestinationTools/ListDestinations.swift b/Sources/Commands/DestinationTools/ListDestinations.swift index eaf919f5fe8..307152839d8 100644 --- a/Sources/Commands/DestinationTools/ListDestinations.swift +++ b/Sources/Commands/DestinationTools/ListDestinations.swift @@ -50,30 +50,14 @@ extension DestinationCommand { destinationsDirectory = try fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory() } - // Get absolute paths to available destination bundles. - let destinationBundles = try fileSystem.getDirectoryContents(destinationsDirectory).filter { - $0.hasSuffix(BinaryTarget.Kind.artifactsArchive.fileExtension) - }.map { - destinationsDirectory.appending(components: [$0]) - } - - // Enumerate available bundles and parse manifests for each of them, then validate supplied destinations. - for bundlePath in destinationBundles { - do { - let destinationsBundle = try DestinationsBundle.parseAndValidate( - bundlePath: bundlePath, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) + let validBundles = try DestinationsBundle.getAllValidBundles( + destinationsDirectory: destinationsDirectory, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) - destinationsBundle.artifacts.keys.forEach { print($0) } - } catch { - observabilityScope.emit( - .warning( - "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" - ) - ) - } + for bundle in validBundles { + bundle.artifacts.keys.forEach { print($0) } } } } diff --git a/Sources/PackageModel/Manifest.swift b/Sources/PackageModel/Manifest.swift index 1b28e3c4db9..06bf1c21bf7 100644 --- a/Sources/PackageModel/Manifest.swift +++ b/Sources/PackageModel/Manifest.swift @@ -180,14 +180,14 @@ public final class Manifest { } var requiredDependencies: Set = [] - for targetTriple in self.targetsRequired(for: products) { - for targetDependency in targetTriple.dependencies { + for linuxGNUTargetTriple in self.targetsRequired(for: products) { + for targetDependency in linuxGNUTargetTriple.dependencies { if let dependency = self.packageDependency(referencedBy: targetDependency) { requiredDependencies.insert(dependency.identity) } } - targetTriple.pluginUsages?.forEach { + linuxGNUTargetTriple.pluginUsages?.forEach { if let dependency = self.packageDependency(referencedBy: $0) { requiredDependencies.insert(dependency.identity) } diff --git a/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift b/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift index a584596e5ca..1af3c0bb08c 100644 --- a/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift +++ b/Sources/SPMBuildCore/ArtifactsArchiveMetadata.swift @@ -38,9 +38,10 @@ public struct ArtifactsArchiveMetadata: Equatable { } } - // In the future we are likely to extend the ArtifactsArchive file format to carry other types of artifacts beyond executables. - // Additional fields may be required to support these new artifact types e.g. headers path for libraries. - // This can also support resource-only artifacts as well. For example, 3d models along with associated textures, or fonts, etc. + // In the future we are likely to extend the ArtifactsArchive file format to carry other types of artifacts beyond + // executables and cross-compilation destinations. Additional fields may be required to support these new artifact + // types e.g. headers path for libraries. This can also support resource-only artifacts as well. For example, + // 3d models along with associated textures, or fonts, etc. public enum ArtifactType: String, RawRepresentable, Decodable { case executable case crossCompilationDestination diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift index 68c6c3972bc..3d92dcefdb0 100644 --- a/Sources/SPMBuildCore/DestinationsBundle.swift +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -13,10 +13,11 @@ import Basics import PackageModel import TSCBasic +import TSCUtility /// Represents an `.artifactbundle` on the filesystem that contains cross-compilation destinations. public struct DestinationsBundle { - public struct Variant { + public struct Variant: Equatable { let metadata: ArtifactsArchiveMetadata.Variant let destination: Destination } @@ -26,6 +27,41 @@ public struct DestinationsBundle { /// Mapping of artifact IDs to variants available for a corresponding artifact. public fileprivate(set) var artifacts = [String: [Variant]]() + /// Lists all valid cross-compilation destination bundles in a given directory. + /// - Parameters: + /// - destinationsDirectory: the directory to scan for destination bundles. + /// - fileSystem: the filesystem the directory is located on. + /// - observabilityScope: observability scope to report bundle validation errors. + /// - Returns: an array of valid destination bundles. + public static func getAllValidBundles( + destinationsDirectory: AbsolutePath, + fileSystem: FileSystem, + observabilityScope: ObservabilityScope + ) throws -> [Self] { + // Get absolute paths to available destination bundles. + try fileSystem.getDirectoryContents(destinationsDirectory).filter { + $0.hasSuffix(BinaryTarget.Kind.artifactsArchive.fileExtension) + }.map { + destinationsDirectory.appending(components: [$0]) + }.compactMap { + do { + // Enumerate available bundles and parse manifests for each of them, then validate supplied destinations. + return try Self.parseAndValidate( + bundlePath: $0, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + } catch { + observabilityScope.emit( + .warning( + "Couldn't parse `info.json` manifest of a destination bundle at \($0): \(error)" + ) + ) + return nil + } + } + } + /// Parses metadata of an `.artifactbundle` and validates it as a bundle containing /// cross-compilation destinations. /// - Parameters: @@ -104,3 +140,70 @@ private extension ArtifactsArchiveMetadata { return result } } + +extension Array where Element == DestinationsBundle { + /// Select destinations in an array matching a given query and host triple. + /// - Parameters: + /// - query: either an artifact ID or target triple to filter with. + /// - hostTriple: triple of the host building with these destinations. + /// - observabilityScope: observability scope to log warnings about multiple matches. + /// - Returns: `Destination` value matching `query` either by artifact ID or target triple. + public func selectDestination( + matching query: String, + hostTriple: Triple, + observabilityScope: ObservabilityScope + ) -> Destination? { + var matchedByID: (path: AbsolutePath, variant: DestinationsBundle.Variant)? + var matchedByTriple: (path: AbsolutePath, variant: DestinationsBundle.Variant)? + + for bundle in self { + for (artifactID, variants) in bundle.artifacts { + for variant in variants { + guard variant.metadata.supportedTriples.contains(hostTriple) else { + continue + } + + if artifactID == query { + if let matchedByID = matchedByID { + observabilityScope.emit(warning: + """ + multiple destinations match ID `\(artifactID)` and host triple \( + hostTriple + ), selected one at \(matchedByID.path.appending(component: matchedByID.variant.metadata.path)) + """ + ) + } else { + matchedByID = (bundle.path, variant) + } + } + + if variant.destination.targetTriple?.tripleString == query { + if let matchedByTriple = matchedByTriple { + observabilityScope.emit(warning: + """ + multiple destinations match target triple `\(query)` and host triple \( + hostTriple + ), selected one at \(matchedByTriple.path.appending(component: matchedByTriple.variant.metadata.path)) + """ + ) + } else { + matchedByTriple = (bundle.path, variant) + } + } + } + } + } + + if let matchedByID = matchedByID, let matchedByTriple = matchedByTriple, matchedByID != matchedByTriple { + observabilityScope.emit(warning: + """ + multiple destinations match the query `\(query)` and host triple \( + hostTriple + ), selected one at \(matchedByID.path.appending(component: matchedByID.variant.metadata.path)) + """ + ) + } + + return matchedByID?.variant.destination ?? matchedByTriple?.variant.destination + } +} diff --git a/Sources/SPMTestSupport/Observability.swift b/Sources/SPMTestSupport/Observability.swift index 335c24b009c..95309ca4262 100644 --- a/Sources/SPMTestSupport/Observability.swift +++ b/Sources/SPMTestSupport/Observability.swift @@ -48,7 +48,7 @@ public struct TestingObservability { self.collector.hasWarnings } - struct Collector: ObservabilityHandlerProvider, DiagnosticsHandler, CustomStringConvertible { + final class Collector: ObservabilityHandlerProvider, DiagnosticsHandler, CustomStringConvertible { var diagnosticsHandler: DiagnosticsHandler { return self } let diagnostics: ThreadSafeArrayStore diff --git a/Tests/PackageModelTests/DestinationTests.swift b/Tests/PackageModelTests/DestinationTests.swift index 941029d281f..8d6e2ff3e69 100644 --- a/Tests/PackageModelTests/DestinationTests.swift +++ b/Tests/PackageModelTests/DestinationTests.swift @@ -12,6 +12,7 @@ import Basics @testable import PackageModel +@testable import SPMBuildCore import TSCBasic import TSCUtility import XCTest @@ -19,8 +20,9 @@ import XCTest private let bundleRootPath = try! AbsolutePath(validating: "/tmp/cross-toolchain") private let toolchainBinDir = RelativePath("swift.xctoolchain/usr/bin") private let sdkRootDir = RelativePath("ubuntu-jammy.sdk") -private let hostTriple = "arm64-apple-darwin22.1.0" -private let targetTriple = "x86_64-unknown-linux-gnu" +private let hostTriple = try! Triple("arm64-apple-darwin22.1.0") +private let linuxGNUTargetTriple = try! Triple("x86_64-unknown-linux-gnu") +private let linuxMuslTargetTriple = try! Triple("x86_64-unknown-linux-musl") private let extraFlags = BuildFlags( cCompilerFlags: ["-fintegrated-as"], cxxCompilerFlags: ["-fno-exceptions"], @@ -34,7 +36,7 @@ private let destinationV1JSON = "version": 1, "sdk": "\#(bundleRootPath.appending(sdkRootDir))", "toolchain-bin-dir": "\#(bundleRootPath.appending(toolchainBinDir))", - "target": "\#(targetTriple)", + "target": "\#(linuxGNUTargetTriple)", "extra-cc-flags": \#(extraFlags.cCompilerFlags), "extra-swiftc-flags": \#(extraFlags.swiftCompilerFlags), "extra-cpp-flags": \#(extraFlags.cxxCompilerFlags) @@ -48,7 +50,7 @@ private let destinationV2JSON = "sdkRootDir": "\#(sdkRootDir)", "toolchainBinDir": "\#(toolchainBinDir)", "hostTriples": ["\#(hostTriple)"], - "targetTriples": ["\#(targetTriple)"], + "targetTriples": ["\#(linuxGNUTargetTriple)"], "extraCCFlags": \#(extraFlags.cCompilerFlags), "extraSwiftCFlags": \#(extraFlags.swiftCompilerFlags), "extraCXXFlags": \#(extraFlags.cxxCompilerFlags), @@ -56,6 +58,25 @@ private let destinationV2JSON = } """# +private let sdkRootAbsolutePath = bundleRootPath.appending(sdkRootDir) +private let toolchainBinAbsolutePath = bundleRootPath.appending(toolchainBinDir) + +private let parsedDestinationV2GNU = Destination( + hostTriple: hostTriple, + targetTriple: linuxGNUTargetTriple, + sdkRootDir: sdkRootAbsolutePath, + toolchainBinDir: toolchainBinAbsolutePath, + extraFlags: extraFlags +) + +private let parsedDestinationV2Musl = Destination( + hostTriple: hostTriple, + targetTriple: linuxMuslTargetTriple, + sdkRootDir: sdkRootAbsolutePath, + toolchainBinDir: toolchainBinAbsolutePath, + extraFlags: extraFlags +) + final class DestinationTests: XCTestCase { func testDestinationCodable() throws { let fs = InMemoryFileSystem(files: [ @@ -68,13 +89,10 @@ final class DestinationTests: XCTestCase { var flagsWithoutLinkerFlags = extraFlags flagsWithoutLinkerFlags.linkerFlags = [] - let sdkRootAbsolutePath = bundleRootPath.appending(sdkRootDir) - let toolchainBinAbsolutePath = bundleRootPath.appending(toolchainBinDir) - XCTAssertEqual( destinationV1, Destination( - targetTriple: try Triple(targetTriple), + targetTriple: linuxGNUTargetTriple, sdkRootDir: sdkRootAbsolutePath, toolchainBinDir: toolchainBinAbsolutePath, extraFlags: flagsWithoutLinkerFlags @@ -83,15 +101,73 @@ final class DestinationTests: XCTestCase { let destinationV2 = try Destination(fromFile: bundleRootPath.appending(.init("destinationV2.json")), fileSystem: fs) + XCTAssertEqual(destinationV2, parsedDestinationV2GNU) + } + + func testSelectDestination() throws { + let bundles = [ + DestinationsBundle( + path: try AbsolutePath(validating: "/destination.artifactsbundle"), + artifacts: [ + "id1": [ + .init( + metadata: .init( + path: "id1", + supportedTriples: [hostTriple] + ), + destination: parsedDestinationV2GNU + ) + ], + "id2": [ + .init( + metadata: .init( + path: "id2", + supportedTriples: [] + ), + destination: parsedDestinationV2GNU + ) + ], + "id3": [ + .init( + metadata: .init( + path: "id3", + supportedTriples: [hostTriple] + ), + destination: parsedDestinationV2Musl + ) + ] + ] + ) + ] + + let system = ObservabilitySystem.makeForTesting() + XCTAssertEqual( - destinationV2, - Destination( - hostTriple: try Triple(hostTriple), - targetTriple: try Triple(targetTriple), - sdkRootDir: sdkRootAbsolutePath, - toolchainBinDir: toolchainBinAbsolutePath, - extraFlags: extraFlags + bundles.selectDestination( + matching: "id1", + hostTriple: hostTriple, + observabilityScope: system.topScope + ), + parsedDestinationV2GNU + ) + + // Expecting `nil` because no host triple is specified for this destination + // in the fake destination bundle. + XCTAssertNil( + bundles.selectDestination( + matching: "id2", + hostTriple: hostTriple, + observabilityScope: system.topScope ) ) + + XCTAssertEqual( + bundles.selectDestination( + matching: "id3", + hostTriple: hostTriple, + observabilityScope: system.topScope + ), + parsedDestinationV2Musl + ) } } From 2f9cd535eb1ea5d9d5580c0847db8eec66d5ba08 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 22 Nov 2022 18:33:20 +0000 Subject: [PATCH 07/15] SwiftTool: apply destination selectors --- Sources/CoreCommands/Options.swift | 4 + Sources/CoreCommands/SwiftTool.swift | 30 ++++-- .../Workspace/Workspace+Configuration.swift | 13 ++- Sources/Workspace/Workspace.swift | 96 ++++++++++--------- 4 files changed, 86 insertions(+), 57 deletions(-) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index d2350192e80..0cbb7d2bf0a 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -286,6 +286,10 @@ public struct BuildOptions: ParsableArguments { shouldDisplay: false)) public var archs: [String] = [] + /// Path to the compilation destination describing JSON file. + @Option(name: .customLong("experimental-destination-selector"), help: .hidden, completion: .directory) + public var crossCompilationDestinationSelector: String? + /// Which compile-time sanitizers should be enabled. @Option(name: .customLong("sanitize"), help: "Turn on runtime checks for erroneous behavior, possible values: \(Sanitizer.formattedValues)", diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index e8eff8e4627..419111e4717 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -45,6 +45,7 @@ import class TSCUtility.MultiLineNinjaProgressAnimation import class TSCUtility.NinjaProgressAnimation import class TSCUtility.PercentProgressAnimation import protocol TSCUtility.ProgressAnimationProtocol +import struct TSCUtility.Triple import var TSCUtility.verbosity typealias Diagnostic = Basics.Diagnostic @@ -631,13 +632,28 @@ public final class SwiftTool { var destination: Destination let hostDestination: Destination do { - hostDestination = try self._hostToolchain.get().destination + let hostToolchain = try _hostToolchain.get() + hostDestination = hostToolchain.destination + let hostTriple = Triple.getHostTriple(usingSwiftCompiler: hostToolchain.swiftCompilerPath) + // Create custom toolchain if present. - if let customDestination = self.options.locations.customCompileDestination { - destination = try Destination(fromFile: customDestination, fileSystem: self.fileSystem) - } else if let target = self.options.build.customCompileTriple, + if let customDestination = options.locations.customCompileDestination { + destination = try Destination(fromFile: customDestination, fileSystem: fileSystem) + } else if let target = options.build.customCompileTriple, let targetDestination = Destination.defaultDestination(for: target, host: hostDestination) { destination = targetDestination + } else if let destinationSelector = options.build.crossCompilationDestinationSelector, + let destinationsDirectory = sharedCrossCompilationDestinationsDirectory, + let selectedDestination = try DestinationsBundle.getAllValidBundles( + destinationsDirectory: destinationsDirectory, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ).selectDestination( + matching: destinationSelector, + hostTriple: hostTriple, + observabilityScope: observabilityScope + ) { + destination = selectedDestination } else { // Otherwise use the host toolchain. destination = hostDestination @@ -646,13 +662,13 @@ public final class SwiftTool { return .failure(error) } // Apply any manual overrides. - if let triple = self.options.build.customCompileTriple { + if let triple = options.build.customCompileTriple { destination.targetTriple = triple } - if let binDir = self.options.build.customCompileToolchain { + if let binDir = options.build.customCompileToolchain { destination.toolchainBinDir = binDir.appending(components: "usr", "bin") } - if let sdk = self.options.build.customCompileSDK { + if let sdk = options.build.customCompileSDK { destination.sdkRootDir = sdk } destination.archs = options.build.archs diff --git a/Sources/Workspace/Workspace+Configuration.swift b/Sources/Workspace/Workspace+Configuration.swift index 153d2a76fe3..4c2a772041d 100644 --- a/Sources/Workspace/Workspace+Configuration.swift +++ b/Sources/Workspace/Workspace+Configuration.swift @@ -36,19 +36,22 @@ extension Workspace { /// Path to the Package.resolved file. public var resolvedVersionsFile: AbsolutePath - /// Path to the local configuration directory + /// Path to the local configuration directory. public var localConfigurationDirectory: AbsolutePath - /// Path to the shared configuration directory + /// Path to the shared configuration directory. public var sharedConfigurationDirectory: AbsolutePath? - /// Path to the shared security directory + /// Path to the shared security directory. public var sharedSecurityDirectory: AbsolutePath? - /// Path to the shared cache directory + /// Path to the shared cache directory. public var sharedCacheDirectory: AbsolutePath? - /// Whether or not to emit a warning about the existence of deprecated configuration files + /// Path to the shared cross-compilation destinations directory. + public var sharedCrossCompilationDestinationsDirectory: AbsolutePath? + + /// Whether or not to emit a warning about the existence of deprecated configuration files. public var emitDeprecatedConfigurationWarning: Bool // working directories diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index f1b988e6e78..f8103bb2f03 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -3559,62 +3559,68 @@ extension Workspace.Location { ) throws -> Self { var location = self - // check that shared configuration directory is accessible, or warn + reset if not - if let sharedConfigurationDirectory = self.sharedConfigurationDirectory { - // It may not always be possible to create default location (for example de to restricted sandbox), - // in which case defaultDirectory would be nil. - let defaultDirectory = try? fileSystem.getOrCreateSwiftPMConfigurationDirectory(warningHandler: self.emitDeprecatedConfigurationWarning ? warningHandler : { _ in }) - if defaultDirectory != nil, sharedConfigurationDirectory != defaultDirectory { - // custom location _must_ be writable, throw if we cannot access it - guard fileSystem.isWritable(sharedConfigurationDirectory) else { - throw StringError("\(sharedConfigurationDirectory) is not accessible or not writable") - } - } else { - // default location _may_ not be writable, in which case we disable the relevant features that depend on it - if !fileSystem.isWritable(sharedConfigurationDirectory) { - location.sharedConfigurationDirectory = .none - warningHandler("\(sharedConfigurationDirectory) is not accessible or not writable, disabling user-level configuration features.") - } - } - } + try location.validate( + keyPath: \.sharedConfigurationDirectory, + fileSystem: fileSystem, + getOrCreateHandler: { + try $0.getOrCreateSwiftPMConfigurationDirectory(warningHandler: self.emitDeprecatedConfigurationWarning ? warningHandler : { _ in }) + }, + warningHandler: warningHandler + ) - // check that shared configuration directory is accessible, or warn + reset if not - if let sharedSecurityDirectory = self.sharedSecurityDirectory { - // It may not always be possible to create default location (for example de to restricted sandbox), - // in which case defaultDirectory would be nil. - let defaultDirectory = try? fileSystem.getOrCreateSwiftPMSecurityDirectory() - if defaultDirectory != nil, sharedSecurityDirectory != defaultDirectory { - // custom location _must_ be writable, throw if we cannot access it - guard fileSystem.isWritable(sharedSecurityDirectory) else { - throw StringError("\(sharedSecurityDirectory) is not accessible or not writable") - } - } else { - // default location _may_ not be writable, in which case we disable the relevant features that depend on it - if !fileSystem.isWritable(sharedSecurityDirectory) { - location.sharedSecurityDirectory = .none - warningHandler("\(sharedSecurityDirectory) is not accessible or not writable, disabling user-level security features.") - } - } - } + try location.validate( + keyPath: \.sharedSecurityDirectory, + fileSystem: fileSystem, + getOrCreateHandler: { + try $0.getOrCreateSwiftPMSecurityDirectory() + }, + warningHandler: warningHandler + ) + try location.validate( + keyPath: \.sharedCacheDirectory, + fileSystem: fileSystem, + getOrCreateHandler: { + try $0.getOrCreateSwiftPMCacheDirectory() + }, + warningHandler: warningHandler + ) + + try location.validate( + keyPath: \.sharedCrossCompilationDestinationsDirectory, + fileSystem: fileSystem, + getOrCreateHandler: { + try $0.getOrCreateSwiftPMCrossCompilationDestinationsDirectory() + }, + warningHandler: warningHandler + ) + + return location + } + + mutating func validate( + keyPath: WritableKeyPath, + fileSystem: FileSystem, + getOrCreateHandler: (FileSystem) throws -> AbsolutePath, + warningHandler: @escaping (String) -> Void + ) throws { // check that shared configuration directory is accessible, or warn + reset if not - if let sharedCacheDirectory = self.sharedCacheDirectory { + if let sharedDirectory = self[keyPath: keyPath] { // It may not always be possible to create default location (for example de to restricted sandbox), // in which case defaultDirectory would be nil. - let defaultDirectory = try? fileSystem.getOrCreateSwiftPMCacheDirectory() - if defaultDirectory != nil, sharedCacheDirectory != defaultDirectory { + let defaultDirectory = try? getOrCreateHandler(fileSystem) + if defaultDirectory != nil, sharedDirectory != defaultDirectory { // custom location _must_ be writable, throw if we cannot access it - guard fileSystem.isWritable(sharedCacheDirectory) else { - throw StringError("\(sharedCacheDirectory) is not accessible or not writable") + guard fileSystem.isWritable(sharedDirectory) else { + throw StringError("\(sharedDirectory) is not accessible or not writable") } } else { - if !fileSystem.isWritable(sharedCacheDirectory) { - location.sharedCacheDirectory = .none - warningHandler("\(sharedCacheDirectory) is not accessible or not writable, disabling user-level cache features.") + if !fileSystem.isWritable(sharedDirectory) { + self[keyPath: keyPath] = nil + warningHandler("\(sharedDirectory) is not accessible or not writable, disabling user-level cache features.") } } } - return location } } From 96c970d67e60abd5cf9823387f0a213000d3d898 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 28 Nov 2022 14:11:45 +0000 Subject: [PATCH 08/15] Refactor destination selection, `swiftCFlags` use --- Sources/CoreCommands/Options.swift | 2 +- Sources/CoreCommands/SwiftTool.swift | 20 ++--- Sources/PackageModel/Destination.swift | 2 +- Sources/SPMBuildCore/DestinationsBundle.swift | 82 ++++++++++++++++--- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/Sources/CoreCommands/Options.swift b/Sources/CoreCommands/Options.swift index 0cbb7d2bf0a..98df9e36e4d 100644 --- a/Sources/CoreCommands/Options.swift +++ b/Sources/CoreCommands/Options.swift @@ -287,7 +287,7 @@ public struct BuildOptions: ParsableArguments { public var archs: [String] = [] /// Path to the compilation destination describing JSON file. - @Option(name: .customLong("experimental-destination-selector"), help: .hidden, completion: .directory) + @Option(name: .customLong("experimental-destination-selector"), help: .hidden) public var crossCompilationDestinationSelector: String? /// Which compile-time sanitizers should be enabled. diff --git a/Sources/CoreCommands/SwiftTool.swift b/Sources/CoreCommands/SwiftTool.swift index 419111e4717..fca76b07623 100644 --- a/Sources/CoreCommands/SwiftTool.swift +++ b/Sources/CoreCommands/SwiftTool.swift @@ -642,18 +642,14 @@ public final class SwiftTool { } else if let target = options.build.customCompileTriple, let targetDestination = Destination.defaultDestination(for: target, host: hostDestination) { destination = targetDestination - } else if let destinationSelector = options.build.crossCompilationDestinationSelector, - let destinationsDirectory = sharedCrossCompilationDestinationsDirectory, - let selectedDestination = try DestinationsBundle.getAllValidBundles( - destinationsDirectory: destinationsDirectory, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ).selectDestination( - matching: destinationSelector, - hostTriple: hostTriple, - observabilityScope: observabilityScope - ) { - destination = selectedDestination + } else if let destinationSelector = options.build.crossCompilationDestinationSelector { + destination = try DestinationsBundle.selectDestination( + fromBundlesAt: sharedCrossCompilationDestinationsDirectory, + fileSystem: fileSystem, + matching: destinationSelector, + hostTriple: hostTriple, + observabilityScope: observabilityScope + ) } else { // Otherwise use the host toolchain. destination = hostDestination diff --git a/Sources/PackageModel/Destination.swift b/Sources/PackageModel/Destination.swift index 709d3a360e7..ad0e114fb8d 100644 --- a/Sources/PackageModel/Destination.swift +++ b/Sources/PackageModel/Destination.swift @@ -103,7 +103,7 @@ public struct Destination: Encodable, Equatable { } /// Additional flags to be passed to the build tools. - public let extraFlags: BuildFlags + public var extraFlags: BuildFlags /// Creates a compilation destination with the specified properties. @available(*, deprecated, message: "use `init(targetTriple:sdkRootDir:toolchainBinDir:extraFlags)` instead") diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift index 3d92dcefdb0..38b154c6fde 100644 --- a/Sources/SPMBuildCore/DestinationsBundle.swift +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -62,6 +62,60 @@ public struct DestinationsBundle { } } + public static func selectDestination( + fromBundlesAt destinationsDirectory: AbsolutePath?, + fileSystem: FileSystem, + matching selector: String, + hostTriple: Triple, + observabilityScope: ObservabilityScope + ) throws -> Destination { + guard let destinationsDirectory else { + throw StringError( + """ + No cross-compilation destinations directory found, specify one + with `experimental-destinations-path` option. + """) + } + + let validBundles = try DestinationsBundle.getAllValidBundles( + destinationsDirectory: destinationsDirectory, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) + + guard !validBundles.isEmpty else { + throw StringError( + "No valid cross-compilation destination bundles found at \(destinationsDirectory)." + ) + } + + guard var selectedDestination = validBundles.selectDestination( + matching: selector, + hostTriple: hostTriple, + observabilityScope: observabilityScope + ) else { + throw StringError( + """ + No cross-compilation destination found matching query `\(selector)` and host triple + `\(hostTriple.tripleString)`. Use `swift package experimental-destination list` command to see + available destinations. + """ + ) + } + + selectedDestination.extraFlags.swiftCompilerFlags += [ + "-tools-directory", selectedDestination.toolchainBinDir.pathString, + ] + + if let sdkDirPath = selectedDestination.sdkRootDir?.pathString { + selectedDestination.extraFlags.swiftCompilerFlags += [ + "-sdk", sdkDirPath, + ] + } + + return selectedDestination + } + /// Parses metadata of an `.artifactbundle` and validates it as a bundle containing /// cross-compilation destinations. /// - Parameters: @@ -70,7 +124,7 @@ public struct DestinationsBundle { /// - observabilityScope: observability scope to log validation warnings. /// - Returns: Validated `DestinationsBundle` containing validated `Destination` values for /// each artifact and its variants. - public static func parseAndValidate( + private static func parseAndValidate( bundlePath: AbsolutePath, fileSystem: FileSystem, observabilityScope: ObservabilityScope @@ -147,9 +201,9 @@ extension Array where Element == DestinationsBundle { /// - query: either an artifact ID or target triple to filter with. /// - hostTriple: triple of the host building with these destinations. /// - observabilityScope: observability scope to log warnings about multiple matches. - /// - Returns: `Destination` value matching `query` either by artifact ID or target triple. + /// - Returns: `Destination` value matching `query` either by artifact ID or target triple, `nil` if none found. public func selectDestination( - matching query: String, + matching selector: String, hostTriple: Triple, observabilityScope: ObservabilityScope ) -> Destination? { @@ -163,13 +217,15 @@ extension Array where Element == DestinationsBundle { continue } - if artifactID == query { + if artifactID == selector { if let matchedByID = matchedByID { observabilityScope.emit(warning: """ multiple destinations match ID `\(artifactID)` and host triple \( - hostTriple - ), selected one at \(matchedByID.path.appending(component: matchedByID.variant.metadata.path)) + hostTriple.tripleString + ), selected one at \( + matchedByID.path.appending(component: matchedByID.variant.metadata.path) + ) """ ) } else { @@ -177,13 +233,15 @@ extension Array where Element == DestinationsBundle { } } - if variant.destination.targetTriple?.tripleString == query { + if variant.destination.targetTriple?.tripleString == selector { if let matchedByTriple = matchedByTriple { observabilityScope.emit(warning: """ - multiple destinations match target triple `\(query)` and host triple \( - hostTriple - ), selected one at \(matchedByTriple.path.appending(component: matchedByTriple.variant.metadata.path)) + multiple destinations match target triple `\(selector)` and host triple \( + hostTriple.tripleString + ), selected one at \( + matchedByTriple.path.appending(component: matchedByTriple.variant.metadata.path) + ) """ ) } else { @@ -197,8 +255,8 @@ extension Array where Element == DestinationsBundle { if let matchedByID = matchedByID, let matchedByTriple = matchedByTriple, matchedByID != matchedByTriple { observabilityScope.emit(warning: """ - multiple destinations match the query `\(query)` and host triple \( - hostTriple + multiple destinations match the query `\(selector)` and host triple \( + hostTriple.tripleString ), selected one at \(matchedByID.path.appending(component: matchedByID.variant.metadata.path)) """ ) From ead79beb377b5697abf9ef4b6dd5d4878ef168bb Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 28 Nov 2022 14:15:54 +0000 Subject: [PATCH 09/15] Add missing doc comment --- Sources/SPMBuildCore/DestinationsBundle.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift index 38b154c6fde..5b88a1ac30f 100644 --- a/Sources/SPMBuildCore/DestinationsBundle.swift +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -62,6 +62,14 @@ public struct DestinationsBundle { } } + /// Select destinations matching a given query and host triple from all destinations available in a directory. + /// - Parameters: + /// - destinationsDirectory: the directory to scan for destination bundles. + /// - fileSystem: the filesystem the directory is located on. + /// - query: either an artifact ID or target triple to filter with. + /// - hostTriple: triple of the host building with these destinations. + /// - observabilityScope: observability scope to log warnings about multiple matches. + /// - Returns: `Destination` value matching `query` either by artifact ID or target triple, `nil` if none found. public static func selectDestination( fromBundlesAt destinationsDirectory: AbsolutePath?, fileSystem: FileSystem, @@ -196,7 +204,7 @@ private extension ArtifactsArchiveMetadata { } extension Array where Element == DestinationsBundle { - /// Select destinations in an array matching a given query and host triple. + /// Select destinations matching a given query and host triple from a `self` array of available destinations. /// - Parameters: /// - query: either an artifact ID or target triple to filter with. /// - hostTriple: triple of the host building with these destinations. From 90be750671a295e14d3d1cfc2365b1485cd2b52d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 28 Nov 2022 20:29:05 +0000 Subject: [PATCH 10/15] Fix renaming refactoring leaking outside --- Sources/PackageModel/Manifest.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/PackageModel/Manifest.swift b/Sources/PackageModel/Manifest.swift index 06bf1c21bf7..1b28e3c4db9 100644 --- a/Sources/PackageModel/Manifest.swift +++ b/Sources/PackageModel/Manifest.swift @@ -180,14 +180,14 @@ public final class Manifest { } var requiredDependencies: Set = [] - for linuxGNUTargetTriple in self.targetsRequired(for: products) { - for targetDependency in linuxGNUTargetTriple.dependencies { + for targetTriple in self.targetsRequired(for: products) { + for targetDependency in targetTriple.dependencies { if let dependency = self.packageDependency(referencedBy: targetDependency) { requiredDependencies.insert(dependency.identity) } } - linuxGNUTargetTriple.pluginUsages?.forEach { + targetTriple.pluginUsages?.forEach { if let dependency = self.packageDependency(referencedBy: $0) { requiredDependencies.insert(dependency.identity) } From d22a62e1b3fcda9f37106721151ffa5bc9aa5209 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 28 Nov 2022 22:09:19 +0000 Subject: [PATCH 11/15] Address PR feedback --- Sources/Workspace/Workspace.swift | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index f8103bb2f03..7a4cb16f9f8 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -3563,7 +3563,7 @@ extension Workspace.Location { keyPath: \.sharedConfigurationDirectory, fileSystem: fileSystem, getOrCreateHandler: { - try $0.getOrCreateSwiftPMConfigurationDirectory(warningHandler: self.emitDeprecatedConfigurationWarning ? warningHandler : { _ in }) + try fileSystem.getOrCreateSwiftPMConfigurationDirectory(warningHandler: self.emitDeprecatedConfigurationWarning ? warningHandler : { _ in }) }, warningHandler: warningHandler ) @@ -3571,44 +3571,38 @@ extension Workspace.Location { try location.validate( keyPath: \.sharedSecurityDirectory, fileSystem: fileSystem, - getOrCreateHandler: { - try $0.getOrCreateSwiftPMSecurityDirectory() - }, + getOrCreateHandler: fileSystem.getOrCreateSwiftPMSecurityDirectory, warningHandler: warningHandler ) try location.validate( keyPath: \.sharedCacheDirectory, fileSystem: fileSystem, - getOrCreateHandler: { - try $0.getOrCreateSwiftPMCacheDirectory() - }, + getOrCreateHandler: fileSystem.getOrCreateSwiftPMCacheDirectory, warningHandler: warningHandler ) try location.validate( keyPath: \.sharedCrossCompilationDestinationsDirectory, fileSystem: fileSystem, - getOrCreateHandler: { - try $0.getOrCreateSwiftPMCrossCompilationDestinationsDirectory() - }, + getOrCreateHandler: fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory, warningHandler: warningHandler ) return location } - mutating func validate( + mutating func validate( keyPath: WritableKeyPath, - fileSystem: FileSystem, - getOrCreateHandler: (FileSystem) throws -> AbsolutePath, + fileSystem: FS, + getOrCreateHandler: () throws -> AbsolutePath, warningHandler: @escaping (String) -> Void ) throws { // check that shared configuration directory is accessible, or warn + reset if not if let sharedDirectory = self[keyPath: keyPath] { // It may not always be possible to create default location (for example de to restricted sandbox), // in which case defaultDirectory would be nil. - let defaultDirectory = try? getOrCreateHandler(fileSystem) + let defaultDirectory = try? getOrCreateHandler() if defaultDirectory != nil, sharedDirectory != defaultDirectory { // custom location _must_ be writable, throw if we cannot access it guard fileSystem.isWritable(sharedDirectory) else { From 22c54a94ef8295622f83e960bb63825e95ea63a7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 28 Nov 2022 22:11:09 +0000 Subject: [PATCH 12/15] Remove redundant generic constraint --- Sources/Workspace/Workspace.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Workspace/Workspace.swift b/Sources/Workspace/Workspace.swift index 7a4cb16f9f8..b247884387c 100644 --- a/Sources/Workspace/Workspace.swift +++ b/Sources/Workspace/Workspace.swift @@ -3592,9 +3592,9 @@ extension Workspace.Location { return location } - mutating func validate( + mutating func validate( keyPath: WritableKeyPath, - fileSystem: FS, + fileSystem: FileSystem, getOrCreateHandler: () throws -> AbsolutePath, warningHandler: @escaping (String) -> Void ) throws { From 55ffcbf0a2c1ff893bec41fe11b68b7cf9d90ee3 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 15 Dec 2022 22:42:32 +0000 Subject: [PATCH 13/15] Fix build issues after merge conflict --- Sources/Commands/CMakeLists.txt | 2 - .../DestinationTools/DestinationCommand.swift | 26 -------- .../DestinationTools/ListDestinations.swift | 64 ------------------- .../PackageTools/SwiftPackageTool.swift | 2 - .../ListDestinations.swift | 30 ++------- 5 files changed, 7 insertions(+), 117 deletions(-) delete mode 100644 Sources/Commands/DestinationTools/DestinationCommand.swift delete mode 100644 Sources/Commands/DestinationTools/ListDestinations.swift diff --git a/Sources/Commands/CMakeLists.txt b/Sources/Commands/CMakeLists.txt index 8825d46a87c..06453788734 100644 --- a/Sources/Commands/CMakeLists.txt +++ b/Sources/Commands/CMakeLists.txt @@ -7,8 +7,6 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_library(Commands - DestinationTools/DestinationCommand.swift - DestinationTools/ListDestinations.swift PackageTools/APIDiff.swift PackageTools/ArchiveSource.swift PackageTools/CompletionTool.swift diff --git a/Sources/Commands/DestinationTools/DestinationCommand.swift b/Sources/Commands/DestinationTools/DestinationCommand.swift deleted file mode 100644 index de353232b70..00000000000 --- a/Sources/Commands/DestinationTools/DestinationCommand.swift +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import ArgumentParser - -public struct DestinationCommand: ParsableCommand { - public static var configuration = CommandConfiguration( - commandName: "experimental-destination", - _superCommandName: "package", - abstract: "Perform operations on Swift cross-compilation destinations.", - subcommands: [ - ListDestinations.self, - ], - helpNames: [.short, .long, .customLong("help", withSingleDash: true)]) - - public init() {} -} diff --git a/Sources/Commands/DestinationTools/ListDestinations.swift b/Sources/Commands/DestinationTools/ListDestinations.swift deleted file mode 100644 index 307152839d8..00000000000 --- a/Sources/Commands/DestinationTools/ListDestinations.swift +++ /dev/null @@ -1,64 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2014-2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import ArgumentParser -import Basics -import CoreCommands -import SPMBuildCore -import PackageModel -import TSCBasic - -extension DestinationCommand { - struct ListDestinations: ParsableCommand { - static let configuration = CommandConfiguration( - commandName: "list", - abstract: - """ - Print a list of IDs of available cross-compilation destinations available on the filesystem. - """ - ) - - @OptionGroup() - public var locations: LocationOptions - - func run() throws { - let fileSystem = localFileSystem - let observabilitySystem = ObservabilitySystem( - SwiftToolObservabilityHandler(outputStream: stdoutStream, logLevel: .warning) - ) - let observabilityScope = observabilitySystem.topScope - - guard var destinationsDirectory = try fileSystem.getSharedCrossCompilationDestinationsDirectory( - explicitDirectory: locations.crossCompilationDestinationsDirectory - ) else { - let expectedPath = try fileSystem.swiftPMCrossCompilationDestinationsDirectory - throw StringError( - "Couldn't find or create a directory where cross-compilation destinations are stored: \(expectedPath)" - ) - } - - if !fileSystem.exists(destinationsDirectory) { - destinationsDirectory = try fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory() - } - - let validBundles = try DestinationsBundle.getAllValidBundles( - destinationsDirectory: destinationsDirectory, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) - - for bundle in validBundles { - bundle.artifacts.keys.forEach { print($0) } - } - } - } -} diff --git a/Sources/Commands/PackageTools/SwiftPackageTool.swift b/Sources/Commands/PackageTools/SwiftPackageTool.swift index 61b294965ea..6ea1c236afd 100644 --- a/Sources/Commands/PackageTools/SwiftPackageTool.swift +++ b/Sources/Commands/PackageTools/SwiftPackageTool.swift @@ -56,8 +56,6 @@ public struct SwiftPackageTool: ParsableCommand { Resolve.self, Fetch.self, - DestinationCommand.self, - ShowDependencies.self, ToolsVersionCommand.self, ComputeChecksum.self, diff --git a/Sources/CrossCompilationDestinationsTool/ListDestinations.swift b/Sources/CrossCompilationDestinationsTool/ListDestinations.swift index 85f59022b77..55abf32f38b 100644 --- a/Sources/CrossCompilationDestinationsTool/ListDestinations.swift +++ b/Sources/CrossCompilationDestinationsTool/ListDestinations.swift @@ -49,30 +49,14 @@ struct ListDestinations: ParsableCommand { destinationsDirectory = try fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory() } - // Get absolute paths to available destination bundles. - let destinationBundles = try fileSystem.getDirectoryContents(destinationsDirectory).filter { - $0.hasSuffix(BinaryTarget.Kind.artifactsArchive.fileExtension) - }.map { - destinationsDirectory.appending(components: [$0]) - } - - // Enumerate available bundles and parse manifests for each of them, then validate supplied destinations. - for bundlePath in destinationBundles { - do { - let destinationsBundle = try DestinationsBundle.parseAndValidate( - bundlePath: bundlePath, - fileSystem: fileSystem, - observabilityScope: observabilityScope - ) + let validBundles = try DestinationsBundle.getAllValidBundles( + destinationsDirectory: destinationsDirectory, + fileSystem: fileSystem, + observabilityScope: observabilityScope + ) - destinationsBundle.artifacts.keys.forEach { print($0) } - } catch { - observabilityScope.emit( - .warning( - "Couldn't parse `info.json` manifest of a destination bundle at \(bundlePath): \(error)" - ) - ) - } + for bundle in validBundles { + bundle.artifacts.keys.forEach { print($0) } } } } From b0f1dfffbe577fd13a2fa17529588cc8440d9a1a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 15 Dec 2022 23:03:54 +0000 Subject: [PATCH 14/15] Fix triple JSON serialization in a test fixture --- Tests/PackageModelTests/DestinationTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/PackageModelTests/DestinationTests.swift b/Tests/PackageModelTests/DestinationTests.swift index 8d6e2ff3e69..6797d972ab6 100644 --- a/Tests/PackageModelTests/DestinationTests.swift +++ b/Tests/PackageModelTests/DestinationTests.swift @@ -36,7 +36,7 @@ private let destinationV1JSON = "version": 1, "sdk": "\#(bundleRootPath.appending(sdkRootDir))", "toolchain-bin-dir": "\#(bundleRootPath.appending(toolchainBinDir))", - "target": "\#(linuxGNUTargetTriple)", + "target": "\#(linuxGNUTargetTriple.tripleString)", "extra-cc-flags": \#(extraFlags.cCompilerFlags), "extra-swiftc-flags": \#(extraFlags.swiftCompilerFlags), "extra-cpp-flags": \#(extraFlags.cxxCompilerFlags) @@ -49,8 +49,8 @@ private let destinationV2JSON = "version": 2, "sdkRootDir": "\#(sdkRootDir)", "toolchainBinDir": "\#(toolchainBinDir)", - "hostTriples": ["\#(hostTriple)"], - "targetTriples": ["\#(linuxGNUTargetTriple)"], + "hostTriples": ["\#(hostTriple.tripleString)"], + "targetTriples": ["\#(linuxGNUTargetTriple.tripleString)"], "extraCCFlags": \#(extraFlags.cCompilerFlags), "extraSwiftCFlags": \#(extraFlags.swiftCompilerFlags), "extraCXXFlags": \#(extraFlags.cxxCompilerFlags), From d84b18105af68eaa238c788d667db2a77673d3ac Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 16 Dec 2022 10:25:10 +0000 Subject: [PATCH 15/15] Restore compatibility with older Swift versions --- Sources/SPMBuildCore/DestinationsBundle.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SPMBuildCore/DestinationsBundle.swift b/Sources/SPMBuildCore/DestinationsBundle.swift index 5b88a1ac30f..b885649c245 100644 --- a/Sources/SPMBuildCore/DestinationsBundle.swift +++ b/Sources/SPMBuildCore/DestinationsBundle.swift @@ -77,7 +77,7 @@ public struct DestinationsBundle { hostTriple: Triple, observabilityScope: ObservabilityScope ) throws -> Destination { - guard let destinationsDirectory else { + guard let destinationsDirectory = destinationsDirectory else { throw StringError( """ No cross-compilation destinations directory found, specify one