diff --git a/Sources/BuildSystemIntegration/BuildSystemManager.swift b/Sources/BuildSystemIntegration/BuildSystemManager.swift index def53ffc..3cf801c1 100644 --- a/Sources/BuildSystemIntegration/BuildSystemManager.swift +++ b/Sources/BuildSystemIntegration/BuildSystemManager.swift @@ -21,6 +21,7 @@ import SwiftExtensions import ToolchainRegistry import struct TSCBasic.AbsolutePath +import func TSCBasic.resolveSymlinks fileprivate typealias RequestCache = Cache @@ -78,6 +79,18 @@ fileprivate extension BuildTarget { } } +fileprivate extension DocumentURI { + /// If this is a file URI pointing to a symlink, return the realpath of the URI, otherwise return `nil`. + var symlinkTarget: DocumentURI? { + guard let fileUrl = fileURL, let path = AbsolutePath(validatingOrNil: fileUrl.path), + let symlinksResolved = try? TSCBasic.resolveSymlinks(path), symlinksResolved != path + else { + return nil + } + return DocumentURI(symlinksResolved.asURL) + } +} + /// Entry point for all build system queries. package actor BuildSystemManager: QueueBasedMessageHandler { package static let signpostLoggingCategory: String = "build-system-manager-message-handling" @@ -570,9 +583,8 @@ package actor BuildSystemManager: QueueBasedMessageHandler { return nil } if connectionToBuildSystem == nil { - // If there is no build system and we only have the fallback build system, - // we will never get real build settings. Consider the build settings - // non-fallback. + // If there is no build system and we only have the fallback build system, we will never get real build settings. + // Consider the build settings non-fallback. settings.isFallback = false } return settings @@ -588,16 +600,37 @@ package actor BuildSystemManager: QueueBasedMessageHandler { for document: DocumentURI, language: Language ) async -> FileBuildSettings? { - let mainFile = await mainFile(for: document, language: language) - let target = await canonicalTarget(for: mainFile) - guard var settings = await buildSettings(for: mainFile, in: target, language: language) else { + func mainFileAndSettings( + basedOn document: DocumentURI + ) async -> (mainFile: DocumentURI, settings: FileBuildSettings)? { + let mainFile = await self.mainFile(for: document, language: language) + let target = await canonicalTarget(for: mainFile) + guard let settings = await buildSettings(for: mainFile, in: target, language: language) else { + return nil + } + return (mainFile, settings) + } + + var settings: FileBuildSettings? + var mainFile: DocumentURI? + if let mainFileAndSettings = await mainFileAndSettings(basedOn: document) { + (mainFile, settings) = mainFileAndSettings + } + if settings?.isFallback ?? true, let symlinkTarget = document.symlinkTarget, + let mainFileAndSettings = await mainFileAndSettings(basedOn: symlinkTarget) + { + (mainFile, settings) = mainFileAndSettings + } + guard var settings, let mainFile else { return nil } + if mainFile != document { // If the main file isn't the file itself, we need to patch the build settings // to reference `document` instead of `mainFile`. settings = settings.patching(newFile: document, originalFile: mainFile) } + await BuildSettingsLogger.shared.log(settings: settings, for: document) return settings } diff --git a/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift b/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift index ed6e74aa..25509624 100644 --- a/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift +++ b/Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift @@ -204,9 +204,6 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem { /// The entry point via with we can access the `SourceKitLSPAPI` provided by SwiftPM. private var buildDescription: SourceKitLSPAPI.BuildDescription? - /// Maps source and header files to the target that include them. - private var fileToTargets: [DocumentURI: Set] = [:] - /// Maps target ids to their SwiftPM build target. private var swiftPMTargets: [BuildTargetIdentifier: SwiftBuildTarget] = [:] @@ -405,7 +402,6 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem { /// with only some properties modified. self.swiftPMTargets = [:] - self.fileToTargets = [:] self.targetDependencies = [:] buildDescription.traverseModules { buildTarget, parent, depth in @@ -413,11 +409,6 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem { guard let targetIdentifier else { return } - if swiftPMTargets[targetIdentifier] == nil { - for source in buildTarget.sources + buildTarget.headers { - fileToTargets[DocumentURI(source), default: []].insert(targetIdentifier) - } - } if let parent, let parentIdentifier = orLog("Getting parent build target identifier", { try BuildTargetIdentifier(parent) }) { @@ -576,27 +567,6 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem { ) } - package func targets(for uri: DocumentURI) -> [BuildTargetIdentifier] { - guard let url = uri.fileURL, let path = try? AbsolutePath(validating: url.path) else { - // We can't determine targets for non-file URIs. - return [] - } - - let targets = buildTargets(for: uri) - if !targets.isEmpty { - // Sort targets to get deterministic ordering. The actual order does not matter. - return targets.sorted { $0.uri.stringValue < $1.uri.stringValue } - } - - if path.basename == "Package.swift" - && projectRoot == (try? TSCBasic.resolveSymlinks(TSCBasic.AbsolutePath(path.parentDirectory))) - { - return [BuildTargetIdentifier.forPackageManifest] - } - - return [] - } - package func waitForBuildSystemUpdates(request: WorkspaceWaitForBuildSystemUpdatesRequest) async -> VoidResponse { await self.packageLoadingQueue.async {}.valuePropagatingCancellation return VoidResponse() @@ -707,23 +677,6 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem { } } - /// Returns the resolved target descriptions for the given file, if one is known. - private func buildTargets(for file: DocumentURI) -> Set { - if let targets = fileToTargets[file] { - return targets - } - - if let fileURL = file.fileURL, - let realpath = try? resolveSymlinks(AbsolutePath(validating: fileURL.path)), - let targets = fileToTargets[DocumentURI(realpath.asURL)] - { - fileToTargets[file] = targets - return targets - } - - return [] - } - /// An event is relevant if it modifies a file that matches one of the file rules used by the SwiftPM workspace. private func fileEventShouldTriggerPackageReload(event: FileEvent) -> Bool { guard let fileURL = event.uri.fileURL else { diff --git a/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift b/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift index c277c2ec..d7e631cb 100644 --- a/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift +++ b/Tests/BuildSystemIntegrationTests/SwiftPMBuildSystemTests.swift @@ -23,28 +23,24 @@ import TSCBasic import ToolchainRegistry import XCTest +import struct Basics.Triple import struct PackageModel.BuildFlags #if canImport(SPMBuildCore) @preconcurrency import SPMBuildCore #endif -fileprivate extension SwiftPMBuildSystem { - func buildSettings(for uri: DocumentURI, language: Language) async throws -> TextDocumentSourceKitOptionsResponse? { - guard let target = self.targets(for: uri).only else { - return nil - } - return try await sourceKitOptions( - request: TextDocumentSourceKitOptionsRequest( - textDocument: TextDocumentIdentifier(uri), - target: target, - language: language - ) +private var hostTriple: Triple { + get async throws { + let toolchain = try await unwrap( + ToolchainRegistry.forTesting.preferredToolchain(containing: [\.clang, \.clangd, \.sourcekitd, \.swift, \.swiftc]) ) - } + let destinationToolchainBinDir = try XCTUnwrap(toolchain.swiftc?.parentDirectory) - func waitForUpToDateBuildGraph() async { - let _: VoidResponse = await self.waitForBuildSystemUpdates(request: WorkspaceWaitForBuildSystemUpdatesRequest()) + let hostSDK = try SwiftSDK.hostSwiftSDK(.init(destinationToolchainBinDir)) + let hostSwiftPMToolchain = try UserToolchain(swiftSDK: hostSDK) + + return hostSwiftPMToolchain.targetTriple } } @@ -109,24 +105,22 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") - let hostTriple = await swiftpmBuildSystem.destinationBuildParameters.triple - let build = buildPath(root: packageRoot, platform: hostTriple.platformBuildPathComponent) + let build = try await buildPath(root: packageRoot, platform: hostTriple.platformBuildPathComponent) - assertEqual(await swiftpmBuildSystem.buildPath, build) - assertNotNil(await swiftpmBuildSystem.indexStorePath) - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + assertNotNil(await buildSystemManager.initializationData?.indexDatabasePath) + assertNotNil(await buildSystemManager.initializationData?.indexStorePath) + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain("-module-name", "lib", arguments: arguments) assertArgumentsContain("-emit-dependencies", arguments: arguments) @@ -141,13 +135,13 @@ final class SwiftPMBuildSystemTests: XCTestCase { let versionString = PackageModel.Platform.macOS.oldestSupportedVersion.versionString assertArgumentsContain( "-target", - hostTriple.tripleString(forPlatformVersion: versionString), + try await hostTriple.tripleString(forPlatformVersion: versionString), arguments: arguments ) assertArgumentsContain("-sdk", arguments: arguments) assertArgumentsContain("-F", arguments: arguments, allowMultiple: true) #else - assertArgumentsContain("-target", hostTriple.tripleString, arguments: arguments) + assertArgumentsContain("-target", try await hostTriple.tripleString, arguments: arguments) #endif assertArgumentsContain("-I", build.appending(component: "Modules").pathString, arguments: arguments) @@ -174,24 +168,19 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aPlusSomething = packageRoot.appending(components: "Sources", "lib", "a+something.swift") - let hostTriple = await swiftpmBuildSystem.destinationBuildParameters.triple - let build = buildPath(root: packageRoot, platform: hostTriple.platformBuildPathComponent) - assertEqual(await swiftpmBuildSystem.buildPath, build) - assertNotNil(await swiftpmBuildSystem.indexStorePath) + assertNotNil(await buildSystemManager.initializationData?.indexStorePath) let arguments = try await unwrap( - swiftpmBuildSystem.buildSettings( + buildSystemManager.buildSettingsInferredFromMainFile( for: DocumentURI(URL(string: "file://\(aPlusSomething.asURL.path.replacing("+", with: "%2B"))")!), language: .swift ) @@ -228,7 +217,6 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = tempDir.appending(component: "pkg") - let tr = ToolchainRegistry.forTesting let options = SourceKitLSPOptions.SwiftPMOptions( configuration: .release, @@ -237,22 +225,19 @@ final class SwiftPMBuildSystemTests: XCTestCase { swiftCompilerFlags: ["-typecheck"] ) - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(swiftPM: options), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") - let hostTriple = await swiftpmBuildSystem.destinationBuildParameters.triple - let build = buildPath(root: packageRoot, options: options, platform: hostTriple.platformBuildPathComponent) - assertEqual(await swiftpmBuildSystem.buildPath, build) - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain("-typecheck", arguments: arguments) assertArgumentsContain("-Xcc", "-m32", arguments: arguments) @@ -316,19 +301,18 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = tempDir.appending(component: "pkg") - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let source = try resolveSymlinks(packageRoot.appending(component: "Package.swift")) - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: source.asURI, language: .swift)) - .compilerArguments + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: source.asURI, language: .swift) + ).compilerArguments assertArgumentsContain("-swift-version", "4.2", arguments: arguments) assertArgumentsContain(source.pathString, arguments: arguments) @@ -352,25 +336,25 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") let bswift = packageRoot.appending(components: "Sources", "lib", "b.swift") - let argumentsA = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + let argumentsA = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain(aswift.pathString, arguments: argumentsA) assertArgumentsContain(bswift.pathString, arguments: argumentsA) - let argumentsB = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + let argumentsB = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain(aswift.pathString, arguments: argumentsB) assertArgumentsContain(bswift.pathString, arguments: argumentsB) } @@ -400,20 +384,19 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift") let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift") - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain(aswift.pathString, arguments: arguments) assertArgumentsDoNotContain(bswift.pathString, arguments: arguments) assertArgumentsContain( @@ -424,8 +407,9 @@ final class SwiftPMBuildSystemTests: XCTestCase { arguments: arguments ) - let argumentsB = try await unwrap(swiftpmBuildSystem.buildSettings(for: bswift.asURI, language: .swift)) - .compilerArguments + let argumentsB = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: bswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain(bswift.pathString, arguments: argumentsB) assertArgumentsDoNotContain(aswift.pathString, arguments: argumentsB) assertArgumentsDoNotContain( @@ -454,25 +438,27 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = tempDir.appending(component: "pkg") - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift") let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift") - assertNotNil(try await swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - assertNil(try await swiftpmBuildSystem.buildSettings(for: bswift.asURI, language: .swift)) - assertNil( - try await swiftpmBuildSystem.buildSettings( + assertNotNil(await buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift)) + assertEqual( + await buildSystemManager.buildSettingsInferredFromMainFile(for: bswift.asURI, language: .swift)?.isFallback, + true + ) + assertEqual( + await buildSystemManager.buildSettingsInferredFromMainFile( for: DocumentURI(URL(string: "https://www.apple.com")!), language: .swift - ) + )?.isFallback, + true ) } } @@ -497,27 +483,25 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let acxx = packageRoot.appending(components: "Sources", "lib", "a.cpp") let bcxx = packageRoot.appending(components: "Sources", "lib", "b.cpp") let header = packageRoot.appending(components: "Sources", "lib", "include", "a.h") - let hostTriple = await swiftpmBuildSystem.destinationBuildParameters.triple - let build = buildPath(root: packageRoot, platform: hostTriple.platformBuildPathComponent) + let build = buildPath(root: packageRoot, platform: try await hostTriple.platformBuildPathComponent) - assertEqual(await swiftpmBuildSystem.buildPath, build) - assertNotNil(await swiftpmBuildSystem.indexStorePath) + assertNotNil(await buildSystemManager.initializationData?.indexStorePath) for file in [acxx, header] { - let args = try await unwrap(swiftpmBuildSystem.buildSettings(for: file.asURI, language: .cpp)).compilerArguments + let args = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: file.asURI, language: .cpp) + ).compilerArguments assertArgumentsContain("-std=c++14", arguments: args) @@ -527,13 +511,13 @@ final class SwiftPMBuildSystemTests: XCTestCase { let versionString = PackageModel.Platform.macOS.oldestSupportedVersion.versionString assertArgumentsContain( "-target", - hostTriple.tripleString(forPlatformVersion: versionString), + try await hostTriple.tripleString(forPlatformVersion: versionString), arguments: args ) assertArgumentsContain("-isysroot", arguments: args) assertArgumentsContain("-F", arguments: args, allowMultiple: true) #else - assertArgumentsContain("-target", hostTriple.tripleString, arguments: args) + assertArgumentsContain("-target", try await hostTriple.tripleString, arguments: args) #endif assertArgumentsContain( @@ -578,29 +562,28 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = tempDir.appending(component: "pkg") - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: ToolchainRegistry.forTesting, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments assertArgumentsContain("-target", arguments: arguments) // Only one! - let hostTriple = await swiftpmBuildSystem.destinationBuildParameters.triple #if os(macOS) - assertArgumentsContain( + try await assertArgumentsContain( "-target", hostTriple.tripleString(forPlatformVersion: "10.13"), arguments: arguments ) #else - assertArgumentsContain("-target", hostTriple.tripleString, arguments: arguments) + assertArgumentsContain("-target", try await hostTriple.tripleString, arguments: arguments) #endif } } @@ -628,40 +611,47 @@ final class SwiftPMBuildSystemTests: XCTestCase { withDestinationURL: URL(fileURLWithPath: tempDir.appending(component: "pkg_real").pathString) ) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: XCTUnwrap(SwiftPMBuildSystem.projectRoot(for: packageRoot, options: .testDefault())), - toolchainRegistry: tr, + let projectRoot = try XCTUnwrap(SwiftPMBuildSystem.projectRoot(for: packageRoot, options: .testDefault())) + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: projectRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() - let aswift1 = packageRoot.appending(components: "Sources", "lib", "a.swift") - let aswift2 = - tempDir - .appending(component: "pkg_real") - .appending(components: "Sources", "lib", "a.swift") + let aswiftSymlink = packageRoot.appending(components: "Sources", "lib", "a.swift") + let aswiftReal = try resolveSymlinks(aswiftSymlink) let manifest = packageRoot.appending(components: "Package.swift") - let arguments1 = try await swiftpmBuildSystem.buildSettings(for: aswift1.asURI, language: .swift)? - .compilerArguments - let arguments2 = try await swiftpmBuildSystem.buildSettings(for: aswift2.asURI, language: .swift)? - .compilerArguments - XCTAssertNotNil(arguments1) - XCTAssertNotNil(arguments2) - XCTAssertEqual(arguments1, arguments2) + let argumentsFromSymlink = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswiftSymlink.asURI, language: .swift) + ).compilerArguments + let argumentsFromReal = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswiftReal.asURI, language: .swift) + ).compilerArguments - assertArgumentsDoNotContain(aswift1.pathString, arguments: arguments1 ?? []) - assertArgumentsContain(try resolveSymlinks(aswift1).pathString, arguments: arguments1 ?? []) + // The arguments retrieved from the symlink and the real document should be the same, except that both should + // contain they file the build settings were created. + // FIXME: Or should the build settings always reference the main file? + XCTAssertEqual( + argumentsFromSymlink.filter { $0 != aswiftSymlink.pathString && $0 != aswiftReal.pathString }, + argumentsFromReal.filter { $0 != aswiftSymlink.pathString && $0 != aswiftReal.pathString } + ) - let argsManifest = try await swiftpmBuildSystem.buildSettings(for: manifest.asURI, language: .swift)? - .compilerArguments + assertArgumentsContain(aswiftSymlink.pathString, arguments: argumentsFromSymlink) + assertArgumentsDoNotContain(aswiftReal.pathString, arguments: argumentsFromSymlink) + + assertArgumentsContain(aswiftReal.pathString, arguments: argumentsFromReal) + assertArgumentsDoNotContain(aswiftSymlink.pathString, arguments: argumentsFromReal) + + let argsManifest = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: manifest.asURI, language: .swift) + ).compilerArguments XCTAssertNotNil(argsManifest) - assertArgumentsContain(manifest.pathString, arguments: argsManifest ?? []) - assertArgumentsDoNotContain(try resolveSymlinks(manifest).pathString, arguments: argsManifest ?? []) + assertArgumentsContain(manifest.pathString, arguments: argsManifest) + assertArgumentsDoNotContain(try resolveSymlinks(manifest).pathString, arguments: argsManifest) } } @@ -696,22 +686,25 @@ final class SwiftPMBuildSystemTests: XCTestCase { withDestinationURL: URL(fileURLWithPath: tempDir.appending(component: "pkg_real").pathString) ) - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: XCTUnwrap(SwiftPMBuildSystem.projectRoot(for: symlinkRoot, options: .testDefault())), - toolchainRegistry: ToolchainRegistry.forTesting, + let projectRoot = try XCTUnwrap(SwiftPMBuildSystem.projectRoot(for: symlinkRoot, options: .testDefault())) + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: projectRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() for file in [acpp, ah] { let args = try unwrap( - await swiftpmBuildSystem.buildSettings(for: symlinkRoot.appending(components: file).asURI, language: .cpp)? - .compilerArguments + await buildSystemManager.buildSettingsInferredFromMainFile( + for: symlinkRoot.appending(components: file).asURI, + language: .cpp + )? + .compilerArguments ) - assertArgumentsContain(realRoot.appending(components: file).pathString, arguments: args) - assertArgumentsDoNotContain(symlinkRoot.appending(components: file).pathString, arguments: args) + assertArgumentsDoNotContain(realRoot.appending(components: file).pathString, arguments: args) + assertArgumentsContain(symlinkRoot.appending(components: file).pathString, arguments: args) } } } @@ -734,19 +727,19 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = try resolveSymlinks(tempDir.appending(component: "pkg")) - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ) + .compilerArguments assertArgumentsContain(aswift.pathString, arguments: arguments) XCTAssertNotNil( arguments.firstIndex(where: { @@ -801,24 +794,20 @@ final class SwiftPMBuildSystemTests: XCTestCase { ] ) let packageRoot = tempDir.appending(component: "pkg") - let tr = ToolchainRegistry.forTesting - let swiftpmBuildSystem = try await SwiftPMBuildSystem( - projectRoot: packageRoot, - toolchainRegistry: tr, + let buildSystemManager = await BuildSystemManager( + buildSystemKind: .swiftPM(projectRoot: packageRoot), + toolchainRegistry: .forTesting, options: SourceKitLSPOptions(), - connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"), - testHooks: SwiftPMTestHooks() + buildSystemTestHooks: BuildSystemTestHooks() ) - await swiftpmBuildSystem.waitForUpToDateBuildGraph() + await buildSystemManager.waitForUpToDateBuildGraph() let aswift = packageRoot.appending(components: "Plugins", "MyPlugin", "a.swift") - let hostTriple = await swiftpmBuildSystem.destinationBuildParameters.triple - let build = buildPath(root: packageRoot, platform: hostTriple.platformBuildPathComponent) - assertEqual(await swiftpmBuildSystem.buildPath, build) - assertNotNil(await swiftpmBuildSystem.indexStorePath) - let arguments = try await unwrap(swiftpmBuildSystem.buildSettings(for: aswift.asURI, language: .swift)) - .compilerArguments + assertNotNil(await buildSystemManager.initializationData?.indexStorePath) + let arguments = try await unwrap( + buildSystemManager.buildSettingsInferredFromMainFile(for: aswift.asURI, language: .swift) + ).compilerArguments // Plugins get compiled with the same compiler arguments as the package manifest assertArgumentsContain("-package-description-version", "5.7.0", arguments: arguments)