diff --git a/Package.swift b/Package.swift index 5b6c5c4c..c3e27ba7 100644 --- a/Package.swift +++ b/Package.swift @@ -19,23 +19,16 @@ let package = Package( .package( url: "https://github.com/swiftlang/swift-tools-protocols", - revision: "f86b413a803761408e5e718912c46d75a340801b" + revision: "88612c51de4cbf636a6b948c64ea5ebd55b8a0ad" ), .package( url: "https://github.com/apple/swift-argument-parser", revision: "1.5.0" ), - .package( - url: "https://github.com/swiftlang/sourcekit-lsp", - revision: "0e061c5c1075152bc2e6187679a11b81d0c3e326" // latest main commit November 29, 2025 - // TODO: Ideally it would be better to upstream these changes to sourceKit-lsp - // url: "https://github.com/rockbruno/sourcekit-lsp", - // revision: "c052baae81ec6532bb2f939a21acc4650fb1dc86" - ), .package( url: "https://github.com/apple/swift-protobuf.git", - revision: "102a647b573f60f73afdce5613a51d71349fe507" + revision: "1.33.3" ), ], targets: [ @@ -78,7 +71,7 @@ let package = Package( ], resources: [ .copy("Resources/aquery.pb"), - .copy("Resources/streamdeps.pb"), + .copy("Resources/cquery.pb"), ], ), .target( @@ -98,7 +91,6 @@ let package = Package( dependencies: ["BazelProtobufBindings"], resources: [ .copy("Resources/actions.pb"), - .copy("Resources/streamdeps.pb"), ], ), ] diff --git a/Sources/BazelProtobufBindings/BazelProtobufBindings.swift b/Sources/BazelProtobufBindings/BazelProtobufBindings.swift index 9b5af5a2..12b0a694 100644 --- a/Sources/BazelProtobufBindings/BazelProtobufBindings.swift +++ b/Sources/BazelProtobufBindings/BazelProtobufBindings.swift @@ -20,116 +20,11 @@ import Foundation package enum BazelProtobufBindings { - package static func parseQueryTargets(data: Data) throws -> [BlazeQuery_Target] { - var targets: [BlazeQuery_Target] = [] - let messages = try parseMultipleDelimitedMessages(from: data) - for message in messages { - let target = try BlazeQuery_Target(serializedBytes: message) - targets.append(target) - } - - return targets - } - package static func parseActionGraph(data: Data) throws -> Analysis_ActionGraphContainer { try Analysis_ActionGraphContainer(serializedBytes: data) } -} - -extension BazelProtobufBindings { - /// Bazel query outputs a series of messages and each one is prefixed with length to indicate - /// the number of bytes in the payload. Returns a tuple of (value, bytesConsumed). - /// Protobuf [documentation](https://protobuf.dev/programming-guides/encoding/) provides more - /// details on how `varint` works. - private static func parseVarint( - from data: Data, - startIndex: Int - ) throws -> (UInt64, Int) { - guard startIndex < data.count else { - throw VarintError.truncated - } - - var result: UInt64 = 0 - var shift = 0 - var bytesRead = 0 - var index = startIndex - - while index < data.count { - let byte = data[index] - bytesRead += 1 - index += 1 - - // Check for overflow (varints can be at most 10 bytes for 64-bit values) - if bytesRead > 10 { - throw VarintError.overflow - } - - // Extract the 7 data bits - let dataBits = UInt64(byte & 0x7F) - - // Check for shift overflow - if shift >= 64 { - throw VarintError.overflow - } - - // little-endian -> big-endian - result |= dataBits << shift - - // If the continuation bit (MSB) is not set, we're done - if (byte & 0x80) == 0 { - return (result, bytesRead) - } - - shift += 7 - } - // If we get here, the varint was truncated - throw VarintError.truncated + package static func parseCqueryResult(data: Data) throws -> Analysis_CqueryResult { + try Analysis_CqueryResult(serializedBytes: data) } - - /// Parse the length prefix and return the message data - private static func parseDelimitedMessage( - from data: Data, - startIndex: Int = 0 - ) throws -> (Data, Int) { - let (messageLength, lengthBytes) = try parseVarint( - from: data, - startIndex: startIndex - ) - - let messageStart = startIndex + lengthBytes - let messageEnd = messageStart + Int(messageLength) - - guard messageEnd <= data.count else { - throw VarintError.truncated - } - - let messageData = data.subdata(in: messageStart.. [Data] { - var messages: [Data] = [] - var currentIndex = 0 - - while currentIndex < data.count { - let (messageData, bytesConsumed) = try parseDelimitedMessage( - from: data, - startIndex: currentIndex - ) - messages.append(messageData) - currentIndex += bytesConsumed - } - - return messages - } -} - -package enum VarintError: Error { - case truncated - case overflow - case invalidData } diff --git a/Sources/BazelProtobufBindings/protos/analysis_v2.pb.swift b/Sources/BazelProtobufBindings/protos/analysis_v2.pb.swift index 86c70ce8..9a6c0498 100644 --- a/Sources/BazelProtobufBindings/protos/analysis_v2.pb.swift +++ b/Sources/BazelProtobufBindings/protos/analysis_v2.pb.swift @@ -3,7 +3,7 @@ // swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. -// Source: analysis_v2.proto +// Source: Sources/BazelProtobufBindings/protos/analysis_v2.proto // // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ @@ -445,8 +445,8 @@ public struct Analysis_ConfiguredTarget: Sendable { /// the Target defined in this file because blaze_query.Target is much heavier /// and will output proto results similar to what users are familiar with from /// regular blaze query. - public var target: Analysis_Target { - get {return _target ?? Analysis_Target()} + public var target: BlazeQuery_Target { + get {return _target ?? BlazeQuery_Target()} set {_target = newValue} } /// Returns true if `target` has been explicitly set. @@ -475,7 +475,7 @@ public struct Analysis_ConfiguredTarget: Sendable { public init() {} - fileprivate var _target: Analysis_Target? = nil + fileprivate var _target: BlazeQuery_Target? = nil fileprivate var _configuration: Analysis_Configuration? = nil } @@ -540,16 +540,7 @@ fileprivate let _protobuf_package = "analysis" extension Analysis_ActionGraphContainer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ActionGraphContainer" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "artifacts"), - 2: .same(proto: "actions"), - 3: .same(proto: "targets"), - 4: .standard(proto: "dep_set_of_files"), - 5: .same(proto: "configuration"), - 6: .standard(proto: "aspect_descriptors"), - 7: .standard(proto: "rule_classes"), - 8: .standard(proto: "path_fragments"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}artifacts\0\u{1}actions\0\u{1}targets\0\u{3}dep_set_of_files\0\u{1}configuration\0\u{3}aspect_descriptors\0\u{3}rule_classes\0\u{3}path_fragments\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -614,11 +605,7 @@ extension Analysis_ActionGraphContainer: SwiftProtobuf.Message, SwiftProtobuf._M extension Analysis_Artifact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Artifact" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .standard(proto: "path_fragment_id"), - 3: .standard(proto: "is_tree_artifact"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{3}path_fragment_id\0\u{3}is_tree_artifact\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -658,30 +645,7 @@ extension Analysis_Artifact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem extension Analysis_Action: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Action" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap( - reservedNames: [], - reservedRanges: [20..<21], - numberNameMappings: [ - 1: .standard(proto: "target_id"), - 2: .standard(proto: "aspect_descriptor_ids"), - 3: .standard(proto: "action_key"), - 4: .same(proto: "mnemonic"), - 5: .standard(proto: "configuration_id"), - 6: .same(proto: "arguments"), - 7: .standard(proto: "environment_variables"), - 8: .standard(proto: "input_dep_set_ids"), - 9: .standard(proto: "output_ids"), - 10: .standard(proto: "discovers_inputs"), - 11: .standard(proto: "execution_info"), - 12: .standard(proto: "param_files"), - 13: .standard(proto: "primary_output_id"), - 14: .standard(proto: "execution_platform"), - 15: .standard(proto: "template_content"), - 16: .same(proto: "substitutions"), - 17: .standard(proto: "file_contents"), - 18: .standard(proto: "unresolved_symlink_target"), - 19: .standard(proto: "is_executable"), - ]) + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}target_id\0\u{3}aspect_descriptor_ids\0\u{3}action_key\0\u{1}mnemonic\0\u{3}configuration_id\0\u{1}arguments\0\u{3}environment_variables\0\u{3}input_dep_set_ids\0\u{3}output_ids\0\u{3}discovers_inputs\0\u{3}execution_info\0\u{3}param_files\0\u{3}primary_output_id\0\u{3}execution_platform\0\u{3}template_content\0\u{1}substitutions\0\u{3}file_contents\0\u{3}unresolved_symlink_target\0\u{3}is_executable\0\u{c}\u{14}\u{1}") fileprivate class _StorageClass { var _targetID: UInt32 = 0 @@ -873,11 +837,7 @@ extension Analysis_Action: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen extension Analysis_Target: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Target" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "label"), - 3: .standard(proto: "rule_class_id"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}label\0\u{3}rule_class_id\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -917,10 +877,7 @@ extension Analysis_Target: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen extension Analysis_RuleClass: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".RuleClass" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "name"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}name\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -955,11 +912,7 @@ extension Analysis_RuleClass: SwiftProtobuf.Message, SwiftProtobuf._MessageImple extension Analysis_AspectDescriptor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".AspectDescriptor" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "name"), - 3: .same(proto: "parameters"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}name\0\u{1}parameters\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -999,11 +952,7 @@ extension Analysis_AspectDescriptor: SwiftProtobuf.Message, SwiftProtobuf._Messa extension Analysis_DepSetOfFiles: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".DepSetOfFiles" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .standard(proto: "transitive_dep_set_ids"), - 3: .standard(proto: "direct_artifact_ids"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{3}transitive_dep_set_ids\0\u{3}direct_artifact_ids\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1043,15 +992,7 @@ extension Analysis_DepSetOfFiles: SwiftProtobuf.Message, SwiftProtobuf._MessageI extension Analysis_Configuration: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Configuration" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "mnemonic"), - 3: .standard(proto: "platform_name"), - 4: .same(proto: "checksum"), - 5: .standard(proto: "is_tool"), - 6: .same(proto: "fragments"), - 7: .standard(proto: "fragment_options"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}mnemonic\0\u{3}platform_name\0\u{1}checksum\0\u{3}is_tool\0\u{1}fragments\0\u{3}fragment_options\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1111,10 +1052,7 @@ extension Analysis_Configuration: SwiftProtobuf.Message, SwiftProtobuf._MessageI extension Analysis_Fragment: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Fragment" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "name"), - 2: .standard(proto: "fragment_option_names"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{3}fragment_option_names\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1149,10 +1087,7 @@ extension Analysis_Fragment: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem extension Analysis_FragmentOptions: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".FragmentOptions" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "name"), - 2: .same(proto: "options"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{1}options\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1187,10 +1122,7 @@ extension Analysis_FragmentOptions: SwiftProtobuf.Message, SwiftProtobuf._Messag extension Analysis_Option: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Option" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "name"), - 2: .same(proto: "value"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{1}value\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1229,10 +1161,7 @@ extension Analysis_Option: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen extension Analysis_KeyValuePair: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".KeyValuePair" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "key"), - 2: .same(proto: "value"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}key\0\u{1}value\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1267,11 +1196,12 @@ extension Analysis_KeyValuePair: SwiftProtobuf.Message, SwiftProtobuf._MessageIm extension Analysis_ConfiguredTarget: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ConfiguredTarget" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "target"), - 2: .same(proto: "configuration"), - 3: .standard(proto: "configuration_id"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}target\0\u{1}configuration\0\u{3}configuration_id\0") + + public var isInitialized: Bool { + if let v = self._target, !v.isInitialized {return false} + return true + } public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1315,10 +1245,12 @@ extension Analysis_ConfiguredTarget: SwiftProtobuf.Message, SwiftProtobuf._Messa extension Analysis_CqueryResult: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".CqueryResult" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "results"), - 2: .same(proto: "configurations"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}results\0\u{1}configurations\0") + + public var isInitialized: Bool { + if !SwiftProtobuf.Internal.areAllInitialized(self.results) {return false} + return true + } public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1353,10 +1285,7 @@ extension Analysis_CqueryResult: SwiftProtobuf.Message, SwiftProtobuf._MessageIm extension Analysis_ParamFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ParamFile" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "exec_path"), - 2: .same(proto: "arguments"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}exec_path\0\u{1}arguments\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -1391,11 +1320,7 @@ extension Analysis_ParamFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImple extension Analysis_PathFragment: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".PathFragment" - public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "id"), - 2: .same(proto: "label"), - 3: .standard(proto: "parent_id"), - ] + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}label\0\u{3}parent_id\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/Sources/BazelProtobufBindings/protos/analysis_v2.proto b/Sources/BazelProtobufBindings/protos/analysis_v2.proto index 49768646..23392af0 100644 --- a/Sources/BazelProtobufBindings/protos/analysis_v2.proto +++ b/Sources/BazelProtobufBindings/protos/analysis_v2.proto @@ -16,6 +16,8 @@ syntax = "proto3"; package analysis; +import "Sources/BazelProtobufBindings/protos/build.proto"; + option java_package = "com.google.devtools.build.lib.analysis"; option java_outer_classname = "AnalysisProtosV2"; @@ -249,7 +251,7 @@ message ConfiguredTarget { // the Target defined in this file because blaze_query.Target is much heavier // and will output proto results similar to what users are familiar with from // regular blaze query. - Target target = 1; + blaze_query.Target target = 1; // DEPRECATED. Use configuration_id instead. Configuration configuration = 2 [deprecated = true]; diff --git a/Sources/BazelProtobufBindings/protos/build.proto b/Sources/BazelProtobufBindings/protos/build.proto index eda7ca34..353d8b2b 100644 --- a/Sources/BazelProtobufBindings/protos/build.proto +++ b/Sources/BazelProtobufBindings/protos/build.proto @@ -19,7 +19,7 @@ syntax = "proto2"; package blaze_query; -import "stardoc_output.proto"; +import "Sources/BazelProtobufBindings/protos/stardoc_output.proto"; // option cc_api_version = 2; // option java_api_version = 1; diff --git a/Sources/BazelProtobufBindings/protos/stardoc_output.proto b/Sources/BazelProtobufBindings/protos/stardoc_output.proto index 22f68f99..cf6bb056 100644 --- a/Sources/BazelProtobufBindings/protos/stardoc_output.proto +++ b/Sources/BazelProtobufBindings/protos/stardoc_output.proto @@ -48,6 +48,8 @@ message ModuleInfo { repeated RepositoryRuleInfo repository_rule_info = 8; repeated MacroInfo macro_info = 9; + + repeated StarlarkOtherSymbolInfo starlark_other_symbol_info = 10; } // Representation of a Starlark rule attribute type. These generally @@ -168,6 +170,10 @@ message AttributeInfo { // If true, the attribute is defined in Bazel's native code, not in Starlark. bool natively_defined = 8; + + // If non-empty, the string representations of the allowed values for this + // attribute. + repeated string values = 9; } // Representation of a set of providers. @@ -397,6 +403,21 @@ message RepositoryRuleInfo { OriginKey origin_key = 5; } +// Representation of a Starlark symbol whose value is of type which lacks +// built-in docstring and needs to be documented using `#:`-prefixed doc +// comments; for example, a bool, dict, float, int, list, range, set, string, +// struct, or tuple. +message StarlarkOtherSymbolInfo { + // The name of the symbol. + string name = 1; + + // The content of the symbol's doc comments. + string doc = 2; + + // The symbol's value's data type. + string type_name = 3; +} + // Representation of the origin of a rule, provider, aspect, or function. // Intended to be used for building unambiguous cross-references: for example, // between an element of a ProviderNameGroup required by a rule attribute and diff --git a/Sources/SourceKitBazelBSP/RequestHandlers/BuildTargets/BazelTargetQuerier.swift b/Sources/SourceKitBazelBSP/RequestHandlers/BuildTargets/BazelTargetQuerier.swift index 18185b6f..34982400 100644 --- a/Sources/SourceKitBazelBSP/RequestHandlers/BuildTargets/BazelTargetQuerier.swift +++ b/Sources/SourceKitBazelBSP/RequestHandlers/BuildTargets/BazelTargetQuerier.swift @@ -25,13 +25,11 @@ private let logger = makeFileLevelBSPLogger() enum BazelTargetQuerierError: Error, LocalizedError { case noKinds case noTargets - case invalidQueryOutput var errorDescription: String? { switch self { case .noKinds: return "A list of kinds is necessary to query targets" case .noTargets: return "A list of targets is necessary to query targets" - case .invalidQueryOutput: return "Query output is not valid XML" } } } @@ -94,7 +92,9 @@ final class BazelTargetQuerier { return cached } - let cmd = "query '\(topLevelTargetsQuery)' --notool_deps --noimplicit_deps --output streamed_proto" + // We use cquery here because we are interested on what's actually compiled. + // Also, this shares more analysis cache compared to the regular query. + let cmd = "cquery '\(topLevelTargetsQuery)' --notool_deps --noimplicit_deps --output proto" let output: Data = try commandRunner.bazelIndexAction( baseConfig: config.baseConfig, outputBase: config.outputBase, @@ -102,9 +102,9 @@ final class BazelTargetQuerier { rootUri: config.rootUri ) - guard let targets = try? BazelProtobufBindings.parseQueryTargets(data: output) else { - throw BazelTargetQuerierError.invalidQueryOutput - } + let result = try BazelProtobufBindings.parseCqueryResult(data: output) + + let targets = result.results.map { $0.target } logger.debug("Parsed \(targets.count, privacy: .public) targets for cache key: \(cacheKey, privacy: .public)") queryCache[cacheKey] = targets @@ -140,7 +140,9 @@ final class BazelTargetQuerier { return cached } - let cmd = "query \"kind('\(kindsFilter)', \(depsQuery))\" --output graph" + // We use cquery here because we are interested on what's actually compiled. + // Also, this shares more analysis cache compared to the regular query. + let cmd = "cquery \"kind('\(kindsFilter)', \(depsQuery))\" --output graph" let output: String = try commandRunner.bazelIndexAction( baseConfig: config.baseConfig, outputBase: config.outputBase, @@ -155,12 +157,14 @@ final class BazelTargetQuerier { for line in rawGraph { let parts = line.components(separatedBy: "\"") // Example line: - // "//path/to/target" -> "//path/to/target2\n//path/to/target3" + // "//path/to/target (abc)" -> "//path/to/target2 (abc)\n//path/to/target3 (abc)" guard parts.count == 5 else { continue } - let source = parts[1] - let targets = parts[3].components(separatedBy: "\n") + let source = parts[1].components(separatedBy: " (")[0] + let targets = parts[3].components(separatedBy: "\n").map { + $0.components(separatedBy: " (")[0] + } graph[source, default: []].append(contentsOf: targets) } diff --git a/Tests/BazelProtobufBindingsTests/BazelProtobufBindingsTests.swift b/Tests/BazelProtobufBindingsTests/BazelProtobufBindingsTests.swift index a7b45414..13566ae3 100644 --- a/Tests/BazelProtobufBindingsTests/BazelProtobufBindingsTests.swift +++ b/Tests/BazelProtobufBindingsTests/BazelProtobufBindingsTests.swift @@ -107,31 +107,4 @@ struct BazelProtobufBindingsTests { #expect(actual == expected) } - - @Test - func decodesStreamProto() throws { - guard let url = Bundle.module.url(forResource: "streamdeps", withExtension: "pb"), - let data = try? Data(contentsOf: url) - else { - Issue.record("Failed get streamdeps.pb") - return - } - - let targets = try BazelProtobufBindings.parseQueryTargets(data: data) - - let actual = - targets - .filter({ $0.rule.ruleClass == "swift_library" }) - .map(\.rule.name) - .sorted() - - let expected = [ - "//HelloWorld:ExpandedTemplate", - "//HelloWorld:GeneratedDummy", - "//HelloWorld:HelloWorldLib", - "//HelloWorld:TodoModels", - ].sorted() - - #expect(actual == expected) - } } diff --git a/Tests/BazelProtobufBindingsTests/Resources/streamdeps.pb b/Tests/BazelProtobufBindingsTests/Resources/streamdeps.pb deleted file mode 100644 index f2ad7174..00000000 Binary files a/Tests/BazelProtobufBindingsTests/Resources/streamdeps.pb and /dev/null differ diff --git a/Tests/SourceKitBazelBSPTests/BazelTargetParserTests.swift b/Tests/SourceKitBazelBSPTests/BazelTargetParserTests.swift index 6fef15a0..78bb9491 100644 --- a/Tests/SourceKitBazelBSPTests/BazelTargetParserTests.swift +++ b/Tests/SourceKitBazelBSPTests/BazelTargetParserTests.swift @@ -24,10 +24,7 @@ import Testing @Suite struct BazelTargetParserTests { - /// Tests that BazelQueryParser correctly processes protobuf query results. - /// The test uses real protobuf data from streamdeps.pb to ensure the parser handles - @Test("With given ServerConfig, ensure target parser output is correct") - func testBazelQueryParser() throws { + func canParseCqueryResult() throws { let config = BaseServerConfig( bazelWrapper: "bazel", targets: ["//HelloWorld:HelloWorld"], @@ -53,11 +50,11 @@ struct BazelTargetParserTests { let querier = BazelTargetQuerier(commandRunner: runner) let toolchainPath = "/path/to/toolchain" let command = - "bazel --output_base=/path/to/output/base query \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"objc_library|source file|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto" + "bazel --output_base=/path/to/output/base cquery \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"objc_library|source file|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto" let dependencyKinds: Set = ["objc_library", "source file", "swift_library"] - runner.setResponse(for: command, cwd: rootUri, response: mockProtobuf) + runner.setResponse(for: command, cwd: rootUri, response: exampleCqueryOutput) let targets = try querier.queryTargets( config: initializedConfig, diff --git a/Tests/SourceKitBazelBSPTests/BazelTargetQuerierTests.swift b/Tests/SourceKitBazelBSPTests/BazelTargetQuerierTests.swift index 06c97479..888607aa 100644 --- a/Tests/SourceKitBazelBSPTests/BazelTargetQuerierTests.swift +++ b/Tests/SourceKitBazelBSPTests/BazelTargetQuerierTests.swift @@ -52,8 +52,8 @@ struct BazelTargetQuerierTests { ) let expectedCommand = - "bazelisk --output_base=/path/to/output/base query \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"source file|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto --config=test" - runnerMock.setResponse(for: expectedCommand, cwd: mockRootUri, response: mockProtobuf) + "bazelisk --output_base=/path/to/output/base cquery \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"source file|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output proto --config=test" + runnerMock.setResponse(for: expectedCommand, cwd: mockRootUri, response: exampleCqueryOutput) let kinds: Set = ["source file", "swift_library"] let result = try querier.queryTargets( @@ -95,8 +95,8 @@ struct BazelTargetQuerierTests { ) let expectedCommand = - "bazelisk --output_base=/path/to/output/base query \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld //Tests:Tests)) in $topLevelTargets union kind(\"objc_library|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto --config=test" - runnerMock.setResponse(for: expectedCommand, cwd: mockRootUri, response: mockProtobuf) + "bazelisk --output_base=/path/to/output/base cquery \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld //Tests:Tests)) in $topLevelTargets union kind(\"objc_library|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output proto --config=test" + runnerMock.setResponse(for: expectedCommand, cwd: mockRootUri, response: exampleCqueryOutput) let kinds: Set = ["objc_library", "swift_library"] let result = try querier.queryTargets( @@ -148,15 +148,15 @@ struct BazelTargetQuerierTests { runnerMock.setResponse( for: - "bazel --output_base=/path/to/output/base query \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto", + "bazel --output_base=/path/to/output/base cquery \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output proto", cwd: mockRootUri, - response: mockProtobuf + response: exampleCqueryOutput ) runnerMock.setResponse( for: - "bazel --output_base=/path/to/output/base query \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"objc_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto", + "bazel --output_base=/path/to/output/base cquery \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"objc_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output proto", cwd: mockRootUri, - response: mockProtobuf + response: exampleCqueryOutput ) try run(dependencyKinds: kinds) @@ -177,7 +177,6 @@ struct BazelTargetQuerierTests { #expect(runnerMock.commands.count == 2) } - @Test("With given ServerConfig, ensure query is correct") func executeCorrectBazelCommandProto() throws { let runner = CommandRunnerFake() let querier = BazelTargetQuerier(commandRunner: runner) @@ -203,15 +202,9 @@ struct BazelTargetQuerierTests { ) let command = - "bazel --output_base=/path/to/output/base query \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"objc_library|source file|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output streamed_proto" - guard let url = Bundle.module.url(forResource: "streamdeps", withExtension: "pb"), - let data = try? Data(contentsOf: url) - else { - Issue.record("Failed get streamdeps.pb") - return - } + "bazel --output_base=/path/to/output/base cquery \'let topLevelTargets = kind(\"rule\", set(//HelloWorld:HelloWorld)) in $topLevelTargets union kind(\"objc_library|source file|swift_library\", deps($topLevelTargets))\' --notool_deps --noimplicit_deps --output proto" - runner.setResponse(for: command, cwd: rootUri, response: data) + runner.setResponse(for: command, cwd: rootUri, response: exampleCqueryOutput) let dependencyKinds: Set = ["objc_library", "source file", "swift_library"] @@ -233,11 +226,9 @@ struct BazelTargetQuerierTests { } } -let mockProtobuf: Data = { - guard let url = Bundle.module.url(forResource: "streamdeps", withExtension: "pb"), - let data = try? Data(contentsOf: url) - else { - fatalError("Failed get streamdeps.pb") - } +let exampleCqueryOutput: Data = { + guard let url = Bundle.module.url(forResource: "cquery", withExtension: "pb"), + let data = try? Data.init(contentsOf: url) + else { fatalError("cquery.pb is not found in Resources folder") } return data }() diff --git a/Tests/SourceKitBazelBSPTests/Resources/cquery.pb b/Tests/SourceKitBazelBSPTests/Resources/cquery.pb new file mode 100644 index 00000000..e559f7d2 Binary files /dev/null and b/Tests/SourceKitBazelBSPTests/Resources/cquery.pb differ diff --git a/Tests/SourceKitBazelBSPTests/Resources/streamdeps.pb b/Tests/SourceKitBazelBSPTests/Resources/streamdeps.pb deleted file mode 100644 index f2ad7174..00000000 Binary files a/Tests/SourceKitBazelBSPTests/Resources/streamdeps.pb and /dev/null differ