Skip to content

Commit e695f59

Browse files
authored
Add a --experimental-prepare-for-indexing-no-skipping command line option to prepare a target without function body skipping (#7720)
The flags for lazy type checking and function body skipping are still experimental and haven’t been thoroughly tested in the wild yet. In case they are causing issues, add an option to prepare a target without these flags, which users could enable as a workaround. rdar://130333568
1 parent 7f20357 commit e695f59

File tree

10 files changed

+89
-26
lines changed

10 files changed

+89
-26
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.build
2+
.index-build
23
DerivedData
34
/.previous-build
45
xcuserdata

Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift

+11-6
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ public final class SwiftModuleBuildDescription {
338338
return
339339
}
340340

341-
guard
341+
guard
342342
self.buildParameters.triple.isDarwin() &&
343343
self.buildParameters.testingParameters.experimentalTestOutput
344344
else {
@@ -541,14 +541,19 @@ public final class SwiftModuleBuildDescription {
541541
args += ["-emit-module-interface-path", self.parseableModuleInterfaceOutputPath.pathString]
542542
}
543543

544-
if self.buildParameters.prepareForIndexing {
544+
switch self.buildParameters.prepareForIndexing {
545+
case .off:
546+
break
547+
case .on:
548+
args += ["-Xfrontend", "-experimental-lazy-typecheck",]
545549
if !args.contains("-enable-testing") {
546550
// enable-testing needs the non-exportable-decls
547551
args += ["-Xfrontend", "-experimental-skip-non-exportable-decls"]
548552
}
553+
fallthrough
554+
case .noLazy:
549555
args += [
550556
"-Xfrontend", "-experimental-skip-all-function-bodies",
551-
"-Xfrontend", "-experimental-lazy-typecheck",
552557
"-Xfrontend", "-experimental-allow-module-with-compiler-errors",
553558
"-Xfrontend", "-empty-abi-descriptor"
554559
]
@@ -594,7 +599,7 @@ public final class SwiftModuleBuildDescription {
594599

595600
// Pass `-user-module-version` for versioned packages that aren't pre-releases.
596601
if
597-
let version = package.manifest.version,
602+
let version = package.manifest.version,
598603
version.prereleaseIdentifiers.isEmpty &&
599604
version.buildMetadataIdentifiers.isEmpty &&
600605
toolsVersion >= .v6_0
@@ -607,7 +612,7 @@ public final class SwiftModuleBuildDescription {
607612
isPackageNameSupported: self.buildParameters.driverParameters.isPackageAccessModifierSupported
608613
)
609614
args += try self.macroArguments()
610-
615+
611616
// rdar://117578677
612617
// Pass -fno-omit-frame-pointer to support backtraces
613618
// this can be removed once the backtracer uses DWARF instead of frame pointers
@@ -621,7 +626,7 @@ public final class SwiftModuleBuildDescription {
621626

622627
return args
623628
}
624-
629+
625630
/// Determines the arguments needed to run `swift-symbolgraph-extract` for
626631
/// this module.
627632
package func symbolGraphExtractArguments() throws -> [String] {

Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift

+4-4
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 = target.buildParameters.prepareForIndexing ? [] : try target.objects.map(Node.file)
44+
let objectNodes = target.buildParameters.prepareForIndexing == .off ? try target.objects.map(Node.file) : []
4545
let moduleNode = Node.file(target.moduleOutputPath)
4646
let cmdOutputs = objectNodes + [moduleNode]
4747

@@ -395,7 +395,7 @@ extension LLBuildManifestBuilder {
395395
isLibrary: isLibrary,
396396
wholeModuleOptimization: target.buildParameters.configuration == .release,
397397
outputFileMapPath: try target.writeOutputFileMap(), // FIXME: Eliminate side effect.
398-
prepareForIndexing: target.buildParameters.prepareForIndexing
398+
prepareForIndexing: target.buildParameters.prepareForIndexing != .off
399399
)
400400
}
401401

@@ -433,7 +433,7 @@ extension LLBuildManifestBuilder {
433433
if target.underlying is ProvidedLibraryModule { return }
434434

435435
// Depend on the binary for executable targets.
436-
if target.type == .executable && !prepareForIndexing {
436+
if target.type == .executable && prepareForIndexing == .off {
437437
// FIXME: Optimize.
438438
if let productDescription = try plan.productMap.values.first(where: {
439439
try $0.product.type == .executable && $0.product.executableModule.id == target.id
@@ -447,7 +447,7 @@ extension LLBuildManifestBuilder {
447447
case .swift(let target)?:
448448
inputs.append(file: target.moduleOutputPath)
449449
case .clang(let target)?:
450-
if prepareForIndexing {
450+
if prepareForIndexing != .off {
451451
// In preparation, we're only building swiftmodules
452452
// propagate the dependency to the header files in this target
453453
for header in target.clangTarget.headers {

Sources/Build/BuildOperation.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -1117,9 +1117,9 @@ extension BuildDescription {
11171117
fileSystem: fileSystem,
11181118
observabilityScope: config.observabilityScope
11191119
)
1120-
let buildManifest = plan.destinationBuildParameters.prepareForIndexing
1121-
? try llbuild.generatePrepareManifest(at: config.manifestPath)
1122-
: try llbuild.generateManifest(at: config.manifestPath)
1120+
let buildManifest = plan.destinationBuildParameters.prepareForIndexing == .off
1121+
? try llbuild.generateManifest(at: config.manifestPath)
1122+
: try llbuild.generatePrepareManifest(at: config.manifestPath)
11231123

11241124
let swiftCommands = llbuild.manifest.getCmdToolMap(kind: SwiftCompilerTool.self)
11251125
let swiftFrontendCommands = llbuild.manifest.getCmdToolMap(kind: SwiftFrontendTool.self)

Sources/CoreCommands/Options.swift

+12
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,21 @@ public struct BuildOptions: ParsableArguments {
439439
@Flag(help: "Enable or disable indexing-while-building feature")
440440
public var indexStoreMode: StoreMode = .autoIndexStore
441441

442+
/// Instead of building the target, perform the minimal amount of work to prepare it for indexing.
443+
///
444+
/// This builds Swift module files for all dependencies but skips generation of object files. It also continues
445+
/// building modules in the presence of compilation errors.
442446
@Flag(name: .customLong("experimental-prepare-for-indexing"), help: .hidden)
443447
var prepareForIndexing: Bool = false
444448

449+
/// Don't pass `-experimental-lazy-typecheck` during preparation.
450+
///
451+
/// This is intended as a workaround if lazy type checking is causing compiler crashes.
452+
///
453+
/// Only applicable in conjunction with `--experimental-prepare-for-indexing`
454+
@Flag(name: .customLong("experimental-prepare-for-indexing-no-lazy"), help: .hidden)
455+
var prepareForIndexingNoLazy: Bool = false
456+
445457
/// Whether to enable generation of `.swiftinterface`s alongside `.swiftmodule`s.
446458
@Flag(name: .customLong("enable-parseable-module-interfaces"))
447459
public var shouldEnableParseableModuleInterfaces: Bool = false

Sources/CoreCommands/SwiftCommandState.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,13 @@ public final class SwiftCommandState {
773773
observabilityScope.emit(warning: Self.entitlementsMacOSWarning)
774774
}
775775

776+
let prepareForIndexingMode: BuildParameters.PrepareForIndexingMode =
777+
switch (options.build.prepareForIndexing, options.build.prepareForIndexingNoLazy) {
778+
case (false, _): .off
779+
case (true, false): .on
780+
case (true, true): .noLazy
781+
}
782+
776783
return try BuildParameters(
777784
destination: destination,
778785
dataPath: dataPath,
@@ -786,7 +793,7 @@ public final class SwiftCommandState {
786793
sanitizers: options.build.enabledSanitizers,
787794
indexStoreMode: options.build.indexStoreMode.buildParameter,
788795
isXcodeBuildSystemEnabled: options.build.buildSystem == .xcode,
789-
prepareForIndexing: prepareForIndexing ?? options.build.prepareForIndexing,
796+
prepareForIndexing: prepareForIndexingMode,
790797
debuggingParameters: .init(
791798
debugInfoFormat: options.build.debugInfoFormat.buildParameter,
792799
triple: triple,

Sources/SPMBuildCore/BuildParameters/BuildParameters+Debugging.swift

+1-1
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, !prepareForIndexing else {
66+
guard configuration == .debug, prepareForIndexing == .off else {
6767
return nil
6868
}
6969

Sources/SPMBuildCore/BuildParameters/BuildParameters.swift

+13-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ import PackageModel
1616
import PackageGraph
1717

1818
public struct BuildParameters: Encodable {
19+
public enum PrepareForIndexingMode: Encodable {
20+
/// Perform a normal build and don't prepare for indexing
21+
case off
22+
/// Prepare for indexing but don't pass `-experimental-lazy-typecheck`.
23+
///
24+
/// This is intended as a workaround if lazy type checking is causing compiler crashes.
25+
case noLazy
26+
/// Do minimal build to prepare for indexing
27+
case on
28+
}
29+
1930
/// Mode for the indexing-while-building feature.
2031
public enum IndexStoreMode: String, Encodable {
2132
/// Index store should be enabled.
@@ -115,7 +126,7 @@ public struct BuildParameters: Encodable {
115126
public var shouldSkipBuilding: Bool
116127

117128
/// Do minimal build to prepare for indexing
118-
public var prepareForIndexing: Bool
129+
public var prepareForIndexing: PrepareForIndexingMode
119130

120131
/// Build parameters related to debugging.
121132
public var debuggingParameters: Debugging
@@ -147,7 +158,7 @@ public struct BuildParameters: Encodable {
147158
indexStoreMode: IndexStoreMode = .auto,
148159
isXcodeBuildSystemEnabled: Bool = false,
149160
shouldSkipBuilding: Bool = false,
150-
prepareForIndexing: Bool = false,
161+
prepareForIndexing: PrepareForIndexingMode = .off,
151162
debuggingParameters: Debugging? = nil,
152163
driverParameters: Driver = .init(),
153164
linkingParameters: Linking = .init(),

Sources/_InternalTestSupport/MockBuildTestHelper.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public func mockBuildParameters(
9090
linkerDeadStrip: Bool = true,
9191
linkTimeOptimizationMode: BuildParameters.LinkTimeOptimizationMode? = nil,
9292
omitFramePointers: Bool? = nil,
93-
prepareForIndexing: Bool = false
93+
prepareForIndexing: BuildParameters.PrepareForIndexingMode = .off
9494
) -> BuildParameters {
9595
try! BuildParameters(
9696
destination: destination,

Tests/BuildTests/PrepareForIndexTests.swift

+35-8
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class PrepareForIndexTests: XCTestCase {
2727
let (graph, fs, scope) = try macrosPackageGraph()
2828

2929
let plan = try BuildPlan(
30-
destinationBuildParameters: mockBuildParameters(destination: .target, prepareForIndexing: true),
31-
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: false),
30+
destinationBuildParameters: mockBuildParameters(destination: .target, prepareForIndexing: .on),
31+
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: .off),
3232
graph: graph,
3333
fileSystem: fs,
3434
observabilityScope: scope
@@ -72,8 +72,8 @@ class PrepareForIndexTests: XCTestCase {
7272
let (graph, fs, scope) = try trivialPackageGraph()
7373

7474
let plan = try BuildPlan(
75-
destinationBuildParameters: mockBuildParameters(destination: .target, prepareForIndexing: true),
76-
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: false),
75+
destinationBuildParameters: mockBuildParameters(destination: .target, prepareForIndexing: .on),
76+
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: .off),
7777
graph: graph,
7878
fileSystem: fs,
7979
observabilityScope: scope
@@ -116,8 +116,8 @@ class PrepareForIndexTests: XCTestCase {
116116

117117
// Under debug, enable-testing is turned on by default. Make sure the flag is not added.
118118
let debugPlan = try BuildPlan(
119-
destinationBuildParameters: mockBuildParameters(destination: .target, config: .debug, prepareForIndexing: true),
120-
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: false),
119+
destinationBuildParameters: mockBuildParameters(destination: .target, config: .debug, prepareForIndexing: .on),
120+
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: .off),
121121
graph: graph,
122122
fileSystem: fs,
123123
observabilityScope: observability.topScope
@@ -137,8 +137,8 @@ class PrepareForIndexTests: XCTestCase {
137137

138138
// Under release, enable-testing is turned off by default so we should see our flag
139139
let releasePlan = try BuildPlan(
140-
destinationBuildParameters: mockBuildParameters(destination: .target, config: .release, prepareForIndexing: true),
141-
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: false),
140+
destinationBuildParameters: mockBuildParameters(destination: .target, config: .release, prepareForIndexing: .on),
141+
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: .off),
142142
graph: graph,
143143
fileSystem: fs,
144144
observabilityScope: observability.topScope
@@ -156,4 +156,31 @@ class PrepareForIndexTests: XCTestCase {
156156
&& !swiftCommand.otherArguments.contains("-enable-testing")
157157
}).count, 1)
158158
}
159+
160+
func testPrepareNoLazy() throws {
161+
let (graph, fs, scope) = try macrosPackageGraph()
162+
163+
let plan = try BuildPlan(
164+
destinationBuildParameters: mockBuildParameters(destination: .target, prepareForIndexing: .noLazy),
165+
toolsBuildParameters: mockBuildParameters(destination: .host, prepareForIndexing: .off),
166+
graph: graph,
167+
fileSystem: fs,
168+
observabilityScope: scope
169+
)
170+
171+
let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope)
172+
let manifest = try builder.generatePrepareManifest(at: "/manifest")
173+
174+
// Ensure swiftmodules built with correct arguments
175+
let coreCommands = manifest.commands.values.filter {
176+
$0.tool.outputs.contains(where: {
177+
$0.name.hasSuffix("debug/Core.build/Core.swiftmodule")
178+
})
179+
}
180+
XCTAssertEqual(coreCommands.count, 1)
181+
let coreSwiftc = try XCTUnwrap(coreCommands.first?.tool as? SwiftCompilerTool)
182+
XCTAssertFalse(coreSwiftc.otherArguments.contains("-experimental-lazy-typecheck"))
183+
XCTAssertTrue(coreSwiftc.otherArguments.contains("-experimental-allow-module-with-compiler-errors"))
184+
}
185+
159186
}

0 commit comments

Comments
 (0)