Skip to content

Commit ec2a626

Browse files
authored
Pass symbol graph options to swift build to customize the output (#9124)
1 parent 7dfca5a commit ec2a626

File tree

7 files changed

+174
-41
lines changed

7 files changed

+174
-41
lines changed

.swift-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.1.0
1+
6.1.2

Sources/Commands/PackageCommands/DumpCommands.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,16 @@ struct DumpSymbolGraph: AsyncSwiftCommand {
5656
enableAllTraits: true,
5757
cacheBuildManifest: false
5858
)
59-
// TODO pass along the various flags as associated values to the symbol graph build output (e.g. includeSPISymbols)
60-
let buildResult = try await buildSystem.build(subset: .allExcludingTests, buildOutputs: [.symbolGraph, .buildPlan])
59+
let buildResult = try await buildSystem.build(subset: .allExcludingTests, buildOutputs: [.symbolGraph(
60+
BuildOutput.SymbolGraphOptions(
61+
prettyPrint: prettyPrint,
62+
minimumAccessLevel: .accessLevel(minimumAccessLevel),
63+
includeSynthesized: !skipSynthesizedMembers,
64+
includeSPI: includeSPISymbols,
65+
emitExtensionBlocks: extensionBlockSymbolBehavior != .omitExtensionBlockSymbols,
66+
// TODO skip inherited docs
67+
)
68+
), .buildPlan])
6169

6270
let symbolGraphDirectory = try swiftCommandState.productsBuildParameters.dataPath.appending("symbolgraph")
6371

@@ -189,3 +197,9 @@ struct DumpPIF: AsyncSwiftCommand {
189197
return .init(wantsMultipleTestProducts: true)
190198
}
191199
}
200+
201+
fileprivate extension BuildOutput.SymbolGraphAccessLevel {
202+
fileprivate static func accessLevel(_ accessLevel: SymbolGraphExtract.AccessLevel) -> BuildOutput.SymbolGraphAccessLevel {
203+
return BuildOutput.SymbolGraphAccessLevel.init(rawValue: accessLevel.rawValue)!
204+
}
205+
}

Sources/Commands/Utilities/PluginDelegate.swift

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,7 @@ final class PluginDelegate: PluginInvocationDelegate {
413413
)
414414

415415
// Build the target, if needed. We are interested in symbol graph (ideally) or a build plan.
416-
// TODO pass along the options as associated values to the symbol graph build output (e.g. includeSPI)
417-
let buildResult = try await buildSystem.build(subset: .target(targetName), buildOutputs: [.symbolGraph, .buildPlan])
416+
let buildResult = try await buildSystem.build(subset: .target(targetName), buildOutputs: [.symbolGraph(options), .buildPlan])
418417

419418
if let symbolGraph = buildResult.symbolGraph {
420419
let path = (try swiftCommandState.productsBuildParameters.buildPath)
@@ -505,3 +504,33 @@ extension BuildSystem {
505504
}
506505
}
507506
}
507+
508+
extension BuildOutput {
509+
static func symbolGraph(_ options: PluginInvocationSymbolGraphOptions) -> BuildOutput {
510+
return .symbolGraph(SymbolGraphOptions(
511+
minimumAccessLevel: .accessLevel(options.minimumAccessLevel),
512+
includeSynthesized: options.includeSynthesized,
513+
includeSPI: options.includeSPI,
514+
emitExtensionBlocks: options.emitExtensionBlocks
515+
))
516+
}
517+
}
518+
519+
fileprivate extension BuildOutput.SymbolGraphAccessLevel {
520+
fileprivate static func accessLevel(_ accessLevel: PluginInvocationSymbolGraphOptions.AccessLevel) -> BuildOutput.SymbolGraphAccessLevel {
521+
return switch accessLevel {
522+
case .private:
523+
.private
524+
case .fileprivate:
525+
.fileprivate
526+
case .internal:
527+
.internal
528+
case .package:
529+
.package
530+
case .public:
531+
.public
532+
case .open:
533+
.open
534+
}
535+
}
536+
}

