Skip to content

Commit 47573ce

Browse files
authored
[SE-0301] Updating a project and its manifest programmatically and from the command line (#7467)
1 parent b556563 commit 47573ce

25 files changed

+2269
-15
lines changed
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
SET(SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE ${CMAKE_SOURCE_DIR}/../swift-syntax)
2+
message(STATUS "swift-syntax path: ${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}")
3+
4+
include(FetchContent)
5+
6+
if(NOT EXISTS "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}")
7+
message(SEND_ERROR "swift-syntax is required to build SwiftPM. Please run update-checkout or specify SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE")
8+
return()
9+
endif()
10+
11+
# Build swift-syntax libraries with FetchContent.
12+
# set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE})
13+
set(BUILD_SHARED_LIBS OFF)
14+
15+
file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path)
16+
FetchContent_Declare(SwiftSyntax SOURCE_DIR "${swift_syntax_path}")
17+
FetchContent_MakeAvailable(SwiftSyntax)

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ find_package(SQLite3 REQUIRED)
5454
# Enable `package` modifier for the whole package.
5555
add_compile_options("$<$<COMPILE_LANGUAGE:Swift>:-package-name;SwiftPM>")
5656

57+
add_subdirectory(BuildSupport/SwiftSyntax)
5758
add_subdirectory(Sources)
5859
add_subdirectory(cmake/modules)

Package.swift

+30
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ let swiftPMDataModelProduct = (
4949
"PackageLoading",
5050
"PackageMetadata",
5151
"PackageModel",
52+
"PackageModelSyntax",
5253
"SourceControl",
5354
"Workspace",
5455
]
@@ -246,6 +247,23 @@ let package = Package(
246247
swiftSettings: packageModelResourcesSettings
247248
),
248249

