From 7270b19bfb6433c09f330cc778a09b7f5795b6b7 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Tue, 24 Jan 2023 13:16:16 +0100 Subject: [PATCH 01/20] WIP --- build.sbt | 4 +- .../core/application/Context.scala | 2 + .../core/buildtool/bsp/BspClient.scala | 29 +++++ .../buildtool/bsp/BspConnectionDetails.scala | 29 +++++ .../core/buildtool/bsp/BspExtractor.scala | 102 ++++++++++++++++++ .../core/buildtool/bsp/BspProcess.scala | 64 +++++++++++ .../core/buildtool/bsp/BspServerType.scala | 38 +++++++ .../core/buildtool/mill/MillAlg.scala | 34 ++++-- project/Dependencies.scala | 1 + repos.md | 5 - 10 files changed, 293 insertions(+), 15 deletions(-) create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspClient.scala create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspConnectionDetails.scala create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala diff --git a/build.sbt b/build.sbt index 0c313721c2..ea14d85109 100644 --- a/build.sbt +++ b/build.sbt @@ -107,6 +107,7 @@ lazy val core = myCrossProject("core") libraryDependencies ++= Seq( Dependencies.bcprovJdk15to18, Dependencies.betterFiles, + Dependencies.bsp4j, Dependencies.catsCore, Dependencies.catsEffect, Dependencies.catsParse, @@ -177,7 +178,8 @@ lazy val core = myCrossProject("core") BuildInfoKey("mainBranch" -> mainBranch), BuildInfoKey.map(git.gitHeadCommit) { case (k, v) => k -> v.getOrElse(mainBranch) }, BuildInfoKey("millPluginArtifactName" -> Dependencies.scalaStewardMillPluginArtifactName), - BuildInfoKey("millPluginVersion" -> Dependencies.scalaStewardMillPlugin.revision) + BuildInfoKey("millPluginVersion" -> Dependencies.scalaStewardMillPlugin.revision), + BuildInfoKey("bsp4jVersion" -> Dependencies.bsp4j.revision) ), buildInfoPackage := moduleRootPkg.value, initialCommands += s""" diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala index 53b654905b..7d766e13d9 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala @@ -27,6 +27,7 @@ import org.http4s.client.Client import org.http4s.headers.`User-Agent` import org.scalasteward.core.application.Config.StewardUsage import org.scalasteward.core.buildtool.BuildToolDispatcher +import org.scalasteward.core.buildtool.bsp.BspExtractor import org.scalasteward.core.buildtool.maven.MavenAlg import org.scalasteward.core.buildtool.mill.MillAlg import org.scalasteward.core.buildtool.sbt.SbtAlg @@ -225,6 +226,7 @@ object Context { implicit val mavenAlg: MavenAlg[F] = new MavenAlg[F](config) implicit val sbtAlg: SbtAlg[F] = new SbtAlg[F](config) implicit val scalaCliAlg: ScalaCliAlg[F] = new ScalaCliAlg[F] + implicit val bspExtractor: BspExtractor[F] = new BspExtractor[F](config.defaultResolver) implicit val millAlg: MillAlg[F] = new MillAlg[F] implicit val buildToolDispatcher: BuildToolDispatcher[F] = new BuildToolDispatcher[F] implicit val refreshErrorAlg: RefreshErrorAlg[F] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspClient.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspClient.scala new file mode 100644 index 0000000000..a935cd05a4 --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspClient.scala @@ -0,0 +1,29 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bsp + +import ch.epfl.scala.bsp4j._ + +class BspClient extends BuildClient { + override def onBuildShowMessage(params: ShowMessageParams): Unit = () + override def onBuildLogMessage(params: LogMessageParams): Unit = () + override def onBuildTaskStart(params: TaskStartParams): Unit = () + override def onBuildTaskProgress(params: TaskProgressParams): Unit = () + override def onBuildTaskFinish(params: TaskFinishParams): Unit = () + override def onBuildPublishDiagnostics(params: PublishDiagnosticsParams): Unit = () + override def onBuildTargetDidChange(params: DidChangeBuildTarget): Unit = () +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspConnectionDetails.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspConnectionDetails.scala new file mode 100644 index 0000000000..6e476cdb17 --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspConnectionDetails.scala @@ -0,0 +1,29 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bsp + +import io.circe.Decoder +import io.circe.generic.semiauto.deriveDecoder +import org.scalasteward.core.util.Nel + +// https://build-server-protocol.github.io/docs/overview/server-discovery#the-bsp-connection-details +final case class BspConnectionDetails(argv: Nel[String]) + +object BspConnectionDetails { + implicit val bspConnectionDetailsDecoder: Decoder[BspConnectionDetails] = + deriveDecoder +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala new file mode 100644 index 0000000000..f5e286271d --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -0,0 +1,102 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bsp + +import better.files.File +import cats.effect.Async +import cats.syntax.all._ +import ch.epfl.scala.bsp4j.{ + BuildClientCapabilities, + DependencyModulesParams, + DependencyModulesResult, + InitializeBuildParams +} +import io.circe.parser.decode +import java.util.Collections +import java.util.concurrent.CompletableFuture +import org.scalasteward.core.buildtool.BuildRoot +import org.scalasteward.core.data._ +import org.scalasteward.core.io.{FileAlg, ProcessAlg, WorkspaceAlg} +import scala.jdk.CollectionConverters._ + +final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit + fileAlg: FileAlg[F], + processAlg: ProcessAlg[F], + workspaceAlg: WorkspaceAlg[F], + F: Async[F] +) { + def getDependencies( + bspServerType: BspServerType, + buildRoot: BuildRoot + ): F[List[Scope.Dependencies]] = + for { + buildRootDir <- workspaceAlg.buildRootDir(buildRoot) + connectionDetails <- createConnectionDetailsFile(bspServerType, buildRoot) + details <- readConnectionDetailsFile(connectionDetails) + initBuildParams = new InitializeBuildParams( + "Scala Steward", + org.scalasteward.core.BuildInfo.version, + org.scalasteward.core.BuildInfo.bsp4jVersion, + buildRootDir.uri.toString, + new BuildClientCapabilities(Collections.emptyList()) + ) + bspDependencies <- BspProcess.run(details.argv, buildRootDir).use { p => + for { + _ <- lift(p.buildInitialize(initBuildParams).thenAccept(_ => p.onBuildInitialized())) + targets <- lift(p.workspaceBuildTargets()) + dependencyModulesParams = + new DependencyModulesParams(targets.getTargets.asScala.map(_.getId).asJava) + dependencyModulesResult <- lift(p.buildTargetDependencyModules(dependencyModulesParams)) + _ <- lift(p.buildShutdown().thenAccept(_ => p.onBuildExit())) + } yield dependencyModulesResult + } + } yield dependenciesFrom(bspDependencies) + + private def createConnectionDetailsFile( + bspServerType: BspServerType, + buildRoot: BuildRoot + ): F[File] = + for { + buildRootDir <- workspaceAlg.buildRootDir(buildRoot) + _ <- processAlg.execSandboxed(bspServerType.connectionDetailsCommand, buildRootDir) + } yield buildRootDir / ".bsp" / bspServerType.connectionDetailsName + + private def readConnectionDetailsFile(connectionDetailsFile: File): F[BspConnectionDetails] = + fileAlg.readFile(connectionDetailsFile).flatMap { + case Some(content) => + decode[BspConnectionDetails](content) match { + case Right(connectionDetails) => F.pure(connectionDetails) + case Left(error) => F.raiseError(error) + } + case None => F.raiseError(new Throwable) + } + + private def lift[A](fut: => CompletableFuture[A]): F[A] = + F.fromCompletableFuture(F.blocking(fut)) + + private def dependenciesFrom(bspDependencies: DependencyModulesResult): List[Scope.Dependencies] = + bspDependencies.getItems.asScala.toList.map { item => + val dependencies = item.getModules.asScala.toList.mapFilter { module => + module.getName.split(':') match { + case Array(groupId, artifactId) => + Dependency(GroupId(groupId), ArtifactId(artifactId), Version(module.getVersion)).some + case _ => None + } + } + Scope(dependencies, List(defaultResolver)) + } +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala new file mode 100644 index 0000000000..2a6c044290 --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala @@ -0,0 +1,64 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bsp + +import better.files.File +import cats.effect.implicits._ +import cats.effect.{Async, Resource} +import cats.syntax.all._ +import ch.epfl.scala.bsp4j._ +import java.util +import java.util.Collections +import java.util.concurrent.{AbstractExecutorService, ExecutorService, TimeUnit} +import org.eclipse.lsp4j.jsonrpc.Launcher +import org.scalasteward.core.util.Nel +import scala.concurrent.ExecutionContext + +object BspProcess { + def run[F[_]]( + command: Nel[String], + workingDirectory: File + )(implicit F: Async[F]): Resource[F, BuildServer] = + for { + process <- Resource.make(F.blocking { + val pb = new ProcessBuilder(command.toList: _*) + pb.directory(workingDirectory.toJava) + pb.start() + })(process => F.blocking(process.waitFor()).void) + ec <- Resource.eval(F.executionContext) + launcher <- Resource.eval(F.blocking { + new Launcher.Builder[BuildServer]() + .setOutput(process.getOutputStream) + .setInput(process.getInputStream) + .setExecutorService(fromExecutionContext(ec)) + .setLocalService(new BspClient) + .setRemoteInterface(classOf[BuildServer]) + .create() + }) + _ <- F.blocking(launcher.startListening()).background + } yield launcher.getRemoteProxy + + private def fromExecutionContext(ec: ExecutionContext): ExecutorService = + new AbstractExecutorService { + override def shutdown(): Unit = () + override def shutdownNow(): util.List[Runnable] = Collections.emptyList() + override def isShutdown: Boolean = false + override def isTerminated: Boolean = false + override def awaitTermination(timeout: Long, unit: TimeUnit): Boolean = false + override def execute(command: Runnable): Unit = ec.execute(command) + } +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala new file mode 100644 index 0000000000..4f63aad02f --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -0,0 +1,38 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bsp + +import org.scalasteward.core.util.Nel + +sealed abstract class BspServerType( + val connectionDetailsCommand: Nel[String], + val connectionDetailsName: String +) + +object BspServerType { + case object Mill + extends BspServerType( + connectionDetailsCommand = Nel.of("mill", "mill.bsp.BSP/install"), + connectionDetailsName = "mill-bsp.json" + ) + + case object Sbt + extends BspServerType( + connectionDetailsCommand = Nel.of("sbt", "bspConfig"), + connectionDetailsName = "sbt.json" + ) +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala index 94cd015ef9..039351f9c4 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala @@ -19,6 +19,7 @@ package org.scalasteward.core.buildtool.mill import better.files.File import cats.effect.MonadCancelThrow import cats.syntax.all._ +import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} import org.scalasteward.core.buildtool.mill.MillAlg._ import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} import org.scalasteward.core.data._ @@ -28,6 +29,7 @@ import org.scalasteward.core.util.Nel import org.typelevel.log4cats.Logger final class MillAlg[F[_]](implicit + bspExtractor: BspExtractor[F], fileAlg: FileAlg[F], logger: Logger[F], processAlg: ProcessAlg[F], @@ -44,16 +46,8 @@ final class MillAlg[F[_]](implicit override def getDependencies(buildRoot: BuildRoot): F[List[Scope.Dependencies]] = for { buildRootDir <- workspaceAlg.buildRootDir(buildRoot) - predef = buildRootDir / "scala-steward.sc" millBuildVersion <- getMillVersion(buildRootDir) - extracted <- fileAlg.createTemporarily(predef, content(millBuildVersion)).surround { - val command = Nel("mill", List("-i", "-p", predef.toString, "show", extractDeps)) - processAlg.execSandboxed(command, buildRootDir) - } - parsed <- F.fromEither( - parser.parseModules(extracted.dropWhile(!_.startsWith("{")).mkString("\n")) - ) - dependencies = parsed.map(module => Scope(module.dependencies, module.repositories)) + dependencies <- getBuildDependencies(buildRoot, buildRootDir, millBuildVersion) millBuildDeps = millBuildVersion.toSeq.map(version => Scope(List(millMainArtifact(version)), List(millMainResolver)) ) @@ -63,6 +57,28 @@ final class MillAlg[F[_]](implicit } } yield dependencies ++ millBuildDeps ++ millPluginDeps + private def getBuildDependencies( + buildRoot: BuildRoot, + buildRootDir: File, + millBuildVersion: Option[Version] + ): F[List[Scope.Dependencies]] = { + val useBsp = false + if (useBsp) bspExtractor.getDependencies(BspServerType.Mill, buildRoot) + else { + for { + _ <- F.unit + predef = buildRootDir / "scala-steward.sc" + extracted <- fileAlg.createTemporarily(predef, content(millBuildVersion)).surround { + val command = Nel("mill", List("-i", "-p", predef.toString, "show", extractDeps)) + processAlg.execSandboxed(command, buildRootDir) + } + parsed <- + F.fromEither(parser.parseModules(extracted.dropWhile(!_.startsWith("{")).mkString("\n"))) + dependencies = parsed.map(module => Scope(module.dependencies, module.repositories)) + } yield dependencies + } + } + override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = logger.warn( s"Scalafix migrations are currently not supported in $name projects, see https://github.com/scala-steward-org/scala-steward/issues/2838 for details" diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7ff0caa2ca..99da645207 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,6 +3,7 @@ import sbt._ object Dependencies { val bcprovJdk15to18 = "org.bouncycastle" % "bcprov-jdk15to18" % "1.72" val betterFiles = "com.github.pathikrit" %% "better-files" % "3.9.1" + val bsp4j = "ch.epfl.scala" % "bsp4j" % "2.1.0-M4" val catsEffect = "org.typelevel" %% "cats-effect" % "3.4.5" val catsCore = "org.typelevel" %% "cats-core" % "2.9.0" val catsLaws = "org.typelevel" %% "cats-laws" % catsCore.revision diff --git a/repos.md b/repos.md index 39136251d2..a038239cdd 100644 --- a/repos.md +++ b/repos.md @@ -6,9 +6,4 @@ The format is "- $owner/$repo". If you want Scala Steward to keep a non-default branch up-to-date, use "- $owner/$repo:$branch". All lines that do not start with a hyphen and space are ignored. -- scala-steward-org/mill-plugin -- scala-steward-org/sbt-plugin -- scala-steward-org/scala-steward -- scala-steward-org/test-repo-1 -- scala-steward-org/test-repo-1:scala-cli-test - scala-steward-org/test-repo-2 From c3b8c1ea48e3c5a3e7f772798ae767e091c17b95 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 28 Jan 2023 12:48:37 +0100 Subject: [PATCH 02/20] Add BspServerType.Bazel --- .../core/buildtool/bsp/BspServerType.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index 4f63aad02f..b117b47db1 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -24,6 +24,20 @@ sealed abstract class BspServerType( ) object BspServerType { + // https://github.com/JetBrains/bazel-bsp#easy-way-coursier + case object Bazel + extends BspServerType( + connectionDetailsCommand = Nel.of( + "cs", + "launch", + "org.jetbrains.bsp:bazel-bsp:2.4.0", + "-M", + "org.jetbrains.bsp.bazel.install.Install" + ), + connectionDetailsName = "bazelbsp.json" + ) + + // https://com-lihaoyi.github.io/mill/mill/Plugin_BSP.html case object Mill extends BspServerType( connectionDetailsCommand = Nel.of("mill", "mill.bsp.BSP/install"), From 3b7229f7f25c2f7a758b2ef194ac917ec64957f4 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 28 Jan 2023 13:02:21 +0100 Subject: [PATCH 03/20] Add BspServerType.ScalaCli --- .../org/scalasteward/core/buildtool/bsp/BspServerType.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index b117b47db1..a17713afc4 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -49,4 +49,10 @@ object BspServerType { connectionDetailsCommand = Nel.of("sbt", "bspConfig"), connectionDetailsName = "sbt.json" ) + + case object ScalaCli + extends BspServerType( + connectionDetailsCommand = Nel.of("scala-cli", "setup-ide", "."), + connectionDetailsName = "scala-cli.json" + ) } From 36a35593e5dec9aded7c260de1fb95f7fa98ce02 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 28 Jan 2023 16:45:07 +0100 Subject: [PATCH 04/20] Extract new method getBspDependencies --- .../core/buildtool/bsp/BspExtractor.scala | 44 +++++++++++-------- .../core/buildtool/bsp/BspServerType.scala | 1 + 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index f5e286271d..7cefc4d367 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -47,24 +47,8 @@ final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit buildRootDir <- workspaceAlg.buildRootDir(buildRoot) connectionDetails <- createConnectionDetailsFile(bspServerType, buildRoot) details <- readConnectionDetailsFile(connectionDetails) - initBuildParams = new InitializeBuildParams( - "Scala Steward", - org.scalasteward.core.BuildInfo.version, - org.scalasteward.core.BuildInfo.bsp4jVersion, - buildRootDir.uri.toString, - new BuildClientCapabilities(Collections.emptyList()) - ) - bspDependencies <- BspProcess.run(details.argv, buildRootDir).use { p => - for { - _ <- lift(p.buildInitialize(initBuildParams).thenAccept(_ => p.onBuildInitialized())) - targets <- lift(p.workspaceBuildTargets()) - dependencyModulesParams = - new DependencyModulesParams(targets.getTargets.asScala.map(_.getId).asJava) - dependencyModulesResult <- lift(p.buildTargetDependencyModules(dependencyModulesParams)) - _ <- lift(p.buildShutdown().thenAccept(_ => p.onBuildExit())) - } yield dependencyModulesResult - } - } yield dependenciesFrom(bspDependencies) + bspDependencies <- getBspDependencies(buildRootDir, details) + } yield transform(bspDependencies) private def createConnectionDetailsFile( bspServerType: BspServerType, @@ -85,10 +69,32 @@ final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit case None => F.raiseError(new Throwable) } + private def getBspDependencies( + buildRootDir: File, + details: BspConnectionDetails + ): F[DependencyModulesResult] = + BspProcess.run(details.argv, buildRootDir).use { p => + val initBuildParams = new InitializeBuildParams( + "Scala Steward", + org.scalasteward.core.BuildInfo.version, + org.scalasteward.core.BuildInfo.bsp4jVersion, + buildRootDir.uri.toString, + new BuildClientCapabilities(Collections.emptyList()) + ) + for { + _ <- lift(p.buildInitialize(initBuildParams).thenAccept(_ => p.onBuildInitialized())) + targets <- lift(p.workspaceBuildTargets()) + dependencyModulesParams = + new DependencyModulesParams(targets.getTargets.asScala.map(_.getId).asJava) + dependencyModulesResult <- lift(p.buildTargetDependencyModules(dependencyModulesParams)) + _ <- lift(p.buildShutdown().thenAccept(_ => p.onBuildExit())) + } yield dependencyModulesResult + } + private def lift[A](fut: => CompletableFuture[A]): F[A] = F.fromCompletableFuture(F.blocking(fut)) - private def dependenciesFrom(bspDependencies: DependencyModulesResult): List[Scope.Dependencies] = + private def transform(bspDependencies: DependencyModulesResult): List[Scope.Dependencies] = bspDependencies.getItems.asScala.toList.map { item => val dependencies = item.getModules.asScala.toList.mapFilter { module => module.getName.split(':') match { diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index a17713afc4..ab718204c9 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -50,6 +50,7 @@ object BspServerType { connectionDetailsName = "sbt.json" ) + // https://scala-cli.virtuslab.org/docs/commands/setup-ide#ide-support-internals case object ScalaCli extends BspServerType( connectionDetailsCommand = Nel.of("scala-cli", "setup-ide", "."), From a5d26f614c0d04824b034cf21328cdbecec6303b Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 28 Jan 2023 21:31:05 +0100 Subject: [PATCH 05/20] Check if BSP server is a dependency modules provider --- .../core/buildtool/bsp/BspExtractor.scala | 21 +++++++++++++------ .../core/buildtool/bsp/BspProcess.scala | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index 7cefc4d367..d74780b8c4 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -18,6 +18,7 @@ package org.scalasteward.core.buildtool.bsp import better.files.File import cats.effect.Async +import cats.effect.syntax.all._ import cats.syntax.all._ import ch.epfl.scala.bsp4j.{ BuildClientCapabilities, @@ -81,14 +82,22 @@ final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit buildRootDir.uri.toString, new BuildClientCapabilities(Collections.emptyList()) ) - for { - _ <- lift(p.buildInitialize(initBuildParams).thenAccept(_ => p.onBuildInitialized())) - targets <- lift(p.workspaceBuildTargets()) + (for { + initBuildResult <- lift(p.buildInitialize(initBuildParams)) + _ <- F.blocking(p.onBuildInitialized()) + isDependencyModulesProvider = + Option(initBuildResult.getCapabilities.getDependencyModulesProvider) + .exists(_.booleanValue()) + _ <- F.raiseWhen(!isDependencyModulesProvider) { + new Throwable(s"${initBuildResult.getDisplayName} is not a dependency modules provider") + } + buildTargetsResult <- lift(p.workspaceBuildTargets()) dependencyModulesParams = - new DependencyModulesParams(targets.getTargets.asScala.map(_.getId).asJava) + new DependencyModulesParams(buildTargetsResult.getTargets.asScala.map(_.getId).asJava) dependencyModulesResult <- lift(p.buildTargetDependencyModules(dependencyModulesParams)) - _ <- lift(p.buildShutdown().thenAccept(_ => p.onBuildExit())) - } yield dependencyModulesResult + } yield dependencyModulesResult).guarantee { + lift(p.buildShutdown()) >> F.blocking(p.onBuildExit()) + } } private def lift[A](fut: => CompletableFuture[A]): F[A] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala index 2a6c044290..d5671bce04 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala @@ -38,8 +38,8 @@ object BspProcess { val pb = new ProcessBuilder(command.toList: _*) pb.directory(workingDirectory.toJava) pb.start() - })(process => F.blocking(process.waitFor()).void) - ec <- Resource.eval(F.executionContext) + })(process => F.blocking(process.waitFor(1L, TimeUnit.SECONDS)).void) + ec <- Resource.executionContext launcher <- Resource.eval(F.blocking { new Launcher.Builder[BuildServer]() .setOutput(process.getOutputStream) From 5c814f0c7945a1c688f5da2d88420ca42708659d Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sat, 28 Jan 2023 22:34:16 +0100 Subject: [PATCH 06/20] Ensure that we always use the latest bazel-bsp version --- build.sbt | 4 +++- .../org/scalasteward/core/buildtool/bsp/BspServerType.scala | 2 +- project/Dependencies.scala | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index ea14d85109..8d837ef76a 100644 --- a/build.sbt +++ b/build.sbt @@ -179,7 +179,8 @@ lazy val core = myCrossProject("core") BuildInfoKey.map(git.gitHeadCommit) { case (k, v) => k -> v.getOrElse(mainBranch) }, BuildInfoKey("millPluginArtifactName" -> Dependencies.scalaStewardMillPluginArtifactName), BuildInfoKey("millPluginVersion" -> Dependencies.scalaStewardMillPlugin.revision), - BuildInfoKey("bsp4jVersion" -> Dependencies.bsp4j.revision) + BuildInfoKey("bsp4jVersion" -> Dependencies.bsp4j.revision), + BuildInfoKey("bazelBsp" -> Dependencies.bazelBsp) ), buildInfoPackage := moduleRootPkg.value, initialCommands += s""" @@ -265,6 +266,7 @@ lazy val dummy = myCrossProject("dummy") .settings(noPublishSettings) .settings( libraryDependencies ++= Seq( + Dependencies.bazelBsp, Dependencies.millMain, Dependencies.scalaStewardMillPlugin ) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index ab718204c9..690c5f84e9 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -30,7 +30,7 @@ object BspServerType { connectionDetailsCommand = Nel.of( "cs", "launch", - "org.jetbrains.bsp:bazel-bsp:2.4.0", + org.scalasteward.core.BuildInfo.bazelBsp, "-M", "org.jetbrains.bsp.bazel.install.Install" ), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 99da645207..2c806c2bde 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,6 +1,7 @@ import sbt._ object Dependencies { + val bazelBsp = "org.jetbrains.bsp" % "bazel-bsp" % "2.4.0" val bcprovJdk15to18 = "org.bouncycastle" % "bcprov-jdk15to18" % "1.72" val betterFiles = "com.github.pathikrit" %% "better-files" % "3.9.1" val bsp4j = "ch.epfl.scala" % "bsp4j" % "2.1.0-M4" From 43dbe1d3c20e5b3ee368b1da294e51a7ddf7175d Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sun, 29 Jan 2023 17:08:53 +0100 Subject: [PATCH 07/20] Handle artifactIds with cross build suffix --- .../org/scalasteward/core/buildtool/bsp/BspExtractor.scala | 5 ++++- .../main/scala/org/scalasteward/core/data/ArtifactId.scala | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index d74780b8c4..f26e6dc1be 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -108,7 +108,10 @@ final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit val dependencies = item.getModules.asScala.toList.mapFilter { module => module.getName.split(':') match { case Array(groupId, artifactId) => - Dependency(GroupId(groupId), ArtifactId(artifactId), Version(module.getVersion)).some + val g = GroupId(groupId) + val a = ArtifactId.from(artifactId) + val v = Version(module.getVersion) + Dependency(g, a, v).some case _ => None } } diff --git a/modules/core/src/main/scala/org/scalasteward/core/data/ArtifactId.scala b/modules/core/src/main/scala/org/scalasteward/core/data/ArtifactId.scala index 4126844ace..0254c52fe6 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/data/ArtifactId.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/data/ArtifactId.scala @@ -33,6 +33,13 @@ object ArtifactId { def apply(name: String, crossName: String): ArtifactId = ArtifactId(name, Some(crossName)) + def from(maybeCrossName: String): ArtifactId = + maybeCrossName.split('_').toList match { + case Nil => ArtifactId(maybeCrossName) + case _ :: Nil => ArtifactId(maybeCrossName) + case list @ x :: _ => ArtifactId(list.find(_.nonEmpty).getOrElse(x), maybeCrossName) + } + implicit val artifactIdCodec: Codec[ArtifactId] = deriveCodec From 636f01419fbbcfe76a53f27e3d4439b563d5cb86 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sun, 29 Jan 2023 19:28:01 +0100 Subject: [PATCH 08/20] Rename function to create an ExecutorService --- .../org/scalasteward/core/buildtool/bsp/BspProcess.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala index d5671bce04..8a4ac45aec 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspProcess.scala @@ -44,7 +44,7 @@ object BspProcess { new Launcher.Builder[BuildServer]() .setOutput(process.getOutputStream) .setInput(process.getInputStream) - .setExecutorService(fromExecutionContext(ec)) + .setExecutorService(executorServiceFrom(ec)) .setLocalService(new BspClient) .setRemoteInterface(classOf[BuildServer]) .create() @@ -52,7 +52,7 @@ object BspProcess { _ <- F.blocking(launcher.startListening()).background } yield launcher.getRemoteProxy - private def fromExecutionContext(ec: ExecutionContext): ExecutorService = + private def executorServiceFrom(ec: ExecutionContext): ExecutorService = new AbstractExecutorService { override def shutdown(): Unit = () override def shutdownNow(): util.List[Runnable] = Collections.emptyList() From 968cac5afc550d3284ead48158ee26b170a7b3de Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Thu, 7 Dec 2023 20:16:46 +0100 Subject: [PATCH 09/20] Add BleepAlg --- .../core/application/Context.scala | 2 + .../core/buildtool/BuildToolDispatcher.scala | 4 +- .../core/buildtool/bleep/BleepAlg.scala | 45 +++++++++++++++++++ .../core/buildtool/bsp/BspServerType.scala | 6 +++ repos.md | 2 +- 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala index f74deb3ba2..7a56756349 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala @@ -27,6 +27,7 @@ import org.http4s.client.Client import org.http4s.headers.`User-Agent` import org.scalasteward.core.application.Config.{ForgeCfg, StewardUsage} import org.scalasteward.core.buildtool.BuildToolDispatcher +import org.scalasteward.core.buildtool.bleep.BleepAlg import org.scalasteward.core.buildtool.bsp.BspExtractor import org.scalasteward.core.buildtool.maven.MavenAlg import org.scalasteward.core.buildtool.mill.MillAlg @@ -227,6 +228,7 @@ object Context { implicit val sbtAlg: SbtAlg[F] = new SbtAlg[F](config) implicit val scalaCliAlg: ScalaCliAlg[F] = new ScalaCliAlg[F] implicit val bspExtractor: BspExtractor[F] = new BspExtractor[F](config.defaultResolver) + implicit val bleepAlg: BleepAlg[F] = new BleepAlg[F] implicit val millAlg: MillAlg[F] = new MillAlg[F] implicit val buildToolDispatcher: BuildToolDispatcher[F] = new BuildToolDispatcher[F] implicit val refreshErrorAlg: RefreshErrorAlg[F] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala index 80538d4b1a..d4de94f330 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala @@ -18,6 +18,7 @@ package org.scalasteward.core.buildtool import cats.Monad import cats.syntax.all._ +import org.scalasteward.core.buildtool.bleep.BleepAlg import org.scalasteward.core.buildtool.maven.MavenAlg import org.scalasteward.core.buildtool.mill.MillAlg import org.scalasteward.core.buildtool.sbt.SbtAlg @@ -29,6 +30,7 @@ import org.scalasteward.core.scalafmt.ScalafmtAlg import org.typelevel.log4cats.Logger final class BuildToolDispatcher[F[_]](implicit + bleepAlg: BleepAlg[F], logger: Logger[F], mavenAlg: MavenAlg[F], millAlg: MillAlg[F], @@ -53,7 +55,7 @@ final class BuildToolDispatcher[F[_]](implicit buildTools.traverse_(_.runMigration(buildRoot, migration)) }) - private val allBuildTools = List(mavenAlg, millAlg, sbtAlg, scalaCliAlg) + private val allBuildTools = List(bleepAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg) private val fallbackBuildTool = List(sbtAlg) private def findBuildTools(buildRoot: BuildRoot): F[(BuildRoot, List[BuildToolAlg[F]])] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala new file mode 100644 index 0000000000..93043ee613 --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bleep + +import cats.Monad +import cats.syntax.all._ +import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} +import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} +import org.scalasteward.core.data.Scope.Dependencies +import org.scalasteward.core.edit.scalafix.ScalafixMigration +import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} + +final class BleepAlg[F[_]](implicit + bspExtractor: BspExtractor[F], + fileAlg: FileAlg[F], + workspaceAlg: WorkspaceAlg[F], + F: Monad[F] +) extends BuildToolAlg[F] { + override def name: String = "bleep" + + override def containsBuild(buildRoot: BuildRoot): F[Boolean] = + workspaceAlg + .buildRootDir(buildRoot) + .flatMap(buildRootDir => fileAlg.isRegularFile(buildRootDir / "bleep.yaml")) + + override def getDependencies(buildRoot: BuildRoot): F[List[Dependencies]] = + bspExtractor.getDependencies(BspServerType.Bleep, buildRoot) + + override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = + F.unit +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index 690c5f84e9..7ce130cc6b 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -37,6 +37,12 @@ object BspServerType { connectionDetailsName = "bazelbsp.json" ) + case object Bleep + extends BspServerType( + connectionDetailsCommand = Nel.of("bleep", "setup-ide"), + connectionDetailsName = "bleep.json" + ) + // https://com-lihaoyi.github.io/mill/mill/Plugin_BSP.html case object Mill extends BspServerType( diff --git a/repos.md b/repos.md index a038239cdd..12a7622712 100644 --- a/repos.md +++ b/repos.md @@ -6,4 +6,4 @@ The format is "- $owner/$repo". If you want Scala Steward to keep a non-default branch up-to-date, use "- $owner/$repo:$branch". All lines that do not start with a hyphen and space are ignored. -- scala-steward-org/test-repo-2 +- scala-steward-org/test-repo-1:bleep-test From 574972e72c1b05f147b21594c3d839fc4b7e6a04 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Thu, 7 Dec 2023 21:48:48 +0100 Subject: [PATCH 10/20] Add BazelAlg --- .../core/application/Context.scala | 4 +- .../core/buildtool/BuildToolDispatcher.scala | 4 +- .../core/buildtool/bazel/BazelAlg.scala | 45 +++++++++++++++++++ .../core/buildtool/scalacli/ScalaCliAlg.scala | 10 ++++- project/Dependencies.scala | 2 +- repos.md | 2 +- 6 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala index 7a56756349..3f4daabc2a 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala @@ -27,6 +27,7 @@ import org.http4s.client.Client import org.http4s.headers.`User-Agent` import org.scalasteward.core.application.Config.{ForgeCfg, StewardUsage} import org.scalasteward.core.buildtool.BuildToolDispatcher +import org.scalasteward.core.buildtool.bazel.BazelAlg import org.scalasteward.core.buildtool.bleep.BleepAlg import org.scalasteward.core.buildtool.bsp.BspExtractor import org.scalasteward.core.buildtool.maven.MavenAlg @@ -226,10 +227,11 @@ object Context { implicit val updateAlg: UpdateAlg[F] = new UpdateAlg[F] implicit val mavenAlg: MavenAlg[F] = new MavenAlg[F](config) implicit val sbtAlg: SbtAlg[F] = new SbtAlg[F](config) - implicit val scalaCliAlg: ScalaCliAlg[F] = new ScalaCliAlg[F] implicit val bspExtractor: BspExtractor[F] = new BspExtractor[F](config.defaultResolver) + implicit val bazelAlg: BazelAlg[F] = new BazelAlg[F] implicit val bleepAlg: BleepAlg[F] = new BleepAlg[F] implicit val millAlg: MillAlg[F] = new MillAlg[F] + implicit val scalaCliAlg: ScalaCliAlg[F] = new ScalaCliAlg[F] implicit val buildToolDispatcher: BuildToolDispatcher[F] = new BuildToolDispatcher[F] implicit val refreshErrorAlg: RefreshErrorAlg[F] = new RefreshErrorAlg[F](refreshErrorStore, config.refreshBackoffPeriod) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala index d4de94f330..2a50f7590f 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala @@ -18,6 +18,7 @@ package org.scalasteward.core.buildtool import cats.Monad import cats.syntax.all._ +import org.scalasteward.core.buildtool.bazel.BazelAlg import org.scalasteward.core.buildtool.bleep.BleepAlg import org.scalasteward.core.buildtool.maven.MavenAlg import org.scalasteward.core.buildtool.mill.MillAlg @@ -30,6 +31,7 @@ import org.scalasteward.core.scalafmt.ScalafmtAlg import org.typelevel.log4cats.Logger final class BuildToolDispatcher[F[_]](implicit + bazelAlg: BazelAlg[F], bleepAlg: BleepAlg[F], logger: Logger[F], mavenAlg: MavenAlg[F], @@ -55,7 +57,7 @@ final class BuildToolDispatcher[F[_]](implicit buildTools.traverse_(_.runMigration(buildRoot, migration)) }) - private val allBuildTools = List(bleepAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg) + private val allBuildTools = List(bazelAlg, bleepAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg) private val fallbackBuildTool = List(sbtAlg) private def findBuildTools(buildRoot: BuildRoot): F[(BuildRoot, List[BuildToolAlg[F]])] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala new file mode 100644 index 0000000000..51d6a36385 --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala @@ -0,0 +1,45 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.bazel + +import cats.Monad +import cats.syntax.all._ +import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} +import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} +import org.scalasteward.core.data.Scope.Dependencies +import org.scalasteward.core.edit.scalafix.ScalafixMigration +import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} + +final class BazelAlg[F[_]](implicit + bspExtractor: BspExtractor[F], + fileAlg: FileAlg[F], + workspaceAlg: WorkspaceAlg[F], + F: Monad[F] +) extends BuildToolAlg[F] { + override def name: String = "Bazel" + + override def containsBuild(buildRoot: BuildRoot): F[Boolean] = + workspaceAlg + .buildRootDir(buildRoot) + .flatMap(buildRootDir => fileAlg.isRegularFile(buildRootDir / "BUILD")) + + override def getDependencies(buildRoot: BuildRoot): F[List[Dependencies]] = + bspExtractor.getDependencies(BspServerType.Bazel, buildRoot) + + override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = + F.unit +} diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala index 6de5d15c6f..0458e81f52 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala @@ -18,6 +18,7 @@ package org.scalasteward.core.buildtool.scalacli import cats.Monad import cats.syntax.all._ +import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} import org.scalasteward.core.buildtool.sbt.SbtAlg import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} import org.scalasteward.core.data.Scope @@ -51,6 +52,7 @@ object ScalaCliAlg { } final class ScalaCliAlg[F[_]](implicit + bspExtractor: BspExtractor[F], fileAlg: FileAlg[F], gitAlg: GitAlg[F], logger: Logger[F], @@ -69,7 +71,13 @@ final class ScalaCliAlg[F[_]](implicit .map(_.exists(path => path.startsWith(buildRootPath) && extensions.exists(path.endsWith))) } - override def getDependencies(buildRoot: BuildRoot): F[List[Scope.Dependencies]] = + override def getDependencies(buildRoot: BuildRoot): F[List[Scope.Dependencies]] = { + val useBsp = true + if (useBsp) bspExtractor.getDependencies(BspServerType.ScalaCli, buildRoot) + else getDependenciesViaSbtExport(buildRoot) + } + + private def getDependenciesViaSbtExport(buildRoot: BuildRoot): F[List[Scope.Dependencies]] = for { buildRootDir <- workspaceAlg.buildRootDir(buildRoot) exportDir = "tmp-sbt-build-for-scala-steward" diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a04157499a..e6a99e46c1 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,7 +1,7 @@ import sbt._ object Dependencies { - val bazelBsp = "org.jetbrains.bsp" % "bazel-bsp" % "2.5.1" + val bazelBsp = "org.jetbrains.bsp" % "bazel-bsp" % "3.1.0" val bcprovJdk15to18 = "org.bouncycastle" % "bcprov-jdk15to18" % "1.77" val betterFiles = "com.github.pathikrit" %% "better-files" % "3.9.2" val bsp4j = "ch.epfl.scala" % "bsp4j" % "2.1.0-M4" diff --git a/repos.md b/repos.md index 12a7622712..d29e872bfc 100644 --- a/repos.md +++ b/repos.md @@ -6,4 +6,4 @@ The format is "- $owner/$repo". If you want Scala Steward to keep a non-default branch up-to-date, use "- $owner/$repo:$branch". All lines that do not start with a hyphen and space are ignored. -- scala-steward-org/test-repo-1:bleep-test +- scala-steward-org/test-repo-1:scala-cli-test From bb8fad3d63b2826515afedf8703bc1be1a4df97b Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Fri, 8 Dec 2023 07:52:48 +0100 Subject: [PATCH 11/20] Disable BazelAlg and BleepAlg --- .../core/buildtool/BuildToolAlg.scala | 2 ++ .../core/buildtool/BuildToolDispatcher.scala | 3 +- .../core/buildtool/bazel/BazelAlg.scala | 2 ++ .../core/buildtool/bleep/BleepAlg.scala | 2 ++ .../core/buildtool/mill/MillAlg.scala | 33 +++++++++---------- .../core/buildtool/scalacli/ScalaCliAlg.scala | 2 +- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolAlg.scala index 6a62e4cd23..b2357e01b2 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolAlg.scala @@ -20,6 +20,8 @@ import org.scalasteward.core.data.Scope import org.scalasteward.core.edit.scalafix.ScalafixMigration trait BuildToolAlg[F[_]] { + def enabled: Boolean = true + def name: String def containsBuild(buildRoot: BuildRoot): F[Boolean] diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala index 2a50f7590f..797e4963f2 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala @@ -57,7 +57,8 @@ final class BuildToolDispatcher[F[_]](implicit buildTools.traverse_(_.runMigration(buildRoot, migration)) }) - private val allBuildTools = List(bazelAlg, bleepAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg) + private val allBuildTools = + List(bazelAlg, bleepAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg).filter(_.enabled) private val fallbackBuildTool = List(sbtAlg) private def findBuildTools(buildRoot: BuildRoot): F[(BuildRoot, List[BuildToolAlg[F]])] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala index 51d6a36385..677dc78830 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala @@ -30,6 +30,8 @@ final class BazelAlg[F[_]](implicit workspaceAlg: WorkspaceAlg[F], F: Monad[F] ) extends BuildToolAlg[F] { + override def enabled: Boolean = false + override def name: String = "Bazel" override def containsBuild(buildRoot: BuildRoot): F[Boolean] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala index 93043ee613..944c4e9acc 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala @@ -30,6 +30,8 @@ final class BleepAlg[F[_]](implicit workspaceAlg: WorkspaceAlg[F], F: Monad[F] ) extends BuildToolAlg[F] { + override def enabled: Boolean = false + override def name: String = "bleep" override def containsBuild(buildRoot: BuildRoot): F[Boolean] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala index 7c7b806154..51d879a427 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/mill/MillAlg.scala @@ -60,7 +60,10 @@ final class MillAlg[F[_]](implicit for { buildRootDir <- workspaceAlg.buildRootDir(buildRoot) millBuildVersion <- getMillVersion(buildRootDir) - dependencies <- getBuildDependencies(buildRoot, buildRootDir, millBuildVersion) + useBsp = false + dependencies <- + if (useBsp) bspExtractor.getDependencies(BspServerType.Mill, buildRoot) + else getProjectDependencies(buildRootDir, millBuildVersion) millBuildDeps = millBuildVersion.toSeq.map(version => Scope(List(millMainArtifact(version)), List(millMainResolver)) ) @@ -70,25 +73,19 @@ final class MillAlg[F[_]](implicit } } yield dependencies ++ millBuildDeps ++ millPluginDeps - private def getBuildDependencies( - buildRoot: BuildRoot, + private def getProjectDependencies( buildRootDir: File, millBuildVersion: Option[Version] - ): F[List[Scope.Dependencies]] = { - val useBsp = false - if (useBsp) bspExtractor.getDependencies(BspServerType.Mill, buildRoot) - else { - for { - extracted <- - if (isMillVersionGreaterOrEqual011(millBuildVersion)) runMill(buildRootDir) - else runMillUnder011(buildRootDir, millBuildVersion) - parsed <- F.fromEither( - parser.parseModules(extracted.dropWhile(!_.startsWith("{")).mkString("\n")) - ) - dependencies = parsed.map(module => Scope(module.dependencies, module.repositories)) - } yield dependencies - } - } + ): F[List[Scope.Dependencies]] = + for { + extracted <- + if (isMillVersionGreaterOrEqual011(millBuildVersion)) runMill(buildRootDir) + else runMillUnder011(buildRootDir, millBuildVersion) + parsed <- F.fromEither( + parser.parseModules(extracted.dropWhile(!_.startsWith("{")).mkString("\n")) + ) + dependencies = parsed.map(module => Scope(module.dependencies, module.repositories)) + } yield dependencies override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = logger.warn( diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala index 0458e81f52..0668db4b05 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala @@ -72,7 +72,7 @@ final class ScalaCliAlg[F[_]](implicit } override def getDependencies(buildRoot: BuildRoot): F[List[Scope.Dependencies]] = { - val useBsp = true + val useBsp = false if (useBsp) bspExtractor.getDependencies(BspServerType.ScalaCli, buildRoot) else getDependenciesViaSbtExport(buildRoot) } From 3433b490c01082c8ac4629679f8d02c379f6c923 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Fri, 8 Dec 2023 08:05:20 +0100 Subject: [PATCH 12/20] Gradle? --- .../core/buildtool/bsp/BspServerType.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index 7ce130cc6b..47db1295ac 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -37,12 +37,22 @@ object BspServerType { connectionDetailsName = "bazelbsp.json" ) + // https://bleep.build/ case object Bleep extends BspServerType( connectionDetailsCommand = Nel.of("bleep", "setup-ide"), connectionDetailsName = "bleep.json" ) + // https://github.com/microsoft/build-server-for-gradle + /* + case object Gradle + extends BspServerType( + connectionDetailsCommand = ???, + connectionDetailsName = ??? + ) + */ + // https://com-lihaoyi.github.io/mill/mill/Plugin_BSP.html case object Mill extends BspServerType( @@ -50,6 +60,8 @@ object BspServerType { connectionDetailsName = "mill-bsp.json" ) + // https://www.scala-sbt.org + // not usable until https://github.com/sbt/sbt/issues/6957 is resolved case object Sbt extends BspServerType( connectionDetailsCommand = Nel.of("sbt", "bspConfig"), From 1aaf1c33d031c377fd12f8b7d8c85308bef3271a Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Fri, 8 Dec 2023 13:02:50 +0100 Subject: [PATCH 13/20] Add more repos --- repos.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/repos.md b/repos.md index d29e872bfc..44aa1e9979 100644 --- a/repos.md +++ b/repos.md @@ -6,4 +6,11 @@ The format is "- $owner/$repo". If you want Scala Steward to keep a non-default branch up-to-date, use "- $owner/$repo:$branch". All lines that do not start with a hyphen and space are ignored. +- scala-steward-org/mill-plugin +- scala-steward-org/sbt-plugin +- scala-steward-org/scala-steward +- scala-steward-org/test-repo-1 +- scala-steward-org/test-repo-1:bleep-test - scala-steward-org/test-repo-1:scala-cli-test +- scala-steward-org/test-repo-2 +- scala-steward-org/test-repo-2:bazel From 7f8179040620922d1946ecb728dc1fd785d5da09 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Tue, 12 Dec 2023 10:28:04 +0100 Subject: [PATCH 14/20] Add a timeout to the BSP process --- .../core/application/Context.scala | 3 +- .../core/buildtool/bsp/BspExtractor.scala | 61 +++++++++++-------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala index d5923ccda4..9eb02b64bc 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala @@ -228,7 +228,8 @@ object Context { implicit val updateAlg: UpdateAlg[F] = new UpdateAlg[F] implicit val mavenAlg: MavenAlg[F] = new MavenAlg[F](config) implicit val sbtAlg: SbtAlg[F] = new SbtAlg[F](config) - implicit val bspExtractor: BspExtractor[F] = new BspExtractor[F](config.defaultResolver) + implicit val bspExtractor: BspExtractor[F] = + new BspExtractor[F](config.defaultResolver, config.processCfg.processTimeout) implicit val bazelAlg: BazelAlg[F] = new BazelAlg[F] implicit val bleepAlg: BleepAlg[F] = new BleepAlg[F] implicit val millAlg: MillAlg[F] = new MillAlg[F](config.defaultResolver) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index f26e6dc1be..0efc9b7f55 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -32,9 +32,10 @@ import java.util.concurrent.CompletableFuture import org.scalasteward.core.buildtool.BuildRoot import org.scalasteward.core.data._ import org.scalasteward.core.io.{FileAlg, ProcessAlg, WorkspaceAlg} +import scala.concurrent.duration.FiniteDuration import scala.jdk.CollectionConverters._ -final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit +final class BspExtractor[F[_]](defaultResolver: Resolver, processTimeout: FiniteDuration)(implicit fileAlg: FileAlg[F], processAlg: ProcessAlg[F], workspaceAlg: WorkspaceAlg[F], @@ -67,38 +68,43 @@ final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit case Right(connectionDetails) => F.pure(connectionDetails) case Left(error) => F.raiseError(error) } - case None => F.raiseError(new Throwable) + case None => + F.raiseError(new Throwable(s"${connectionDetailsFile.pathAsString} does not exist")) } private def getBspDependencies( buildRootDir: File, details: BspConnectionDetails ): F[DependencyModulesResult] = - BspProcess.run(details.argv, buildRootDir).use { p => - val initBuildParams = new InitializeBuildParams( - "Scala Steward", - org.scalasteward.core.BuildInfo.version, - org.scalasteward.core.BuildInfo.bsp4jVersion, - buildRootDir.uri.toString, - new BuildClientCapabilities(Collections.emptyList()) - ) - (for { - initBuildResult <- lift(p.buildInitialize(initBuildParams)) - _ <- F.blocking(p.onBuildInitialized()) - isDependencyModulesProvider = - Option(initBuildResult.getCapabilities.getDependencyModulesProvider) - .exists(_.booleanValue()) - _ <- F.raiseWhen(!isDependencyModulesProvider) { - new Throwable(s"${initBuildResult.getDisplayName} is not a dependency modules provider") - } - buildTargetsResult <- lift(p.workspaceBuildTargets()) - dependencyModulesParams = - new DependencyModulesParams(buildTargetsResult.getTargets.asScala.map(_.getId).asJava) - dependencyModulesResult <- lift(p.buildTargetDependencyModules(dependencyModulesParams)) - } yield dependencyModulesResult).guarantee { - lift(p.buildShutdown()) >> F.blocking(p.onBuildExit()) + BspProcess + .run(details.argv, buildRootDir) + .use { p => + val result = for { + initBuildResult <- lift(p.buildInitialize(initBuildParams(buildRootDir))) + _ <- F.blocking(p.onBuildInitialized()) + isDependencyModulesProvider = + Option(initBuildResult.getCapabilities.getDependencyModulesProvider) + .exists(_.booleanValue()) + _ <- F.raiseWhen(!isDependencyModulesProvider) { + new Throwable(s"${initBuildResult.getDisplayName} is not a dependency modules provider") + } + buildTargetsResult <- lift(p.workspaceBuildTargets()) + dependencyModulesParams = + new DependencyModulesParams(buildTargetsResult.getTargets.asScala.map(_.getId).asJava) + dependencyModulesResult <- lift(p.buildTargetDependencyModules(dependencyModulesParams)) + } yield dependencyModulesResult + result.guarantee(lift(p.buildShutdown()) >> F.blocking(p.onBuildExit())) } - } + .timeoutAndForget(processTimeout) + + private def initBuildParams(buildRootDir: File): InitializeBuildParams = + new InitializeBuildParams( + "Scala Steward", + org.scalasteward.core.BuildInfo.version, + org.scalasteward.core.BuildInfo.bsp4jVersion, + buildRootDir.uri.toString, + new BuildClientCapabilities(Collections.emptyList()) + ) private def lift[A](fut: => CompletableFuture[A]): F[A] = F.fromCompletableFuture(F.blocking(fut)) @@ -115,6 +121,9 @@ final class BspExtractor[F[_]](defaultResolver: Resolver)(implicit case _ => None } } + // The BSP does not yet provide resolvers, so we use the default resolver here. + // See https://github.com/build-server-protocol/build-server-protocol/discussions/500 + // for a proposal to add resolvers to BSP. Scope(dependencies, List(defaultResolver)) } } From d39c8cc47a49e8870f4209808da68b9192ecaaa4 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Wed, 13 Dec 2023 21:55:04 +0100 Subject: [PATCH 15/20] Add GradleAlg --- .../core/application/Context.scala | 2 + .../core/buildtool/BuildToolDispatcher.scala | 4 +- .../core/buildtool/bsp/BspServerType.scala | 9 ++-- .../core/buildtool/gradle/GradleAlg.scala | 48 +++++++++++++++++++ 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala diff --git a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala index 9eb02b64bc..1281fa2903 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/application/Context.scala @@ -30,6 +30,7 @@ import org.scalasteward.core.buildtool.BuildToolDispatcher import org.scalasteward.core.buildtool.bazel.BazelAlg import org.scalasteward.core.buildtool.bleep.BleepAlg import org.scalasteward.core.buildtool.bsp.BspExtractor +import org.scalasteward.core.buildtool.gradle.GradleAlg import org.scalasteward.core.buildtool.maven.MavenAlg import org.scalasteward.core.buildtool.mill.MillAlg import org.scalasteward.core.buildtool.sbt.SbtAlg @@ -232,6 +233,7 @@ object Context { new BspExtractor[F](config.defaultResolver, config.processCfg.processTimeout) implicit val bazelAlg: BazelAlg[F] = new BazelAlg[F] implicit val bleepAlg: BleepAlg[F] = new BleepAlg[F] + implicit val gradleAlg: GradleAlg[F] = new GradleAlg[F] implicit val millAlg: MillAlg[F] = new MillAlg[F](config.defaultResolver) implicit val scalaCliAlg: ScalaCliAlg[F] = new ScalaCliAlg[F] implicit val buildToolDispatcher: BuildToolDispatcher[F] = new BuildToolDispatcher[F] diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala index 797e4963f2..7426b8fd9c 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/BuildToolDispatcher.scala @@ -20,6 +20,7 @@ import cats.Monad import cats.syntax.all._ import org.scalasteward.core.buildtool.bazel.BazelAlg import org.scalasteward.core.buildtool.bleep.BleepAlg +import org.scalasteward.core.buildtool.gradle.GradleAlg import org.scalasteward.core.buildtool.maven.MavenAlg import org.scalasteward.core.buildtool.mill.MillAlg import org.scalasteward.core.buildtool.sbt.SbtAlg @@ -33,6 +34,7 @@ import org.typelevel.log4cats.Logger final class BuildToolDispatcher[F[_]](implicit bazelAlg: BazelAlg[F], bleepAlg: BleepAlg[F], + gradleAlg: GradleAlg[F], logger: Logger[F], mavenAlg: MavenAlg[F], millAlg: MillAlg[F], @@ -58,7 +60,7 @@ final class BuildToolDispatcher[F[_]](implicit }) private val allBuildTools = - List(bazelAlg, bleepAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg).filter(_.enabled) + List(bazelAlg, bleepAlg, gradleAlg, mavenAlg, millAlg, sbtAlg, scalaCliAlg).filter(_.enabled) private val fallbackBuildTool = List(sbtAlg) private def findBuildTools(buildRoot: BuildRoot): F[(BuildRoot, List[BuildToolAlg[F]])] = diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index 47db1295ac..7c3cbc5bbd 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -45,13 +45,12 @@ object BspServerType { ) // https://github.com/microsoft/build-server-for-gradle - /* + // waiting for https://github.com/microsoft/build-server-for-gradle/issues/115 case object Gradle extends BspServerType( - connectionDetailsCommand = ???, - connectionDetailsName = ??? + connectionDetailsCommand = Nel.one("???"), + connectionDetailsName = "???" ) - */ // https://com-lihaoyi.github.io/mill/mill/Plugin_BSP.html case object Mill @@ -61,7 +60,7 @@ object BspServerType { ) // https://www.scala-sbt.org - // not usable until https://github.com/sbt/sbt/issues/6957 is resolved + // waiting for https://github.com/sbt/sbt/issues/6957 case object Sbt extends BspServerType( connectionDetailsCommand = Nel.of("sbt", "bspConfig"), diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala new file mode 100644 index 0000000000..977f8c550d --- /dev/null +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala @@ -0,0 +1,48 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.scalasteward.core.buildtool.gradle + +import cats.Monad +import cats.syntax.all._ +import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} +import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} +import org.scalasteward.core.data.Scope.Dependencies +import org.scalasteward.core.edit.scalafix.ScalafixMigration +import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} + +final class GradleAlg[F[_]](implicit + bspExtractor: BspExtractor[F], + fileAlg: FileAlg[F], + workspaceAlg: WorkspaceAlg[F], + F: Monad[F] +) extends BuildToolAlg[F] { + override def enabled: Boolean = false + + override def name: String = "Gradle" + + override def containsBuild(buildRoot: BuildRoot): F[Boolean] = + workspaceAlg.buildRootDir(buildRoot).flatMap { buildRootDir => + List("build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts") + .existsM(name => fileAlg.isRegularFile(buildRootDir / name)) + } + + override def getDependencies(buildRoot: BuildRoot): F[List[Dependencies]] = + bspExtractor.getDependencies(BspServerType.Gradle, buildRoot) + + override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = + F.unit +} From 61180bbfcd39f647d0d6523c75ed5339c7106f61 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Thu, 14 Dec 2023 09:03:11 +0100 Subject: [PATCH 16/20] Remove runMigrations --- .../org/scalasteward/core/buildtool/bazel/BazelAlg.scala | 6 ++---- .../org/scalasteward/core/buildtool/bleep/BleepAlg.scala | 6 ++---- .../org/scalasteward/core/buildtool/gradle/GradleAlg.scala | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala index 677dc78830..a0f9224e2a 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bazel/BazelAlg.scala @@ -21,12 +21,13 @@ import cats.syntax.all._ import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} import org.scalasteward.core.data.Scope.Dependencies -import org.scalasteward.core.edit.scalafix.ScalafixMigration import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} +import org.typelevel.log4cats.Logger final class BazelAlg[F[_]](implicit bspExtractor: BspExtractor[F], fileAlg: FileAlg[F], + override protected val logger: Logger[F], workspaceAlg: WorkspaceAlg[F], F: Monad[F] ) extends BuildToolAlg[F] { @@ -41,7 +42,4 @@ final class BazelAlg[F[_]](implicit override def getDependencies(buildRoot: BuildRoot): F[List[Dependencies]] = bspExtractor.getDependencies(BspServerType.Bazel, buildRoot) - - override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = - F.unit } diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala index 944c4e9acc..4ecc6fae0a 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bleep/BleepAlg.scala @@ -21,12 +21,13 @@ import cats.syntax.all._ import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} import org.scalasteward.core.data.Scope.Dependencies -import org.scalasteward.core.edit.scalafix.ScalafixMigration import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} +import org.typelevel.log4cats.Logger final class BleepAlg[F[_]](implicit bspExtractor: BspExtractor[F], fileAlg: FileAlg[F], + override protected val logger: Logger[F], workspaceAlg: WorkspaceAlg[F], F: Monad[F] ) extends BuildToolAlg[F] { @@ -41,7 +42,4 @@ final class BleepAlg[F[_]](implicit override def getDependencies(buildRoot: BuildRoot): F[List[Dependencies]] = bspExtractor.getDependencies(BspServerType.Bleep, buildRoot) - - override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = - F.unit } diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala index 977f8c550d..28fe41fa4c 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/gradle/GradleAlg.scala @@ -21,12 +21,13 @@ import cats.syntax.all._ import org.scalasteward.core.buildtool.bsp.{BspExtractor, BspServerType} import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg} import org.scalasteward.core.data.Scope.Dependencies -import org.scalasteward.core.edit.scalafix.ScalafixMigration import org.scalasteward.core.io.{FileAlg, WorkspaceAlg} +import org.typelevel.log4cats.Logger final class GradleAlg[F[_]](implicit bspExtractor: BspExtractor[F], fileAlg: FileAlg[F], + override protected val logger: Logger[F], workspaceAlg: WorkspaceAlg[F], F: Monad[F] ) extends BuildToolAlg[F] { @@ -42,7 +43,4 @@ final class GradleAlg[F[_]](implicit override def getDependencies(buildRoot: BuildRoot): F[List[Dependencies]] = bspExtractor.getDependencies(BspServerType.Gradle, buildRoot) - - override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] = - F.unit } From e5e5dadb84f346ecb1b46c2bfd7369e115d029fe Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Thu, 14 Dec 2023 09:12:32 +0100 Subject: [PATCH 17/20] Add status to every BspServerType --- .../scalasteward/core/buildtool/bsp/BspServerType.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index 7c3cbc5bbd..c5faadcba3 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -25,6 +25,7 @@ sealed abstract class BspServerType( object BspServerType { // https://github.com/JetBrains/bazel-bsp#easy-way-coursier + // Status: waiting for https://youtrack.jetbrains.com/issue/BAZEL-616 case object Bazel extends BspServerType( connectionDetailsCommand = Nel.of( @@ -38,6 +39,7 @@ object BspServerType { ) // https://bleep.build/ + // Status: waiting for https://github.com/scalacenter/bloop/pull/2197 to propagate to bleep case object Bleep extends BspServerType( connectionDetailsCommand = Nel.of("bleep", "setup-ide"), @@ -45,7 +47,7 @@ object BspServerType { ) // https://github.com/microsoft/build-server-for-gradle - // waiting for https://github.com/microsoft/build-server-for-gradle/issues/115 + // Status: waiting for https://github.com/microsoft/build-server-for-gradle/issues/115 case object Gradle extends BspServerType( connectionDetailsCommand = Nel.one("???"), @@ -53,6 +55,7 @@ object BspServerType { ) // https://com-lihaoyi.github.io/mill/mill/Plugin_BSP.html + // Status: working case object Mill extends BspServerType( connectionDetailsCommand = Nel.of("mill", "mill.bsp.BSP/install"), @@ -60,7 +63,7 @@ object BspServerType { ) // https://www.scala-sbt.org - // waiting for https://github.com/sbt/sbt/issues/6957 + // Status: waiting for https://github.com/sbt/sbt/issues/6957 case object Sbt extends BspServerType( connectionDetailsCommand = Nel.of("sbt", "bspConfig"), @@ -68,6 +71,7 @@ object BspServerType { ) // https://scala-cli.virtuslab.org/docs/commands/setup-ide#ide-support-internals + // Status: waiting for https://github.com/scalacenter/bloop/pull/2197 to propagate to Scala CLI case object ScalaCli extends BspServerType( connectionDetailsCommand = Nel.of("scala-cli", "setup-ide", "."), From 412aef2166390c47f870d2194c155c571133905b Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Tue, 9 Jan 2024 12:53:11 +0100 Subject: [PATCH 18/20] Use better names --- .../org/scalasteward/core/buildtool/bsp/BspExtractor.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index 0efc9b7f55..f7369763d7 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -47,9 +47,9 @@ final class BspExtractor[F[_]](defaultResolver: Resolver, processTimeout: Finite ): F[List[Scope.Dependencies]] = for { buildRootDir <- workspaceAlg.buildRootDir(buildRoot) - connectionDetails <- createConnectionDetailsFile(bspServerType, buildRoot) - details <- readConnectionDetailsFile(connectionDetails) - bspDependencies <- getBspDependencies(buildRootDir, details) + connectionDetailsFile <- createConnectionDetailsFile(bspServerType, buildRoot) + connectionDetails <- readConnectionDetailsFile(connectionDetailsFile) + bspDependencies <- getBspDependencies(buildRootDir, connectionDetails) } yield transform(bspDependencies) private def createConnectionDetailsFile( From 24078ba97b8240cd02ac220d2e242a6d6b66bc73 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Tue, 9 Jan 2024 12:58:00 +0100 Subject: [PATCH 19/20] details -> connectionDetails --- .../org/scalasteward/core/buildtool/bsp/BspExtractor.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index f7369763d7..bda7dd8ca5 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -74,10 +74,10 @@ final class BspExtractor[F[_]](defaultResolver: Resolver, processTimeout: Finite private def getBspDependencies( buildRootDir: File, - details: BspConnectionDetails + connectionDetails: BspConnectionDetails ): F[DependencyModulesResult] = BspProcess - .run(details.argv, buildRootDir) + .run(connectionDetails.argv, buildRootDir) .use { p => val result = for { initBuildResult <- lift(p.buildInitialize(initBuildParams(buildRootDir))) From cf26eca236d487f4e7088910adfd1ece405914d1 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Wed, 11 Sep 2024 09:04:29 +0200 Subject: [PATCH 20/20] Get dependencies from Scala CLI via BSP --- .../core/buildtool/bsp/BspExtractor.scala | 26 +++++++++++++------ .../core/buildtool/bsp/BspServerType.scala | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala index bda7dd8ca5..10230de524 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspExtractor.scala @@ -112,14 +112,24 @@ final class BspExtractor[F[_]](defaultResolver: Resolver, processTimeout: Finite private def transform(bspDependencies: DependencyModulesResult): List[Scope.Dependencies] = bspDependencies.getItems.asScala.toList.map { item => val dependencies = item.getModules.asScala.toList.mapFilter { module => - module.getName.split(':') match { - case Array(groupId, artifactId) => - val g = GroupId(groupId) - val a = ArtifactId.from(artifactId) - val v = Version(module.getVersion) - Dependency(g, a, v).some - case _ => None - } + if ( + module.getDataKind === "maven" && module.getData.isInstanceOf[com.google.gson.JsonObject] + ) { + val data = module.getData.asInstanceOf[com.google.gson.JsonObject] + val g = GroupId(data.get("organization").getAsString) + val a = ArtifactId.from(data.get("name").getAsString) + val v = Version(data.get("version").getAsString) + Dependency(g, a, v).some + } else if (module.getName.contains(':')) { + module.getName.split(':') match { + case Array(groupId, artifactId) => + val g = GroupId(groupId) + val a = ArtifactId.from(artifactId) + val v = Version(module.getVersion) + Dependency(g, a, v).some + case _ => None + } + } else None } // The BSP does not yet provide resolvers, so we use the default resolver here. // See https://github.com/build-server-protocol/build-server-protocol/discussions/500 diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala index c5faadcba3..d36caa09db 100644 --- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala +++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/bsp/BspServerType.scala @@ -71,7 +71,7 @@ object BspServerType { ) // https://scala-cli.virtuslab.org/docs/commands/setup-ide#ide-support-internals - // Status: waiting for https://github.com/scalacenter/bloop/pull/2197 to propagate to Scala CLI + // Status: working case object ScalaCli extends BspServerType( connectionDetailsCommand = Nel.of("scala-cli", "setup-ide", "."),