@@ -40,7 +40,7 @@ public final class Core: Sendable {
4040 /// Get a configured instance of the core.
4141 ///
4242 /// - 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 ? {
4444 // Enable macro expression interning during loading.
4545 return await MacroNamespace . withExpressionInterningEnabled {
4646 let hostOperatingSystem : OperatingSystem
@@ -59,9 +59,9 @@ public final class Core: Sendable {
5959 await extraPluginRegistration ( [ ] )
6060 #endif
6161
62- let resolvedDeveloperPath : String
62+ let resolvedDeveloperPath : DeveloperPath
6363 do {
64- if let resolved = developerPath? . nilIfEmpty ? . str {
64+ if let resolved = developerPath {
6565 resolvedDeveloperPath = resolved
6666 } else {
6767 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 {
7070 delegate. error ( " Could not determine path to developer directory because no extensions provided a fallback value " )
7171 return nil
7272 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+ }
7479 default :
7580 delegate. error ( " Could not determine path to developer directory because multiple extensions provided conflicting fallback values: \( values. sorted ( ) . map { $0. str } . joined ( separator: " , " ) ) " )
7681 return nil
@@ -169,8 +174,26 @@ public final class Core: Sendable {
169174
170175 public let pluginManager : PluginManager
171176
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+
172195 /// The path to the "Developer" directory.
173- public let developerPath : Path
196+ public let developerPath : DeveloperPath
174197
175198 /// Additional search paths to be used when looking up resource bundles.
176199 public let resourceSearchPaths : [ Path ]
@@ -205,31 +228,39 @@ public final class Core: Sendable {
205228
206229 public let connectionMode : ServiceHostConnectionMode
207230
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 {
209232 self . delegate = delegate
210233 self . hostOperatingSystem = hostOperatingSystem
211234 self . pluginManager = pluginManager
212- self . developerPath = Path ( developerPath)
235+ self . developerPath = developerPath
213236 self . resourceSearchPaths = resourceSearchPaths
214237 self . inferiorProductsPath = inferiorProductsPath
215238 self . additionalContentPaths = additionalContentPaths
216239 self . buildServiceModTime = buildServiceModTime
217240 self . connectionMode = connectionMode
218241 self . environment = environment
219242
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 " )
221246
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
227252
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
233264 self . xcodeVersion = Version ( 99 , 99 , 99 )
234265 self . xcodeProductBuildVersion = ProductBuildVersion ( major: 99 , train: " T " , build: 999 )
235266 self . xcodeProductBuildVersionString = xcodeProductBuildVersion. description
@@ -242,7 +273,17 @@ public final class Core: Sendable {
242273 self . toolchainPaths = {
243274 var toolchainPaths = [ ( Path, strict: Bool) ] ( )
244275
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+ }
246287
247288 // FIXME: We should support building the toolchain locally (for `inferiorProductsPath`).
248289
@@ -372,12 +413,14 @@ public final class Core: Sendable {
372413 public func lookupCASPlugin( ) -> ToolchainCASPlugin ? {
373414 return casPlugin. withLock { casPlugin in
374415 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 " )
377419 let plugin = try ? ToolchainCASPlugin ( dylib: pluginPath)
378420 casPlugin = plugin
379- } else {
421+ case . swiftToolchain , . fallback :
380422 // Unimplemented
423+ break
381424 }
382425 }
383426 return casPlugin
@@ -401,8 +444,19 @@ public final class Core: Sendable {
401444 if let onlySearchAdditionalPlatformPaths = getEnvironmentVariable ( " XCODE_ONLY_EXTRA_PLATFORM_FOLDERS " ) , onlySearchAdditionalPlatformPaths. boolValue {
402445 searchPaths = [ ]
403446 } 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+ }
406460 }
407461 if let additionalPlatformSearchPaths = getEnvironmentVariable ( " XCODE_EXTRA_PLATFORM_FOLDERS " ) {
408462 for searchPath in additionalPlatformSearchPaths. split ( separator: Path . pathEnvironmentSeparator) {
@@ -690,7 +744,7 @@ struct CoreRegistryDelegate : PlatformRegistryDelegate, SDKRegistryDelegate, Spe
690744 core. pluginManager
691745 }
692746
693- var developerPath : Path {
747+ var developerPath : Core . DeveloperPath {
694748 core. developerPath
695749 }
696750}
0 commit comments