Skip to content

Commit cdd1629

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 e5dbd8d commit cdd1629

File tree

5 files changed

+159
-27
lines changed

5 files changed

+159
-27
lines changed
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: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,13 @@ 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 try ClangSupport.checkCompilerFlags(
230+
flags: ["-index-store-path", try self.fileSystem.tempDirectory.pathString],
231+
toolchain: self.buildParameters.toolchain,
232+
triple: self.buildParameters.triple,
233+
fileSystem: self.fileSystem
234+
) {
238235
args += self.buildParameters.indexStoreArguments(for: target)
239236
}
240237

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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
#if !os(macOS)
23+
// We're not necessarily sure that the used clang supports `-index-store-path` on non-macOS, so skip this.
24+
try XCTSkipIf(true, "test is only supported on macOS")
25+
#endif
26+
let targetDescription = try makeTargetBuildDescription()
27+
XCTAssertTrue(try targetDescription.basicArguments().contains("-index-store-path"))
28+
}
29+
30+
private func makeClangTarget() throws -> ClangTarget {
31+
try ClangTarget(
32+
name: "dummy",
33+
cLanguageStandard: nil,
34+
cxxLanguageStandard: nil,
35+
includeDir: .root,
36+
moduleMapType: .none,
37+
type: .library,
38+
path: .root,
39+
sources: .init(paths: [.root.appending(component: "foo.c")], root: .root),
40+
usesUnsafeFlags: false
41+
)
42+
}
43+
44+
private func makeResolvedTarget() throws -> ResolvedTarget {
45+
ResolvedTarget(
46+
packageIdentity: .plain("dummy"),
47+
underlying: try makeClangTarget(),
48+
dependencies: [],
49+
supportedPlatforms: [],
50+
platformVersionProvider: .init(implementation: .minimumDeploymentTargetDefault)
51+
)
52+
}
53+
54+
private func makeTargetBuildDescription() throws -> ClangTargetBuildDescription {
55+
let observability = ObservabilitySystem.makeForTesting(verbose: false)
56+
return try ClangTargetBuildDescription(
57+
target: try makeResolvedTarget(),
58+
toolsVersion: .current,
59+
buildParameters: mockBuildParameters(
60+
toolchain: try UserToolchain.default,
61+
indexStoreMode: .on
62+
),
63+
fileSystem: localFileSystem,
64+
observabilityScope: observability.topScope
65+
)
66+
}
67+
}

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)