diff --git a/build.sbt b/build.sbt index 238a2ccc..8da3c5ee 100644 --- a/build.sbt +++ b/build.sbt @@ -22,6 +22,8 @@ lazy val V = def semanticdbKotlinc = "0.2.0" def testcontainers = "0.39.3" def requests = "0.6.5" + def minimalMillVersion = "0.10.3" + def millScipVersion = "0.2.1" } inThisBuild( @@ -166,7 +168,9 @@ lazy val cli = project "scala213" -> V.scala213, "scala3" -> V.scala3, "bloopVersion" -> V.bloop, - "bspVersion" -> V.bsp + "bspVersion" -> V.bsp, + "minimalMillVersion" -> V.minimalMillVersion, + "millScipVersion" -> V.millScipVersion ), buildInfoPackage := "com.sourcegraph.scip_java", libraryDependencies ++= diff --git a/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/BuildTool.scala b/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/BuildTool.scala index da3f5206..335f284c 100644 --- a/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/BuildTool.scala +++ b/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/BuildTool.scala @@ -24,7 +24,8 @@ object BuildTool { new GradleBuildTool(index), new MavenBuildTool(index), new ScipBuildTool(index), - new SbtBuildTool(index) + new SbtBuildTool(index), + new MillBuildTool(index) ) def allNames: String = all(IndexCommand()).filterNot(_.isHidden).map(_.name).mkString(", ") diff --git a/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/MillBuildTool.scala b/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/MillBuildTool.scala new file mode 100644 index 00000000..309a9934 --- /dev/null +++ b/scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/MillBuildTool.scala @@ -0,0 +1,110 @@ +package com.sourcegraph.scip_java.buildtools + +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +import scala.jdk.CollectionConverters._ + +import com.sourcegraph.scip_java.commands.IndexCommand +import com.sourcegraph.scip_java.BuildInfo + +class MillBuildTool(index: IndexCommand) extends BuildTool("mill", index) { + + override def usedInCurrentDirectory(): Boolean = + Files.isRegularFile(index.workingDirectory.resolve("build.sc")) + + override def generateScip(): Int = + millVersion() match { + case Some(version) if isSupportedMillVersion(version) => + unconditionallyGenerateScip() + case Some(version) => + failFast( + s"Unsupported Mill version '${version}'. " + + s"To fix this problem, upgrade Mill to at least ${minimalMillVersion} and try again." + ) + case None => + failFast( + s"No Mill version detected. " + + s"To fix this problem, run the following command and try again: " + + s"echo '${minimalMillVersion}' >> .mill-version" + ) + } + + private def failFast(message: String): Int = { + index.app.error(message) + 1 + } + + private def unconditionallyGenerateScip(): Int = { + val localMill = Files.isRegularFile(millFile) + val command = + if (localMill) { + "./mill" + } else { + "mill" + } + val millProcess = index.process( + List( + command, + "--import", + s"ivy:io.chris-kipp::mill-scip::${BuildInfo.millScipVersion}", + "io.kipp.mill.scip.Scip/generate" + ) + ) + val scipFile = index + .workingDirectory + .resolve("out") + .resolve("io") + .resolve("kipp") + .resolve("mill") + .resolve("scip") + .resolve("Scip") + .resolve("generate.dest") + .resolve("index.scip") + + if (millProcess.exitCode == 0 && Files.isRegularFile(scipFile)) { + val output = index.workingDirectory.resolve("index.scip") + Files.copy(scipFile, output, StandardCopyOption.REPLACE_EXISTING) + index.app.info(output.toString) + } + millProcess.exitCode + } + + private lazy val minimalMillVersion = BuildInfo.minimalMillVersion + + private lazy val millFile = index.workingDirectory.resolve("mill") + + private def isSupportedMillVersion(version: String): Boolean = { + // Only supported in > 0.10.3 atm and we are on the 0.10x series. This will + // have to be updated in the future problably to just make sure it doesn't + // start with anything lower than 0.10.3 + if ( + version.startsWith("0.10") && + (version != "0.10.0" || version != "0.10.1" || version != "0.10.2") + ) + true + else + false + } + + /** + * Try to grab the Mill version from the .mill-version file. If not found we + * fall back to the mill file which could be the official mill launcher or the + * millw launcher renamed as mill, both which will have a DEFAULT_MILL_VERSION + * line. + */ + private def millVersion(): Option[String] = { + val millVersionFile = index.workingDirectory.resolve(".mill-version") + if (Files.isRegularFile(millVersionFile)) { + Files.readAllLines(millVersionFile).asScala.headOption + } else if (Files.isRegularFile(millFile)) { + Files + .readAllLines(millFile) + .asScala + .find(_.startsWith("DEFAULT_MILL_VERSION")) + .map(line => line.dropWhile(!_.isDigit)) + } else { + None + } + } +} diff --git a/tests/buildTools/src/test/scala/tests/MillBuildToolSuite.scala b/tests/buildTools/src/test/scala/tests/MillBuildToolSuite.scala new file mode 100644 index 00000000..44bcac4f --- /dev/null +++ b/tests/buildTools/src/test/scala/tests/MillBuildToolSuite.scala @@ -0,0 +1,32 @@ +package tests + +class MillBuildToolSuite extends BaseBuildToolSuite { + checkBuild( + s"minimal", + s"""|/.mill-version + |0.10.5 + |/build.sc + |import mill._, scalalib._ + |object minimal extends ScalaModule { + | def scalaVersion = "2.13.8" + | object test extends Tests with TestModule.Munit { + | def ivyDeps = Agg(ivy"org.scalameta::munit:1.0.0-M6") + | } + |} + |/minimal/src/Main.scala + |package minimal + |object Main extends App + |/minimal/test/src/MainSuite.scala + |package minimal + |class MainSpec extends munit.FunSuite { + | test("numbers") { + | assertEquals(1, 1) + | } + |} + |""".stripMargin, + expectedSemanticdbFiles = 2, + expectedPackages = + """|maven:munit:munit:1.0.0-M6 + |""".stripMargin + ) +}