Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce -emit-api-descriptor-path option #1460

Merged
merged 2 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ public struct Driver {
/// Path to the module's digester baseline file.
let digesterBaselinePath: VirtualPath.Handle?

/// Path to the emitted API descriptor file.
let apiDescriptorFilePath: VirtualPath.Handle?

/// The mode the API digester should run in.
let digesterMode: DigesterMode

Expand Down Expand Up @@ -954,6 +957,15 @@ public struct Driver {
outputFileMap: self.outputFileMap,
moduleName: moduleOutputInfo.name)

self.apiDescriptorFilePath = try Self.computeSupplementaryOutputPath(
&parsedOptions, type: .jsonAPIDescriptor, isOutputOptions: [],
outputPath: .emitApiDescriptorPath,
compilerOutputType: compilerOutputType,
compilerMode: compilerMode,
emitModuleSeparately: emitModuleSeparately,
outputFileMap: self.outputFileMap,
moduleName: moduleOutputInfo.name)

Self.validateDigesterArgs(&parsedOptions,
moduleOutputInfo: moduleOutputInfo,
digesterMode: self.digesterMode,
Expand Down
5 changes: 2 additions & 3 deletions Sources/SwiftDriver/Jobs/CompileJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ extension Driver {
.moduleTrace, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .pcm, .pch,
.clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts,
.indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline,
.swiftConstValues, nil:
.swiftConstValues, .jsonAPIDescriptor, nil:
return false
}
}
Expand Down Expand Up @@ -364,7 +364,6 @@ extension Driver {
}

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

try commandLine.appendLast(.trackSystemDependencies, from: &parsedOptions)
try commandLine.appendLast(.CrossModuleOptimization, from: &parsedOptions)
Expand Down Expand Up @@ -467,7 +466,7 @@ extension FileType {
.bitstreamOptimizationRecord, .swiftInterface, .privateSwiftInterface,
.swiftSourceInfoFile, .clangModuleMap, .jsonSwiftArtifacts,
.indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline,
.swiftConstValues:
.swiftConstValues, .jsonAPIDescriptor:
fatalError("Output type can never be a primary output")
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/SwiftDriver/Jobs/EmitModuleJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ extension Driver {
addSupplementalOutput(path: swiftPrivateInterfacePath, flag: "-emit-private-module-interface-path", type: .privateSwiftInterface)
addSupplementalOutput(path: objcGeneratedHeaderPath, flag: "-emit-objc-header-path", type: .objcHeader)
addSupplementalOutput(path: tbdPath, flag: "-emit-tbd-path", type: .tbd)
addSupplementalOutput(path: apiDescriptorFilePath, flag: "-emit-api-descriptor-path", type: .jsonAPIDescriptor)

if isMergeModule {
return
Expand Down
6 changes: 6 additions & 0 deletions Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,12 @@ extension Driver {
input: nil,
flag: "-emit-abi-descriptor-path")
}

try addOutputOfType(
outputType: .jsonAPIDescriptor,
finalOutputPath: apiDescriptorFilePath,
input: nil,
flag: "-emit-api-descriptor-path")
}
}

Expand Down
16 changes: 13 additions & 3 deletions Sources/SwiftDriver/Utilities/FileType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ public enum FileType: String, Hashable, CaseIterable, Codable {

/// ABI baseline JSON
case jsonABIBaseline = "abi.json"

/// API descriptor JSON
case jsonAPIDescriptor
}

extension FileType: CustomStringConvertible {
Expand Down Expand Up @@ -235,6 +238,9 @@ extension FileType: CustomStringConvertible {

case .swiftConstValues:
return "const-values"

case .jsonAPIDescriptor:
return "api-descriptor-json"
}
}
}
Expand All @@ -254,7 +260,7 @@ extension FileType {
.swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile,
.jsonDependencies, .clangModuleMap, .jsonTargetInfo, .jsonCompilerFeatures,
.jsonSwiftArtifacts, .indexUnitOutputPath, .modDepCache, .jsonAPIBaseline,
.jsonABIBaseline, .swiftConstValues:
.jsonABIBaseline, .swiftConstValues, .jsonAPIDescriptor:
return false
}
}
Expand Down Expand Up @@ -359,6 +365,8 @@ extension FileType {
return "abi-baseline-json"
case .swiftConstValues:
return "const-values"
case .jsonAPIDescriptor:
return "api-descriptor-json"
}
}
}
Expand All @@ -370,7 +378,8 @@ extension FileType {
.raw_sil, .llvmIR,.objcHeader, .autolink, .importedModules, .tbd,
.moduleTrace, .yamlOptimizationRecord, .swiftInterface, .privateSwiftInterface,
.jsonDependencies, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo,
.jsonSwiftArtifacts, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues:
.jsonSwiftArtifacts, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues,
.jsonAPIDescriptor:
return true
case .image, .object, .dSYM, .pch, .sib, .raw_sib, .swiftModule,
.swiftDocumentation, .swiftSourceInfoFile, .llvmBitcode, .diagnostics,
Expand All @@ -393,7 +402,8 @@ extension FileType {
.importedModules, .tbd, .moduleTrace, .indexData, .yamlOptimizationRecord,
.modDepCache, .bitstreamOptimizationRecord, .pcm, .pch, .jsonDependencies,
.clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts,
.indexUnitOutputPath, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues:
.indexUnitOutputPath, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues,
.jsonAPIDescriptor:
return false
}
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftOptions/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ extension Option {
public static let embedBitcode: Option = Option("-embed-bitcode", .flag, attributes: [.frontend, .noInteractive], helpText: "Embed LLVM IR bitcode as data")
public static let embedTbdForModule: Option = Option("-embed-tbd-for-module", .separate, attributes: [.frontend], helpText: "Embed symbols from the module in the emitted tbd file")
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>")
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>")
public static let emitAssembly: Option = Option("-emit-assembly", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild, .cacheInvariant], helpText: "Emit assembly file(s) (-S)", group: .modes)
public static let emitAst: Option = Option("-emit-ast", .flag, alias: Option.dumpAst, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild])
public static let emitBc: Option = Option("-emit-bc", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild, .cacheInvariant], helpText: "Emit LLVM BC file(s)", group: .modes)
Expand Down Expand Up @@ -1070,6 +1071,7 @@ extension Option {
Option.embedBitcode,
Option.embedTbdForModule,
Option.emitAbiDescriptorPath,
Option.emitApiDescriptorPath,
Option.emitAssembly,
Option.emitAst,
Option.emitBc,
Expand Down
169 changes: 58 additions & 111 deletions Tests/SwiftDriverTests/SwiftDriverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2811,41 +2811,12 @@ final class SwiftDriverTests: XCTestCase {
])
let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs()
XCTAssertEqual(plannedJobs.count, 1)
let suppleArg = "-supplementary-output-file-map"
// Make sure we are using supplementary file map
XCTAssert(plannedJobs[0].commandLine.contains(.flag(suppleArg)))
let args = plannedJobs[0].commandLine
var fileMapPath: VirtualPath?
for pair in args.enumerated() {
if pair.element == .flag(suppleArg) {
let filemap = args[pair.offset + 1]
switch filemap {
case .path(let p):
fileMapPath = p
default:
break
}
}
}
XCTAssert(fileMapPath != nil)
switch fileMapPath! {
case .fileList(_, let list):
switch list {
case .outputFileMap(let map):
// This is to match the legacy driver behavior
// Make sure the supplementary output map has an entry for the Swift file
// under indexing and its indexData entry is the primary output file
let entry = map.entries[VirtualPath.relative(try RelativePath(validating: "foo5.swift")).intern()]!
XCTAssert(VirtualPath.lookup(entry[.indexData]!) == .absolute(try .init(validating: "/tmp/t.o")))
return
default:
break
}
break
default:
break
}
XCTAssert(false)
let map = try XCTUnwrap(plannedJobs[0].commandLine.supplementaryOutputFilemap)
// This is to match the legacy driver behavior
// Make sure the supplementary output map has an entry for the Swift file
// under indexing and its indexData entry is the primary output file
let entry = map.entries[VirtualPath.relative(try RelativePath(validating: "foo5.swift")).intern()]!
XCTAssert(VirtualPath.lookup(entry[.indexData]!) == .absolute(try .init(validating: "/tmp/t.o")))
}

