Skip to content

Commit 11b93c3

Browse files
authored
CrossCompilationDestinationsTool: add remove subcommand (#6210)
Adds new `swift experimental-destination remove` subcommand per [the cross-compilation destinations proposal](https://github.com/apple/swift-evolution/blob/139e51f120b20e2d6aa9a61d44474ec698d9ec8c/proposals/0387-cross-compilation-destinations.md#destination-bundle-installation-and-configuration).
1 parent 6939221 commit 11b93c3

File tree

6 files changed

+115
-43
lines changed

6 files changed

+115
-43
lines changed

Sources/CrossCompilationDestinationsTool/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_library(CrossCompilationDestinationsTool
10+
DestinationCommand.swift
1011
InstallDestination.swift
1112
ListDestinations.swift
13+
RemoveDestination.swift
1214
SwiftDestinationTool.swift)
1315
target_link_libraries(CrossCompilationDestinationsTool PUBLIC
1416
ArgumentParser
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
import CoreCommands
15+
import TSCBasic
16+
17+
/// A protocol for functions and properties common to all destination subcommands.
18+
protocol DestinationCommand: ParsableCommand {
19+
/// Common locations options provided by ArgumentParser.
20+
var locations: LocationOptions { get }
21+
}
22+
23+
extension DestinationCommand {
24+
/// The file system used by default by this command.
25+
var fileSystem: FileSystem { localFileSystem }
26+
27+
/// Parses destinations directory option if provided or uses the default path for cross-compilation destinations
28+
/// on the file system. A new directory at this path is created if one doesn't exist already.
29+
/// - Returns: existing or a newly created directory at the computed location.
30+
func getOrCreateDestinationsDirectory() throws -> AbsolutePath {
31+
guard var destinationsDirectory = try fileSystem.getSharedCrossCompilationDestinationsDirectory(
32+
explicitDirectory: locations.crossCompilationDestinationsDirectory
33+
) else {
34+
let expectedPath = try fileSystem.swiftPMCrossCompilationDestinationsDirectory
35+
throw StringError(
36+
"Couldn't find or create a directory where cross-compilation destinations are stored: `\(expectedPath)`"
37+
)
38+
}
39+
40+
if !self.fileSystem.exists(destinationsDirectory) {
41+
destinationsDirectory = try self.fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory()
42+
}
43+
44+
return destinationsDirectory
45+
}
46+
}

Sources/CrossCompilationDestinationsTool/InstallDestination.swift

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
1413
import ArgumentParser
1514
import Basics
1615
import CoreCommands
1716
import Foundation
1817

19-
import func TSCBasic.tsc_await
2018
import struct TSCBasic.AbsolutePath
2119
import var TSCBasic.localFileSystem
2220
import var TSCBasic.stdoutStream
21+
import func TSCBasic.tsc_await
2322

24-
public struct InstallDestination: ParsableCommand {
25-
public static let configuration = CommandConfiguration(
23+
struct InstallDestination: DestinationCommand {
24+
static let configuration = CommandConfiguration(
2625
commandName: "install",
2726
abstract: """
2827
Installs a given destination artifact bundle to a location discoverable by SwiftPM. If the artifact bundle \
@@ -36,32 +35,15 @@ public struct InstallDestination: ParsableCommand {
3635
@Argument(help: "A local filesystem path or a URL of an artifact bundle to install.")
3736
var bundlePathOrURL: String
3837

39-
public init() {}
40-
41-
public func run() throws {
42-
let fileSystem = localFileSystem
43-
44-
guard var destinationsDirectory = try fileSystem.getSharedCrossCompilationDestinationsDirectory(
45-
explicitDirectory: locations.crossCompilationDestinationsDirectory
46-
) else {
47-
let expectedPath = try fileSystem.swiftPMCrossCompilationDestinationsDirectory
48-
throw StringError(
49-
"Couldn't find or create a directory where cross-compilation destinations are stored: `\(expectedPath)`"
50-
)
51-
}
52-
53-
// FIXME: generalize path calculation and creation with `ListDestinations` subcommand
54-
if !fileSystem.exists(destinationsDirectory) {
55-
destinationsDirectory = try fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory()
56-
}
57-
38+
func run() throws {
5839
let observabilitySystem = ObservabilitySystem.swiftTool()
5940
let observabilityScope = observabilitySystem.topScope
41+
let destinationsDirectory = try self.getOrCreateDestinationsDirectory()
6042

6143
if
6244
let bundleURL = URL(string: bundlePathOrURL),
6345
let scheme = bundleURL.scheme,
64-
scheme == "http" || scheme == "https"
46+
scheme == "http" || scheme == "https"
6547
{
6648
let response = try tsc_await { (completion: @escaping (Result<HTTPClientResponse, Error>) -> Void) in
6749
let client = LegacyHTTPClient()
@@ -79,12 +61,12 @@ public struct InstallDestination: ParsableCommand {
7961

8062
let fileName = bundleURL.lastPathComponent
8163

82-
try fileSystem.writeFileContents(destinationsDirectory.appending(component: fileName), data: body)
64+
try self.fileSystem.writeFileContents(destinationsDirectory.appending(component: fileName), data: body)
8365
} else if
84-
let cwd = fileSystem.currentWorkingDirectory,
66+
let cwd = self.fileSystem.currentWorkingDirectory,
8567
let bundlePath = try? AbsolutePath(validating: bundlePathOrURL, relativeTo: cwd),
8668
let bundleName = bundlePath.components.last,
87-
fileSystem.exists(bundlePath)
69+
self.fileSystem.exists(bundlePath)
8870
{
8971
let destinationPath = destinationsDirectory.appending(component: bundleName)
9072
if fileSystem.exists(destinationPath) {

Sources/CrossCompilationDestinationsTool/ListDestinations.swift

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import PackageModel
1717
import SPMBuildCore
1818
import TSCBasic
1919

20-
public struct ListDestinations: ParsableCommand {
20+
public struct ListDestinations: DestinationCommand {
2121
public static let configuration = CommandConfiguration(
2222
commandName: "list",
2323
abstract:
@@ -32,22 +32,9 @@ public struct ListDestinations: ParsableCommand {
3232
public init() {}
3333

3434
public func run() throws {
35-
let fileSystem = localFileSystem
3635
let observabilitySystem = ObservabilitySystem.swiftTool()
3736
let observabilityScope = observabilitySystem.topScope
38-
39-
guard var destinationsDirectory = try fileSystem.getSharedCrossCompilationDestinationsDirectory(
40-
explicitDirectory: locations.crossCompilationDestinationsDirectory
41-
) else {
42-
let expectedPath = try fileSystem.swiftPMCrossCompilationDestinationsDirectory
43-
throw StringError(
44-
"Couldn't find or create a directory where cross-compilation destinations are stored: \(expectedPath)"
45-
)
46-
}
47-
48-
if !fileSystem.exists(destinationsDirectory) {
49-
destinationsDirectory = try fileSystem.getOrCreateSwiftPMCrossCompilationDestinationsDirectory()
50-
}
37+
let destinationsDirectory = try self.getOrCreateDestinationsDirectory()
5138

5239
let validBundles = try DestinationsBundle.getAllValidBundles(
5340
destinationsDirectory: destinationsDirectory,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
import Basics
15+
import CoreCommands
16+
17+
struct RemoveDestination: DestinationCommand {
18+
static let configuration = CommandConfiguration(
19+
commandName: "remove",
20+
abstract: """
21+
Removes a previously installed destination artifact bundle from the filesystem.
22+
"""
23+
)
24+
25+
@OptionGroup(visibility: .hidden)
26+
var locations: LocationOptions
27+
28+
@Argument(help: "Name of the destination artifact bundle to remove from the filesystem.")
29+
var bundleName: String
30+
31+
func run() throws {
32+
let destinationsDirectory = try self.getOrCreateDestinationsDirectory()
33+
let artifactBundleDirectory = destinationsDirectory.appending(component: self.bundleName)
34+
35+
guard fileSystem.exists(artifactBundleDirectory) else {
36+
throw StringError(
37+
"""
38+
Destination artifact bundle with name `\(self.bundleName)` is not currently installed, so \
39+
it can't be removed.
40+
"""
41+
)
42+
}
43+
44+
try fileSystem.removeFileTree(artifactBundleDirectory)
45+
46+
print(
47+
"""
48+
Destination artifact bundle at path `\(artifactBundleDirectory)` was successfully removed from the file \
49+
system.
50+
"""
51+
)
52+
}
53+
}

Sources/CrossCompilationDestinationsTool/SwiftDestinationTool.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ public struct SwiftDestinationTool: ParsableCommand {
2222
subcommands: [
2323
InstallDestination.self,
2424
ListDestinations.self,
25+
RemoveDestination.self,
2526
],
26-
helpNames: [.short, .long, .customLong("help", withSingleDash: true)])
27+
helpNames: [.short, .long, .customLong("help", withSingleDash: true)]
28+
)
2729

2830
public init() {}
2931
}

0 commit comments

Comments
 (0)