From e66141db9ddc89e68e12c6349626be6f6b25f742 Mon Sep 17 00:00:00 2001 From: Bruno Henrique da Rocha e Silva Date: Tue, 16 Dec 2025 19:05:05 +0100 Subject: [PATCH] Add support for using only one output base --- Example/HelloWorld/BUILD | 1 + .../RequestHandlers/InitializeHandler.swift | 17 +++++++++---- .../Server/BaseServerConfig.swift | 5 +++- .../Server/InitializedServerConfig.swift | 2 ++ .../SharedUtils/Shell/CommandRunner.swift | 25 ++++++++++--------- .../sourcekit-bazel-bsp/Commands/Serve.swift | 9 ++++++- rules/setup_sourcekit_bsp.bzl | 6 +++++ 7 files changed, 46 insertions(+), 19 deletions(-) diff --git a/Example/HelloWorld/BUILD b/Example/HelloWorld/BUILD index c55543aa..2eaf646d 100644 --- a/Example/HelloWorld/BUILD +++ b/Example/HelloWorld/BUILD @@ -263,4 +263,5 @@ setup_sourcekit_bsp( targets = [ "//HelloWorld/...", ], + no_extra_output_base = True, ) diff --git a/Sources/SourceKitBazelBSP/RequestHandlers/InitializeHandler.swift b/Sources/SourceKitBazelBSP/RequestHandlers/InitializeHandler.swift index 399c74d4..ced4b736 100644 --- a/Sources/SourceKitBazelBSP/RequestHandlers/InitializeHandler.swift +++ b/Sources/SourceKitBazelBSP/RequestHandlers/InitializeHandler.swift @@ -85,11 +85,18 @@ final class InitializeHandler { logger.debug("regularOutputBase: \(regularOutputBase, privacy: .public)") // Setup the special output base path where we will run indexing commands from. - let regularOutputBaseLastPath = regularOutputBase.lastPathComponent - let outputBase = regularOutputBase.deletingLastPathComponent().appendingPathComponent( - "\(regularOutputBaseLastPath)-sourcekit-bazel-bsp" - ).path - logger.debug("outputBase: \(outputBase, privacy: .public)") + let outputBase: String + if baseConfig.noExtraOutputBase { + outputBase = regularOutputBase.path + logger.debug("Will use the regular output base for all actions") + } else { + let regularOutputBaseLastPath = regularOutputBase.lastPathComponent + outputBase = + regularOutputBase.deletingLastPathComponent().appendingPathComponent( + "\(regularOutputBaseLastPath)-sourcekit-bazel-bsp" + ).path + logger.debug("outputBase: \(outputBase, privacy: .public)") + } // Now, get the full output path based on the above output base. let outputPath: String = try commandRunner.bazelIndexAction( diff --git a/Sources/SourceKitBazelBSP/Server/BaseServerConfig.swift b/Sources/SourceKitBazelBSP/Server/BaseServerConfig.swift index 238f8d00..526a13fa 100644 --- a/Sources/SourceKitBazelBSP/Server/BaseServerConfig.swift +++ b/Sources/SourceKitBazelBSP/Server/BaseServerConfig.swift @@ -34,6 +34,7 @@ package struct BaseServerConfig: Equatable { let dependencyRulesToDiscover: [DependencyRuleType] let topLevelTargetsToExclude: [String] let dependencyTargetsToExclude: [String] + let noExtraOutputBase: Bool package init( bazelWrapper: String, @@ -44,7 +45,8 @@ package struct BaseServerConfig: Equatable { topLevelRulesToDiscover: [TopLevelRuleType] = TopLevelRuleType.allCases, dependencyRulesToDiscover: [DependencyRuleType] = DependencyRuleType.allCases, topLevelTargetsToExclude: [String] = [], - dependencyTargetsToExclude: [String] = [] + dependencyTargetsToExclude: [String] = [], + noExtraOutputBase: Bool = false ) { self.bazelWrapper = bazelWrapper self.targets = targets @@ -55,5 +57,6 @@ package struct BaseServerConfig: Equatable { self.dependencyRulesToDiscover = dependencyRulesToDiscover self.topLevelTargetsToExclude = topLevelTargetsToExclude self.dependencyTargetsToExclude = dependencyTargetsToExclude + self.noExtraOutputBase = noExtraOutputBase } } diff --git a/Sources/SourceKitBazelBSP/Server/InitializedServerConfig.swift b/Sources/SourceKitBazelBSP/Server/InitializedServerConfig.swift index c341e96c..42cc1cc3 100644 --- a/Sources/SourceKitBazelBSP/Server/InitializedServerConfig.swift +++ b/Sources/SourceKitBazelBSP/Server/InitializedServerConfig.swift @@ -38,6 +38,8 @@ struct InitializedServerConfig: Equatable { } var indexStorePath: String { + // This is the same path hardcoded by rules_swift + // when the global index store feature is enabled. outputPath + "/_global_index_store" } } diff --git a/Sources/SourceKitBazelBSP/SharedUtils/Shell/CommandRunner.swift b/Sources/SourceKitBazelBSP/SharedUtils/Shell/CommandRunner.swift index 6b4f5b0c..ec318d2d 100644 --- a/Sources/SourceKitBazelBSP/SharedUtils/Shell/CommandRunner.swift +++ b/Sources/SourceKitBazelBSP/SharedUtils/Shell/CommandRunner.swift @@ -85,20 +85,21 @@ extension CommandRunner { additionalStartupFlags: [String] = [] ) -> String { let indexFlags = baseConfig.indexFlags - let additionalFlags = additionalFlags.map { " \($0)" } - let flagsString: String - if indexFlags.isEmpty { - flagsString = additionalFlags.joined(separator: "") - } else { - flagsString = (additionalFlags + indexFlags.map { " \($0)" }).joined(separator: "") + var components: [String] = [] + if !baseConfig.noExtraOutputBase { + components.append("--output_base=\(outputBase)") } - let startupFlagsString: String - if additionalStartupFlags.isEmpty { - startupFlagsString = "" - } else { - startupFlagsString = " " + additionalStartupFlags.joined(separator: " ") + if !additionalStartupFlags.isEmpty { + components.append(contentsOf: additionalStartupFlags) } - return "--output_base=\(outputBase)\(startupFlagsString) \(cmd)\(flagsString)" + components.append(cmd) + if !additionalFlags.isEmpty { + components.append(contentsOf: additionalFlags) + } + if !indexFlags.isEmpty { + components.append(contentsOf: indexFlags) + } + return components.joined(separator: " ") } /// A regular bazel command, but at this BSP's special output base and taking into account the special index flags. diff --git a/Sources/sourcekit-bazel-bsp/Commands/Serve.swift b/Sources/sourcekit-bazel-bsp/Commands/Serve.swift index ca2ae80d..cc61cd3f 100644 --- a/Sources/sourcekit-bazel-bsp/Commands/Serve.swift +++ b/Sources/sourcekit-bazel-bsp/Commands/Serve.swift @@ -79,6 +79,12 @@ struct Serve: ParsableCommand { ) var dependencyTargetToExclude: [String] = [] + @Flag( + help: + "If enabled, the BSP will not create a separate output base for its indexing actions. You can use this in conjunction with rules_swift's `index_while_building` and `use_global_module_cache` to improve indexing performance and reduce disk usage at the cost of potentially slower builds." + ) + var noExtraOutputBase: Bool = false + func run() throws { logger.info("`serve` invoked, initializing BSP server...") @@ -98,7 +104,8 @@ struct Serve: ParsableCommand { topLevelRulesToDiscover: topLevelRulesToDiscover, dependencyRulesToDiscover: dependencyRulesToDiscover, topLevelTargetsToExclude: topLevelTargetToExclude, - dependencyTargetsToExclude: dependencyTargetToExclude + dependencyTargetsToExclude: dependencyTargetToExclude, + noExtraOutputBase: noExtraOutputBase ) logger.debug("Initializing BSP with targets: \(targets)") diff --git a/rules/setup_sourcekit_bsp.bzl b/rules/setup_sourcekit_bsp.bzl index fb260b6d..214df227 100644 --- a/rules/setup_sourcekit_bsp.bzl +++ b/rules/setup_sourcekit_bsp.bzl @@ -35,6 +35,8 @@ def _setup_sourcekit_bsp_impl(ctx): if files_to_watch: bsp_config_argv.append("--files-to-watch") bsp_config_argv.append(files_to_watch) + if ctx.attr.no_extra_output_base: + bsp_config_argv.append("--no-extra-output-base") ctx.actions.expand_template( template = ctx.file._bsp_config_template, output = rendered_bsp_config, @@ -146,5 +148,9 @@ setup_sourcekit_bsp = rule( doc = "Instead of attempting to build targets individually, build the top-level parent. If your project contains build_test targets for your individual libraries and you're passing them as the top-level targets for the BSP, you can use this flag to build those targets directly for better predictability and caching.", default = False, ), + "no_extra_output_base": attr.bool( + doc = "If enabled, the BSP will not create a separate output base for its indexing actions. You can use this in conjunction with rules_swift's `index_while_building` and `use_global_module_cache` to improve indexing performance and reduce disk usage at the cost of potentially slower builds.", + default = False, + ), }, )