Skip to content

Commit 8649aa1

Browse files
authored
Merge pull request #451 from owenv/resolve-repl-args
2 parents 2855d00 + 219fbcd commit 8649aa1

File tree

7 files changed

+68
-15
lines changed

7 files changed

+68
-15
lines changed

Sources/SwiftDriver/Execution/ArgsResolver.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,15 @@ public final class ArgsResolver {
6767
return try lock.withLock {
6868
return try unsafeResolve(path: path)
6969
}
70+
7071
case .responseFilePath(let path):
7172
return "@\(try resolve(.path(path)))"
73+
7274
case let .joinedOptionAndPath(option, path):
7375
return option + (try resolve(.path(path)))
76+
77+
case let .squashedArgumentList(option: option, args: args):
78+
return try option + args.map{ try resolve($0) }.joined(separator: " ")
7479
}
7580
}
7681

Sources/SwiftDriver/Jobs/CommandLineArguments.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ extension Array where Element == Job.ArgTemplate {
162162
}
163163

164164
/// A shell-escaped string representation of the arguments, as they would appear on the command line.
165-
public var joinedArguments: String {
165+
/// Note: does not resolve arguments.
166+
public var joinedUnresolvedArguments: String {
166167
return self.map {
167168
switch $0 {
168169
case .flag(let string):
@@ -173,6 +174,8 @@ extension Array where Element == Job.ArgTemplate {
173174
return "@\(path.name.spm_shellEscaped())"
174175
case let .joinedOptionAndPath(option, path):
175176
return option.spm_shellEscaped() + path.name.spm_shellEscaped()
177+
case let .squashedArgumentList(option, args):
178+
return (option + args.joinedUnresolvedArguments).spm_shellEscaped()
176179
}
177180
}.joined(separator: " ")
178181
}

Sources/SwiftDriver/Jobs/Job.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public struct Job: Codable, Equatable, Hashable {
4949

5050
/// Represents a joined option+path combo.
5151
case joinedOptionAndPath(String, VirtualPath)
52+
53+
/// Represents a list of arguments squashed together and passed as a single argument.
54+
case squashedArgumentList(option: String, args: [ArgTemplate])
5255
}
5356

5457
/// The Swift module this job involves.
@@ -234,11 +237,15 @@ extension Job.Kind {
234237

235238
extension Job.ArgTemplate: Codable {
236239
private enum CodingKeys: String, CodingKey {
237-
case flag, path, responseFilePath, joinedOptionAndPath
240+
case flag, path, responseFilePath, joinedOptionAndPath, squashedArgumentList
238241

239242
enum JoinedOptionAndPathCodingKeys: String, CodingKey {
240243
case option, path
241244
}
245+
246+
enum SquashedArgumentListCodingKeys: String, CodingKey {
247+
case option, args
248+
}
242249
}
243250

244251
public func encode(to encoder: Encoder) throws {
@@ -259,6 +266,12 @@ extension Job.ArgTemplate: Codable {
259266
forKey: .joinedOptionAndPath)
260267
try keyedContainer.encode(option, forKey: .option)
261268
try keyedContainer.encode(path, forKey: .path)
269+
case .squashedArgumentList(option: let option, args: let args):
270+
var keyedContainer = container.nestedContainer(
271+
keyedBy: CodingKeys.SquashedArgumentListCodingKeys.self,
272+
forKey: .squashedArgumentList)
273+
try keyedContainer.encode(option, forKey: .option)
274+
try keyedContainer.encode(args, forKey: .args)
262275
}
263276
}
264277

@@ -286,6 +299,12 @@ extension Job.ArgTemplate: Codable {
286299
forKey: .joinedOptionAndPath)
287300
self = .joinedOptionAndPath(try keyedValues.decode(String.self, forKey: .option),
288301
try keyedValues.decode(VirtualPath.self, forKey: .path))
302+
case .squashedArgumentList:
303+
let keyedValues = try values.nestedContainer(
304+
keyedBy: CodingKeys.SquashedArgumentListCodingKeys.self,
305+
forKey: .squashedArgumentList)
306+
self = .squashedArgumentList(option: try keyedValues.decode(String.self, forKey: .option),
307+
args: try keyedValues.decode([Job.ArgTemplate].self, forKey: .args))
289308
}
290309
}
291310
}

