Skip to content

Commit 4b35f2e

Browse files
authored
Merge pull request swiftlang#48 from DougGregor/emit-pcm
Add support for Clang module emission via -emit-pcm
2 parents fd20017 + b179e0c commit 4b35f2e

File tree

9 files changed

+149
-28
lines changed

9 files changed

+149
-28
lines changed

Sources/SwiftDriver/Driver/CompilerMode.swift

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public enum CompilerMode: Equatable {
2626

2727
/// Compile and execute the inputs immediately.
2828
case immediate
29+
30+
/// Compile a Clang module (.pcm).
31+
case compilePCM
2932
}
3033

3134
/// Information about batch mode, which is used to determine how to form
@@ -40,7 +43,7 @@ extension CompilerMode {
4043
/// Whether this compilation mode uses -primary-file to specify its inputs.
4144
public var usesPrimaryFileInputs: Bool {
4245
switch self {
43-
case .immediate, .repl, .singleCompile:
46+
case .immediate, .repl, .singleCompile, .compilePCM:
4447
return false
4548

4649
case .standardCompile, .batchCompile:
@@ -54,26 +57,27 @@ extension CompilerMode {
5457
case .immediate, .repl, .standardCompile, .batchCompile:
5558
return false
5659

57-
case .singleCompile:
60+
case .singleCompile, .compilePCM:
5861
return true
5962
}
6063
}
6164
}
6265

6366
extension CompilerMode: CustomStringConvertible {
6467
public var description: String {
65-
switch self {
66-
67-
case .standardCompile:
68-
return "standard compilation"
69-
case .batchCompile:
70-
return "batch compilation"
71-
case .singleCompile:
72-
return "whole module optimization"
73-
case .repl:
74-
return "read-eval-print-loop compilation"
75-
case .immediate:
76-
return "immediate compilation"
77-
}
68+
switch self {
69+
case .standardCompile:
70+
return "standard compilation"
71+
case .batchCompile:
72+
return "batch compilation"
73+
case .singleCompile:
74+
return "whole module optimization"
75+
case .repl:
76+
return "read-eval-print-loop compilation"
77+
case .immediate:
78+
return "immediate compilation"
79+
case .compilePCM:
80+
return "compile Clang module (.pcm)"
81+
}
7882
}
7983
}

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,9 @@ extension Driver {
701701
case .repl, .deprecatedIntegratedRepl, .lldbRepl:
702702
return .repl
703703

704+
case .emitPcm:
705+
return .compilePCM
706+
704707
default:
705708
// Output flag doesn't determine the compiler mode.
706709
break
@@ -860,6 +863,9 @@ extension Driver {
860863
case .emitPch:
861864
compilerOutputType = .pch
862865

866+
case .emitPcm:
867+
compilerOutputType = .pcm
868+
863869
case .emitImportedModules:
864870
compilerOutputType = .importedModules
865871

Sources/SwiftDriver/Incremental Compilation/IncrementalCompilation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ fileprivate extension CompilerMode {
154154
var supportsIncrementalCompilation: Bool {
155155
switch self {
156156
case .standardCompile, .immediate, .repl, .batchCompile: return true
157-
case .singleCompile: return false
157+
case .singleCompile, .compilePCM: return false
158158
}
159159
}
160160
}

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,29 @@ fileprivate func shouldColorDiagnostics() -> Bool {
2121
}
2222

2323
extension Driver {
24+
/// How the bridging header should be handled.
25+
enum BridgingHeaderHandling {
26+
/// Ignore the bridging header entirely.
27+
case ignored
28+
29+
/// Parse the bridging header, even if other jobs will use a precompiled
30+
/// bridging header.
31+
///
32+
/// This is typically used only when precompiling the bridging header.
33+
case parsed
34+
35+
/// Use the precompiled bridging header.
36+
case precompiled
37+
}
2438
/// Add frontend options that are common to different frontend invocations.
25-
mutating func addCommonFrontendOptions(commandLine: inout [Job.ArgTemplate],
26-
requestPrecompiledObjCHeader: Bool = true) throws {
39+
mutating func addCommonFrontendOptions(
40+
commandLine: inout [Job.ArgTemplate],
41+
bridgingHeaderHandling: BridgingHeaderHandling = .precompiled
42+
) throws {
2743
// Only pass -target to the REPL or immediate modes if it was explicitly
2844
// specified on the command line.
2945
switch compilerMode {
30-
case .standardCompile, .singleCompile, .batchCompile:
46+
case .standardCompile, .singleCompile, .batchCompile, .compilePCM:
3147
commandLine.appendFlag(.target)
3248
commandLine.appendFlag(targetTriple.triple)
3349

@@ -156,9 +172,11 @@ extension Driver {
156172
try commandLine.appendAll(.Xllvm, from: &parsedOptions)
157173
try commandLine.appendAll(.Xcc, from: &parsedOptions)
158174

159-
if let importedObjCHeader = importedObjCHeader {
175+
if let importedObjCHeader = importedObjCHeader,
176+
bridgingHeaderHandling != .ignored {
160177
commandLine.appendFlag(.importObjcHeader)
161-
if requestPrecompiledObjCHeader, let pch = bridgingPrecompiledHeader {
178+
if bridgingHeaderHandling == .precompiled,
179+
let pch = bridgingPrecompiledHeader {
162180
if parsedOptions.contains(.pchOutputDir) {
163181
commandLine.appendPath(importedObjCHeader)
164182
try commandLine.appendLast(.pchOutputDir, from: &parsedOptions)

Sources/SwiftDriver/Jobs/GeneratePCHJob.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ extension Driver {
2222

2323
commandLine.appendFlag("-frontend")
2424

25-
try addCommonFrontendOptions(commandLine: &commandLine, requestPrecompiledObjCHeader: false)
25+
try addCommonFrontendOptions(
26+
commandLine: &commandLine, bridgingHeaderHandling: .parsed)
27+
28+
try commandLine.appendLast(.indexStorePath, from: &parsedOptions)
2629

2730
// TODO: Should this just be pch output with extension changed?
2831
if parsedOptions.hasArgument(.serializeDiagnostics), let outputDirectory = parsedOptions.getLastArgument(.pchOutputDir)?.asSingle {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===--------------- GeneratePCMJob.swift - Generate PCM Job ----===//
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+
/// Create a job that generates a Clang module (.pcm) that is suitable for
15+
/// use.
16+
///
17+
/// The input is a Clang module map
18+
/// (https://clang.llvm.org/docs/Modules.html#module-map-language) and the
19+
/// output is a compiled module that also includes the additional information
20+
/// needed by Swift's Clang importer, e.g., the Swift name lookup tables.
21+
mutating func generatePCMJob(input: TypedVirtualPath) throws -> Job {
22+
var inputs = [TypedVirtualPath]()
23+
var outputs = [TypedVirtualPath]()
24+
25+
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
26+
27+
commandLine.appendFlag("-frontend")
28+
commandLine.appendFlag(.emitPcm)
29+
30+
// Input module map.
31+
inputs.append(input)
32+
commandLine.appendPath(input.file)
33+
34+
// Compute the output file.
35+
let output: TypedVirtualPath
36+
if let outputArg = parsedOptions.getLastArgument(.o) {
37+
output = .init(file: try VirtualPath(path: outputArg.asSingle),
38+
type: .pcm)
39+
} else {
40+
output = .init(
41+
file: try VirtualPath(
42+
path: moduleName.appendingFileTypeExtension(.pcm)),
43+
type: .pcm)
44+
}
45+
46+
outputs.append(output)
47+
commandLine.appendFlag(.o)
48+
commandLine.appendPath(output.file)
49+
50+
try addCommonFrontendOptions(
51+
commandLine: &commandLine, bridgingHeaderHandling: .ignored)
52+
53+
try commandLine.appendLast(.indexStorePath, from: &parsedOptions)
54+
55+
return Job(
56+
kind: .generatePCM,
57+
tool: .absolute(try toolchain.getToolPath(.swiftCompiler)),
58+
commandLine: commandLine,
59+
displayInputs: [],
60+
inputs: inputs,
61+
outputs: outputs
62+
)
63+
}
64+
}

Sources/SwiftDriver/Jobs/Job.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public struct Job: Codable, Equatable {
2121
case autolinkExtract = "autolink-extract"
2222
case emitModule = "emit-module"
2323
case generatePCH = "generate-pch"
24+
25+
/// Generate a compiled Clang module.
26+
case generatePCM = "generate-pcm"
2427
case interpret
2528
case repl
2629
case verifyDebugInfo = "verify-debug-info"

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212

1313
public enum PlanningError: Error, DiagnosticData {
1414
case replReceivedInput
15+
case emitPCMWrongInputFiles
1516

1617
public var description: String {
1718
switch self {
1819
case .replReceivedInput:
1920
return "REPL mode requires no input files"
21+
22+
case .emitPCMWrongInputFiles:
23+
return "Clang module emission requires exactly one input file (the module map)"
2024
}
2125
}
2226
}
@@ -45,7 +49,8 @@ extension Driver {
4549
}
4650
}
4751
}
48-
52+
53+
// Precompile the bridging header if needed.
4954
if let importedObjCHeader = importedObjCHeader,
5055
let bridgingPrecompiledHeader = bridgingPrecompiledHeader {
5156
jobs.append(try generatePCHJob(input: .init(file: importedObjCHeader, type: .objcHeader),
@@ -62,8 +67,8 @@ extension Driver {
6267
case .batchCompile(let batchInfo):
6368
partitions = batchPartitions(batchInfo)
6469

65-
case .immediate, .repl:
66-
fatalError("immediate and REPL modes are currently unsupported")
70+
case .immediate, .repl, .compilePCM:
71+
fatalError("compiler mode \(compilerMode) is handled elsewhere")
6772

6873
case .singleCompile:
6974
// Create a single compile job for all of the files, none of which
@@ -157,8 +162,6 @@ extension Driver {
157162
}
158163
}
159164

160-
// FIXME: Lots of follow-up actions for merging modules, etc.
161-
162165
return jobs
163166
}
164167

@@ -177,6 +180,12 @@ extension Driver {
177180

178181
case .standardCompile, .batchCompile, .singleCompile:
179182
return try planStandardCompile()
183+
184+
case .compilePCM:
185+
if inputFiles.count != 1 {
186+
throw PlanningError.emitPCMWrongInputFiles
187+
}
188+
return [try generatePCMJob(input: inputFiles.first!)]
180189
}
181190
}
182191
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ final class SwiftDriverTests: XCTestCase {
12021202
let driver2 = try Driver(args: ["swift", "main.swift"], env: env)
12031203
XCTAssertNoThrow(try driver2.toolchain.getToolPath(.dsymutil))
12041204
}
1205-
1205+
12061206
func testPCHGeneration() throws {
12071207
do {
12081208
var driver = try Driver(args: ["swiftc", "-typecheck", "-import-objc-header", "TestInputHeader.h", "foo.swift"])
@@ -1438,6 +1438,20 @@ final class SwiftDriverTests: XCTestCase {
14381438
XCTAssertEqual(plannedJobs[1].inputs[0].file, try VirtualPath(path: "foo.swift"))
14391439
}
14401440
}
1441+
1442+
func testPCMGeneration() throws {
1443+
do {
1444+
var driver = try Driver(args: ["swiftc", "-emit-pcm", "module.modulemap", "-module-name", "Test"])
1445+
let plannedJobs = try driver.planBuild()
1446+
XCTAssertEqual(plannedJobs.count, 1)
1447+
1448+
XCTAssertEqual(plannedJobs[0].kind, .generatePCM)
1449+
XCTAssertEqual(plannedJobs[0].inputs.count, 1)
1450+
XCTAssertEqual(plannedJobs[0].inputs[0].file, .relative(RelativePath("module.modulemap")))
1451+
XCTAssertEqual(plannedJobs[0].outputs.count, 1)
1452+
XCTAssertEqual(plannedJobs[0].outputs[0].file, .relative(RelativePath("Test.pcm")))
1453+
}
1454+
}
14411455
}
14421456

14431457
func assertString(

0 commit comments

Comments
 (0)