Skip to content

Commit b73aaa4

Browse files
authored
Fix swift-symbolgraph-extract search paths (#7621)
Fixes an architecture and implementation bug where the include paths for `swift-symbolgraph-extract` were being determined inside build plan instead of by the target build description. This bug resulted in the wrong set of paths being determined. The new code moves the path determination logic into the proper target build descriptions and releverages the already computed include paths. The result of this is symbol graphs can be generated for Swift and C targets properly now without duplicate module in the search paths and with correct `-fmodule` usage.
1 parent 09efb06 commit b73aaa4

File tree

7 files changed

+174
-30
lines changed

7 files changed

+174
-30
lines changed

Sources/Build/BuildDescription/ClangTargetBuildDescription.swift

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,19 @@ public final class ClangTargetBuildDescription {
209209

210210
/// Determines the arguments needed to run `swift-symbolgraph-extract` for
211211
/// this module.
212-
public func symbolGraphExtractArguments() throws -> [String] {
212+
package func symbolGraphExtractArguments() throws -> [String] {
213213
var args = [String]()
214-
215214
if self.clangTarget.isCXX {
216215
args += ["-cxx-interoperability-mode=default"]
217216
}
218217
if let cxxLanguageStandard = self.clangTarget.cxxLanguageStandard {
219218
args += ["-Xcc", "-std=\(cxxLanguageStandard)"]
220219
}
220+
args += ["-I", self.clangTarget.includeDir.pathString]
221+
args += self.additionalFlags.asSwiftcCCompilerFlags()
222+
// Unconditionally use clang modules with swift tools.
223+
args += try self.clangModuleArguments().asSwiftcCCompilerFlags()
224+
args += try self.currentModuleMapFileArguments().asSwiftcCCompilerFlags()
221225
return args
222226
}
223227

@@ -263,7 +267,7 @@ public final class ClangTargetBuildDescription {
263267
// clang modules aren't fully supported in C++ mode in the current Darwin SDKs.
264268
let enableModules = triple.isDarwin() && !isCXX
265269
if enableModules {
266-
args += ["-fmodules", "-fmodule-name=" + target.c99name]
270+
args += try self.clangModuleArguments()
267271
}
268272

269273
// Only add the build path to the framework search path if there are binary frameworks to link against.
@@ -273,9 +277,7 @@ public final class ClangTargetBuildDescription {
273277

274278
args += ["-I", clangTarget.includeDir.pathString]
275279
args += additionalFlags
276-
if enableModules {
277-
args += try moduleCacheArgs
278-
}
280+
279281
args += buildParameters.sanitizers.compileCFlags()
280282

281283
// Add arguments from declared build settings.
@@ -433,11 +435,22 @@ public final class ClangTargetBuildDescription {
433435
return compilationConditions
434436
}
435437

436-
/// Module cache arguments.
437-
private var moduleCacheArgs: [String] {
438-
get throws {
439-
try ["-fmodules-cache-path=\(buildParameters.moduleCache.pathString)"]
438+
/// Enable Clang module flags.
439+
private func clangModuleArguments() throws -> [String] {
440+
let cachePath = try self.buildParameters.moduleCache.pathString
441+
return [
442+
"-fmodules",
443+
"-fmodule-name=\(self.target.c99name)",
444+
"-fmodules-cache-path=\(cachePath)",
445+
]
446+
}
447+
448+
private func currentModuleMapFileArguments() throws -> [String] {
449+
// Pass the path to the current module's module map if present.
450+
if let moduleMap = self.moduleMap {
451+
return ["-fmodule-map-file=\(moduleMap.pathString)"]
440452
}
453+
return []
441454
}
442455

443456
/// Generate the resource bundle accessor, if appropriate.

Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,26 @@ public final class SwiftTargetBuildDescription {
624624

625625
/// Determines the arguments needed to run `swift-symbolgraph-extract` for
626626
/// this module.
627-
public func symbolGraphExtractArguments() throws -> [String] {
627+
package func symbolGraphExtractArguments() throws -> [String] {
628628
var args = [String]()
629629
args += try self.cxxInteroperabilityModeArguments(
630630
propagateFromCurrentModuleOtherSwiftFlags: true)
631+
632+
args += self.buildParameters.toolchain.extraFlags.swiftCompilerFlags
633+
634+
// Include search paths determined during planning
635+
args += self.additionalFlags
636+
// FIXME: only pass paths to the actual dependencies of the module
637+
// Include search paths for swift module dependencies.
638+
args += ["-I", self.modulesPath.pathString]
639+
640+
// FIXME: Only include valid args
641+
// This condition should instead only include args which are known to be
642+
// compatible instead of filtering out specific unknown args.
643+
//
644+
// swift-symbolgraph-extract does not support parsing `-use-ld=lld` and
645+
// will silently error failing the operation.
646+
args = args.filter { !$0.starts(with: "-use-ld=") }
631647
return args
632648
}
633649

Sources/Build/BuildDescription/TargetBuildDescription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public enum TargetBuildDescription {
118118

119119
/// Determines the arguments needed to run `swift-symbolgraph-extract` for
120120
/// this module.
121-
public func symbolGraphExtractArguments() throws -> [String] {
121+
package func symbolGraphExtractArguments() throws -> [String] {
122122
switch self {
123123
case .swift(let target): try target.symbolGraphExtractArguments()
124124
case .clang(let target): try target.symbolGraphExtractArguments()

Sources/Build/BuildPlan/BuildPlan.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
648648
}
649649
}
650650

651+
/// Determines the arguments needed to run `swift-symbolgraph-extract` for
652+
/// a particular module.
651653
public func symbolGraphExtractArguments(for module: ResolvedModule) throws -> [String] {
652654
guard let description = self.targetMap[module.id] else {
653655
throw InternalError("Expected description for module \(module)")

Sources/Commands/Utilities/SymbolGraphExtract.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ public struct SymbolGraphExtract {
7070
// FIXME: everything here should be in symbolGraphExtractArguments
7171
commandLine += ["-module-name", module.c99name]
7272
commandLine += try buildParameters.tripleArgs(for: module)
73-
commandLine += try buildPlan.createAPIToolCommonArgs(includeLibrarySearchPaths: true)
7473
commandLine += ["-module-cache-path", try buildParameters.moduleCache.pathString]
7574
if verboseOutput {
7675
commandLine += ["-v"]

Sources/SPMBuildCore/BuildSystem/BuildSystem.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public protocol BuildPlan {
9191
func createAPIToolCommonArgs(includeLibrarySearchPaths: Bool) throws -> [String]
9292
func createREPLArguments() throws -> [String]
9393

94+
/// Determines the arguments needed to run `swift-symbolgraph-extract` for
95+
/// a particular module.
9496
func symbolGraphExtractArguments(for module: ResolvedModule) throws -> [String]
9597
}
9698

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 129 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,13 +1410,13 @@ final class BuildPlanTests: XCTestCase {
14101410
args += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1"]
14111411
args += ["-fblocks"]
14121412
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1413-
args += ["-fmodules", "-fmodule-name=extlib"]
1413+
args += [
1414+
"-fmodules",
1415+
"-fmodule-name=extlib",
1416+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
1417+
]
14141418
#endif
14151419
args += ["-I", ExtPkg.appending(components: "Sources", "extlib", "include").pathString]
1416-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1417-
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
1418-
#endif
1419-
14201420
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
14211421

14221422
if hostTriple.isLinux() {
@@ -1439,7 +1439,11 @@ final class BuildPlanTests: XCTestCase {
14391439
args += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1"]
14401440
args += ["-fblocks"]
14411441
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1442-
args += ["-fmodules", "-fmodule-name=exe"]
1442+
args += [
1443+
"-fmodules",
1444+
"-fmodule-name=exe",
1445+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
1446+
]
14431447
#endif
14441448
args += [
14451449
"-I", Pkg.appending(components: "Sources", "exe", "include").pathString,
@@ -1448,9 +1452,6 @@ final class BuildPlanTests: XCTestCase {
14481452
"-I", ExtPkg.appending(components: "Sources", "extlib", "include").pathString,
14491453
"-fmodule-map-file=\(buildPath.appending(components: "extlib.build", "module.modulemap"))",
14501454
]
1451-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1452-
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
1453-
#endif
14541455
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
14551456

14561457
if hostTriple.isLinux() {
@@ -1796,12 +1797,13 @@ final class BuildPlanTests: XCTestCase {
17961797
args += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1"]
17971798
args += ["-fblocks"]
17981799
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1799-
args += ["-fmodules", "-fmodule-name=lib"]
1800+
args += [
1801+
"-fmodules",
1802+
"-fmodule-name=lib",
1803+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
1804+
]
18001805
#endif
18011806
args += ["-I", Pkg.appending(components: "Sources", "lib", "include").pathString]
1802-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1803-
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
1804-
#endif
18051807
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
18061808

18071809
if hostTriple.isLinux() {
@@ -1999,6 +2001,115 @@ final class BuildPlanTests: XCTestCase {
19992001
}
20002002
}
20012003

2004+
func test_symbolGraphExtract_arguments() throws {
2005+
// ModuleGraph:
2006+
// .
2007+
// ├── A (Swift)
2008+
// │ ├── B (Swift)
2009+
// │   └── C (C)
2010+
// └── D (C)
2011+
// ├── B (Swift)
2012+
//   └── C (C)
2013+
2014+
let Pkg: AbsolutePath = "/Pkg"
2015+
let fs: FileSystem = InMemoryFileSystem(
2016+
emptyFiles:
2017+
// A
2018+
Pkg.appending(components: "Sources", "A", "A.swift").pathString,
2019+
// B
2020+
Pkg.appending(components: "Sources", "B", "B.swift").pathString,
2021+
// C
2022+
Pkg.appending(components: "Sources", "C", "C.c").pathString,
2023+
Pkg.appending(components: "Sources", "C", "include", "C.h").pathString,
2024+
// D
2025+
Pkg.appending(components: "Sources", "D", "D.c").pathString,
2026+
Pkg.appending(components: "Sources", "D", "include", "D.h").pathString
2027+
)
2028+
2029+
let observability = ObservabilitySystem.makeForTesting()
2030+
let graph = try loadModulesGraph(
2031+
fileSystem: fs,
2032+
manifests: [
2033+
Manifest.createRootManifest(
2034+
displayName: "Pkg",
2035+
path: .init(validating: Pkg.pathString),
2036+
targets: [
2037+
TargetDescription(name: "A", dependencies: ["B", "C"]),
2038+
TargetDescription(name: "B", dependencies: []),
2039+
TargetDescription(name: "C", dependencies: []),
2040+
TargetDescription(name: "D", dependencies: ["B", "C"]),
2041+
]
2042+
),
2043+
],
2044+
observabilityScope: observability.topScope
2045+
)
2046+
XCTAssertNoDiagnostics(observability.diagnostics)
2047+
2048+
let plan = try mockBuildPlan(
2049+
graph: graph,
2050+
fileSystem: fs,
2051+
observabilityScope: observability.topScope
2052+
)
2053+
2054+
let result = try BuildPlanResult(plan: plan)
2055+
let triple = result.plan.destinationBuildParameters.triple
2056+
2057+
func XCTAssertMatchesSubSequences(
2058+
_ value: [String],
2059+
_ patterns: [StringPattern]...,
2060+
file: StaticString = #file,
2061+
line: UInt = #line
2062+
) {
2063+
for pattern in patterns {
2064+
var pattern = pattern
2065+
pattern.insert(.anySequence, at: 0)
2066+
pattern.append(.anySequence)
2067+
XCTAssertMatch(value, pattern, file: file, line: line)
2068+
}
2069+
}
2070+
2071+
// A
2072+
do {
2073+
try XCTAssertMatchesSubSequences(
2074+
result.target(for: "A").symbolGraphExtractArguments(),
2075+
// Swift Module dependencies
2076+
["-I", "/path/to/build/\(triple)/debug/Modules"],
2077+
// C Module dependencies
2078+
["-Xcc", "-I", "-Xcc", "/Pkg/Sources/C/include"],
2079+
["-Xcc", "-fmodule-map-file=/path/to/build/\(triple)/debug/C.build/module.modulemap"]
2080+
)
2081+
}
2082+
2083+
// D
2084+
do {
2085+
try XCTAssertMatchesSubSequences(
2086+
result.target(for: "D").symbolGraphExtractArguments(),
2087+
// Self Module
2088+
["-I", "/Pkg/Sources/D/include"],
2089+
["-Xcc", "-fmodule-map-file=/path/to/build/\(triple)/debug/D.build/module.modulemap"],
2090+
2091+
// C Module dependencies
2092+
["-Xcc", "-I", "-Xcc", "/Pkg/Sources/C/include"],
2093+
["-Xcc", "-fmodule-map-file=/path/to/build/\(triple)/debug/C.build/module.modulemap"],
2094+
2095+
// General Args
2096+
[
2097+
"-Xcc", "-fmodules",
2098+
"-Xcc", "-fmodule-name=D",
2099+
"-Xcc", "-fmodules-cache-path=/path/to/build/\(triple)/debug/ModuleCache",
2100+
]
2101+
)
2102+
2103+
#if os(macOS)
2104+
try XCTAssertMatchesSubSequences(
2105+
result.target(for: "D").symbolGraphExtractArguments(),
2106+
// Swift Module dependencies
2107+
["-Xcc", "-fmodule-map-file=/path/to/build/\(triple)/debug/B.build/module.modulemap"]
2108+
)
2109+
#endif
2110+
}
2111+
}
2112+
20022113
func testREPLArguments() throws {
20032114
let Dep = AbsolutePath("/Dep")
20042115
let fs = InMemoryFileSystem(
@@ -3035,12 +3146,13 @@ final class BuildPlanTests: XCTestCase {
30353146
expectedExeBasicArgs += ["-target", defaultTargetTriple]
30363147
expectedExeBasicArgs += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1", "-fblocks"]
30373148
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
3038-
expectedExeBasicArgs += ["-fmodules", "-fmodule-name=exe"]
3149+
expectedExeBasicArgs += [
3150+
"-fmodules",
3151+
"-fmodule-name=exe",
3152+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"
3153+
]
30393154
#endif
30403155
expectedExeBasicArgs += ["-I", Pkg.appending(components: "Sources", "exe", "include").pathString]
3041-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
3042-
expectedExeBasicArgs += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
3043-
#endif
30443156

30453157
expectedExeBasicArgs += [triple.isWindows() ? "-gdwarf" : "-g"]
30463158

0 commit comments

Comments
 (0)