Skip to content

Commit bde66c6

Browse files
committed
Rewrite spm-manifest-tool using swift-argument-parser and the updated SPMPackageEditor
1 parent e2c9cb2 commit bde66c6

File tree

4 files changed

+155
-164
lines changed

4 files changed

+155
-164
lines changed

Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,5 +322,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] != nil {
322322
dependencies: ["Workspace", "PackageModel", "PackageLoading",
323323
"SourceControl", "SwiftSyntax", "SwiftToolsSupport-auto"]),
324324
.testTarget(name: "SPMPackageEditorTests", dependencies: ["SPMPackageEditor", "SPMTestSupport"]),
325+
.target(name: "swiftpm-manifest-tool",
326+
dependencies: ["SPMPackageEditor", "ArgumentParser"])
325327
]
326328
}

Sources/SPMPackageEditor/PackageEditor.swift

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ public final class PackageEditor {
2626
let context: PackageEditorContext
2727

2828
/// Create a package editor instance.
29-
public convenience init(buildDir: AbsolutePath, toolchain: UserToolchain) throws {
30-
self.init(context: try PackageEditorContext(buildDir: buildDir, toolchain: toolchain))
29+
public convenience init(manifestPath: AbsolutePath,
30+
buildDir: AbsolutePath,
31+
toolchain: UserToolchain) throws {
32+
self.init(context: try PackageEditorContext(manifestPath: manifestPath,
33+
buildDir: buildDir,
34+
toolchain: toolchain))
3135
}
3236

3337
/// Create a package editor instance.
@@ -41,41 +45,41 @@ public final class PackageEditor {
4145
}
4246

4347
/// Add a package dependency.
44-
public func addPackageDependency(options: Options.AddPackageDependency) throws {
45-
var options = options
46-
48+
public func addPackageDependency(url: String, requirement: PackageDependencyRequirement?) throws {
49+
var requirement = requirement
50+
let manifestPath = context.manifestPath
4751
// Validate that the package doesn't already contain this dependency.
4852
// FIXME: We need to handle version-specific manifests.
49-
let loadedManifest = try context.loadManifest(at: options.manifestPath.parentDirectory)
53+
let loadedManifest = try context.loadManifest(at: context.manifestPath.parentDirectory)
5054
let containsDependency = loadedManifest.dependencies.contains {
5155
return PackageIdentity(options.url) == PackageIdentity($0.url)
5256
}
5357
guard !containsDependency else {
54-
throw StringError("Already has dependency \(options.url)")
58+
throw StringError("Already has dependency \(url)")
5559
}
5660

5761
// If the input URL is a path, force the requirement to be a local package.
58-
if TSCUtility.URL.scheme(options.url) == nil {
59-
assert(options.requirement == nil || options.requirement == .localPackage)
60-
options.requirement = .localPackage
62+
if TSCUtility.URL.scheme(url) == nil {
63+
assert(requirement == nil || requirement == .localPackage)
64+
requirement = .localPackage
6165
}
6266

6367
// Load the dependency manifest depending on the inputs.
6468
let dependencyManifest: Manifest
65-
let requirement: PackageDependencyRequirement
66-
if options.requirement == .localPackage {
69+
if requirement == .localPackage {
6770
// For local packages, load the manifest and get the first library product name.
68-
let path = AbsolutePath(options.url, relativeTo: fs.currentWorkingDirectory!)
71+
let path = AbsolutePath(url, relativeTo: fs.currentWorkingDirectory!)
6972
dependencyManifest = try context.loadManifest(at: path)
7073
requirement = .localPackage
7174
} else {
7275
// Otherwise, first lookup the dependency.
7376
let spec = RepositorySpecifier(url: options.url)
7477
let handle = try tsc_await{ context.repositoryManager.lookup(repository: spec, completion: $0) }
78+
7579
let repo = try handle.open()
7680

7781
// Compute the requirement.
78-
if let inputRequirement = options.requirement {
82+
if let inputRequirement = requirement {
7983
requirement = inputRequirement
8084
} else {
8185
// Use the latest version or the master branch.
@@ -85,15 +89,15 @@ public final class PackageEditor {
8589
}
8690

8791
// Load the manifest.
88-
let revision = try repo.resolveRevision(identifier: requirement.ref!)
92+
let revision = try repo.resolveRevision(identifier: requirement!.ref!)
8993
let repoFS = try repo.openFileView(revision: revision)
9094
dependencyManifest = try context.loadManifest(at: .root, fs: repoFS)
9195
}
9296

9397
// Add the package dependency.
94-
let manifestContents = try fs.readFileContents(options.manifestPath).cString
98+
let manifestContents = try fs.readFileContents(manifestPath).cString
9599
let editor = try ManifestRewriter(manifestContents)
96-
try editor.addPackageDependency(url: options.url, requirement: requirement)
100+
try editor.addPackageDependency(url: url, requirement: requirement!)
97101

98102
// Add the product in the first regular target, if possible.
99103
let productName = dependencyManifest.products.filter{ $0.type.isLibrary }.map{ $0.name }.first
@@ -105,40 +109,39 @@ public final class PackageEditor {
105109
}
106110

107111
// FIXME: We should verify our edits by loading the edited manifest before writing it to disk.
108-
try fs.writeFileContents(options.manifestPath, bytes: ByteString(encodingAsUTF8: editor.editedManifest))
112+
try fs.writeFileContents(manifestPath, bytes: ByteString(encodingAsUTF8: editor.editedManifest))
109113
}
110114

111115
/// Add a new target.
112-
public func addTarget(options: Options.AddTarget) throws {
113-
let manifest = options.manifestPath
114-
let targetName = options.targetName
116+
public func addTarget(name targetName: String, type targetType: TargetType?) throws {
117+
let manifestPath = context.manifestPath
115118
let testTargetName = targetName + "Tests"
116119

117120
// Validate that the package doesn't already contain this dependency.
118121
// FIXME: We need to handle version-specific manifests.
119-
let loadedManifest = try context.loadManifest(at: options.manifestPath.parentDirectory)
122+
let loadedManifest = try context.loadManifest(at: manifestPath.parentDirectory)
120123
if loadedManifest.targets.contains(where: { $0.name == targetName }) {
121124
throw StringError("Already has a target named \(targetName)")
122125
}
123126

124-
let manifestContents = try fs.readFileContents(options.manifestPath).cString
127+
let manifestContents = try fs.readFileContents(manifestPath).cString
125128
let editor = try ManifestRewriter(manifestContents)
126129
try editor.addTarget(targetName: targetName)
127130
try editor.addTarget(targetName: testTargetName, type: .test)
128131
try editor.addTargetDependency(target: testTargetName, dependency: targetName)
129132

130133
// FIXME: We should verify our edits by loading the edited manifest before writing it to disk.
131-
try fs.writeFileContents(manifest, bytes: ByteString(encodingAsUTF8: editor.editedManifest))
134+
try fs.writeFileContents(manifestPath, bytes: ByteString(encodingAsUTF8: editor.editedManifest))
132135

133136
// Write template files.
134-
let targetPath = manifest.parentDirectory.appending(components: "Sources", targetName)
137+
let targetPath = manifestPath.parentDirectory.appending(components: "Sources", targetName)
135138
if !localFileSystem.exists(targetPath) {
136139
let file = targetPath.appending(components: targetName + ".swift")
137140
try fs.createDirectory(targetPath)
138141
try fs.writeFileContents(file, bytes: "")
139142
}
140143

141-
let testTargetPath = manifest.parentDirectory.appending(components: "Tests", testTargetName)
144+
let testTargetPath = manifestPath.parentDirectory.appending(components: "Tests", testTargetName)
142145
if !fs.exists(testTargetPath) {
143146
let file = testTargetPath.appending(components: testTargetName + ".swift")
144147
try fs.createDirectory(testTargetPath)
@@ -204,40 +207,6 @@ public enum PackageDependencyRequirement: Equatable {
204207
}
205208
}
206209

207-
public enum Options {
208-
public struct AddPackageDependency {
209-
public var manifestPath: AbsolutePath
210-
public var url: String
211-
public var requirement: PackageDependencyRequirement?
212-
213-
public init(
214-
manifestPath: AbsolutePath,
215-
url: String,
216-
requirement: PackageDependencyRequirement? = nil
217-
) {
218-
self.manifestPath = manifestPath
219-
self.url = url
220-
self.requirement = requirement
221-
}
222-
}
223-
224-
public struct AddTarget {
225-
public var manifestPath: AbsolutePath
226-
public var targetName: String
227-
public var targetType: TargetType
228-
229-
public init(
230-
manifestPath: AbsolutePath,
231-
targetName: String,
232-
targetType: TargetType = .regular
233-
) {
234-
self.manifestPath = manifestPath
235-
self.targetName = targetName
236-
self.targetType = targetType
237-
}
238-
}
239-
}
240-
241210
extension ProductType {
242211
var isLibrary: Bool {
243212
switch self {
@@ -251,6 +220,8 @@ extension ProductType {
251220

252221
/// The global context for package editor.
253222
public final class PackageEditorContext {
223+
/// Path to the package manifest.
224+
let manifestPath: AbsolutePath
254225

255226
/// Path to the build directory of the package.
256227
let buildDir: AbsolutePath
@@ -264,7 +235,11 @@ public final class PackageEditorContext {
264235
/// The file system in use.
265236
let fs: FileSystem
266237

267-
public init(buildDir: AbsolutePath, toolchain: UserToolchain, fs: FileSystem = localFileSystem) throws {
238+
public init(manifestPath: AbsolutePath,
239+
buildDir: AbsolutePath,
240+
toolchain: UserToolchain,
241+
fs: FileSystem = localFileSystem) throws {
242+
self.manifestPath = manifestPath
268243
self.buildDir = buildDir
269244
self.fs = fs
270245

0 commit comments

Comments
 (0)