@@ -22,6 +22,7 @@ import PackageModel
22
22
import SKCore
23
23
import SKSupport
24
24
import SourceControl
25
+ import SourceKitLSPAPI
25
26
import Workspace
26
27
27
28
import struct Basics. AbsolutePath
@@ -43,6 +44,12 @@ public enum ReloadPackageStatus {
43
44
case end
44
45
}
45
46
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
+
46
53
/// Same as `toolchainRegistry.default`.
47
54
///
48
55
/// Needed to work around a compiler crash that prevents us from accessing `toolchainRegistry.default` in
@@ -83,8 +90,8 @@ public actor SwiftPMWorkspace {
83
90
public let buildParameters : BuildParameters
84
91
let fileSystem : FileSystem
85
92
86
- var fileToTarget : [ AbsolutePath : TargetBuildDescription ] = [ : ]
87
- var sourceDirToTarget : [ AbsolutePath : TargetBuildDescription ] = [ : ]
93
+ var fileToTarget : [ AbsolutePath : SwiftBuildTarget ] = [ : ]
94
+ var sourceDirToTarget : [ AbsolutePath : SwiftBuildTarget ] = [ : ]
88
95
89
96
/// The URIs for which the delegate has registered for change notifications,
90
97
/// mapped to the language the delegate specified when registering for change notifications.
@@ -215,19 +222,20 @@ extension SwiftPMWorkspace {
215
222
fileSystem: fileSystem,
216
223
observabilityScope: observabilitySystem. topScope
217
224
)
225
+ let buildDescription = BuildDescription ( buildPlan: plan)
218
226
219
227
/// Make sure to execute any throwing statements before setting any
220
228
/// properties because otherwise we might end up in an inconsistent state
221
229
/// with only some properties modified.
222
230
self . packageGraph = packageGraph
223
231
224
- self . fileToTarget = [ AbsolutePath: TargetBuildDescription ] (
232
+ self . fileToTarget = [ AbsolutePath: SwiftBuildTarget ] (
225
233
packageGraph. allTargets. flatMap { target in
226
234
return target. sources. paths. compactMap {
227
- guard let td = plan . targetMap [ target. id ] else {
235
+ guard let buildTarget = buildDescription . getBuildTarget ( for : target) else {
228
236
return nil
229
237
}
230
- return ( key: $0, value: td )
238
+ return ( key: $0, value: buildTarget )
231
239
}
232
240
} ,
233
241
uniquingKeysWith: { td, _ in
@@ -236,12 +244,12 @@ extension SwiftPMWorkspace {
236
244
}
237
245
)
238
246
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 {
242
250
return nil
243
251
}
244
- return ( key: target. sources. root, value: td )
252
+ return ( key: target. sources. root, value: buildTarget )
245
253
} ,
246
254
uniquingKeysWith: { td, _ in
247
255
// FIXME: is there a preferred target?
@@ -275,14 +283,6 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
275
283
276
284
public var indexPrefixMappings : [ PathPrefixMapping ] { return [ ] }
277
285
278
- /// **Public for testing only**
279
- public func _settings(
280
- for uri: DocumentURI ,
281
- _ language: Language
282
- ) throws -> FileBuildSettings ? {
283
- try self . buildSettings ( for: uri, language: language)
284
- }
285
-
286
286
public func buildSettings( for uri: DocumentURI , language: Language ) throws -> FileBuildSettings ? {
287
287
guard let url = uri. fileURL else {
288
288
// We can't determine build settings for non-file URIs.
@@ -292,8 +292,11 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
292
292
return nil
293
293
}
294
294
295
- if let td = try targetDescription ( for: path) {
296
- 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
+ )
297
300
}
298
301
299
302
if path. basename == " Package.swift " {
@@ -318,7 +321,7 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
318
321
}
319
322
320
323
/// Returns the resolved target description for the given file, if one is known.
321
- private func targetDescription ( for file: AbsolutePath ) throws -> TargetBuildDescription ? {
324
+ private func buildTarget ( for file: AbsolutePath ) throws -> SwiftBuildTarget ? {
322
325
if let td = fileToTarget [ file] {
323
326
return td
324
327
}
@@ -367,7 +370,7 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
367
370
guard let fileUrl = uri. fileURL else {
368
371
return . unhandled
369
372
}
370
- if ( try ? targetDescription ( for: AbsolutePath ( validating: fileUrl. path) ) ) != nil {
373
+ if ( try ? buildTarget ( for: AbsolutePath ( validating: fileUrl. path) ) ) != nil {
371
374
return . handled
372
375
} else {
373
376
return . unhandled
@@ -379,24 +382,6 @@ extension SwiftPMWorkspace {
379
382
380
383
// MARK: Implementation details
381
384
382
- /// Retrieve settings for the given file, which is part of a known target build description.
383
- public func settings(
384
- for path: AbsolutePath ,
385
- _ language: Language ,
386
- _ td: TargetBuildDescription
387
- ) throws -> FileBuildSettings ? {
388
- switch ( td, language) {
389
- case ( . swift( let td) , . swift) :
390
- return try settings ( forSwiftFile: path, td)
391
- case ( . clang, . swift) :
392
- return nil
393
- case ( . clang( let td) , _) :
394
- return try settings ( forClangFile: path, language, td)
395
- default :
396
- return nil
397
- }
398
- }
399
-
400
385
/// Retrieve settings for a package manifest (Package.swift).
401
386
private func settings( forPackageManifest path: AbsolutePath ) throws -> FileBuildSettings ? {
402
387
func impl( _ path: AbsolutePath ) -> FileBuildSettings ? {
@@ -416,12 +401,24 @@ extension SwiftPMWorkspace {
416
401
}
417
402
418
403
/// 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.
419
410
private func settings( forHeader path: AbsolutePath , _ language: Language ) throws -> FileBuildSettings ? {
420
411
func impl( _ path: AbsolutePath ) throws -> FileBuildSettings ? {
421
412
var dir = path. parentDirectory
422
413
while !dir. isRoot {
423
- if let td = sourceDirToTarget [ dir] {
424
- 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
425
422
}
426
423
dir = dir. parentDirectory
427
424
}
@@ -435,103 +432,6 @@ extension SwiftPMWorkspace {
435
432
let canonicalPath = try resolveSymlinks ( path)
436
433
return try canonicalPath == path ? nil : impl ( canonicalPath)
437
434
}
438
-
439
- /// Retrieve settings for the given swift file, which is part of a known target build description.
440
- public func settings(
441
- forSwiftFile path: AbsolutePath ,
442
- _ td: SwiftTargetBuildDescription
443
- ) throws -> FileBuildSettings {
444
- // FIXME: this is re-implementing llbuild's constructCommandLineArgs.
445
- var args : [ String ] = [
446
- " -module-name " ,
447
- td. target. c99name,
448
- " -incremental " ,
449
- " -emit-dependencies " ,
450
- " -emit-module " ,
451
- " -emit-module-path " ,
452
- buildPath. appending ( component: " \( td. target. c99name) .swiftmodule " ) . pathString,
453
- // -output-file-map <path>
454
- ]
455
- if td. target. type == . library || td. target. type == . test {
456
- args += [ " -parse-as-library " ]
457
- }
458
- args += [ " -c " ]
459
- args += td. sources. map { $0. pathString }
460
- args += [ " -I " , td. moduleOutputPath. parentDirectory. pathString]
461
- args += try td. compileArguments ( )
462
-
463
- return FileBuildSettings (
464
- compilerArguments: args,
465
- workingDirectory: workspacePath. pathString
466
- )
467
- }
468
-
469
- /// Retrieve settings for the given C-family language file, which is part of a known target build
470
- /// description.
471
- ///
472
- /// - Note: language must be a C-family language.
473
- public func settings(
474
- forClangFile path: AbsolutePath ,
475
- _ language: Language ,
476
- _ td: ClangTargetBuildDescription
477
- ) throws -> FileBuildSettings {
478
- // FIXME: this is re-implementing things from swiftpm's createClangCompileTarget
479
-
480
- var args = try td. basicArguments ( )
481
-
482
- let nativePath : AbsolutePath =
483
- try URL ( fileURLWithPath: path. pathString) . withUnsafeFileSystemRepresentation {
484
- try AbsolutePath ( validating: String ( cString: $0!) )
485
- }
486
- let compilePath = try td. compilePaths ( ) . first ( where: { $0. source == nativePath } )
487
- if let compilePath = compilePath {
488
- args += [
489
- " -MD " ,
490
- " -MT " ,
491
- " dependencies " ,
492
- " -MF " ,
493
- compilePath. deps. pathString,
494
- ]
495
- }
496
-
497
- switch language {
498
- case . c:
499
- if let std = td. clangTarget. cLanguageStandard {
500
- args += [ " -std= \( std) " ]
501
- }
502
- case . cpp:
503
- if let std = td. clangTarget. cxxLanguageStandard {
504
- args += [ " -std= \( std) " ]
505
- }
506
- default :
507
- break
508
- }
509
-
510
- if let compilePath = compilePath {
511
- args += [
512
- " -c " ,
513
- compilePath. source. pathString,
514
- " -o " ,
515
- compilePath. object. pathString,
516
- ]
517
- } else if path. extension == " h " {
518
- args += [ " -c " ]
519
- if let xflag = language. xflagHeader {
520
- args += [ " -x " , xflag]
521
- }
522
- args += [ path. pathString]
523
- } else {
524
- args += [
525
- " -c " ,
526
- path. pathString,
527
- ]
528
- }
529
-
530
- return FileBuildSettings (
531
- compilerArguments: args,
532
- workingDirectory: workspacePath. pathString
533
- )
534
- }
535
435
}
536
436
537
437
/// Find a Swift Package root directory that contains the given path, if any.
0 commit comments