Skip to content

Commit 1ac2578

Browse files
authored
Merge pull request #1460 from tshortli/api-extract-supplementary-output
Introduce -emit-api-descriptor-path option
2 parents 04f9f28 + bab5b4c commit 1ac2578

File tree

7 files changed

+94
-117
lines changed

7 files changed

+94
-117
lines changed

Sources/SwiftDriver/Driver/Driver.swift

+12
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ public struct Driver {
347347
/// Path to the module's digester baseline file.
348348
let digesterBaselinePath: VirtualPath.Handle?
349349

350+
/// Path to the emitted API descriptor file.
351+
let apiDescriptorFilePath: VirtualPath.Handle?
352+
350353
/// The mode the API digester should run in.
351354
let digesterMode: DigesterMode
352355

@@ -954,6 +957,15 @@ public struct Driver {
954957
outputFileMap: self.outputFileMap,
955958
moduleName: moduleOutputInfo.name)
956959

960+
self.apiDescriptorFilePath = try Self.computeSupplementaryOutputPath(
961+
&parsedOptions, type: .jsonAPIDescriptor, isOutputOptions: [],
962+
outputPath: .emitApiDescriptorPath,
963+
compilerOutputType: compilerOutputType,
964+
compilerMode: compilerMode,
965+
emitModuleSeparately: emitModuleSeparately,
966+
outputFileMap: self.outputFileMap,
967+
moduleName: moduleOutputInfo.name)
968+
957969
Self.validateDigesterArgs(&parsedOptions,
958970
moduleOutputInfo: moduleOutputInfo,
959971
digesterMode: self.digesterMode,

Sources/SwiftDriver/Jobs/CompileJob.swift

+2-3
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ extension Driver {
9797
.moduleTrace, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .pcm, .pch,
9898
.clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts,
9999
.indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline,
100-
.swiftConstValues, nil:
100+
.swiftConstValues, .jsonAPIDescriptor, nil:
101101
return false
102102
}
103103
}
@@ -364,7 +364,6 @@ extension Driver {
364364
}
365365

366366
let expirementalFeatures = parsedOptions.arguments(for: .enableExperimentalFeature)
367-
let embeddedEnabled = expirementalFeatures.map(\.argument).map(\.asSingle).contains("Embedded")
368367

369368
try commandLine.appendLast(.trackSystemDependencies, from: &parsedOptions)
370369
try commandLine.appendLast(.CrossModuleOptimization, from: &parsedOptions)
@@ -467,7 +466,7 @@ extension FileType {
467466
.bitstreamOptimizationRecord, .swiftInterface, .privateSwiftInterface,
468467
.swiftSourceInfoFile, .clangModuleMap, .jsonSwiftArtifacts,
469468
.indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline,
470-
.swiftConstValues:
469+
.swiftConstValues, .jsonAPIDescriptor:
471470
fatalError("Output type can never be a primary output")
472471
}
473472
}

Sources/SwiftDriver/Jobs/EmitModuleJob.swift

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extension Driver {
3434
addSupplementalOutput(path: swiftPrivateInterfacePath, flag: "-emit-private-module-interface-path", type: .privateSwiftInterface)
3535
addSupplementalOutput(path: objcGeneratedHeaderPath, flag: "-emit-objc-header-path", type: .objcHeader)
3636
addSupplementalOutput(path: tbdPath, flag: "-emit-tbd-path", type: .tbd)
37+
addSupplementalOutput(path: apiDescriptorFilePath, flag: "-emit-api-descriptor-path", type: .jsonAPIDescriptor)
3738

3839
if isMergeModule {
3940
return

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

+6
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,12 @@ extension Driver {
592592
input: nil,
593593
flag: "-emit-abi-descriptor-path")
594594
}
595+
596+
try addOutputOfType(
597+
outputType: .jsonAPIDescriptor,
598+
finalOutputPath: apiDescriptorFilePath,
599+
input: nil,
600+
flag: "-emit-api-descriptor-path")
595601
}
596602
}
597603

Sources/SwiftDriver/Utilities/FileType.swift

+13-3
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ public enum FileType: String, Hashable, CaseIterable, Codable {
151151

152152
/// ABI baseline JSON
153153
case jsonABIBaseline = "abi.json"
154+
155+
/// API descriptor JSON
156+
case jsonAPIDescriptor
154157
}
155158