Sources/SwiftDriver/Jobs/ReplJob.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ extension Driver {
2222
try commandLine.appendAll(.l, .framework, .L, from: &parsedOptions)
2323

2424
// Squash important frontend options into a single argument for LLDB.
25-
let lldbArg = "--repl=\(commandLine.joinedArguments)"
25+
let lldbCommandLine: [Job.ArgTemplate] = [.squashedArgumentList(option: "--repl=", args: commandLine)]
2626
return Job(
2727
moduleName: moduleOutputInfo.name,
2828
kind: .repl,
2929
tool: .absolute(try toolchain.getToolPath(.lldb)),
30-
commandLine: [Job.ArgTemplate.flag(lldbArg)],
30+
commandLine: lldbCommandLine,
3131
inputs: inputs,
3232
primaryInputs: [],
3333
outputs: [],

Sources/SwiftDriverExecution/SwiftDriverExecutor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ public final class SwiftDriverExecutor: DriverExecutor {
8282

8383
public func description(of job: Job, forceResponseFiles: Bool) throws -> String {
8484
let (args, usedResponseFile) = try resolver.resolveArgumentList(for: job, forceResponseFiles: forceResponseFiles)
85-
var result = args.joined(separator: " ")
85+
var result = args.map { $0.spm_shellEscaped() }.joined(separator: " ")
8686

8787
if usedResponseFile {
8888
// Print the response file arguments as a comment.
89-
result += " # \(job.commandLine.joinedArguments)"
89+
result += " # \(job.commandLine.joinedUnresolvedArguments)"
9090
}
9191

9292
if !job.extraEnvironment.isEmpty {

Tests/SwiftDriverTests/JobExecutorTests.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,21 @@ final class JobExecutorTests: XCTestCase {
289289
}
290290
}
291291

292+
func testShellEscapingArgsInJobDescription() throws {
293+
let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(),
294+
processSet: ProcessSet(),
295+
fileSystem: localFileSystem,
296+
env: [:])
297+
let job = Job(moduleName: "Module",
298+
kind: .compile,
299+
tool: .absolute(.init("/path/to/the tool")),
300+
commandLine: [.path(.absolute(.init("/with space"))),
301+
.path(.absolute(.init("/withoutspace")))],
302+
inputs: [], primaryInputs: [], outputs: [])
303+
XCTAssertEqual(try executor.description(of: job, forceResponseFiles: false),
304+
"'/path/to/the tool' '/with space' /withoutspace")
305+
}
306+
292307
func testInputModifiedDuringMultiJobBuild() throws {
293308
try withTemporaryDirectory { path in
294309
let main = path.appending(component: "main.swift")
@@ -330,6 +345,18 @@ final class JobExecutorTests: XCTestCase {
330345
}
331346
}
332347

348+
func testResolveSquashedArgs() throws {
349+
try withTemporaryDirectory { path in
350+
let resolver = try ArgsResolver(fileSystem: localFileSystem, temporaryDirectory: .absolute(path))
351+
let tmpPath = VirtualPath.temporaryWithKnownContents(.init("one.txt"), "hello, world!".data(using: .utf8)!)
352+
let tmpPath2 = VirtualPath.temporaryWithKnownContents(.init("two.txt"), "goodbye!".data(using: .utf8)!)
353+
let resolvedCommandLine = try resolver.resolve(
354+
.squashedArgumentList(option: "--opt=", args: [.path(tmpPath), .path(tmpPath2)]))
355+
XCTAssertEqual(resolvedCommandLine, "--opt=\(path.appending(component: "one.txt").pathString) \(path.appending(component: "two.txt").pathString)")
356+
XCTAssertEqual(resolvedCommandLine.spm_shellEscaped(), "'--opt=\(path.appending(component: "one.txt").pathString) \(path.appending(component: "two.txt").pathString)'")
357+
}
358+
}
359+
333360
func testSaveTemps() throws {
334361
do {
335362
try withTemporaryDirectory { path in

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,7 +1626,7 @@ final class SwiftDriverTests: XCTestCase {
16261626
let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs()
16271627
XCTAssertEqual(plannedJobs.count, 2)
16281628
XCTAssertEqual(plannedJobs[0].kind, .compile)
1629-
print(plannedJobs[0].commandLine.joinedArguments)
1629+
print(plannedJobs[0].commandLine.joinedUnresolvedArguments)
16301630
XCTAssert(plannedJobs[0].commandLine.contains(.flag("-supplementary-output-file-map")))
16311631
}
16321632

@@ -1821,10 +1821,9 @@ final class SwiftDriverTests: XCTestCase {
18211821
throw XCTSkip()
18221822
}
18231823

1824-
func isLLDBREPLFlag(_ arg: Job.ArgTemplate) -> Bool {
1825-
if case .flag(let replString) = arg {
1826-
return replString.hasPrefix("--repl=") &&
1827-
!replString.contains("-module-name")
1824+
func isExpectedLLDBREPLFlag(_ arg: Job.ArgTemplate) -> Bool {
1825+
if case let .squashedArgumentList(option: opt, args: args) = arg {
1826+
return opt == "--repl=" && !args.contains("-module-name")
18281827
}
18291828
return false
18301829
}
@@ -1836,7 +1835,7 @@ final class SwiftDriverTests: XCTestCase {
18361835
let replJob = plannedJobs.first!
18371836
XCTAssertTrue(replJob.tool.name.contains("lldb"))
18381837
XCTAssertTrue(replJob.requiresInPlaceExecution)
1839-
XCTAssert(replJob.commandLine.contains(where: { isLLDBREPLFlag($0) }))
1838+
XCTAssert(replJob.commandLine.contains(where: { isExpectedLLDBREPLFlag($0) }))
18401839
}
18411840

18421841
do {
@@ -1846,7 +1845,7 @@ final class SwiftDriverTests: XCTestCase {
18461845
let replJob = plannedJobs.first!
18471846
XCTAssertTrue(replJob.tool.name.contains("lldb"))
18481847
XCTAssertTrue(replJob.requiresInPlaceExecution)
1849-
XCTAssert(replJob.commandLine.contains(where: { isLLDBREPLFlag($0) }))
1848+
XCTAssert(replJob.commandLine.contains(where: { isExpectedLLDBREPLFlag($0) }))
18501849
}
18511850

18521851
do {
@@ -1858,7 +1857,7 @@ final class SwiftDriverTests: XCTestCase {
18581857
let replJob = plannedJobs.first!
18591858
XCTAssertTrue(replJob.tool.name.contains("lldb"))
18601859
XCTAssertTrue(replJob.requiresInPlaceExecution)
1861-
XCTAssert(replJob.commandLine.contains(where: { isLLDBREPLFlag($0) }))
1860+
XCTAssert(replJob.commandLine.contains(where: { isExpectedLLDBREPLFlag($0) }))
18621861
}
18631862

18641863
do {
@@ -4312,7 +4311,7 @@ private extension Array where Element == Job.ArgTemplate {
43124311
switch $0 {
43134312
case let .path(path):
43144313
return path.basename == basename
4315-
case .flag, .responseFilePath, .joinedOptionAndPath:
4314+
case .flag, .responseFilePath, .joinedOptionAndPath, .squashedArgumentList:
43164315
return false
43174316
}
43184317
}

0 commit comments

Comments
 (0)