Skip to content

Commit 74d04d9

Browse files
authored
Merge pull request swiftlang#567 from artemcm/OptionalFrontendParseable
Add an option that causes individual `swift-frontend` invocations to emit parseable output, instead of the driver.
2 parents 901e1e8 + 5e1b269 commit 74d04d9

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ public struct Driver {
503503
fileSystem: fileSystem,
504504
workingDirectory: workingDirectory,
505505
diagnosticEngine: diagnosticEngine)
506+
Self.validateParseableOutputArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
506507
Self.validateCompilationConditionArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
507508
Self.validateFrameworkSearchPathArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
508509
Self.validateCoverageArgs(&parsedOptions, diagnosticsEngine: diagnosticEngine)
@@ -2092,6 +2093,14 @@ extension Driver {
20922093
}
20932094
}
20942095

2096+
static func validateParseableOutputArgs(_ parsedOptions: inout ParsedOptions,
2097+
diagnosticEngine: DiagnosticsEngine) {
2098+
if parsedOptions.contains(.parseableOutput) &&
2099+
parsedOptions.contains(.useFrontendParseableOutput) {
2100+
diagnosticEngine.emit(Error.conflictingOptions(.parseableOutput, .useFrontendParseableOutput))
2101+
}
2102+
}
2103+
20952104
static func validateCompilationConditionArgs(_ parsedOptions: inout ParsedOptions,
20962105
diagnosticEngine: DiagnosticsEngine) {
20972106
for arg in parsedOptions.arguments(for: .D).map(\.argument.asSingle) {

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ extension Driver {
242242
commandLine.appendFlags("-module-name", moduleOutputInfo.name)
243243
}
244244

245+
// Enable frontend Parseable-output, if needed.
246+
if parsedOptions.contains(.useFrontendParseableOutput) {
247+
commandLine.appendFlag("-frontend-parseable-output")
248+
}
249+
245250
try toolchain.addPlatformSpecificCommonFrontendOptions(commandLine: &commandLine,
246251
inputs: &inputs,
247252
frontendTargetInfo: frontendTargetInfo)

Sources/SwiftOptions/ExtraOptions.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@ extension Option {
1313
public static let driverPrintGraphviz: Option = Option("-driver-print-graphviz", .flag, attributes: [.helpHidden, .doesNotAffectIncrementalBuild], helpText: "Write the job graph as a graphviz file", group: .internalDebug)
1414
public static let driverExplicitModuleBuild: Option = Option("-experimental-explicit-module-build", .flag, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit")
1515
public static let driverScanDependenciesNonLib: Option = Option("-nonlib-dependency-scanner", .flag, attributes: [.helpHidden], helpText: "Use calls to `swift-frontend -scan-dependencies` instead of dedicated dependency scanning library")
16-
public static let driverWarnUnusedOptions: Option = Option("-driver-warn-unused-options", .flag, attributes: [.helpHidden], helpText: "Emit warnings for any provided options which are unused by the driver.")
16+
public static let driverWarnUnusedOptions: Option = Option("-driver-warn-unused-options", .flag, attributes: [.helpHidden], helpText: "Emit warnings for any provided options which are unused by the driver")
1717
public static let emitModuleSeparately: Option = Option("-experimental-emit-module-separately", .flag, attributes: [.helpHidden], helpText: "Emit module files as a distinct job")
18+
public static let useFrontendParseableOutput: Option = Option("-use-frontend-parseable-output", .flag, attributes: [.helpHidden], helpText: "Emit parseable-output from swift-frontend jobs instead of from the driver")
1819

1920
public static var extraOptions: [Option] {
2021
return [
2122
Option.driverPrintGraphviz,
2223
Option.driverExplicitModuleBuild,
2324
Option.driverScanDependenciesNonLib,
2425
Option.driverWarnUnusedOptions,
25-
Option.emitModuleSeparately
26+
Option.emitModuleSeparately,
27+
Option.useFrontendParseableOutput
2628
]
2729
}
2830
}

Tests/SwiftDriverTests/ParsableMessageTests.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212
import XCTest
1313
import Foundation
14+
import TSCBasic
15+
import TSCUtility
1416

1517
@_spi(Testing) import SwiftDriver
1618

@@ -88,4 +90,56 @@ final class ParsableMessageTests: XCTestCase {
8890
}
8991
""")
9092
}
93+
94+
func testFrontendMessages() throws {
95+
do {
96+
try withTemporaryDirectory { path in
97+
let main = path.appending(component: "main.swift")
98+
let output = path.appending(component: "main.o")
99+
try localFileSystem.writeFileContents(main) {
100+
$0 <<< "print(\"hello, world!\")"
101+
}
102+
let diags = DiagnosticsEngine()
103+
var driver = try Driver(args: ["swiftc", main.pathString,
104+
"-use-frontend-parseable-output",
105+
"-o", output.pathString],
106+
env: ProcessEnv.vars,
107+
diagnosticsEngine: diags,
108+
fileSystem: localFileSystem)
109+
let jobs = try driver.planBuild()
110+
XCTAssertEqual(jobs.removingAutolinkExtractJobs().map(\.kind), [.compile, .link])
111+
XCTAssertEqual(jobs[0].outputs.count, 1)
112+
let compileArgs = jobs[0].commandLine
113+
XCTAssertTrue(compileArgs.contains((.flag("-frontend-parseable-output"))))
114+
115+
// Replace the error stream with one we capture here.
116+
let errorStream = stderrStream
117+
let errorOutputFile = path.appending(component: "dummy_error_stream")
118+
TSCBasic.stderrStream = try! ThreadSafeOutputByteStream(LocalFileOutputByteStream(errorOutputFile))
119+
120+
try driver.run(jobs: jobs)
121+
let invocationErrorOutput = try localFileSystem.readFileContents(errorOutputFile).description
122+
XCTAssertTrue(invocationErrorOutput.contains("""
123+
{
124+
"kind": "began",
125+
"name": "compile",
126+
"""))
127+
XCTAssertTrue(invocationErrorOutput.contains("""
128+
{
129+
"kind": "finished",
130+
"name": "compile",
131+
"""))
132+
// Restore the error stream to what it was
133+
TSCBasic.stderrStream = errorStream
134+
}
135+
}
136+
137+
do {
138+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-parseable-output",
139+
"-use-frontend-parseable-output"]) {
140+
$1.expect(.error(Driver.Error.conflictingOptions(.parseableOutput,
141+
.useFrontendParseableOutput)))
142+
}
143+
}
144+
}
91145
}

0 commit comments

Comments
 (0)