156159
extension FileType: CustomStringConvertible {
@@ -235,6 +238,9 @@ extension FileType: CustomStringConvertible {
235238

236239
case .swiftConstValues:
237240
return "const-values"
241+
242+
case .jsonAPIDescriptor:
243+
return "api-descriptor-json"
238244
}
239245
}
240246
}
@@ -254,7 +260,7 @@ extension FileType {
254260
.swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile,
255261
.jsonDependencies, .clangModuleMap, .jsonTargetInfo, .jsonCompilerFeatures,
256262
.jsonSwiftArtifacts, .indexUnitOutputPath, .modDepCache, .jsonAPIBaseline,
257-
.jsonABIBaseline, .swiftConstValues:
263+
.jsonABIBaseline, .swiftConstValues, .jsonAPIDescriptor:
258264
return false
259265
}
260266
}
@@ -359,6 +365,8 @@ extension FileType {
359365
return "abi-baseline-json"
360366
case .swiftConstValues:
361367
return "const-values"
368+
case .jsonAPIDescriptor:
369+
return "api-descriptor-json"
362370
}
363371
}
364372
}
@@ -370,7 +378,8 @@ extension FileType {
370378
.raw_sil, .llvmIR,.objcHeader, .autolink, .importedModules, .tbd,
371379
.moduleTrace, .yamlOptimizationRecord, .swiftInterface, .privateSwiftInterface,
372380
.jsonDependencies, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo,
373-
.jsonSwiftArtifacts, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues:
381+
.jsonSwiftArtifacts, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues,
382+
.jsonAPIDescriptor:
374383
return true
375384
case .image, .object, .dSYM, .pch, .sib, .raw_sib, .swiftModule,
376385
.swiftDocumentation, .swiftSourceInfoFile, .llvmBitcode, .diagnostics,
@@ -393,7 +402,8 @@ extension FileType {
393402
.importedModules, .tbd, .moduleTrace, .indexData, .yamlOptimizationRecord,
394403
.modDepCache, .bitstreamOptimizationRecord, .pcm, .pch, .jsonDependencies,
395404
.clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts,
396-
.indexUnitOutputPath, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues:
405+
.indexUnitOutputPath, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues,
406+
.jsonAPIDescriptor:
397407
return false
398408
}
399409
}

Sources/SwiftOptions/Options.swift

+2
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ extension Option {
273273
public static let embedBitcode: Option = Option("-embed-bitcode", .flag, attributes: [.frontend, .noInteractive], helpText: "Embed LLVM IR bitcode as data")
274274
public static let embedTbdForModule: Option = Option("-embed-tbd-for-module", .separate, attributes: [.frontend], helpText: "Embed symbols from the module in the emitted tbd file")
275275
public static let emitAbiDescriptorPath: Option = Option("-emit-abi-descriptor-path", .separate, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: "<path>", helpText: "Output the ABI descriptor of current module to <path>")
276+
public static let emitApiDescriptorPath: Option = Option("-emit-api-descriptor-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "<path>", helpText: "Output the API descriptor of current module to <path>")
276277
public static let emitAssembly: Option = Option("-emit-assembly", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild, .cacheInvariant], helpText: "Emit assembly file(s) (-S)", group: .modes)
277278
public static let emitAst: Option = Option("-emit-ast", .flag, alias: Option.dumpAst, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild])
278279
public static let emitBc: Option = Option("-emit-bc", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild, .cacheInvariant], helpText: "Emit LLVM BC file(s)", group: .modes)
@@ -1070,6 +1071,7 @@ extension Option {
10701071
Option.embedBitcode,
10711072
Option.embedTbdForModule,
10721073
Option.emitAbiDescriptorPath,
1074+
Option.emitApiDescriptorPath,
10731075
Option.emitAssembly,
10741076
Option.emitAst,
10751077
Option.emitBc,

Tests/SwiftDriverTests/SwiftDriverTests.swift

+58-111
Original file line numberDiff line numberDiff line change
@@ -2811,41 +2811,12 @@ final class SwiftDriverTests: XCTestCase {
28112811
])
28122812
let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs()
28132813
XCTAssertEqual(plannedJobs.count, 1)
2814-
let suppleArg = "-supplementary-output-file-map"
2815-
// Make sure we are using supplementary file map
2816-
XCTAssert(plannedJobs[0].commandLine.contains(.flag(suppleArg)))
2817-
let args = plannedJobs[0].commandLine
2818-
var fileMapPath: VirtualPath?
2819-
for pair in args.enumerated() {
2820-
if pair.element == .flag(suppleArg) {
2821-
let filemap = args[pair.offset + 1]
2822-
switch filemap {
2823-
case .path(let p):
2824-
fileMapPath = p
2825-
default:
2826-
break
2827-
}
2828-
}
2829-
}
2830-
XCTAssert(fileMapPath != nil)
2831-
switch fileMapPath! {
2832-
case .fileList(_, let list):
2833-
switch list {
2834-
case .outputFileMap(let map):
2835-
// This is to match the legacy driver behavior
2836-
// Make sure the supplementary output map has an entry for the Swift file
2837-
// under indexing and its indexData entry is the primary output file
2838-
let entry = map.entries[VirtualPath.relative(try RelativePath(validating: "foo5.swift")).intern()]!
2839-
XCTAssert(VirtualPath.lookup(entry[.indexData]!) == .absolute(try .init(validating: "/tmp/t.o")))
2840-
return
2841-
default:
2842-
break
2843-
}
2844-
break
2845-
default:
2846-
break
2847-
}
2848-
XCTAssert(false)
2814+
let map = try XCTUnwrap(plannedJobs[0].commandLine.supplementaryOutputFilemap)
2815+
// This is to match the legacy driver behavior
2816+
// Make sure the supplementary output map has an entry for the Swift file
2817+
// under indexing and its indexData entry is the primary output file
2818+
let entry = map.entries[VirtualPath.relative(try RelativePath(validating: "foo5.swift")).intern()]!
2819+
XCTAssert(VirtualPath.lookup(entry[.indexData]!) == .absolute(try .init(validating: "/tmp/t.o")))
28492820
}
28502821

