Skip to content

Commit 913ed0d

Browse files
committed
Enable index store based on Clang feature detection
Today, the index store is only enabled on Darwin by default and needs a manual opt-in on other platforms. We can instead switch this to enabling it based on whether the used clang supports `-index-store-path`. rdar://117744039
1 parent e10ff90 commit 913ed0d

File tree

5 files changed

+114
-17
lines changed

5 files changed

+114
-17
lines changed

Sources/Build/BuildDescription/ClangTargetBuildDescription.swift

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,11 @@ public final class ClangTargetBuildDescription {
225225
args += activeCompilationConditions
226226
args += ["-fblocks"]
227227

228-
let buildTriple = self.buildParameters.triple
229228
// Enable index store, if appropriate.
230-
//
231-
// This feature is not widely available in OSS clang. So, we only enable
232-
// index store for Apple's clang or if explicitly asked to.
233-
if ProcessEnv.vars.keys.contains("SWIFTPM_ENABLE_CLANG_INDEX_STORE") {
234-
args += self.buildParameters.indexStoreArguments(for: target)
235-
} else if buildTriple.isDarwin(),
236-
(try? self.buildParameters.toolchain._isClangCompilerVendorApple()) == true
237-
{
229+
if let supported = try? ClangSupport.supportsFeature(
230+
name: "index-unit-output-path",
231+
toolchain: self.buildParameters.toolchain
232+
), supported {
238233
args += self.buildParameters.indexStoreArguments(for: target)
239234
}
240235

Sources/Build/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_library(Build
2424
BuildPlan/BuildPlan+Product.swift
2525
BuildPlan/BuildPlan+Swift.swift
2626
BuildPlan/BuildPlan+Test.swift
27+
ClangSupport.swift
2728
SwiftCompilerOutputParser.swift
2829
TestObservation.swift)
2930
target_link_libraries(Build PUBLIC

Sources/Build/ClangSupport.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 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 Basics
14+
import Foundation
15+
import PackageModel
16+
17+
public enum ClangSupport {
18+
private struct Feature: Decodable {
19+
let name: String
20+
let value: [String]?
21+
}
22+
23+
private struct Features: Decodable {
24+
let features: [Feature]
25+
}
26+
27+
private static var cachedFeatures = ThreadSafeBox<Features>()
28+
29+
public static func supportsFeature(name: String, toolchain: PackageModel.Toolchain) throws -> Bool {
30+
let features = try cachedFeatures.memoize {
31+
let clangPath = try toolchain.getClangCompiler()
32+
let featuresPath = clangPath.parentDirectory.parentDirectory.appending(components: ["share", "clang", "features.json"])
33+
return try JSONDecoder.makeWithDefaults().decode(
34+
path: featuresPath,
35+
fileSystem: localFileSystem,
36+
as: Features.self
37+
)
38+
}
39+
return features.features.first(where: { $0.name == name }) != nil
40+
}
41+
}

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3613,7 +3613,11 @@ final class BuildPlanTests: XCTestCase {
36133613

36143614
func check(for mode: BuildParameters.IndexStoreMode, config: BuildConfiguration) throws {
36153615
let result = try BuildPlanResult(plan: BuildPlan(
3616-
buildParameters: mockBuildParameters(config: config, indexStoreMode: mode),
3616+
buildParameters: mockBuildParameters(
3617+
config: config,
3618+
toolchain: try UserToolchain.default,
3619+
indexStoreMode: mode
3620+
),
36173621
graph: graph,
36183622
fileSystem: fs,
36193623
observabilityScope: observability.topScope
@@ -3622,17 +3626,10 @@ final class BuildPlanTests: XCTestCase {
36223626
let lib = try result.target(for: "lib").clangTarget()
36233627
let path = StringPattern.equal(result.plan.destinationBuildParameters.indexStore.pathString)
36243628

3625-
#if os(macOS)
36263629
XCTAssertMatch(
36273630
try lib.basicArguments(isCXX: false),
36283631
[.anySequence, "-index-store-path", path, .anySequence]
36293632
)
3630-
#else
3631-
XCTAssertNoMatch(
3632-
try lib.basicArguments(isCXX: false),
3633-
[.anySequence, "-index-store-path", path, .anySequence]
3634-
)
3635-
#endif
36363633

36373634
let exe = try result.target(for: "exe").swiftTarget().compileArguments()
36383635
XCTAssertMatch(exe, [.anySequence, "-index-store-path", path, .anySequence])
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 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 Basics
14+
@testable import Build
15+
import PackageGraph
16+
import PackageModel
17+
import SPMTestSupport
18+
import XCTest
19+
20+
final class ClangTargetBuildDescriptionTests: XCTestCase {
21+
func testClangIndexStorePath() throws {
22+
let targetDescription = try makeTargetBuildDescription()
23+
XCTAssertTrue(try targetDescription.basicArguments().contains("-index-store-path"))
24+
}
25+
26+
private func makeClangTarget() throws -> ClangTarget {
27+
try ClangTarget(
28+
name: "dummy",
29+
cLanguageStandard: nil,
30+
cxxLanguageStandard: nil,
31+
includeDir: .root,
32+
moduleMapType: .none,
33+
type: .library,
34+
path: .root,
35+
sources: .init(paths: [.root.appending(component: "foo.c")], root: .root),
36+
usesUnsafeFlags: false
37+
)
38+
}
39+
40+
private func makeResolvedTarget() throws -> ResolvedTarget {
41+
ResolvedTarget(
42+
packageIdentity: .plain("dummy"),
43+
underlying: try makeClangTarget(),
44+
dependencies: [],
45+
supportedPlatforms: [],
46+
platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault)
47+
)
48+
}
49+
50+
private func makeTargetBuildDescription() throws -> ClangTargetBuildDescription {
51+
let observability = ObservabilitySystem.makeForTesting(verbose: false)
52+
return try ClangTargetBuildDescription(
53+
target: try makeResolvedTarget(),
54+
toolsVersion: .current,
55+
buildParameters: mockBuildParameters(
56+
toolchain: try UserToolchain.default,
57+
indexStoreMode: .on
58+
),
59+
fileSystem: localFileSystem,
60+
observabilityScope: observability.topScope
61+
)
62+
}
63+
}

0 commit comments

Comments
 (0)