From 441fc96676d5d85950d8f2a2a558749fbb5c1779 Mon Sep 17 00:00:00 2001 From: Mikalai Kukhta Date: Tue, 13 Aug 2024 18:38:52 +0200 Subject: [PATCH] add option to use runtimeClasspath --- .../kt/classpath/DefaultClassPathResolver.kt | 33 ++++++++++++----- .../kt/classpath/GradleClassPathResolver.kt | 35 +++++++++++++++---- .../resources/projectClassPathFinder.gradle | 5 ++- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/DefaultClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/DefaultClassPathResolver.kt index 02a7a4bde..de6f8aff0 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/DefaultClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/DefaultClassPathResolver.kt @@ -6,27 +6,44 @@ import java.nio.file.Path import java.nio.file.PathMatcher import java.nio.file.FileSystems -fun defaultClassPathResolver(workspaceRoots: Collection, db: Database? = null): ClassPathResolver { +data class ResolverOptions( + // Whether to use the compile classpath or the runtime classpath during classpath resolution + val useCompileClasspath: Boolean, +) { + companion object { + fun default(): ResolverOptions { + return ResolverOptions(useCompileClasspath = true) + } + } +} + +val DefaultResolverOptions = ResolverOptions.default() + +fun defaultClassPathResolver( + workspaceRoots: Collection, + db: Database? = null, + resolverOptions: ResolverOptions = DefaultResolverOptions, +): ClassPathResolver { val childResolver = WithStdlibResolver( ShellClassPathResolver.global(workspaceRoots.firstOrNull()) - .or(workspaceRoots.asSequence().flatMap { workspaceResolvers(it) }.joined) + .or(workspaceRoots.asSequence().flatMap { workspaceResolvers(it, resolverOptions) }.joined) ).or(BackupClassPathResolver) return db?.let { CachedClassPathResolver(childResolver, it) } ?: childResolver } /** Searches the workspace for all files that could provide classpath info. */ -private fun workspaceResolvers(workspaceRoot: Path): Sequence { +private fun workspaceResolvers(workspaceRoot: Path, resolverOptions: ResolverOptions): Sequence { val ignored: List = ignoredPathPatterns(workspaceRoot, workspaceRoot.resolve(".gitignore")) - return folderResolvers(workspaceRoot, ignored).asSequence() + return folderResolvers(workspaceRoot, ignored, resolverOptions).asSequence() } /** Searches the folder for all build-files. */ -private fun folderResolvers(root: Path, ignored: List): Collection = +private fun folderResolvers(root: Path, ignored: List, resolverOptions: ResolverOptions): Collection = root.toFile() .walk() .onEnter { file -> ignored.none { it.matches(file.toPath()) } } - .mapNotNull { asClassPathProvider(it.toPath()) } + .mapNotNull { asClassPathProvider(it.toPath(), resolverOptions) } .toList() /** Tries to read glob patterns from a gitignore. */ @@ -51,7 +68,7 @@ private fun ignoredPathPatterns(root: Path, gitignore: Path): List ?: emptyList() /** Tries to create a classpath resolver from a file using as many sources as possible */ -private fun asClassPathProvider(path: Path): ClassPathResolver? = +private fun asClassPathProvider(path: Path, resolverOptions: ResolverOptions): ClassPathResolver? = MavenClassPathResolver.maybeCreate(path) - ?: GradleClassPathResolver.maybeCreate(path) + ?: GradleClassPathResolver.maybeCreate(path, resolverOptions) ?: ShellClassPathResolver.maybeCreate(path) diff --git a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt index 1e0cbc7b3..1fb9281e7 100644 --- a/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt +++ b/shared/src/main/kotlin/org/javacs/kt/classpath/GradleClassPathResolver.kt @@ -10,7 +10,11 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -internal class GradleClassPathResolver(private val path: Path, private val includeKotlinDSL: Boolean): ClassPathResolver { +internal class GradleClassPathResolver( + private val path: Path, + private val includeKotlinDSL: Boolean, + private val useCompileClasspath: Boolean +): ClassPathResolver { override val resolverType: String = "Gradle" private val projectDirectory: Path get() = path.parent @@ -18,7 +22,7 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu val scripts = listOf("projectClassPathFinder.gradle") val tasks = listOf("kotlinLSPProjectDeps") - return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks) + return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks, useCompileClasspath) .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") } .map { ClassPathEntry(it, null) }.toSet() } @@ -38,9 +42,15 @@ internal class GradleClassPathResolver(private val path: Path, private val inclu companion object { /** Create a Gradle resolver if a file is a pom. */ - fun maybeCreate(file: Path): GradleClassPathResolver? = + fun maybeCreate(file: Path, options: ResolverOptions): GradleClassPathResolver? = file.takeIf { file.endsWith("build.gradle") || file.endsWith("build.gradle.kts") } - ?.let { GradleClassPathResolver(it, includeKotlinDSL = file.toString().endsWith(".kts")) } + ?.let { + GradleClassPathResolver( + path = it, + includeKotlinDSL = file.endsWith(".kts"), + useCompileClasspath = options.useCompileClasspath, + ) + } } } @@ -73,13 +83,26 @@ private fun getGradleCommand(workspace: Path): Path { } } -private fun readDependenciesViaGradleCLI(projectDirectory: Path, gradleScripts: List, gradleTasks: List): Set { +private fun readDependenciesViaGradleCLI( + projectDirectory: Path, + gradleScripts: List, + gradleTasks: List, + useCompileClasspath: Boolean = true, +): Set { LOG.info("Resolving dependencies for '{}' through Gradle's CLI using tasks {}...", projectDirectory.fileName, gradleTasks) val tmpScripts = gradleScripts.map { gradleScriptToTempFile(it, deleteOnExit = false).toPath().toAbsolutePath() } val gradle = getGradleCommand(projectDirectory) - val command = listOf(gradle.toString()) + tmpScripts.flatMap { listOf("-I", it.toString()) } + gradleTasks + listOf("--console=plain") + val command = mutableListOf().apply { + add(gradle.toString()) + addAll(tmpScripts.flatMap { listOf("-I", it.toString()) }) + addAll(gradleTasks) + add("--console=plain") + + if (useCompileClasspath) add("-PuseCompileClasspath=1") + }.toList() + val dependencies = findGradleCLIDependencies(command, projectDirectory) ?.also { LOG.debug("Classpath for task {}", it) } .orEmpty() diff --git a/shared/src/main/resources/projectClassPathFinder.gradle b/shared/src/main/resources/projectClassPathFinder.gradle index 7fce53927..9d6d838bb 100644 --- a/shared/src/main/resources/projectClassPathFinder.gradle +++ b/shared/src/main/resources/projectClassPathFinder.gradle @@ -52,8 +52,11 @@ allprojects { project -> } } else if (project.hasProperty('sourceSets')) { // Print the list of all dependencies jar files. + def useCompileClasspath = project.hasProperty("useCompileClasspath") + sourceSets.forEach { - it.compileClasspath.forEach { + def classPathSource = useCompileClasspath ? it.compileClasspath : it.runtimeClasspath + classPathSource.forEach { System.out.println "kotlin-lsp-gradle $it" } }