Skip to content
This repository was archived by the owner on Aug 5, 2024. It is now read-only.

Commit dc830f8

Browse files
gottagofaster236Space Team
authored andcommitted
[feature] Jetpack Compose preview support | #BAZEL-868
[fix] Explicitly build the Android Kotlin target during build [feature] buildTarget/jvmBinaryJars request for Jetpack Compose preview [maintenance] AndroidBuildTarget should use String instead of URI to conform with BSP [maintenance] Move Android resource calculation to the Android plugin Merge-request: BAZEL-MR-947 Merged-by: Lev Leontev <[email protected]>
1 parent da0d47e commit dc830f8

File tree

15 files changed

+135
-28
lines changed

15 files changed

+135
-28
lines changed

protocol/src/main/kotlin/org/jetbrains/bsp/AndroidBuildTarget.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.jetbrains.bsp
33
import ch.epfl.scala.bsp4j.JvmBuildTarget
44
import com.google.gson.annotations.JsonAdapter
55
import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapter
6-
import java.net.URI
76

87
@JsonAdapter(EnumTypeAdapter.Factory::class)
98
public enum class AndroidTargetType(public val value: Int) {
@@ -13,10 +12,10 @@ public enum class AndroidTargetType(public val value: Int) {
1312
}
1413

1514
public data class AndroidBuildTarget(
16-
val androidJar: URI,
15+
val androidJar: String,
1716
val androidTargetType: AndroidTargetType,
18-
val manifest: URI?,
19-
val resourceFolders: List<URI>,
17+
val manifest: String?,
18+
val resourceFolders: List<String>,
2019
var jvmBuildTarget: JvmBuildTarget? = null,
2120
var kotlinBuildTarget: KotlinBuildTarget? = null,
2221
)

protocol/src/main/kotlin/org/jetbrains/bsp/BazelBuildServer.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ public interface BazelBuildServer {
1919

2020
@JsonRequest("buildTarget/mobileInstall")
2121
public fun buildTargetMobileInstall(params: MobileInstallParams): CompletableFuture<MobileInstallResult>
22+
23+
@JsonRequest("buildTarget/jvmBinaryJars")
24+
public fun buildTargetJvmBinaryJars(params: JvmBinaryJarsParams): CompletableFuture<JvmBinaryJarsResult>
2225
}

protocol/src/main/kotlin/org/jetbrains/bsp/BazelBuildServerCapabilities.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class BazelBuildServerCapabilities(
2828
public val workspaceDirectoriesProvider: Boolean = false,
2929
public val workspaceInvalidTargetsProvider: Boolean = false,
3030
public val runWithDebugProvider: Boolean = false,
31+
public val jvmBinaryJarsProvider: Boolean = false,
3132
) : BuildServerCapabilities() {
3233
init {
3334
this.compileProvider = compileProvider
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.jetbrains.bsp
2+
3+
import ch.epfl.scala.bsp4j.BuildTargetIdentifier
4+
5+
public data class JvmBinaryJarsParams(
6+
val targets: List<BuildTargetIdentifier>,
7+
)
8+
9+
public data class JvmBinaryJarsResult(
10+
val items: List<JvmBinaryJarsItem>,
11+
)
12+
13+
public data class JvmBinaryJarsItem(
14+
val target: BuildTargetIdentifier,
15+
val jars: List<String>,
16+
)

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/bsp/BspServerApi.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import ch.epfl.scala.bsp4j.TestResult
5454
import ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult
5555
import org.eclipse.lsp4j.jsonrpc.CancelChecker
5656
import org.jetbrains.bsp.BazelBuildServer
57+
import org.jetbrains.bsp.JvmBinaryJarsParams
58+
import org.jetbrains.bsp.JvmBinaryJarsResult
5759
import org.jetbrains.bsp.MobileInstallParams
5860
import org.jetbrains.bsp.MobileInstallResult
5961
import org.jetbrains.bsp.RunWithDebugParams
@@ -362,6 +364,16 @@ class BspServerApi(private val bazelServicesBuilder: (BuildClient) -> BazelServi
362364
)
363365
}
364366

367+
override fun buildTargetJvmBinaryJars(params: JvmBinaryJarsParams): CompletableFuture<JvmBinaryJarsResult> {
368+
return runner.handleRequest(
369+
"jvmBinaryJars", { cancelChecker: CancelChecker, params: JvmBinaryJarsParams ->
370+
projectSyncService.jvmBinaryJars(
371+
cancelChecker, params
372+
)
373+
}, params
374+
)
375+
}
376+
365377
override fun workspaceLibraries(): CompletableFuture<WorkspaceLibrariesResult> {
366378
return runner.handleRequest("libraries") { cancelChecker: CancelChecker ->
367379
projectSyncService.workspaceBuildLibraries(

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/BazelProjectMapper.kt

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ class BazelProjectMapper(
417417
val baseDirectory = label.toDirectoryUri()
418418
val languagePlugin = languagePluginsService.getPlugin(languages)
419419
val sourceSet = resolveSourceSet(target, languagePlugin)
420-
val resources = resolveResources(target)
420+
val resources = resolveResources(target, languagePlugin)
421421
val languageData = languagePlugin.resolveModule(target)
422422
val sourceDependencies = languagePlugin.dependencySources(target, dependencyGraph)
423423
val environment = environmentItem(target)
@@ -479,17 +479,8 @@ class BazelProjectMapper(
479479
bspClientLogger.error(message)
480480
}
481481

482-
private fun resolveResources(target: TargetInfo): Set<URI> =
483-
bazelPathsResolver.resolveUris(target.resourcesList).toSet() + resolveAndroidResources(target)
484-
485-
private fun resolveAndroidResources(target: TargetInfo): Set<URI> {
486-
if (!target.hasAndroidTargetInfo()) return emptySet()
487-
val androidTargetInfo = target.androidTargetInfo
488-
if (!androidTargetInfo.hasManifest()) return emptySet()
489-
490-
return bazelPathsResolver
491-
.resolveUris(listOf(target.androidTargetInfo.manifest) + target.androidTargetInfo.resourcesList).toSet()
492-
}
482+
private fun resolveResources(target: TargetInfo, languagePlugin: LanguagePlugin<*>): Set<URI> =
483+
bazelPathsResolver.resolveUris(target.resourcesList).toSet() + languagePlugin.resolveAdditionalResources(target)
493484

494485
private fun buildReverseSourceMapping(modules: List<Module>): Map<URI, Label> =
495486
modules.asSequence().flatMap(::buildReverseSourceMappingForModule).toMap()

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/BspProjectMapper.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ import ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult
5353
import org.eclipse.lsp4j.jsonrpc.CancelChecker
5454
import org.jetbrains.bsp.BazelBuildServerCapabilities
5555
import org.jetbrains.bsp.DirectoryItem
56+
import org.jetbrains.bsp.JvmBinaryJarsItem
57+
import org.jetbrains.bsp.JvmBinaryJarsParams
58+
import org.jetbrains.bsp.JvmBinaryJarsResult
5659
import org.jetbrains.bsp.LibraryItem
5760
import org.jetbrains.bsp.WorkspaceDirectoriesResult
5861
import org.jetbrains.bsp.WorkspaceInvalidTargetsResult
@@ -103,7 +106,8 @@ class BspProjectMapper(
103106
workspaceLibrariesProvider = true,
104107
workspaceDirectoriesProvider = true,
105108
workspaceInvalidTargetsProvider = true,
106-
runWithDebugProvider = true
109+
runWithDebugProvider = true,
110+
jvmBinaryJarsProvider = true,
107111
)
108112
return InitializeBuildResult(
109113
Constants.NAME, Constants.VERSION, Constants.BSP_VERSION, capabilities
@@ -325,6 +329,21 @@ class BspProjectMapper(
325329
}
326330
}
327331

332+
fun jvmBinaryJars(project: Project, params: JvmBinaryJarsParams): JvmBinaryJarsResult {
333+
fun toJvmBinaryJarsItem(module: Module): JvmBinaryJarsItem? =
334+
module.javaModule?.let { javaModule ->
335+
val jars = javaModule.binaryOutputs.map { it.toString() }
336+
JvmBinaryJarsItem(BspMappings.toBspId(module), jars)
337+
}
338+
339+
val jvmBinaryJarsItems = params.targets.mapNotNull { target ->
340+
val label = Label(target.uri)
341+
val module = project.findModule(label)
342+
module?.let { toJvmBinaryJarsItem(it) }
343+
}
344+
return JvmBinaryJarsResult(jvmBinaryJarsItems)
345+
}
346+
328347
fun buildTargetJavacOptions(project: Project, params: JavacOptionsParams, cancelChecker: CancelChecker): JavacOptionsResult {
329348
val items = params.targets.collectClasspathForTargetsAndApply(project, cancelChecker) { module, ideClasspath ->
330349
module.javaModule?.let { toJavacOptionsItem(module, it, ideClasspath) }

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/ExecuteService.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ import org.jetbrains.bsp.bazel.server.bsp.managers.BepReader
3232
import org.jetbrains.bsp.bazel.server.diagnostics.DiagnosticsService
3333
import org.jetbrains.bsp.bazel.server.paths.BazelPathsResolver
3434
import org.jetbrains.bsp.bazel.server.sync.BspMappings.toBspId
35+
import org.jetbrains.bsp.bazel.server.sync.languages.android.AdditionalAndroidBuildTargetsQuery.additionalAndroidBuildTargetsQuery
3536
import org.jetbrains.bsp.bazel.server.sync.model.Module
3637
import org.jetbrains.bsp.bazel.server.sync.model.Tag
3738
import org.jetbrains.bsp.bazel.workspacecontext.TargetsSpec
3839
import org.jetbrains.bsp.bazel.workspacecontext.WorkspaceContextProvider
39-
import java.util.Optional
4040

4141
class ExecuteService(
4242
private val compilationManager: BazelBspCompilationManager,
@@ -167,14 +167,20 @@ class ExecuteService(
167167
}
168168

169169
private fun build(cancelChecker: CancelChecker, bspIds: List<BuildTargetIdentifier>, originId: String): BazelProcessResult {
170-
val targetsSpec = TargetsSpec(bspIds, emptyList())
170+
val targets = bspIds + getAdditionalBuildTargets(cancelChecker, bspIds)
171+
val targetsSpec = TargetsSpec(targets, emptyList())
171172
return compilationManager.buildTargetsWithBep(
172173
cancelChecker,
173174
targetsSpec,
174175
originId
175176
).processResult
176177
}
177178

179+
private fun getAdditionalBuildTargets(
180+
cancelChecker: CancelChecker,
181+
bspIds: List<BuildTargetIdentifier>,
182+
): List<BuildTargetIdentifier> = additionalAndroidBuildTargetsQuery(bspIds, bazelRunner, cancelChecker)
183+
178184
private fun selectTargets(cancelChecker: CancelChecker, targets: List<BuildTargetIdentifier>): List<BuildTargetIdentifier> =
179185
selectModules(cancelChecker, targets).map { toBspId(it) }
180186

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/ProjectSyncService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import ch.epfl.scala.bsp4j.SourcesParams
3333
import ch.epfl.scala.bsp4j.SourcesResult
3434
import ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult
3535
import org.eclipse.lsp4j.jsonrpc.CancelChecker
36+
import org.jetbrains.bsp.JvmBinaryJarsParams
37+
import org.jetbrains.bsp.JvmBinaryJarsResult
3638
import org.jetbrains.bsp.WorkspaceDirectoriesResult
3739
import org.jetbrains.bsp.WorkspaceInvalidTargetsResult
3840
import org.jetbrains.bsp.WorkspaceLibrariesResult
@@ -113,6 +115,11 @@ class ProjectSyncService(private val bspMapper: BspProjectMapper, private val pr
113115
return bspMapper.jvmTestEnvironment(project, params, cancelChecker)
114116
}
115117

118+
fun jvmBinaryJars(cancelChecker: CancelChecker, params: JvmBinaryJarsParams): JvmBinaryJarsResult {
119+
val project = projectProvider.get(cancelChecker)
120+
return bspMapper.jvmBinaryJars(project, params)
121+
}
122+
116123
fun buildTargetJavacOptions(cancelChecker: CancelChecker, params: JavacOptionsParams): JavacOptionsResult {
117124
val project = projectProvider.get(cancelChecker)
118125
return bspMapper.buildTargetJavacOptions(project, params, cancelChecker)

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/languages/LanguagePlugin.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import java.nio.file.Path
99
abstract class LanguagePlugin<T : LanguageData> {
1010

1111
open fun calculateSourceRoot(source: Path): Path? = null
12+
open fun resolveAdditionalResources(targetInfo: BspTargetInfo.TargetInfo): Set<URI> = emptySet()
1213
open fun prepareSync(targets: Sequence<BspTargetInfo.TargetInfo>) {}
1314
open fun resolveModule(targetInfo: BspTargetInfo.TargetInfo): T? = null
1415

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.jetbrains.bsp.bazel.server.sync.languages.android
2+
3+
import ch.epfl.scala.bsp4j.BuildTargetIdentifier
4+
import ch.epfl.scala.bsp4j.StatusCode
5+
import org.eclipse.lsp4j.jsonrpc.CancelChecker
6+
import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner
7+
import org.jetbrains.bsp.bazel.bazelrunner.utils.BazelArgumentsUtils.getJoinedBazelTargets
8+
import org.jetbrains.bsp.bazel.bazelrunner.utils.BazelArgumentsUtils.toRawUris
9+
10+
/**
11+
* Every Kotlin Android target actually produces three targets, which we merge inside [KotlinAndroidModulesMerger].
12+
* However, in order for all the dependent libraries to be unpacked properly (e.g. for Jetpack Compose preview),
13+
* we still have to pass the dependent Kotlin target explicitly during build (and not just the merged target).
14+
*/
15+
object AdditionalAndroidBuildTargetsQuery {
16+
fun additionalAndroidBuildTargetsQuery(
17+
targets: List<BuildTargetIdentifier>,
18+
bazelRunner: BazelRunner,
19+
cancelChecker: CancelChecker,
20+
): List<BuildTargetIdentifier> {
21+
val targetUris = toRawUris(targets)
22+
val joinedTargets = getJoinedBazelTargets(targetUris)
23+
val childTargetsResult = bazelRunner.commandBuilder().query()
24+
.withArgument("""kind("kt_jvm_library", deps(kind("android_", $joinedTargets), 1))""")
25+
.executeBazelCommand(null).waitAndGetResult(cancelChecker)
26+
27+
if (childTargetsResult.statusCode != StatusCode.OK) {
28+
error("Could not retrieve additional android build targets")
29+
}
30+
31+
val targetUriSet = targetUris.asSequence().map { it.trimStart('@') }.toSet()
32+
33+
return childTargetsResult.stdoutLines
34+
.asSequence()
35+
.filter { it.endsWith("_kt") }
36+
.filter { it.dropLast(3) in targetUriSet }
37+
.map { BuildTargetIdentifier(it) }
38+
.toList()
39+
}
40+
}

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/languages/android/AndroidLanguagePlugin.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ class AndroidLanguagePlugin(
1818
override fun applyModuleData(moduleData: AndroidModule, buildTarget: BuildTarget) {
1919
val androidBuildTarget = with(moduleData) {
2020
AndroidBuildTarget(
21-
androidJar = androidJar,
21+
androidJar = androidJar.toString(),
2222
androidTargetType = androidTargetType,
23-
manifest = manifest,
24-
resourceFolders = resourceFolders,
23+
manifest = manifest?.toString(),
24+
resourceFolders = resourceFolders.map { it.toString() },
2525
)
2626
}
2727
moduleData.javaModule?.let { javaLanguagePlugin.toJvmBuildTarget(it) }?.let {
@@ -68,4 +68,14 @@ class AndroidLanguagePlugin(
6868

6969
override fun calculateSourceRoot(source: Path): Path =
7070
javaLanguagePlugin.calculateSourceRoot(source)
71+
72+
override fun resolveAdditionalResources(targetInfo: BspTargetInfo.TargetInfo): Set<URI> {
73+
if (!targetInfo.hasAndroidTargetInfo()) return emptySet()
74+
val androidTargetInfo = targetInfo.androidTargetInfo
75+
if (!androidTargetInfo.hasManifest()) return emptySet()
76+
77+
return bazelPathsResolver
78+
.resolveUris(listOf(targetInfo.androidTargetInfo.manifest) + targetInfo.androidTargetInfo.resourcesList)
79+
.toSet()
80+
}
7181
}

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/languages/android/KotlinAndroidModulesMerger.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.jetbrains.bsp.bazel.server.sync.languages.android
22

3+
import org.jetbrains.bsp.bazel.server.sync.languages.jvm.javaModule
34
import org.jetbrains.bsp.bazel.server.sync.languages.kotlin.KotlinModule
45
import org.jetbrains.bsp.bazel.server.sync.model.Module
56

@@ -48,7 +49,10 @@ class KotlinAndroidModulesMerger {
4849
val kotlinLanguageData = kotlinModule.languageData
4950
if (kotlinLanguageData !is KotlinModule?) return null
5051
val androidLanguageData = androidModule.languageData as? AndroidModule ?: return null
51-
val kotlinAndroidLanguageData = androidLanguageData.copy(kotlinModule = kotlinLanguageData)
52+
val javaModule = androidLanguageData.javaModule?.run {
53+
copy(binaryOutputs = binaryOutputs + kotlinModule.javaModule?.binaryOutputs.orEmpty())
54+
}
55+
val kotlinAndroidLanguageData = androidLanguageData.copy(kotlinModule = kotlinLanguageData, javaModule = javaModule)
5256

5357
val kotlinModuleWithoutSdk = kotlinModule.directDependencies.asSequence()
5458
.filterNot { it.value.endsWith("//third_party:android_sdk") }

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/languages/java/JavaLanguagePlugin.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ class JavaLanguagePlugin(
2727
override fun resolveModule(targetInfo: TargetInfo): JavaModule? =
2828
targetInfo.takeIf(TargetInfo::hasJvmTargetInfo)?.jvmTargetInfo?.run {
2929
val mainOutput = bazelPathsResolver.resolveUri(getJars(0).getBinaryJars(0))
30-
val allOutputs = jarsList.flatMap {
31-
it.interfaceJarsList + it.binaryJarsList
32-
}.map(bazelPathsResolver::resolveUri)
30+
val binaryOutputs = jarsList.flatMap { it.binaryJarsList }.map(bazelPathsResolver::resolveUri)
3331
val mainClass = getMainClass(this)
3432
val runtimeJdk = jdkResolver.resolveJdk(targetInfo)
3533

@@ -39,7 +37,7 @@ class JavaLanguagePlugin(
3937
javacOptsList,
4038
jvmFlagsList,
4139
mainOutput,
42-
allOutputs,
40+
binaryOutputs,
4341
mainClass,
4442
argsList,
4543
)

server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/languages/java/JavaModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ data class JavaModule(
1414
val javacOpts: List<String>,
1515
val jvmOps: List<String>,
1616
val mainOutput: URI,
17-
val allOutputs: List<URI>,
17+
val binaryOutputs: List<URI>,
1818
val mainClass: String?,
1919
val args: List<String>,
2020
) : LanguageData

0 commit comments

Comments
 (0)