Skip to content

Commit c0963cb

Browse files
mikeashairspeedswift
authored andcommitted
[swiftdt] Switch to ArgumentParser instead of handcrafted argument parsing
rdar://problem/55481578
1 parent 404867e commit c0963cb

File tree

3 files changed

+45
-87
lines changed

3 files changed

+45
-87
lines changed

tools/swiftdt/Package.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import PackageDescription
66
let package = Package(
77
name: "swiftdt",
88
dependencies: [
9-
// Dependencies declare other packages that this package depends on.
10-
// .package(path: "../SymbolicationShims"),
9+
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"),
1110
],
1211
targets: [
1312
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
1413
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
1514
.target(
1615
name: "swiftdt",
17-
dependencies: ["SymbolicationShims"]),
16+
dependencies: [
17+
"SymbolicationShims",
18+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
19+
]),
1820
.testTarget(
1921
name: "swiftdtTests",
2022
dependencies: ["swiftdt"]),

tools/swiftdt/Sources/swiftdt/RemoteMirrorExtensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SwiftRemoteMirror
22

33
extension SwiftReflectionContextRef {
4-
struct Error: Swift.Error {
4+
struct Error: Swift.Error, CustomStringConvertible {
55
var description: String
66

77
init(cString: UnsafePointer<CChar>) {
Lines changed: 39 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ArgumentParser
12
import SwiftRemoteMirror
23

34

@@ -12,47 +13,6 @@ func machErrStr(_ kr: kern_return_t) -> String {
1213
return "\(errStr) (0x\(errHex))"
1314
}
1415

15-
16-
var argv = ArraySlice(CommandLine.arguments)
17-
guard let executableName = argv.popFirst() else {
18-
argFail("Command line arguments are completely empty!")
19-
}
20-
21-
struct Command {
22-
var name: String
23-
var help: String
24-
var call: (inout ArraySlice<String>) throws -> Void
25-
26-
init(name: String, help: String,
27-
call: @escaping (inout ArraySlice<String>) -> Void) {
28-
self.name = name
29-
self.help = help
30-
self.call = call
31-
}
32-
33-
init(name: String, help: String,
34-
call: @escaping (SwiftReflectionContextRef) throws -> Void) {
35-
self.name = name
36-
self.help = help
37-
self.call = { try withReflectionContext(args: &$0, call: call) }
38-
}
39-
}
40-
41-
let commands = [
42-
Command(
43-
name: "dump-conformance-cache",
44-
help: "Print the contents of the target's protocol conformance cache.",
45-
call: dumpConformanceCache),
46-
Command(
47-
name: "dump-metadata-allocations",
48-
help: "Print the target's metadata allocations.",
49-
call: dumpMetadataAllocations),
50-
Command(
51-
name: "help",
52-
help: "Print this help.",
53-
call: printUsage),
54-
]
55-
5616
func dumpConformanceCache(context: SwiftReflectionContextRef) throws {
5717
try context.iterateConformanceCache(call: { type, proto in
5818
let typeName = context.name(metadata: type) ?? "<unknown>"
@@ -90,29 +50,10 @@ func dumpMetadataAllocations(context: SwiftReflectionContextRef) throws {
9050
}
9151
}
9252

93-
func printUsage(args: inout ArraySlice<String>) {
94-
print("Usage: \(executableName) <command>", to: &Std.err)
95-
print("", to: &Std.err)
96-
print("Available commands:", to: &Std.err)
97-
98-
let maxWidth = commands.map({ $0.name.count }).max() ?? 0
99-
for command in commands {
100-
var paddedName = command.name
101-
while paddedName.count < maxWidth {
102-
paddedName = " " + paddedName
103-
}
104-
print(" \(paddedName) - \(command.help)", to: &Std.err)
105-
}
106-
}
107-
108-
func makeReflectionContext(args: inout ArraySlice<String>)
53+
func makeReflectionContext(nameOrPid: String)
10954
-> (Inspector, SwiftReflectionContextRef) {
110-
guard let pidStr = args.popFirst() else {
111-
argFail("Must specify a pid or process name")
112-
}
113-
114-
guard let pid = pidFromHint(pidStr) else {
115-
argFail("Cannot find pid/process \(pidStr)")
55+
guard let pid = pidFromHint(nameOrPid) else {
56+
argFail("Cannot find pid/process \(nameOrPid)")
11657
}
11758

11859
guard let inspector = Inspector(pid: pid) else {
@@ -133,36 +74,51 @@ func makeReflectionContext(args: inout ArraySlice<String>)
13374
}
13475

13576
func withReflectionContext(
136-
args: inout ArraySlice<String>,
77+
nameOrPid: String,
13778
call: (SwiftReflectionContextRef) throws -> Void) throws {
138-
let (inspector, context) = makeReflectionContext(args: &args)
79+
let (inspector, context) = makeReflectionContext(nameOrPid: nameOrPid)
13980
defer {
14081
swift_reflection_destroyReflectionContext(context)
14182
inspector.destroyContext()
14283
}
14384
try call(context)
14485
}
14586

146-
let commandName = argv.popFirst()
147-
for command in commands {
148-
if command.name == commandName {
149-
do {
150-
try command.call(&argv)
151-
} catch let error as SwiftReflectionContextRef.Error {
152-
print("Error: \(error.description)", to: &Std.err)
153-
exit(1)
154-
} catch {
155-
print("Unknown error: \(error)")
87+
struct Swiftdt: ParsableCommand {
88+
static let configuration = CommandConfiguration(
89+
abstract: "Swift runtime debug tool",
90+
subcommands: [
91+
DumpConformanceCache.self,
92+
DumpMetadataAllocations.self,
93+
])
94+
}
95+
96+
struct DumpConformanceCache: ParsableCommand {
97+
static let configuration = CommandConfiguration(
98+
abstract: "Print the contents of the target's protocol conformance cache.")
99+
100+
@Argument(help: "The pid or partial name of the target process")
101+
var nameOrPid: String
102+
103+
func run() throws {
104+
try withReflectionContext(nameOrPid: nameOrPid) {
105+
try dumpConformanceCache(context: $0)
156106
}
157-
exit(0)
158107
}
159108
}
160109

161-
if let commandName = commandName {
162-
print("error: \(executableName): unknown command \(commandName)", to: &Std.err)
163-
} else {
164-
print("error: \(executableName): missing command", to: &Std.err)
110+
struct DumpMetadataAllocations: ParsableCommand {
111+
static let configuration = CommandConfiguration(
112+
abstract: "Print the target's metadata allocations.")
113+
@Argument(help: "The pid or partial name of the target process")
114+
115+
var nameOrPid: String
116+
117+
func run() throws {
118+
try withReflectionContext(nameOrPid: nameOrPid) {
119+
try dumpMetadataAllocations(context: $0)
120+
}
121+
}
165122
}
166-
print("", to: &Std.err)
167-
printUsage(args: &argv)
168-
exit(EX_USAGE)
123+
124+
Swiftdt.main()

0 commit comments

Comments
 (0)