Skip to content

Commit e6d4fa8

Browse files
dschaefer2bnbarham
authored andcommitted
Add prepare for index experimental build argument (#7574)
This will be used by sourcekit-lsp to build the swiftmodule files it needs for indexing. Adds experimental-prepare-for-indexing flag to swift build. Creates a llbuild manifest specific for the prepare build that skips generating object files and linking of those files and calls swiftc to only create the swiftmodule as quickly as it can. ### Motivation: To support background indexing in sourcekit-lsp, it will request a prepare for index build to build the swiftmodule files it needs to do indexing. This build should be minimal to ensure indexing is fast so it can respond to language server requests that require the index as soon as possible. ### Modifications: - Add an experimental-prepare-for-indexing flag to the BuildOptions and pass it around to every that needs it. - Build a custom llbuild manifest so that only the commands necessary are added to the prepare build - Add a target property that also ensures tool builds required for the prepare build are performed as usual. - In SwiftTargetBuildDescription, pass compile options that put the swift compiler in prepare "mode". - Ensure object files and binaries are only generated for tools when in prepare mode. ### Result: Implements prepare build mode without affecting regular build mode. (cherry picked from commit 7bd34cc)
1 parent 8004609 commit e6d4fa8

13 files changed

+160
-14
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ Package.resolved
1818
.docc-build
1919
.vscode
2020
Utilities/InstalledSwiftPMConfiguration/config.json
21+
.devcontainer

Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,17 @@ public final class SwiftTargetBuildDescription {
561561
args += ["-emit-module-interface-path", self.parseableModuleInterfaceOutputPath.pathString]
562562
}
563563

564+
if self.buildParameters.prepareForIndexing {
565+
args += [
566+
"-Xfrontend", "-enable-library-evolution",
567+
"-Xfrontend", "-experimental-skip-all-function-bodies",
568+
"-Xfrontend", "-experimental-lazy-typecheck",
569+
"-Xfrontend", "-experimental-skip-non-exportable-decls",
570+
"-Xfrontend", "-experimental-allow-module-with-compiler-errors",
571+
"-Xfrontend", "-empty-abi-descriptor"
572+
]
573+
}
574+
564575
args += self.buildParameters.toolchain.extraFlags.swiftCompilerFlags
565576
// User arguments (from -Xswiftc) should follow generated arguments to allow user overrides
566577
args += self.buildParameters.flags.swiftCompilerFlags

Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extension LLBuildManifestBuilder {
4141
let inputs = try self.computeSwiftCompileCmdInputs(target)
4242

4343
// Outputs.
44-
let objectNodes = try target.objects.map(Node.file)
44+
let objectNodes = target.buildParameters.prepareForIndexing ? [] : try target.objects.map(Node.file)
4545
let moduleNode = Node.file(target.moduleOutputPath)
4646
let cmdOutputs = objectNodes + [moduleNode]
4747

@@ -394,7 +394,8 @@ extension LLBuildManifestBuilder {
394394
fileList: target.sourcesFileListPath,
395395
isLibrary: isLibrary,
396396
wholeModuleOptimization: target.buildParameters.configuration == .release,
397-
outputFileMapPath: try target.writeOutputFileMap() // FIXME: Eliminate side effect.
397+
outputFileMapPath: try target.writeOutputFileMap(), // FIXME: Eliminate side effect.
398+
prepareForIndexing: target.buildParameters.prepareForIndexing
398399
)
399400
}
400401

@@ -419,6 +420,8 @@ extension LLBuildManifestBuilder {
419420
self.manifest.addWriteEmbeddedResourcesCommand(resources: resourceFilesToEmbed, outputPath: resourcesEmbeddingSource)
420421
}
421422

423+
let prepareForIndexing = target.buildParameters.prepareForIndexing
424+
422425
func addStaticTargetInputs(_ target: ResolvedModule) throws {
423426
// Ignore C Modules.
424427
if target.underlying is SystemLibraryTarget { return }
@@ -428,7 +431,7 @@ extension LLBuildManifestBuilder {
428431
if target.underlying is PluginTarget { return }
429432

430433
// Depend on the binary for executable targets.
431-
if target.type == .executable {
434+
if target.type == .executable && !prepareForIndexing {
432435
// FIXME: Optimize.
433436
if let productDescription = try plan.productMap.values.first(where: {
434437
try $0.product.type == .executable && $0.product.executableTarget.id == target.id
@@ -442,8 +445,16 @@ extension LLBuildManifestBuilder {
442445
case .swift(let target)?:
443446
inputs.append(file: target.moduleOutputPath)
444447
case .clang(let target)?:
445-
for object in try target.objects {
446-
inputs.append(file: object)
448+
if prepareForIndexing {
449+
// In preparation, we're only building swiftmodules
450+
// propagate the dependency to the header files in this target
451+
for header in target.clangTarget.headers {
452+
inputs.append(file: header)
453+
}
454+
} else {
455+
for object in try target.objects {
456+
inputs.append(file: object)
457+
}
447458
}
448459
case nil:
449460
throw InternalError("unexpected: target \(target) not in target map \(self.plan.targetMap)")

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,41 @@ public class LLBuildManifestBuilder {
127127
return self.manifest
128128
}
129129

130+
package func generatePrepareManifest(at path: AbsolutePath) throws -> LLBuildManifest {
131+
self.swiftGetVersionFiles.removeAll()
132+
133+
self.manifest.createTarget(TargetKind.main.targetName)
134+
self.manifest.createTarget(TargetKind.test.targetName)
135+
self.manifest.defaultTarget = TargetKind.main.targetName
136+
137+
addPackageStructureCommand()
138+
139+
for (_, description) in self.plan.targetMap {
140+
switch description {
141+
case .swift(let desc):
142+
try self.createSwiftCompileCommand(desc)
143+
case .clang(let desc):
144+
// Need the clang targets for tools
145+
if desc.target.buildTriple == .tools {
146+
try self.createClangCompileCommand(desc)
147+
}
148+
}
149+
}
150+
151+
for (_, description) in self.plan.productMap {
152+
// Need to generate macro products
153+
switch description.product.type {
154+
case .macro, .plugin:
155+
try self.createProductCommand(description)
156+
default:
157+
break
158+
}
159+
}
160+
161+
try LLBuildManifestWriter.write(self.manifest, at: path, fileSystem: self.fileSystem)
162+
return self.manifest
163+
}
164+
130165
func addNode(_ node: Node, toTarget targetKind: TargetKind) {
131166
self.manifest.addNode(node, toTarget: targetKind.targetName)
132167
}

Sources/Build/BuildOperation.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,9 @@ extension BuildDescription {
900900
) throws -> (BuildDescription, LLBuildManifest) {
901901
// Generate the llbuild manifest.
902902
let llbuild = LLBuildManifestBuilder(plan, disableSandboxForPluginCommands: disableSandboxForPluginCommands, fileSystem: fileSystem, observabilityScope: observabilityScope)
903-
let buildManifest = try llbuild.generateManifest(at: plan.destinationBuildParameters.llbuildManifest)
903+
let buildManifest = plan.destinationBuildParameters.prepareForIndexing
904+
? try llbuild.generatePrepareManifest(at: plan.destinationBuildParameters.llbuildManifest)
905+
: try llbuild.generateManifest(at: plan.destinationBuildParameters.llbuildManifest)
904906

905907
let swiftCommands = llbuild.manifest.getCmdToolMap(kind: SwiftCompilerTool.self)
906908
let swiftFrontendCommands = llbuild.manifest.getCmdToolMap(kind: SwiftFrontendTool.self)

Sources/CoreCommands/Options.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ public struct BuildOptions: ParsableArguments {
438438
@Flag(help: "Enable or disable indexing-while-building feature")
439439
public var indexStoreMode: StoreMode = .autoIndexStore
440440

441+
@Flag(name: .customLong("experimental-prepare-for-indexing"), help: .hidden)
442+
var prepareForIndexing: Bool = false
443+
441444
/// Whether to enable generation of `.swiftinterface`s alongside `.swiftmodule`s.
442445
@Flag(name: .customLong("enable-parseable-module-interfaces"))
443446
public var shouldEnableParseableModuleInterfaces: Bool = false

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,8 @@ public final class SwiftCommandState {
728728

729729
private func _buildParams(
730730
toolchain: UserToolchain,
731-
destination: BuildParameters.Destination
731+
destination: BuildParameters.Destination,
732+
prepareForIndexing: Bool? = nil
732733
) throws -> BuildParameters {
733734
let triple = toolchain.targetTriple
734735

@@ -753,6 +754,7 @@ public final class SwiftCommandState {
753754
sanitizers: options.build.enabledSanitizers,
754755
indexStoreMode: options.build.indexStoreMode.buildParameter,
755756
isXcodeBuildSystemEnabled: options.build.buildSystem == .xcode,
757+
prepareForIndexing: prepareForIndexing ?? options.build.prepareForIndexing,
756758
debuggingParameters: .init(
757759
debugInfoFormat: options.build.debugInfoFormat.buildParameter,
758760
triple: triple,
@@ -801,7 +803,7 @@ public final class SwiftCommandState {
801803

802804
private lazy var _toolsBuildParameters: Result<BuildParameters, Swift.Error> = {
803805
Result(catching: {
804-
try _buildParams(toolchain: self.getHostToolchain(), destination: .host)
806+
try _buildParams(toolchain: self.getHostToolchain(), destination: .host, prepareForIndexing: false)
805807
})
806808
}()
807809

Sources/LLBuildManifest/LLBuildManifest.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,8 @@ public struct LLBuildManifest {
408408
fileList: AbsolutePath,
409409
isLibrary: Bool,
410410
wholeModuleOptimization: Bool,
411-
outputFileMapPath: AbsolutePath
411+
outputFileMapPath: AbsolutePath,
412+
prepareForIndexing: Bool
412413
) {
413414
assert(commands[name] == nil, "already had a command named '\(name)'")
414415
let tool = SwiftCompilerTool(
@@ -426,7 +427,8 @@ public struct LLBuildManifest {
426427
fileList: fileList,
427428
isLibrary: isLibrary,
428429
wholeModuleOptimization: wholeModuleOptimization,
429-
outputFileMapPath: outputFileMapPath
430+
outputFileMapPath: outputFileMapPath,
431+
prepareForIndexing: prepareForIndexing
430432
)
431433
commands[name] = Command(name: name, tool: tool)
432434
}

Sources/LLBuildManifest/Tools.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ public struct SwiftCompilerTool: ToolProtocol {
271271
public var isLibrary: Bool
272272
public var wholeModuleOptimization: Bool
273273
public var outputFileMapPath: AbsolutePath
274+
public var prepareForIndexing: Bool
274275

275276
init(
276277
inputs: [Node],
@@ -287,7 +288,8 @@ public struct SwiftCompilerTool: ToolProtocol {
287288
fileList: AbsolutePath,
288289
isLibrary: Bool,
289290
wholeModuleOptimization: Bool,
290-
outputFileMapPath: AbsolutePath
291+
outputFileMapPath: AbsolutePath,
292+
prepareForIndexing: Bool
291293
) {
292294
self.inputs = inputs
293295
self.outputs = outputs
@@ -304,6 +306,7 @@ public struct SwiftCompilerTool: ToolProtocol {
304306
self.isLibrary = isLibrary
305307
self.wholeModuleOptimization = wholeModuleOptimization
306308
self.outputFileMapPath = outputFileMapPath
309+
self.prepareForIndexing = prepareForIndexing
307310
}
308311

309312
var description: String {
@@ -334,7 +337,10 @@ public struct SwiftCompilerTool: ToolProtocol {
334337
} else {
335338
arguments += ["-incremental"]
336339
}
337-
arguments += ["-c", "@\(self.fileList.pathString)"]
340+
if !prepareForIndexing {
341+
arguments += ["-c"]
342+
}
343+
arguments += ["@\(self.fileList.pathString)"]
338344
arguments += ["-I", importPath.pathString]
339345
arguments += otherArguments
340346
return arguments

Sources/SPMBuildCore/BuildParameters/BuildParameters+Debugging.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ extension BuildParameters {
6363

6464
/// The debugging strategy according to the current build parameters.
6565
public var debuggingStrategy: DebuggingStrategy? {
66-
guard configuration == .debug else {
66+
guard configuration == .debug, !prepareForIndexing else {
6767
return nil
6868
}
6969

Sources/SPMBuildCore/BuildParameters/BuildParameters.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ public struct BuildParameters: Encodable {
114114

115115
public var shouldSkipBuilding: Bool
116116

117+
/// Do minimal build to prepare for indexing
118+
public var prepareForIndexing: Bool
119+
117120
/// Build parameters related to debugging.
118121
public var debuggingParameters: Debugging
119122

@@ -144,6 +147,7 @@ public struct BuildParameters: Encodable {
144147
indexStoreMode: IndexStoreMode = .auto,
145148
isXcodeBuildSystemEnabled: Bool = false,
146149
shouldSkipBuilding: Bool = false,
150+
prepareForIndexing: Bool = false,
147151
debuggingParameters: Debugging? = nil,
148152
driverParameters: Driver = .init(),
149153
linkingParameters: Linking = .init(),
@@ -199,6 +203,7 @@ public struct BuildParameters: Encodable {
199203
self.indexStoreMode = indexStoreMode
200204
self.isXcodeBuildSystemEnabled = isXcodeBuildSystemEnabled
201205
self.shouldSkipBuilding = shouldSkipBuilding
206+
self.prepareForIndexing = prepareForIndexing
202207
self.driverParameters = driverParameters
203208
self.linkingParameters = linkingParameters
204209
self.outputParameters = outputParameters

Sources/SPMTestSupport/MockBuildTestHelper.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ package func mockBuildParameters(
8989
useExplicitModuleBuild: Bool = false,
9090
linkerDeadStrip: Bool = true,
9191
linkTimeOptimizationMode: BuildParameters.LinkTimeOptimizationMode? = nil,
92-
omitFramePointers: Bool? = nil
92+
omitFramePointers: Bool? = nil,
93+
prepareForIndexing: Bool = false
9394
) -> BuildParameters {
9495
try! BuildParameters(
9596
destination: destination,
@@ -101,6 +102,7 @@ package func mockBuildParameters(
101102
pkgConfigDirectories: [],
102103
workers: 3,
103104
indexStoreMode: indexStoreMode,
105+
prepareForIndexing: prepareForIndexing,
104106
debuggingParameters: .init(
105107
triple: triple,
106108
shouldEnableDebuggingEntitlement: config == .debug,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2015-2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Build
14+
import Foundation
15+
import LLBuildManifest
16+
@_spi(SwiftPMInternal)
17+
import SPMTestSupport
18+
import TSCBasic
19+
import XCTest
20+
21+
class PrepareForIndexTests: XCTestCase {
22+
func testPrepare() throws {
23+
let (graph, fs, scope) = try macrosPackageGraph()
24+
25+
let plan = try BuildPlan(
26+
destinationBuildParameters: mockBuildParameters(destination: .target, prepareForIndexing: true),
27+
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: false),
28+
graph: graph,
29+
fileSystem: fs,
30+
observabilityScope: scope
31+
)
32+
33+
let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope)
34+
let manifest = try builder.generatePrepareManifest(at: "/manifest")
35+
36+
// Make sure we're building the swift modules
37+
let outputs = manifest.commands.flatMap(\.value.tool.outputs).map(\.name)
38+
XCTAssertTrue(outputs.contains(where: { $0.hasSuffix(".swiftmodule") }))
39+
40+
// Ensure swiftmodules built with correct arguments
41+
let coreCommands = manifest.commands.values.filter {
42+
$0.tool.outputs.contains(where: {
43+
$0.name.hasSuffix("debug/Core.build/Core.swiftmodule")
44+
})
45+
}
46+
XCTAssertEqual(coreCommands.count, 1)
47+
let coreSwiftc = try XCTUnwrap(coreCommands.first?.tool as? SwiftCompilerTool)
48+
XCTAssertTrue(coreSwiftc.otherArguments.contains("-experimental-skip-all-function-bodies"))
49+
50+
// Ensure tools are built normally
51+
let toolCommands = manifest.commands.values.filter {
52+
$0.tool.outputs.contains(where: {
53+
$0.name.hasSuffix("debug/Modules-tool/SwiftSyntax.swiftmodule")
54+
})
55+
}
56+
XCTAssertEqual(toolCommands.count, 1)
57+
let toolSwiftc = try XCTUnwrap(toolCommands.first?.tool as? SwiftCompilerTool)
58+
XCTAssertFalse(toolSwiftc.otherArguments.contains("-experimental-skip-all-function-bodies"))
59+
60+
// Make sure only object files for tools are built
61+
XCTAssertTrue(
62+
outputs.filter { $0.hasSuffix(".o") }.allSatisfy { $0.contains("-tool.build/") },
63+
"outputs:\n\t\(outputs.filter { $0.hasSuffix(".o") }.joined(separator: "\n\t"))"
64+
)
65+
}
66+
}

0 commit comments

Comments
 (0)