Sources/SPMBuildCore/BuildSystem/BuildSystem.swift

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,59 @@ public enum BuildSubset {
3838
/// can produce certain extra outputs in the process of building. Not all
3939
/// build systems can produce all possible build outputs. Check the build
4040
/// result for indication that the output was produced.
41-
public enum BuildOutput {
42-
case symbolGraph
43-
// TODO associated values for the following symbol graph options:
44-
// "-skip-inherited-docs"
45-
// "-symbol-graph-minimum-access-level", “<LEVEL>”
46-
// "-include-spi-symbols"
47-
// "-emit-extension-block-symbols"
48-
// "-emit-synthesized-members"
41+
public enum BuildOutput: Equatable {
42+
public static func == (lhs: BuildOutput, rhs: BuildOutput) -> Bool {
43+
switch lhs {
44+
case .symbolGraph(let leftOptions):
45+
switch rhs {
46+
case .symbolGraph(let rightOptions):
47+
return leftOptions == rightOptions
48+
default:
49+
return false
50+
}
51+
case .buildPlan:
52+
switch rhs {
53+
case .buildPlan:
54+
return true
55+
default:
56+
return false
57+
}
58+
case .replArguments:
59+
switch rhs {
60+
case .replArguments:
61+
return true
62+
default:
63+
return false
64+
}
65+
}
66+
}
67+
68+
public enum SymbolGraphAccessLevel: String {
69+
case `private`, `fileprivate`, `internal`, `package`, `public`, `open`
70+
}
71+
public struct SymbolGraphOptions: Equatable {
72+
public var prettyPrint: Bool
73+
public var minimumAccessLevel: SymbolGraphAccessLevel
74+
public var includeSynthesized: Bool
75+
public var includeSPI: Bool
76+
public var emitExtensionBlocks: Bool
77+
78+
public init(
79+
prettyPrint: Bool = false,
80+
minimumAccessLevel: SymbolGraphAccessLevel,
81+
includeSynthesized: Bool,
82+
includeSPI: Bool,
83+
emitExtensionBlocks: Bool
84+
) {
85+
self.prettyPrint = prettyPrint
86+
self.minimumAccessLevel = minimumAccessLevel
87+
self.includeSynthesized = includeSynthesized
88+
self.includeSPI = includeSPI
89+
self.emitExtensionBlocks = emitExtensionBlocks
90+
}
91+
}
92+
93+
case symbolGraph(SymbolGraphOptions)
4994
case buildPlan
5095
case replArguments
5196
}

Sources/SwiftBuildSupport/SwiftBuildSystem.swift

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -351,16 +351,26 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
351351

352352
try await writePIF(buildParameters: buildParameters)
353353

354+
var symbolGraphOptions: BuildOutput.SymbolGraphOptions?
355+
for output in buildOutputs {
356+
switch output {
357+
case .symbolGraph(let options):
358+
symbolGraphOptions = options
359+
default:
360+
continue
361+
}
362+
}
363+
354364
return try await startSWBuildOperation(
355365
pifTargetName: subset.pifTargetName,
356-
genSymbolGraph: buildOutputs.contains(.symbolGraph),
366+
symbolGraphOptions: symbolGraphOptions,
357367
generateReplArguments: buildOutputs.contains(.replArguments),
358368
)
359369
}
360370

361371
private func startSWBuildOperation(
362372
pifTargetName: String,
363-
genSymbolGraph: Bool,
373+
symbolGraphOptions: BuildOutput.SymbolGraphOptions?,
364374
generateReplArguments: Bool
365375
) async throws -> BuildResult {
366376
let buildStartTime = ContinuousClock.Instant.now
@@ -413,7 +423,7 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
413423
throw error
414424
}
415425

416-
let request = try await self.makeBuildRequest(session: session, configuredTargets: configuredTargets, derivedDataPath: derivedDataPath, genSymbolGraph: genSymbolGraph)
426+
let request = try await self.makeBuildRequest(session: session, configuredTargets: configuredTargets, derivedDataPath: derivedDataPath, symbolGraphOptions: symbolGraphOptions)
417427

418428
struct BuildState {
419429
private var targetsByID: [Int: SwiftBuild.SwiftBuildMessage.TargetStartedInfo] = [:]
@@ -631,7 +641,7 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
631641
)
632642
}
633643

