Skip to content

Commit

Permalink
[feature] workspace/directories implementation
Browse files Browse the repository at this point in the history
symlinks are excluded by default

review

empty lines

rebase fix

Merge remote-tracking branch 'origin/master' into abrams/directories_endpoint

# Conflicts:
#	executioncontext/workspacecontext/src/main/kotlin/org/jetbrains/bsp/bazel/workspacecontext/DirectoriesSpec.kt
#	executioncontext/workspacecontext/src/test/kotlin/org/jetbrains/bsp/bazel/workspacecontext/DirectoriesSpecExtractorTest.kt
#	server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/ProjectSyncService.java
#	server/src/main/kotlin/org/jetbrains/bsp/bazel/server/sync/ProjectSyncService.kt

endpoint impl

directories spec!

ProjectSyncService is now kotlin!

Rename .java to .kt


Merge-request: BAZEL-MR-497
Merged-by: Marcin Abramowicz <marcin.abramowicz@jetbrains.com>
  • Loading branch information
abrams27 authored and Space Team committed Oct 3, 2023
1 parent 119eb05 commit 43abdfb
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 165 deletions.
@@ -0,0 +1,32 @@
package org.jetbrains.bsp.bazel.workspacecontext

import org.jetbrains.bsp.bazel.executioncontext.api.ExecutionContextEntityExtractor
import org.jetbrains.bsp.bazel.executioncontext.api.ExecutionContextExcludableListEntity
import org.jetbrains.bsp.bazel.projectview.model.ProjectView
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDirectoriesSection
import java.nio.file.Path

data class DirectoriesSpec(
override val values: List<Path>,
override val excludedValues: List<Path>,
) : ExecutionContextExcludableListEntity<Path>()

