Skip to content

Commit 9b9d124

Browse files
committed
Use --output=streamed_proto and --output_file if supported
1 parent e07830e commit 9b9d124

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

.bazelrc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@ run -c opt --show_loading_progress=false --show_progress=false --ui_event_filter
22
run:verbose -c dbg --show_loading_progress=true --show_progress=true --ui_event_filters=info,error,debug
33
# https://github.com/mockito/mockito/issues/1879
44
test --sandbox_tmpfs_path=/tmp
5+
6+
# Use a hermetic JDK for tests.
7+
common --java_runtime_version=remotejdk_21
8+
9+
# Avoid cache thrashing, but allow integration tests to find "bazel" on the PATH.
10+
common --incompatible_strict_action_env
11+
common --test_env=PATH

cli/src/main/kotlin/com/bazel_diff/bazel/BazelQueryService.kt

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import kotlinx.coroutines.runBlocking
1313
import org.koin.core.component.KoinComponent
1414
import org.koin.core.component.inject
1515

16+
private val versionComparator = compareBy<Triple<Int, Int, Int>> { it.first }
17+
.thenBy { it.second }
18+
.thenBy { it.third }
19+
1620
class BazelQueryService(
1721
private val workingDirectory: Path,
1822
private val bazelPath: Path,
@@ -23,6 +27,44 @@ class BazelQueryService(
2327
private val noBazelrc: Boolean,
2428
) : KoinComponent {
2529
private val logger: Logger by inject()
30+
private val version: Triple<Int, Int, Int> by lazy {
31+
runBlocking { determineBazelVersion() }
32+
}
33+
34+
@OptIn(ExperimentalCoroutinesApi::class)
35+
private suspend fun determineBazelVersion(): Triple<Int, Int, Int> {
36+
val cmd = arrayOf(bazelPath.toString(), "--version")
37+
logger.i { "Executing Bazel version command: ${cmd.joinToString()}" }
38+
val result = process(
39+
*cmd,
40+
stdout = Redirect.CAPTURE,
41+
workingDirectory = workingDirectory.toFile(),
42+
stderr = Redirect.PRINT,
43+
destroyForcibly = true,
44+
)
45+
46+
if (result.resultCode != 0) {
47+
throw RuntimeException("Bazel version command failed, exit code ${result.resultCode}")
48+
}
49+
50+
if (result.output.size != 1 || !result.output.first().startsWith("bazel ")) {
51+
throw RuntimeException("Bazel version command returned unexpected output: ${result.output}")
52+
}
53+
// Trim off any prerelease suffixes.
54+
val versionString = result.output.first().removePrefix("bazel ").trim().split('-')[0]
55+
val version = versionString.split('.').map { it.toInt() }.toTypedArray()
56+
return Triple(version[0], version[1], version[2])
57+
}
58+
59+
// Use streamed_proto output for cquery if available. This is more efficient than the proto output.
60+
// https://github.com/bazelbuild/bazel/commit/607d0f7335f95aa0ee236ba3c18ce2a232370cdb
61+
private val canUseStreamedProtoWithCquery
62+
get() = versionComparator.compare(version, Triple(7, 0, 0)) >= 0
63+
64+
// Use an output file for (c)query if supported. This avoids excessively large stdout, which is sent out on the BES.
65+
// https://github.com/bazelbuild/bazel/commit/514e9052f2c603c53126fbd9436bdd3ad3a1b0c7
66+
private val canUseOutputFile
67+
get() = versionComparator.compare(version, Triple(8, 2, 0)) >= 0
2668

2769
suspend fun query(query: String, useCquery: Boolean = false): List<BazelTarget> {
2870
// Unfortunately, there is still no direct way to tell if a target is compatible or not with the
@@ -44,10 +86,21 @@ class BazelQueryService(
4486
val targets =
4587
outputFile.inputStream().buffered().use { proto ->
4688
if (useCquery) {
47-
val cqueryResult = AnalysisProtosV2.CqueryResult.parseFrom(proto)
48-
cqueryResult.resultsList
49-
.mapNotNull { toBazelTarget(it.target) }
50-
.filter { it.name in compatibleTargetSet }
89+
if (canUseStreamedProtoWithCquery) {
90+
mutableListOf<AnalysisProtosV2.CqueryResult>()
91+
.apply {
92+
while (true) {
93+
val result = AnalysisProtosV2.CqueryResult.parseDelimitedFrom(proto) ?: break
94+
// EOF
95+
add(result)
96+
}
97+
}
98+
.flatMap { it.resultsList }
99+
} else {
100+
AnalysisProtosV2.CqueryResult.parseFrom(proto).resultsList
101+
}
102+
.mapNotNull { toBazelTarget(it.target) }
103+
.filter { it.name in compatibleTargetSet }
51104
} else {
52105
mutableListOf<Build.Target>()
53106
.apply {
@@ -98,9 +151,9 @@ class BazelQueryService(
98151
if (outputCompatibleTargets) {
99152
add("starlark")
100153
add("--starlark:file")
101-
val cqueryOutputFile = Files.createTempFile(null, ".cquery").toFile()
102-
cqueryOutputFile.deleteOnExit()
103-
cqueryOutputFile.writeText(
154+
val cqueryStarlarkFile = Files.createTempFile(null, ".cquery").toFile()
155+
cqueryStarlarkFile.deleteOnExit()
156+
cqueryStarlarkFile.writeText(
104157
"""
105158
def format(target):
106159
if providers(target) == None:
@@ -112,13 +165,11 @@ class BazelQueryService(
112165
return str(target.label)
113166
return ""
114167
"""
115-
.trimIndent())
116-
add(cqueryOutputFile.toString())
168+
.trimIndent()
169+
)
170+
add(cqueryStarlarkFile.toString())
117171
} else {
118-
// Unfortunately, cquery does not support streamed_proto yet.
119-
// See https://github.com/bazelbuild/bazel/issues/17743. This poses an issue for large
120-
// monorepos.
121-
add("proto")
172+
add(if (canUseStreamedProtoWithCquery) "streamed_proto" else "proto")
122173
}
123174
} else {
124175
add("streamed_proto")
@@ -137,19 +188,21 @@ class BazelQueryService(
137188
}
138189
add("--query_file")
139190
add(queryFile.toString())
191+
if (canUseOutputFile) {
192+
add("--output_file")
193+
add(outputFile.toString())
194+
}
140195
}
141196

142197
logger.i { "Executing Query: $query" }
143198
logger.i { "Command: ${cmd.toTypedArray().joinToString()}" }
144-
val result = runBlocking {
145-
process(
199+
val result = process(
146200
*cmd.toTypedArray(),
147-
stdout = Redirect.ToFile(outputFile),
201+
stdout = if (canUseOutputFile) Redirect.SILENT else Redirect.ToFile(outputFile),
148202
workingDirectory = workingDirectory.toFile(),
149203
stderr = Redirect.PRINT,
150204
destroyForcibly = true,
151205
)
152-
}
153206

154207
if (result.resultCode != 0)
155208
throw RuntimeException("Bazel query failed, exit code ${result.resultCode}")

0 commit comments

Comments
 (0)