func testMultiThreadedWholeModuleOptimizationCompiles() throws {
Expand Down Expand Up @@ -2917,14 +2888,7 @@ final class SwiftDriverTests: XCTestCase {
XCTAssertEqual(plannedJobs.count, 2)
let compileJob = plannedJobs[0]
XCTAssertEqual(compileJob.kind, .compile)
XCTAssert(compileJob.commandLine.contains(.flag("-supplementary-output-file-map")))
let argIdx = try XCTUnwrap(compileJob.commandLine.firstIndex(where: { $0 == .flag("-supplementary-output-file-map") }))
let supplOutputs = compileJob.commandLine[argIdx+1]
guard case let .path(path) = supplOutputs,
case let .fileList(_, fileList) = path,
case let .outputFileMap(outFileMap) = fileList else {
throw StringError("Unexpected argument for output file map")
}
let outFileMap = try XCTUnwrap(compileJob.commandLine.supplementaryOutputFilemap)
let firstKey: String = try VirtualPath.lookup(XCTUnwrap(outFileMap.entries.keys.first)).description
XCTAssertEqual(firstKey, "foo.swift")
}
Expand Down Expand Up @@ -3073,14 +3037,7 @@ final class SwiftDriverTests: XCTestCase {
XCTAssertEqual(plannedJobs.count, 2)
XCTAssertEqual(plannedJobs[0].kind, .compile)
print(plannedJobs[0].commandLine.joinedUnresolvedArguments)
XCTAssert(plannedJobs[0].commandLine.contains(.flag("-supplementary-output-file-map")))
let argIdx = try XCTUnwrap(plannedJobs[0].commandLine.firstIndex(where: { $0 == .flag("-supplementary-output-file-map") }))
let supplOutputs = plannedJobs[0].commandLine[argIdx+1]
guard case let .path(path) = supplOutputs,
case let .fileList(_, fileList) = path,
case let .outputFileMap(outFileMap) = fileList else {
throw StringError("Unexpected argument for output file map")
}
let outFileMap = try XCTUnwrap(plannedJobs[0].commandLine.supplementaryOutputFilemap)
XCTAssertEqual(outFileMap.entries.values.first?.keys.first, fileType)
}

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

let jobA = plannedJobs[0]
let flagA = jobA.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
let fileListArgumentA = jobA.commandLine[jobA.commandLine.index(after: flagA)]
guard case let .path(.fileList(_, fileListA)) = fileListArgumentA else {
XCTFail("Argument wasn't a filelist")
return
}
guard case let .outputFileMap(mapA) = fileListA else {
XCTFail("FileList wasn't OutputFileMap")
return
}
let mapA = try XCTUnwrap(jobA.commandLine.supplementaryOutputFilemap)
let filesA = try XCTUnwrap(mapA.entries[VirtualPath.relative(try RelativePath(validating: "a.swift")).intern()])
XCTAssertTrue(filesA.keys.contains(.swiftModule))
XCTAssertTrue(filesA.keys.contains(.swiftDocumentation))
XCTAssertTrue(filesA.keys.contains(.swiftSourceInfoFile))

let jobB = plannedJobs[1]
let flagB = jobB.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
let fileListArgumentB = jobB.commandLine[jobB.commandLine.index(after: flagB)]
guard case let .path(.fileList(_, fileListB)) = fileListArgumentB else {
XCTFail("Argument wasn't a filelist")
return
}
guard case let .outputFileMap(mapB) = fileListB else {
XCTFail("FileList wasn't OutputFileMap")
return
}
let mapB = try XCTUnwrap(jobB.commandLine.supplementaryOutputFilemap)
let filesB = try XCTUnwrap(mapB.entries[VirtualPath.relative(try RelativePath(validating: "b.swift")).intern()])
XCTAssertTrue(filesB.keys.contains(.swiftModule))
XCTAssertTrue(filesB.keys.contains(.swiftDocumentation))
XCTAssertTrue(filesB.keys.contains(.swiftSourceInfoFile))

let jobC = plannedJobs[2]
let flagC = jobC.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
let fileListArgumentC = jobC.commandLine[jobC.commandLine.index(after: flagC)]
guard case let .path(.fileList(_, fileListC)) = fileListArgumentC else {
XCTFail("Argument wasn't a filelist")
return
}
guard case let .outputFileMap(mapC) = fileListC else {
XCTFail("FileList wasn't OutputFileMap")
return
}
let mapC = try XCTUnwrap(jobC.commandLine.supplementaryOutputFilemap)
let filesC = try XCTUnwrap(mapC.entries[VirtualPath.relative(try RelativePath(validating: "c.swift")).intern()])
XCTAssertTrue(filesC.keys.contains(.swiftModule))
XCTAssertTrue(filesC.keys.contains(.swiftDocumentation))
Expand Down Expand Up @@ -6800,29 +6730,11 @@ final class SwiftDriverTests: XCTestCase {
let plannedJobs = try driver.planBuild()

let jobA = plannedJobs[0]
let flagA = jobA.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
let fileListArgumentA = jobA.commandLine[jobA.commandLine.index(after: flagA)]
guard case let .path(.fileList(_, fileListA)) = fileListArgumentA else {
XCTFail("Argument wasn't a filelist")
return
}
guard case let .outputFileMap(mapA) = fileListA else {
XCTFail("FileList wasn't OutputFileMap")
return
}
let mapA = try XCTUnwrap(jobA.commandLine.supplementaryOutputFilemap)
XCTAssertEqual(mapA.entries, [VirtualPath.relative(try .init(validating: "a.swift")).intern(): [:]])

let jobB = plannedJobs[1]
let flagB = jobB.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
let fileListArgumentB = jobB.commandLine[jobB.commandLine.index(after: flagB)]
guard case let .path(.fileList(_, fileListB)) = fileListArgumentB else {
XCTFail("Argument wasn't a filelist")
return
}
guard case let .outputFileMap(mapB) = fileListB else {
XCTFail("FileList wasn't OutputFileMap")
return
}
let mapB = try XCTUnwrap(jobB.commandLine.supplementaryOutputFilemap)
XCTAssertEqual(mapB.entries, [VirtualPath.relative(try .init(validating: "b.swift")).intern(): [:]])
}

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

let jobA = plannedJobs[0]
let flagA = jobA.commandLine.firstIndex(of: .flag("-supplementary-output-file-map"))!
let fileListArgumentA = jobA.commandLine[jobA.commandLine.index(after: flagA)]
guard case let .path(.fileList(_, fileListA)) = fileListArgumentA else {
XCTFail("Argument wasn't a filelist")
return
}
guard case let .outputFileMap(mapA) = fileListA else {
XCTFail("FileList wasn't OutputFileMap")
return
}
let mapA = try XCTUnwrap(jobA.commandLine.supplementaryOutputFilemap)
XCTAssertEqual(mapA.entries, [VirtualPath.relative(try .init(validating: "a.swift")).intern(): [:]])
}
}
Expand Down Expand Up @@ -7325,6 +7228,35 @@ final class SwiftDriverTests: XCTestCase {
let emitModuleJob = try XCTUnwrap(jobs.first(where: {$0.kind == .emitModule}))
XCTAssertTrue(emitModuleJob.commandLine.contains(.flag("-experimental-lazy-typecheck")))
}

