1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Foundation
package import SourceKitD
import ToolchainRegistry
/// The path to the `SwiftSourceKitPluginTests` test bundle. This gives us a hook into the the build directory.
private let xctestBundle: URL = {
#if canImport(Darwin)
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
return bundle.bundleURL
}
preconditionFailure("Failed to find xctest bundle")
#else
return URL(
fileURLWithPath: CommandLine.arguments.first!,
relativeTo: URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
)
#endif
}()
/// When running tests from Xcode, determine the build configuration of the package.
var inferredXcodeBuildConfiguration: String? {
if let xcodeBuildDirectory = ProcessInfo.processInfo.environment["__XCODE_BUILT_PRODUCTS_DIR_PATHS"] {
return URL(fileURLWithPath: xcodeBuildDirectory).lastPathComponent
} else {
return nil
}
}
/// Shorthand for `FileManager.fileExists`
private func fileExists(at url: URL) -> Bool {
return FileManager.default.fileExists(atPath: url.path)
}
/// Try to find the client and server plugin relative to `base`.
///
/// Implementation detail of `sourceKitPluginPaths` which walks up the directory structure, repeatedly calling this method.
private func pluginPaths(relativeTo base: URL) -> PluginPaths? {
// When building in Xcode
if let buildConfiguration = inferredXcodeBuildConfiguration {
let frameworksDir = base.appendingPathComponent("Products")
.appendingPathComponent(buildConfiguration)
.appendingPathComponent("PackageFrameworks")
let clientPlugin =
frameworksDir
.appendingPathComponent("SwiftSourceKitClientPlugin.framework")
.appendingPathComponent("SwiftSourceKitClientPlugin")
let servicePlugin =
frameworksDir
.appendingPathComponent("SwiftSourceKitPlugin.framework")
.appendingPathComponent("SwiftSourceKitPlugin")
if fileExists(at: clientPlugin) && fileExists(at: servicePlugin) {
return PluginPaths(clientPlugin: clientPlugin, servicePlugin: servicePlugin)
}
}
// When creating an `xctestproducts` bundle
do {
let frameworksDir = base.appendingPathComponent("PackageFrameworks")
let clientPlugin =
frameworksDir
.appendingPathComponent("SwiftSourceKitClientPlugin.framework")
.appendingPathComponent("SwiftSourceKitClientPlugin")
let servicePlugin =
frameworksDir
.appendingPathComponent("SwiftSourceKitPlugin.framework")
.appendingPathComponent("SwiftSourceKitPlugin")
if fileExists(at: clientPlugin) && fileExists(at: servicePlugin) {
return PluginPaths(clientPlugin: clientPlugin, servicePlugin: servicePlugin)
}
}
// When building using 'swift test'
do {
#if canImport(Darwin)
let clientPluginName = "libSwiftSourceKitClientPlugin.dylib"
let servicePluginName = "libSwiftSourceKitPlugin.dylib"
#elseif os(Windows)
let clientPluginName = "SwiftSourceKitClientPlugin.dll"
let servicePluginName = "SwiftSourceKitPlugin.dll"
#else
let clientPluginName = "libSwiftSourceKitClientPlugin.so"
let servicePluginName = "libSwiftSourceKitPlugin.so"
#endif
let clientPlugin = base.appendingPathComponent(clientPluginName)
let servicePlugin = base.appendingPathComponent(servicePluginName)
if fileExists(at: clientPlugin) && fileExists(at: servicePlugin) {
return PluginPaths(clientPlugin: clientPlugin, servicePlugin: servicePlugin)
}
}
return nil
}
/// Returns the paths from which the SourceKit plugins should be loaded or throws an error if the plugins cannot be
/// found.
package var sourceKitPluginPaths: PluginPaths {
get async throws {
struct PluginLoadingError: Error, CustomStringConvertible {
let searchBase: URL
var description: String {
// We can't declare a dependency from the test *target* on the SourceKit plugin *product*
// (https://github.com/swiftlang/swift-package-manager/issues/8245).
// We thus require a build before running the tests to ensure the plugin dylibs are in the build products
// folder.
"""
Could not find SourceKit plugin. Ensure that you build the entire SourceKit-LSP package before running tests.
Searching for plugin relative to \(searchBase)
"""
}
}
var base: URL
if let pluginPaths = ProcessInfo.processInfo.environment["SOURCEKIT_LSP_TEST_PLUGIN_PATHS"] {
if pluginPaths == "RELATIVE_TO_SOURCEKITD" {
let toolchain = await ToolchainRegistry.forTesting.default
guard let clientPlugin = toolchain?.sourceKitClientPlugin, let servicePlugin = toolchain?.sourceKitServicePlugin
else {
throw PluginLoadingError(searchBase: URL(string: "relative-to-sourcekitd://")!)
}
return PluginPaths(clientPlugin: clientPlugin, servicePlugin: servicePlugin)
} else {
base = URL(fileURLWithPath: pluginPaths)
}
} else {
base = xctestBundle
}
var searchPath = base
while searchPath.pathComponents.count > 1 {
if let paths = pluginPaths(relativeTo: searchPath) {
return paths
}
searchPath = searchPath.deletingLastPathComponent()
}
throw PluginLoadingError(searchBase: base)
}
}
|