Skip to content

Commit

Permalink
Add target variant ABI digester file
Browse files Browse the repository at this point in the history
Now teaching the compiler to emit the ABI JSON file for the target
variant module.
  • Loading branch information
etcwilde committed Jan 2, 2025
1 parent f882b3d commit b3c8853
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 23 deletions.
55 changes: 36 additions & 19 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@ public struct Driver {

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

/// Path to the emitted ABI descriptor file.
let abiDescriptorFilePath: TypedVirtualPath?
}

private static func computeModuleOutputPaths(
Expand All @@ -411,12 +414,14 @@ public struct Driver {
outputFileMap: OutputFileMap?,
projectDirectory: VirtualPath.Handle?,
apiDescriptorDirectory: VirtualPath?,
supportedFrontendFeatures: Set<String>,
target: FrontendTargetInfo.Target,
isVariant: Bool) throws -> SupplementalModuleTargetOutputPaths {
struct SupplementalPathOptions {
let moduleDocPath: Option
let sourceInfoPath: Option
let apiDescriptorPath: Option
let abiDescriptorPath: Option
let moduleInterfacePath: Option
let privateInterfacePath: Option
let packageInterfacePath: Option
Expand All @@ -425,6 +430,7 @@ public struct Driver {
moduleDocPath: .emitModuleDocPath,
sourceInfoPath: .emitModuleSourceInfoPath,
apiDescriptorPath: .emitApiDescriptorPath,
abiDescriptorPath: .emitAbiDescriptorPath,
moduleInterfacePath: .emitModuleInterfacePath,
privateInterfacePath: .emitPrivateModuleInterfacePath,
packageInterfacePath: .emitPackageModuleInterfacePath)
Expand All @@ -433,6 +439,7 @@ public struct Driver {
moduleDocPath: .emitVariantModuleDocPath,
sourceInfoPath: .emitVariantModuleSourceInfoPath,
apiDescriptorPath: .emitVariantApiDescriptorPath,
abiDescriptorPath: .emitVariantAbiDescriptorPath,
moduleInterfacePath: .emitVariantModuleInterfacePath,
privateInterfacePath: .emitVariantPrivateModuleInterfacePath,
packageInterfacePath: .emitVariantPackageModuleInterfacePath)
Expand All @@ -459,6 +466,31 @@ public struct Driver {
moduleName: moduleOutputInfo.name,
projectDirectory: projectDirectory)

// ---------------------
// ABI Descriptor Path
func computeABIDescriptorFilePath(target: FrontendTargetInfo.Target,
features: Set<String>) -> TypedVirtualPath? {
guard features.contains(KnownCompilerFeature.emit_abi_descriptor.rawValue) else {
return nil
}
// Emit the descriptor only on platforms where Library Evolution is
// supported
guard target.triple.isDarwin || parsedOptions.hasArgument(.enableLibraryEvolution) else {
return nil
}
guard let moduleOutput = moduleOutputInfo.output else {
return nil
}

guard let path = try? VirtualPath.lookup(moduleOutput.outputPath)
.replacingExtension(with: .jsonABIBaseline) else {
return nil
}
return TypedVirtualPath(file: path.intern(), type: .jsonABIBaseline)
}
let abiDescriptorFilePath = computeABIDescriptorFilePath(target: target,
features: supportedFrontendFeatures)

// ---------------------
// API Descriptor Path
let apiDescriptorFilePath: VirtualPath.Handle?
Expand Down Expand Up @@ -545,7 +577,8 @@ public struct Driver {
swiftPrivateInterfacePath: swiftPrivateInterfacePath,
swiftPackageInterfacePath: swiftPackageInterfacePath,
moduleSourceInfoPath: moduleSourceInfoPath,
apiDescriptorFilePath: apiDescriptorFilePath)
apiDescriptorFilePath: apiDescriptorFilePath,
abiDescriptorFilePath: abiDescriptorFilePath)
}

/// Structure storing paths to supplemental outputs for the target module
Expand Down Expand Up @@ -622,24 +655,6 @@ public struct Driver {
.appending(component: "Frameworks")
} ()

lazy var abiDescriptorPath: TypedVirtualPath? = {
guard isFeatureSupported(.emit_abi_descriptor) else {
return nil
}
// Emit the descriptor only on platforms where Library Evolution is supported,
// or opted-into explicitly.
guard targetTriple.isDarwin || parsedOptions.hasArgument(.enableLibraryEvolution) else {
return nil
}
guard let moduleOutput = moduleOutputInfo.output else {
return nil
}
guard let path = try? VirtualPath.lookup(moduleOutput.outputPath).replacingExtension(with: .jsonABIBaseline) else {
return nil
}
return TypedVirtualPath(file: path.intern(), type: .jsonABIBaseline)
}()

public static func isOptionFound(_ opt: String, allOpts: Set<String>) -> Bool {
var current = opt
while(true) {
Expand Down Expand Up @@ -1221,6 +1236,7 @@ public struct Driver {
outputFileMap: self.outputFileMap,
projectDirectory: projectDirectory,
apiDescriptorDirectory: apiDescriptorDirectory,
supportedFrontendFeatures: self.supportedFrontendFeatures,
target: frontendTargetInfo.target,
isVariant: false)

Expand All @@ -1237,6 +1253,7 @@ public struct Driver {
outputFileMap: self.outputFileMap,
projectDirectory: projectDirectory,
apiDescriptorDirectory: apiDescriptorDirectory,
supportedFrontendFeatures: self.supportedFrontendFeatures,
target: targetVariant,
isVariant: true)
} else {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftDriver/Jobs/APIDigesterJobs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ extension Driver {
case .api:
return nil
case .abi:
return abiDescriptorPath
return moduleOutputPaths.abiDescriptorFilePath
}
}
guard let currentABI = getDescriptorPath(for: mode) else {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftDriver/Jobs/EmitModuleJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ extension Driver {
let outputPath = VirtualPath.lookup(moduleOutputPath)
commandLine.appendFlag(.o)
commandLine.appendPath(outputPath)
if let abiPath = abiDescriptorPath {
if let abiPath = moduleOutputPaths.abiDescriptorFilePath {
commandLine.appendFlag(.emitAbiDescriptorPath)
commandLine.appendPath(abiPath.file)
outputs.append(abiPath)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ extension Driver {
input: nil,
flag: "-emit-tbd-path")

if let abiDescriptorPath = abiDescriptorPath {
if let abiDescriptorPath = moduleOutputPaths.abiDescriptorFilePath {
try addOutputOfType(outputType: .jsonABIBaseline,
finalOutputPath: abiDescriptorPath.fileHandle,
input: nil,
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftDriver/Jobs/MergeModuleJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ extension Driver {
commandLine.appendFlag(.o)
commandLine.appendPath(outputPath)

if let abiPath = abiDescriptorPath {
if let abiPath = moduleOutputPaths.abiDescriptorFilePath {
commandLine.appendFlag(.emitAbiDescriptorPath)
commandLine.appendPath(abiPath.file)
outputs.append(abiPath)
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftOptions/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ extension Option {
public static let emitTbdPathEQ: Option = Option("-emit-tbd-path=", .joined, alias: Option.emitTbdPath, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant])
public static let emitTbdPath: Option = Option("-emit-tbd-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "<path>", helpText: "Emit the TBD file to <path>")
public static let emitTbd: Option = Option("-emit-tbd", .flag, attributes: [.frontend, .noInteractive, .supplementaryOutput], helpText: "Emit a TBD file")
public static let emitVariantAbiDescriptorPath: Option = Option("-emit-variant-abi-descriptor-path", .separate, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: "<path>", helpText: "Output the ABI descriptor of current target variant module to <path>")
public static let emitVariantApiDescriptorPath: Option = Option("-emit-variant-api-descriptor-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "<path>", helpText: "Output a JSON file describing the target variant module's API to <path>")
public static let emitVariantModuleDocPath: Option = Option("-emit-variant-module-doc-path", .separate, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: "<path>", helpText: "Output module documentation file for the target variant to <path>")
public static let emitVariantModuleInterfacePath: Option = Option("-emit-variant-module-interface-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "<path>", helpText: "Output module interface file for the target variant to <path>")
Expand Down Expand Up @@ -1301,6 +1302,7 @@ extension Option {
Option.emitTbdPathEQ,
Option.emitTbdPath,
Option.emitTbd,
Option.emitVariantAbiDescriptorPath,
Option.emitVariantApiDescriptorPath,
Option.emitVariantModuleDocPath,
Option.emitVariantModuleInterfacePath,
Expand Down
70 changes: 70 additions & 0 deletions Tests/SwiftDriverTests/SwiftDriverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4090,13 +4090,83 @@ final class SwiftDriverTests: XCTestCase {

XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftdoc")))))
XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftsourceinfo")))))
XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.abi.json")))))
XCTAssertTrue(targetModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/target.swiftmodule")))]))

XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftdoc")))))
XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftsourceinfo")))))
XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.abi.json")))))
XCTAssertTrue(variantModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftmodule")))]))
}

do {
// explicitly emit variant supplemental outputs
var driver = try Driver(args: ["swiftc",
"-target", "x86_64-apple-macosx10.14",
"-target-variant", "x86_64-apple-ios13.1-macabi",
"-enable-library-evolution",
"-package-name", "Susan",
"-emit-module",
"-emit-module-path", "target.swiftmodule",
"-emit-variant-module-path", "variant.swiftmodule",
"-Xfrontend", "-emit-module-doc-path", "-Xfrontend", "target.swiftdoc",
"-Xfrontend", "-emit-variant-module-doc-path", "variant.swiftdoc",
"-emit-module-source-info-path", "target.sourceinfo",
"-emit-variant-module-source-info-path", "variant.sourceinfo",
"-emit-package-module-interface-path", "target.package.swiftinterface",
"-emit-variant-package-module-interface-path", "variant.package.swiftinterface",
"-emit-private-module-interface-path", "target.private.swiftinterface",
"-emit-variant-private-module-interface-path", "variant.private.swiftinterface",
"-emit-module-interface-path", "target.swiftinterface",
"-emit-variant-module-interface-path", "variant.swiftinterface",
"foo.swift"])

let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs()
// emit module, emit module, compile foo.swift,
// verify target.swiftinterface,
// verify target.private.swiftinterface,
// verify target.package.swiftinterface,
XCTAssertEqual(plannedJobs.count, 6)

let targetModuleJob: Job = plannedJobs[0]
let variantModuleJob = plannedJobs[1]

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftModule }.last!.file,
try toPath("target.swiftmodule"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftModule }.last!.file,
try toPath("variant.swiftmodule"))

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftDocumentation }.last!.file,
try toPath("target.swiftdoc"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftDocumentation }.last!.file,
try toPath("variant.swiftdoc"))

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftSourceInfoFile }.last!.file,
try toPath("target.sourceinfo"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftSourceInfoFile }.last!.file,
try toPath("variant.sourceinfo"))

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftInterface}.last!.file,
try toPath("target.swiftinterface"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftInterface}.last!.file,
try toPath("variant.swiftinterface"))

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .privateSwiftInterface}.last!.file,
try toPath("target.private.swiftinterface"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .privateSwiftInterface}.last!.file,
try toPath("variant.private.swiftinterface"))

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .packageSwiftInterface}.last!.file,
try toPath("target.package.swiftinterface"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .packageSwiftInterface}.last!.file,
try toPath("variant.package.swiftinterface"))

XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .jsonABIBaseline }.last!.file,
try toPath("target.abi.json"))
XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .jsonABIBaseline}.last!.file,
try toPath("variant.abi.json"))
}

#if os(macOS)
do {
try withTemporaryDirectory { path in
Expand Down

0 comments on commit b3c8853

Please sign in to comment.