Skip to content
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

[fix] current target jar is excluded from dependencies - aka jump to definition within a module should work now #340

Merged
merged 5 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class BazelPathsResolver(private val bazelInfo: BazelInfo) {
return resolveUri(bazelInfo.workspaceRoot.resolve(relativePath))
}

private fun extractRelativePath(label: String): String {
fun extractRelativePath(label: String): String {
val prefix = bazelInfo.release.mainRepositoryReferencePrefix()
require(label.startsWith(prefix)) {
String.format(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,66 @@
package org.jetbrains.bsp.bazel.server.sync.languages.java

import org.jetbrains.bsp.bazel.server.sync.BazelPathsResolver
import org.jetbrains.bsp.bazel.server.sync.model.Label
import java.net.URI

class IdeClasspathResolver(runtimeClasspath: Sequence<URI>, compileClasspath: Sequence<URI>) {
private val runtimeJars: Set<String>
private val runtimeMavenJarSuffixes: Set<String>
private val compileJars: Sequence<String>
class IdeClasspathResolver(
private val label: Label,
private val bazelPathsResolver: BazelPathsResolver,
runtimeClasspath: Sequence<URI>,
compileClasspath: Sequence<URI>,
) {
private val runtimeJars: Set<String>
private val runtimeMavenJarSuffixes: Set<String>
private val compileJars: Sequence<String>

init {
runtimeJars = runtimeClasspath.map { obj: URI -> obj.toString() }
.toSet()
runtimeMavenJarSuffixes = runtimeJars.mapNotNull(::toMavenSuffix).toSet()
compileJars = compileClasspath.map { obj: URI -> obj.toString() }
}
init {
runtimeJars = runtimeClasspath.map { it.toString() }.toSet()
runtimeMavenJarSuffixes = runtimeJars.mapNotNull(::toMavenSuffix).toSet()
compileJars = compileClasspath.map { it.toString() }
}

fun resolve(): Sequence<URI> =
compileJars
.map(::findRuntimeEquivalent)
.map(URI::create)

private fun findRuntimeEquivalent(compileJar: String): String {
val runtimeJar = compileJar.replace(JAR_PATTERN, ".jar")
if (runtimeJars.contains(runtimeJar)) {
return runtimeJar
}
val headerSuffix = toMavenSuffix(compileJar)
val mavenJarSuffix = headerSuffix?.let { s: String ->
s.replace(
"/header_([^/]+)\\.jar$".toRegex(),
"/$1.jar"
)
}
return mavenJarSuffix?.takeIf(runtimeMavenJarSuffixes::contains)?.let { suffix ->
runtimeJars.find { jar:String -> jar.endsWith(suffix) }
} ?: compileJar
}
fun resolve(): Sequence<URI> =
compileJars
.map(::findRuntimeEquivalent)
.filterNot { isItJarOfTheCurrentTarget(it) }
.map(URI::create)

private fun toMavenSuffix(uri: String): String? {
val indicator = "/maven2/"
val index = uri.lastIndexOf(indicator)
return if (index < 0) {
null
} else {
uri.substring(index + indicator.length)
}
private fun findRuntimeEquivalent(compileJar: String): String {
val runtimeJar = compileJar.replace(JAR_PATTERN, ".jar")
if (runtimeJars.contains(runtimeJar)) {
return runtimeJar
}
val headerSuffix = toMavenSuffix(compileJar)
val mavenJarSuffix = headerSuffix?.let { s: String ->
s.replace(
"/header_([^/]+)\\.jar$".toRegex(),
"/$1.jar"
)
}
return mavenJarSuffix?.takeIf(runtimeMavenJarSuffixes::contains)?.let { suffix ->
runtimeJars.find { jar: String -> jar.endsWith(suffix) }
} ?: compileJar
}

companion object {
private val JAR_PATTERN = ("-[hi]jar\\.jar$").toRegex()
private fun toMavenSuffix(uri: String): String? {
val indicator = "/maven2/"
val index = uri.lastIndexOf(indicator)
return if (index < 0) {
null
} else {
uri.substring(index + indicator.length)
}
}

private fun isItJarOfTheCurrentTarget(jar: String): Boolean {
val targetPath = bazelPathsResolver.extractRelativePath(label.value)
val targetJar = "$targetPath/${label.targetName()}.jar"

return jar.endsWith(targetJar)
}

companion object {
private val JAR_PATTERN = ("((-[hi]jar)|(\\.abi))\\.jar\$").toRegex()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.jetbrains.bsp.bazel.server.sync.BspMappings
import org.jetbrains.bsp.bazel.server.sync.dependencytree.DependencyTree
import org.jetbrains.bsp.bazel.server.sync.languages.JVMLanguagePluginParser
import org.jetbrains.bsp.bazel.server.sync.languages.LanguagePlugin
import org.jetbrains.bsp.bazel.server.sync.model.Label
import org.jetbrains.bsp.bazel.server.sync.model.Module
import java.net.URI
import java.nio.file.Path
Expand All @@ -42,7 +43,8 @@ class JavaLanguagePlugin(
val runtimeClasspath = bazelPathsResolver.resolveUris(runtimeClasspathList)
val compileClasspath = bazelPathsResolver.resolveUris(compileClasspathList)
val sourcesClasspath = bazelPathsResolver.resolveUris(sourceClasspathList)
val ideClasspath = resolveIdeClasspath(
val ideClasspath = resolveIdeClasspath(Label(targetInfo.id),
bazelPathsResolver,
runtimeClasspath.asSequence(), compileClasspath.asSequence()
)
val runtimeJdk = jdkResolver.resolveJdk(targetInfo)
Expand Down Expand Up @@ -73,8 +75,9 @@ class JavaLanguagePlugin(
private fun getJdk(): Jdk = jdk ?: throw RuntimeException("Failed to resolve JDK for project")

private fun resolveIdeClasspath(
runtimeClasspath: Sequence<URI>, compileClasspath: Sequence<URI>
): List<URI> = IdeClasspathResolver(runtimeClasspath, compileClasspath).resolve().toList()
targetId: Label,
bazelPathsResolver: BazelPathsResolver, runtimeClasspath: Sequence<URI>, compileClasspath: Sequence<URI>
): List<URI> = IdeClasspathResolver(targetId, bazelPathsResolver, runtimeClasspath, compileClasspath).resolve().toList()

override fun dependencySources(
targetInfo: TargetInfo, dependencyTree: DependencyTree
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
package org.jetbrains.bsp.bazel.server.sync.model

data class Label(val value: String)
data class Label(val value: String) {

fun targetName(): String =
value.substringAfterLast(":", "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("//:junit5.bzl", "kt_junit5_test")

kt_junit5_test(
name = "IdeClasspathResolverTest",
size = "small",
srcs = ["IdeClasspathResolverTest.kt"],
test_package = "org.jetbrains.bsp.bazel.server.sync.languages.java",
deps = [
"//server/src/main/java/org/jetbrains/bsp/bazel/server/sync",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.jetbrains.bsp.bazel.server.sync.languages.java

import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
import org.jetbrains.bsp.bazel.bazelrunner.BasicBazelInfo
import org.jetbrains.bsp.bazel.bazelrunner.BazelRelease
import org.jetbrains.bsp.bazel.server.sync.BazelPathsResolver
import org.jetbrains.bsp.bazel.server.sync.model.Label
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.net.URI
import java.nio.file.Paths

// TODO we should add more tests for the "default" behavior
class IdeClasspathResolverTest {

private val execRoot = "/private/var/tmp/_bazel/125c7a6ca879ed16a4b4b1a74bc5f27b/execroot/bazel_bsp"
private lateinit var bazelPathsResolver: BazelPathsResolver

@BeforeEach
fun beforeEach() {
// given
val bazelInfo = BasicBazelInfo(
execRoot = execRoot,
workspaceRoot = Paths.get("/Users/user/workspace/bazel-bsp"),
release = BazelRelease.fromReleaseString("release 6.0.0")
)

bazelPathsResolver = BazelPathsResolver(bazelInfo)
}

@Test
fun `should return runtime classpath for if exists and compile classpath if runtime doesnt exist and filter current target jar out`() {
// given
val compileClasspath = listOf(
URI.create("file:///path/to/normal/maven/library-ijar.jar"),
URI.create("file://$execRoot/path/to/target/targetName.abi.jar"),
URI.create("file://$execRoot/path/to/another/target/anotherTargetName.abi.jar"),
URI.create("file:///path/to/another/normal/maven/library-ijar.jar"),
)

val runtimeClasspath = listOf(
URI.create("file:///path/to/normal/maven/library.jar"),
URI.create("file://$execRoot/path/to/target/targetName.jar"),
URI.create("file://$execRoot/path/to/another/target/anotherTargetName.jar"),
)

val resolver = IdeClasspathResolver(
label = Label("@//path/to/target:targetName"),
bazelPathsResolver = bazelPathsResolver,
compileClasspath = compileClasspath.asSequence(),
runtimeClasspath = runtimeClasspath.asSequence(),
)

// when
val resolvedClasspath = resolver.resolve().toList()

// then
resolvedClasspath shouldContainExactlyInAnyOrder listOf(
URI.create("file:///path/to/normal/maven/library.jar"),
URI.create("file://$execRoot/path/to/another/target/anotherTargetName.jar"),
URI.create("file:///path/to/another/normal/maven/library-ijar.jar"),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("//:junit5.bzl", "kt_junit5_test")

kt_junit5_test(
name = "LabelTest",
size = "small",
srcs = ["LabelTest.kt"],
test_package = "org.jetbrains.bsp.bazel.server.sync.model",
deps = [
"//server/src/main/java/org/jetbrains/bsp/bazel/server/sync",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.jetbrains.bsp.bazel.server.sync.model

import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test

class LabelTest {

@Test
fun `should return target name for label with bazel 6 target`() {
// given
val label = Label("@//path/to/target:targetName")

// when
val targetName = label.targetName()

// then
targetName shouldBe "targetName"
}

@Test
fun `should return target name for label with bazel 5 target`() {
// given
val label = Label("//path/to/target:targetName")

// when
val targetName = label.targetName()

// then
targetName shouldBe "targetName"
}

@Test
fun `should return empty string for label with target without target name`() {
// given
val label = Label("//path/to/target")

// when
val targetName = label.targetName()

// then
targetName shouldBe ""
}
}