Skip to content

Commit 9eb96cf

Browse files
authored
Merge pull request #1188 from apple/an-e-prescription
Add “swift -e” support
2 parents 4e924da + 499468d commit 9eb96cf

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ public struct Driver {
562562
swiftCompilerPrefixArgs: self.swiftCompilerPrefixArgs)
563563

564564
// Classify and collect all of the input files.
565-
let inputFiles = try Self.collectInputFiles(&self.parsedOptions, diagnosticsEngine: diagnosticsEngine)
565+
let inputFiles = try Self.collectInputFiles(&self.parsedOptions, diagnosticsEngine: diagnosticsEngine, fileSystem: self.fileSystem)
566566
self.inputFiles = inputFiles
567567
self.recordedInputModificationDates = .init(uniqueKeysWithValues:
568568
Set(inputFiles).compactMap {
@@ -1791,10 +1791,11 @@ extension Driver {
17911791
/// Collect all of the input files from the parsed options, translating them into input files.
17921792
private static func collectInputFiles(
17931793
_ parsedOptions: inout ParsedOptions,
1794-
diagnosticsEngine: DiagnosticsEngine
1794+
diagnosticsEngine: DiagnosticsEngine,
1795+
fileSystem: FileSystem
17951796
) throws -> [TypedVirtualPath] {
17961797
var swiftFiles = [String: String]() // [Basename: Path]
1797-
return try parsedOptions.allInputs.map { input in
1798+
var paths = try parsedOptions.allInputs.map { input in
17981799
// Standard input is assumed to be Swift code.
17991800
if input == "-" {
18001801
return TypedVirtualPath(file: .standardInput, type: .swift)
@@ -1824,6 +1825,29 @@ extension Driver {
18241825

18251826
return TypedVirtualPath(file: inputHandle, type: fileType)
18261827
}
1828+
1829+
if parsedOptions.hasArgument(.e) {
1830+
if let mainPath = swiftFiles["main.swift"] {
1831+
diagnosticsEngine.emit(.error_two_files_same_name(basename: "main.swift", firstPath: mainPath, secondPath: "-e"))
1832+
diagnosticsEngine.emit(.note_explain_two_files_same_name)
1833+
throw Diagnostics.fatalError
1834+
}
1835+
1836+
try withTemporaryDirectory(dir: fileSystem.tempDirectory, removeTreeOnDeinit: false) { absPath in
1837+
let filePath = VirtualPath.absolute(absPath.appending(component: "main.swift"))
1838+
1839+
try fileSystem.writeFileContents(filePath) { file in
1840+
file <<< ###"#sourceLocation(file: "-e", line: 1)\###n"###
1841+
for option in parsedOptions.arguments(for: .e) {
1842+
file <<< option.argument.asSingle <<< "\n"
1843+
}
1844+
}
1845+
1846+
paths.append(TypedVirtualPath(file: filePath.intern(), type: .swift))
1847+
}
1848+
}
1849+
1850+
return paths
18271851
}
18281852

18291853
/// Determine the primary compiler and linker output kinds.

Sources/SwiftOptions/OptionParsing.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ extension OptionTable {
4545
}
4646

4747
var parsedOptions = ParsedOptions()
48+
var seenDashE = false
4849
var index = arguments.startIndex
4950
while index < arguments.endIndex {
5051
// Capture the next argument.
@@ -58,6 +59,12 @@ extension OptionTable {
5859

5960
// If this is not a flag, record it as an input.
6061
if argument == "-" || argument.first! != "-" {
62+
// If we've seen -e, this argument and all remaining ones are arguments to the -e script.
63+
if driverKind == .interactive && seenDashE {
64+
parsedOptions.addOption(.DASHDASH, argument: .multiple(Array(arguments[(index-1)...])))
65+
break
66+
}
67+
6168
parsedOptions.addInput(argument)
6269

6370
// In interactive mode, synthesize a "--" argument for all args after the first input.
@@ -90,6 +97,10 @@ extension OptionTable {
9097
currentDriverKind: driverKind)
9198
}
9299
}
100+
101+
if option == .e {
102+
seenDashE = true
103+
}
93104

94105
// Translate the argument
95106
switch option.kind {

Sources/SwiftOptions/Options.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ extension Option {
246246
public static let emitDigesterBaselinePath: Option = Option("-emit-digester-baseline-path", .separate, attributes: [.noInteractive, .argumentIsPath, .supplementaryOutput], metaVar: "<path>", helpText: "Emit a baseline file for the module to <path> using the API digester")
247247
public static let emitDigesterBaseline: Option = Option("-emit-digester-baseline", .flag, attributes: [.noInteractive, .supplementaryOutput], helpText: "Emit a baseline file for the module using the API digester")
248248
public static let emitExecutable: Option = Option("-emit-executable", .flag, attributes: [.noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit a linked executable", group: .modes)
249+
public static let emitExtensionBlockSymbols: Option = Option("-emit-extension-block-symbols", .flag, attributes: [.helpHidden, .frontend, .noInteractive, .supplementaryOutput], helpText: "Emit 'swift.extension' symbols for extensions to external types instead of directly associating members and conformances with the extended nominal when generating symbol graphs")
249250
public static let emitFineGrainedDependencySourcefileDotFiles: Option = Option("-emit-fine-grained-dependency-sourcefile-dot-files", .flag, attributes: [.helpHidden, .frontend], helpText: "Emit dot files for every source file.")
250251
public static let emitFixitsPath: Option = Option("-emit-fixits-path", .separate, attributes: [.frontend, .noDriver], metaVar: "<path>", helpText: "Output compiler fixits as source edits to <path>")
251252
public static let emitImportedModules: Option = Option("-emit-imported-modules", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit a list of the imported modules", group: .modes)
@@ -394,6 +395,7 @@ extension Option {
394395
public static let driverExplicitModuleBuild: Option = Option("-explicit-module-build", .flag, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit")
395396
public static let explicitSwiftModuleMap: Option = Option("-explicit-swift-module-map-file", .separate, attributes: [.frontend, .noDriver], metaVar: "<path>", helpText: "Specify a JSON file containing information of explicit Swift modules")
396397
public static let externalPassPipelineFilename: Option = Option("-external-pass-pipeline-filename", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<pass_pipeline_file>", helpText: "Use the pass pipeline defined by <pass_pipeline_file>")
398+
public static let e: Option = Option("-e", .joinedOrSeparate, attributes: [], helpText: "Executes a line of code provided on the command line")
397399
public static let FEQ: Option = Option("-F=", .joined, alias: Option.F, attributes: [.frontend, .argumentIsPath])
398400
public static let fileCompilationDir: Option = Option("-file-compilation-dir", .separate, attributes: [.frontend], metaVar: "<path>", helpText: "The compilation directory to embed in the debug info. Coverage mapping is not supported yet.")
399401
public static let filePrefixMap: Option = Option("-file-prefix-map", .separate, attributes: [.frontend], metaVar: "<prefix=replacement>", helpText: "Remap source paths in debug, coverage, and index info")
@@ -575,7 +577,8 @@ extension Option {
575577
public static let repl: Option = Option("-repl", .flag, attributes: [.helpHidden, .frontend, .noBatch], helpText: "REPL mode (the default if there is no input file)", group: .modes)
576578
public static let reportErrorsToDebugger: Option = Option("-report-errors-to-debugger", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Deprecated, will be removed in future versions.")
577579
public static let requireExplicitAvailabilityTarget: Option = Option("-require-explicit-availability-target", .separate, attributes: [.frontend, .noInteractive], metaVar: "<target>", helpText: "Suggest fix-its adding @available(<target>, *) to public declarations without availability")
578-
public static let requireExplicitAvailability: Option = Option("-require-explicit-availability", .flag, attributes: [.frontend, .noInteractive], helpText: "Require explicit availability on public declarations")
580+
public static let requireExplicitAvailabilityEQ: Option = Option("-require-explicit-availability=", .joined, attributes: [.frontend, .noInteractive], metaVar: "<error,warn,ignore>", helpText: "Set diagnostic level to report public declarations without an availability attribute")
581+
public static let requireExplicitAvailability: Option = Option("-require-explicit-availability", .flag, attributes: [.frontend, .noInteractive], helpText: "Warn on public declarations without an availability attribute")
579582
public static let requireExplicitSendable: Option = Option("-require-explicit-sendable", .flag, attributes: [.frontend, .noInteractive], helpText: "Require explicit Sendable annotations on public declarations")
580583
public static let requirementMachineMaxConcreteNesting: Option = Option("-requirement-machine-max-concrete-nesting=", .joined, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Set the maximum concrete type nesting depth before giving up")
581584
public static let requirementMachineMaxRuleCount: Option = Option("-requirement-machine-max-rule-count=", .joined, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Set the maximum number of rules before giving up")
@@ -962,6 +965,7 @@ extension Option {
962965
Option.emitDigesterBaselinePath,
963966
Option.emitDigesterBaseline,
964967
Option.emitExecutable,
968+
Option.emitExtensionBlockSymbols,
965969
Option.emitFineGrainedDependencySourcefileDotFiles,
966970
Option.emitFixitsPath,
967971
Option.emitImportedModules,
@@ -1110,6 +1114,7 @@ extension Option {
11101114
Option.driverExplicitModuleBuild,
11111115
Option.explicitSwiftModuleMap,
11121116
Option.externalPassPipelineFilename,
1117+
Option.e,
11131118
Option.FEQ,
11141119
Option.fileCompilationDir,
11151120
Option.filePrefixMap,
@@ -1291,6 +1296,7 @@ extension Option {
12911296
Option.repl,
12921297
Option.reportErrorsToDebugger,
12931298
Option.requireExplicitAvailabilityTarget,
1299+
Option.requireExplicitAvailabilityEQ,
12941300
Option.requireExplicitAvailability,
12951301
Option.requireExplicitSendable,
12961302
Option.requirementMachineMaxConcreteNesting,

Sources/SwiftOptions/ParsedOptions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ extension ParsedOptions {
260260
///
261261
/// This operation does not consume any inputs.
262262
public var hasAnyInput: Bool {
263-
return !lookupWithoutConsuming(.INPUT).isEmpty
263+
return !lookupWithoutConsuming(.INPUT).isEmpty || !lookupWithoutConsuming(.e).isEmpty
264264
}
265265

266266
/// Walk through all of the parsed options, modifying each one.

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,24 @@ final class SwiftDriverTests: XCTestCase {
298298
let driver4 = try Driver(args: ["swift", "-", "-working-directory" , "-wobble"])
299299
XCTAssertEqual(driver4.inputFiles, [ TypedVirtualPath(file: .standardInput, type: .swift )])
300300
}
301+
302+
func testDashE() throws {
303+
let fs = localFileSystem
304+
305+
var driver1 = try Driver(args: ["swift", "-e", "print(1)", "-eprint(2)", "foo/bar.swift", "baz/quux.swift"], fileSystem: fs)
306+
XCTAssertEqual(driver1.inputFiles.count, 1)
307+
XCTAssertEqual(driver1.inputFiles[0].file.basename, "main.swift")
308+
let tempFileContentsForDriver1 = try fs.readFileContents(XCTUnwrap(driver1.inputFiles[0].file.absolutePath))
309+
XCTAssertTrue(tempFileContentsForDriver1.description.hasSuffix("\nprint(1)\nprint(2)\n"))
310+
311+
let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs()
312+
XCTAssertEqual(plannedJobs.count, 1)
313+
XCTAssertEqual(plannedJobs[0].kind, .interpret)
314+
XCTAssertEqual(plannedJobs[0].commandLine.drop(while: { $0 != .flag("--") }),
315+
[.flag("--"), .flag("foo/bar.swift"), .flag("baz/quux.swift")])
316+
317+
XCTAssertThrowsError(try Driver(args: ["swiftc", "baz/main.swift", "-e", "print(1)"], fileSystem: fs))
318+
}
301319

302320
func testRecordedInputModificationDates() throws {
303321
guard let cwd = localFileSystem.currentWorkingDirectory else {

0 commit comments

Comments
 (0)