Advertisement
kolbka_

GradleClassPathResolver

Aug 7th, 2023
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.18 KB | None | 0 0
  1. package org.javacs.kt.classpath
  2.  
  3. import org.javacs.kt.LOG
  4. import org.javacs.kt.util.KotlinLSException
  5. import org.javacs.kt.util.execAndReadStdoutAndStderr
  6. import org.javacs.kt.util.findCommandOnPath
  7. import org.javacs.kt.util.isOSWindows
  8. import java.nio.file.Files
  9. import java.nio.file.Path
  10. import java.nio.file.Paths
  11.  
  12. import org.gradle.tooling.GradleConnector
  13. import org.gradle.tooling.ProjectConnection
  14. import org.gradle.tooling.model.build.GradleEnvironment
  15. import java.io.*
  16.  
  17. internal class GradleClassPathResolver(
  18. private val path: Path,
  19. private val includeKotlinDSL: Boolean
  20. ) : ClassPathResolver {
  21. override val resolverType: String = "Gradle"
  22. private val projectDirectory: Path get() = path.getParent()
  23. override val classpath: Set<ClassPathEntry>
  24. get() {
  25. return emptySet()
  26. // val scripts = listOf("projectClassPathFinder.gradle")
  27. // val tasks = listOf("kotlinLSPProjectDeps")
  28. //
  29. // // TODO: remove this shit
  30. // return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
  31. // .apply { if (isNotEmpty()) LOG.info("Successfully resolved dependencies for '${projectDirectory.fileName}' using Gradle") }
  32. // .map { ClassPathEntry(it, null) }.toSet()
  33. }
  34. override val buildScriptClasspath: Set<Path>
  35. get() {
  36. LOG.info { "BUILDED SCRIPTS" }
  37. return if (includeKotlinDSL) {
  38. val scripts = listOf("kotlinDSLClassPathFinder.gradle")
  39. val tasks = listOf("kotlinLSPKotlinDSLDeps")
  40.  
  41. return readDependenciesViaGradleCLI(projectDirectory, scripts, tasks)
  42. .apply { if (isNotEmpty()) LOG.info("Successfully resolved build script dependencies for '${projectDirectory.fileName}' using Gradle") }
  43. } else {
  44. emptySet()
  45. }
  46. }
  47.  
  48. companion object {
  49. /** Create a Gradle resolver if a file is a pom. */
  50. fun maybeCreate(file: Path): GradleClassPathResolver? =
  51. file.takeIf { file.endsWith("build.gradle") || file.endsWith("build.gradle.kts") }
  52. ?.let {
  53. GradleClassPathResolver(
  54. it,
  55. includeKotlinDSL = file.toString().endsWith(".kts")
  56. )
  57. }
  58. }
  59. }
  60.  
  61. private fun gradleScriptToTempFile(scriptName: String, deleteOnExit: Boolean = false): File {
  62. val config = File.createTempFile("classpath", ".gradle")
  63. if (deleteOnExit) {
  64. config.deleteOnExit()
  65. }
  66.  
  67. LOG.debug("Creating temporary gradle file {}", config.absolutePath)
  68.  
  69. config.bufferedWriter().use { configWriter ->
  70. GradleClassPathResolver::class.java.getResourceAsStream("/$scriptName").bufferedReader()
  71. .use { configReader ->
  72. configReader.copyTo(configWriter)
  73. }
  74. }
  75.  
  76. return config
  77. }
  78.  
  79. private fun getGradleCommand(workspace: Path): Path {
  80. val wrapperName = if (isOSWindows()) "gradlew.bat" else "gradlew"
  81. val wrapper = workspace.resolve(wrapperName).toAbsolutePath()
  82. if (Files.isExecutable(wrapper)) {
  83. return wrapper
  84. } else {
  85. return workspace.parent?.let(::getGradleCommand)
  86. // TODO: replace it via invoking tooling api
  87. ?: findCommandOnPath("gradle")
  88. ?: throw KotlinLSException("Could not find 'gradle' on PATH")
  89. }
  90. }
  91.  
  92. private fun readDependenciesViaGradleCLI(
  93. projectDirectory: Path,
  94. gradleScripts: List<String>,
  95. gradleTasks: List<String>
  96. ): Set<Path> {
  97. LOG.info(
  98. "Resolving dependencies for '{}' through Gradle's CLI using tasks {}...",
  99. projectDirectory.fileName,
  100. gradleTasks
  101. )
  102.  
  103. val tmpScripts = gradleScripts.map {
  104. gradleScriptToTempFile(it, deleteOnExit = false).toPath().toAbsolutePath()
  105. }
  106. // TODO: check later
  107.  
  108. // try{
  109. // GradleConnector.newConnector()
  110. // .forProjectDirectory(projectDirectory.toFile()).connect().use{
  111. // val gradleUserHome: File = it.getModel(
  112. // GradleEnvironment::class.java
  113. // ).gradleUserHome
  114. //
  115. // LOG.info { "[Gradle home is]:$gradleUserHome" }
  116. // }
  117. // }
  118. // catch(e : Exception){
  119. // LOG.info { "[GRADLE ERROR]:$e" }
  120. // }
  121. // val gradle = getGradleCommand(projectDirectory)
  122. //
  123. val command = listOf("gradle") + tmpScripts.flatMap { listOf("-I", it.toString()) } + gradleTasks + listOf("--console=plain")
  124. // val dependencies = findGradleCLIDependencies(command, projectDirectory)
  125. // ?.also { LOG.debug("Classpath for task {}", it) }
  126. // .orEmpty()
  127. // .filter { it.toString().lowercase().endsWith(".jar") || Files.isDirectory(it) } // Some Gradle plugins seem to cause this to output POMs, therefore filter JARs
  128. // .toSet()
  129. //
  130. // tmpScripts.forEach(Files::delete)
  131. val stdout = ByteArrayOutputStream()
  132. val stderr = ByteArrayOutputStream()
  133. LOG.info { "project directory = $projectDirectory" }
  134. LOG.info { "command = $command" }
  135. GradleConnector.newConnector().useGradleVersion("8.2.1")
  136. .forProjectDirectory(projectDirectory.toFile()).connect().use {
  137. it.newBuild()
  138. .addArguments(tmpScripts.flatMap { listOf("-I", it.toString()) })
  139. .forTasks(gradleTasks.get(0))
  140. .addArguments("--console=plain")
  141. .setStandardOutput(stdout)
  142. .setStandardError(stderr)
  143. .run()
  144.  
  145. }
  146. if ("FAILURE: Build failed" in stderr.toString()) {
  147. LOG.warn("Gradle task failed: {}", stderr.toString())
  148. } else {
  149. for (error in stderr.toString().lines()) {
  150. if ("ERROR: " in error) {
  151. LOG.warn("Gradle error: {}", error)
  152. }
  153. }
  154. }
  155. val dependencies = parseGradleCLIDependencies(stdout.toString())
  156. ?.also { LOG.debug("Classpath for task {}", it) }
  157. .orEmpty()
  158. .filter {
  159. it.toString().lowercase().endsWith(".jar") || Files.isDirectory(it)
  160. } // Some Gradle plugins seem to cause this to output POMs, therefore filter JARs
  161. .toSet()
  162.  
  163. tmpScripts.forEach(Files::delete)
  164. return dependencies
  165. }
  166.  
  167. public fun main() {
  168.  
  169.  
  170. val projectDirectory = File("/Users/kolavladimirov/runtime-New_configuration(1)/t/bin")
  171. val stdout = ByteArrayOutputStream()
  172. val stderr = ByteArrayOutputStream()
  173. LOG.info { "project directory = $projectDirectory" }
  174.  
  175. GradleConnector.newConnector().useGradleVersion("8.2.1")
  176. .forProjectDirectory(projectDirectory).connect().use {
  177. it.newBuild()
  178. .addArguments("-I kotlinDSLClassPathFinder.gradle")
  179. .forTasks("kotlinLSPProjectDeps")
  180. .addArguments("--console=plain")
  181. .setStandardOutput(stdout)
  182. .setStandardError(stderr)
  183. .run()
  184.  
  185. }
  186. if ("FAILURE: Build failed" in stderr.toString()) {
  187. LOG.warn("Gradle task failed: {}", stderr.toString())
  188. } else {
  189. for (error in stderr.toString().lines()) {
  190. if ("ERROR: " in error) {
  191. LOG.warn("Gradle error: {}", error)
  192. }
  193. }
  194. }
  195. }
  196.  
  197. private fun findGradleCLIDependencies(command: List<String>, projectDirectory: Path): Set<Path>? {
  198. val (result, errors) = execAndReadStdoutAndStderr(command, projectDirectory)
  199. if ("FAILURE: Build failed" in errors) {
  200. LOG.warn("Gradle task failed: {}", errors)
  201. } else {
  202. for (error in errors.lines()) {
  203. if ("ERROR: " in error) {
  204. LOG.warn("Gradle error: {}", error)
  205. }
  206. }
  207. }
  208. return parseGradleCLIDependencies(result)
  209. }
  210.  
  211. private val artifactPattern by lazy { "kotlin-lsp-gradle (.+)(?:\r?\n)".toRegex() }
  212. private val gradleErrorWherePattern by lazy { "\\*\\s+Where:[\r\n]+(\\S\\.*)".toRegex() }
  213.  
  214. private fun parseGradleCLIDependencies(output: String): Set<Path>? {
  215. LOG.debug(output)
  216. val artifacts = artifactPattern.findAll(output)
  217. .mapNotNull { Paths.get(it.groups[1]?.value) }
  218. .filterNotNull()
  219. return artifacts.toSet()
  220. }
  221.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement