1010//
1111//===----------------------------------------------------------------------===//
1212
13- import struct Foundation. URL
14-
15- private import struct Basics. AbsolutePath
16- private import func Basics. resolveSymlinks
17-
18- internal import SPMBuildCore
19-
20- // FIXME: should import these module with `private` or `internal` access control
21- import class Build. BuildPlan
22- import class Build. ClangModuleBuildDescription
23- import class Build. SwiftModuleBuildDescription
24- import struct PackageGraph. ResolvedModule
25- import struct PackageGraph. ModulesGraph
26- internal import class PackageModel. UserToolchain
13+ import Foundation
14+ import TSCBasic
15+
16+ // Ideally wouldn't expose these (it defeats the purpose of this module), but we should replace this entire API with
17+ // a BSP server, so this is good enough for now (and LSP is using all these types internally anyway).
18+ import Basics
19+ import Build
20+ import PackageGraph
21+ internal import PackageLoading
22+ internal import PackageModel
23+ import SPMBuildCore
2724
2825public enum BuildDestination {
2926 case host
@@ -90,7 +87,13 @@ private struct WrappedClangTargetBuildDescription: BuildTarget {
9087 }
9188
9289 var others : [ URL ] {
93- return description. others. map ( \. asURL)
90+ var others = Set ( description. others)
91+ for pluginResult in description. buildToolPluginInvocationResults {
92+ for buildCommand in pluginResult. buildCommands {
93+ others. formUnion ( buildCommand. inputFiles)
94+ }
95+ }
96+ return others. map ( \. asURL)
9497 }
9598
9699 public var name : String {
@@ -102,7 +105,7 @@ private struct WrappedClangTargetBuildDescription: BuildTarget {
102105 }
103106
104107 public func compileArguments( for fileURL: URL ) throws -> [ String ] {
105- let filePath = try resolveSymlinks ( try AbsolutePath ( validating: fileURL. path) )
108+ let filePath = try resolveSymlinks ( try Basics . AbsolutePath ( validating: fileURL. path) )
106109 let commandLine = try description. emitCommandLine ( for: filePath)
107110 // First element on the command line is the compiler itself, not an argument.
108111 return Array ( commandLine. dropFirst ( ) )
@@ -143,7 +146,13 @@ private struct WrappedSwiftTargetBuildDescription: BuildTarget {
143146 }
144147
145148 var others : [ URL ] {
146- return description. others. map ( \. asURL)
149+ var others = Set ( description. others)
150+ for pluginResult in description. buildToolPluginInvocationResults {
151+ for buildCommand in pluginResult. buildCommands {
152+ others. formUnion ( buildCommand. inputFiles)
153+ }
154+ }
155+ return others. map ( \. asURL)
147156 }
148157
149158 func compileArguments( for fileURL: URL ) throws -> [ String ] {
@@ -160,14 +169,52 @@ public struct BuildDescription {
160169
161170 /// The inputs of the build plan so we don't need to re-compute them on every call to
162171 /// `fileAffectsSwiftOrClangBuildSettings`.
163- private let inputs : [ BuildPlan . Input ]
172+ private let inputs : [ Build . BuildPlan . Input ]
164173
165- // FIXME: should not use `BuildPlan` in the public interface
174+ /// Wrap an already constructed build plan.
166175 public init ( buildPlan: Build . BuildPlan ) {
167176 self . buildPlan = buildPlan
168177 self . inputs = buildPlan. inputs
169178 }
170179
180+ /// Construct a build description, compiling build tool plugins and generating their output when necessary.
181+ public static func load(
182+ destinationBuildParameters: BuildParameters ,
183+ toolsBuildParameters: BuildParameters ,
184+ packageGraph: ModulesGraph ,
185+ pluginConfiguration: PluginConfiguration ,
186+ traitConfiguration: TraitConfiguration ,
187+ disableSandbox: Bool ,
188+ scratchDirectory: URL ,
189+ fileSystem: any FileSystem ,
190+ observabilityScope: ObservabilityScope
191+ ) async throws -> ( description: BuildDescription , errors: String ) {
192+ let bufferedOutput = BufferedOutputByteStream ( )
193+ let threadSafeOutput = ThreadSafeOutputByteStream ( bufferedOutput)
194+
195+ // This is quite an abuse of `BuildOperation`, building plugins should really be refactored out of it. Though
196+ // even better would be to have a BSP server that handles both preparing and getting settings.
197+ // https://github.com/swiftlang/swift-package-manager/issues/8287
198+ let operation = BuildOperation (
199+ productsBuildParameters: destinationBuildParameters,
200+ toolsBuildParameters: toolsBuildParameters,
201+ cacheBuildManifest: true ,
202+ packageGraphLoader: { packageGraph } ,
203+ pluginConfiguration: pluginConfiguration,
204+ scratchDirectory: try Basics . AbsolutePath ( validating: scratchDirectory. path) ,
205+ traitConfiguration: traitConfiguration,
206+ additionalFileRules: FileRuleDescription . swiftpmFileTypes,
207+ pkgConfigDirectories: [ ] ,
208+ outputStream: threadSafeOutput,
209+ logLevel: . error,
210+ fileSystem: fileSystem,
211+ observabilityScope: observabilityScope
212+ )
213+
214+ let plan = try await operation. generatePlan ( )
215+ return ( BuildDescription ( buildPlan: plan) , bufferedOutput. bytes. description)
216+ }
217+
171218 func getBuildTarget(
172219 for module: ResolvedModule ,
173220 destination: BuildParameters . Destination
@@ -219,7 +266,7 @@ public struct BuildDescription {
219266 /// Returns `true` if the file at the given path might influence build settings for a `swiftc` or `clang` invocation
220267 /// generated by SwiftPM.
221268 public func fileAffectsSwiftOrClangBuildSettings( _ url: URL ) -> Bool {
222- guard let filePath = try ? AbsolutePath ( validating: url. path) else {
269+ guard let filePath = try ? Basics . AbsolutePath ( validating: url. path) else {
223270 return false
224271 }
225272
0 commit comments