Skip to content

Commit 9177b8f

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 02f047a commit 9177b8f

File tree

7 files changed

+158
-26
lines changed

7 files changed

+158
-26
lines changed

Sources/Basics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_library(Basics
1616
Collections/ByteString+Extensions.swift
1717
Collections/Dictionary+Extensions.swift
1818
Collections/IdentifiableSet.swift
19+
Collections/RandomAccessCollection+Extensions.swift
1920
Collections/String+Extensions.swift
2021
Concurrency/ConcurrencyHelpers.swift
2122
Concurrency/NSLock+Extensions.swift
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
// Since 'contains' is only available in macOS SDKs 13.0 or newer, we need our own little implementation.
14+
extension RandomAccessCollection where Element: Equatable {
15+
public func firstIndex(of pattern: some RandomAccessCollection<Element>) -> Index? {
16+
guard !pattern.isEmpty && count >= pattern.count else {
17+
return nil
18+
}
19+
20+
var i = startIndex
21+
for _ in 0..<(count - pattern.count + 1) {
22+
if self[i...].starts(with: pattern) {
23+
return i
24+
}
25+
i = self.index(after: i)
26+
}
27+
return nil
28+
}
29+
}

Sources/Build/BuildDescription/ClangTargetBuildDescription.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,13 @@ public final class ClangTargetBuildDescription {
227227

228228
let buildTriple = self.buildParameters.triple
229229
// 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-
{
230+
if try (buildTriple.isDarwin() &&
231+
(try? self.buildParameters.toolchain._isClangCompilerVendorApple()) == true) || ClangSupport.checkCompilerFlags(
232+
flags: ["-index-store-path", try self.fileSystem.tempDirectory.pathString],
233+
toolchain: self.buildParameters.toolchain,
234+
triple: self.buildParameters.triple,
235+
fileSystem: self.fileSystem
236+
) {
238237
args += self.buildParameters.indexStoreArguments(for: target)
239238
}
240239

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: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 PackageModel
15+
import class TSCBasic.Process
16+
import func TSCBasic.withTemporaryDirectory
17+
18+
public enum ClangSupport {
19+
private static var flagsMap = ThreadSafeBox<[String: [String]]>()
20+
21+
public static func checkCompilerFlags(
22+
flags: [String],
23+
toolchain: PackageModel.Toolchain,
24+
triple: Triple,
25+
fileSystem: FileSystem
26+
) throws -> Bool {
27+
let clangPath = try toolchain.getClangCompiler().pathString
28+
if let entry = flagsMap.get(), let cachedSupportedFlags = entry[clangPath] {
29+
return cachedSupportedFlags.firstIndex(of: flags) != nil
30+
}
31+
let extraFlags: [String]
32+
if triple.isDarwin(), let sdkRootPath = toolchain.sdkRootPath {
33+
extraFlags = ["-isysroot", sdkRootPath.pathString]
34+
} else {
35+
extraFlags = []
36+
}
37+
do {
38+
try withTemporaryDirectory { tmpPath in
39+
let inputPath = tmpPath.appending(component: "foo.c")
40+
try localFileSystem.writeFileContents(inputPath, string: "int main() {\nreturn 0;\n}")
41+
let outputPath = tmpPath.appending("foo")
42+
try Process.checkNonZeroExit(arguments: [
43+
clangPath,
44+
inputPath.pathString,
45+
"-o",
46+
outputPath.pathString
47+
] + extraFlags + flags, environment: [:])
48+
try Process.checkNonZeroExit(arguments: [outputPath.pathString])
49+
}
50+
} catch {
51+
return false
52+
}
53+
// Note: the cache only supports a single list of flags being checked.
54+
flagsMap.put([clangPath: flags])
55+
return true
56+
}
57+
}
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+
}

Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,3 @@ extension SourceKitLSPAPI.BuildDescription {
7373
return result
7474
}
7575
}
76-
77-
// Since 'contains' is only available in macOS SDKs 13.0 or newer, we need our own little implementation.
78-
extension RandomAccessCollection where Element: Equatable {
79-
fileprivate func firstIndex(of pattern: some RandomAccessCollection<Element>) -> Index? {
80-
guard !pattern.isEmpty && count >= pattern.count else {
81-
return nil
82-
}
83-
84-
var i = startIndex
85-
for _ in 0..<(count - pattern.count + 1) {
86-
if self[i...].starts(with: pattern) {
87-
return i
88-
}
89-
i = self.index(after: i)
90-
}
91-
return nil
92-
}
93-
}

0 commit comments

Comments
 (0)