Skip to content

Commit 82b899e

Browse files
committed
Get compiler arguments for files in the Plugins folder
This uses the new API in SwiftPM introduced by swiftlang/swift-package-manager#6763 to get compiler arguments for a target instead of stitching them together in sourcekit-lsp. Fixes swiftlang#664 rdar://102213837
1 parent c3196a0 commit 82b899e

File tree

3 files changed

+133
-194
lines changed

3 files changed

+133
-194
lines changed

Sources/SKSwiftPMWorkspace/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ target_link_libraries(SKSwiftPMWorkspace PRIVATE
1010
SKCore
1111
TSCBasic)
1212
target_link_libraries(SKSwiftPMWorkspace PUBLIC
13-
Build)
13+
Build
14+
SourceKitLSPAPI)

Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift

Lines changed: 38 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import PackageModel
2222
import SKCore
2323
import SKSupport
2424
import SourceControl
25+
import SourceKitLSPAPI
2526
import Workspace
2627

2728
import struct Basics.AbsolutePath
@@ -43,6 +44,12 @@ public enum ReloadPackageStatus {
4344
case end
4445
}
4546

47+
/// A build target in SwiftPM
48+
public typealias SwiftBuildTarget = SourceKitLSPAPI.BuildTarget
49+
50+
/// A build target in `BuildServerProtocol`
51+
public typealias BuildServerTarget = BuildServerProtocol.BuildTarget
52+
4653
/// Same as `toolchainRegistry.default`.
4754
///
4855
/// Needed to work around a compiler crash that prevents us from accessing `toolchainRegistry.default` in
@@ -83,8 +90,8 @@ public actor SwiftPMWorkspace {
8390
public let buildParameters: BuildParameters
8491
let fileSystem: FileSystem
8592

86-
var fileToTarget: [AbsolutePath: TargetBuildDescription] = [:]
87-
var sourceDirToTarget: [AbsolutePath: TargetBuildDescription] = [:]
93+
var fileToTarget: [AbsolutePath: SwiftBuildTarget] = [:]
94+
var sourceDirToTarget: [AbsolutePath: SwiftBuildTarget] = [:]
8895

8996
/// The URIs for which the delegate has registered for change notifications,
9097
/// mapped to the language the delegate specified when registering for change notifications.
@@ -215,19 +222,20 @@ extension SwiftPMWorkspace {
215222
fileSystem: fileSystem,
216223
observabilityScope: observabilitySystem.topScope
217224
)
225+
let buildDescription = BuildDescription(buildPlan: plan)
218226

219227
/// Make sure to execute any throwing statements before setting any
220228
/// properties because otherwise we might end up in an inconsistent state
221229
/// with only some properties modified.
222230
self.packageGraph = packageGraph
223231

224-
self.fileToTarget = [AbsolutePath: TargetBuildDescription](
232+
self.fileToTarget = [AbsolutePath: SwiftBuildTarget](
225233
packageGraph.allTargets.flatMap { target in
226234
return target.sources.paths.compactMap {
227-
guard let td = plan.targetMap[target.id] else {
235+
guard let buildTarget = buildDescription.getBuildTarget(for: target) else {
228236
return nil
229237
}
230-
return (key: $0, value: td)
238+
return (key: $0, value: buildTarget)
231239
}
232240
},
233241
uniquingKeysWith: { td, _ in
@@ -236,12 +244,12 @@ extension SwiftPMWorkspace {
236244
}
237245
)
238246

239-
self.sourceDirToTarget = [AbsolutePath: TargetBuildDescription](
240-
packageGraph.allTargets.compactMap { target in
241-
guard let td = plan.targetMap[target.id] else {
247+
self.sourceDirToTarget = [AbsolutePath: SwiftBuildTarget](
248+
packageGraph.allTargets.compactMap { (target) -> (AbsolutePath, SwiftBuildTarget)? in
249+
guard let buildTarget = buildDescription.getBuildTarget(for: target) else {
242250
return nil
243251
}
244-
return (key: target.sources.root, value: td)
252+
return (key: target.sources.root, value: buildTarget)
245253
},
246254
uniquingKeysWith: { td, _ in
247255
// FIXME: is there a preferred target?
@@ -284,8 +292,11 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
284292
return nil
285293
}
286294

287-
if let td = try targetDescription(for: path) {
288-
return try settings(for: path, language, td)
295+
if let buildTarget = try buildTarget(for: path) {
296+
return FileBuildSettings(
297+
compilerArguments: try buildTarget.compileArguments(for: path.asURL),
298+
workingDirectory: workspacePath.pathString
299+
)
289300
}
290301

291302
if path.basename == "Package.swift" {
@@ -310,7 +321,7 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
310321
}
311322

312323
/// Returns the resolved target description for the given file, if one is known.
313-
private func targetDescription(for file: AbsolutePath) throws -> TargetBuildDescription? {
324+
private func buildTarget(for file: AbsolutePath) throws -> SwiftBuildTarget? {
314325
if let td = fileToTarget[file] {
315326
return td
316327
}
@@ -359,7 +370,7 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
359370
guard let fileUrl = uri.fileURL else {
360371
return .unhandled
361372
}
362-
if (try? targetDescription(for: AbsolutePath(validating: fileUrl.path))) != nil {
373+
if (try? buildTarget(for: AbsolutePath(validating: fileUrl.path))) != nil {
363374
return .handled
364375
} else {
365376
return .unhandled
@@ -371,24 +382,6 @@ extension SwiftPMWorkspace {
371382

372383
// MARK: Implementation details
373384

374-
/// Retrieve settings for the given file, which is part of a known target build description.
375-
public func settings(
376-
for path: AbsolutePath,
377-
_ language: Language,
378-
_ td: TargetBuildDescription
379-
) throws -> FileBuildSettings? {
380-
switch (td, language) {
381-
case (.swift(let td), .swift):
382-
return try settings(forSwiftFile: path, td)
383-
case (.clang, .swift):
384-
return nil
385-
case (.clang(let td), _):
386-
return try settings(forClangFile: path, language, td)
387-
default:
388-
return nil
389-
}
390-
}
391-
392385
/// Retrieve settings for a package manifest (Package.swift).
393386
private func settings(forPackageManifest path: AbsolutePath) throws -> FileBuildSettings? {
394387
func impl(_ path: AbsolutePath) -> FileBuildSettings? {
@@ -408,12 +401,24 @@ extension SwiftPMWorkspace {
408401
}
409402

410403
/// Retrieve settings for a given header file.
404+
///
405+
/// This finds the target the header belongs to based on its location in the file system, retrieves the build settings
406+
/// for any file within that target and generates compiler arguments by replacing that picked file with the header
407+
/// file.
408+
/// This is safe because all files within one target have the same build settings except for reference to the file
409+
/// itself, which we are replacing.
411410
private func settings(forHeader path: AbsolutePath, _ language: Language) throws -> FileBuildSettings? {
412411
func impl(_ path: AbsolutePath) throws -> FileBuildSettings? {
413412
var dir = path.parentDirectory
414413
while !dir.isRoot {
415-
if let td = sourceDirToTarget[dir] {
416-
return try settings(for: path, language, td)
414+
if let buildTarget = sourceDirToTarget[dir] {
415+
if let sourceFile = buildTarget.sources.first {
416+
return FileBuildSettings(
417+
compilerArguments: try buildTarget.compileArguments(for: sourceFile),
418+
workingDirectory: workspacePath.pathString
419+
).patching(newFile: path.pathString, originalFile: sourceFile.absoluteString)
420+
}
421+
return nil
417422
}
418423
dir = dir.parentDirectory
419424
}
@@ -427,103 +432,6 @@ extension SwiftPMWorkspace {
427432
let canonicalPath = try resolveSymlinks(path)
428433
return try canonicalPath == path ? nil : impl(canonicalPath)
429434
}
430-
431-
/// Retrieve settings for the given swift file, which is part of a known target build description.
432-
public func settings(
433-
forSwiftFile path: AbsolutePath,
434-
_ td: SwiftTargetBuildDescription
435-
) throws -> FileBuildSettings {
436-
// FIXME: this is re-implementing llbuild's constructCommandLineArgs.
437-
var args: [String] = [
438-
"-module-name",
439-
td.target.c99name,
440-
"-incremental",
441-
"-emit-dependencies",
442-
"-emit-module",
443-
"-emit-module-path",
444-
buildPath.appending(component: "\(td.target.c99name).swiftmodule").pathString,
445-
// -output-file-map <path>
446-
]
447-
if td.target.type == .library || td.target.type == .test {
448-
args += ["-parse-as-library"]
449-
}
450-
args += ["-c"]
451-
args += td.sources.map { $0.pathString }
452-
args += ["-I", td.moduleOutputPath.parentDirectory.pathString]
453-
args += try td.compileArguments()
454-
455-
return FileBuildSettings(
456-
compilerArguments: args,
457-
workingDirectory: workspacePath.pathString
458-
)
459-
}
460-
461-
/// Retrieve settings for the given C-family language file, which is part of a known target build
462-
/// description.
463-
///
464-
/// - Note: language must be a C-family language.
465-
public func settings(
466-
forClangFile path: AbsolutePath,
467-
_ language: Language,
468-
_ td: ClangTargetBuildDescription
469-
) throws -> FileBuildSettings {
470-
// FIXME: this is re-implementing things from swiftpm's createClangCompileTarget
471-
472-
var args = try td.basicArguments()
473-
474-
let nativePath: AbsolutePath =
475-
try URL(fileURLWithPath: path.pathString).withUnsafeFileSystemRepresentation {
476-
try AbsolutePath(validating: String(cString: $0!))
477-
}
478-
let compilePath = try td.compilePaths().first(where: { $0.source == nativePath })
479-
if let compilePath = compilePath {
480-
args += [
481-
"-MD",
482-
"-MT",
483-
"dependencies",
484-
"-MF",
485-
compilePath.deps.pathString,
486-
]
487-
}
488-
489-
switch language {
490-
case .c:
491-
if let std = td.clangTarget.cLanguageStandard {
492-
args += ["-std=\(std)"]
493-
}
494-
case .cpp:
495-
if let std = td.clangTarget.cxxLanguageStandard {
496-
args += ["-std=\(std)"]
497-
}
498-
default:
499-
break
500-
}
501-
502-
if let compilePath = compilePath {
503-
args += [
504-
"-c",
505-
compilePath.source.pathString,
506-
"-o",
507-
compilePath.object.pathString,
508-
]
509-
} else if path.extension == "h" {
510-
args += ["-c"]
511-
if let xflag = language.xflagHeader {
512-
args += ["-x", xflag]
513-
}
514-
args += [path.pathString]
515-
} else {
516-
args += [
517-
"-c",
518-
path.pathString,
519-
]
520-
}
521-
522-
return FileBuildSettings(
523-
compilerArguments: args,
524-
workingDirectory: workspacePath.pathString
525-
)
526-
}
527435
}
528436

529437
/// Find a Swift Package root directory that contains the given path, if any.

0 commit comments

Comments
 (0)