Skip to content

Commit 20c3e0c

Browse files
committed
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 65ec44f commit 20c3e0c

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.
@@ -421,11 +423,22 @@ public final class ClangTargetBuildDescription {
421423
return compilationConditions
422424
}
423425

424-
/// Module cache arguments.
425-
private var moduleCacheArgs: [String] {
426-
get throws {
427-
try ["-fmodules-cache-path=\(buildParameters.moduleCache.pathString)"]
426+
/// Enable Clang module flags.
427+
private func clangModuleArguments() throws -> [String] {
428+
let cachePath = try self.buildParameters.moduleCache.pathString
429+
return [
430+
"-fmodules",
431+
"-fmodule-name=\(self.target.c99name)",
432+
"-fmodules-cache-path=\(cachePath)",
433+
]
434+
}
435+
436+
private func currentModuleMapFileArguments() throws -> [String] {
437+
// Pass the path to the current module's module map if present.
438+
if let moduleMap = self.moduleMap {
439+
return ["-fmodule-map-file=\(moduleMap.pathString)"]
428440
}
441+
return []
429442
}
430443

431444
/// 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
@@ -623,10 +623,26 @@ public final class SwiftTargetBuildDescription {
623623

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

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
@@ -1408,13 +1408,13 @@ final class BuildPlanTests: XCTestCase {
14081408
args += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1"]
14091409
args += ["-fblocks"]
14101410
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1411-
args += ["-fmodules", "-fmodule-name=extlib"]
1411+
args += [
1412+
"-fmodules",
1413+
"-fmodule-name=extlib",
1414+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
1415+
]
14121416
#endif
14131417
args += ["-I", ExtPkg.appending(components: "Sources", "extlib", "include").pathString]
1414-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1415-
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
1416-
#endif
1417-
14181418
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
14191419

14201420
if hostTriple.isLinux() {
@@ -1437,7 +1437,11 @@ final class BuildPlanTests: XCTestCase {
14371437
args += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1"]
14381438
args += ["-fblocks"]
14391439
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1440-
args += ["-fmodules", "-fmodule-name=exe"]
1440+
args += [
1441+
"-fmodules",
1442+
"-fmodule-name=exe",
1443+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
1444+
]
14411445
#endif
14421446
args += [
14431447
"-I", Pkg.appending(components: "Sources", "exe", "include").pathString,
@@ -1446,9 +1450,6 @@ final class BuildPlanTests: XCTestCase {
14461450
"-I", ExtPkg.appending(components: "Sources", "extlib", "include").pathString,
14471451
"-fmodule-map-file=\(buildPath.appending(components: "extlib.build", "module.modulemap"))",
14481452
]
1449-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1450-
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
1451-
#endif
14521453
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
14531454

14541455
if hostTriple.isLinux() {
@@ -1794,12 +1795,13 @@ final class BuildPlanTests: XCTestCase {
17941795
args += ["-O0", "-DSWIFT_PACKAGE=1", "-DDEBUG=1"]
17951796
args += ["-fblocks"]
17961797
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1797-
args += ["-fmodules", "-fmodule-name=lib"]
1798+
args += [
1799+
"-fmodules",
1800+
"-fmodule-name=lib",
1801+
"-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))",
1802+
]
17981803
#endif
17991804
args += ["-I", Pkg.appending(components: "Sources", "lib", "include").pathString]
1800-
#if os(macOS) // FIXME(5473) - support modules on non-Apple platforms
1801-
args += ["-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))"]
1802-
#endif
18031805
args += [hostTriple.isWindows() ? "-gdwarf" : "-g"]
18041806

18051807
if hostTriple.isLinux() {
@@ -1997,6 +1999,115 @@ final class BuildPlanTests: XCTestCase {
19971999
}
19982000
}
19992001

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

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

0 commit comments

Comments
 (0)