-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add support for symbol graph generation for clang targets #10057
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1087,6 +1087,83 @@ struct PIFBuilderTests { | |
| ] | ||
| #expect(sources == expected) | ||
| } | ||
|
|
||
| @Test func symbolGraphExtractorBuildSettings() async throws { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would also be valuable to have a test for this which runs a build end to end and verifies we can correctly extract the symbol graph
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm hoping to avoid yet another e2e test in SwiftPM. There are other e2e tests for symbol graphs in general. If it's important then I'll add it. |
||
| try await withGeneratedPIF(fromFixture: "CFamilyTargets/ModuleMapGenerationCases") { pif, observabilitySystem in | ||
| #expect(observabilitySystem.diagnostics.filter { $0.severity == .error }.isEmpty) | ||
|
|
||
| // configureSourceModuleBuildSettings is called for every source module via the same | ||
| // delegate path, so verifying on representative C targets is sufficient coverage. | ||
| for targetName in ["UmbrellaHeader", "FlatInclude"] { | ||
| let config = try pif.workspace | ||
| .project(named: "ModuleMapGenerationCases") | ||
| .target(named: targetName) | ||
| .buildConfig(named: .release) | ||
|
|
||
| let expectedDir = "$(TARGET_BUILD_DIR)/$(CURRENT_ARCH)/\(targetName).symbolgraphs" | ||
| #expect(config.settings[.SYMBOL_GRAPH_EXTRACTOR_OUTPUT_DIR] == expectedDir, "target: \(targetName)") | ||
| #expect(config.settings[.TAPI_EXTRACT_API_OUTPUT_DIR] == expectedDir, "target: \(targetName)") | ||
| #expect(config.settings[.DOCC_EXTRACT_PROJECT_HEADERS_DOCUMENTATION] == "YES", "target: \(targetName)") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Test func cFamilyHeadersAddedToHeadersBuildPhase() async throws { | ||
| try await withGeneratedPIF(fromFixture: "CFamilyTargets/ModuleMapGenerationCases") { pif, observabilitySystem in | ||
| #expect(observabilitySystem.diagnostics.filter { $0.severity == .error }.isEmpty) | ||
|
|
||
| let project = try pif.workspace.project(named: "ModuleMapGenerationCases") | ||
|
|
||
| // UmbrellaHeader has include/UmbrellaHeader/UmbrellaHeader.h — expect a headers build phase | ||
| do { | ||
| let umbrellaTarget = try project.target(named: "UmbrellaHeader") | ||
| let umbrellaHeadersPhase: ProjectModel.HeadersBuildPhase = try #require( | ||
| umbrellaTarget.common.buildPhases.compactMap({ | ||
| guard case let .headers(phase) = $0 else { return nil } | ||
| return phase | ||
| }).only, | ||
| "Expected exactly one headers build phase for UmbrellaHeader" | ||
| ) | ||
|
|
||
| let umbrellaHeaderPaths: [AbsolutePath] = umbrellaHeadersPhase.files.compactMap { | ||
| guard case .reference(id: let refId) = $0.ref else { return nil } | ||
| return try? project.underlying.mainGroup.findSource(ref: refId) | ||
| }.sorted() | ||
| #expect(umbrellaHeaderPaths.contains { $0.basename == "UmbrellaHeader.h" }) | ||
| // nil headerVisibility means "project" visibility — what we set for symbol graph extraction | ||
| #expect(umbrellaHeadersPhase.files.allSatisfy { $0.headerVisibility == nil }) | ||
| } | ||
|
|
||
| // FlatInclude has include/FlatIncludeHeader.h — expect a headers build phase | ||
| do { | ||
| let flatIncludeTarget = try project.target(named: "FlatInclude") | ||
| let flatIncludeHeadersPhase: ProjectModel.HeadersBuildPhase = try #require( | ||
| flatIncludeTarget.common.buildPhases.compactMap({ | ||
| guard case let .headers(phase) = $0 else { return nil } | ||
| return phase | ||
| }).only, | ||
| "Expected exactly one headers build phase for FlatInclude" | ||
| ) | ||
|
|
||
| let flatIncludeHeaderPaths: [AbsolutePath] = flatIncludeHeadersPhase.files.compactMap { | ||
| guard case .reference(id: let refId) = $0.ref else { return nil } | ||
| return try? project.underlying.mainGroup.findSource(ref: refId) | ||
| }.sorted() | ||
| #expect(flatIncludeHeaderPaths.contains { $0.basename == "FlatIncludeHeader.h" }) | ||
| #expect(flatIncludeHeadersPhase.files.allSatisfy { $0.headerVisibility == nil }) | ||
| } | ||
|
|
||
| // NoIncludeDir has no header files — should have no headers build phase | ||
| do { | ||
| let noIncludeDirTarget = try project.target(named: "NoIncludeDir") | ||
| let noHeadersPhases = noIncludeDirTarget.common.buildPhases.filter { | ||
| guard case .headers = $0 else { return false } | ||
| return true | ||
| } | ||
| #expect(noHeadersPhases.isEmpty) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| extension ProjectModel.Group { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing we should double check is that this doesn't cause us to generate any headermaps we didn't before