28512822
func testMultiThreadedWholeModuleOptimizationCompiles() throws {
@@ -2917,14 +2888,7 @@ final class SwiftDriverTests: XCTestCase {
29172888
XCTAssertEqual(plannedJobs.count, 2)
29182889
let compileJob = plannedJobs[0]
29192890
XCTAssertEqual(compileJob.kind, .compile)
2920-
XCTAssert(compileJob.commandLine.contains(.flag("-supplementary-output-file-map")))
2921-
let argIdx = try XCTUnwrap(compileJob.commandLine.firstIndex(where: { $0 == .flag("-supplementary-output-file-map") }))
2922-
let supplOutputs = compileJob.commandLine[argIdx+1]
2923-
guard case let .path(path) = supplOutputs,
2924-
case let .fileList(_, fileList) = path,
2925-
case let .outputFileMap(outFileMap) = fileList else {
2926-
throw StringError("Unexpected argument for output file map")
2927-
}
2891+
let outFileMap = try XCTUnwrap(compileJob.commandLine.supplementaryOutputFilemap)
29282892
let firstKey: String = try VirtualPath.lookup(XCTUnwrap(outFileMap.entries.keys.first)).description
29292893
XCTAssertEqual(firstKey, "foo.swift")
29302894
}
@@ -3073,14 +3037,7 @@ final class SwiftDriverTests: XCTestCase {
30733037
XCTAssertEqual(plannedJobs.count, 2)
30743038
XCTAssertEqual(plannedJobs[0].kind, .compile)
30753039
print(plannedJobs[0].commandLine.joinedUnresolvedArguments)
3076-
XCTAssert(plannedJobs[0].commandLine.contains(.flag("-supplementary-output-file-map")))
3077-
let argIdx = try XCTUnwrap(plannedJobs[0].commandLine.firstIndex(where: { $0 == .flag("-supplementary-output-file-map") }))
3078-
let supplOutputs = plannedJobs[0].commandLine[argIdx+1]
3079-
guard case let .path(path) = supplOutputs,
3080-
case let .fileList(_, fileList) = path,
3081-
case let .outputFileMap(outFileMap) = fileList else {
3082-
throw StringError("Unexpected argument for output file map")
3083-
}
3040+
let outFileMap = try XCTUnwrap(plannedJobs[0].commandLine.supplementaryOutputFilemap)
30843041
XCTAssertEqual(outFileMap.entries.values.first?.keys.first, fileType)
30853042
}
30863043

@@ -6636,48 +6593,21 @@ final class SwiftDriverTests: XCTestCase {
66366593
let plannedJobs = try driver.planBuild()
66376594

66386595
let jobA = plannedJobs[0]
6639-
let flagA = jobA.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
6640-
let fileListArgumentA = jobA.commandLine[jobA.commandLine.index(after: flagA)]
6641-
guard case let .path(.fileList(_, fileListA)) = fileListArgumentA else {
6642-
XCTFail("Argument wasn't a filelist")
6643-
return
6644-
}
6645-
guard case let .outputFileMap(mapA) = fileListA else {
6646-
XCTFail("FileList wasn't OutputFileMap")
6647-
return
6648-
}
6596+
let mapA = try XCTUnwrap(jobA.commandLine.supplementaryOutputFilemap)
66496597
let filesA = try XCTUnwrap(mapA.entries[VirtualPath.relative(try RelativePath(validating: "a.swift")).intern()])
66506598
XCTAssertTrue(filesA.keys.contains(.swiftModule))
66516599
XCTAssertTrue(filesA.keys.contains(.swiftDocumentation))
66526600
XCTAssertTrue(filesA.keys.contains(.swiftSourceInfoFile))
66536601

66546602
let jobB = plannedJobs[1]
6655-
let flagB = jobB.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
6656-
let fileListArgumentB = jobB.commandLine[jobB.commandLine.index(after: flagB)]
6657-
guard case let .path(.fileList(_, fileListB)) = fileListArgumentB else {
6658-
XCTFail("Argument wasn't a filelist")
6659-
return
6660-
}
6661-
guard case let .outputFileMap(mapB) = fileListB else {
6662-
XCTFail("FileList wasn't OutputFileMap")
6663-
return
6664-
}
6603+
let mapB = try XCTUnwrap(jobB.commandLine.supplementaryOutputFilemap)
66656604
let filesB = try XCTUnwrap(mapB.entries[VirtualPath.relative(try RelativePath(validating: "b.swift")).intern()])
66666605
XCTAssertTrue(filesB.keys.contains(.swiftModule))
66676606
XCTAssertTrue(filesB.keys.contains(.swiftDocumentation))
66686607
XCTAssertTrue(filesB.keys.contains(.swiftSourceInfoFile))
66696608

66706609
let jobC = plannedJobs[2]
6671-
let flagC = jobC.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
6672-
let fileListArgumentC = jobC.commandLine[jobC.commandLine.index(after: flagC)]
6673-
guard case let .path(.fileList(_, fileListC)) = fileListArgumentC else {
6674-
XCTFail("Argument wasn't a filelist")
6675-
return
6676-
}
6677-
guard case let .outputFileMap(mapC) = fileListC else {
6678-
XCTFail("FileList wasn't OutputFileMap")
6679-
return
6680-
}
6610+
let mapC = try XCTUnwrap(jobC.commandLine.supplementaryOutputFilemap)
66816611
let filesC = try XCTUnwrap(mapC.entries[VirtualPath.relative(try RelativePath(validating: "c.swift")).intern()])
66826612
XCTAssertTrue(filesC.keys.contains(.swiftModule))
66836613
XCTAssertTrue(filesC.keys.contains(.swiftDocumentation))
@@ -6800,29 +6730,11 @@ final class SwiftDriverTests: XCTestCase {
68006730
let plannedJobs = try driver.planBuild()
68016731

68026732
let jobA = plannedJobs[0]
6803-
let flagA = jobA.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
6804-
let fileListArgumentA = jobA.commandLine[jobA.commandLine.index(after: flagA)]
6805-
guard case let .path(.fileList(_, fileListA)) = fileListArgumentA else {
6806-
XCTFail("Argument wasn't a filelist")
6807-
return
6808-
}
6809-
guard case let .outputFileMap(mapA) = fileListA else {
6810-
XCTFail("FileList wasn't OutputFileMap")
6811-
return
6812-
}
6733+
let mapA = try XCTUnwrap(jobA.commandLine.supplementaryOutputFilemap)
68136734
XCTAssertEqual(mapA.entries, [VirtualPath.relative(try .init(validating: "a.swift")).intern(): [:]])
68146735

68156736
let jobB = plannedJobs[1]
6816-
let flagB = jobB.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
6817-
let fileListArgumentB = jobB.commandLine[jobB.commandLine.index(after: flagB)]
6818-
guard case let .path(.fileList(_, fileListB)) = fileListArgumentB else {
6819-
XCTFail("Argument wasn't a filelist")
6820-
return
6821-
}
6822-
guard case let .outputFileMap(mapB) = fileListB else {
6823-
XCTFail("FileList wasn't OutputFileMap")
6824-
return
6825-
}
6737+
let mapB = try XCTUnwrap(jobB.commandLine.supplementaryOutputFilemap)
68266738
XCTAssertEqual(mapB.entries, [VirtualPath.relative(try .init(validating: "b.swift")).intern(): [:]])
68276739
}
68286740

@@ -6831,16 +6743,7 @@ final class SwiftDriverTests: XCTestCase {
68316743
let plannedJobs = try driver.planBuild()
68326744

68336745
let jobA = plannedJobs[0]
6834-
let flagA = jobA.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
6835-
let fileListArgumentA = jobA.commandLine[jobA.commandLine.index(after: flagA)]
6836-
guard case let .path(.fileList(_, fileListA)) = fileListArgumentA else {
6837-
XCTFail("Argument wasn't a filelist")
6838-
return
6839-
}
6840-
guard case let .outputFileMap(mapA) = fileListA else {
6841-
XCTFail("FileList wasn't OutputFileMap")
6842-
return
6843-
}
6746+
let mapA = try XCTUnwrap(jobA.commandLine.supplementaryOutputFilemap)
68446747
XCTAssertEqual(mapA.entries, [VirtualPath.relative(try .init(validating: "a.swift")).intern(): [:]])
68456748
}
68466749
}
@@ -7325,6 +7228,35 @@ final class SwiftDriverTests: XCTestCase {
73257228
let emitModuleJob = try XCTUnwrap(jobs.first(where: {$0.kind == .emitModule}))
73267229
XCTAssertTrue(emitModuleJob.commandLine.contains(.flag("-experimental-lazy-typecheck")))
73277230
}
7231+
7232+
func testEmitAPIDescriptorEmitModule() throws {
7233+
try withTemporaryDirectory { path in
7234+
let apiDescriptorPath = path.appending(component: "api.json").nativePathString(escaped: true)
7235+
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "baz.swift",
7236+
"-emit-module", "-module-name", "Test",
7237+
"-emit-api-descriptor-path", apiDescriptorPath])
7238+
7239+
let jobs = try driver.planBuild().removingAutolinkExtractJobs()
7240+
let emitModuleJob = try jobs.findJob(.emitModule)
7241+
XCTAssert(emitModuleJob.commandLine.contains(.flag("-emit-api-descriptor-path")))
7242+
}
7243+
}
7244+
7245+
func testEmitAPIDescriptorWholeModuleOptimization() throws {
7246+
try withTemporaryDirectory { path in
7247+
let apiDescriptorPath = path.appending(component: "api.json").nativePathString(escaped: true)
7248+
var driver = try Driver(args: ["swiftc", "-whole-module-optimization",
7249+
"-driver-filelist-threshold=0",
7250+
"foo.swift", "bar.swift", "baz.swift",
7251+
"-module-name", "Test", "-emit-module",
7252+
"-emit-api-descriptor-path", apiDescriptorPath])
7253+
7254+
let jobs = try driver.planBuild().removingAutolinkExtractJobs()
7255+
let compileJob = try jobs.findJob(.compile)
7256+
let supplementaryOutputs = try XCTUnwrap(compileJob.commandLine.supplementaryOutputFilemap)
7257+
XCTAssertNotNil(supplementaryOutputs.entries.values.first?[.jsonAPIDescriptor])
7258+
}
7259+
}
73287260
}
73297261

73307262
func assertString(
@@ -7394,4 +7326,19 @@ private extension Array where Element == Job.ArgTemplate {
73947326
}
73957327
}
73967328
}
7329+
7330+
var supplementaryOutputFilemap: OutputFileMap? {
7331+
get throws {
7332+
guard let argIdx = firstIndex(where: { $0 == .flag("-supplementary-output-file-map") }) else {
7333+
return nil
7334+
}
7335+
let supplementaryOutputs = self[argIdx + 1]
7336+
guard case let .path(path) = supplementaryOutputs,
7337+
case let .fileList(_, fileList) = path,
7338+
case let .outputFileMap(outputFileMap) = fileList else {
7339+
throw StringError("Unexpected argument for output file map")
7340+
}
7341+
return outputFileMap
7342+
}
7343+
}
73977344
}

0 commit comments

Comments
 (0)