Skip to content

Commit 8c81cb9

Browse files
committed
Add support for launching the REPL
1 parent 4efb328 commit 8c81cb9

File tree

10 files changed

+148
-13
lines changed

10 files changed

+148
-13
lines changed

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,17 @@ extension Array where Element == Job.ArgTemplate {
311311
)
312312
appendFlag(isTrue ? trueFlag : falseFlag)
313313
}
314+
315+
var joinedArguments: String {
316+
return self.map {
317+
switch $0 {
318+
case .flag(let string):
319+
return string.spm_shellEscaped()
320+
case .path(let path):
321+
return path.name.spm_shellEscaped()
322+
}
323+
}.joined(separator: " ")
324+
}
314325
}
315326

316327

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ extension Driver {
160160
commandLine.appendPath(importedObjCHeader)
161161
}
162162

163-
commandLine.appendFlags("-module-name", moduleName)
163+
// Repl Jobs may include -module-name depending on the selected REPL (LLDB or integrated).
164+
if compilerMode != .repl {
165+
commandLine.appendFlags("-module-name", moduleName)
166+
}
164167
}
165168

166169
mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate], primaryInputs: [TypedVirtualPath]) throws -> [TypedVirtualPath] {

Sources/SwiftDriver/Jobs/Job.swift

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public struct Job: Codable, Equatable {
2121
case autolinkExtract = "autolink-extract"
2222
case emitModule = "emit-module"
2323
case interpret
24+
case repl
2425
}
2526

2627
public enum ArgTemplate: Equatable {
@@ -73,17 +74,7 @@ public struct Job: Codable, Equatable {
7374

7475
extension Job: CustomStringConvertible {
7576
public var description: String {
76-
var result: String = tool.name
77-
78-
for arg in commandLine {
79-
result += " "
80-
switch arg {
81-
case .flag(let string):
82-
result += string.spm_shellEscaped()
83-
case .path(let path):
84-
result += path.name.spm_shellEscaped()
85-
}
86-
}
77+
var result: String = "\(tool.name) \(commandLine.joinedArguments)"
8778

8879
if !self.extraEnvironment.isEmpty {
8980
result += " #"

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
13+
public enum PlanningError: Error, DiagnosticData {
14+
case replReceivedInput
15+
16+
public var description: String {
17+
switch self {
18+
case .replReceivedInput:
19+
return "REPL mode requires no input files"
20+
}
21+
}
22+
}
23+
1224
/// Planning for builds
1325
extension Driver {
1426
/// Plan a standard compilation, which produces jobs for compiling separate
@@ -145,7 +157,10 @@ extension Driver {
145157
// Plan the build.
146158
switch compilerMode {
147159
case .repl:
148-
fatalError("Not yet supported")
160+
if !inputFiles.isEmpty {
161+
throw PlanningError.replReceivedInput
162+
}
163+
return [try replJob()]
149164

150165
case .immediate:
151166
return [try interpretJob(inputs: inputFiles)]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===--------------- ReplJob.swift - Swift REPL ---------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
extension Driver {
14+
mutating func replJob() throws -> Job {
15+
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
16+
17+
try addCommonFrontendOptions(commandLine: &commandLine)
18+
// FIXME: MSVC runtime flags
19+
20+
try commandLine.appendLast(.importObjcHeader, from: &parsedOptions)
21+
try commandLine.appendAll(.l, .framework, .L, from: &parsedOptions)
22+
23+
// Look for -lldb-repl or -deprecated-integrated-repl to determine which
24+
// REPL to use. If neither is provided, prefer LLDB if it can be found.
25+
if parsedOptions.hasFlag(positive: .lldbRepl,
26+
negative: .deprecatedIntegratedRepl,
27+
default: (try? toolchain.getToolPath(.lldb)) != nil) {
28+
// Squash important frontend options into a single argument for LLDB.
29+
let lldbArg = "--repl=\(commandLine.joinedArguments)"
30+
return Job(
31+
kind: .repl,
32+
tool: .absolute(try toolchain.getToolPath(.lldb)),
33+
commandLine: [Job.ArgTemplate.flag(lldbArg)],
34+
inputs: [],
35+
outputs: []
36+
)
37+
} else {
38+
// Invoke the integrated REPL, which is part of the frontend.
39+
commandLine = [.flag("-frontend"), .flag("-repl")] + commandLine
40+
commandLine.appendFlags("-module-name", moduleName)
41+
return Job(
42+
kind: .repl,
43+
tool: .absolute(try toolchain.getToolPath(.swiftCompiler)),
44+
commandLine: commandLine,
45+
inputs: [],
46+
outputs: []
47+
)
48+
}
49+
}
50+
}

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public final class DarwinToolchain: Toolchain {
4444
return AbsolutePath(result)
4545
case .swiftAutolinkExtract:
4646
return try lookup(executable: "swift-autolink-extract")
47+
case .lldb:
48+
return try lookup(executable: "lldb")
4749
}
4850
}
4951

Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public final class GenericUnixToolchain: Toolchain {
4242
return try lookup(executable: "swift-autolink-extract")
4343
case .dsymutil:
4444
return try lookup(executable: "dsymutil")
45+
case .lldb:
46+
return try lookup(executable: "lldb")
4547
}
4648
}
4749

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public enum Tool {
1919
case clang
2020
case swiftAutolinkExtract
2121
case dsymutil
22+
case lldb
2223
}
2324

2425
/// Describes a toolchain, which includes information about compilers, linkers

Sources/SwiftDriver/Utilities/Diagnostics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import TSCBasic
1313

1414
public typealias Diagnostic = TSCBasic.Diagnostic
15+
public typealias DiagnosticData = TSCBasic.DiagnosticData
1516

1617
extension Diagnostic.Message {
1718
static var error_static_emit_executable_disallowed: Diagnostic.Message {

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,65 @@ final class SwiftDriverTests: XCTestCase {
885885

886886
}
887887

888+
func testRepl() throws {
889+
890+
func isLLDBREPLFlag(_ arg: Job.ArgTemplate) -> Bool {
891+
if case .flag(let replString) = arg {
892+
return replString.hasPrefix("--repl=") &&
893+
!replString.contains("-module-name")
894+
}
895+
return false
896+
}
897+
898+
do {
899+
var driver = try Driver(args: ["swift"])
900+
let plannedJobs = try driver.planBuild()
901+
XCTAssertEqual(plannedJobs.count, 1)
902+
let replJob = plannedJobs.first!
903+
XCTAssertTrue(replJob.tool.name.contains("lldb"))
904+
XCTAssert(replJob.commandLine.contains(where: { isLLDBREPLFlag($0) }))
905+
}
906+
907+
do {
908+
var driver = try Driver(args: ["swift", "-repl"])
909+
let plannedJobs = try driver.planBuild()
910+
XCTAssertEqual(plannedJobs.count, 1)
911+
let replJob = plannedJobs.first!
912+
XCTAssertTrue(replJob.tool.name.contains("lldb"))
913+
XCTAssert(replJob.commandLine.contains(where: { isLLDBREPLFlag($0) }))
914+
}
915+
916+
do {
917+
let (mode, args) = try Driver.invocationRunMode(forArgs: ["swift", "repl"])
918+
XCTAssertEqual(mode, .normal(isRepl: true))
919+
var driver = try Driver(args: args)
920+
let plannedJobs = try driver.planBuild()
921+
XCTAssertEqual(plannedJobs.count, 1)
922+
let replJob = plannedJobs.first!
923+
XCTAssertTrue(replJob.tool.name.contains("lldb"))
924+
XCTAssert(replJob.commandLine.contains(where: { isLLDBREPLFlag($0) }))
925+
}
926+
927+
do {
928+
var driver = try Driver(args: ["swift", "-deprecated-integrated-repl"])
929+
let plannedJobs = try driver.planBuild()
930+
XCTAssertEqual(plannedJobs.count, 1)
931+
let replJob = plannedJobs.first!
932+
XCTAssertTrue(replJob.tool.name.contains("swift"))
933+
XCTAssertTrue(replJob.commandLine.count >= 2)
934+
XCTAssertEqual(replJob.commandLine[0], .flag("-frontend"))
935+
XCTAssertEqual(replJob.commandLine[1], .flag("-repl"))
936+
XCTAssert(replJob.commandLine.contains(.flag("-module-name")))
937+
}
938+
939+
do {
940+
var driver = try Driver(args: ["swift", "-repl", "/foo/bar/Test.swift"])
941+
XCTAssertThrowsError(try driver.planBuild()) { error in
942+
XCTAssertEqual(error as? PlanningError, .replReceivedInput)
943+
}
944+
}
945+
}
946+
888947
func testImmediateMode() throws {
889948
do {
890949
var driver = try Driver(args: ["swift", "foo.swift"])

0 commit comments

Comments
 (0)