func testEmitAPIDescriptorEmitModule() throws {
try withTemporaryDirectory { path in
let apiDescriptorPath = path.appending(component: "api.json").nativePathString(escaped: true)
var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "baz.swift",
"-emit-module", "-module-name", "Test",
"-emit-api-descriptor-path", apiDescriptorPath])

let jobs = try driver.planBuild().removingAutolinkExtractJobs()
let emitModuleJob = try jobs.findJob(.emitModule)
XCTAssert(emitModuleJob.commandLine.contains(.flag("-emit-api-descriptor-path")))
}
}

func testEmitAPIDescriptorWholeModuleOptimization() throws {
try withTemporaryDirectory { path in
let apiDescriptorPath = path.appending(component: "api.json").nativePathString(escaped: true)
var driver = try Driver(args: ["swiftc", "-whole-module-optimization",
"-driver-filelist-threshold=0",
"foo.swift", "bar.swift", "baz.swift",
"-module-name", "Test", "-emit-module",
"-emit-api-descriptor-path", apiDescriptorPath])

let jobs = try driver.planBuild().removingAutolinkExtractJobs()
let compileJob = try jobs.findJob(.compile)
let supplementaryOutputs = try XCTUnwrap(compileJob.commandLine.supplementaryOutputFilemap)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this always use file map file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently so with enough input files. This is the logic that determines whether or not an output file map will be used:

if inputsGeneratingCodeCount * FileType.allCases.count > fileListThreshold {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, my question is if you just pass the option to set threshold to 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah I could do that.

XCTAssertNotNil(supplementaryOutputs.entries.values.first?[.jsonAPIDescriptor])
}
}
}

func assertString(
Expand Down Expand Up @@ -7394,4 +7326,19 @@ private extension Array where Element == Job.ArgTemplate {
}
}
}

var supplementaryOutputFilemap: OutputFileMap? {
get throws {
guard let argIdx = firstIndex(where: { $0 == .flag("-supplementary-output-file-map") }) else {
return nil
}
let supplementaryOutputs = self[argIdx + 1]
guard case let .path(path) = supplementaryOutputs,
case let .fileList(_, fileList) = path,
case let .outputFileMap(outputFileMap) = fileList else {
throw StringError("Unexpected argument for output file map")
}
return outputFileMap
}
}
}