634-
private func makeBuildParameters(session: SWBBuildServiceSession, genSymbolGraph: Bool) async throws -> SwiftBuild.SWBBuildParameters {
644+
private func makeBuildParameters(session: SWBBuildServiceSession, symbolGraphOptions: BuildOutput.SymbolGraphOptions?) async throws -> SwiftBuild.SWBBuildParameters {
635645
// Generate the run destination parameters.
636646
let runDestination = makeRunDestination()
637647

@@ -658,9 +668,39 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
658668

659669
// FIXME: workaround for old Xcode installations such as what is in CI
660670
settings["LM_SKIP_METADATA_EXTRACTION"] = "YES"
661-
if genSymbolGraph {
671+
if let symbolGraphOptions {
662672
settings["RUN_SYMBOL_GRAPH_EXTRACT"] = "YES"
663-
// TODO set additional symbol graph options from the build output here, such as "include-spi-symbols"
673+
674+
if symbolGraphOptions.prettyPrint {
675+
settings["DOCC_PRETTY_PRINT"] = "YES"
676+
}
677+
678+
if symbolGraphOptions.emitExtensionBlocks {
679+
settings["DOCC_EXTRACT_EXTENSION_SYMBOLS"] = "YES"
680+
}
681+
682+
if !symbolGraphOptions.includeSynthesized {
683+
settings["DOCC_SKIP_SYNTHESIZED_MEMBERS"] = "YES"
684+
}
685+
686+
if symbolGraphOptions.includeSPI {
687+
settings["DOCC_EXTRACT_SPI_DOCUMENTATION"] = "YES"
688+
}
689+
690+
switch symbolGraphOptions.minimumAccessLevel {
691+
case .private:
692+
settings["DOCC_MINIMUM_ACCESS_LEVEL"] = "private"
693+
case .fileprivate:
694+
settings["DOCC_MINIMUM_ACCESS_LEVEL"] = "fileprivate"
695+
case .internal:
696+
settings["DOCC_MINIMUM_ACCESS_LEVEL"] = "internal"
697+
case .package:
698+
settings["DOCC_MINIMUM_ACCESS_LEVEL"] = "package"
699+
case .public:
700+
settings["DOCC_MINIMUM_ACCESS_LEVEL"] = "public"
701+
case .open:
702+
settings["DOCC_MINIMUM_ACCESS_LEVEL"] = "open"
703+
}
664704
}
665705

666706
let normalizedTriple = Triple(buildParameters.triple.triple, normalizing: true)
@@ -741,9 +781,9 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
741781
return params
742782
}
743783

744-
public func makeBuildRequest(session: SWBBuildServiceSession, configuredTargets: [SWBTargetGUID], derivedDataPath: Basics.AbsolutePath, genSymbolGraph: Bool) async throws -> SWBBuildRequest {
784+
public func makeBuildRequest(session: SWBBuildServiceSession, configuredTargets: [SWBTargetGUID], derivedDataPath: Basics.AbsolutePath, symbolGraphOptions: BuildOutput.SymbolGraphOptions?) async throws -> SWBBuildRequest {
745785
var request = SWBBuildRequest()
746-
request.parameters = try await makeBuildParameters(session: session, genSymbolGraph: genSymbolGraph)
786+
request.parameters = try await makeBuildParameters(session: session, symbolGraphOptions: symbolGraphOptions)
747787
request.configuredTargets = configuredTargets.map { SWBConfiguredTarget(guid: $0.rawValue, parameters: request.parameters) }
748788
request.useParallelTargets = true
749789
request.useImplicitDependencies = false

Tests/CommandsTests/PackageCommandTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5971,7 +5971,7 @@ struct PackageCommandTests {
59715971
func commandPluginSymbolGraphCallbacks(
59725972
data: BuildData,
59735973
) async throws {
5974-
try await withKnownIssue {
5974+
try await withKnownIssue(isIntermittent: true) {
59755975
try await testWithTemporaryDirectory { tmpPath in
59765976
// Create a sample package with a library, and executable, and a plugin.
59775977
let packageDir = tmpPath.appending(components: "MyPackage")
@@ -6102,7 +6102,8 @@ struct PackageCommandTests {
61026102
}
61036103
}
61046104
} when: {
6105-
ProcessInfo.hostOperatingSystem == .windows && data.buildSystem == .swiftbuild
6105+
(ProcessInfo.hostOperatingSystem == .windows && data.buildSystem == .swiftbuild)
6106+
|| !CiEnvironment.runningInSmokeTestPipeline
61066107
}
61076108
}
61086109

Tests/FunctionalTests/TraitTests.swift

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -595,25 +595,29 @@ struct TraitTests {
595595
buildSystem: BuildSystemProvider.Kind,
596596
configuration: BuildConfiguration,
597597
) async throws {
598-
try await fixture(name: "Traits") { fixturePath in
599-
// The swiftbuild build system doesn't yet have the ability for command plugins to request symbol graphs
600-
try await withKnownIssue(
601-
"https://github.com/swiftlang/swift-build/issues/609",
602-
isIntermittent: true,
603-
) {
604-
let (stdout, _) = try await executeSwiftPackage(
605-
fixturePath.appending("Package10"),
606-
configuration: configuration,
607-
extraArgs: ["plugin", "extract"],
608-
buildSystem: buildSystem,
609-
)
610-
let path = String(stdout.split(whereSeparator: \.isNewline).first!)
611-
let symbolGraph = try String(contentsOfFile: "\(path)/Package10Library1.symbols.json", encoding: .utf8)
612-
#expect(symbolGraph.contains("TypeGatedByPackage10Trait1"))
613-
#expect(symbolGraph.contains("TypeGatedByPackage10Trait2"))
614-
} when: {
615-
buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows
598+
try await withKnownIssue(isIntermittent: true) {
599+
try await fixture(name: "Traits") { fixturePath in
600+
// The swiftbuild build system doesn't yet have the ability for command plugins to request symbol graphs
601+
try await withKnownIssue(
602+
"https://github.com/swiftlang/swift-build/issues/609",
603+
isIntermittent: true,
604+
) {
605+
let (stdout, _) = try await executeSwiftPackage(
606+
fixturePath.appending("Package10"),
607+
configuration: configuration,
608+
extraArgs: ["plugin", "extract"],
609+
buildSystem: buildSystem,
610+
)
611+
let path = String(stdout.split(whereSeparator: \.isNewline).first!)
612+
let symbolGraph = try String(contentsOfFile: "\(path)/Package10Library1.symbols.json", encoding: .utf8)
613+
#expect(symbolGraph.contains("TypeGatedByPackage10Trait1"))
614+
#expect(symbolGraph.contains("TypeGatedByPackage10Trait2"))
615+
} when: {
616+
buildSystem == .swiftbuild && ProcessInfo.hostOperatingSystem == .windows
617+
}
616618
}
619+
} when: {
620+
!CiEnvironment.runningInSmokeTestPipeline
617621
}
618622
}
619623

0 commit comments

Comments
 (0)