-
Notifications
You must be signed in to change notification settings - Fork 86
Implement index-dump executable to dump unit and record files - #264 #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
48a1168
231314c
8567317
eb2baf7
f981265
d4aa158
7e18248
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||
| //===----------------------------------------------------------------------===// | ||||||
| // | ||||||
| // This source file is part of the Swift.org open source project | ||||||
| // | ||||||
| // Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors | ||||||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||||||
| // | ||||||
| // See https://swift.org/LICENSE.txt for license information | ||||||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||||||
| // | ||||||
| //===----------------------------------------------------------------------===// | ||||||
|
|
||||||
| import Foundation | ||||||
|
|
||||||
| extension IndexStoreUnit: CustomStringConvertible { | ||||||
| public var description: String { | ||||||
| var result = """ | ||||||
| Module: \(moduleName.string) | ||||||
| Has Main File: \(hasMainFile) | ||||||
| Main File: \(mainFile.string) | ||||||
| Output File: \(outputFile.string) | ||||||
| Target: \(target.string) | ||||||
| Sysroot: \(sysrootPath.string) | ||||||
| Working Directory: \(workingDirectory.string) | ||||||
| Is System: \(isSystemUnit) | ||||||
| Is Module: \(isModuleUnit) | ||||||
| Is Debug: \(isDebugCompilation) | ||||||
| Provider Identifier: \(providerIdentifier.string) | ||||||
| Provider Version: \(providerVersion.string) | ||||||
| Mod Date: \(modificationDate) | ||||||
|
|
||||||
| DEPENDENCIES START | ||||||
| \(dependencies.map { dep in | ||||||
| "\(String(describing: dep.kind).capitalized) | \(dep.name.string)" | ||||||
| }.joined(separator: "\n")) | ||||||
| DEPENDENCIES END | ||||||
| """ | ||||||
|
|
||||||
| return result | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| extension IndexStoreRecord: CustomStringConvertible { | ||||||
| public var description: String { | ||||||
| let symbolLines = symbols.map { symbol in | ||||||
| "\(symbol.kind) | \(symbol.name.string) | USR: \(symbol.usr.string)" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| let occurrencesLines = occurrences.map { occurrence in | ||||||
| var result = | ||||||
| [ | ||||||
| """ | ||||||
| \(occurrence.position.line):\(occurrence.position.column) \ | ||||||
| | \(occurrence.symbol.kind) \ | ||||||
| | USR: \(occurrence.symbol.usr.string) \ | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For anyone working on this, it’ll be obvious that this is a USR (kind of lesson number 1 you need to learn), so I’d omit that.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will always keep this in my mind ! next time onwards - Sorry |
||||||
| | Roles: \(occurrence.roles) | ||||||
| """ | ||||||
| ] | ||||||
| + occurrence.relations.map { relation in | ||||||
| " Relation | \(relation.symbol.usr.string) | Roles: \(relation.roles)" | ||||||
| } | ||||||
| return result | ||||||
| } | ||||||
|
|
||||||
| return """ | ||||||
| SYMBOLS START | ||||||
| \(symbolLines.joined(separator: "\n")) | ||||||
| SYMBOLS END | ||||||
| OCCURRENCES START | ||||||
| \(occurrencesLines.joined(separator: "\n")) | ||||||
| OCCURRENCES END | ||||||
| """ | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift.org open source project | ||
| // | ||
| // Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import Foundation | ||
|
|
||
| #if os(Windows) | ||
| import WinSDK | ||
| #endif | ||
|
|
||
| /// Provides utilities to discover and locate libIndexStore in the system. | ||
| private enum LibIndexStoreProvider { | ||
| /// Find a tool using xcrun/which/where (copied logic from TibsToolchain.findTool) | ||
| static func findTool(name: String) -> URL? { | ||
DPrakashhh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #if os(macOS) | ||
| let cmd = ["/usr/bin/xcrun", "--find", name] | ||
| #elseif os(Windows) | ||
| var buf = [WCHAR](repeating: 0, count: Int(MAX_PATH)) | ||
| GetWindowsDirectoryW(&buf, UINT(MAX_PATH)) | ||
| var wherePath = String(decodingCString: &buf, as: UTF16.self) | ||
| wherePath = (wherePath as NSString).appendingPathComponent("system32") | ||
| wherePath = (wherePath as NSString).appendingPathComponent("where.exe") | ||
| let cmd = [wherePath, name] | ||
| #else | ||
| let cmd = ["/usr/bin/which", name] | ||
| #endif | ||
|
|
||
| let process = Process() | ||
| process.executableURL = URL(fileURLWithPath: cmd[0]) | ||
| process.arguments = Array(cmd.dropFirst()) | ||
| let pipe = Pipe() | ||
| process.standardOutput = pipe | ||
|
|
||
| try? process.run() | ||
| process.waitUntilExit() | ||
| guard process.terminationStatus == 0 else { return nil } | ||
|
|
||
| let data = pipe.fileHandleForReading.readDataToEndOfFile() | ||
| var path = String(decoding: data, as: UTF8.self) | ||
| #if os(Windows) | ||
| path = String((path.split { $0.isNewline })[0]) | ||
| #endif | ||
| return URL(fileURLWithPath: path.trimmingCharacters(in: .whitespacesAndNewlines)) | ||
| } | ||
|
|
||
| /// Infer libIndexStore dylib path from the default toolchain | ||
| static func inferLibPath() -> URL? { | ||
| guard let swiftURL = findTool(name: "swift") else { | ||
| return nil | ||
| } | ||
|
|
||
| let toolchainURL = swiftURL.deletingLastPathComponent().deletingLastPathComponent() | ||
|
|
||
| #if os(macOS) | ||
| let libName = "libIndexStore.dylib" | ||
| #elseif os(Windows) | ||
| let libName = "IndexStore.dll" | ||
| #else | ||
| let libName = "libIndexStore.so" | ||
| #endif | ||
|
|
||
| let libURL = toolchainURL.appending(components: "lib", libName) | ||
| guard FileManager.default.fileExists(atPath: libURL.path) else { | ||
| return nil | ||
| } | ||
| return libURL | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift.org open source project | ||
| // | ||
| // Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import ArgumentParser | ||
| import Foundation | ||
DPrakashhh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import IndexStore | ||
|
|
||
| #if os(Windows) | ||
| import WinSDK | ||
| #endif | ||
|
|
||
| @main | ||
| struct IndexDump: AsyncParsableCommand { | ||
| static let configuration = CommandConfiguration( | ||
| abstract: "Dumps the content of unit or record files from an IndexStore." | ||
| ) | ||
|
|
||
| @Argument(help: "Name of the unit/record or a direct path to the file") | ||
| var nameOrPath: String | ||
|
|
||
| @Option(help: "Path to libIndexStore. Inferred if omitted.") | ||
| var libIndexStore: String? | ||
|
|
||
| @Option(help: "Path to the index store directory. Inferred if omitted.") | ||
| var indexStore: String? | ||
|
|
||
| @Option(help: "Explicitly set mode (unit/record).") | ||
| var mode: Mode? | ||
|
|
||
| enum Mode: String, ExpressibleByArgument { | ||
| case unit, record | ||
| } | ||
|
|
||
| func run() async throws { | ||
| let libURL = try explicitOrInferredLibPath() | ||
| let storeURL = try explicitOrInferredStorePath() | ||
|
|
||
| let lib = try await IndexStoreLibrary.at(dylibPath: libURL) | ||
| let store = try lib.indexStore(at: storeURL) | ||
|
|
||
| let determinedMode = try mode ?? inferMode(from: nameOrPath) | ||
| let cleanName = URL(fileURLWithPath: nameOrPath).lastPathComponent | ||
|
|
||
| switch determinedMode { | ||
| case .unit: | ||
| let unit = try store.unit(named: cleanName) | ||
| print(unit) | ||
| case .record: | ||
| let record = try store.record(named: cleanName) | ||
| print(record) | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Mode Inference | ||
|
|
||
| private func inferMode(from path: String) throws -> Mode { | ||
| let components = URL(fileURLWithPath: path).pathComponents | ||
| if components.contains("units") { return .unit } | ||
| if components.contains("records") { return .record } | ||
| throw ValidationError("Could not infer mode from path. Please specify --mode explicitly.") | ||
| } | ||
|
|
||
| private func explicitOrInferredStorePath() throws -> URL { | ||
| if let explicit = indexStore { return URL(fileURLWithPath: explicit) } | ||
|
|
||
| var url = URL(fileURLWithPath: nameOrPath) | ||
| if url.pathComponents.contains("v5") { | ||
| while url.lastPathComponent != "v5" && url.pathComponents.count > 1 { | ||
| url = url.deletingLastPathComponent() | ||
| } | ||
| return url.deletingLastPathComponent() | ||
| } | ||
| throw ValidationError("Could not infer store path. Please specify --index-store.") | ||
| } | ||
|
|
||
| private func explicitOrInferredLibPath() throws -> URL { | ||
| if let explicit = libIndexStore { return URL(fileURLWithPath: explicit) } | ||
|
|
||
| guard let libURL = LibIndexStoreProvider.inferLibPath() else { | ||
| throw ValidationError( | ||
| "Could not find 'swift' to infer toolchain path. Please specify --lib-index-store explicitly." | ||
| ) | ||
| } | ||
| return libURL | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.