internal class DirectoriesSpecExtractor(private val workspaceRoot: Path)
: ExecutionContextEntityExtractor<DirectoriesSpec> {

override fun fromProjectView(projectView: ProjectView): DirectoriesSpec =
projectView.directories?.toDirectoriesSpec()
?: DirectoriesSpec(
values = listOf(workspaceRoot),
excludedValues = emptyList(),
)

private fun ProjectViewDirectoriesSection.toDirectoriesSpec(): DirectoriesSpec =
DirectoriesSpec(
values = values.map { it.resolveAndNormalize() },
excludedValues = excludedValues.map { it.resolveAndNormalize() },
)

private fun Path.resolveAndNormalize() =
workspaceRoot.resolve(this).normalize()
}
Expand Up @@ -21,6 +21,14 @@ data class WorkspaceContext(
*/
val targets: TargetsSpec,

/**
* Directories (included and excluded) in the project.
*
* Obtained from 'ProjectView' simply by mapping 'directories' section if not null,
* otherwise the whole project is included (project root is included).
*/
val directories: DirectoriesSpec,

/**
* Build flags which should be added to each bazel call.
*
Expand Down Expand Up @@ -60,24 +68,21 @@ data class WorkspaceContext(
class WorkspaceContextConstructor(workspaceRoot: Path) : ExecutionContextConstructor<WorkspaceContext> {

private val dotBazelBspDirPathSpecExtractor = DotBazelBspDirPathSpecExtractor(workspaceRoot)
private val directoriesSpecExtractor = DirectoriesSpecExtractor(workspaceRoot)

private val log = LogManager.getLogger(WorkspaceContextConstructor::class.java)

override fun construct(projectView: ProjectView): WorkspaceContext {
log.info("Constructing workspace context for: {}.", projectView)

val targetsSpec = TargetsSpecExtractor.fromProjectView(projectView)
val buildFlagsSpec = BuildFlagsSpecExtractor.fromProjectView(projectView)
val bazelBinarySpec = BazelBinarySpecExtractor.fromProjectView(projectView)
val dotBazelBspDirPathSpec = dotBazelBspDirPathSpecExtractor.fromProjectView(projectView)
val buildManualTargetsSpec = BuildManualTargetsSpecExtractor.fromProjectView(projectView)
val importDepthSpec = ImportDepthSpecExtractor.fromProjectView(projectView)
return WorkspaceContext(
targets = targetsSpec,
buildFlags = buildFlagsSpec,
bazelBinary = bazelBinarySpec,
dotBazelBspDirPath = dotBazelBspDirPathSpec,
buildManualTargets = buildManualTargetsSpec,
importDepth = importDepthSpec,
targets = TargetsSpecExtractor.fromProjectView(projectView),
directories = directoriesSpecExtractor.fromProjectView(projectView),
buildFlags = BuildFlagsSpecExtractor.fromProjectView(projectView),
bazelBinary = BazelBinarySpecExtractor.fromProjectView(projectView),
dotBazelBspDirPath = dotBazelBspDirPathSpecExtractor.fromProjectView(projectView),
buildManualTargets = BuildManualTargetsSpecExtractor.fromProjectView(projectView),
importDepth = ImportDepthSpecExtractor.fromProjectView(projectView),
)
}
}
Expand Up @@ -62,3 +62,12 @@ kt_test(
"//executioncontext/workspacecontext/src/main/kotlin/org/jetbrains/bsp/bazel/workspacecontext",
],
)

kt_test(
name = "DirectoriesSpecExtractorTest",
size = "small",
src = "DirectoriesSpecExtractorTest.kt",
associates = [
"//executioncontext/workspacecontext/src/main/kotlin/org/jetbrains/bsp/bazel/workspacecontext",
],
)
@@ -0,0 +1,74 @@
package org.jetbrains.bsp.bazel.workspacecontext

import io.kotest.matchers.shouldBe
import org.jetbrains.bsp.bazel.projectview.model.ProjectView
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDirectoriesSection
import org.junit.jupiter.api.Test
import kotlin.io.path.Path

class DirectoriesSpecExtractorTest {

@Test
fun `should return workspace root if directories section is null`() {
// given
val workspaceRoot = Path("path/to/workspace")
val projectView = ProjectView.Builder(directories = null).build()

// when
val directoriesSpec = DirectoriesSpecExtractor(workspaceRoot).fromProjectView(projectView)

// then
val expectedDirectoriesSpec = DirectoriesSpec(
values = listOf(workspaceRoot),
excludedValues = emptyList()
)
directoriesSpec shouldBe expectedDirectoriesSpec
}

@Test
fun `should return workspace root if directories section contains only dot wildcard`() {
// given
val workspaceRoot = Path("path/to/workspace")
val projectView = ProjectView.Builder(
directories = ProjectViewDirectoriesSection(
values = listOf(Path(".")),
excludedValues = emptyList()
)
).build()

// when
val directoriesSpec = DirectoriesSpecExtractor(workspaceRoot).fromProjectView(projectView)

// then
val expectedDirectoriesSpec = DirectoriesSpec(
values = listOf(workspaceRoot),
excludedValues = emptyList()
)
directoriesSpec shouldBe expectedDirectoriesSpec
}

@Test
fun `should return resolved paths if directories section is not null`() {
// given
val workspaceRoot = Path("path/to/workspace")
val projectView = ProjectView.Builder(
directories = ProjectViewDirectoriesSection(
values = listOf(Path("path/to/included1"), Path("path/to/included2")),
excludedValues = listOf(Path("path/to/excluded")),
)
).build()

// when
val directoriesSpec = DirectoriesSpecExtractor(workspaceRoot).fromProjectView(projectView)

// then
val expectedDirectoriesSpec = DirectoriesSpec(
values = listOf(
workspaceRoot.resolve("path/to/included1"),
workspaceRoot.resolve("path/to/included2")
),
excludedValues = listOf(workspaceRoot.resolve("path/to/excluded"))
)
directoriesSpec shouldBe expectedDirectoriesSpec
}
}
Expand Up @@ -6,6 +6,7 @@ import org.jetbrains.bsp.bazel.projectview.model.ProjectView
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelBinarySection
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBuildFlagsSection
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBuildManualTargetsSection
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDirectoriesSection
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewImportDepthSection
import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection
import org.junit.jupiter.api.DisplayName
Expand Down Expand Up @@ -34,6 +35,15 @@ class WorkspaceContextConstructorTest {
),
listOf(BuildTargetIdentifier("//excluded_target1")),
),
directories = ProjectViewDirectoriesSection(
values = listOf(
Path("path/to/included1"),
Path("path/to/included2"),
),
excludedValues = listOf(
Path("path/to/excluded"),
)
),
buildFlags = ProjectViewBuildFlagsSection(
listOf(
"--build_flag1=value1",
Expand All @@ -60,6 +70,16 @@ class WorkspaceContextConstructorTest {
)
workspaceContext.targets shouldBe expectedTargets

val expectedDirectories = DirectoriesSpec(
values = listOf(
workspaceRoot.resolve("path/to/included1"),
workspaceRoot.resolve("path/to/included2")
),
excludedValues = listOf(
workspaceRoot.resolve("path/to/excluded"),
)
)
workspaceContext.directories shouldBe expectedDirectories
val expectedBuildFlagsSpec = BuildFlagsSpec(
listOf(
"--build_flag1=value1",
Expand Down
@@ -1,6 +1,5 @@
package org.jetbrains.bsp.bazel.server.bsp;

import ch.epfl.scala.bsp4j.BuildClient;
import ch.epfl.scala.bsp4j.BuildServer;
import ch.epfl.scala.bsp4j.CleanCacheParams;
import ch.epfl.scala.bsp4j.CleanCacheResult;
Expand Down Expand Up @@ -54,6 +53,7 @@
import org.jetbrains.bsp.bazel.server.sync.BazelBuildServer;
import org.jetbrains.bsp.bazel.server.sync.ExecuteService;
import org.jetbrains.bsp.bazel.server.sync.ProjectSyncService;
import org.jetbrains.bsp.bazel.server.sync.WorkspaceDirectoriesResult;
import org.jetbrains.bsp.bazel.server.sync.WorkspaceLibrariesResult;

public class BspServerApi
Expand Down Expand Up @@ -246,4 +246,9 @@ public CompletableFuture<JvmTestEnvironmentResult> buildTargetJvmTestEnvironment
public CompletableFuture<WorkspaceLibrariesResult> workspaceLibraries() {
return runner.handleRequest("libraries", projectSyncService::workspaceBuildLibraries);
}

@Override
public CompletableFuture<WorkspaceDirectoriesResult> workspaceDirectories() {
return runner.handleRequest("libraries", projectSyncService::workspaceDirectories);
}
}
Expand Up @@ -15,7 +15,19 @@ data class WorkspaceLibrariesResult(
val libraries: List<LibraryItem>
)

data class DirectoryItem(
val uri: String,
)

data class WorkspaceDirectoriesResult(
val includedDirectories: List<DirectoryItem>,
val excludedDirectories: List<DirectoryItem>,
)

interface BazelBuildServer {
@JsonRequest("workspace/libraries")
fun workspaceLibraries(): CompletableFuture<WorkspaceLibrariesResult>

@JsonRequest("workspace/directories")
fun workspaceDirectories(): CompletableFuture<WorkspaceDirectoriesResult>
}
Expand Up @@ -57,6 +57,10 @@ import org.jetbrains.bsp.bazel.server.sync.model.Module
import org.jetbrains.bsp.bazel.server.sync.model.Project
import org.jetbrains.bsp.bazel.server.sync.model.Tag
import org.jetbrains.bsp.bazel.workspacecontext.WorkspaceContextProvider
import java.net.URI
import java.nio.file.Path
import kotlin.io.path.name
import kotlin.io.path.toPath

class BspProjectMapper(
private val languagePluginsService: LanguagePluginsService,
Expand Down Expand Up @@ -98,6 +102,37 @@ class BspProjectMapper(
return WorkspaceLibrariesResult(libraries)
}

fun workspaceDirectories(project: Project): WorkspaceDirectoriesResult {
val workspaceContext = workspaceContextProvider.currentWorkspaceContext()
val directoriesSection = workspaceContext.directories

val symlinksToExclude = computeSymlinksToExclude(project.workspaceRoot)
val directoriesToExclude = directoriesSection.excludedValues + symlinksToExclude

return WorkspaceDirectoriesResult(
includedDirectories = directoriesSection.values.map { it.toDirectoryItem() },
excludedDirectories = directoriesToExclude.map { it.toDirectoryItem() }
)
}

// copied from IntelliJProjectTreeViewFix, but it will be removed with:
// https://youtrack.jetbrains.com/issue/BAZEL-665
private fun computeSymlinksToExclude(workspaceRoot: URI): List<Path> {
val stableSymlinkNames = setOf("bazel-out", "bazel-testlogs", "bazel-bin")
val workspaceRootPath = workspaceRoot.toPath()
val sanitizedWorkspaceRootPath = workspaceRootPath
.name
.replace("[^A-Za-z0-9]".toRegex(), "-")
val workspaceSymlinkNames = setOf("bazel-$sanitizedWorkspaceRootPath")

return (stableSymlinkNames + workspaceSymlinkNames).map { workspaceRootPath.resolve(it) }
}

private fun Path.toDirectoryItem() =
DirectoryItem(
uri = this.toUri().toString()
)

private fun toBuildTarget(module: Module): BuildTarget {
val label = BspMappings.toBspId(module)
val dependencies =
Expand Down

0 comments on commit 43abdfb

Please sign in to comment.