250+
.target(
251+
/** Primary Package model objects relationship to SwiftSyntax */
252+
name: "PackageModelSyntax",
253+
dependencies: [
254+
"Basics",
255+
"PackageLoading",
256+
"PackageModel",
257+
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
258+
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
259+
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
260+
.product(name: "SwiftParser", package: "swift-syntax"),
261+
.product(name: "SwiftSyntax", package: "swift-syntax"),
262+
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
263+
],
264+
exclude: ["CMakeLists.txt"]
265+
),
266+
249267
.target(
250268
/** Package model conventions and loading support */
251269
name: "PackageLoading",
@@ -414,10 +432,12 @@ let package = Package(
414432
dependencies: [
415433
.product(name: "ArgumentParser", package: "swift-argument-parser"),
416434
.product(name: "OrderedCollections", package: "swift-collections"),
435+
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
417436
"Basics",
418437
"Build",
419438
"CoreCommands",
420439
"PackageGraph",
440+
"PackageModelSyntax",
421441
"SourceControl",
422442
"Workspace",
423443
"XCBuildSupport",
@@ -635,6 +655,14 @@ let package = Package(
635655
name: "PackageModelTests",
636656
dependencies: ["PackageModel", "SPMTestSupport"]
637657
),
658+
.testTarget(
659+
name: "PackageModelSyntaxTests",
660+
dependencies: [
661+
"PackageModelSyntax",
662+
"SPMTestSupport",
663+
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
664+
]
665+
),
638666
.testTarget(
639667
name: "PackageGraphTests",
640668
dependencies: ["PackageGraph", "SPMTestSupport"]
@@ -785,6 +813,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
785813
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.2")),
786814
.package(url: "https://github.com/apple/swift-driver.git", branch: relatedDependenciesBranch),
787815
.package(url: "https://github.com/apple/swift-crypto.git", .upToNextMinor(from: "3.0.0")),
816+
.package(url: "https://github.com/apple/swift-syntax.git", branch: relatedDependenciesBranch),
788817
.package(url: "https://github.com/apple/swift-system.git", .upToNextMinor(from: "1.1.1")),
789818
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.0.1")),
790819
.package(url: "https://github.com/apple/swift-certificates.git", .upToNextMinor(from: "1.0.1")),
@@ -795,6 +824,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
795824
.package(path: "../swift-argument-parser"),
796825
.package(path: "../swift-driver"),
797826
.package(path: "../swift-crypto"),
827+
.package(path: "../swift-syntax"),
798828
.package(path: "../swift-system"),
799829
.package(path: "../swift-collections"),
800830
.package(path: "../swift-certificates"),

Sources/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_subdirectory(PackageFingerprint)
2121
add_subdirectory(PackageGraph)
2222
add_subdirectory(PackageLoading)
2323
add_subdirectory(PackageModel)
24+
add_subdirectory(PackageModelSyntax)
2425
add_subdirectory(PackagePlugin)
2526
add_subdirectory(PackageRegistry)
2627
add_subdirectory(PackageSigning)

Sources/Commands/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_library(Commands
10+
PackageCommands/AddDependency.swift
11+
PackageCommands/AddTarget.swift
1012
PackageCommands/APIDiff.swift
1113
PackageCommands/ArchiveSource.swift
1214
PackageCommands/CompletionCommand.swift
@@ -56,6 +58,7 @@ target_link_libraries(Commands PUBLIC
5658
CoreCommands
5759
LLBuildManifest
5860
PackageGraph
61+
PackageModelSyntax
5962
SourceControl
6063
TSCBasic
6164
TSCUtility
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2014-2024 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+
import PackageModel
17+
import PackageModelSyntax
18+
import SwiftParser
19+
import SwiftSyntax
20+
import TSCBasic
21+
import TSCUtility
22+
import Workspace
23+
24+
extension SwiftPackageCommand {
25+
struct AddDependency: SwiftCommand {
26+
package static let configuration = CommandConfiguration(
27+
abstract: "Add a package dependency to the manifest")
28+
29+
@Argument(help: "The URL or directory of the package to add")
30+
var dependency: String
31+
32+
@OptionGroup(visibility: .hidden)
33+
var globalOptions: GlobalOptions
34+
35+
@Option(help: "The exact package version to depend on")
36+
var exact: Version?
37+
38+
@Option(help: "The specific package revision to depend on")
39+
var revision: String?
40+
41+
@Option(help: "The branch of the package to depend on")
42+
var branch: String?
43+
44+
@Option(help: "The package version to depend on (up to the next major version)")
45+
var from: Version?
46+
47+
@Option(help: "The package version to depend on (up to the next minor version)")
48+
var upToNextMinorFrom: Version?
49+
50+
@Option(help: "Specify upper bound on the package version range (exclusive)")
51+
var to: Version?
52+
53+
func run(_ swiftCommandState: SwiftCommandState) throws {
54+
let workspace = try swiftCommandState.getActiveWorkspace()
55+
56+
guard let packagePath = try swiftCommandState.getWorkspaceRoot().packages.first else {
57+
throw StringError("unknown package")
58+
}
59+
60+
// Load the manifest file
61+
let fileSystem = workspace.fileSystem
62+
let manifestPath = packagePath.appending("Package.swift")
63+
let manifestContents: ByteString
64+
do {
65+
manifestContents = try fileSystem.readFileContents(manifestPath)
66+
} catch {
67+
throw StringError("cannot find package manifest in \(manifestPath)")
68+
}
69+
70+
// Parse the manifest.
71+
let manifestSyntax = manifestContents.withData { data in
72+
data.withUnsafeBytes { buffer in
73+
buffer.withMemoryRebound(to: UInt8.self) { buffer in
74+
Parser.parse(source: buffer)
75+
}
76+
}
77+
}
78+
79+
let identity = PackageIdentity(url: .init(dependency))
80+
81+
// Collect all of the possible version requirements.
82+
var requirements: [PackageDependency.SourceControl.Requirement] = []
83+
if let exact {
84+
requirements.append(.exact(exact))
85+
}
86+
87+
if let branch {
88+
requirements.append(.branch(branch))
89+
}
90+
91+
if let revision {
92+
requirements.append(.revision(revision))
93+
}
94+
95+
if let from {
96+
requirements.append(.range(.upToNextMajor(from: from)))
97+
}
98+
99+
if let upToNextMinorFrom {
100+
requirements.append(.range(.upToNextMinor(from: upToNextMinorFrom)))
101+
}
102+
103+
if requirements.count > 1 {
104+
throw StringError("must specify at most one of --exact, --branch, --revision, --from, or --up-to-next-minor-from")
105+
}
106+
107+
guard let firstRequirement = requirements.first else {
108+
throw StringError("must specify one of --exact, --branch, --revision, --from, or --up-to-next-minor-from")
109+
}
110+
111+
let requirement: PackageDependency.SourceControl.Requirement
112+
if case .range(let range) = firstRequirement {
113+
if let to {
114+
requirement = .range(range.lowerBound..<to)
115+
} else {
116+
requirement = .range(range)
117+
}
118+
} else {
119+
requirement = firstRequirement
120+
121+
if to != nil {
122+
throw StringError("--to can only be specified with --from or --up-to-next-minor-from")
123+
}
124+
}
125+
126+
// Figure out the location of the package.
127+
let location: PackageDependency.SourceControl.Location
128+
if let path = try? Basics.AbsolutePath(validating: dependency) {
129+
location = .local(path)
130+
} else {
131+
location = .remote(.init(dependency))
132+
}
133+
134+
let packageDependency: PackageDependency = .sourceControl(
135+
identity: identity,
136+
nameForTargetDependencyResolutionOnly: nil,
137+
location: location,
138+
requirement: requirement,
139+
productFilter: .everything
140+
)
141+
142+
let editResult = try AddPackageDependency.addPackageDependency(
143+
packageDependency,
144+
to: manifestSyntax
145+
)
146+
147+
try editResult.applyEdits(
148+
to: fileSystem,
149+
manifest: manifestSyntax,
150+
manifestPath: manifestPath,
151+
verbose: !globalOptions.logging.quiet
152+
)
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)