@@ -40,7 +40,7 @@ public final class Core: Sendable {
40
40
/// Get a configured instance of the core.
41
41
///
42
42
/// - returns: An initialized Core instance on which all discovery and loading will have been completed. If there are errors during that process, they will be logged to `stderr` and no instance will be returned. Otherwise, the initialized object is returned.
43
- public static func getInitializedCore( _ delegate: any CoreDelegate , pluginManager: PluginManager , developerPath: Path ? = nil , resourceSearchPaths: [ Path ] = [ ] , inferiorProductsPath: Path ? = nil , extraPluginRegistration: @PluginExtensionSystemActor ( _ pluginPaths: [ Path ] ) -> Void = { _ in } , additionalContentPaths: [ Path ] = [ ] , environment: [ String : String ] = [ : ] , buildServiceModTime: Date , connectionMode: ServiceHostConnectionMode ) async -> Core ? {
43
+ public static func getInitializedCore( _ delegate: any CoreDelegate , pluginManager: PluginManager , developerPath: DeveloperPath ? = nil , resourceSearchPaths: [ Path ] = [ ] , inferiorProductsPath: Path ? = nil , extraPluginRegistration: @PluginExtensionSystemActor ( _ pluginPaths: [ Path ] ) -> Void = { _ in } , additionalContentPaths: [ Path ] = [ ] , environment: [ String : String ] = [ : ] , buildServiceModTime: Date , connectionMode: ServiceHostConnectionMode ) async -> Core ? {
44
44
// Enable macro expression interning during loading.
45
45
return await MacroNamespace . withExpressionInterningEnabled {
46
46
let hostOperatingSystem : OperatingSystem
@@ -59,9 +59,9 @@ public final class Core: Sendable {
59
59
await extraPluginRegistration ( [ ] )
60
60
#endif
61
61
62
- let resolvedDeveloperPath : String
62
+ let resolvedDeveloperPath : DeveloperPath
63
63
do {
64
- if let resolved = developerPath? . nilIfEmpty ? . str {
64
+ if let resolved = developerPath {
65
65
resolvedDeveloperPath = resolved
66
66
} else {
67
67
let values = try await Set ( pluginManager. extensions ( of: DeveloperDirectoryExtensionPoint . self) . asyncMap { try await $0. fallbackDeveloperDirectory ( hostOperatingSystem: hostOperatingSystem) } ) . compactMap { $0 }
@@ -70,7 +70,12 @@ public final class Core: Sendable {
70
70
delegate. error ( " Could not determine path to developer directory because no extensions provided a fallback value " )
71
71
return nil
72
72
case 1 :
73
- resolvedDeveloperPath = values [ 0 ] . str
73
+ let path = values [ 0 ]
74
+ if path. str. hasSuffix ( " .app/Contents/Developer " ) {
75
+ resolvedDeveloperPath = . xcode( path)
76
+ } else {
77
+ resolvedDeveloperPath = . fallback( values [ 0 ] )
78
+ }
74
79
default :
75
80
delegate. error ( " Could not determine path to developer directory because multiple extensions provided conflicting fallback values: \( values. sorted ( ) . map { $0. str } . joined ( separator: " , " ) ) " )
76
81
return nil
@@ -169,8 +174,26 @@ public final class Core: Sendable {
169
174
170
175
public let pluginManager : PluginManager
171
176
177
+ public enum DeveloperPath : Sendable , Hashable {
178
+ // A path to an Xcode install's "/Contents/Developer" directory
179
+ case xcode( Path )
180
+
181
+ // A path to the root of a Swift toolchain, optionally paired with the developer path of an installed Xcode
182
+ case swiftToolchain( Path , xcodeDeveloperPath: Path ? )
183
+
184
+ // A fallback resolved path.
185
+ case fallback( Path )
186
+
187
+ public var path : Path {
188
+ switch self {
189
+ case . xcode( let path) , . swiftToolchain( let path, xcodeDeveloperPath: _) , . fallback( let path) :
190
+ return path
191
+ }
192
+ }
193
+ }
194
+
172
195
/// The path to the "Developer" directory.
173
- public let developerPath : Path
196
+ public let developerPath : DeveloperPath
174
197
175
198
/// Additional search paths to be used when looking up resource bundles.
176
199
public let resourceSearchPaths : [ Path ]
@@ -205,31 +228,39 @@ public final class Core: Sendable {
205
228
206
229
public let connectionMode : ServiceHostConnectionMode
207
230
208
- @_spi ( Testing) public init ( delegate: any CoreDelegate , hostOperatingSystem: OperatingSystem , pluginManager: PluginManager , developerPath: String , resourceSearchPaths: [ Path ] , inferiorProductsPath: Path ? , additionalContentPaths: [ Path ] , environment: [ String : String ] , buildServiceModTime: Date , connectionMode: ServiceHostConnectionMode ) async throws {
231
+ @_spi ( Testing) public init ( delegate: any CoreDelegate , hostOperatingSystem: OperatingSystem , pluginManager: PluginManager , developerPath: DeveloperPath , resourceSearchPaths: [ Path ] , inferiorProductsPath: Path ? , additionalContentPaths: [ Path ] , environment: [ String : String ] , buildServiceModTime: Date , connectionMode: ServiceHostConnectionMode ) async throws {
209
232
self . delegate = delegate
210
233
self . hostOperatingSystem = hostOperatingSystem
211
234
self . pluginManager = pluginManager
212
- self . developerPath = Path ( developerPath)
235
+ self . developerPath = developerPath
213
236
self . resourceSearchPaths = resourceSearchPaths
214
237
self . inferiorProductsPath = inferiorProductsPath
215
238
self . additionalContentPaths = additionalContentPaths
216
239
self . buildServiceModTime = buildServiceModTime
217
240
self . connectionMode = connectionMode
218
241
self . environment = environment
219
242
220
- let versionPath = self . developerPath. dirname. join ( " version.plist " )
243
+ switch developerPath {
244
+ case . xcode( let path) :
245
+ let versionPath = path. dirname. join ( " version.plist " )
221
246
222
- // Load the containing app (Xcode or Playgrounds) version information, if available.
223
- //
224
- // We make this optional so tests do not need to provide it.
225
- if let info = try XcodeVersionInfo . versionInfo ( versionPath: versionPath) {
226
- self . xcodeVersion = info. shortVersion
247
+ // Load the containing app (Xcode or Playgrounds) version information, if available.
248
+ //
249
+ // We make this optional so tests do not need to provide it.
250
+ if let info = try XcodeVersionInfo . versionInfo ( versionPath: versionPath) {
251
+ self . xcodeVersion = info. shortVersion
227
252
228
- // If the ProductBuildVersion key is missing, we use "UNKNOWN" as the value.
229
- self . xcodeProductBuildVersion = info. productBuildVersion ?? ProductBuildVersion ( major: 0 , train: " A " , build: 0 , buildSuffix: " " )
230
- self . xcodeProductBuildVersionString = info. productBuildVersion? . description ?? " UNKNOWN "
231
- } else {
232
- // Set an arbitrary version for testing purposes.
253
+ // If the ProductBuildVersion key is missing, we use "UNKNOWN" as the value.
254
+ self . xcodeProductBuildVersion = info. productBuildVersion ?? ProductBuildVersion ( major: 0 , train: " A " , build: 0 , buildSuffix: " " )
255
+ self . xcodeProductBuildVersionString = info. productBuildVersion? . description ?? " UNKNOWN "
256
+ } else {
257
+ // Set an arbitrary version for testing purposes.
258
+ self . xcodeVersion = Version ( 99 , 99 , 99 )
259
+ self . xcodeProductBuildVersion = ProductBuildVersion ( major: 99 , train: " T " , build: 999 )
260
+ self . xcodeProductBuildVersionString = xcodeProductBuildVersion. description
261
+ }
262
+ case . swiftToolchain, . fallback:
263
+ // FIXME: Eliminate this requirment for Swift toolchains
233
264
self . xcodeVersion = Version ( 99 , 99 , 99 )
234
265
self . xcodeProductBuildVersion = ProductBuildVersion ( major: 99 , train: " T " , build: 999 )
235
266
self . xcodeProductBuildVersionString = xcodeProductBuildVersion. description
@@ -242,7 +273,17 @@ public final class Core: Sendable {
242
273
self . toolchainPaths = {
243
274
var toolchainPaths = [ ( Path, strict: Bool) ] ( )
244
275
245
- toolchainPaths. append ( ( Path ( developerPath) . join ( " Toolchains " ) , strict: developerPath. hasSuffix ( " .app/Contents/Developer " ) ) )
276
+ switch developerPath {
277
+ case . xcode( let path) :
278
+ toolchainPaths. append ( ( path. join ( " Toolchains " ) , strict: path. str. hasSuffix ( " .app/Contents/Developer " ) ) )
279
+ case . swiftToolchain( let path, xcodeDeveloperPath: let xcodeDeveloperPath) :
280
+ toolchainPaths. append ( ( path, strict: true ) )
281
+ if let xcodeDeveloperPath {
282
+ toolchainPaths. append ( ( xcodeDeveloperPath. join ( " Toolchains " ) , strict: xcodeDeveloperPath. str. hasSuffix ( " .app/Contents/Developer " ) ) )
283
+ }
284
+ case . fallback( let path) :
285
+ toolchainPaths. append ( ( path. join ( " Toolchains " ) , strict: false ) )
286
+ }
246
287
247
288
// FIXME: We should support building the toolchain locally (for `inferiorProductsPath`).
248
289
@@ -372,12 +413,14 @@ public final class Core: Sendable {
372
413
public func lookupCASPlugin( ) -> ToolchainCASPlugin ? {
373
414
return casPlugin. withLock { casPlugin in
374
415
if casPlugin == nil {
375
- if hostOperatingSystem == . macOS {
376
- let pluginPath = developerPath. join ( " usr/lib/libToolchainCASPlugin.dylib " )
416
+ switch developerPath {
417
+ case . xcode( let path) :
418
+ let pluginPath = path. join ( " usr/lib/libToolchainCASPlugin.dylib " )
377
419
let plugin = try ? ToolchainCASPlugin ( dylib: pluginPath)
378
420
casPlugin = plugin
379
- } else {
421
+ case . swiftToolchain , . fallback :
380
422
// Unimplemented
423
+ break
381
424
}
382
425
}
383
426
return casPlugin
@@ -401,8 +444,19 @@ public final class Core: Sendable {
401
444
if let onlySearchAdditionalPlatformPaths = getEnvironmentVariable ( " XCODE_ONLY_EXTRA_PLATFORM_FOLDERS " ) , onlySearchAdditionalPlatformPaths. boolValue {
402
445
searchPaths = [ ]
403
446
} else {
404
- let platformsDir = self . developerPath. join ( " Platforms " )
405
- searchPaths = [ platformsDir]
447
+ switch developerPath {
448
+ case . xcode( let path) :
449
+ let platformsDir = path. join ( " Platforms " )
450
+ searchPaths = [ platformsDir]
451
+ case . swiftToolchain( _, let xcodeDeveloperDirectoryPath) :
452
+ if let xcodeDeveloperDirectoryPath {
453
+ searchPaths = [ xcodeDeveloperDirectoryPath. join ( " Platforms " ) ]
454
+ } else {
455
+ searchPaths = [ ]
456
+ }
457
+ case . fallback:
458
+ searchPaths = [ ]
459
+ }
406
460
}
407
461
if let additionalPlatformSearchPaths = getEnvironmentVariable ( " XCODE_EXTRA_PLATFORM_FOLDERS " ) {
408
462
for searchPath in additionalPlatformSearchPaths. split ( separator: Path . pathEnvironmentSeparator) {
@@ -690,7 +744,7 @@ struct CoreRegistryDelegate : PlatformRegistryDelegate, SDKRegistryDelegate, Spe
690
744
core. pluginManager
691
745
}
692
746
693
- var developerPath : Path {
747
+ var developerPath : Core . DeveloperPath {
694
748
core. developerPath
695
749
}
696
750
}
0 commit comments