From c05d3a10eefbcdd753cc349e57439e22730ac62d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 16:21:25 +0200 Subject: [PATCH 01/72] Update scala-cli.sh launcher for 0.1.4 (#910) Co-authored-by: gh-actions --- scala-cli.bat | 2 +- scala-cli.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scala-cli.bat b/scala-cli.bat index 9a942eb694..c6f0125eaf 100644 --- a/scala-cli.bat +++ b/scala-cli.bat @@ -7,7 +7,7 @@ rem Download the latest version of this script at https://github.com/VirtusLab/s setlocal enabledelayedexpansion -set "SCALA_CLI_VERSION=0.1.3" +set "SCALA_CLI_VERSION=0.1.4" set SCALA_CLI_URL=https://github.com/VirtusLab/scala-cli/releases/download/v%SCALA_CLI_VERSION%/scala-cli.bat set CACHE_BASE=%localappdata%/Coursier/v1 diff --git a/scala-cli.sh b/scala-cli.sh index 8f4671dfa0..65bc2c8696 100755 --- a/scala-cli.sh +++ b/scala-cli.sh @@ -7,7 +7,7 @@ set -eu -SCALA_CLI_VERSION="0.1.3" +SCALA_CLI_VERSION="0.1.4" if [ "$(expr substr $(uname -s) 1 5 2>/dev/null)" == "Linux" ]; then SCALA_CLI_URL="https://github.com/VirtusLab/scala-cli/releases/download/v$SCALA_CLI_VERSION/scala-cli-x86_64-pc-linux.gz" From 94e7d889c53967fc129fd7a6cc05f3d3cb3252fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 16:31:31 +0200 Subject: [PATCH 02/72] Back port of documentation changes to main (#911) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use absolute path for loading images/gifs (#874) * Trigger docs deploy Co-authored-by: Łukasz Wroński <46607934+lwronski@users.noreply.github.com> Co-authored-by: gh-actions Co-authored-by: Łukasz Wroński From 8150388fd0ecb090e9f3d30ed3f28c1aaf8ea54a Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Wed, 20 Apr 2022 00:10:37 +0200 Subject: [PATCH 03/72] Update jsoniter-scala-core, ... to 2.13.16 (#912) --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index 1539765123..3ccae14d8d 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -52,7 +52,7 @@ object Deps { object Versions { // jni-utils version may need to be sync-ed when bumping the coursier version def coursier = "2.1.0-M5-18-gfebf9838c" - def jsoniterScala = "2.13.15" + def jsoniterScala = "2.13.16" def scalaMeta = "4.5.4" def scalaNative = "0.4.4" def scalaPackager = "0.1.26" From 9b4f317c11daa30aeb2649cdf8b9496304b2a39f Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Wed, 20 Apr 2022 00:52:29 +0200 Subject: [PATCH 04/72] Update cli_2.13, shared_2.13 to 0.1.3 (#891) --- project/deps.sc | 2 +- project/settings.sc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/deps.sc b/project/deps.sc index 3ccae14d8d..37f97dbb59 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -56,7 +56,7 @@ object Deps { def scalaMeta = "4.5.4" def scalaNative = "0.4.4" def scalaPackager = "0.1.26" - def signingCli = "0.1.2" + def signingCli = "0.1.3" } def ammonite = ivy"com.lihaoyi:::ammonite:2.5.3" def asm = ivy"org.ow2.asm:asm:9.3" diff --git a/project/settings.sc b/project/settings.sc index ef7feb71b4..d66ef500e5 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -691,7 +691,7 @@ trait FormatNativeImageConf extends JavaModule { import mill.scalalib.api.CompilationResult trait ScalaCliCompile extends ScalaModule { - def compileScalaCliVersion = "0.1.2" + def compileScalaCliVersion = "0.1.3" def compileScalaCliUrl = { val ver = compileScalaCliVersion if (Properties.isLinux) Some( From e72f54d3ffce19f4c6754cfb7e1f68825b2c1d38 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Fri, 15 Apr 2022 14:52:29 +0200 Subject: [PATCH 05/72] Add an integration test for BSP workspace/reload of a --dependency option --- .../cli/integration/BspTestDefinitions.scala | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala index 45ea8f9e41..054cfd7ae8 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala @@ -72,7 +72,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) inputs: TestInputs, args: Seq[String], attempts: Int = if (TestUtil.isCI) 3 else 1, - pauseDuration: FiniteDuration = 5.seconds + pauseDuration: FiniteDuration = 5.seconds, + bspOptions: List[String] = List.empty, + reuseRoot: Option[os.Path] = None )( f: ( os.Path, @@ -82,9 +84,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) ): T = { def attempt(): Try[T] = Try { - val root = inputs.root() + val root = reuseRoot.getOrElse(inputs.root()) - val proc = os.proc(TestUtil.cli, "bsp", extraOptions, args) + val proc = os.proc(TestUtil.cli, "bsp", bspOptions ++ extraOptions, args) .spawn(cwd = root) var remoteServer: b.BuildServer with b.ScalaBuildServer with b.JavaBuildServer = null @@ -924,6 +926,56 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) } } } + + test("workspace/reload --dependency option") { + val inputs = TestInputs( + Seq( + os.rel / "ReloadTest.scala" -> + s"""import os.pwd + |object ReloadTest { + | println(pwd) + |} + |""".stripMargin + ) + ) + inputs.fromRoot { root => + os.proc(TestUtil.cli, "setup-ide", ".", extraOptions) + .call( + cwd = root, + stdout = os.Inherit + ) + val ideOptionsPath = root / Constants.workspaceDirName / "ide-options-v2.json" + val jsonOptions = List("--json-options", ideOptionsPath.toString) + withBsp(inputs, Seq("."), bspOptions = jsonOptions, reuseRoot = Some(root)) { + (_, _, remoteServer) => + async { + val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq + + val resp = + await(remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala) + expect(resp.getStatusCode == b.StatusCode.ERROR) + + val dependencyOptions = List("--dependency", "com.lihaoyi::os-lib::0.8.0") + os.proc(TestUtil.cli, "setup-ide", ".", dependencyOptions ++ extraOptions) + .call( + cwd = root, + stdout = os.Inherit + ) + + await(remoteServer.workspaceReload().asScala) + + val buildTargetsResp0 = await(remoteServer.workspaceBuildTargets().asScala) + val targets0 = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq + + val resp0 = + await(remoteServer.buildTargetCompile(new b.CompileParams(targets0.asJava)).asScala) + expect(resp0.getStatusCode == b.StatusCode.OK) + } + } + + } + } } object BspTestDefinitions { From 065f2e9aaa656cafb0222f8f2173704cd43765d6 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Fri, 15 Apr 2022 15:37:30 +0200 Subject: [PATCH 06/72] Add an integration test for BSP workspace/reload of an extra sources directory --- .../cli/integration/BspTestDefinitions.scala | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala index 054cfd7ae8..bea82603d9 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala @@ -976,6 +976,58 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) } } + + test("workspace/reload of an extra sources directory") { + val dir1 = "dir1" + val dir2 = "dir2" + val inputs = TestInputs( + Seq( + os.rel / dir1 / "ReloadTest.scala" -> + s"""object ReloadTest { + | val container = MissingCaseClass(value = "Hello") + | println(container.value) + |} + |""".stripMargin + ) + ) + val extraInputs = inputs.add( + os.rel / dir2 / "MissingCaseClass.scala" -> "case class MissingCaseClass(value: String)" + ) + extraInputs.fromRoot { root => + os.proc(TestUtil.cli, "setup-ide", dir1, extraOptions) + .call( + cwd = root, + stdout = os.Inherit + ) + withBsp(inputs, Seq(dir1), reuseRoot = Some(root)) { + (_, _, remoteServer) => + async { + val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq + + val resp = + await(remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala) + expect(resp.getStatusCode == b.StatusCode.ERROR) + + os.proc(TestUtil.cli, "setup-ide", dir1, dir2, extraOptions) + .call( + cwd = root, + stdout = os.Inherit + ) + + await(remoteServer.workspaceReload().asScala) + + val buildTargetsResp0 = await(remoteServer.workspaceBuildTargets().asScala) + val targets0 = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq + + val resp0 = + await(remoteServer.buildTargetCompile(new b.CompileParams(targets0.asJava)).asScala) + expect(resp0.getStatusCode == b.StatusCode.OK) + } + } + + } + } } object BspTestDefinitions { From cde38700a75d8d8e66622f97db8b066505a2f03b Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Wed, 20 Apr 2022 10:18:04 +0200 Subject: [PATCH 07/72] Add an integration test for BSP workspace/reload for a scenario when the reload fails --- .../cli/integration/BspTestDefinitions.scala | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala index bea82603d9..932ea032e1 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala @@ -5,10 +5,12 @@ import ch.epfl.scala.{bsp4j => b} import com.eed3si9n.expecty.Expecty.expect import com.github.plokhotnyuk.jsoniter_scala.core._ import com.github.plokhotnyuk.jsoniter_scala.macros._ +import com.google.gson.Gson +import com.google.gson.internal.LinkedTreeMap +import org.eclipse.lsp4j.jsonrpc.messages.ResponseError import java.net.URI import java.nio.file.Paths - import scala.annotation.tailrec import scala.async.Async.{async, await} import scala.concurrent.ExecutionContext.Implicits.global @@ -963,7 +965,8 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - await(remoteServer.workspaceReload().asScala) + val reloadResponse = extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) + expect(reloadResponse.isEmpty) val buildTargetsResp0 = await(remoteServer.workspaceBuildTargets().asScala) val targets0 = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq @@ -1015,7 +1018,8 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - await(remoteServer.workspaceReload().asScala) + val reloadResponse = extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) + expect(reloadResponse.isEmpty) val buildTargetsResp0 = await(remoteServer.workspaceBuildTargets().asScala) val targets0 = buildTargetsResp0.getTargets.asScala.map(_.getId).toSeq @@ -1028,6 +1032,42 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) } } + + test("workspace/reload error response when no inputs json present") { + val inputs = TestInputs( + Seq( + os.rel / "ReloadTest.scala" -> + s"""object ReloadTest { + | println("Hello") + |} + |""".stripMargin + ) + ) + withBsp(inputs, Seq(".")) { + (_, _, remoteServer) => + async { + val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq + + val resp = + await(remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala) + expect(resp.getStatusCode == b.StatusCode.OK) + + val Some(responseError) = extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) + expect(responseError.getCode == -32603) + expect(responseError.getMessage.nonEmpty) + } + } + + } + + private def extractWorkspaceReloadResponse(workspaceReloadResult: AnyRef): Option[ResponseError] = + workspaceReloadResult match { + case gsonMap: LinkedTreeMap[_, _] if !gsonMap.isEmpty => + val gson = new Gson() + Some(gson.fromJson(gson.toJson(gsonMap), classOf[ResponseError])) + case _ => None + } } object BspTestDefinitions { From 2fdc183b8ef98a87fb479a0e34918cf169003fbe Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Wed, 20 Apr 2022 10:28:28 +0200 Subject: [PATCH 08/72] NIT Refactor BspTestDefinitions - remove unnecessary brackets - apply explicit types for class fields where applicable - fix formatting --- .../cli/integration/BspTestDefinitions.scala | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala index 932ea032e1..4f1786cc48 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/BspTestDefinitions.scala @@ -11,6 +11,8 @@ import org.eclipse.lsp4j.jsonrpc.messages.ResponseError import java.net.URI import java.nio.file.Paths +import java.util.concurrent.{ExecutorService, ScheduledExecutorService} + import scala.annotation.tailrec import scala.async.Async.{async, await} import scala.concurrent.ExecutionContext.Implicits.global @@ -36,8 +38,8 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) new b.BuildClientCapabilities(List("java", "scala").asJava) ) - val pool = TestUtil.threadPool("bsp-tests-jsonrpc", 4) - val scheduler = TestUtil.scheduler("bsp-tests-scheduler") + val pool: ExecutorService = TestUtil.threadPool("bsp-tests-jsonrpc", 4) + val scheduler: ScheduledExecutorService = TestUtil.scheduler("bsp-tests-scheduler") def completeIn(duration: FiniteDuration): Future[Unit] = { val p = Promise[Unit]() @@ -194,7 +196,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) } } - val importPprintOnlyProject = TestInputs( + val importPprintOnlyProject: TestInputs = TestInputs( Seq( os.rel / "simple.sc" -> s"import $$ivy.`com.lihaoyi::pprint:${Constants.pprintVersion}`" ) @@ -279,7 +281,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) async { val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) val target = { - val targets = buildTargetsResp.getTargets().asScala.map(_.getId).toSeq + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq expect(targets.length == 2) extractMainTargets(targets) } @@ -295,9 +297,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) .buildTargetDependencySources(new b.DependencySourcesParams(targets)) .asScala } - val foundTargets = resp.getItems().asScala.map(_.getTarget.getUri).toSeq + val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq expect(foundTargets == Seq(targetUri)) - val foundDepSources = resp.getItems().asScala + val foundDepSources = resp.getItems.asScala .flatMap(_.getSources.asScala) .toSeq .map { uri => @@ -318,9 +320,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) { val resp = await(remoteServer.buildTargetSources(new b.SourcesParams(targets)).asScala) - val foundTargets = resp.getItems().asScala.map(_.getTarget.getUri).toSeq + val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq expect(foundTargets == Seq(targetUri)) - val foundSources = resp.getItems().asScala + val foundSources = resp.getItems.asScala .map(_.getSources.asScala.map(_.getUri).toSeq) .toSeq .map(_.map(TestUtil.normalizeUri)) @@ -339,12 +341,12 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) .asScala } val foundTargets = resp - .getItems() + .getItems .asScala .map(_.getTarget.getUri) .map(TestUtil.normalizeUri) expect(foundTargets == Seq(targetUri)) - val foundOptions = resp.getItems().asScala.flatMap(_.getOptions.asScala).toSeq + val foundOptions = resp.getItems.asScala.flatMap(_.getOptions.asScala).toSeq if (actualScalaVersion.startsWith("2.")) expect(foundOptions.exists { opt => opt.startsWith("-Xplugin:") && opt.contains("semanticdb-scalac") @@ -359,7 +361,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) remoteServer.buildTargetJavacOptions(new b.JavacOptionsParams(targets)).asScala } val foundTargets = resp - .getItems() + .getItems .asScala .map(_.getTarget.getUri) .map(TestUtil.normalizeUri) @@ -367,7 +369,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) } val classDir = os.Path( - Paths.get(new URI(scalacOptionsResp.getItems().asScala.head.getClassDirectory)) + Paths.get(new URI(scalacOptionsResp.getItems.asScala.head.getClassDirectory)) ) { @@ -402,7 +404,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) async { val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) val target = { - val targets = buildTargetsResp.getTargets().asScala.map(_.getId).toSeq + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq expect(targets.length == 2) extractMainTargets(targets) } @@ -469,7 +471,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) async { val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) val target = { - val targets = buildTargetsResp.getTargets().asScala.map(_.getId).toSeq + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq expect(targets.length == 2) extractMainTargets(targets) } @@ -614,7 +616,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) async { val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) val target = { - val targets = buildTargetsResp.getTargets().asScala.map(_.getId).toSeq + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq expect(targets.length == 2) extractMainTargets(targets) } @@ -635,9 +637,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) .buildTargetDependencySources(new b.DependencySourcesParams(targets)) .asScala } - val foundTargets = resp.getItems().asScala.map(_.getTarget.getUri).toSeq + val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq expect(foundTargets == Seq(targetUri)) - val foundDepSources = resp.getItems().asScala + val foundDepSources = resp.getItems.asScala .flatMap(_.getSources.asScala) .toSeq .map { uri => @@ -690,9 +692,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) .buildTargetDependencySources(new b.DependencySourcesParams(targets)) .asScala } - val foundTargets = resp.getItems().asScala.map(_.getTarget.getUri).toSeq + val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq expect(foundTargets == Seq(targetUri)) - val foundDepSources = resp.getItems().asScala + val foundDepSources = resp.getItems.asScala .flatMap(_.getSources.asScala) .toSeq .map { uri => @@ -725,7 +727,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) async { val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) val target = { - val targets = buildTargetsResp.getTargets().asScala.map(_.getId).toSeq + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq expect(targets.length == 2) extractMainTargets(targets) } @@ -746,9 +748,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) .buildTargetDependencySources(new b.DependencySourcesParams(targets)) .asScala } - val foundTargets = resp.getItems().asScala.map(_.getTarget.getUri).toSeq + val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq expect(foundTargets == Seq(targetUri)) - val foundDepSources = resp.getItems().asScala + val foundDepSources = resp.getItems.asScala .flatMap(_.getSources.asScala) .toSeq .map { uri => @@ -833,7 +835,7 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) async { val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) val target = { - val targets = buildTargetsResp.getTargets().asScala.map(_.getId).toSeq + val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq expect(targets.length == 2) extractTestTargets(targets) } @@ -854,9 +856,9 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) .buildTargetDependencySources(new b.DependencySourcesParams(targets)) .asScala } - val foundTargets = resp.getItems().asScala.map(_.getTarget.getUri).toSeq + val foundTargets = resp.getItems.asScala.map(_.getTarget.getUri).toSeq expect(foundTargets == Seq(targetUri)) - val foundDepSources = resp.getItems().asScala + val foundDepSources = resp.getItems.asScala .flatMap(_.getSources.asScala) .toSeq .map { uri => @@ -911,17 +913,17 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) // prepare build val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala) // build code - val targets = buildTargetsResp.getTargets().asScala.map(_.getId()).asJava + val targets = buildTargetsResp.getTargets.asScala.map(_.getId()).asJava await(remoteServer.buildTargetCompile(new b.CompileParams(targets)).asScala) val visibleDiagnostics = - localClient.diagnostics().takeWhile(!_.getReset()).flatMap(_.getDiagnostics().asScala) + localClient.diagnostics().takeWhile(!_.getReset).flatMap(_.getDiagnostics.asScala) expect(visibleDiagnostics.nonEmpty) visibleDiagnostics.foreach { d => expect( - d.getSeverity() == b.DiagnosticSeverity.WARNING, - d.getMessage().contains("deprecated"), + d.getSeverity == b.DiagnosticSeverity.WARNING, + d.getMessage.contains("deprecated"), d.getMessage.contains("directive") ) } @@ -965,7 +967,8 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - val reloadResponse = extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) + val reloadResponse = + extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) expect(reloadResponse.isEmpty) val buildTargetsResp0 = await(remoteServer.workspaceBuildTargets().asScala) @@ -1018,7 +1021,8 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - val reloadResponse = extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) + val reloadResponse = + extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) expect(reloadResponse.isEmpty) val buildTargetsResp0 = await(remoteServer.workspaceBuildTargets().asScala) @@ -1053,7 +1057,8 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String]) await(remoteServer.buildTargetCompile(new b.CompileParams(targets.asJava)).asScala) expect(resp.getStatusCode == b.StatusCode.OK) - val Some(responseError) = extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) + val Some(responseError) = + extractWorkspaceReloadResponse(await(remoteServer.workspaceReload().asScala)) expect(responseError.getCode == -32603) expect(responseError.getMessage.nonEmpty) } From f17c1717f7822f940572af245b365b9a76fda613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= Date: Wed, 20 Apr 2022 15:07:57 +0200 Subject: [PATCH 09/72] Rename module build to build-module to allow fix ./mill --repl --- build.sc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build.sc b/build.sc index eea480b8d4..fdadceea23 100644 --- a/build.sc +++ b/build.sc @@ -43,7 +43,7 @@ object options extends Cross[Options](Scala.defaultInternal, Scala.scala3 object scalaparse extends ScalaParse object directives extends Cross[Directives](Scala.defaultInternal, Scala.scala3) object core extends Cross[Core](Scala.defaultInternal, Scala.scala3) -object build extends Cross[Build](Scala.defaultInternal, Scala.scala3) +object `build-module` extends Cross[Build](Scala.defaultInternal, Scala.scala3) object runner extends Cross[Runner](Scala.all: _*) object `test-runner` extends Cross[TestRunner](Scala.all: _*) object `bloop-rifle` extends Cross[BloopRifle](Scala.all: _*) @@ -515,6 +515,7 @@ trait Scala3GraalProcessor extends ScalaModule { } class Build(val crossScalaVersion: String) extends BuildLikeModule { + def millSourcePath = super.millSourcePath / os.up / "build" def moduleDeps = Seq( `options`(), scalaparse, @@ -606,7 +607,7 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers super.javacOptions() ++ Seq("--release", "16") } def moduleDeps = Seq( - build(myScalaVersion), + `build-module`(myScalaVersion), `cli-options`, `test-runner`(myScalaVersion), `scala3-graal`(myScalaVersion) @@ -635,7 +636,7 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers object test extends Tests with ScalaCliScalafixModule { def moduleDeps = super.moduleDeps ++ Seq( - build(myScalaVersion).test + `build-module`(myScalaVersion).test ) } } @@ -1015,7 +1016,7 @@ def uploadLaunchers(directory: String = "artifacts") = T.command { } def unitTests() = T.command { - build(Scala.defaultInternal).test.test()() + `build-module`(Scala.defaultInternal).test.test()() cli.test.test()() } From 13c890d1a3b4bf02f6de0afdc067e267db1607c9 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Wed, 20 Apr 2022 15:20:00 +0200 Subject: [PATCH 10/72] Add a missing reflect config for ResponseError to make bsp workspace/reload return valid responses on GraalVM --- .../org.virtuslab/scala-cli-core/reflect-config.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json b/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json index 831690b696..c19cd52e1c 100644 --- a/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json +++ b/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json @@ -881,6 +881,13 @@ "allDeclaredMethods": true, "allDeclaredFields": true }, + { + "name": "org.eclipse.lsp4j.jsonrpc.messages.ResponseError", + "allDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredMethods": true, + "allDeclaredFields": true + }, { "name": "org.scalajs.jsenv.ExternalJSRun", "allDeclaredConstructors": true, From ec608831ad5c3b3960d438351bf3dc0131232944 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:49:40 +0200 Subject: [PATCH 11/72] Add negative tests dialect override to scalafmt conf --- .scalafmt.conf | 5 ++++- modules/build-macros/src/negative-tests/MismatchedLeft.scala | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index ca41cb70f8..1ec6ea0be8 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -44,7 +44,10 @@ fileOverride { "glob:**/gifs/**" { runner.dialect = scala3 } - "glob:**/scala-3.1/**" { + "glob:**/scala-3.1/**" { + runner.dialect = scala3 + } + "glob:**/negative-tests/**" { runner.dialect = scala3 } } diff --git a/modules/build-macros/src/negative-tests/MismatchedLeft.scala b/modules/build-macros/src/negative-tests/MismatchedLeft.scala index 699ba21a95..4bb43ab9cf 100644 --- a/modules/build-macros/src/negative-tests/MismatchedLeft.scala +++ b/modules/build-macros/src/negative-tests/MismatchedLeft.scala @@ -1,7 +1,7 @@ import scala.build.EitherCps.* class E -class EE1 extends E +class EE1 extends E class EE2 extends E class E2 class V From 23db0965be507b0efca8a6cd5ef7fa05af9ef856 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 20 Apr 2022 16:17:28 +0200 Subject: [PATCH 12/72] Use Scala CLI 0.1.4 in build --- project/settings.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/settings.sc b/project/settings.sc index d66ef500e5..7623e05d99 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -691,7 +691,7 @@ trait FormatNativeImageConf extends JavaModule { import mill.scalalib.api.CompilationResult trait ScalaCliCompile extends ScalaModule { - def compileScalaCliVersion = "0.1.3" + def compileScalaCliVersion = "0.1.4" def compileScalaCliUrl = { val ver = compileScalaCliVersion if (Properties.isLinux) Some( From 4e74417fe80393bd34c061e8cd0cc5e9edc41c09 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:49:54 +0200 Subject: [PATCH 13/72] Add back --strict-bloop-json-check=false in build This is allowed since the switch to Scala CLI >= 0.1.3 in the build. Setting that option to false speeds things up by avoiding parsing Bloop JSON files at each scala-cli invocation. Before 0.1.3, this was buggy: newly created files were not added to former Bloop JSON files. It's fixed since 0.1.3. --- project/settings.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/settings.sc b/project/settings.sc index 7623e05d99..eaa1e1d82b 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -772,7 +772,7 @@ trait ScalaCliCompile extends ScalaModule { asOpt("--jar", compileClasspath().map(_.path)), asOpt("-O", scalacPluginClasspath().map(p => s"-Xplugin:${p.path}")), Seq("--jvm", "zulu:17"), - "--strict-bloop-json-check=false", // don't check Bloop JSON files at each run + // "--strict-bloop-json-check=false", // don't check Bloop JSON files at each run workspace, sourceFiles ) From a475f87d5e75b897dca448c8495803616fa0c9c2 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:50:08 +0200 Subject: [PATCH 14/72] NIT Re-order dependencies --- build.sc | 4 ++-- project/deps.sc | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build.sc b/build.sc index eea480b8d4..52f9c0cdd7 100644 --- a/build.sc +++ b/build.sc @@ -620,10 +620,10 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers Deps.jimfs, // scalaJsEnvNodeJs pulls jimfs:1.1, whose class path seems borked (bin compat issue with the guava version it depends on) Deps.jniUtils, Deps.jsoniterCore, + Deps.metaconfigTypesafe, Deps.scalaPackager, Deps.signingCli, - Deps.slf4jNop, // to silence jgit - Deps.metaconfigTypesafe + Deps.slf4jNop // to silence jgit ) def compileIvyDeps = super.compileIvyDeps() ++ Agg( Deps.jsoniterMacros, diff --git a/project/deps.sc b/project/deps.sc index 37f97dbb59..08b2181c74 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -84,6 +84,7 @@ object Deps { ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${Versions.jsoniterScala}" def libdaemonjvm = ivy"io.github.alexarchambault.libdaemon::libdaemon:0.0.10" def macroParadise = ivy"org.scalamacros:::paradise:2.1.1" + def metaconfigTypesafe = ivy"com.geirsson::metaconfig-typesafe-config:0.10.0" def munit = ivy"org.scalameta::munit:0.7.29" def nativeTestRunner = ivy"org.scala-native::test-runner:${Versions.scalaNative}" def nativeTools = ivy"org.scala-native::tools:${Versions.scalaNative}" @@ -121,11 +122,10 @@ object Deps { def snailgun(force213: Boolean = false) = if (force213) ivy"me.vican.jorge:snailgun-core_2.13:0.4.0" else ivy"me.vican.jorge::snailgun-core:0.4.0" - def svm = ivy"org.graalvm.nativeimage:svm:$graalVmVersion" - def swoval = ivy"com.swoval:file-tree-views:2.1.9" - def testInterface = ivy"org.scala-sbt:test-interface:1.0" - def usingDirectives = ivy"org.virtuslab:using_directives:0.0.8" - val metaconfigTypesafe = ivy"com.geirsson::metaconfig-typesafe-config:0.10.0" + def svm = ivy"org.graalvm.nativeimage:svm:$graalVmVersion" + def swoval = ivy"com.swoval:file-tree-views:2.1.9" + def testInterface = ivy"org.scala-sbt:test-interface:1.0" + def usingDirectives = ivy"org.virtuslab:using_directives:0.0.8" } def graalVmVersion = "22.0.0" From 26e7db80caa8116d017ec94aca41069a93206cd7 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:50:22 +0200 Subject: [PATCH 15/72] Add s01.oss.sonatype.org snapshot repo in build Sometimes, some snapshot dependencies land there rather than on sonatype:snapshots. --- project/deps.sc | 3 ++- project/settings.sc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/project/deps.sc b/project/deps.sc index 08b2181c74..6835645cb3 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -150,7 +150,8 @@ object Docker { def customRepositories = Seq( - coursier.Repositories.sonatype("snapshots") + coursier.Repositories.sonatype("snapshots"), + coursier.MavenRepository("https://s01.oss.sonatype.org/content/repositories/snapshots") // Uncomment for local development // coursier.LocalRepositories.Dangerous.maven2Local ) diff --git a/project/settings.sc b/project/settings.sc index eaa1e1d82b..4b370aa7e4 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -442,7 +442,7 @@ trait HasTests extends SbtModule { def forkArgs = super.forkArgs() ++ Seq("-Xmx512m", "-Xms128m") def repositoriesTask = - T.task(super.repositoriesTask() :+ coursier.Repositories.sonatype("snapshots")) + T.task(super.repositoriesTask() ++ deps.customRepositories) } } From 4867a670bf9101f3512364c1e8dfbf6448892f23 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:50:36 +0200 Subject: [PATCH 16/72] NIT Trailing space / minor reformatting / unused import --- .../scala/build/postprocessing/SemanticDbPostProcessor.scala | 1 + modules/build/src/test/scala/scala/build/tests/BuildTests.scala | 1 - .../src/main/scala/scala/cli/commands/BloopExitOptions.scala | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/postprocessing/SemanticDbPostProcessor.scala b/modules/build/src/main/scala/scala/build/postprocessing/SemanticDbPostProcessor.scala index 0ed869e9dd..d48d1bcef8 100644 --- a/modules/build/src/main/scala/scala/build/postprocessing/SemanticDbPostProcessor.scala +++ b/modules/build/src/main/scala/scala/build/postprocessing/SemanticDbPostProcessor.scala @@ -5,6 +5,7 @@ import java.nio.file.FileSystemException import scala.annotation.tailrec import scala.build.{GeneratedSource, Logger} import scala.util.{Either, Right} + case object SemanticDbPostProcessor extends PostProcessor { def postProcess( generatedSources: Seq[GeneratedSource], diff --git a/modules/build/src/test/scala/scala/build/tests/BuildTests.scala b/modules/build/src/test/scala/scala/build/tests/BuildTests.scala index aadf028b6e..3893d632ff 100644 --- a/modules/build/src/test/scala/scala/build/tests/BuildTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/BuildTests.scala @@ -6,7 +6,6 @@ import dependency.parser.DependencyParser import java.io.IOException import scala.build.Ops._ -import scala.build.Positioned import scala.build.errors.{ DependencyFormatError, InvalidBinaryScalaVersionError, diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/BloopExitOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/BloopExitOptions.scala index a6aa1c00ee..c12f56d26f 100644 --- a/modules/cli-options/src/main/scala/scala/cli/commands/BloopExitOptions.scala +++ b/modules/cli-options/src/main/scala/scala/cli/commands/BloopExitOptions.scala @@ -13,7 +13,7 @@ final case class BloopExitOptions( directories: SharedDirectoriesOptions = SharedDirectoriesOptions(), @Recurse coursier: CoursierOptions = CoursierOptions() -) +) // format: on object BloopExitOptions { From f08c633ddd4d6f7375d11744c0f5b741c0ca6e0b Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:50:49 +0200 Subject: [PATCH 17/72] Clean-up cs launcher fetching in build Some coursier <= 2.0.16 logic (where the coursier launchers weren't zipped) isn't necessary anymore. --- project/settings.sc | 91 +++++++++++---------------------------------- 1 file changed, 22 insertions(+), 69 deletions(-) diff --git a/project/settings.sc b/project/settings.sc index 4b370aa7e4..c5d0ac4ba1 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -5,10 +5,9 @@ import $file.scalafixthings, scalafixthings.ScalafixModule import de.tobiasroeser.mill.vcs.version.VcsVersion import io.github.alexarchambault.millnativeimage.NativeImage -import java.io.{ByteArrayOutputStream, File, FileInputStream, InputStream} +import java.io.{ByteArrayOutputStream, File, InputStream} import java.nio.charset.StandardCharsets import java.util.Locale -import java.util.zip.{GZIPInputStream, ZipFile} import mill._, scalalib._ import scala.collection.JavaConverters._ import scala.util.Properties @@ -16,39 +15,6 @@ import upickle.default._ private def isCI = System.getenv("CI") != null -private def withGzipContent[T](gzFile: File)(f: InputStream => T): T = { - var fis: FileInputStream = null - var gzis: GZIPInputStream = null - try { - fis = new FileInputStream(gzFile) - gzis = new GZIPInputStream(fis) - f(gzis) - } - finally { - if (gzis != null) gzis.close() - if (fis != null) fis.close() - } -} - -private def withFirstFileInZip[T](zip: File)(f: InputStream => T): T = { - var zf: ZipFile = null - var is: InputStream = null - try { - zf = new ZipFile(zip) - val ent = zf.entries().asScala.find(e => !e.isDirectory).getOrElse { - throw new NoSuchElementException(s"No file found in $zip") - } - is = zf.getInputStream(ent) - f(is) - } - finally { - if (zf != null) - zf.close() - if (is != null) - is.close() - } -} - def fromPath(name: String): String = if (Properties.isWin) { val pathExt = Option(System.getenv("PATHEXT")) @@ -88,60 +54,47 @@ def cs: T[String] = T.persistent { val arch = sys.props.getOrElse("os.arch", "").toLowerCase(Locale.ROOT) val urlOpt = arch match { case "x86_64" | "amd64" => - if (Properties.isWin) Some( - if (buildCsVersion == "2.0.16") - "https://github.com/coursier/coursier/releases/download/v2.0.13/cs-x86_64-pc-win32.exe" - else + if (Properties.isWin) + Some( s"https://github.com/coursier/coursier/releases/download/v$buildCsVersion/cs-x86_64-pc-win32.zip" - ) - else if (Properties.isMac) Some( - if (buildCsVersion == "2.0.16") - "https://github.com/coursier/coursier/releases/download/v2.0.16/cs-x86_64-apple-darwin" - else + ) + else if (Properties.isMac) + Some( s"https://github.com/coursier/coursier/releases/download/v$buildCsVersion/cs-x86_64-apple-darwin.gz" - ) - else if (Properties.isLinux) Some( - if (buildCsVersion == "2.0.16") - "https://github.com/coursier/coursier/releases/download/v2.0.16/cs-x86_64-pc-linux" - else + ) + else if (Properties.isLinux) + Some( s"https://github.com/coursier/coursier/releases/download/v$buildCsVersion/cs-x86_64-pc-linux.gz" - ) + ) else None case "aarch64" => - if (Properties.isLinux) Some( - if (buildCsVersion == "2.0.16") - "https://github.com/coursier/coursier/releases/download/v2.0.16/cs-aarch64-pc-linux" - else + if (Properties.isLinux) + Some( s"https://github.com/coursier/coursier/releases/download/v$buildCsVersion/cs-aarch64-pc-linux.gz" - ) + ) else None case _ => None } urlOpt.map { url => - val cache = coursier.cache.FileCache() - val task = cache.logger.using(cache.file(coursier.util.Artifact(url)).run) + val cache = coursier.cache.FileCache() + val archiveCache = coursier.cache.ArchiveCache().withCache(cache) + val task = cache.logger.using(archiveCache.get(coursier.util.Artifact(url))) val maybeFile = try task.unsafeRun()(cache.ec) catch { case t: Throwable => throw new Exception(t) } - val f = maybeFile.fold(ex => throw new Exception(ex), identity) + val f = maybeFile.fold(ex => throw new Exception(ex), os.Path(_, os.pwd)) val exec = - if (f.getName.endsWith(".gz")) { - val b = withGzipContent(f)(_.readAllBytes()) - os.write(dest, b) - dest - } - else if (f.getName.endsWith(".zip")) { - val b = withFirstFileInZip(f)(_.readAllBytes()) - os.write(dest, b) - dest - } + if (Properties.isWin && os.isDir(f) && f.last.endsWith(".zip")) + os.list(f).find(_.last.endsWith(".exe")).getOrElse( + sys.error(s"No .exe found under $f") + ) else - os.Path(f, os.pwd) + f if (!Properties.isWin) exec.toIO.setExecutable(true) From b3a1dd52d7ac5478ab177b83387307ffa5d77644 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:51:03 +0200 Subject: [PATCH 18/72] Fix multiple word command name in help message commandLength is used in ScalaCommand#error. When passing a wrong option to a multiple-word command, the printed message only printed the first word of the command, like ``` scala-cli bloop start --nope Unrecognized argument: --nope To list all available options, run scala-cli bloop --help ``` This commit fixes that. --- .../cli/src/main/scala/scala/cli/commands/ScalaCommand.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala b/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala index a2763e490a..1e425e5230 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala @@ -26,7 +26,7 @@ abstract class ScalaCommand[T](implicit parser: Parser[T], help: Help[T]) } // TODO Manage to have case-app give use the exact command name that was used instead - protected def commandLength = 1 + protected def commandLength = names.headOption.fold(1)(_.length) override def error(message: Error): Nothing = { System.err.println(message.message) From d2e597ecfd4da1fed1b5808537744677b669e4bb Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:51:16 +0200 Subject: [PATCH 19/72] Handle more parameters in dependency strings Used in subsequent developments --- .../core/src/main/scala/scala/build/internals/Util.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/scala/scala/build/internals/Util.scala b/modules/core/src/main/scala/scala/build/internals/Util.scala index 46d3300f64..2574ab044e 100644 --- a/modules/core/src/main/scala/scala/build/internals/Util.scala +++ b/modules/core/src/main/scala/scala/build/internals/Util.scala @@ -27,7 +27,7 @@ object Util { } } - private implicit class DependencyOps(private val dep: dependency.Dependency) extends AnyVal { + implicit class DependencyOps(private val dep: dependency.Dependency) extends AnyVal { def toCs: coursier.Dependency = { val mod = coursier.Module( coursier.Organization(dep.organization), @@ -42,7 +42,11 @@ object Util { } } for (clOpt <- dep.userParams.get("classifier"); cl <- clOpt) - dep0 = dep0.withConfiguration(coursier.core.Configuration(cl)) + dep0 = dep0.withPublication(dep0.publication.withClassifier(coursier.core.Classifier(cl))) + for (tpeOpt <- dep.userParams.get("type"); tpe <- tpeOpt) + dep0 = dep0.withPublication(dep0.publication.withType(coursier.core.Type(tpe))) + for (extOpt <- dep.userParams.get("ext"); ext <- extOpt) + dep0 = dep0.withPublication(dep0.publication.withExt(coursier.core.Extension(ext))) for (_ <- dep.userParams.get("intransitive")) dep0 = dep0.withTransitive(false) // FIXME From 868c8b092f6174b79938b0a588d9545bf051cb4c Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:51:42 +0200 Subject: [PATCH 20/72] Allow to disable coursier checksums Not sure, but these might slow things down sometimes (even though they've been optimized a bit in coursier some time ago). --- .../main/scala/scala/cli/commands/CoursierOptions.scala | 6 +++++- .../src/main/scala/scala/cli/commands/util/CommonOps.scala | 7 ++++++- website/docs/reference/cli-options.md | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/CoursierOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/CoursierOptions.scala index 1a3233e55e..17691b20b6 100644 --- a/modules/cli-options/src/main/scala/scala/cli/commands/CoursierOptions.scala +++ b/modules/cli-options/src/main/scala/scala/cli/commands/CoursierOptions.scala @@ -15,7 +15,11 @@ final case class CoursierOptions( @HelpMessage("Set the coursier cache location") @ValueDescription("path") @Hidden - cache: Option[String] = None + cache: Option[String] = None, + @Group("Dependency") + @HelpMessage("Enable checksum validation of artifacts downloaded by coursier") + @Hidden + coursierValidateChecksums: Option[Boolean] = None ) // format: on diff --git a/modules/cli/src/main/scala/scala/cli/commands/util/CommonOps.scala b/modules/cli/src/main/scala/scala/cli/commands/util/CommonOps.scala index 09ec6b777f..586318a5f9 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/util/CommonOps.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/util/CommonOps.scala @@ -35,9 +35,14 @@ object CommonOps { implicit class CoursierOptionsOps(v: CoursierOptions) { import v._ + private def validateChecksums = + coursierValidateChecksums.getOrElse(true) + def coursierCache(logger: CacheLogger) = { var baseCache = FileCache().withLogger(logger) - val ttlOpt = ttl.map(_.trim).filter(_.nonEmpty).map(Duration(_)) + if (!validateChecksums) + baseCache = baseCache.withChecksums(Nil) + val ttlOpt = ttl.map(_.trim).filter(_.nonEmpty).map(Duration(_)) for (ttl0 <- ttlOpt) baseCache = baseCache.withTtl(ttl0) for (loc <- cache.filter(_.trim.nonEmpty)) diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 5a34d882c1..5a482092dd 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -232,6 +232,10 @@ Specify a TTL for changing dependencies, such as snapshots Set the coursier cache location +#### `--coursier-validate-checksums` + +Enable checksum validation of artifacts downloaded by coursier + ## Cross options Available in commands: From e9ba524d7ba30b347a9155eab16084cdc3fbe865 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:51:55 +0200 Subject: [PATCH 21/72] Ignore null getPeeledObjectId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not sure when this happens, but I saw it on scalacheck-shapeless… --- .../scala/scala/build/options/publish/ComputeVersion.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala b/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala index 73d0c626e2..98f936585b 100644 --- a/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala +++ b/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala @@ -55,7 +55,7 @@ object ComputeVersion { .call() .asScala .iterator - .map(tag => (tag.getPeeledObjectId.name, tag)) + .flatMap(tag => Option(tag.getPeeledObjectId).iterator.map(id => (id.name, tag))) .toMap val tagsIt = git.log() .call() @@ -88,7 +88,8 @@ object ComputeVersion { (lastTagOpt, lastStableTagOpt) match { case (None, _) => Right(defaultFirstVersion) - case (Some((tag, name)), _) if tag.getPeeledObjectId.name == headCommit.name => + case (Some((tag, name)), _) + if Option(tag.getPeeledObjectId).exists(_.name == headCommit.name) => Right(name) case (Some((tag, _)), _) if dynVer => val tagOrNull = git.describe() From c37b246f00d1b4dedec0fb93d9d20a05a609e69a Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 15 Apr 2022 17:52:09 +0200 Subject: [PATCH 22/72] Add missing using directive alias --- .../preprocessing/directives/UsingPublishDirectiveHandler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala index ef975373a0..be33de1c9c 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala @@ -70,7 +70,7 @@ case object UsingPublishDirectiveHandler extends UsingDirectiveHandler { else scopedDirective.directive.key.stripPrefix(prefix) match { case "organization" => Right(PublishOptions(organization = Some(singleValue))) - case "name" => + case "name" | "moduleName" => Right(PublishOptions(name = Some(singleValue))) case "version" => Right(PublishOptions(version = Some(singleValue))) From e6ce3bf5d1e01913c6313608da3fb94a5df02946 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 19 Apr 2022 12:06:23 +0200 Subject: [PATCH 23/72] Ensure "git:tag" compute version works fine on empty repo --- .../options/publish/ComputeVersionTests.scala | 17 +++ .../scala-cli-core/reflect-config.json | 12 ++ .../options/publish/ComputeVersion.scala | 137 +++++++++--------- 3 files changed, 101 insertions(+), 65 deletions(-) diff --git a/modules/build/src/test/scala/scala/build/options/publish/ComputeVersionTests.scala b/modules/build/src/test/scala/scala/build/options/publish/ComputeVersionTests.scala index 0c7fb779ab..7aee3c8780 100644 --- a/modules/build/src/test/scala/scala/build/options/publish/ComputeVersionTests.scala +++ b/modules/build/src/test/scala/scala/build/options/publish/ComputeVersionTests.scala @@ -1,6 +1,8 @@ package scala.build.options.publish import com.eed3si9n.expecty.Expecty.expect +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.Constants import scala.build.tests.{TestInputs, TestUtil} @@ -46,4 +48,19 @@ class ComputeVersionTests extends munit.FunSuite { } } + test("git tag on empty repo") { + TestInputs().fromRoot { root => + val git = Git.init().setDirectory(root.toIO).call() + val hasHead = git.getRepository.resolve(Constants.HEAD) != null + expect(!hasHead) + + val defaultVersion = "0.0.2-SNAPSHOT" + val cv = ComputeVersion.GitTag(os.rel, true, defaultVersion) + + val version = cv.get(root) + .fold(ex => throw new Exception(ex), identity) + expect(version == defaultVersion) + } + } + } diff --git a/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json b/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json index 831690b696..c08a6ce69f 100644 --- a/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json +++ b/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/reflect-config.json @@ -874,6 +874,18 @@ } ] }, + { + "name": "org.eclipse.jgit.internal.JGitText", + "allPublicFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [ + + ] + } + ] + }, { "name": "org.eclipse.lsp4j.jsonrpc.json.adapters.JsonElementTypeAdapter$Factory", "allDeclaredConstructors": true, diff --git a/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala b/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala index 98f936585b..cde1a2bfa1 100644 --- a/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala +++ b/modules/options/src/main/scala/scala/build/options/publish/ComputeVersion.scala @@ -49,77 +49,84 @@ object ComputeVersion { def get(workspace: os.Path): Either[BuildException, String] = { val repo0 = repo.resolveFrom(workspace) if (os.exists(repo0 / ".git")) { - val git = Git.open(repo0.toIO) - val (lastTagOpt, lastStableTagOpt) = { - val tagMap = git.tagList() - .call() - .asScala - .iterator - .flatMap(tag => Option(tag.getPeeledObjectId).iterator.map(id => (id.name, tag))) - .toMap - val tagsIt = git.log() - .call() - .asScala - .iterator - .flatMap(c => tagMap.get(c.name()).iterator) - .flatMap(r => versionOf(r.getName).map((r, _)).iterator) - .scanLeft((Option.empty[(Ref, String)], Option.empty[(Ref, String)])) { - case ((acc, stableAcc), v @ (_, name)) => - val acc0 = acc.orElse(Some(v)) - val stableAcc0 = stableAcc.orElse { - if (name.forall(c => c == '.' || c.isDigit)) Some(v) - else None - } - (acc0, stableAcc0) + val git = Git.open(repo0.toIO) + val hasHead = git.getRepository.resolve(Constants.HEAD) != null + if (hasHead) { + val (lastTagOpt, lastStableTagOpt) = { + val tagMap = git.tagList() + .call() + .asScala + .iterator + .flatMap(tag => Option(tag.getPeeledObjectId).iterator.map(id => (id.name, tag))) + .toMap + val tagsIt = git.log() + .call() + .asScala + .iterator + .flatMap(c => tagMap.get(c.name()).iterator) + .flatMap(r => versionOf(r.getName).map((r, _)).iterator) + .scanLeft((Option.empty[(Ref, String)], Option.empty[(Ref, String)])) { + case ((acc, stableAcc), v @ (_, name)) => + val acc0 = acc.orElse(Some(v)) + val stableAcc0 = stableAcc.orElse { + if (name.forall(c => c == '.' || c.isDigit)) Some(v) + else None + } + (acc0, stableAcc0) + } + var lastTagOpt0 = Option.empty[(Ref, String)] + var lastStableTagOpt0 = Option.empty[(Ref, String)] + while (tagsIt.hasNext && (lastTagOpt0.isEmpty || lastStableTagOpt0.isEmpty)) { + val v = tagsIt.next() + if (lastTagOpt0.isEmpty) + lastTagOpt0 = v._1 + if (lastStableTagOpt0.isEmpty) + lastStableTagOpt0 = v._2 } - var lastTagOpt0 = Option.empty[(Ref, String)] - var lastStableTagOpt0 = Option.empty[(Ref, String)] - while (tagsIt.hasNext && (lastTagOpt0.isEmpty || lastStableTagOpt0.isEmpty)) { - val v = tagsIt.next() - if (lastTagOpt0.isEmpty) - lastTagOpt0 = v._1 - if (lastStableTagOpt0.isEmpty) - lastStableTagOpt0 = v._2 + (lastTagOpt0, lastStableTagOpt0) } - (lastTagOpt0, lastStableTagOpt0) - } - val headCommit = git.log().call().asScala.iterator.next() + val headCommit = git.log().call().asScala.iterator.next() - (lastTagOpt, lastStableTagOpt) match { - case (None, _) => - Right(defaultFirstVersion) - case (Some((tag, name)), _) - if Option(tag.getPeeledObjectId).exists(_.name == headCommit.name) => - Right(name) - case (Some((tag, _)), _) if dynVer => - val tagOrNull = git.describe() - .setMatch("v[0-9]*", "[0-9]*") - .setTags(true) - .setTarget(headCommit) - .call() - Option(tagOrNull) match { - case None => + (lastTagOpt, lastStableTagOpt) match { + case (None, _) => + Right(defaultFirstVersion) + case (Some((tag, name)), _) + if Option(tag.getPeeledObjectId).exists(_.name == headCommit.name) => + Right(name) + case (Some((tag, _)), _) if dynVer => + val tagOrNull = git.describe() + .setMatch("v[0-9]*", "[0-9]*") + .setTags(true) + .setTarget(headCommit) + .call() + Option(tagOrNull) match { + case None => + Left(new GitTagError( + s"Unexpected error when running git describe from Git repository $repo0 (git describe doesn't find back tag $tag)" + )) + case Some(tag) => + versionOf(tag).map(_ + "-SNAPSHOT").toRight( + new GitTagError( + s"Unexpected error when running git describe from Git repository $repo0 (git describe-provided tag $tag doesn't have the expected shape)" + ) + ) + } + case (Some(_), None) => + Left(new GitTagError(s"No stable tag found in Git repository $repo0")) + case (_, Some((tag, name))) => + val idx = name.lastIndexOf('.') + if ( + idx >= 0 && idx < name.length - 1 && name.iterator.drop(idx + 1).forall(_.isDigit) + ) + Right(name.take(idx + 1) + (name.drop(idx + 1).toInt + 1).toString + "-SNAPSHOT") + else Left(new GitTagError( - s"Unexpected error when running git describe from Git repository $repo0 (git describe doesn't find back tag $tag)" + s"Don't know how to bump version in tag $tag in Git repository $repo0" )) - case Some(tag) => - versionOf(tag).map(_ + "-SNAPSHOT").toRight( - new GitTagError( - s"Unexpected error when running git describe from Git repository $repo0 (git describe-provided tag $tag doesn't have the expected shape)" - ) - ) - } - case (Some(_), None) => - Left(new GitTagError(s"No stable tag found in Git repository $repo0")) - case (_, Some((tag, name))) => - val idx = name.lastIndexOf('.') - if (idx >= 0 && idx < name.length - 1 && name.iterator.drop(idx + 1).forall(_.isDigit)) - Right(name.take(idx + 1) + (name.drop(idx + 1).toInt + 1).toString + "-SNAPSHOT") - else - Left(new GitTagError( - s"Don't know how to bump version in tag $tag in Git repository $repo0" - )) + } } + else + Right(defaultFirstVersion) } else Left(new GitTagError(s"$repo0 doesn't look like a Git repository")) From 9b204588901684bc867e46efd94eeb365b4dfa92 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 19 Apr 2022 12:06:41 +0200 Subject: [PATCH 24/72] NIT Fix GitHub organization case in build --- project/publish.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/publish.sc b/project/publish.sc index 90588c4e9f..0a61aeec38 100644 --- a/project/publish.sc +++ b/project/publish.sc @@ -8,7 +8,7 @@ import java.nio.charset.Charset import scala.concurrent.duration._ -def ghOrg = "Virtuslab" +def ghOrg = "VirtusLab" def ghName = "scala-cli" private def computePublishVersion(state: VcsState, simple: Boolean): String = From cf42ea541e8599d3455f9bd48c641bfc85190056 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 19 Apr 2022 14:26:23 +0200 Subject: [PATCH 25/72] NIT refacto in Publish --- .../main/scala/scala/cli/commands/Publish.scala | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/Publish.scala b/modules/cli/src/main/scala/scala/cli/commands/Publish.scala index 5ad0e1f4fc..130d48048e 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Publish.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Publish.scala @@ -449,18 +449,21 @@ object Publish extends ScalaCommand[PublishOptions] { val finalFileSet = fileSet2.order(ec).unsafeRun()(ec) - val repoUrl = builds.head.options.notForBloopOptions.publishOptions.repository match { + val repo = builds.head.options.notForBloopOptions.publishOptions.repository match { case None => value(Left(new MissingRepositoryError)) case Some(repo) => - if (repo.contains("://")) repo - else os.Path(repo, Os.pwd).toNIO.toUri.toASCIIString + val url = + if (repo.contains("://")) repo + else os.Path(repo, Os.pwd).toNIO.toUri.toASCIIString + MavenRepository(url) } - val repo = MavenRepository(repoUrl) val upload = - if (repo.root.startsWith("http")) HttpURLConnectionUpload.create() - else FileUpload(Paths.get(new URI(repo.root))) + if (repo.root.startsWith("http://") || repo.root.startsWith("https://")) + HttpURLConnectionUpload.create() + else + FileUpload(Paths.get(new URI(repo.root))) val dummy = false val isLocal = true From dc8272c65781474761ac5cc736e6a1f94bd6c27e Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 19 Apr 2022 14:54:10 +0200 Subject: [PATCH 26/72] Address compilation warnings --- .../scala/build/directives/ScopedValue.scala | 20 +------ .../UsingDirectiveExpectationError.scala | 8 +-- .../directives/DirectiveUtil.scala | 28 +++++----- .../RequireScalaVersionDirectiveHandler.scala | 54 ++++++++++--------- ...ngScalaNativeOptionsDirectiveHandler.scala | 1 - 5 files changed, 45 insertions(+), 66 deletions(-) diff --git a/modules/directives/src/main/scala/scala/build/directives/ScopedValue.scala b/modules/directives/src/main/scala/scala/build/directives/ScopedValue.scala index 00b15e6693..46fd7e63f6 100644 --- a/modules/directives/src/main/scala/scala/build/directives/ScopedValue.scala +++ b/modules/directives/src/main/scala/scala/build/directives/ScopedValue.scala @@ -1,12 +1,6 @@ package scala.build.preprocessing.directives -import com.virtuslab.using_directives.custom.model.{ - BooleanValue, - EmptyValue, - NumericValue, - StringValue, - Value -} +import com.virtuslab.using_directives.custom.model.Value import scala.build.Positioned import scala.build.preprocessing.ScopePath @@ -14,14 +8,4 @@ import scala.build.preprocessing.ScopePath case class ScopedValue[T <: Value[_]]( positioned: Positioned[String], maybeScopePath: Option[ScopePath] = None -) { - - def kind(scopedValue: ScopedValue[_]) = scopedValue match { - case _: ScopedValue[StringValue] => UsingDirectiveValueKind.STRING - case _: ScopedValue[NumericValue] => UsingDirectiveValueKind.NUMERIC - case _: ScopedValue[BooleanValue] => UsingDirectiveValueKind.BOOLEAN - case _: ScopedValue[EmptyValue] => UsingDirectiveValueKind.EMPTY - case _ => UsingDirectiveValueKind.UNKNOWN - } - -} +) diff --git a/modules/directives/src/main/scala/scala/build/errors/UsingDirectiveExpectationError.scala b/modules/directives/src/main/scala/scala/build/errors/UsingDirectiveExpectationError.scala index b3691cca2b..1bf03395c0 100644 --- a/modules/directives/src/main/scala/scala/build/errors/UsingDirectiveExpectationError.scala +++ b/modules/directives/src/main/scala/scala/build/errors/UsingDirectiveExpectationError.scala @@ -7,10 +7,8 @@ import scala.build.preprocessing.directives.{ } sealed abstract class UsingDirectiveExpectationError( - maybePath: Either[String, os.Path], - key: String, message: String -) extends BuildException(message = message) +) extends BuildException(message) final class UsingDirectiveWrongValueTypeError( maybePath: Either[String, os.Path], @@ -19,8 +17,6 @@ final class UsingDirectiveWrongValueTypeError( providedPositionedTypesContainer: GroupedScopedValuesContainer, hint: String = "" ) extends UsingDirectiveExpectationError( - maybePath, - key, s"""${expectedTypes.mkString( ", or " )} expected for the $key using directive key${maybePath.map(path => s" at $path").getOrElse( @@ -35,8 +31,6 @@ final class UsingDirectiveValueNumError( usingDirectiveValueNumberBounds: UsingDirectiveValueNumberBounds, providedValueNum: Int ) extends UsingDirectiveExpectationError( - maybePath, - key, s"expected $usingDirectiveValueNumberBounds for the $key using directive key${maybePath.map(path => s" at $path").getOrElse( "" )}; but got $providedValueNum values, instead." diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveUtil.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveUtil.scala index 4491c8447a..f1d793c1ce 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveUtil.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveUtil.scala @@ -70,21 +70,19 @@ object DirectiveUtil { */ def partitionBasedOnHavingScope( groupedPositionedValuesContainer: GroupedScopedValuesContainer - ): (Seq[ScopedValue[_]], Seq[ScopedValue[_]]) = { - val (nonScopedStrings, scopedStrings) = - groupedPositionedValuesContainer.scopedStringValues.partition(_.maybeScopePath.isEmpty) - val (nonScopedNumerics, scopedNumerics) = - groupedPositionedValuesContainer.scopedNumericValues.partition(_.maybeScopePath.isEmpty) - val (nonScopedBooleans, scopedBoleans) = - groupedPositionedValuesContainer.scopedBooleanValues.partition(_.maybeScopePath.isEmpty) - val (nonScopedEmpty, scopedEmpty) = - groupedPositionedValuesContainer.maybeScopedEmptyValue.to(Seq).partition( - _.maybeScopePath.isEmpty - ) - ( - scopedStrings ++ scopedNumerics ++ scopedBoleans ++ scopedEmpty, - nonScopedStrings ++ nonScopedNumerics ++ nonScopedBooleans ++ nonScopedEmpty - ) + ): (Seq[(ScopedValue[_], ScopePath)], Seq[ScopedValue[_]]) = { + + val values = groupedPositionedValuesContainer.scopedStringValues ++ + groupedPositionedValuesContainer.scopedNumericValues ++ + groupedPositionedValuesContainer.scopedBooleanValues ++ + groupedPositionedValuesContainer.maybeScopedEmptyValue.toSeq + + val scoped = values.flatMap { value => + value.maybeScopePath.toSeq.map((value, _)) + } + val nonScoped = values.filter(_.maybeScopePath.isEmpty) + + (scoped, nonScoped) } def concatAllValues(groupedPositionedValuesContainer: GroupedScopedValuesContainer) diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersionDirectiveHandler.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersionDirectiveHandler.scala index c9668774ee..25eff93326 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersionDirectiveHandler.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersionDirectiveHandler.scala @@ -1,4 +1,6 @@ package scala.build.preprocessing.directives + +import scala.build.EitherCps.{either, value} import scala.build.Logger import scala.build.Ops._ import scala.build.errors.{BuildException, CompositeBuildException, DirectiveErrors} @@ -66,35 +68,37 @@ case object RequireScalaVersionDirectiveHandler extends RequireDirectiveHandler def handleValues( scopedDirective: ScopedDirective, logger: Logger - ): Either[BuildException, ProcessedRequireDirective] = - checkIfValuesAreExpected(scopedDirective).flatMap { - groupedPositionedValuesContainer => + ): Either[BuildException, ProcessedRequireDirective] = either { + val groupedPositionedValuesContainer = value(checkIfValuesAreExpected(scopedDirective)) - val (scopedValues, nonScopedValues) = - DirectiveUtil.partitionBasedOnHavingScope(groupedPositionedValuesContainer) + val (scopedValues, nonScopedValues) = + DirectiveUtil.partitionBasedOnHavingScope(groupedPositionedValuesContainer) - val nonScopedBuildRequirements = nonScopedValues.headOption match { - case None => Right(None) - case Some(ScopedValue(positioned, None)) => - handleVersion(scopedDirective.directive.key, positioned.value) - } + val nonScopedBuildRequirements = nonScopedValues.headOption match { + case None => Right(None) + case Some(value) => + handleVersion(scopedDirective.directive.key, value.positioned.value) + } - val scopedBuildRequirements = scopedValues.map { - case ScopedValue(positioned, Some(scopePath)) => handleVersion( - scopedDirective.directive.key, - positioned.value - ).map(_.map(buildRequirements => Scoped(scopePath, buildRequirements))) - } - .sequence - .left.map(CompositeBuildException(_)) - .map(_.flatten) + val scopedBuildRequirements = scopedValues + .map { + case (value, scopePath) => + val maybeReqs = handleVersion( + scopedDirective.directive.key, + value.positioned.value + ) + maybeReqs.map(_.map(buildRequirements => Scoped(scopePath, buildRequirements))) + } + .sequence + .left.map(CompositeBuildException(_)) + .map(_.flatten) - (nonScopedBuildRequirements, scopedBuildRequirements) - .traverseN - .left.map(CompositeBuildException(_)) - .map { - case (ns, s) => ProcessedDirective(ns, s) - } + val (ns, s) = value { + (nonScopedBuildRequirements, scopedBuildRequirements) + .traverseN + .left.map(CompositeBuildException(_)) } + ProcessedDirective(ns, s) + } } diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaNativeOptionsDirectiveHandler.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaNativeOptionsDirectiveHandler.scala index 8a015107fb..9f3b07c427 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaNativeOptionsDirectiveHandler.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaNativeOptionsDirectiveHandler.scala @@ -54,7 +54,6 @@ case object UsingScalaNativeOptionsDirectiveHandler extends UsingDirectiveHandle case "native-mode" | "nativeMode" => UsingDirectiveValueNumberBounds(1, 1) case "native-clang" | "native-clang-pp" | "nativeClang" | "nativeClangPP" => UsingDirectiveValueNumberBounds(1, 1) - case "native-clang-pp" | "nativeMode" => UsingDirectiveValueNumberBounds(1, 1) case "native-version" | "nativeVersion" => UsingDirectiveValueNumberBounds(1, 1) case "native-linking" | "nativeLinking" => UsingDirectiveValueNumberBounds(1, Int.MaxValue) case "native-compile" | "nativeCompile" => UsingDirectiveValueNumberBounds(1, Int.MaxValue) From 374ecf6a4808e5c52b7e34b3f317abb0b4e58b96 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 20 Apr 2022 16:11:30 +0200 Subject: [PATCH 27/72] Small refacto in UsingPublishDirectiveHandler --- .../UsingPublishDirectiveHandler.scala | 100 ++++++++++-------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala index be33de1c9c..b937e780a4 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala @@ -1,5 +1,6 @@ package scala.build.preprocessing.directives +import scala.build.EitherCps.{either, value} import scala.build.Logger import scala.build.errors.{BuildException, UnexpectedDirectiveError} import scala.build.options.publish.{ComputeVersion, Developer, License, Vcs} @@ -60,21 +61,28 @@ case object UsingPublishDirectiveHandler extends UsingDirectiveHandler { def handleValues( scopedDirective: ScopedDirective, logger: Logger - ): Either[BuildException, ProcessedUsingDirective] = - checkIfValuesAreExpected(scopedDirective).flatMap { groupedScopedValuesContainer => - val severalValues = groupedScopedValuesContainer.scopedStringValues.map(_.positioned) - val singleValue = severalValues.head + ): Either[BuildException, ProcessedUsingDirective] = either { - if (!scopedDirective.directive.key.startsWith(prefix)) - Left(new UnexpectedDirectiveError(scopedDirective.directive.key)) - else scopedDirective.directive.key.stripPrefix(prefix) match { - case "organization" => - Right(PublishOptions(organization = Some(singleValue))) - case "name" | "moduleName" => - Right(PublishOptions(name = Some(singleValue))) - case "version" => - Right(PublishOptions(version = Some(singleValue))) - case "computeVersion" | "compute-version" => + val groupedScopedValuesContainer = value(checkIfValuesAreExpected(scopedDirective)) + + val severalValues = groupedScopedValuesContainer.scopedStringValues.map(_.positioned) + val singleValue = severalValues.head + + val strippedKey = + if (scopedDirective.directive.key.startsWith(prefix)) + scopedDirective.directive.key.stripPrefix(prefix) + else + value(Left(new UnexpectedDirectiveError(scopedDirective.directive.key))) + + val publishOptions = strippedKey match { + case "organization" => + PublishOptions(organization = Some(singleValue)) + case "name" | "moduleName" => + PublishOptions(name = Some(singleValue)) + case "version" => + PublishOptions(version = Some(singleValue)) + case "computeVersion" | "compute-version" => + value { ComputeVersion.parse(singleValue).map { computeVersion => PublishOptions( @@ -83,43 +91,49 @@ case object UsingPublishDirectiveHandler extends UsingDirectiveHandler { ) ) } - - case "url" => - Right(PublishOptions(url = Some(singleValue))) - case "license" => + } + case "url" => + PublishOptions(url = Some(singleValue)) + case "license" => + value { License.parse(singleValue).map { license => PublishOptions(license = Some(license)) } - case "versionControl" | "version-control" | "scm" => + } + case "versionControl" | "version-control" | "scm" => + value { Vcs.parse(singleValue).map { versionControl => PublishOptions(versionControl = Some(versionControl)) } - case "description" => - Right(PublishOptions(description = Some(singleValue.value))) - case "developer" => + } + case "description" => + PublishOptions(description = Some(singleValue.value)) + case "developer" => + value { Developer.parse(singleValue).map { developer => PublishOptions(developers = Seq(developer)) - } - case "scalaVersionSuffix" | "scala-version-suffix" => - Right(PublishOptions(scalaVersionSuffix = Some(singleValue.value))) - case "scalaPlatformSuffix" | "scala-platform-suffix" => - Right(PublishOptions(scalaPlatformSuffix = Some(singleValue.value))) - case "repository" => - Right(PublishOptions(repository = Some(singleValue.value))) - case "gpgKey" | "gpg-key" => - Right(PublishOptions(gpgSignatureId = Some(singleValue.value))) - case "gpgOptions" | "gpg-options" | "gpgOption" | "gpg-option" => - Right(PublishOptions(gpgOptions = severalValues.map(_.value).toList)) - case _ => - Left(new UnexpectedDirectiveError(scopedDirective.directive.key)) - } - }.map { publishOptions => - val options = BuildOptions( - notForBloopOptions = PostBuildOptions( - publishOptions = publishOptions - ) - ) - ProcessedDirective(Some(options), Seq.empty) + } + case "scalaVersionSuffix" | "scala-version-suffix" => + PublishOptions(scalaVersionSuffix = Some(singleValue.value)) + case "scalaPlatformSuffix" | "scala-platform-suffix" => + PublishOptions(scalaPlatformSuffix = Some(singleValue.value)) + case "repository" => + PublishOptions(repository = Some(singleValue.value)) + case "gpgKey" | "gpg-key" => + PublishOptions(gpgSignatureId = Some(singleValue.value)) + case "gpgOptions" | "gpg-options" | "gpgOption" | "gpg-option" => + PublishOptions(gpgOptions = severalValues.map(_.value).toList) + case _ => + value(Left(new UnexpectedDirectiveError(scopedDirective.directive.key))) } + + val options = BuildOptions( + notForBloopOptions = PostBuildOptions( + publishOptions = publishOptions + ) + ) + + ProcessedDirective(Some(options), Seq.empty) + } } From 50c3216b0d0b6cccbcfcda4479d6167f6e3be569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= Date: Wed, 20 Apr 2022 18:26:23 +0200 Subject: [PATCH 28/72] Update Readme.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 42e65b7ede..f67f783eab 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The Scala CLI sources ship with Mill launchers, so that Mill itself doesn't need #### Run unit tests ```bash -./mill 'build[_].test' +./mill 'build-module[_].test' ``` #### Run integration tests with the JVM launcher @@ -41,8 +41,8 @@ The Scala CLI sources ship with Mill launchers, so that Mill itself doesn't need Filter test suites with ```bash -./mill integration.jvm.test 'scala.cli.integration.RunTests.*' -./mill integration.jvm.test 'scala.cli.integration.RunTests.Multiple scripts' +./mill integration.jvm.test 'scala.cli.integration.RunTestsDefault.*' +./mill integration.jvm.test 'scala.cli.integration.RunTestsDefault.Multiple scripts' ``` #### Run integration tests with the native launcher From 70dacead5d31213074a0873e12f7a35841a3c710 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 09:50:14 +0200 Subject: [PATCH 29/72] Back port of documentation changes to main (#919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use absolute path for loading images/gifs (#874) * Trigger docs deploy * Update directective instruction for tests Co-authored-by: Łukasz Wroński <46607934+lwronski@users.noreply.github.com> Co-authored-by: gh-actions Co-authored-by: Łukasz Wroński Co-authored-by: Amaal Ali Co-authored-by: Krzysztof Romanowski --- website/docs/commands/test.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/commands/test.md b/website/docs/commands/test.md index e156d7dbaa..d6ffb6598b 100644 --- a/website/docs/commands/test.md +++ b/website/docs/commands/test.md @@ -12,7 +12,7 @@ By default, all command line options apply to both the main and test sources, so A source file is treated as test source if: - - it contains the `using target test` directive, or + - it contains the `//> using target.scope "test"` directive, or - the file name ends with `.test.scala`, or - the file comes from a directory that is provided as input, and the relative path from that file to its original directory contains a `test` directory From fc9b37d1a0cee0b768256dfe1c78f7510ffd9559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= <46607934+lwronski@users.noreply.github.com> Date: Thu, 21 Apr 2022 16:56:43 +0200 Subject: [PATCH 30/72] Fix compute nightly version Scala 2 (#908) * Fix compute nightly version Scala 2 * Increase ttl to 2 hours * Passing exception casue to BuildException --- .../scala/build/tests/BuildOptionsTests.scala | 2 +- .../build/errors/ScalaVersionError.scala | 4 +- .../build/internal/ScalaNightlyVersion.scala | 72 +++++++++++++++++++ .../scala/build/options/BuildOptions.scala | 56 ++------------- 4 files changed, 82 insertions(+), 52 deletions(-) create mode 100644 modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala diff --git a/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala b/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala index c254caacfd..580a5c5ba4 100644 --- a/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala @@ -228,7 +228,7 @@ class BuildOptionsTests extends munit.FunSuite { ) val scalaParams = options.scalaParams.orThrow assert( - scalaParams.scalaVersion == "2.12.16-bin-586302a", + scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined, "-S 2.12.nightly argument does not lead to scala2 nightly build option" ) } diff --git a/modules/core/src/main/scala/scala/build/errors/ScalaVersionError.scala b/modules/core/src/main/scala/scala/build/errors/ScalaVersionError.scala index 02df3f6bc6..141f628395 100644 --- a/modules/core/src/main/scala/scala/build/errors/ScalaVersionError.scala +++ b/modules/core/src/main/scala/scala/build/errors/ScalaVersionError.scala @@ -2,8 +2,8 @@ package scala.build.errors import scala.build.Position -class ScalaVersionError(message: String, positions: Seq[Position] = Nil) - extends BuildException(message, positions = positions) +class ScalaVersionError(message: String, positions: Seq[Position] = Nil, cause: Throwable = null) + extends BuildException(message, positions = positions, cause) object ScalaVersionError { def getTheGeneralErrorInfo(latestSupportedStableVersions: Seq[String]): String = diff --git a/modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala b/modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala new file mode 100644 index 0000000000..72943cbbcf --- /dev/null +++ b/modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala @@ -0,0 +1,72 @@ +package scala.build.internal + +import com.github.plokhotnyuk.jsoniter_scala.core._ +import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker +import coursier.cache.FileCache +import coursier.util.{Artifact, Task} +import dependency.ScalaVersion + +import scala.build.EitherCps.{either, value} +import scala.build.Os +import scala.build.errors.{BuildException, ScalaVersionError} +import scala.concurrent.duration._ +import scala.util.control.NonFatal + +object ScalaNightlyVersion { + + private object Scala2Repo { + final case class ScalaVersion(name: String, lastModified: Long) + final case class ScalaVersionsMetaData(repo: String, children: List[ScalaVersion]) + + val codec: JsonValueCodec[ScalaVersionsMetaData] = JsonCodecMaker.make + } + + private def downloadScala2RepoPage(cache: FileCache[Task]): Either[BuildException, Array[Byte]] = + either { + val scala2NightlyRepo = + "https://scala-ci.typesafe.com/ui/api/v1/ui/nativeBrowser/scala-integration/org/scala-lang/scala-compiler" + val artifact = Artifact(scala2NightlyRepo).withChanging(true) + val res = cache.logger.use { + try cache.withTtl(1.hours).file(artifact).run.unsafeRun()(cache.ec) + catch { + case NonFatal(e) => throw new Exception(e) + } + }.left.map { err => + val msg = + """|Unable to compute the latest Scala 2 nightly version. + |Throws error during downloading web page repository for Scala 2.""".stripMargin + new ScalaVersionError(msg, cause = err) + } + + val res0 = value(res) + val content = os.read.bytes(os.Path(res0, Os.pwd)) + content + } + + def computeLatestScalaNightlyVersions( + versionPrefix: String, + cache: FileCache[Task] + ): Either[BuildException, (String, String)] = either { + val webPageScala2Repo = value(downloadScala2RepoPage(cache)) + val scala2Repo = readFromArray(webPageScala2Repo)(Scala2Repo.codec) + val versions = scala2Repo.children + val sortedVersion = + versions + .filter(_.name.startsWith(versionPrefix)) + .filterNot(_.name.contains("pre")) + .sortBy(_.lastModified) + + val latestNightly = sortedVersion.lastOption.map(_.name) + latestNightly match { + case Some(nightlyVersion) => + val scalaBinaryVersion = ScalaVersion.binary(nightlyVersion) + (nightlyVersion, scalaBinaryVersion) + case None => + val msg = s"""|Unable to compute the latest Scala $versionPrefix nightly version. + |Pass explicitly full Scala 2 nightly version.""".stripMargin + throw new ScalaVersionError(msg) + } + + } + +} diff --git a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala index c0b5c64b41..b44d2822ae 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala @@ -18,7 +18,7 @@ import scala.build.errors._ import scala.build.internal.Constants._ import scala.build.internal.CsLoggerUtil._ import scala.build.internal.Regexes.{scala2NightlyRegex, scala3NightlyNicknameRegex} -import scala.build.internal.{OsLibc, StableScalaVersion, Util} +import scala.build.internal.{OsLibc, ScalaNightlyVersion, StableScalaVersion, Util} import scala.build.options.validation.BuildOptionsRule import scala.build.{Artifacts, Logger, Os, Position, Positioned} import scala.util.control.NonFatal @@ -503,28 +503,6 @@ final case class BuildOptions( (scalaVersion, scalaBinaryVersion) } - /** @return - * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple - */ - private def computeLatestScalaTwoNightlyVersions(): Either[BuildException, (String, String)] = - either { - val moduleVersion: Either[ScalaVersionError, String] = { - def scalaNightly2Module: Module = cmod"org.scala-lang:scala-library" - val res = finalCache.logger.use { - Versions(finalCache) - .withModule(scalaNightly2Module) - .withRepositories(Seq(coursier.Repositories.scalaIntegration)) - .result() - .unsafeRun()(finalCache.ec) - } - latestScalaVersionFrom(res.versions, "latest Scala 2 nightly build") - } - - val scalaVersion = value(moduleVersion) - val scalaBinaryVersion = ScalaVersion.binary(scalaVersion) - (scalaVersion, scalaBinaryVersion) - } - private def turnScala2NightlyVersionArgToVersions(versionString: String) : Either[BuildException, (String, String)] = either { @@ -571,29 +549,6 @@ final case class BuildOptions( (scalaVersion, scalaBinaryVersion) } - def computeLatestScalaTwoTwelveNightlyVersions(): Either[BuildException, (String, String)] = - either { - val moduleVersion: Either[ScalaVersionError, String] = { - def scalaNightly2Module: Module = cmod"org.scala-lang:scala-library" - val res = finalCache.logger.use { - Versions(finalCache) - .withModule(scalaNightly2Module) - .withRepositories(Seq(coursier.Repositories.scalaIntegration)) - .result() - .unsafeRun()(finalCache.ec) - }.versions.available - val twoTwelveNightlies = res.filter(_.startsWith("2.12.")).map(Version(_)) - if (twoTwelveNightlies.nonEmpty) Right(twoTwelveNightlies.max.repr) - else Left( - new NoValidScalaVersionFoundError(res, latestSupportedStableVersions) - ) - } - - val scalaVersion = value(moduleVersion) - val scalaBinaryVersion = ScalaVersion.binary(scalaVersion) - (scalaVersion, scalaBinaryVersion) - } - private def isScala2Nightly(version: String): Boolean = scala2NightlyRegex.unapplySeq(version).isDefined @@ -607,9 +562,12 @@ final case class BuildOptions( case Some("3.nightly") => computeLatestScalaThreeNightlyVersions() case Some(scala3NightlyNicknameRegex(threeSubBinaryNum)) => computeLatestScalaThreeXNightlyVersions(threeSubBinaryNum) - case Some("2.nightly") => computeLatestScalaTwoNightlyVersions() - case Some("2.13.nightly") => computeLatestScalaTwoNightlyVersions() - case Some("2.12.nightly") => computeLatestScalaTwoTwelveNightlyVersions() + case Some("2.nightly") => + ScalaNightlyVersion.computeLatestScalaNightlyVersions("2.13", finalCache) + case Some("2.13.nightly") => + ScalaNightlyVersion.computeLatestScalaNightlyVersions("2.13", finalCache) + case Some("2.12.nightly") => + ScalaNightlyVersion.computeLatestScalaNightlyVersions("2.12", finalCache) case Some(versionString) if isScala3Nightly(versionString) => turnScala3NightlyVersionArgIntoVersion(versionString) case Some(versionString) if isScala2Nightly(versionString) => From 28e7e29123184af5edb71eecfb76199ec8ec5854 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Thu, 21 Apr 2022 10:26:07 +0200 Subject: [PATCH 31/72] Remove deprecated scala-version file --- scala-version.scala | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 scala-version.scala diff --git a/scala-version.scala b/scala-version.scala deleted file mode 100644 index 4baa84d43f..0000000000 --- a/scala-version.scala +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "scalaCliVersion": "0.0.9", - "supportedScalaVersions": ["3.0.2", "2.13.7", "2.12.15"] - } -] \ No newline at end of file From 6847c443cec52357eedd03f824ade47638aaf924 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Thu, 21 Apr 2022 10:26:59 +0200 Subject: [PATCH 32/72] Add more details in the release docs --- .github/release/release-procedure.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/release/release-procedure.md b/.github/release/release-procedure.md index 3aba79ff76..9f748bfa16 100644 --- a/.github/release/release-procedure.md +++ b/.github/release/release-procedure.md @@ -1,6 +1,17 @@ -- [ ] Draft release notes -- [ ] Update supported scala-versions for the released versions of Scala CLI - [Virtuslab/scala-cli-scala-versions ](https://github.com/Virtuslab/scala-cli-scala-versions) -- [ ] Update supported scala-versions in documentation - [scala-cli-scala-versions.md](https://github.com/VirtusLab/scala-cli/blob/main/website/docs/reference/scala-versions.md) -- [ ] Marks release as `pre-release` and then `Publish Release` -- [ ] Wait for green release CI build -- [ ] Unmark release as `pre-release` \ No newline at end of file +# Release procedure reference +- [ ] Draft release notes using the `Draft new release` button in the `Releases` section of `scala-cli` GitHub page. + - [ ] Create a tag for the new release. + - [ ] Use the `Auto-generate release notes` feature to pre-populate the document with pull requests included in the release. + - [ ] Fill in the remaining sections, as in previous releases (features worth mentioning, notable changes, etc). + - [ ] To prepare the `Contributors` section, checkout the repository locally and run the command below. + Make sure the new release tag is added on your local git repository to get the correct results. + ```bash + git shortlog -sn --no-merges {old-release-tag}...{new-release-tag} + ``` +- [ ] Create a pull request with supported scala-versions for the new release to this repository: [Virtuslab/scala-cli-scala-versions ](https://github.com/Virtuslab/scala-cli-scala-versions) +- [ ] Update the supported scala versions for the new release in the docs: + - [ ] Scala: [scala-cli-scala-versions.md](https://github.com/VirtusLab/scala-cli/blob/main/website/docs/reference/scala-versions.md) + - [ ] Scala.js: [scala-js.md](https://github.com/VirtusLab/scala-cli/blob/main/website/docs/guides/scala-js.md#supported-scalajs-versions) +- [ ] Mark the release draft as `pre-release` and then `Publish Release` +- [ ] Wait for a green release CI build with all the updated versions. +- [ ] Unmark release as `pre-release`. From fcb1c972a987f632c70c1b66e22b24b4a9db2ca0 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Fri, 22 Apr 2022 09:01:23 +0200 Subject: [PATCH 33/72] Update jsoniter-scala-core, ... to 2.13.17 (#927) --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index 6835645cb3..8792047957 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -52,7 +52,7 @@ object Deps { object Versions { // jni-utils version may need to be sync-ed when bumping the coursier version def coursier = "2.1.0-M5-18-gfebf9838c" - def jsoniterScala = "2.13.16" + def jsoniterScala = "2.13.17" def scalaMeta = "4.5.4" def scalaNative = "0.4.4" def scalaPackager = "0.1.26" From a7cb83ddee06c66855af4cbfa9d9600968c3551f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Apr 2022 10:21:14 +0200 Subject: [PATCH 34/72] Back port of documentation changes to main (#928) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use absolute path for loading images/gifs (#874) * Trigger docs deploy * Update directective instruction for tests * Update using-directives.md Co-authored-by: Łukasz Wroński <46607934+lwronski@users.noreply.github.com> Co-authored-by: gh-actions Co-authored-by: Łukasz Wroński Co-authored-by: Amaal Ali Co-authored-by: Krzysztof Romanowski --- website/docs/guides/using-directives.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/guides/using-directives.md b/website/docs/guides/using-directives.md index ab20877c4a..6222f6606b 100644 --- a/website/docs/guides/using-directives.md +++ b/website/docs/guides/using-directives.md @@ -73,7 +73,7 @@ Scala CLI produces warnings if any of the syntaxes above is used: [warn] ^^^^^^ ``` -## Details +## Semantics `using` directives can be only declared **before any other Scala code**: @@ -172,4 +172,4 @@ In such cases we suggest to use triple `/` for single line comments, or use `//` */ ``` -Generally, our recommendation is to not use keyword based directives until scala-cli will stop supporting plain comments-based directives. \ No newline at end of file +Generally, our recommendation is to not use keyword based directives until scala-cli will stop supporting plain comments-based directives. From 66a6b01312f43604842aa83a5f7f3a9d6f35e9a5 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Wed, 20 Apr 2022 08:58:23 +0200 Subject: [PATCH 35/72] Switch to Scala 3 by default --- .github/workflows/ci.yml | 18 ------------- build.sc | 26 ++++++++++--------- .../scala/cli/doc/InternalDocOptions.scala | 16 ++++++++++++ .../scala/cli/doc/GenerateReferenceDoc.scala | 4 +-- .../main/scala/scala/cli/doc/Options.scala | 9 ------- project/deps.sc | 13 +++++----- 6 files changed, 39 insertions(+), 47 deletions(-) create mode 100644 modules/cli-options/src/main/scala/scala/cli/doc/InternalDocOptions.scala delete mode 100644 modules/generate-reference-doc/src/main/scala/scala/cli/doc/Options.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 293dbd28ea..12fb6ecb2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -186,24 +186,6 @@ jobs: - name: Check examples run: bash ./scala-cli --jvm temurin:17 .github/scripts/check_examples.sc - migration-tests: - timeout-minutes: 120 - runs-on: "ubuntu-latest" - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: true - - uses: coursier/cache-action@v6.3 - - uses: VirtusLab/scala-cli-setup@267af2f1ed4911180b4bb25619ca4a586753cbd1 - with: - jvm: "temurin:17" - - name: Unit 3.x tests - run: | - ./mill -i cli3.compile - ./mill -i cli3.test - ./mill -i _[3.1.1].test - checks: timeout-minutes: 15 runs-on: ubuntu-latest diff --git a/build.sc b/build.sc index b6966aa379..eeb5434fc4 100644 --- a/build.sc +++ b/build.sc @@ -34,16 +34,18 @@ import _root_.scala.util.Properties implicit def millModuleBasePath: define.BasePath = define.BasePath(super.millModuleBasePath.value / "modules") -object cli extends Cli -// remove once migrate to Scala 3 -object cli3 extends Cli3 +object cli extends Cli3 + +// remove once we do not have blockers with Scala 3 +object cli2 extends Cli + object `cli-options` extends CliOptions -object `build-macros` extends Cross[BuildMacros](Scala.defaultInternal, Scala.scala3) -object options extends Cross[Options](Scala.defaultInternal, Scala.scala3) +object `build-macros` extends Cross[BuildMacros](Scala.mainVersions: _*) +object options extends Cross[Options](Scala.mainVersions: _*) object scalaparse extends ScalaParse -object directives extends Cross[Directives](Scala.defaultInternal, Scala.scala3) -object core extends Cross[Core](Scala.defaultInternal, Scala.scala3) -object `build-module` extends Cross[Build](Scala.defaultInternal, Scala.scala3) +object directives extends Cross[Directives](Scala.mainVersions: _*) +object core extends Cross[Core](Scala.mainVersions: _*) +object `build-module` extends Cross[Build](Scala.mainVersions: _*) object runner extends Cross[Runner](Scala.all: _*) object `test-runner` extends Cross[TestRunner](Scala.all: _*) object `bloop-rifle` extends Cross[BloopRifle](Scala.all: _*) @@ -51,7 +53,7 @@ object `tasty-lib` extends Cross[TastyLib](Scala.all: _*) // Runtime classes used within native image on Scala 3 replacing runtime from Scala object `scala3-runtime` extends Scala3Runtime // Logic to process classes that is shared between build and the scala-cli itself -object `scala3-graal` extends Cross[Scala3Graal](Scala.defaultInternal, Scala.scala3) +object `scala3-graal` extends Cross[Scala3Graal](Scala.mainVersions: _*) // Main app used to process classpath within build itself object `scala3-graal-processor` extends Scala3GraalProcessor @@ -481,7 +483,7 @@ class Options(val crossScalaVersion: String) extends BuildLikeModule { trait ScalaParse extends SbtModule with ScalaCliPublishModule with ScalaCliCompile { def ivyDeps = super.ivyDeps() ++ Agg(Deps.scalaparse) - def scalaVersion = Scala.defaultInternal + def scalaVersion = Scala.scala213 } trait Scala3Runtime extends SbtModule with ScalaCliPublishModule with ScalaCliCompile { @@ -587,7 +589,7 @@ trait CliOptions extends SbtModule with ScalaCliPublishModule with ScalaCliCompi Deps.osLib, Deps.signingCliShared ) - def scalaVersion = Scala.defaultInternal + def scalaVersion = Scala.scala213 def repositories = super.repositories ++ customRepositories } @@ -654,7 +656,7 @@ trait Cli3 extends Cli { mainArgs = Seq(cache.toNIO.toString, classpath), workingDir = os.pwd ) - val cp = res.out.text + val cp = res.out.text.trim cp.split(File.pathSeparator).toSeq.map(p => mill.PathRef(os.Path(p))) } } diff --git a/modules/cli-options/src/main/scala/scala/cli/doc/InternalDocOptions.scala b/modules/cli-options/src/main/scala/scala/cli/doc/InternalDocOptions.scala new file mode 100644 index 0000000000..0b4db013c4 --- /dev/null +++ b/modules/cli-options/src/main/scala/scala/cli/doc/InternalDocOptions.scala @@ -0,0 +1,16 @@ +package scala.cli.doc + +import caseapp._ + +final case class InternalDocOptions( + outputDir: String = "website/docs/reference", + check: Boolean = false +) { + lazy val outputPath: os.Path = + os.Path(outputDir, os.pwd) +} + +object InternalDocOptions { + implicit lazy val parser: Parser[InternalDocOptions] = Parser.derive + implicit lazy val help: Help[InternalDocOptions] = Help.derive +} diff --git a/modules/generate-reference-doc/src/main/scala/scala/cli/doc/GenerateReferenceDoc.scala b/modules/generate-reference-doc/src/main/scala/scala/cli/doc/GenerateReferenceDoc.scala index d22c24d39e..b09b9aa4bd 100644 --- a/modules/generate-reference-doc/src/main/scala/scala/cli/doc/GenerateReferenceDoc.scala +++ b/modules/generate-reference-doc/src/main/scala/scala/cli/doc/GenerateReferenceDoc.scala @@ -16,7 +16,7 @@ import scala.build.preprocessing.directives.{ } import scala.cli.ScalaCliCommands -object GenerateReferenceDoc extends CaseApp[Options] { +object GenerateReferenceDoc extends CaseApp[InternalDocOptions] { private def cleanUpOrigin(origin: String): String = { val origin0 = origin.takeWhile(_ != '[').stripSuffix("Options") @@ -289,7 +289,7 @@ object GenerateReferenceDoc extends CaseApp[Options] { b.toString } - def run(options: Options, args: RemainingArgs): Unit = { + def run(options: InternalDocOptions, args: RemainingArgs): Unit = { val scalaCli = new ScalaCliCommands("scala-cli", isSipScala = false) val commands = scalaCli.commands diff --git a/modules/generate-reference-doc/src/main/scala/scala/cli/doc/Options.scala b/modules/generate-reference-doc/src/main/scala/scala/cli/doc/Options.scala deleted file mode 100644 index 1adb6d60f4..0000000000 --- a/modules/generate-reference-doc/src/main/scala/scala/cli/doc/Options.scala +++ /dev/null @@ -1,9 +0,0 @@ -package scala.cli.doc - -final case class Options( - outputDir: String = "website/docs/reference", - check: Boolean = false -) { - lazy val outputPath: os.Path = - os.Path(outputDir, os.pwd) -} diff --git a/project/deps.sc b/project/deps.sc index 8792047957..77c59866bc 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -4,11 +4,12 @@ import mill._, scalalib._ import scala.util.Properties object Scala { - def scala212 = "2.12.15" - def scala213 = "2.13.8" - def scala3 = "3.1.1" - val allScala2 = Seq(scala213, scala212) - val all = allScala2 ++ Seq(scala3) + def scala212 = "2.12.15" + def scala213 = "2.13.8" + def scala3 = "3.1.1" + val allScala2 = Seq(scala213, scala212) + val all = allScala2 ++ Seq(scala3) + val mainVersions = Seq(scala3, scala213) def scalaJs = "1.10.0" @@ -28,7 +29,7 @@ object Scala { // The Scala version used to build the CLI itself. // We should be able to switch to 3.x when it'll have CPS support // (for the either { value(…) } stuff) - def defaultInternal = scala213 + def defaultInternal = scala3 // The Scala version used by default to compile user input. def defaultUser = scala3 From 6e5c7ed52038a63bd5101cbed936bded0ec66043 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 21 Apr 2022 23:14:12 +0200 Subject: [PATCH 36/72] Tweak cli / cli2 definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've been sometimes having issues locally, with some sources being ignored in cli3. I'm suspecting the millSourcePath or the "object test" in a trait to be issues here… --- build.sc | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/build.sc b/build.sc index eeb5434fc4..4d114e0535 100644 --- a/build.sc +++ b/build.sc @@ -34,10 +34,34 @@ import _root_.scala.util.Properties implicit def millModuleBasePath: define.BasePath = define.BasePath(super.millModuleBasePath.value / "modules") -object cli extends Cli3 +object cli extends Cli3 { + object test extends Tests { + def moduleDeps = super.moduleDeps ++ Seq( + `build-module`(myScalaVersion).test + ) + } +} // remove once we do not have blockers with Scala 3 -object cli2 extends Cli +object cli2 extends Cli { + def sources = T.sources { + super.sources() ++ cli.sources() + } + def resources = T.sources { + super.resources() ++ cli.resources() + } + object test extends Tests { + def sources = T.sources { + super.sources() ++ cli.test.sources() + } + def resources = T.sources { + super.resources() ++ cli.test.resources() + } + def moduleDeps = super.moduleDeps ++ Seq( + `build-module`(myScalaVersion).test + ) + } +} object `cli-options` extends CliOptions object `build-macros` extends Cross[BuildMacros](Scala.mainVersions: _*) @@ -596,8 +620,6 @@ trait CliOptions extends SbtModule with ScalaCliPublishModule with ScalaCliCompi trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers with HasMacroAnnotations with FormatNativeImageConf { - def millSourcePath = super.millSourcePath / os.up / "cli" - def myScalaVersion = Scala.defaultInternal def scalaVersion = T(myScalaVersion) @@ -636,11 +658,7 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers def localRepoJar = `local-repo`.localRepoJar() - object test extends Tests with ScalaCliScalafixModule { - def moduleDeps = super.moduleDeps ++ Seq( - `build-module`(myScalaVersion).test - ) - } + trait Tests extends super.Tests with ScalaCliScalafixModule } trait Cli3 extends Cli { From 41501418d9136fa97fd8d865a1c6a599408bcad1 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 21 Apr 2022 21:13:36 +0200 Subject: [PATCH 37/72] Refactor Scala version processing in BuildOptions --- .../build/internal/ScalaNightlyVersion.scala | 72 ---- .../scala/build/options/BuildOptions.scala | 352 ++++-------------- .../build/options/ScalaVersionUtil.scala | 287 ++++++++++++++ 3 files changed, 356 insertions(+), 355 deletions(-) delete mode 100644 modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala create mode 100644 modules/options/src/main/scala/scala/build/options/ScalaVersionUtil.scala diff --git a/modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala b/modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala deleted file mode 100644 index 72943cbbcf..0000000000 --- a/modules/options/src/main/scala/scala/build/internal/ScalaNightlyVersion.scala +++ /dev/null @@ -1,72 +0,0 @@ -package scala.build.internal - -import com.github.plokhotnyuk.jsoniter_scala.core._ -import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker -import coursier.cache.FileCache -import coursier.util.{Artifact, Task} -import dependency.ScalaVersion - -import scala.build.EitherCps.{either, value} -import scala.build.Os -import scala.build.errors.{BuildException, ScalaVersionError} -import scala.concurrent.duration._ -import scala.util.control.NonFatal - -object ScalaNightlyVersion { - - private object Scala2Repo { - final case class ScalaVersion(name: String, lastModified: Long) - final case class ScalaVersionsMetaData(repo: String, children: List[ScalaVersion]) - - val codec: JsonValueCodec[ScalaVersionsMetaData] = JsonCodecMaker.make - } - - private def downloadScala2RepoPage(cache: FileCache[Task]): Either[BuildException, Array[Byte]] = - either { - val scala2NightlyRepo = - "https://scala-ci.typesafe.com/ui/api/v1/ui/nativeBrowser/scala-integration/org/scala-lang/scala-compiler" - val artifact = Artifact(scala2NightlyRepo).withChanging(true) - val res = cache.logger.use { - try cache.withTtl(1.hours).file(artifact).run.unsafeRun()(cache.ec) - catch { - case NonFatal(e) => throw new Exception(e) - } - }.left.map { err => - val msg = - """|Unable to compute the latest Scala 2 nightly version. - |Throws error during downloading web page repository for Scala 2.""".stripMargin - new ScalaVersionError(msg, cause = err) - } - - val res0 = value(res) - val content = os.read.bytes(os.Path(res0, Os.pwd)) - content - } - - def computeLatestScalaNightlyVersions( - versionPrefix: String, - cache: FileCache[Task] - ): Either[BuildException, (String, String)] = either { - val webPageScala2Repo = value(downloadScala2RepoPage(cache)) - val scala2Repo = readFromArray(webPageScala2Repo)(Scala2Repo.codec) - val versions = scala2Repo.children - val sortedVersion = - versions - .filter(_.name.startsWith(versionPrefix)) - .filterNot(_.name.contains("pre")) - .sortBy(_.lastModified) - - val latestNightly = sortedVersion.lastOption.map(_.name) - latestNightly match { - case Some(nightlyVersion) => - val scalaBinaryVersion = ScalaVersion.binary(nightlyVersion) - (nightlyVersion, scalaBinaryVersion) - case None => - val msg = s"""|Unable to compute the latest Scala $versionPrefix nightly version. - |Pass explicitly full Scala 2 nightly version.""".stripMargin - throw new ScalaVersionError(msg) - } - - } - -} diff --git a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala index b44d2822ae..6b414be67c 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala @@ -2,23 +2,21 @@ package scala.build.options import com.github.plokhotnyuk.jsoniter_scala.core._ import coursier.cache.{ArchiveCache, FileCache} -import coursier.core.{Version, Versions => CoreVersions} +import coursier.core.Version import coursier.jvm.{JavaHome, JvmCache, JvmIndex} import coursier.util.{Artifact, Task} -import coursier.{Module, Versions} import dependency._ import java.math.BigInteger import java.nio.charset.StandardCharsets import java.security.MessageDigest -import scala.build.CoursierUtils._ import scala.build.EitherCps.{either, value} import scala.build.errors._ import scala.build.internal.Constants._ import scala.build.internal.CsLoggerUtil._ -import scala.build.internal.Regexes.{scala2NightlyRegex, scala3NightlyNicknameRegex} -import scala.build.internal.{OsLibc, ScalaNightlyVersion, StableScalaVersion, Util} +import scala.build.internal.Regexes.scala3NightlyNicknameRegex +import scala.build.internal.{OsLibc, StableScalaVersion} import scala.build.options.validation.BuildOptionsRule import scala.build.{Artifacts, Logger, Os, Position, Positioned} import scala.util.control.NonFatal @@ -293,302 +291,90 @@ final case class BuildOptions( private val scala2NightlyRepo = Seq(coursier.Repositories.scalaIntegration.root) - def finalRepositories: Seq[String] = - scalaParams.map { params => - if (isScala2Nightly(params.scalaVersion)) scala2NightlyRepo else Seq.empty - }.getOrElse(Seq.empty) ++ - classPathOptions.extraRepositories ++ internal.localRepository.toSeq + def finalRepositories: Seq[String] = { - private lazy val maxSupportedStableScalaVersions = latestSupportedStableScalaVersion() - - private lazy val latestSupportedStableVersions = maxSupportedStableScalaVersions.map(_.repr) - - private def getAllMatchingStableVersions(scalaVersionArg: Option[String]): Seq[String] = { - - def isStable(version: String): Boolean = - !version.exists(_.isLetter) - - modules(scalaVersionArg).flatMap(moduleVersions(_).versions.available.filter(isStable)).distinct - } - - private def modules(maybeScalaVersionArg: Option[String]) = { - def scala2 = cmod"org.scala-lang:scala-library" - // No unstable, that *ought* not to be a problem down-the-line…? - def scala3 = cmod"org.scala-lang:scala3-library_3" - if (maybeScalaVersionArg.contains("2") || maybeScalaVersionArg.exists(_.startsWith("2."))) - Seq(scala2) - else if (maybeScalaVersionArg.contains("3") || maybeScalaVersionArg.exists(_.startsWith("3."))) - Seq(scala3) - else Seq(scala2, scala3) - } - - private def moduleVersions(mod: Module): Versions.Result = - finalCache.logger.use { - try Versions(finalCache) - .withModule(mod) - .result() - .unsafeRun()(finalCache.ec) - catch { - case NonFatal(e) => throw new Exception(e) - } - } + val nightlyRepos = + if (scalaParams.exists(params => ScalaVersionUtil.isScala2Nightly(params.scalaVersion))) + scala2NightlyRepo + else + Nil - private def getAllMatchingVersions(maybeScalaVersionArg: Option[String]): Seq[String] = - modules(maybeScalaVersionArg).flatMap(moduleVersions(_).versions.available).distinct - - /** @param scalaVersionArg - * the command line, using directive, or default argument passed as scala version - * @param scalaBinaryVersionArg - * the command line, using directive, or default argument passed as scala Binary version - * @return - * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple - */ - private def turnScalaVersionArgToStableScalaVersions( - scalaVersionArg: Option[String], - scalaBinaryVersionArg: Option[String] - ): Either[BuildException, (String, String)] = either { - - lazy val allStableVersions = getAllMatchingStableVersions(scalaVersionArg) - - val scalaVersion = value(matchNewestStableScalaVersion(scalaVersionArg, allStableVersions)) - val scalaBinaryVersion = scalaBinaryVersionArg.getOrElse(ScalaVersion.binary(scalaVersion)) - (scalaVersion, scalaBinaryVersion) + nightlyRepos ++ + classPathOptions.extraRepositories ++ + internal.localRepository.toSeq } - private def turnScalaVersionArgToNonStableScalaVersions( - scalaVersionArg: Option[String], - scalaBinaryVersionArg: Option[String] - ): Either[BuildException, (String, String)] = either { - - lazy val allStableVersions = getAllMatchingVersions(scalaVersionArg) - - val scalaVersion = value(matchNewestNonStableScalaVersion(scalaVersionArg, allStableVersions)) - val scalaBinaryVersion = scalaBinaryVersionArg.getOrElse(ScalaVersion.binary(scalaVersion)) - (scalaVersion, scalaBinaryVersion) - } + lazy val scalaParams: Either[BuildException, ScalaParameters] = either { - private def isSupportedVersion(version: String): Boolean = - version.startsWith("2.12.") || version.startsWith("2.13.") || version.startsWith("3.") - - private def matchNewestStableScalaVersion( - maybeScalaVersionStringArg: Option[String], - versionPool: Seq[String] - ): Either[ScalaVersionError, String] = - maybeScalaVersionStringArg match { - case Some(scalaVersionStringArg) => - val prefix = - if (Util.isFullScalaVersion(scalaVersionStringArg)) scalaVersionStringArg - else if (scalaVersionStringArg.endsWith(".")) scalaVersionStringArg - else scalaVersionStringArg + "." - val matchingStableVersions = versionPool.filter(_.startsWith(prefix)).map(Version(_)) - if (matchingStableVersions.isEmpty) - Left(new InvalidBinaryScalaVersionError( - scalaVersionStringArg, + lazy val maxSupportedStableScalaVersions = latestSupportedStableScalaVersion() + lazy val latestSupportedStableVersions = maxSupportedStableScalaVersions.map(_.repr) + + val scalaVersion = value { + scalaOptions.scalaVersion match { + case Some("3.nightly") => + ScalaVersionUtil.GetNightly.scala3(finalCache) + case Some(scala3NightlyNicknameRegex(threeSubBinaryNum)) => + ScalaVersionUtil.GetNightly.scala3X( + threeSubBinaryNum, + finalCache, latestSupportedStableVersions - )) - else { - val validMaxVersions = maxSupportedStableScalaVersions - .filter(_.repr.startsWith(prefix)) - val validMatchingVersions = { - val filtered = matchingStableVersions.filter(v => validMaxVersions.exists(v <= _)) - if (filtered.isEmpty) matchingStableVersions - else filtered - }.filter(v => isSupportedVersion(v.repr)) - if (validMatchingVersions.isEmpty) - Left(new UnsupportedScalaVersionError( - scalaVersionStringArg, - latestSupportedStableVersions - )) - else - Right(validMatchingVersions.max.repr) - } - case None => - val validVersions = versionPool - .map(Version(_)) - .filter(v => maxSupportedStableScalaVersions.exists(v <= _)) - if (validVersions.isEmpty) - Left(new NoValidScalaVersionFoundError( - versionPool, + ) + case Some("2.nightly" | "2.13.nightly") => + ScalaVersionUtil.GetNightly.scala2("2.13", finalCache) + case Some("2.12.nightly") => + ScalaVersionUtil.GetNightly.scala2("2.12", finalCache) + case Some(versionString) if ScalaVersionUtil.isScala3Nightly(versionString) => + ScalaVersionUtil.CheckNightly.scala3( + versionString, + finalCache, latestSupportedStableVersions - )) - else - Right(validVersions.max.repr) - } - - private def matchNewestNonStableScalaVersion( - maybeScalaVersionStringArg: Option[String], - versionPool: Seq[String] - ): Either[ScalaVersionError, String] = - maybeScalaVersionStringArg match { - case Some(scalaVersionStringArg) => - if (versionPool.contains(scalaVersionStringArg)) - if (isSupportedVersion(scalaVersionStringArg)) - Right(scalaVersionStringArg) - else - Left(new UnsupportedScalaVersionError( - scalaVersionStringArg, - latestSupportedStableVersions - )) - else - Left(new InvalidBinaryScalaVersionError( - scalaVersionStringArg, + ) + .map(_ => versionString) + case Some(versionString) if ScalaVersionUtil.isScala2Nightly(versionString) => + ScalaVersionUtil.CheckNightly.scala2( + versionString, + finalCache, latestSupportedStableVersions - )) - - case None => - Left(new NoValidScalaVersionFoundError( - versionPool, - latestSupportedStableVersions - )) - - } - - private def latestScalaVersionFrom( - versions: CoreVersions, - desc: String - ): Either[scala.build.errors.ScalaVersionError, String] = - versions.latest(coursier.core.Latest.Release) match { - case Some(versionString) => Right(versionString) - case None => - val msg = - s"Unable to find matching version for $desc in available version: ${versions.available.mkString(", ")}. " + - "This error may indicate a network or other problem accessing repository." - Left(new ScalaVersionError(msg)) - } - - /** @return - * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple - */ - private def computeLatestScalaThreeNightlyVersions(): Either[BuildException, (String, String)] = - either { - val moduleVersion: Either[ScalaVersionError, String] = { - def scala3 = cmod"org.scala-lang:scala3-library_3" - val res = finalCache.logger.use { - Versions(finalCache) - .withModule(scala3) - .result() - .unsafeRun()(finalCache.ec) - } - latestScalaVersionFrom(res.versions, "latest Scala 3 nightly build") - } - - val scalaVersion = value(moduleVersion) - val scalaBinaryVersion = ScalaVersion.binary(scalaVersion) - (scalaVersion, scalaBinaryVersion) - } - - /** @return - * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple - */ - private def computeLatestScalaThreeXNightlyVersions(threeSubBinaryNum: String) - : Either[BuildException, (String, String)] = - either { - val moduleVersion: Either[ScalaVersionError, String] = { - def scala3 = cmod"org.scala-lang:scala3-library_3" - val res = finalCache.logger.use { - Versions(finalCache) - .withModule(scala3) - .result() - .unsafeRun()(finalCache.ec) - }.versions.available.filter(_.endsWith("-NIGHTLY")) - - val threeXNightlies = res.filter(_.startsWith(s"3.$threeSubBinaryNum.")).map(Version(_)) - if (threeXNightlies.nonEmpty) Right(threeXNightlies.max.repr) - else Left( - new NoValidScalaVersionFoundError(res, latestSupportedStableVersions) - ) - } - val scalaVersion = value(moduleVersion) - val scalaBinaryVersion = ScalaVersion.binary(scalaVersion) - (scalaVersion, scalaBinaryVersion) - } - - private def turnScala2NightlyVersionArgToVersions(versionString: String) - : Either[BuildException, (String, String)] = either { - - val moduleVersion: Either[ScalaVersionError, String] = { - def scalaNightly2Module: Module = cmod"org.scala-lang:scala-library" - val res = finalCache.logger.use { - Versions(finalCache) - .withModule(scalaNightly2Module) - .withRepositories(Seq(coursier.Repositories.scalaIntegration)) - .result() - .unsafeRun()(finalCache.ec) + ) + .map(_ => versionString) + case Some(versionString) if versionString.exists(_.isLetter) => + val allVersions = ScalaVersionUtil.allMatchingVersions(Some(versionString), finalCache) + ScalaVersionUtil.validateNonStable( + versionString, + allVersions, + latestSupportedStableVersions + ) + case Some(versionString) => + val allStableVersions = + ScalaVersionUtil.allMatchingVersions(Some(versionString), finalCache) + .filter(ScalaVersionUtil.isStable) + ScalaVersionUtil.validateStable( + versionString, + allStableVersions, + latestSupportedStableVersions, + maxSupportedStableScalaVersions + ) + case None => + val allStableVersions = ScalaVersionUtil.allMatchingVersions(None, finalCache) + .filter(ScalaVersionUtil.isStable) + ScalaVersionUtil.default( + allStableVersions, + latestSupportedStableVersions, + maxSupportedStableScalaVersions + ) } - if (res.versions.available.contains(versionString)) Right(versionString) - else - Left( - new NoValidScalaVersionFoundError(res.versions.available, latestSupportedStableVersions) - ) } - val scalaVersion = value(moduleVersion) - val scalaBinaryVersion = ScalaVersion.binary(scalaVersion) - (scalaVersion, scalaBinaryVersion) - } - - private def turnScala3NightlyVersionArgIntoVersion(versionString: String) - : Either[BuildException, (String, String)] = either { - val moduleVersion: Either[ScalaVersionError, String] = { - def scala3 = cmod"org.scala-lang:scala3-library_3" - val res = finalCache.logger.use { - Versions(finalCache) - .withModule(scala3) - .result() - .unsafeRun()(finalCache.ec) - } - if (res.versions.available.contains(versionString)) Right(versionString) - else - Left( - new NoValidScalaVersionFoundError(res.versions.available, latestSupportedStableVersions) - ) + val scalaBinaryVersion = scalaOptions.scalaBinaryVersion.getOrElse { + ScalaVersion.binary(scalaVersion) } - val scalaVersion = value(moduleVersion) - val scalaBinaryVersion = ScalaVersion.binary(scalaVersion) - (scalaVersion, scalaBinaryVersion) - } - - private def isScala2Nightly(version: String): Boolean = - scala2NightlyRegex.unapplySeq(version).isDefined - - lazy val scalaParams: Either[BuildException, ScalaParameters] = either { - def isScala3Nightly(version: String): Boolean = - version.startsWith("3") && version.endsWith("-NIGHTLY") - - val (scalaVersion, scalaBinaryVersion) = - value { - scalaOptions.scalaVersion match { - case Some("3.nightly") => computeLatestScalaThreeNightlyVersions() - case Some(scala3NightlyNicknameRegex(threeSubBinaryNum)) => - computeLatestScalaThreeXNightlyVersions(threeSubBinaryNum) - case Some("2.nightly") => - ScalaNightlyVersion.computeLatestScalaNightlyVersions("2.13", finalCache) - case Some("2.13.nightly") => - ScalaNightlyVersion.computeLatestScalaNightlyVersions("2.13", finalCache) - case Some("2.12.nightly") => - ScalaNightlyVersion.computeLatestScalaNightlyVersions("2.12", finalCache) - case Some(versionString) if isScala3Nightly(versionString) => - turnScala3NightlyVersionArgIntoVersion(versionString) - case Some(versionString) if isScala2Nightly(versionString) => - turnScala2NightlyVersionArgToVersions(versionString) - case Some(versionString) if versionString.exists(_.isLetter) => - turnScalaVersionArgToNonStableScalaVersions( - scalaOptions.scalaVersion, - scalaOptions.scalaBinaryVersion - ) - case _ => turnScalaVersionArgToStableScalaVersions( - scalaOptions.scalaVersion, - scalaOptions.scalaBinaryVersion - ) - } - } - val maybePlatformSuffix = platform.value match { case Platform.JVM => None case Platform.JS => Some(scalaJsOptions.platformSuffix) case Platform.Native => Some(scalaNativeOptions.platformSuffix) } + ScalaParameters(scalaVersion, scalaBinaryVersion, maybePlatformSuffix) } diff --git a/modules/options/src/main/scala/scala/build/options/ScalaVersionUtil.scala b/modules/options/src/main/scala/scala/build/options/ScalaVersionUtil.scala new file mode 100644 index 0000000000..528d4beb74 --- /dev/null +++ b/modules/options/src/main/scala/scala/build/options/ScalaVersionUtil.scala @@ -0,0 +1,287 @@ +package scala.build.options + +import com.github.plokhotnyuk.jsoniter_scala.core._ +import com.github.plokhotnyuk.jsoniter_scala.macros._ +import coursier.Versions +import coursier.cache.FileCache +import coursier.core.{Version, Versions => CoreVersions} +import coursier.util.{Artifact, Task} + +import scala.build.CoursierUtils._ +import scala.build.EitherCps.{either, value} +import scala.build.errors.{ + BuildException, + InvalidBinaryScalaVersionError, + NoValidScalaVersionFoundError, + ScalaVersionError, + UnsupportedScalaVersionError +} +import scala.build.internal.Regexes.scala2NightlyRegex +import scala.build.internal.Util +import scala.concurrent.duration.DurationInt +import scala.util.control.NonFatal + +object ScalaVersionUtil { + + private def scala2Library = cmod"org.scala-lang:scala-library" + private def scala3Library = cmod"org.scala-lang:scala3-library_3" + + object GetNightly { + + private object Scala2Repo { + final case class ScalaVersion(name: String, lastModified: Long) + final case class ScalaVersionsMetaData(repo: String, children: List[ScalaVersion]) + + val codec: JsonValueCodec[ScalaVersionsMetaData] = JsonCodecMaker.make + } + + private def downloadScala2RepoPage(cache: FileCache[Task]) + : Either[BuildException, Array[Byte]] = + either { + val scala2NightlyRepo = + "https://scala-ci.typesafe.com/ui/api/v1/ui/nativeBrowser/scala-integration/org/scala-lang/scala-compiler" + val artifact = Artifact(scala2NightlyRepo).withChanging(true) + val res = cache.logger.use { + try cache.withTtl(1.hours).file(artifact).run.unsafeRun()(cache.ec) + catch { + case NonFatal(e) => throw new Exception(e) + } + }.left.map { err => + val msg = + """|Unable to compute the latest Scala 2 nightly version. + |Throws error during downloading web page repository for Scala 2.""".stripMargin + new ScalaVersionError(msg, cause = err) + } + + val res0 = value(res) + val content = os.read.bytes(os.Path(res0, os.pwd)) + content + } + + def scala2( + versionPrefix: String, + cache: FileCache[Task] + ): Either[BuildException, String] = either { + val webPageScala2Repo = value(downloadScala2RepoPage(cache)) + val scala2Repo = readFromArray(webPageScala2Repo)(Scala2Repo.codec) + val versions = scala2Repo.children + val sortedVersion = + versions + .filter(_.name.startsWith(versionPrefix)) + .filterNot(_.name.contains("pre")) + .sortBy(_.lastModified) + + val latestNightly = sortedVersion.lastOption.map(_.name) + latestNightly.getOrElse { + val msg = s"""|Unable to compute the latest Scala $versionPrefix nightly version. + |Pass explicitly full Scala 2 nightly version.""".stripMargin + throw new ScalaVersionError(msg) + } + } + + /** @return + * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple + */ + def scala3X( + threeSubBinaryNum: String, + cache: FileCache[Task], + latestSupportedStableVersions: Seq[String] + ): Either[BuildException, String] = { + val res = cache.logger.use { + Versions(cache) + .withModule(scala3Library) + .result() + .unsafeRun()(cache.ec) + }.versions.available.filter(_.endsWith("-NIGHTLY")) + + val threeXNightlies = res.filter(_.startsWith(s"3.$threeSubBinaryNum.")).map(Version(_)) + if (threeXNightlies.nonEmpty) Right(threeXNightlies.max.repr) + else Left( + new NoValidScalaVersionFoundError(res, latestSupportedStableVersions) + ) + } + + /** @return + * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple + */ + def scala3(cache: FileCache[Task]): Either[BuildException, String] = { + val res = cache.logger.use { + Versions(cache) + .withModule(scala3Library) + .result() + .unsafeRun()(cache.ec) + } + latestScalaVersionFrom(res.versions, "latest Scala 3 nightly build") + } + + private def latestScalaVersionFrom( + versions: CoreVersions, + desc: String + ): Either[scala.build.errors.ScalaVersionError, String] = + versions.latest(coursier.core.Latest.Release) match { + case Some(versionString) => Right(versionString) + case None => + val msg = + s"Unable to find matching version for $desc in available version: ${versions.available.mkString(", ")}. " + + "This error may indicate a network or other problem accessing repository." + Left(new ScalaVersionError(msg)) + } + + } + + object CheckNightly { + + def scala2( + versionString: String, + cache: FileCache[Task], + latestSupportedStableVersions: Seq[String] + ): Either[BuildException, Unit] = { + val res = cache.logger.use { + Versions(cache) + .withModule(scala2Library) + .withRepositories(Seq(coursier.Repositories.scalaIntegration)) + .result() + .unsafeRun()(cache.ec) + } + if (res.versions.available.contains(versionString)) + Right(()) + else + Left( + new NoValidScalaVersionFoundError(res.versions.available, latestSupportedStableVersions) + ) + } + + def scala3( + versionString: String, + cache: FileCache[Task], + latestSupportedStableVersions: Seq[String] + ): Either[BuildException, Unit] = { + val res = cache.logger.use { + Versions(cache) + .withModule(scala3Library) + .result() + .unsafeRun()(cache.ec) + } + if (res.versions.available.contains(versionString)) + Right(()) + else + Left( + new NoValidScalaVersionFoundError(res.versions.available, latestSupportedStableVersions) + ) + } + + } + + def validateNonStable( + scalaVersionStringArg: String, + versionPool: Seq[String], + latestSupportedStableVersions: Seq[String] + ): Either[ScalaVersionError, String] = + if (versionPool.contains(scalaVersionStringArg)) + if (isSupportedVersion(scalaVersionStringArg)) + Right(scalaVersionStringArg) + else + Left(new UnsupportedScalaVersionError( + scalaVersionStringArg, + latestSupportedStableVersions + )) + else + Left(new InvalidBinaryScalaVersionError( + scalaVersionStringArg, + latestSupportedStableVersions + )) + + def validateStable( + scalaVersionStringArg: String, + versionPool: Seq[String], + latestSupportedStableVersions: Seq[String], + maxSupportedStableScalaVersions: Seq[Version] + ): Either[ScalaVersionError, String] = { + val prefix = + if (Util.isFullScalaVersion(scalaVersionStringArg)) scalaVersionStringArg + else if (scalaVersionStringArg.endsWith(".")) scalaVersionStringArg + else scalaVersionStringArg + "." + val matchingStableVersions = versionPool.filter(_.startsWith(prefix)).map(Version(_)) + if (matchingStableVersions.isEmpty) + Left(new InvalidBinaryScalaVersionError( + scalaVersionStringArg, + latestSupportedStableVersions + )) + else { + val validMaxVersions = maxSupportedStableScalaVersions + .filter(_.repr.startsWith(prefix)) + val validMatchingVersions = { + val filtered = matchingStableVersions.filter(v => validMaxVersions.exists(v <= _)) + if (filtered.isEmpty) matchingStableVersions + else filtered + }.filter(v => isSupportedVersion(v.repr)) + if (validMatchingVersions.isEmpty) + Left(new UnsupportedScalaVersionError( + scalaVersionStringArg, + latestSupportedStableVersions + )) + else + Right(validMatchingVersions.max.repr) + } + } + + def default( + versionPool: Seq[String], + latestSupportedStableVersions: Seq[String], + maxSupportedStableScalaVersions: Seq[Version] + ): Either[ScalaVersionError, String] = { + val validVersions = versionPool + .map(Version(_)) + .filter(v => maxSupportedStableScalaVersions.exists(v <= _)) + if (validVersions.isEmpty) + Left(new NoValidScalaVersionFoundError( + versionPool, + latestSupportedStableVersions + )) + else + Right(validVersions.max.repr) + } + + private def isSupportedVersion(version: String): Boolean = + version.startsWith("2.12.") || version.startsWith("2.13.") || version.startsWith("3.") + + def isScala2Nightly(version: String): Boolean = + scala2NightlyRegex.unapplySeq(version).isDefined + def isScala3Nightly(version: String): Boolean = + version.startsWith("3") && version.endsWith("-NIGHTLY") + + def isStable(version: String): Boolean = + !version.exists(_.isLetter) + + def allMatchingVersions( + maybeScalaVersionArg: Option[String], + cache: FileCache[Task] + ): Seq[String] = { + + val modules = + if (maybeScalaVersionArg.contains("2") || maybeScalaVersionArg.exists(_.startsWith("2."))) + Seq(scala2Library) + else if ( + maybeScalaVersionArg.contains("3") || maybeScalaVersionArg.exists(_.startsWith("3.")) + ) + Seq(scala3Library) + else + Seq(scala2Library, scala3Library) + + modules + .flatMap { mod => + val versions = cache.logger.use { + try Versions(cache) + .withModule(mod) + .result() + .unsafeRun()(cache.ec) + catch { + case NonFatal(e) => throw new Exception(e) + } + } + versions.versions.available + } + .distinct + } + +} From 6155680f11a2d66cc7df85b8a6d6188383ec50e5 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 21 Apr 2022 21:14:19 +0200 Subject: [PATCH 38/72] Clean-up dead code --- modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala b/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala index 1e425e5230..b311ab16e2 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala @@ -62,7 +62,6 @@ abstract class ScalaCommand[T](implicit parser: Parser[T], help: Help[T]) case "dependency" => state.flatMap(sharedOptions).toList.flatMap { sharedOptions => val cache = sharedOptions.coursierCache - sharedOptions.buildOptions(false, None, ignoreErrors = true).scalaOptions.scalaVersion val (fromIndex, completions) = cache.logger.use { coursier.complete.Complete(cache) .withInput(prefix) From 6a318d3c348ec8b3bcf0f4ab2a225b3bbcf0b641 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 15:48:39 +0200 Subject: [PATCH 39/72] Fix semanticdb files being generated alongside sources in build When building Scala 3 stuff --- .gitignore | 1 - project/settings.sc | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index bddcd47ee3..92c15156dc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,5 @@ out/ .vscode/ .idea/ -*.semanticdb .bsp .scala-build \ No newline at end of file diff --git a/project/settings.sc b/project/settings.sc index c5d0ac4ba1..b32068163d 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -789,12 +789,20 @@ trait ScalaCliScalafixModule extends ScalafixModule with ScalaCliCompile { val sourceRoot = sourceFiles.find(_.last == "scala") .orElse(sourceFiles.headOption) .getOrElse(millSourcePath) + val parentOptions = { + val l = super.scalacOptions() + if (isScala2) l.filterNot(_.startsWith("-P:semanticdb:sourceroot:")) + else { + val len = l.length + val idx = l.indexWhere(_.startsWith("-sourceroot")) + if (idx < len - 1) l.take(idx) ++ l.drop(idx + 2) + else l + } + } val semDbOptions = if (isScala2) Seq(s"-P:semanticdb:sourceroot:$sourceRoot") - // Once we switch to Scala CLI 0.1.4, change this to - // else Seq(s"-sourceroot", sourceRoot.toString) - else Nil - super.scalacOptions() ++ semDbOptions + else Seq(s"-sourceroot", sourceRoot.toString) + parentOptions ++ semDbOptions } } From 4753613240ad58488f131550ab13cb547b68f6a0 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 15:10:08 +0200 Subject: [PATCH 40/72] Fix error when Bloop version check fails --- .../src/main/scala/scala/build/bloop/BloopServer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala b/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala index 1168dfa67a..d7ffc3340b 100644 --- a/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala +++ b/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala @@ -111,7 +111,7 @@ object BloopServer { } BloopRifle.getCurrentBloopVersion(config, logger, workdir, startServerChecksPool) - .fold(e => throw new RuntimeException(s"Fatal error, could not spawn Bloop: $e"), identity) + .fold(e => throw new RuntimeException(s"Fatal error, could not spawn Bloop: ${e.message}"), identity) } private def connect( From d06d993393ee1e6945fee4c5d9c9b42fdaef4926 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 15:37:15 +0200 Subject: [PATCH 41/72] NIT Refacto in bloop-rifle --- .../scala/scala/build/bloop/BloopServer.scala | 14 +++---- .../scala/build/blooprifle/BloopRifle.scala | 42 +++++++++---------- .../scala/build/blooprifle/VersionUtil.scala | 4 +- .../scala/build/blooprifle/ParsingTests.scala | 6 +-- 4 files changed, 32 insertions(+), 34 deletions(-) diff --git a/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala b/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala index d7ffc3340b..d7f87ad2b4 100644 --- a/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala +++ b/modules/bloop-rifle/src/main/scala/scala/build/bloop/BloopServer.scala @@ -22,7 +22,7 @@ trait BloopServer { def shutdown(): Unit - def bloopInfo: BloopServerRuntimeInfo + def bloopInfo: BloopRifle.BloopServerRuntimeInfo } object BloopServer { @@ -30,7 +30,7 @@ object BloopServer { server: BuildServer, listeningFuture: JFuture[Void], socket: Socket, - bloopInfo: BloopServerRuntimeInfo + bloopInfo: BloopRifle.BloopServerRuntimeInfo ) extends BloopServer { def shutdown(): Unit = { // Close the jsonrpc thread listening to input messages @@ -47,7 +47,7 @@ object BloopServer { ) private def resolveBloopInfo( - bloopInfo: BloopServerRuntimeInfo, + bloopInfo: BloopRifle.BloopServerRuntimeInfo, config: BloopRifleConfig ): ResolvedBloopParameters = { val bloopV: BloopVersion = config.retainedBloopVersion match { @@ -66,7 +66,7 @@ object BloopServer { config: BloopRifleConfig, startServerChecksPool: ScheduledExecutorService, logger: BloopRifleLogger - ): BloopServerRuntimeInfo = { + ): BloopRifle.BloopServerRuntimeInfo = { val workdir = new File(".").getCanonicalFile.toPath def startBloop(bloopVersion: String, bloopJava: String) = { logger.info("Starting compilation server") @@ -89,8 +89,8 @@ object BloopServer { bloopInfo match { case Left(error) => error match { - case BloopNotRunning => - case ParsingFailed(bloopAboutOutput) => + case BloopRifle.BloopNotRunning => + case BloopRifle.ParsingFailed(bloopAboutOutput) => logger.info(s"Failed to parse output of 'bloop about':\n$bloopAboutOutput") } ResolvedBloopParameters( @@ -149,7 +149,7 @@ object BloopServer { logger: BloopRifleLogger, period: FiniteDuration, timeout: FiniteDuration - ): (BspConnection, Socket, BloopServerRuntimeInfo) = { + ): (BspConnection, Socket, BloopRifle.BloopServerRuntimeInfo) = { val bloopInfo = ensureBloopRunning(config, threads.startServerChecks, logger) diff --git a/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/BloopRifle.scala b/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/BloopRifle.scala index f78b11162c..3bb0b278a3 100644 --- a/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/BloopRifle.scala +++ b/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/BloopRifle.scala @@ -164,7 +164,7 @@ object BloopRifle { val isRunning = BloopRifle.check(config, logger) if (isRunning) { - val bufferedOStream = new ByteArrayOutputStream(100000) + val bufferedOStream = new ByteArrayOutputStream Operations.about( config.address, workdir, @@ -175,31 +175,29 @@ object BloopRifle { scheduler ) val bloopAboutOutput = new String(bufferedOStream.toByteArray) - VersionUtil.parseBloopAbout(bloopAboutOutput) match { - case Some(value) => Right(value) - case None => Left(ParsingFailed(bloopAboutOutput)) - } + VersionUtil.parseBloopAbout(bloopAboutOutput) + .toRight(ParsingFailed(bloopAboutOutput)) } else Left(BloopNotRunning) } -} -sealed abstract class BloopAboutFailure extends Product with Serializable { - def message: String -} -case object BloopNotRunning extends BloopAboutFailure { - def message = "not running" -} -case class ParsingFailed(bloopAboutOutput: String) extends BloopAboutFailure { - def message = s"failed to parse output: '$bloopAboutOutput'" -} + sealed abstract class BloopAboutFailure extends Product with Serializable { + def message: String + } + case object BloopNotRunning extends BloopAboutFailure { + def message = "not running" + } + final case class ParsingFailed(bloopAboutOutput: String) extends BloopAboutFailure { + def message = s"failed to parse output: '$bloopAboutOutput'" + } -case class BloopServerRuntimeInfo( - bloopVersion: BloopVersion, - jvmVersion: Int, - javaHome: String -) { - def message: String = - s"version $bloopVersion, JVM $jvmVersion under $javaHome" + final case class BloopServerRuntimeInfo( + bloopVersion: BloopVersion, + jvmVersion: Int, + javaHome: String + ) { + def message: String = + s"version $bloopVersion, JVM $jvmVersion under $javaHome" + } } diff --git a/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/VersionUtil.scala b/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/VersionUtil.scala index 77dea6c9f8..5b27d42f1f 100644 --- a/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/VersionUtil.scala +++ b/modules/bloop-rifle/src/main/scala/scala/build/blooprifle/VersionUtil.scala @@ -27,7 +27,7 @@ object VersionUtil { versionInt <- jvmRelease(versionNumberGroup) } yield versionInt - def parseBloopAbout(stdoutFromBloopAbout: String): Option[BloopServerRuntimeInfo] = { + def parseBloopAbout(stdoutFromBloopAbout: String): Option[BloopRifle.BloopServerRuntimeInfo] = { val bloopVersionRegex = "bloop v(.*)\\s".r val bloopJvmRegex = "Running on Java ... v([0-9._A-Za-z]+) [(](.*)[)]".r @@ -37,7 +37,7 @@ object VersionUtil { bloopJvmVersion <- bloopJvmRegex.findFirstMatchIn(stdoutFromBloopAbout).map(_.group(1)) javaHome <- bloopJvmRegex.findFirstMatchIn(stdoutFromBloopAbout).map(_.group(2)) jvmRelease <- VersionUtil.jvmRelease(bloopJvmVersion) - } yield BloopServerRuntimeInfo( + } yield BloopRifle.BloopServerRuntimeInfo( bloopVersion = BloopVersion(bloopVersion), jvmVersion = jvmRelease, javaHome = javaHome diff --git a/modules/bloop-rifle/src/test/scala/scala/build/blooprifle/ParsingTests.scala b/modules/bloop-rifle/src/test/scala/scala/build/blooprifle/ParsingTests.scala index 4e8bc63a03..eb446b0fde 100644 --- a/modules/bloop-rifle/src/test/scala/scala/build/blooprifle/ParsingTests.scala +++ b/modules/bloop-rifle/src/test/scala/scala/build/blooprifle/ParsingTests.scala @@ -8,7 +8,7 @@ class ParsingTests extends munit.FunSuite { implicit class BV(s: String) { implicit def v: BloopVersion = BloopVersion(s) - implicit def p: Option[BloopServerRuntimeInfo] = parseBloopAbout(s) + implicit def p: Option[BloopRifle.BloopServerRuntimeInfo] = parseBloopAbout(s) implicit def j: Option[Int] = jvmRelease(s) implicit def jv: Option[Int] = parseJavaVersion(s) } @@ -57,7 +57,7 @@ class ParsingTests extends munit.FunSuite { |Maintained by the Scala Center and the community.""".stripMargin test("parse jre bloop about") { - expect(jreBloopOutput.p == Some(BloopServerRuntimeInfo( + expect(jreBloopOutput.p == Some(BloopRifle.BloopServerRuntimeInfo( BloopVersion("1.4.11"), 11, "/usr/local/openjdk-11" @@ -65,7 +65,7 @@ class ParsingTests extends munit.FunSuite { } test("parse jdk bloop about") { - expect(jdkBloopOutput.p == Some(BloopServerRuntimeInfo( + expect(jdkBloopOutput.p == Some(BloopRifle.BloopServerRuntimeInfo( BloopVersion("1.4.11"), 16, "/usr/lib/jvm/java-16-openjdk-amd64" From d7d3436b66a80560abd24679d6f32f5cb9ef7070 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 15:39:53 +0200 Subject: [PATCH 42/72] Fix './mill __.compile' --- .github/workflows/ci.yml | 2 ++ project/deps.sc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12fb6ecb2c..2469688ae0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,8 @@ jobs: path: artifacts/ if-no-files-found: error retention-days: 2 + - name: Compile everything + run: ./mill -i __.compile - name: Unit tests run: | ./mill -i unitTests diff --git a/project/deps.sc b/project/deps.sc index 77c59866bc..eb8b48e34f 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -96,7 +96,7 @@ object Deps { def scala3Compiler(sv: String) = ivy"org.scala-lang::scala3-compiler:$sv" def scalaAsync = ivy"org.scala-lang.modules::scala-async:1.0.1".exclude("*" -> "*") def scalac(sv: String) = ivy"org.scala-lang:scala-compiler:$sv" - def scalafmtCli = ivy"org.scalameta::scalafmt-cli:3.5.1" + def scalafmtCli = ivy"org.scalameta:scalafmt-cli_2.13:3.5.1" // Force using of 2.13 - is there a better way? def scalaJsEnvJsdomNodejs = ivy"org.scala-js:scalajs-env-jsdom-nodejs_2.13:1.1.0" From 44dc360375b46c21bca05027785c8ddaf0ce0327 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 26 Apr 2022 09:42:57 +0200 Subject: [PATCH 43/72] Update jsoniter-scala-core, ... to 2.13.18 (#939) --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index eb8b48e34f..1fcd0bb8be 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -53,7 +53,7 @@ object Deps { object Versions { // jni-utils version may need to be sync-ed when bumping the coursier version def coursier = "2.1.0-M5-18-gfebf9838c" - def jsoniterScala = "2.13.17" + def jsoniterScala = "2.13.18" def scalaMeta = "4.5.4" def scalaNative = "0.4.4" def scalaPackager = "0.1.26" From 76d517435016e31c10b0b05e37be22d35ca3a1ed Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Mon, 25 Apr 2022 11:04:34 +0200 Subject: [PATCH 44/72] Ensure a main class element is possible to find in Scala code bassed by stdin --- .../main/scala/scala/build/CrossSources.scala | 5 ++++- .../src/main/scala/scala/build/Inputs.scala | 5 +++-- .../preprocessing/ScalaPreprocessor.scala | 19 ++++++++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/CrossSources.scala b/modules/build/src/main/scala/scala/build/CrossSources.scala index 0b8590e7c7..906183671f 100644 --- a/modules/build/src/main/scala/scala/build/CrossSources.scala +++ b/modules/build/src/main/scala/scala/build/CrossSources.scala @@ -133,7 +133,10 @@ object CrossSources { } val mainClassOpt = for { - mainClassPath <- inputs.mainClassElement.map(_.path).map(ScopePath.fromPath(_).path) + mainClassPath <- inputs.mainClassElement.map { + case sf: Inputs.SourceFile => ScopePath.fromPath(sf.path).path + case vsf: Inputs.VirtualScalaFile => vsf.scopePath.path + } processedMainClass <- preprocessedSources.find(_.scopePath.path == mainClassPath) mainClass <- processedMainClass.mainClassOpt } yield mainClass diff --git a/modules/build/src/main/scala/scala/build/Inputs.scala b/modules/build/src/main/scala/scala/build/Inputs.scala index e8d373f527..143b508209 100644 --- a/modules/build/src/main/scala/scala/build/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/Inputs.scala @@ -16,7 +16,7 @@ import scala.util.matching.Regex final case class Inputs( elements: Seq[Inputs.Element], - mainClassElement: Option[Inputs.SourceFile], + mainClassElement: Option[Inputs.SingleElement], workspace: os.Path, baseProjectName: String, mayAppendHash: Boolean, @@ -281,7 +281,8 @@ object Inputs { } val mainClassElemOpt = validElems .collectFirst { - case f: SourceFile => f + case f: SourceFile => f + case vsf: VirtualScalaFile => vsf } Inputs( updatedElems, diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala index c9130c5d0c..24d8143deb 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala @@ -114,6 +114,11 @@ case object ScalaPreprocessor extends Preprocessor { case v: Inputs.VirtualScalaFile => val res = either { + val relPath = os.sub / "stdin.scala" + val className = { + val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(relPath) + (pkg :+ wrapper).map(_.raw).mkString(".") + } val content = new String(v.content, StandardCharsets.UTF_8) val (requirements, scopedRequirements, options, updatedContentOpt) = value( @@ -123,15 +128,15 @@ case object ScalaPreprocessor extends Preprocessor { (reqs, scopedReqs, opts, updatedContent) }.getOrElse((BuildRequirements(), Nil, BuildOptions(), None)) val s = PreprocessedSource.InMemory( - Left(v.source), - v.subPath, + originalPath = Left(v.source), + relPath = relPath, updatedContentOpt.getOrElse(content), - 0, - Some(options), - Some(requirements), + ignoreLen = 0, + options = Some(options), + requirements = Some(requirements), scopedRequirements, - None, - v.scopePath + mainClassOpt = Some(className), + scopePath = v.scopePath ) Seq(s) } From e3de7cc6dc2b649a11661e222dc49fbcec901394 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Mon, 25 Apr 2022 11:05:14 +0200 Subject: [PATCH 45/72] Add integration tests for Scala code passed by stdin --- .../cli/integration/RunTestDefinitions.scala | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index 7c856b389b..c6b05cab9c 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -718,6 +718,32 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) expect(output == message) } } + test("Scala code accepted as piped input") { + val expectedOutput = "Hello" + val pipedInput = s"object Test extends App { println(\"$expectedOutput\") }" + emptyInputs.fromRoot { root => + val output = os.proc(TestUtil.cli, "_.scala", extraOptions) + .call(cwd = root, stdin = pipedInput) + .out.text().trim + expect(output == expectedOutput) + } + } + test("Scala code with references to existing files accepted as piped input") { + val expectedOutput = "Hello" + val pipedInput = + s"""object Test extends App { + | val data = SomeData(value = "$expectedOutput") + | println(data.value) + |}""".stripMargin + val inputs = + TestInputs(Seq(os.rel / "SomeData.scala" -> "case class SomeData(value: String)")) + inputs.fromRoot { root => + val output = os.proc(TestUtil.cli, ".", "_.scala", extraOptions) + .call(cwd = root, stdin = pipedInput) + .out.text().trim + expect(output == expectedOutput) + } + } } def fd(): Unit = { From 4a6c6d29fd0c8252c269525ed3704a6bd855ca7b Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Mon, 25 Apr 2022 13:36:28 +0200 Subject: [PATCH 46/72] prevent `mill` command from devouring stdin input in scala-cli-src script --- scala-cli-src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scala-cli-src b/scala-cli-src index bee6479901..5186ffd76c 100755 --- a/scala-cli-src +++ b/scala-cli-src @@ -1,5 +1,5 @@ #!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" -LAUNCHER="$(cd "$SCRIPT_DIR" && ./mill show cli.launcher | jq -r . | sed 's/ref:[a-z0-9]*://')" +LAUNCHER="$(cd "$SCRIPT_DIR" && ./mill show cli.launcher Date: Mon, 25 Apr 2022 15:35:43 +0200 Subject: [PATCH 47/72] Fix running of zip files containing multiple Scala source files and add a relevant integration test --- .../src/main/scala/scala/build/Inputs.scala | 2 +- .../preprocessing/ScalaPreprocessor.scala | 2 +- .../cli/integration/RunTestDefinitions.scala | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Inputs.scala b/modules/build/src/main/scala/scala/build/Inputs.scala index 143b508209..fed25fc76b 100644 --- a/modules/build/src/main/scala/scala/build/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/Inputs.scala @@ -204,7 +204,7 @@ object Inputs { final case class VirtualScript(content: Array[Byte], source: String, wrapperPath: os.SubPath) extends Virtual with AnyScalaFile with AnyScript final case class VirtualScalaFile(content: Array[Byte], source: String) - extends Virtual with AnyScalaFile + extends Virtual with AnyScalaFile { def isStdin: Boolean = source == "" } final case class VirtualJavaFile(content: Array[Byte], source: String) extends Virtual with Compiled final case class VirtualData(content: Array[Byte], source: String) diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala index 24d8143deb..b7577406e8 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala @@ -114,7 +114,7 @@ case object ScalaPreprocessor extends Preprocessor { case v: Inputs.VirtualScalaFile => val res = either { - val relPath = os.sub / "stdin.scala" + val relPath = if (v.isStdin) os.sub / "stdin.scala" else v.subPath val className = { val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(relPath) (pkg :+ wrapper).map(_.raw).mkString(".") diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index c6b05cab9c..e35cb3716b 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -813,6 +813,30 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } + test("Zip with multiple Scala files") { + val inputs = TestInputs( + Seq( + os.rel / "Hello.scala" -> + s"""object Hello extends App { + | println(Messages.hello) + |} + |""".stripMargin, + os.rel / "Messages.scala" -> + s"""object Messages { + | def hello: String = "Hello" + |} + |""".stripMargin + ) + ) + inputs.asZip { (root, zipPath) => + val message = "Hello" + val output = os.proc(TestUtil.cli, extraOptions, zipPath.toString) + .call(cwd = root) + .out.text().trim + expect(output == message) + } + } + test("Zip with Scala containing resource directive") { val inputs = TestInputs( Seq( From 7cebce4926b08381c9790f8d397da46967482ab3 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Mon, 25 Apr 2022 11:17:49 +0200 Subject: [PATCH 48/72] NIT Refactor - add missing type declarations - remove redundant braces - other small tweaks --- .../preprocessing/ScalaPreprocessor.scala | 6 ++--- .../cli/integration/RunTestDefinitions.scala | 26 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala index b7577406e8..56831acf53 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala @@ -41,7 +41,7 @@ case object ScalaPreprocessor extends Preprocessor { updatedContent: Option[String] ) - val usingDirectiveHandlers = Seq( + val usingDirectiveHandlers: Seq[UsingDirectiveHandler] = Seq( UsingDependencyDirectiveHandler, UsingScalaVersionDirectiveHandler, UsingRepositoryDirectiveHandler, @@ -60,7 +60,7 @@ case object ScalaPreprocessor extends Preprocessor { UsingPublishDirectiveHandler ) - val requireDirectiveHandlers = Seq[RequireDirectiveHandler]( + val requireDirectiveHandlers: Seq[RequireDirectiveHandler] = Seq( RequireScalaVersionDirectiveHandler, RequirePlatformsDirectiveHandler, RequireScopeDirectiveHandler @@ -240,7 +240,7 @@ case object ScalaPreprocessor extends Preprocessor { // for standard imports. val buf = content.toCharArray for (t <- dependencyTrees) { - val substitute = (t.prefix(0) + ".A").padTo(t.end - t.start, ' ') + val substitute = (t.prefix.head + ".A").padTo(t.end - t.start, ' ') assert(substitute.length == (t.end - t.start)) System.arraycopy(substitute.toArray, 0, buf, t.start, substitute.length) } diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index e35cb3716b..26ea65a1d5 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -35,7 +35,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) // warm-up run that downloads compiler bridges // The "Downloading compiler-bridge (from bloop?) pollute the output, and would make the first test fail. - lazy val warmupTest = { + lazy val warmupTest: Unit = { System.err.println("Running RunTests warmup test…") simpleScriptTest(ignoreErrors = true) System.err.println("Done running RunTests warmup test.") @@ -95,9 +95,9 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } - def platformNl = if (Properties.isWin) "\\r\\n" else "\\n" + def platformNl: String = if (Properties.isWin) "\\r\\n" else "\\n" - def canRunScWithNative(): Boolean = + def canRunScWithNative: Boolean = !(actualScalaVersion.startsWith("2.12") || actualScalaVersion.startsWith("3.0")) def simpleNativeTests(): Unit = { @@ -124,7 +124,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } - if (canRunScWithNative()) + if (canRunScWithNative) test("simple script native") { simpleNativeTests() } @@ -289,7 +289,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } - if (canRunScWithNative()) + if (canRunScWithNative) test("Multiple scripts native") { multipleScriptsNative() } @@ -618,7 +618,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) exceptionLines.length == expectedLines.length, clues(output, exceptionLines.length, expectedLines.length) ) - for (i <- 0 until exceptionLines.length) + for (i <- exceptionLines.indices) assert( exceptionLines(i) == expectedLines(i), clues(output, exceptionLines(i), expectedLines(i)) @@ -675,7 +675,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) exceptionLines.length == expectedLines.length, clues(output, exceptionLines.length, expectedLines.length) ) - for (i <- 0 until exceptionLines.length) + for (i <- exceptionLines.indices) assert( exceptionLines(i) == expectedLines(i), clues(output, exceptionLines(i), expectedLines(i)) @@ -688,11 +688,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) scriptStackTraceScala3() } - val emptyInputs = TestInputs( - Seq( - os.rel / ".placeholder" -> "" - ) - ) + val emptyInputs: TestInputs = TestInputs(Seq(os.rel / ".placeholder" -> "")) def piping(): Unit = { emptyInputs.fromRoot { root => @@ -1065,7 +1061,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) // format: off val cmd = Seq[os.Shellable]( "docker", "run", "--rm", termOpt, - "-v", s"${root}:/data", + "-v", s"$root:/data", "-w", "/data", ciOpt, baseImage, @@ -1500,7 +1496,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) // format: off val cmd = Seq[os.Shellable]( "docker", "run", "--rm", termOpt, - "-v", s"${root}:/data", + "-v", s"$root:/data", "-w", "/data", ciOpt, baseImage, @@ -1585,7 +1581,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } - def runAuthProxyTest = + def runAuthProxyTest: Boolean = Properties.isLinux || (Properties.isMac && !TestUtil.isCI) if (runAuthProxyTest) test("auth proxy") { From bed324ea6c5ce743e5bf7907aecd80c26e1397c2 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Tue, 26 Apr 2022 12:53:43 +0200 Subject: [PATCH 49/72] Refactor sources default main class override - it is only to be used by scripts - rename relevant fieds to imply the purpose better --- .../src/main/scala/scala/build/Build.scala | 2 +- .../main/scala/scala/build/CrossSources.scala | 13 ++++------ .../src/main/scala/scala/build/Inputs.scala | 24 ++++++++----------- .../scala/scala/build/ScopedSources.scala | 4 ++-- .../src/main/scala/scala/build/Sources.scala | 2 +- .../preprocessing/ScalaPreprocessor.scala | 18 ++++---------- .../scala/scala/cli/commands/Package.scala | 2 +- .../scala/scala/cli/packaging/Library.scala | 2 +- 8 files changed, 26 insertions(+), 41 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index 36ecc03dfb..2ea51d181e 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -57,7 +57,7 @@ object Build { MainClass.find(output) def retainedMainClass: Either[MainClassError, String] = { lazy val foundMainClasses0 = foundMainClasses() - val defaultMainClassOpt = sources.mainClass + val defaultMainClassOpt = sources.defaultMainClass .filter(name => foundMainClasses0.contains(name)) def foundMainClass = if (foundMainClasses0.isEmpty) Left(new NoMainClassFoundError) diff --git a/modules/build/src/main/scala/scala/build/CrossSources.scala b/modules/build/src/main/scala/scala/build/CrossSources.scala index 906183671f..2c1ba38993 100644 --- a/modules/build/src/main/scala/scala/build/CrossSources.scala +++ b/modules/build/src/main/scala/scala/build/CrossSources.scala @@ -9,7 +9,7 @@ import scala.build.preprocessing._ final case class CrossSources( paths: Seq[HasBuildRequirements[(os.Path, os.RelPath)]], inMemory: Seq[HasBuildRequirements[Sources.InMemory]], - mainClass: Option[String], + defaultMainClass: Option[String], resourceDirs: Seq[HasBuildRequirements[os.Path]], buildOptions: Seq[HasBuildRequirements[BuildOptions]] ) { @@ -49,7 +49,7 @@ final case class CrossSources( .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq) .flatMap(_.withPlatform(platform.value).toSeq) .map(_.scopedValue(defaultScope)), - mainClass, + defaultMainClass, resourceDirs .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq) .flatMap(_.withPlatform(platform.value).toSeq) @@ -132,11 +132,8 @@ object CrossSources { ) } - val mainClassOpt = for { - mainClassPath <- inputs.mainClassElement.map { - case sf: Inputs.SourceFile => ScopePath.fromPath(sf.path).path - case vsf: Inputs.VirtualScalaFile => vsf.scopePath.path - } + val defaultMainClassOpt = for { + mainClassPath <- inputs.defaultMainClassElement.map(s => ScopePath.fromPath(s.path).path) processedMainClass <- preprocessedSources.find(_.scopePath.path == mainClassPath) mainClass <- processedMainClass.mainClassOpt } yield mainClass @@ -165,6 +162,6 @@ object CrossSources { HasBuildRequirements(BuildRequirements(), _) ) - CrossSources(paths, inMemory, mainClassOpt, resourceDirs, buildOptions) + CrossSources(paths, inMemory, defaultMainClassOpt, resourceDirs, buildOptions) } } diff --git a/modules/build/src/main/scala/scala/build/Inputs.scala b/modules/build/src/main/scala/scala/build/Inputs.scala index fed25fc76b..debaac2aa8 100644 --- a/modules/build/src/main/scala/scala/build/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/Inputs.scala @@ -16,7 +16,7 @@ import scala.util.matching.Regex final case class Inputs( elements: Seq[Inputs.Element], - mainClassElement: Option[Inputs.SingleElement], + defaultMainClassElement: Option[Inputs.Script], workspace: os.Path, baseProjectName: String, mayAppendHash: Boolean, @@ -279,14 +279,11 @@ object Inputs { case _: ResourceDirectory => true case _: Virtual => true } - val mainClassElemOpt = validElems - .collectFirst { - case f: SourceFile => f - case vsf: VirtualScalaFile => vsf - } + // only on-disk scripts need a main class override + val defaultMainClassElemOpt = validElems.collectFirst { case script: Script => script } Inputs( updatedElems, - mainClassElemOpt, + defaultMainClassElemOpt, workspace, baseProjectName, mayAppendHash = needsHash, @@ -297,15 +294,14 @@ object Inputs { private val githubGistsArchiveRegex: Regex = s""":\\/\\/gist\\.github\\.com\\/[^\\/]*?\\/[^\\/]*$$""".r - private def resolve(path: String, content: Array[Byte]): Element = { - val wrapperPath = - os.sub / path.split("/").last - + private def resolve(path: String, content: Array[Byte]): Element = if (path.endsWith(".scala")) VirtualScalaFile(content, path) else if (path.endsWith(".java")) VirtualJavaFile(content, path) - else if (path.endsWith(".sc")) VirtualScript(content, path, wrapperPath) + else if (path.endsWith(".sc")) { + val wrapperPath = os.sub / path.split("/").last + VirtualScript(content, path, wrapperPath) + } else VirtualData(content, path) - } private def resolveZipArchive(content: Array[Byte]): Seq[Element] = { val zipInputStream = new ZipInputStream(new ByteArrayInputStream(content)) @@ -429,7 +425,7 @@ object Inputs { def empty(workspace: os.Path): Inputs = Inputs( elements = Nil, - mainClassElement = None, + defaultMainClassElement = None, workspace = workspace, baseProjectName = "project", mayAppendHash = true, diff --git a/modules/build/src/main/scala/scala/build/ScopedSources.scala b/modules/build/src/main/scala/scala/build/ScopedSources.scala index 676cdadbae..07d8769983 100644 --- a/modules/build/src/main/scala/scala/build/ScopedSources.scala +++ b/modules/build/src/main/scala/scala/build/ScopedSources.scala @@ -5,7 +5,7 @@ import scala.build.options.{BuildOptions, HasScope, Scope} final case class ScopedSources( paths: Seq[HasScope[(os.Path, os.RelPath)]], inMemory: Seq[HasScope[Sources.InMemory]], - mainClass: Option[String], + defaultMainClass: Option[String], resourceDirs: Seq[HasScope[os.Path]], buildOptions: Seq[HasScope[BuildOptions]] ) { @@ -13,7 +13,7 @@ final case class ScopedSources( Sources( paths.flatMap(_.valueFor(scope).toSeq), inMemory.flatMap(_.valueFor(scope).toSeq), - mainClass, + defaultMainClass, resourceDirs.flatMap(_.valueFor(scope).toSeq), buildOptions .flatMap(_.valueFor(scope).toSeq) diff --git a/modules/build/src/main/scala/scala/build/Sources.scala b/modules/build/src/main/scala/scala/build/Sources.scala index 6a617679fc..0db3d9f6ad 100644 --- a/modules/build/src/main/scala/scala/build/Sources.scala +++ b/modules/build/src/main/scala/scala/build/Sources.scala @@ -7,7 +7,7 @@ import scala.build.preprocessing._ final case class Sources( paths: Seq[(os.Path, os.RelPath)], inMemory: Seq[Sources.InMemory], - mainClass: Option[String], + defaultMainClass: Option[String], resourceDirs: Seq[os.Path], buildOptions: BuildOptions ) { diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala index 56831acf53..ebe832347a 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala @@ -9,7 +9,7 @@ import java.nio.charset.StandardCharsets import scala.build.EitherCps.{either, value} import scala.build.Ops._ import scala.build.errors._ -import scala.build.internal.{AmmUtil, Util} +import scala.build.internal.Util import scala.build.options.{BuildOptions, BuildRequirements, ClassPathOptions, ShadowingSeq} import scala.build.preprocessing.directives._ import scala.build.{Inputs, Logger, Position, Positioned} @@ -72,16 +72,12 @@ case object ScalaPreprocessor extends Preprocessor { ): Option[Either[BuildException, Seq[PreprocessedSource]]] = input match { case f: Inputs.ScalaFile => - val inferredClsName = { - val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(f.subPath) - (pkg :+ wrapper).map(_.raw).mkString(".") - } val res = either { val content = value(PreprocessingUtil.maybeRead(f.path)) val scopePath = ScopePath.fromPath(f.path) val source = value(process(content, Right(f.path), scopePath / os.up, logger)) match { case None => - PreprocessedSource.OnDisk(f.path, None, None, Nil, Some(inferredClsName)) + PreprocessedSource.OnDisk(f.path, None, None, Nil, None) case Some(ProcessingOutput( requirements, scopedRequirements, @@ -96,7 +92,7 @@ case object ScalaPreprocessor extends Preprocessor { Some(options), Some(requirements), scopedRequirements, - Some(inferredClsName), + None, scopePath ) case Some(ProcessingOutput(requirements, scopedRequirements, options, None)) => @@ -105,7 +101,7 @@ case object ScalaPreprocessor extends Preprocessor { Some(options), Some(requirements), scopedRequirements, - Some(inferredClsName) + None ) } Seq(source) @@ -115,10 +111,6 @@ case object ScalaPreprocessor extends Preprocessor { case v: Inputs.VirtualScalaFile => val res = either { val relPath = if (v.isStdin) os.sub / "stdin.scala" else v.subPath - val className = { - val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(relPath) - (pkg :+ wrapper).map(_.raw).mkString(".") - } val content = new String(v.content, StandardCharsets.UTF_8) val (requirements, scopedRequirements, options, updatedContentOpt) = value( @@ -135,7 +127,7 @@ case object ScalaPreprocessor extends Preprocessor { options = Some(options), requirements = Some(requirements), scopedRequirements, - mainClassOpt = Some(className), + mainClassOpt = None, scopePath = v.scopePath ) Seq(s) diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index 744897e8e6..1113277838 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -183,7 +183,7 @@ object Package extends ScalaCommand[PackageOptions] { val dest = outputOpt .orElse { - build.sources.mainClass + build.sources.defaultMainClass .map(n => n.drop(n.lastIndexOf('.') + 1)) .map(_.stripSuffix("_sc")) .map(_ + extension) diff --git a/modules/cli/src/main/scala/scala/cli/packaging/Library.scala b/modules/cli/src/main/scala/scala/cli/packaging/Library.scala index 955f4f4d88..ce92a138f0 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/Library.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/Library.scala @@ -33,7 +33,7 @@ object Library { manifest.getMainAttributes.put(JarAttributes.Name.MANIFEST_VERSION, "1.0") if (hasActualManifest) - for (mainClass <- mainClassOpt.orElse(build.sources.mainClass) if mainClass.nonEmpty) + for (mainClass <- mainClassOpt.orElse(build.sources.defaultMainClass) if mainClass.nonEmpty) manifest.getMainAttributes.put(JarAttributes.Name.MAIN_CLASS, mainClass) var zos: ZipOutputStream = null From 38fd674144b48f347b8b508fe5611a1275e03c19 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Tue, 26 Apr 2022 13:41:40 +0200 Subject: [PATCH 50/72] NIT Refactor - remove redundant braces - apply Scala 3 syntax where applicable - remove redundant conversions - simplify transformations - fill-in missing type declarations - explicit annotate @tailrec functions - remove redundant regex escapes --- .../src/main/scala/scala/build/Build.scala | 20 +++++++------- .../main/scala/scala/build/CrossSources.scala | 4 +-- .../src/main/scala/scala/build/Inputs.scala | 27 +++++++++---------- .../src/main/scala/scala/build/Sources.scala | 2 +- .../build/internal/NativeBuilderHelper.scala | 2 +- .../preprocessing/ScalaPreprocessor.scala | 12 ++++----- .../scala/scala/cli/commands/Package.scala | 6 ++--- 7 files changed, 37 insertions(+), 36 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index 2ea51d181e..0d444a34b6 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -9,14 +9,15 @@ import java.io.File import java.nio.file.{FileSystemException, Path} import java.util.concurrent.{ScheduledExecutorService, ScheduledFuture} +import scala.annotation.tailrec import scala.build.EitherCps.{either, value} -import scala.build.Ops._ +import scala.build.Ops.* import scala.build.compiler.{ScalaCompiler, ScalaCompilerMaker} -import scala.build.errors._ +import scala.build.errors.* import scala.build.internal.{Constants, CustomCodeWrapper, MainClass, Util} -import scala.build.options._ +import scala.build.options.* import scala.build.options.validation.ValidationException -import scala.build.postprocessing._ +import scala.build.postprocessing.* import scala.collection.mutable.ListBuffer import scala.concurrent.duration.DurationInt import scala.util.Properties @@ -329,7 +330,7 @@ object Build { builds } - private def copyResourceToClassesDir(build: Build) = build match { + private def copyResourceToClassesDir(build: Build): Unit = build match { case b: Build.Successful => for { resourceDirPath <- b.sources.resourceDirs.filter(os.exists(_)) @@ -502,7 +503,7 @@ object Build { logger: Logger, options: BuildOptions ): Either[BuildException, Unit] = { - val (errors, otherDiagnostics) = options.validate.toSeq.partition(_.severity == Severity.Error) + val (errors, otherDiagnostics) = options.validate.partition(_.severity == Severity.Error) logger.log(otherDiagnostics) if (errors.nonEmpty) Left(CompositeBuildException(errors.map(new ValidationException(_)))) @@ -541,7 +542,7 @@ object Build { logger )) - def run() = { + def run(): Unit = { try { val res = build( inputs, @@ -940,6 +941,7 @@ object Build { def onError(t: Throwable): Unit = { // TODO Log that properly System.err.println("got error:") + @tailrec def printEx(t: Throwable): Unit = if (t != null) { System.err.println(t) @@ -973,7 +975,7 @@ object Build { } private val lock = new Object - private var f: ScheduledFuture[_] = null + private var f: ScheduledFuture[?] = _ private val waitFor = 50.millis private val runnable: Runnable = { () => lock.synchronized { @@ -1080,7 +1082,7 @@ object Build { command.iterator.map(_ + System.lineSeparator()).mkString ) - new ProcessBuilder(command: _*) + new ProcessBuilder(command*) .inheritIO() .start() .waitFor() diff --git a/modules/build/src/main/scala/scala/build/CrossSources.scala b/modules/build/src/main/scala/scala/build/CrossSources.scala index 2c1ba38993..1970b53097 100644 --- a/modules/build/src/main/scala/scala/build/CrossSources.scala +++ b/modules/build/src/main/scala/scala/build/CrossSources.scala @@ -1,10 +1,10 @@ package scala.build import scala.build.EitherCps.{either, value} -import scala.build.Ops._ +import scala.build.Ops.* import scala.build.errors.{BuildException, CompositeBuildException} import scala.build.options.{BuildOptions, BuildRequirements, HasBuildRequirements, Scope} -import scala.build.preprocessing._ +import scala.build.preprocessing.* final case class CrossSources( paths: Seq[HasBuildRequirements[(os.Path, os.RelPath)]], diff --git a/modules/build/src/main/scala/scala/build/Inputs.scala b/modules/build/src/main/scala/scala/build/Inputs.scala index debaac2aa8..820f8122c0 100644 --- a/modules/build/src/main/scala/scala/build/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/Inputs.scala @@ -57,7 +57,7 @@ final case class Inputs( private lazy val inputsHash: String = Inputs.inputsHash(elements) - lazy val projectName = { + lazy val projectName: String = { val needsSuffix = mayAppendHash && (elements match { case Seq(d: Inputs.Directory) => d.path != workspace case _ => true @@ -87,19 +87,18 @@ final case class Inputs( if (forbidden.exists(workspace.startsWith)) inHomeDir(directories) else this def checkAttributes(directories: Directories): Inputs = { + @tailrec def existingParent(p: os.Path): Option[os.Path] = if (os.exists(p)) Some(p) else if (p.segmentCount <= 0) None else existingParent(p / os.up) def reallyOwnedByUser(p: os.Path): Boolean = if (Properties.isWin) - p.toIO.canWrite() // Wondering if there's a better way to do that… + p.toIO.canWrite // Wondering if there's a better way to do that… else os.owner(p) == os.owner(os.home) && - p.toIO.canWrite() - val canWrite = existingParent(workspace) - .map(reallyOwnedByUser) - .getOrElse(false) + p.toIO.canWrite + val canWrite = existingParent(workspace).exists(reallyOwnedByUser) if (canWrite) this else inHomeDir(directories) } @@ -123,7 +122,7 @@ final case class Inputs( Iterator(v.content, bytes("\n")) } val md = MessageDigest.getInstance("SHA-1") - it.foreach(md.update(_)) + it.foreach(md.update) val digest = md.digest() val calculatedSum = new BigInteger(1, digest) String.format(s"%040x", calculatedSum) @@ -188,15 +187,15 @@ object Inputs { final case class Script(base: os.Path, subPath: os.SubPath) extends OnDisk with SourceFile with AnyScalaFile with AnyScript { - lazy val path = base / subPath + lazy val path: os.Path = base / subPath } final case class ScalaFile(base: os.Path, subPath: os.SubPath) extends OnDisk with SourceFile with AnyScalaFile { - lazy val path = base / subPath + lazy val path: os.Path = base / subPath } final case class JavaFile(base: os.Path, subPath: os.SubPath) extends OnDisk with SourceFile with Compiled { - lazy val path = base / subPath + lazy val path: os.Path = base / subPath } final case class Directory(path: os.Path) extends OnDisk with Compiled final case class ResourceDirectory(path: os.Path) extends OnDisk @@ -226,13 +225,13 @@ object Inputs { Iterator(bytes("virtual:"), v.content, bytes("\n")) } val md = MessageDigest.getInstance("SHA-1") - it.foreach(md.update(_)) + it.foreach(md.update) val digest = md.digest() val calculatedSum = new BigInteger(1, digest) String.format(s"%040x", calculatedSum).take(10) } - def homeWorkspace(elements: Seq[Element], directories: Directories) = { + def homeWorkspace(elements: Seq[Element], directories: Directories): os.Path = { val hash0 = inputsHash(elements) val dir = directories.virtualProjectsDir / hash0.take(2) / s"project-${hash0.drop(2)}" os.makeDir.all(dir) @@ -292,7 +291,7 @@ object Inputs { } private val githubGistsArchiveRegex: Regex = - s""":\\/\\/gist\\.github\\.com\\/[^\\/]*?\\/[^\\/]*$$""".r + s"""://gist\\.github\\.com/[^/]*?/[^/]*$$""".r private def resolve(path: String, content: Array[Byte]): Element = if (path.endsWith(".scala")) VirtualScalaFile(content, path) @@ -307,7 +306,7 @@ object Inputs { val zipInputStream = new ZipInputStream(new ByteArrayInputStream(content)) @tailrec def readArchive(acc: Seq[Element]): Seq[Element] = - Option(zipInputStream.getNextEntry()) match { + Option(zipInputStream.getNextEntry) match { case Some(entry) if entry.isDirectory => readArchive(acc) case Some(entry) => val content = zipInputStream.readAllBytes() diff --git a/modules/build/src/main/scala/scala/build/Sources.scala b/modules/build/src/main/scala/scala/build/Sources.scala index 0db3d9f6ad..d131cf8b0e 100644 --- a/modules/build/src/main/scala/scala/build/Sources.scala +++ b/modules/build/src/main/scala/scala/build/Sources.scala @@ -2,7 +2,7 @@ package scala.build import scala.build.internal.CodeWrapper import scala.build.options.{BuildOptions, Scope} -import scala.build.preprocessing._ +import scala.build.preprocessing.* final case class Sources( paths: Seq[(os.Path, os.RelPath)], diff --git a/modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala b/modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala index 4768611c44..41ab36afa0 100644 --- a/modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala +++ b/modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala @@ -49,7 +49,7 @@ object NativeBuilderHelper { dest: os.Path, nativeWorkDir: os.Path, currentProjectSha: String - ) = { + ): Unit = { val projectShaPath = resolveProjectShaPath(nativeWorkDir) os.write.over(projectShaPath, currentProjectSha, createFolders = true) diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala index ebe832347a..5a72612cec 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala @@ -7,11 +7,11 @@ import dependency.parser.DependencyParser import java.nio.charset.StandardCharsets import scala.build.EitherCps.{either, value} -import scala.build.Ops._ -import scala.build.errors._ +import scala.build.Ops.* +import scala.build.errors.* import scala.build.internal.Util import scala.build.options.{BuildOptions, BuildRequirements, ClassPathOptions, ShadowingSeq} -import scala.build.preprocessing.directives._ +import scala.build.preprocessing.directives.* import scala.build.{Inputs, Logger, Position, Positioned} case object ScalaPreprocessor extends Preprocessor { @@ -177,9 +177,9 @@ case object ScalaPreprocessor extends Preprocessor { path: Either[String, os.Path] ): Either[BuildException, Option[SpecialImportsProcessingOutput]] = either { - import fastparse._ + import fastparse.* - import scala.build.internal.ScalaParse._ + import scala.build.internal.ScalaParse.* val res = parse(content, Header(_)) @@ -315,7 +315,7 @@ case object ScalaPreprocessor extends Preprocessor { updatedRequirements.scoped, updatedContentOpt )) - case Seq(h, t @ _*) => + case Seq(h, t*) => val errors = ::( handleUnusedValues(ScopedDirective(h, path, cwd)), t.map(d => handleUnusedValues(ScopedDirective(d, path, cwd))).toList diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index 1113277838..1b297167cd 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -195,7 +195,7 @@ object Package extends ScalaCommand[PackageOptions] { def alreadyExistsCheck(): Unit = { val alreadyExists = !force && os.exists(destPath) && - expectedModifyEpochSecondOpt.forall(exp => os.mtime(destPath) != exp) + !expectedModifyEpochSecondOpt.contains(os.mtime(destPath)) if (alreadyExists) { val msg = if (expectedModifyEpochSecondOpt.isEmpty) s"$printableDest already exists" @@ -685,7 +685,7 @@ object Package extends ScalaCommand[PackageOptions] { noOpt: Boolean, logger: Logger ): Either[BuildException, os.Path] = - Library.withLibraryJar(build, dest.last.toString.stripSuffix(".jar")) { mainJar => + Library.withLibraryJar(build, dest.last.stripSuffix(".jar")) { mainJar => val classPath = os.Path(mainJar, os.pwd) +: build.artifacts.classPath val linkingDir = os.temp.dir(prefix = "scala-cli-js-linking") either { @@ -771,7 +771,7 @@ object Package extends ScalaCommand[PackageOptions] { if (cacheData.changed) Library.withLibraryJar(build, dest.last.stripSuffix(".jar")) { mainJar => - val classpath = build.fullClassPath.map(_.toString) :+ mainJar.toString() + val classpath = build.fullClassPath.map(_.toString) :+ mainJar.toString val args = cliOptions ++ logger.scalaNativeCliInternalLoggerOptions ++ From a503c835aceed68aa111415dcdf3ac33ff0af084 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 26 Apr 2022 19:26:14 +0200 Subject: [PATCH 51/72] Update scalafmt-core to 3.5.2 (#930) --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 1ec6ea0be8..14f01fe92f 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "3.5.1" +version = "3.5.2" align.preset = more maxColumn = 100 From 28732bc98f3d8a3d0f62436d56ae45363a11e19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= <46607934+lwronski@users.noreply.github.com> Date: Tue, 26 Apr 2022 19:33:09 +0200 Subject: [PATCH 52/72] Define valid package type for Scala.js and Scala Native (#941) --- .../scala/scala/cli/commands/Package.scala | 50 ++++++++++++++----- .../build/errors/MalformedCliInputError.scala | 4 ++ 2 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 modules/core/src/main/scala/scala/build/errors/MalformedCliInputError.scala diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index 744897e8e6..c3715c073e 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -19,7 +19,7 @@ import java.util.zip.{ZipEntry, ZipOutputStream} import scala.build.EitherCps.{either, value} import scala.build._ -import scala.build.errors.{BuildException, ScalaNativeBuildError} +import scala.build.errors.{BuildException, MalformedCliInputError, ScalaNativeBuildError} import scala.build.internal.{NativeBuilderHelper, Runner, ScalaJsLinkerConfig} import scala.build.options.{PackageType, Platform} import scala.cli.CurrentParams @@ -127,23 +127,47 @@ object Package extends ScalaCommand[PackageOptions] { logger: Logger, outputOpt: Option[String], force: Boolean, - forcedPackageType: Option[PackageType], + forcedPackageTypeOpt: Option[PackageType], build: Build.Successful, extraArgs: Seq[String], expectedModifyEpochSecondOpt: Option[Long] ): Either[BuildException, Option[Long]] = either { - val packageType = forcedPackageType.getOrElse { - // FIXME We'll probably need more refined rules if we start to support extra Scala.js or Scala Native specific types - if (build.options.notForBloopOptions.packageOptions.isDockerEnabled) - PackageType.Docker - else if (build.options.platform.value == Platform.JS) - PackageType.Js - else if (build.options.platform.value == Platform.Native) - PackageType.Native - else - build.options.notForBloopOptions.packageOptions.packageTypeOpt - .getOrElse(PackageType.Bootstrap) + val packageType: PackageType = { + val basePackageTypeOpt = build.options.notForBloopOptions.packageOptions.packageTypeOpt + lazy val validPackageScalaJS = + Seq(PackageType.LibraryJar, PackageType.SourceJar, PackageType.DocJar) + lazy val validPackageScalaNative = + Seq(PackageType.LibraryJar, PackageType.SourceJar, PackageType.DocJar) + + (forcedPackageTypeOpt -> build.options.platform.value) match { + case (Some(forcedPackageType), _) => forcedPackageType + case (_, _) if build.options.notForBloopOptions.packageOptions.isDockerEnabled => + for (basePackageType <- basePackageTypeOpt) + throw new MalformedCliInputError( + s"Unsuported package type: $basePackageType for Docker." + ) + PackageType.Docker + case (_, Platform.JS) => + val validatedPackageType = + for (basePackageType <- basePackageTypeOpt) + yield + if (validPackageScalaJS.contains(basePackageType)) basePackageType + else throw new MalformedCliInputError( + s"Unsuported package type: $basePackageType for Scala.js." + ) + validatedPackageType.getOrElse(PackageType.Js) + case (_, Platform.Native) => + val validatedPackageType = + for (basePackageType <- basePackageTypeOpt) + yield + if (validPackageScalaNative.contains(basePackageType)) basePackageType + else throw new MalformedCliInputError( + s"Unsuported package type: $basePackageType for Scala Native." + ) + validatedPackageType.getOrElse(PackageType.Native) + case _ => basePackageTypeOpt.getOrElse(PackageType.Bootstrap) + } } // TODO When possible, call alreadyExistsCheck() before compiling stuff diff --git a/modules/core/src/main/scala/scala/build/errors/MalformedCliInputError.scala b/modules/core/src/main/scala/scala/build/errors/MalformedCliInputError.scala new file mode 100644 index 0000000000..0c228d49ec --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/MalformedCliInputError.scala @@ -0,0 +1,4 @@ +package scala.build.errors + +final class MalformedCliInputError(message: String) + extends BuildException(message) From c9e9e721ba0222777586bfa90f87a50c7aab56aa Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 26 Apr 2022 21:08:53 +0200 Subject: [PATCH 53/72] Update scalafmt-cli to 3.5.2 (#903) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update scalafmt-cli to 3.5.2 * Use scalafmt-cli_2_13 Co-authored-by: Łukasz Wroński <46607934+lwronski@users.noreply.github.com> Co-authored-by: Łukasz Wroński --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index 1fcd0bb8be..8b54b75801 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -96,7 +96,7 @@ object Deps { def scala3Compiler(sv: String) = ivy"org.scala-lang::scala3-compiler:$sv" def scalaAsync = ivy"org.scala-lang.modules::scala-async:1.0.1".exclude("*" -> "*") def scalac(sv: String) = ivy"org.scala-lang:scala-compiler:$sv" - def scalafmtCli = ivy"org.scalameta:scalafmt-cli_2.13:3.5.1" + def scalafmtCli = ivy"org.scalameta:scalafmt-cli_2.13:3.5.2" // Force using of 2.13 - is there a better way? def scalaJsEnvJsdomNodejs = ivy"org.scala-js:scalajs-env-jsdom-nodejs_2.13:1.1.0" From ae678b0fab0ba52c947d1e02912e014b9d7025f5 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 27 Apr 2022 12:03:24 +0200 Subject: [PATCH 54/72] Update Scala to 3.1.2 --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index 8b54b75801..ff1ced378f 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -6,7 +6,7 @@ import scala.util.Properties object Scala { def scala212 = "2.12.15" def scala213 = "2.13.8" - def scala3 = "3.1.1" + def scala3 = "3.1.2" val allScala2 = Seq(scala213, scala212) val all = allScala2 ++ Seq(scala3) val mainVersions = Seq(scala3, scala213) From 6f680d2411767b46cda7d11234db7ba1860f6344 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Wed, 27 Apr 2022 13:55:18 +0200 Subject: [PATCH 55/72] Make `package` command output name fallback to actual compiled main class name if there's no default main class override present --- .../src/main/scala/scala/cli/commands/Package.scala | 2 ++ .../scala/cli/integration/PackageTestDefinitions.scala | 10 +++++----- website/docs/cookbooks/scala-package.md | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index 1b297167cd..7180aeadbf 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -188,6 +188,8 @@ object Package extends ScalaCommand[PackageOptions] { .map(_.stripSuffix("_sc")) .map(_ + extension) } + .orElse(build.retainedMainClass.map(_.stripSuffix("_sc") + extension).toOption) + .orElse(build.sources.paths.collectFirst(_._1.baseName + extension)) .getOrElse(defaultName) val destPath = os.Path(dest, Os.pwd) val printableDest = CommandUtils.printablePath(destPath) diff --git a/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala index 2ddb1d9438..9ddae984e9 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/PackageTestDefinitions.scala @@ -77,7 +77,7 @@ abstract class PackageTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - val outputName = if (Properties.isWin) "app.bat" else "app" + val outputName = if (Properties.isWin) "simple.bat" else "simple" val launcher = root / outputName expect(os.isFile(launcher)) @@ -109,7 +109,7 @@ abstract class PackageTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - val outputName = if (Properties.isWin) "app.bat" else "app" + val outputName = if (Properties.isWin) "hello.bat" else "hello" val launcher = root / outputName val output = os.proc(launcher.toString).call(cwd = root).out.text().trim @@ -171,7 +171,7 @@ abstract class PackageTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - val outputName = if (Properties.isWin) "app.bat" else "app" + val outputName = if (Properties.isWin) "hello.bat" else "hello" val launcher = root / outputName val output = os.proc(launcher.toString).call(cwd = root).out.text().trim @@ -324,7 +324,7 @@ abstract class PackageTestDefinitions(val scalaVersionOpt: Option[String]) os.rel / fileName -> s"""|//> using jsHeader "$jsHeaderNewLine" |//> using jsMode "release" - | + | |object Hello extends App { | println("Hello") |} @@ -480,7 +480,7 @@ abstract class PackageTestDefinitions(val scalaVersionOpt: Option[String]) stdout = os.Inherit ) - val outputName = if (Properties.isWin) "app.bat" else "app" + val outputName = if (Properties.isWin) "Main.bat" else "Main" val launcher = root / outputName val output = os.proc(launcher.toString).call(cwd = root).out.text().trim diff --git a/website/docs/cookbooks/scala-package.md b/website/docs/cookbooks/scala-package.md index 80177d1ce5..09aa592ce6 100644 --- a/website/docs/cookbooks/scala-package.md +++ b/website/docs/cookbooks/scala-package.md @@ -8,7 +8,7 @@ The JAR file only contains the byte code that’s generated from your source cod As an example, the following snippet contains a short application to detect the OS: ```scala title=DetectOsApp.scala -object DetectOSApp extends App { +object DetectOsApp extends App { def getOperatingSystem(): String = { val os: String = System.getProperty("os.name") os From 47f6cf5768329bd5891c47b6c43dad728b28d336 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:46:29 +0200 Subject: [PATCH 56/72] Update publish_2.13 to 0.1.2 (#950) --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index ff1ced378f..e53433d82a 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -69,7 +69,7 @@ object Deps { // Force using of 2.13 - is there a better way? def coursierJvm = ivy"io.get-coursier:coursier-jvm_2.13:${Versions.coursier}" def coursierLauncher = ivy"io.get-coursier:coursier-launcher_2.13:${Versions.coursier}" - def coursierPublish = ivy"io.get-coursier.publish:publish_2.13:0.1.0" + def coursierPublish = ivy"io.get-coursier.publish:publish_2.13:0.1.2" // TODO - update to working version def dependency = ivy"io.get-coursier::dependency:0.2.2" def dockerClient = ivy"com.spotify:docker-client:8.16.0" From 0def69950c995390cc7cd515bec53fc6d9b75c57 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:34:01 +0200 Subject: [PATCH 57/72] Use ArgParser type class from scala-cli-signing --- build.sc | 2 +- .../scala/cli/commands/PublishOptions.scala | 2 +- .../cli/internal/PasswordOptionParsers.scala | 44 ------------------- project/deps.sc | 2 + 4 files changed, 4 insertions(+), 46 deletions(-) delete mode 100644 modules/cli-options/src/main/scala/scala/cli/internal/PasswordOptionParsers.scala diff --git a/build.sc b/build.sc index 4d114e0535..28c40ddaad 100644 --- a/build.sc +++ b/build.sc @@ -611,7 +611,7 @@ trait CliOptions extends SbtModule with ScalaCliPublishModule with ScalaCliCompi Deps.jsoniterCore, Deps.jsoniterMacros, Deps.osLib, - Deps.signingCliShared + Deps.signingCliOptions ) def scalaVersion = Scala.scala213 def repositories = super.repositories ++ customRepositories diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala index 636d2124dc..3dd3f91e6e 100644 --- a/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala +++ b/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala @@ -2,8 +2,8 @@ package scala.cli.commands import caseapp._ -import scala.cli.internal.PasswordOptionParsers._ import scala.cli.signing.shared.PasswordOption +import scala.cli.signing.util.ArgParsers._ // format: off final case class PublishOptions( diff --git a/modules/cli-options/src/main/scala/scala/cli/internal/PasswordOptionParsers.scala b/modules/cli-options/src/main/scala/scala/cli/internal/PasswordOptionParsers.scala deleted file mode 100644 index 2cb5a2cb12..0000000000 --- a/modules/cli-options/src/main/scala/scala/cli/internal/PasswordOptionParsers.scala +++ /dev/null @@ -1,44 +0,0 @@ -package scala.cli.internal - -import caseapp.core.argparser.{ArgParser, SimpleArgParser} -import com.github.plokhotnyuk.jsoniter_scala.core._ -import com.github.plokhotnyuk.jsoniter_scala.macros._ - -import scala.cli.signing.shared.{PasswordOption, Secret} - -abstract class LowPriorityPasswordOptionParsers { - - private lazy val commandCodec: JsonValueCodec[List[String]] = - JsonCodecMaker.make - - implicit lazy val argParser: ArgParser[PasswordOption] = - SimpleArgParser.from("password") { str => - if (str.startsWith("value:")) - Right(PasswordOption.Value(Secret(str.stripPrefix("value:")))) - else if (str.startsWith("command:[")) - try { - val command = readFromString(str.stripPrefix("command:"))(commandCodec) - Right(PasswordOption.Command(command)) - } - catch { - case e: JsonReaderException => - Left(caseapp.core.Error.Other(s"Error decoding password command: ${e.getMessage}")) - } - else if (str.startsWith("command:")) { - val command = str.stripPrefix("command:").split("\\s+").toSeq - Right(PasswordOption.Command(command)) - } - else - Left(caseapp.core.Error.Other("Malformed password value (expected \"value:...\")")) - } - -} - -object PasswordOptionParsers extends LowPriorityPasswordOptionParsers { - - implicit lazy val optionArgParser: ArgParser[Option[PasswordOption]] = - SimpleArgParser.from("password") { str => - if (str.trim.isEmpty) Right(None) - else argParser(None, -1, -1, str).map(Some(_)) - } -} diff --git a/project/deps.sc b/project/deps.sc index e53433d82a..7560e5ac24 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -115,6 +115,8 @@ object Deps { def semanticDbJavac = ivy"com.sourcegraph:semanticdb-javac:0.7.4" def semanticDbScalac = ivy"org.scalameta:::semanticdb-scalac:${Versions.scalaMeta}" def shapeless = ivy"com.chuusai::shapeless:2.3.9" + def signingCliOptions = + ivy"io.github.alexarchambault.scala-cli.signing:cli-options_2.13:${Versions.signingCli}" def signingCliShared = ivy"io.github.alexarchambault.scala-cli.signing:shared_2.13:${Versions.signingCli}" def signingCli = ivy"io.github.alexarchambault.scala-cli.signing:cli_2.13:${Versions.signingCli}" From d7bf1cef54d0b89ed5d4fa7dfbef3ce4524e7bcf Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:34:13 +0200 Subject: [PATCH 58/72] Print Java 17 requirement message in case of UnsupportedClassVersionError --- modules/cli/src/main/scala/scala/cli/ScalaCli.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/cli/src/main/scala/scala/cli/ScalaCli.scala b/modules/cli/src/main/scala/scala/cli/ScalaCli.scala index fdf204f437..b842e466df 100644 --- a/modules/cli/src/main/scala/scala/cli/ScalaCli.scala +++ b/modules/cli/src/main/scala/scala/cli/ScalaCli.scala @@ -92,14 +92,14 @@ object ScalaCli { } e match { + case _: UnsupportedClassVersionError if javaMajorVersion < 17 => + warnRequiresJava17() case _: NoClassDefFoundError if isJava17ClassName(e.getMessage) && CurrentParams.verbosity <= 1 && javaMajorVersion < 16 => - // Actually Java >= 16, but let's recommend a LTS version… - System.err.println( - s"Java >= 17 is required to run Scala CLI (found Java $javaMajorVersion)" - ) + // Actually Java >= 16 here, but let's recommend a LTS version… + warnRequiresJava17() case _ => } @@ -108,6 +108,11 @@ object ScalaCli { } } + private def warnRequiresJava17(): Unit = + System.err.println( + s"Java >= 17 is required to run Scala CLI (found Java $javaMajorVersion)" + ) + private def main0(args: Array[String]): Unit = { val remainingArgs = LauncherOptions.parser.stopAtFirstUnrecognized.parse(args.toVector) match { case Left(e) => From 0928e45631f56545fd32e6450d4931e47eb89920 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:34:25 +0200 Subject: [PATCH 59/72] Make Project.scalaCompiler an Option --- .../src/main/scala/scala/build/Build.scala | 21 ++++++++++--------- .../src/main/scala/scala/build/Project.scala | 13 ++++++------ .../main/scala/scala/build/bsp/BspImpl.scala | 19 +++++++++-------- .../scala/build/compiler/ScalaCompiler.scala | 13 ++++++------ .../build/compiler/SimpleScalaCompiler.scala | 6 +++--- .../scala/build/tests/BuildProjectTests.scala | 5 ++++- .../scala/scala/cli/commands/Package.scala | 2 +- 7 files changed, 43 insertions(+), 36 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index 0d444a34b6..90aa14821c 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -159,15 +159,16 @@ object Build { def doPostProcess(build: Build, inputs: Inputs, scope: Scope): Unit = build match { case build: Build.Successful => - postProcess( - build.generatedSources, - inputs.generatedSrcRoot(scope), - build.output, - logger, - inputs.workspace, - updateSemanticDbs = true, - scalaVersion = build.project.scalaCompiler.scalaVersion - ).left.foreach(_.foreach(logger.message(_))) + for (sv <- build.project.scalaCompiler.map(_.scalaVersion)) + postProcess( + build.generatedSources, + inputs.generatedSrcRoot(scope), + build.output, + logger, + inputs.workspace, + updateSemanticDbs = true, + scalaVersion = sv + ).left.foreach(_.foreach(logger.message(_))) case _ => } @@ -753,7 +754,7 @@ object Build { workspace = inputs.workspace, classesDir = classesDir0, scaladocDir = scaladocDir, - scalaCompiler = scalaCompilerParams, + scalaCompiler = Some(scalaCompilerParams), scalaJsOptions = if (options.platform.value == Platform.JS) Some(options.scalaJsOptions.config(logger)) else None, diff --git a/modules/build/src/main/scala/scala/build/Project.scala b/modules/build/src/main/scala/scala/build/Project.scala index 6e7854171d..3b8729f6f7 100644 --- a/modules/build/src/main/scala/scala/build/Project.scala +++ b/modules/build/src/main/scala/scala/build/Project.scala @@ -17,7 +17,7 @@ final case class Project( directory: os.Path, classesDir: os.Path, scaladocDir: os.Path, - scalaCompiler: ScalaCompilerParams, + scalaCompiler: Option[ScalaCompilerParams], scalaJsOptions: Option[BloopConfig.JsConfig], scalaNativeOptions: Option[BloopConfig.NativeConfig], projectName: String, @@ -44,11 +44,12 @@ final case class Project( case (_, Some(nativeConfig)) => BloopConfig.Platform.Native(config = nativeConfig, mainClass = None) } - val scalaConfig = - bloopScalaConfig("org.scala-lang", "scala-compiler", scalaCompiler.scalaVersion).copy( - options = scalaCompiler.scalacOptions.toList, - jars = scalaCompiler.compilerClassPath.map(_.toNIO).toList + val scalaConfigOpt = scalaCompiler.map { scalaCompiler0 => + bloopScalaConfig("org.scala-lang", "scala-compiler", scalaCompiler0.scalaVersion).copy( + options = scalaCompiler0.scalacOptions.toList, + jars = scalaCompiler0.compilerClassPath.map(_.toNIO).toList ) + } baseBloopProject( projectName, directory.toNIO, @@ -62,7 +63,7 @@ final case class Project( sources = sources.iterator.map(_.toNIO).toList, resources = Some(resourceDirs).filter(_.nonEmpty).map(_.iterator.map(_.toNIO).toList), platform = Some(platform), - `scala` = Some(scalaConfig), + `scala` = scalaConfigOpt, java = Some(BloopConfig.Java(javacOptions)), resolution = resolution ) diff --git a/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala b/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala index 514b996b98..0d5b5144c8 100644 --- a/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala +++ b/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala @@ -242,15 +242,16 @@ final class BspImpl( doCompile().thenCompose { res => def doPostProcess(data: PreBuildData, scope: Scope) = - Build.postProcess( - data.generatedSources, - currentBloopSession.inputs.generatedSrcRoot(scope), - data.classesDir, - reloadableOptions.logger, - currentBloopSession.inputs.workspace, - updateSemanticDbs = true, - scalaVersion = data.project.scalaCompiler.scalaVersion - ).left.foreach(_.foreach(showGlobalWarningOnce)) + for (sv <- data.project.scalaCompiler.map(_.scalaVersion)) + Build.postProcess( + data.generatedSources, + currentBloopSession.inputs.generatedSrcRoot(scope), + data.classesDir, + reloadableOptions.logger, + currentBloopSession.inputs.workspace, + updateSemanticDbs = true, + scalaVersion = sv + ).left.foreach(_.foreach(showGlobalWarningOnce)) if (res.getStatusCode == b.StatusCode.OK) CompletableFuture.supplyAsync( diff --git a/modules/build/src/main/scala/scala/build/compiler/ScalaCompiler.scala b/modules/build/src/main/scala/scala/build/compiler/ScalaCompiler.scala index 0d9d598016..c8897e062c 100644 --- a/modules/build/src/main/scala/scala/build/compiler/ScalaCompiler.scala +++ b/modules/build/src/main/scala/scala/build/compiler/ScalaCompiler.scala @@ -22,12 +22,13 @@ object ScalaCompiler { private def ignore( project: Project, logger: Logger - ): Boolean = { - val scalaVer = project.scalaCompiler.scalaVersion - val isScala2 = scalaVer.startsWith("2.") - logger.debug(s"Ignoring compilation for Scala version $scalaVer") - isScala2 - } + ): Boolean = + project.scalaCompiler.exists { scalaCompiler0 => + val scalaVer = scalaCompiler0.scalaVersion + val isScala2 = scalaVer.startsWith("2.") + logger.debug(s"Ignoring compilation for Scala version $scalaVer") + isScala2 + } def jvmVersion: Option[Positioned[Int]] = compiler.jvmVersion diff --git a/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala b/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala index 26a8eb419f..fec9330151 100644 --- a/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala +++ b/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala @@ -32,7 +32,7 @@ final case class SimpleScalaCompiler( if (project.sources.isEmpty) true else { - val isScala2 = project.scalaCompiler.scalaVersion.startsWith("2.") + val isScala2 = project.scalaCompiler.exists(_.scalaVersion.startsWith("2.")) val mainClassOpt = if (isScala2) @@ -54,7 +54,7 @@ final case class SimpleScalaCompiler( // initially adapted from https://github.com/VirtusLab/scala-cli/pull/103/files#diff-d13a7e6d602b8f84d9177e3138487872f0341d006accfe425886a561f029a9c3R120 and around val args = - project.scalaCompiler.scalacOptions ++ + project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ Seq( "-d", outputDir.toString, @@ -79,7 +79,7 @@ final case class SimpleScalaCompiler( val res = Runner.runJvm( javaCommand, javaOptions, - project.scalaCompiler.compilerClassPath.map(_.toIO), + project.scalaCompiler.map(_.compilerClassPath.map(_.toIO)).getOrElse(Nil), mainClass, args, logger, diff --git a/modules/build/src/test/scala/scala/build/tests/BuildProjectTests.scala b/modules/build/src/test/scala/scala/build/tests/BuildProjectTests.scala index fcf6c450be..476b6d75da 100644 --- a/modules/build/src/test/scala/scala/build/tests/BuildProjectTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/BuildProjectTests.scala @@ -91,7 +91,10 @@ class BuildProjectTests extends munit.FunSuite { logger ) - val scalaCompilerOptions = res.fold(throw _, identity).scalaCompiler.scalacOptions + val scalaCompilerOptions = res.fold(throw _, identity) + .scalaCompiler + .toSeq + .flatMap(_.scalacOptions) (scalaCompilerOptions, res.fold(throw _, identity).javacOptions, logger.diagnostics) } diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index 9380a2359d..db387346a4 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -427,7 +427,7 @@ object Package extends ScalaCommand[PackageOptions] { else Nil val args = baseArgs ++ - build.project.scalaCompiler.scalacOptions ++ + build.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ extraArgs ++ defaultArgs ++ Seq(build.output.toString) From 1abe0be116cbaea913636622063c69ea8207cfa6 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:34:40 +0200 Subject: [PATCH 60/72] NIT refacto in Build This groups together Java options-specific and Scala-options specific stuff --- .../src/main/scala/scala/build/Build.scala | 148 +++++++++--------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index 90aa14821c..aabff47874 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -648,7 +648,6 @@ object Build { logger: Logger ): Either[BuildException, Project] = either { - val params = value(options.scalaParams) val allSources = sources.paths.map(_._1) ++ generatedSources.map(_.generated) val classesDir0 = classesDir(inputs.workspace, inputs.projectName, scope) @@ -656,85 +655,94 @@ object Build { val artifacts = value(options.artifacts(logger)) - val pluginScalacOptions = artifacts.compilerPlugins.distinct.map { - case (_, _, path) => - ScalacOpt(s"-Xplugin:$path") - } - val generateSemanticDbs = options.scalaOptions.generateSemanticDbs.getOrElse(false) - val semanticDbScalacOptions = - if (generateSemanticDbs) - if (params.scalaVersion.startsWith("2.")) - Seq( - "-Yrangepos", - "-P:semanticdb:failures:warning", - "-P:semanticdb:synthetics:on", - s"-P:semanticdb:sourceroot:${inputs.workspace}" - ).map(ScalacOpt(_)) - else - Seq( - "-Xsemanticdb", - "-sourceroot", - inputs.workspace.toString - ).map(ScalacOpt(_)) - else Nil + val releaseFlagVersion = releaseFlag(options, compilerJvmVersionOpt, logger).map(_.toString) - val semanticDbJavacOptions = - // FIXME Should this be in scalaOptions, now that we use it for javac stuff too? - if (generateSemanticDbs) { - // from https://github.com/scalameta/metals/blob/04405c0401121b372ea1971c361e05108fb36193/metals/src/main/scala/scala/meta/internal/metals/JavaInteractiveSemanticdb.scala#L137-L146 - val compilerPackages = Seq( - "com.sun.tools.javac.api", - "com.sun.tools.javac.code", - "com.sun.tools.javac.model", - "com.sun.tools.javac.tree", - "com.sun.tools.javac.util" - ) - val exports = compilerPackages.flatMap { pkg => - Seq("-J--add-exports", s"-Jjdk.compiler/$pkg=ALL-UNNAMED") - } + val scalaCompilerParams = { - Seq( - // does the path need to be escaped somehow? - s"-Xplugin:semanticdb -sourceroot:${inputs.workspace} -targetroot:javac-classes-directory" - ) ++ exports + val params = value(options.scalaParams) + + val pluginScalacOptions = artifacts.compilerPlugins.distinct.map { + case (_, _, path) => + ScalacOpt(s"-Xplugin:$path") } - else - Nil - val sourceRootScalacOptions = - if (params.scalaVersion.startsWith("2.")) Nil - else Seq("-sourceroot", inputs.workspace.toString).map(ScalacOpt(_)) + val semanticDbScalacOptions = + if (generateSemanticDbs) + if (params.scalaVersion.startsWith("2.")) + Seq( + "-Yrangepos", + "-P:semanticdb:failures:warning", + "-P:semanticdb:synthetics:on", + s"-P:semanticdb:sourceroot:${inputs.workspace}" + ).map(ScalacOpt(_)) + else + Seq( + "-Xsemanticdb", + "-sourceroot", + inputs.workspace.toString + ).map(ScalacOpt(_)) + else Nil + + val sourceRootScalacOptions = + if (params.scalaVersion.startsWith("2.")) Nil + else Seq("-sourceroot", inputs.workspace.toString).map(ScalacOpt(_)) + + val scalaJsScalacOptions = + if (options.platform.value == Platform.JS && !params.scalaVersion.startsWith("2.")) + Seq(ScalacOpt("-scalajs")) + else Nil + + val scalacReleaseV = releaseFlagVersion + .map(v => List("-release", v).map(ScalacOpt(_))) + .getOrElse(Nil) + + val scalacOptions = + options.scalaOptions.scalacOptions.map(_.value) ++ + pluginScalacOptions ++ + semanticDbScalacOptions ++ + sourceRootScalacOptions ++ + scalaJsScalacOptions ++ + scalacReleaseV + + ScalaCompilerParams( + scalaVersion = params.scalaVersion, + scalaBinaryVersion = params.scalaBinaryVersion, + scalacOptions = scalacOptions.toSeq.map(_.value), + compilerClassPath = artifacts.compilerClassPath + ) + } - val scalaJsScalacOptions = - if (options.platform.value == Platform.JS && !params.scalaVersion.startsWith("2.")) - Seq(ScalacOpt("-scalajs")) - else Nil + val javacOptions = { + + val semanticDbJavacOptions = + // FIXME Should this be in scalaOptions, now that we use it for javac stuff too? + if (generateSemanticDbs) { + // from https://github.com/scalameta/metals/blob/04405c0401121b372ea1971c361e05108fb36193/metals/src/main/scala/scala/meta/internal/metals/JavaInteractiveSemanticdb.scala#L137-L146 + val compilerPackages = Seq( + "com.sun.tools.javac.api", + "com.sun.tools.javac.code", + "com.sun.tools.javac.model", + "com.sun.tools.javac.tree", + "com.sun.tools.javac.util" + ) + val exports = compilerPackages.flatMap { pkg => + Seq("-J--add-exports", s"-Jjdk.compiler/$pkg=ALL-UNNAMED") + } - val releaseFlagVersion = releaseFlag(options, compilerJvmVersionOpt, logger).map(_.toString) + Seq( + // does the path need to be escaped somehow? + s"-Xplugin:semanticdb -sourceroot:${inputs.workspace} -targetroot:javac-classes-directory" + ) ++ exports + } + else + Nil - val scalacReleaseV = releaseFlagVersion - .map(v => List("-release", v).map(ScalacOpt(_))) - .getOrElse(Nil) - val javacReleaseV = releaseFlagVersion.map(v => List("--release", v)).getOrElse(Nil) - - val scalacOptions = - options.scalaOptions.scalacOptions.map(_.value) ++ - pluginScalacOptions ++ - semanticDbScalacOptions ++ - sourceRootScalacOptions ++ - scalaJsScalacOptions ++ - scalacReleaseV - - val scalaCompilerParams = ScalaCompilerParams( - scalaVersion = params.scalaVersion, - scalaBinaryVersion = params.scalaBinaryVersion, - scalacOptions = scalacOptions.toSeq.map(_.value), - compilerClassPath = artifacts.compilerClassPath - ) + val javacReleaseV = releaseFlagVersion.map(v => List("--release", v)).getOrElse(Nil) - val javacOptions = javacReleaseV ++ semanticDbJavacOptions ++ options.javaOptions.javacOptions + javacReleaseV ++ semanticDbJavacOptions ++ options.javaOptions.javacOptions + } // `test` scope should contains class path to main scope val mainClassesPath = From b0652c9b1891e854b613ce8fe030a08fc0e56c19 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:35:03 +0200 Subject: [PATCH 61/72] Rename NativeBuilderHelper to CachedBinary And move it to the cli module (it's only used there) It's currently used for Scala Native and native image stuff, but should be used for javadoc JARs in upcoming commits. --- build.sc | 6 ++- .../scala/build/tests/BuildOptionsTests.scala | 2 +- .../scala/scala/cli/commands/Package.scala | 8 ++-- .../scala/cli/internal/CachedBinary.scala} | 27 ++++++------- .../scala/cli/packaging/NativeImage.scala | 7 ++-- .../scala/cli/tests/CachedBinaryTests.scala} | 39 ++++++++++--------- 6 files changed, 48 insertions(+), 41 deletions(-) rename modules/{build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala => cli/src/main/scala/scala/cli/internal/CachedBinary.scala} (74%) rename modules/{build/src/test/scala/scala/build/tests/NativeBuilderHelperTests.scala => cli/src/test/scala/cli/tests/CachedBinaryTests.scala} (84%) diff --git a/build.sc b/build.sc index 28c40ddaad..433911a113 100644 --- a/build.sc +++ b/build.sc @@ -658,7 +658,11 @@ trait Cli extends SbtModule with ProtoBuildModule with CliLaunchers def localRepoJar = `local-repo`.localRepoJar() - trait Tests extends super.Tests with ScalaCliScalafixModule + trait Tests extends super.Tests with ScalaCliScalafixModule { + def runClasspath = T { + super.runClasspath() ++ Seq(localRepoJar()) + } + } } trait Cli3 extends Cli { diff --git a/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala b/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala index 580a5c5ba4..e29ebcb894 100644 --- a/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala @@ -368,7 +368,7 @@ class BuildOptionsTests extends munit.FunSuite { case _ => sys.error(s"Unexpected failed or cancelled build $build") } - val rawOptions = build0.project.scalaCompiler.scalacOptions + val rawOptions = build0.project.scalaCompiler.toSeq.flatMap(_.scalacOptions) val seq = ShadowingSeq.from(rawOptions.map(ScalacOpt(_))) expect(seq.toSeq.length == rawOptions.length) // no option needs to be shadowed diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index db387346a4..75d7fe515b 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -20,14 +20,14 @@ import java.util.zip.{ZipEntry, ZipOutputStream} import scala.build.EitherCps.{either, value} import scala.build._ import scala.build.errors.{BuildException, MalformedCliInputError, ScalaNativeBuildError} -import scala.build.internal.{NativeBuilderHelper, Runner, ScalaJsLinkerConfig} +import scala.build.internal.{Runner, ScalaJsLinkerConfig} import scala.build.options.{PackageType, Platform} import scala.cli.CurrentParams import scala.cli.commands.OptionsHelper._ import scala.cli.commands.util.PackageOptionsUtil._ import scala.cli.commands.util.SharedOptionsUtil._ import scala.cli.errors.{ScalaJsLinkingError, ScaladocGenerationFailedError} -import scala.cli.internal.{ProcUtil, ScalaJsLinker} +import scala.cli.internal.{CachedBinary, ProcUtil, ScalaJsLinker} import scala.cli.packaging.{Library, NativeImage} import scala.util.Properties @@ -787,7 +787,7 @@ object Package extends ScalaCommand[PackageOptions] { os.makeDir.all(nativeWorkDir) val cacheData = - NativeBuilderHelper.getCacheData( + CachedBinary.getCacheData( build, cliOptions, dest, @@ -820,7 +820,7 @@ object Package extends ScalaCommand[PackageOptions] { logger ).waitFor() if (exitCode == 0) - NativeBuilderHelper.updateProjectAndOutputSha(dest, nativeWorkDir, cacheData.projectSha) + CachedBinary.updateProjectAndOutputSha(dest, nativeWorkDir, cacheData.projectSha) else throw new ScalaNativeBuildError } diff --git a/modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala b/modules/cli/src/main/scala/scala/cli/internal/CachedBinary.scala similarity index 74% rename from modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala rename to modules/cli/src/main/scala/scala/cli/internal/CachedBinary.scala index 41ab36afa0..b6a7d338dc 100644 --- a/modules/build/src/main/scala/scala/build/internal/NativeBuilderHelper.scala +++ b/modules/cli/src/main/scala/scala/cli/internal/CachedBinary.scala @@ -1,17 +1,18 @@ -package scala.build.internal +package scala.cli.internal import java.math.BigInteger import java.nio.charset.StandardCharsets import java.security.MessageDigest import scala.build.Build +import scala.build.internal.Constants -object NativeBuilderHelper { +object CachedBinary { - case class SNCacheData(changed: Boolean, projectSha: String) + final case class CacheData(changed: Boolean, projectSha: String) - private def resolveProjectShaPath(nativeWorkDir: os.Path) = nativeWorkDir / ".project_sha" - private def resolveOutputShaPath(nativeWorkDir: os.Path) = nativeWorkDir / ".output_sha" + private def resolveProjectShaPath(workDir: os.Path) = workDir / ".project_sha" + private def resolveOutputShaPath(workDir: os.Path) = workDir / ".output_sha" private def fileSha(filePath: os.Path): String = { val md = MessageDigest.getInstance("SHA-1") @@ -47,13 +48,13 @@ object NativeBuilderHelper { def updateProjectAndOutputSha( dest: os.Path, - nativeWorkDir: os.Path, + workDir: os.Path, currentProjectSha: String ): Unit = { - val projectShaPath = resolveProjectShaPath(nativeWorkDir) + val projectShaPath = resolveProjectShaPath(workDir) os.write.over(projectShaPath, currentProjectSha, createFolders = true) - val outputShaPath = resolveOutputShaPath(nativeWorkDir) + val outputShaPath = resolveOutputShaPath(workDir) val sha = fileSha(dest) os.write.over(outputShaPath, sha) } @@ -62,10 +63,10 @@ object NativeBuilderHelper { build: Build.Successful, config: List[String], dest: os.Path, - nativeWorkDir: os.Path - ): SNCacheData = { - val projectShaPath = resolveProjectShaPath(nativeWorkDir) - val outputShaPath = resolveOutputShaPath(nativeWorkDir) + workDir: os.Path + ): CacheData = { + val projectShaPath = resolveProjectShaPath(workDir) + val outputShaPath = resolveOutputShaPath(workDir) val currentProjectSha = projectSha(build, config) val currentOutputSha = if (os.exists(dest)) Some(fileSha(dest)) else None @@ -78,6 +79,6 @@ object NativeBuilderHelper { previousOutputSha != currentOutputSha || !os.exists(dest) - SNCacheData(changed, currentProjectSha) + CacheData(changed, currentProjectSha) } } diff --git a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala index 6c5b7ff882..a95392b358 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala @@ -3,10 +3,11 @@ package scala.cli.packaging import java.io.{File, OutputStream} import scala.annotation.tailrec -import scala.build.internal.{NativeBuilderHelper, Runner} +import scala.build.internal.Runner import scala.build.{Build, Logger} import scala.cli.errors.GraalVMNativeImageError import scala.cli.graal.{BytecodeProcessor, TempCache} +import scala.cli.internal.CachedBinary import scala.util.Properties object NativeImage { @@ -218,7 +219,7 @@ object NativeImage { val javaHome = options.javaHome().value - val cacheData = NativeBuilderHelper.getCacheData( + val cacheData = CachedBinary.getCacheData( build, s"--java-home=${javaHome.javaHome.toString}" :: "--" :: extraOptions.toList, dest, @@ -290,7 +291,7 @@ object NativeImage { else dest / os.up / s"${dest.last}.exe" else dest - NativeBuilderHelper.updateProjectAndOutputSha( + CachedBinary.updateProjectAndOutputSha( actualDest, nativeImageWorkDir, cacheData.projectSha diff --git a/modules/build/src/test/scala/scala/build/tests/NativeBuilderHelperTests.scala b/modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala similarity index 84% rename from modules/build/src/test/scala/scala/build/tests/NativeBuilderHelperTests.scala rename to modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala index 571171d232..118c494a4d 100644 --- a/modules/build/src/test/scala/scala/build/tests/NativeBuilderHelperTests.scala +++ b/modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala @@ -1,14 +1,15 @@ -package scala.build.tests +package scala.cli.tests import com.eed3si9n.expecty.Expecty.{assert => expect} -import scala.build.internal.NativeBuilderHelper import scala.build.options.{BuildOptions, InternalOptions} +import scala.build.tests.TestInputs import scala.build.tests.util.BloopServer import scala.build.{BuildThreads, Directories, LocalRepo} +import scala.cli.internal.CachedBinary import scala.util.{Properties, Random} -class NativeBuilderHelperTests extends munit.FunSuite { +class CachedBinaryTests extends munit.FunSuite { val buildThreads = BuildThreads.create() def bloopConfig = BloopServer.bloopConfig @@ -54,7 +55,7 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) val cacheData = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) expect(cacheData.changed) } } @@ -71,8 +72,8 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) val cacheData = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) - NativeBuilderHelper.updateProjectAndOutputSha( + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.updateProjectAndOutputSha( destPath, nativeWorkDir, cacheData.projectSha @@ -80,7 +81,7 @@ class NativeBuilderHelperTests extends munit.FunSuite { expect(cacheData.changed) val sameBuildCache = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) expect(!sameBuildCache.changed) } } @@ -97,8 +98,8 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) val cacheData = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) - NativeBuilderHelper.updateProjectAndOutputSha( + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.updateProjectAndOutputSha( destPath, nativeWorkDir, cacheData.projectSha @@ -107,7 +108,7 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.remove(destPath) val afterDeleteCache = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) expect(afterDeleteCache.changed) } } @@ -124,8 +125,8 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) val cacheData = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) - NativeBuilderHelper.updateProjectAndOutputSha( + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.updateProjectAndOutputSha( destPath, nativeWorkDir, cacheData.projectSha @@ -134,7 +135,7 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write.over(destPath, Random.alphanumeric.take(10).mkString("")) val cacheAfterFileUpdate = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) expect(cacheAfterFileUpdate.changed) } } @@ -150,8 +151,8 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) val cacheData = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) - NativeBuilderHelper.updateProjectAndOutputSha( + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.updateProjectAndOutputSha( destPath, nativeWorkDir, cacheData.projectSha @@ -160,7 +161,7 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write.append(root / helloFileName, Random.alphanumeric.take(10).mkString("")) val cacheAfterFileUpdate = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) expect(cacheAfterFileUpdate.changed) } } @@ -176,8 +177,8 @@ class NativeBuilderHelperTests extends munit.FunSuite { os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) val cacheData = - NativeBuilderHelper.getCacheData(build, config, destPath, nativeWorkDir) - NativeBuilderHelper.updateProjectAndOutputSha( + CachedBinary.getCacheData(build, config, destPath, nativeWorkDir) + CachedBinary.updateProjectAndOutputSha( destPath, nativeWorkDir, cacheData.projectSha @@ -194,7 +195,7 @@ class NativeBuilderHelperTests extends munit.FunSuite { val updatedConfig = updatedBuild.options.scalaNativeOptions.configCliOptions() val cacheAfterConfigUpdate = - NativeBuilderHelper.getCacheData( + CachedBinary.getCacheData( updatedBuild, updatedConfig, destPath, From b85d47f654a4f707f2f263dd32caf60dc27ed9c3 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:35:17 +0200 Subject: [PATCH 62/72] NIT Refacto of working directories handling These only rely on Inputs fields, not on BuildOptions ones. So it's better if those live in Inputs. --- .../src/main/scala/scala/build/Inputs.scala | 5 +++ .../scala/scala/cli/commands/Package.scala | 43 +++++-------------- .../main/scala/scala/cli/commands/Run.scala | 6 +-- .../main/scala/scala/cli/commands/Test.scala | 1 - .../scala/cli/tests/CachedBinaryTests.scala | 12 +++--- .../scala/build/options/BuildOptions.scala | 3 -- .../build/options/ScalaNativeOptions.scala | 3 -- 7 files changed, 24 insertions(+), 49 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Inputs.scala b/modules/build/src/main/scala/scala/build/Inputs.scala index 820f8122c0..2cb2958753 100644 --- a/modules/build/src/main/scala/scala/build/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/Inputs.scala @@ -143,6 +143,11 @@ final case class Inputs( .toVector .sortBy(_.subPath.segments) } + + def nativeWorkDir: os.Path = + workspace / Constants.workspaceDirName / projectName / "native" + def nativeImageWorkDir: os.Path = + workspace / Constants.workspaceDirName / projectName / "native-image" } object Inputs { diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index 75d7fe515b..cd166ef1d5 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -272,11 +272,18 @@ object Package extends ScalaCommand[PackageOptions] { value(buildJs(build, destPath, value(mainClass), logger)) case PackageType.Native => - buildNative(build, destPath, value(mainClass), logger) + buildNative(build, value(mainClass), destPath, logger) destPath case PackageType.GraalVMNativeImage => - buildGraalVMNativeImage(build, destPath, value(mainClass), extraArgs, logger) + NativeImage.buildNativeImage( + build, + value(mainClass), + destPath, + build.inputs.nativeImageWorkDir, + extraArgs, + logger + ) destPath case nativePackagerType: PackageType.NativePackagerType => @@ -543,7 +550,7 @@ object Package extends ScalaCommand[PackageOptions] { build.options.platform.value match { case Platform.JVM => bootstrap(build, appPath, mainClass, () => ()) case Platform.JS => buildJs(build, appPath, mainClass, logger) - case Platform.Native => buildNative(build, appPath, mainClass, logger) + case Platform.Native => buildNative(build, mainClass, appPath, logger) } logger.message( @@ -577,34 +584,6 @@ object Package extends ScalaCommand[PackageOptions] { ) } - private def buildNative( - build: Build.Successful, - destPath: os.Path, - mainClass: String, - logger: Logger - ): Unit = { - val workDir = - build.options.scalaNativeOptions.nativeWorkDir( - build.inputs.workspace, - build.inputs.projectName - ) - - buildNative(build, mainClass, destPath, workDir, logger) - } - - private def buildGraalVMNativeImage( - build: Build.Successful, - destPath: os.Path, - mainClass: String, - extraArgs: Seq[String], - logger: Logger - ): Unit = { - val workDir = - build.options.nativeImageWorkDir(build.inputs.workspace, build.inputs.projectName) - - NativeImage.buildNativeImage(build, mainClass, destPath, workDir, extraArgs, logger) - } - private def bootstrap( build: Build.Successful, destPath: os.Path, @@ -778,12 +757,12 @@ object Package extends ScalaCommand[PackageOptions] { build: Build.Successful, mainClass: String, dest: os.Path, - nativeWorkDir: os.Path, logger: Logger ): Unit = { val cliOptions = build.options.scalaNativeOptions.configCliOptions() + val nativeWorkDir = build.inputs.nativeWorkDir os.makeDir.all(nativeWorkDir) val cacheData = diff --git a/modules/cli/src/main/scala/scala/cli/commands/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/Run.scala index f6dbec269c..93f70d9576 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Run.scala @@ -241,7 +241,6 @@ object Run extends ScalaCommand[RunOptions] { withNativeLauncher( build, mainClass, - build.options.scalaNativeOptions.nativeWorkDir(root, projectName), logger ) { launcher => Runner.runNative( @@ -294,11 +293,10 @@ object Run extends ScalaCommand[RunOptions] { def withNativeLauncher[T]( build: Build.Successful, mainClass: String, - workDir: os.Path, logger: Logger )(f: os.Path => T): T = { - val dest = workDir / s"main${if (Properties.isWin) ".exe" else ""}" - Package.buildNative(build, mainClass, dest, workDir, logger) + val dest = build.inputs.nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" + Package.buildNative(build, mainClass, dest, logger) f(dest) } } diff --git a/modules/cli/src/main/scala/scala/cli/commands/Test.scala b/modules/cli/src/main/scala/scala/cli/commands/Test.scala index ac2e9fcb34..47cf8d8c9d 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Test.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Test.scala @@ -191,7 +191,6 @@ object Test extends ScalaCommand[TestOptions] { Run.withNativeLauncher( build, "scala.scalanative.testinterface.TestMain", - build.options.scalaNativeOptions.nativeWorkDir(root, projectName), logger ) { launcher => Runner.testNative( diff --git a/modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala b/modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala index 118c494a4d..9638dd470e 100644 --- a/modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala +++ b/modules/cli/src/test/scala/cli/tests/CachedBinaryTests.scala @@ -49,7 +49,7 @@ class CachedBinaryTests extends munit.FunSuite { val build = maybeBuild.successfulOpt.get val config = build.options.scalaNativeOptions.configCliOptions() - val nativeWorkDir = build.options.scalaNativeOptions.nativeWorkDir(root, "native-test") + val nativeWorkDir = build.inputs.nativeWorkDir val destPath = nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" // generate dummy output os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) @@ -66,7 +66,7 @@ class CachedBinaryTests extends munit.FunSuite { val build = maybeBuild.successfulOpt.get val config = build.options.scalaNativeOptions.configCliOptions() - val nativeWorkDir = build.options.scalaNativeOptions.nativeWorkDir(root, "native-test") + val nativeWorkDir = build.inputs.nativeWorkDir val destPath = nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" // generate dummy output os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) @@ -92,7 +92,7 @@ class CachedBinaryTests extends munit.FunSuite { val build = maybeBuild.successfulOpt.get val config = build.options.scalaNativeOptions.configCliOptions() - val nativeWorkDir = build.options.scalaNativeOptions.nativeWorkDir(root, "native-test") + val nativeWorkDir = build.inputs.nativeWorkDir val destPath = nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" // generate dummy output os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) @@ -119,7 +119,7 @@ class CachedBinaryTests extends munit.FunSuite { val build = maybeBuild.successfulOpt.get val config = build.options.scalaNativeOptions.configCliOptions() - val nativeWorkDir = build.options.scalaNativeOptions.nativeWorkDir(root, "native-test") + val nativeWorkDir = build.inputs.nativeWorkDir val destPath = nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" // generate dummy output os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) @@ -146,7 +146,7 @@ class CachedBinaryTests extends munit.FunSuite { val build = maybeBuild.successfulOpt.get val config = build.options.scalaNativeOptions.configCliOptions() - val nativeWorkDir = build.options.scalaNativeOptions.nativeWorkDir(root, "native-test") + val nativeWorkDir = build.inputs.nativeWorkDir val destPath = nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) @@ -172,7 +172,7 @@ class CachedBinaryTests extends munit.FunSuite { val build = maybeBuild.successfulOpt.get val config = build.options.scalaNativeOptions.configCliOptions() - val nativeWorkDir = build.options.scalaNativeOptions.nativeWorkDir(root, "native-test") + val nativeWorkDir = build.inputs.nativeWorkDir val destPath = nativeWorkDir / s"main${if (Properties.isWin) ".exe" else ""}" os.write(destPath, Random.alphanumeric.take(10).mkString(""), createFolders = true) diff --git a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala index 6b414be67c..ab2d68be40 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala @@ -495,9 +495,6 @@ final case class BuildOptions( BuildOptions.monoid.orElse(this, other) def validate: Seq[Diagnostic] = BuildOptionsRule.validateAll(this) - - def nativeImageWorkDir(root: os.Path, projectName: String): os.Path = - root / workspaceDirName / projectName / "native-image" } object BuildOptions { diff --git a/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala b/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala index 94b785bb84..f3c9c47705 100644 --- a/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/ScalaNativeOptions.scala @@ -20,9 +20,6 @@ final case class ScalaNativeOptions( compileDefaults: Option[Boolean] = None ) { - def nativeWorkDir(root: os.Path, projectName: String): os.Path = - root / Constants.workspaceDirName / projectName / "native" - def finalVersion = version.map(_.trim).filter(_.nonEmpty).getOrElse(Constants.scalaNativeVersion) def numeralVersion = SNNumeralVersion.parse(finalVersion) From ad142756d919e79b86a8ff8f20d3e9055e9a2199 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:35:29 +0200 Subject: [PATCH 63/72] Use os-lib more --- .../src/main/scala/scala/build/Build.scala | 7 +++--- .../scala/scala/cli/commands/Compile.scala | 2 +- .../scala/scala/cli/commands/Metabrowse.scala | 6 ++--- .../scala/scala/cli/commands/Package.scala | 22 ++++++++----------- .../main/scala/scala/cli/commands/Run.scala | 2 +- .../main/scala/scala/cli/commands/Test.scala | 8 +++---- .../scala/scala/cli/packaging/Library.scala | 14 ++++++------ .../scala/cli/packaging/NativeImage.scala | 2 +- 8 files changed, 29 insertions(+), 34 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index aabff47874..9de5743595 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -6,7 +6,7 @@ import com.swoval.files.{PathWatcher, PathWatchers} import dependency.ScalaParameters import java.io.File -import java.nio.file.{FileSystemException, Path} +import java.nio.file.FileSystemException import java.util.concurrent.{ScheduledExecutorService, ScheduledFuture} import scala.annotation.tailrec @@ -52,8 +52,7 @@ object Build { def success: Boolean = true def successfulOpt: Some[this.type] = Some(this) def outputOpt: Some[os.Path] = Some(output) - def fullClassPath: Seq[Path] = - Seq(output.toNIO) ++ sources.resourceDirs.map(_.toNIO) ++ artifacts.classPath.map(_.toNIO) + def fullClassPath: Seq[os.Path] = Seq(output) ++ sources.resourceDirs ++ artifacts.classPath def foundMainClasses(): Seq[String] = MainClass.find(output) def retainedMainClass: Either[MainClassError, String] = { @@ -1021,7 +1020,7 @@ object Build { val retCode = run( javaCommand, - build.fullClassPath.map(_.toFile), + build.fullClassPath.map(_.toIO), "org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator", Seq(printable(build.output), printable(jmhSourceDir), printable(jmhResourceDir), "default"), logger diff --git a/modules/cli/src/main/scala/scala/cli/commands/Compile.scala b/modules/cli/src/main/scala/scala/cli/commands/Compile.scala index b70316b6b5..9b87c2ad64 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Compile.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Compile.scala @@ -65,7 +65,7 @@ object Compile extends ScalaCommand[CompileOptions] { } yield s if (options.classPath) for (s <- successulBuildOpt) { - val cp = s.fullClassPath.map(_.toAbsolutePath.toString).mkString(File.pathSeparator) + val cp = s.fullClassPath.map(_.toString).mkString(File.pathSeparator) println(cp) } for (output <- outputPath(options); s <- successulBuildOpt) diff --git a/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala b/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala index 3f029d4bf9..d13ae70bd8 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala @@ -89,8 +89,8 @@ object Metabrowse extends ScalaCommand[MetabrowseOptions] { options: MetabrowseOptions, logger: Logger, successfulBuild: Build.Successful, - jar: Path, - sourceJar: Path + jar: os.Path, + sourceJar: os.Path ): Unit = { val launcher = options.metabrowseLauncher @@ -117,7 +117,7 @@ object Metabrowse extends ScalaCommand[MetabrowseOptions] { successfulBuild.options.javaHomeLocation().value / "jre" / "lib" / "rt.jar" val rtJarOpt = - if (os.isFile(rtJarLocation)) Some(rtJarLocation.toNIO) + if (os.isFile(rtJarLocation)) Some(rtJarLocation) else None if (rtJarOpt.isEmpty && options.shared.logging.verbosity >= 0) diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index cd166ef1d5..d7b434c663 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -14,7 +14,6 @@ import packager.windows.WindowsPackage import java.io.{ByteArrayOutputStream, File} import java.nio.charset.StandardCharsets import java.nio.file.attribute.FileTime -import java.nio.file.{Files, Path} import java.util.zip.{ZipEntry, ZipOutputStream} import scala.build.EitherCps.{either, value} @@ -603,13 +602,13 @@ object Package extends ScalaCommand[PackageOptions] { } // TODO Generate that in memory - val tmpJar = Files.createTempFile(destPath.last.stripSuffix(".jar"), ".jar") + val tmpJar = os.temp(prefix = destPath.last.stripSuffix(".jar"), suffix = ".jar") val tmpJarParams = Parameters.Assembly() .withExtraZipEntries(byteCodeZipEntries) .withMainClass(mainClass) - AssemblyGenerator.generate(tmpJarParams, tmpJar) - val tmpJarContent = os.read.bytes(os.Path(tmpJar)) - Files.deleteIfExists(tmpJar) + AssemblyGenerator.generate(tmpJarParams, tmpJar.toNIO) + val tmpJarContent = os.read.bytes(tmpJar) + os.remove(tmpJar) def dependencyEntries = build.artifacts.artifacts.map { @@ -670,14 +669,11 @@ object Package extends ScalaCommand[PackageOptions] { build: Build.Successful, defaultLastModified: Long, fileName: String = "library" - )(f: Path => T): T = { + )(f: os.Path => T): T = { val jarContent = sourceJar(build, defaultLastModified) - val jar = Files.createTempFile(fileName.stripSuffix(".jar"), "-sources.jar") - try { - Files.write(jar, jarContent) - f(jar) - } - finally Files.deleteIfExists(jar) + val jar = os.temp(jarContent, prefix = fileName.stripSuffix(".jar"), suffix = "-sources.jar") + try f(jar) + finally os.remove(jar) } def linkJs( @@ -691,7 +687,7 @@ object Package extends ScalaCommand[PackageOptions] { logger: Logger ): Either[BuildException, os.Path] = Library.withLibraryJar(build, dest.last.stripSuffix(".jar")) { mainJar => - val classPath = os.Path(mainJar, os.pwd) +: build.artifacts.classPath + val classPath = mainJar +: build.artifacts.classPath val linkingDir = os.temp.dir(prefix = "scala-cli-js-linking") either { value { diff --git a/modules/cli/src/main/scala/scala/cli/commands/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/Run.scala index 93f70d9576..bc174b9a9a 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Run.scala @@ -254,7 +254,7 @@ object Run extends ScalaCommand[RunOptions] { Runner.runJvm( build.options.javaHome().value.javaCommand, build.options.javaOptions.javaOpts.toSeq.map(_.value.value), - build.fullClassPath.map(_.toFile), + build.fullClassPath.map(_.toIO), mainClass, args, logger, diff --git a/modules/cli/src/main/scala/scala/cli/commands/Test.scala b/modules/cli/src/main/scala/scala/cli/commands/Test.scala index 47cf8d8c9d..189c7797e7 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Test.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Test.scala @@ -176,7 +176,7 @@ object Test extends ScalaCommand[TestOptions] { logger ) { js => Runner.testJs( - build.fullClassPath, + build.fullClassPath.map(_.toNIO), js.toIO, requireTests, args, @@ -194,7 +194,7 @@ object Test extends ScalaCommand[TestOptions] { logger ) { launcher => Runner.testNative( - build.fullClassPath, + build.fullClassPath.map(_.toNIO), launcher.toIO, testFrameworkOpt, requireTests, @@ -207,7 +207,7 @@ object Test extends ScalaCommand[TestOptions] { val classPath = build.fullClassPath val testFrameworkOpt0 = testFrameworkOpt.orElse { - findTestFramework(classPath, logger) + findTestFramework(classPath.map(_.toNIO), logger) } val extraArgs = @@ -219,7 +219,7 @@ object Test extends ScalaCommand[TestOptions] { Runner.runJvm( build.options.javaHome().value.javaCommand, build.options.javaOptions.javaOpts.toSeq.map(_.value.value), - classPath.map(_.toFile), + classPath.map(_.toIO), Constants.testRunnerMainClass, extraArgs, logger, diff --git a/modules/cli/src/main/scala/scala/cli/packaging/Library.scala b/modules/cli/src/main/scala/scala/cli/packaging/Library.scala index ce92a138f0..82998b6679 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/Library.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/Library.scala @@ -10,14 +10,14 @@ import scala.build.Build object Library { - def withLibraryJar[T](build: Build.Successful, fileName: String = "library")(f: Path => T): T = { + def withLibraryJar[T]( + build: Build.Successful, + fileName: String = "library" + )(f: os.Path => T): T = { val mainJarContent = libraryJar(build) - val mainJar = Files.createTempFile(fileName.stripSuffix(".jar"), ".jar") - try { - Files.write(mainJar, mainJarContent) - f(mainJar) - } - finally Files.deleteIfExists(mainJar) + val mainJar = os.temp(mainJarContent, prefix = fileName.stripSuffix(".jar"), suffix = ".jar") + try f(mainJar) + finally os.remove(mainJar) } def libraryJar( diff --git a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala index a95392b358..c331bb3c17 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala @@ -232,7 +232,7 @@ object NativeImage { val originalClasspath = build.fullClassPath :+ mainJar maybeWithManifestClassPath( createManifest = Properties.isWin, - classPath = originalClasspath.map(os.Path(_, os.pwd)) + classPath = originalClasspath ) { processedClassPath => val (classPath, toClean, scala3extraOptions) = if (!build.scalaParams.scalaBinaryVersion.startsWith("3")) From 4afd3645e28055f7af5c3cac7ba069d5521a3426 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:35:41 +0200 Subject: [PATCH 64/72] Introduce moduleName / name distinction in publish Tested in the upcoming pure Java support --- .../scala/cli/commands/PublishOptions.scala | 5 +- .../scala/scala/cli/commands/Publish.scala | 52 ++++++++++--------- .../UsingPublishDirectiveHandler.scala | 11 ++-- .../scala/build/options/PublishOptions.scala | 1 + website/docs/reference/cli-options.md | 6 ++- website/docs/reference/directives.md | 4 +- 6 files changed, 47 insertions(+), 32 deletions(-) diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala index 3dd3f91e6e..81309de396 100644 --- a/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala +++ b/modules/cli-options/src/main/scala/scala/cli/commands/PublishOptions.scala @@ -25,7 +25,10 @@ final case class PublishOptions( @HelpMessage("Organization to publish artifacts under") organization: Option[String] = None, @Group("Publishing") - @HelpMessage("Module name to publish artifacts as") + @HelpMessage("Name to publish artifacts as") + name: Option[String] = None, + @Group("Publishing") + @HelpMessage("Final name to publish artifacts as, including Scala version and platform suffixes if any") moduleName: Option[String] = None, @Group("Publishing") @HelpMessage("Version to publish artifacts as") diff --git a/modules/cli/src/main/scala/scala/cli/commands/Publish.scala b/modules/cli/src/main/scala/scala/cli/commands/Publish.scala index 130d48048e..618858675b 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Publish.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Publish.scala @@ -49,7 +49,8 @@ object Publish extends ScalaCommand[PublishOptions] { notForBloopOptions = baseOptions.notForBloopOptions.copy( publishOptions = baseOptions.notForBloopOptions.publishOptions.copy( organization = organization.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)), - name = moduleName.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)), + name = ops.name.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)), + moduleName = moduleName.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)), version = version.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)), url = url.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)), license = value { @@ -220,10 +221,25 @@ object Publish extends ScalaCommand[PublishOptions] { case Some(org0) => org0.value case None => value(defaultOrganization) } - val name = publishOptions.name match { + + val moduleName = publishOptions.moduleName match { case Some(name0) => name0.value - case None => value(defaultName) + case None => + val name = publishOptions.name match { + case Some(name0) => name0.value + case None => value(defaultName) + } + val params = build.artifacts.params + val pf = publishOptions.scalaPlatformSuffix.getOrElse { + // FIXME Allow full cross version too + "_" + params.scalaBinaryVersion + } + val sv = publishOptions.scalaVersionSuffix.getOrElse { + params.platform.fold("")("_" + _) + } + name + pf + sv } + val ver = publishOptions.version match { case Some(ver0) => ver0.value case None => @@ -243,18 +259,6 @@ object Publish extends ScalaCommand[PublishOptions] { (dep0.module.organization, dep0.module.name, dep0.version, config) } - val fullName = { - val params = build.artifacts.params - val pf = publishOptions.scalaPlatformSuffix.getOrElse { - // FIXME Allow full cross version too - "_" + params.scalaBinaryVersion - } - val sv = publishOptions.scalaVersionSuffix.getOrElse { - params.platform.fold("")("_" + _) - } - name + pf + sv - } - val mainClassOpt = build.options.mainClass.orElse { build.retainedMainClass match { case Left(_: NoMainClassFoundError) => None @@ -265,12 +269,12 @@ object Publish extends ScalaCommand[PublishOptions] { } } val mainJarContent = Library.libraryJar(build, mainClassOpt) - val mainJar = workingDir / org / s"$fullName-$ver.jar" + val mainJar = workingDir / org / s"$moduleName-$ver.jar" os.write(mainJar, mainJarContent, createFolders = true) val pomContent = Pom.create( organization = coursier.Organization(org), - moduleName = coursier.ModuleName(fullName), + moduleName = coursier.ModuleName(moduleName), version = ver, packaging = None, url = publishOptions.url.map(_.value), @@ -291,7 +295,7 @@ object Publish extends ScalaCommand[PublishOptions] { val sourceJarOpt = if (publishOptions.sourceJar.getOrElse(true)) { val content = Package.sourceJar(build, now.toEpochMilli) - val sourceJar = workingDir / org / s"$fullName-$ver-sources.jar" + val sourceJar = workingDir / org / s"$moduleName-$ver-sources.jar" os.write(sourceJar, content, createFolders = true) Some(sourceJar) } @@ -304,32 +308,32 @@ object Publish extends ScalaCommand[PublishOptions] { case None => None case Some(docBuild) => val content = value(Package.docJar(docBuild, logger, Nil)) - val docJar = workingDir / org / s"$fullName-$ver-javadoc.jar" + val docJar = workingDir / org / s"$moduleName-$ver-javadoc.jar" os.write(docJar, content, createFolders = true) Some(docJar) } else None - val basePath = Path(org.split('.').toSeq ++ Seq(fullName, ver)) + val basePath = Path(org.split('.').toSeq ++ Seq(moduleName, ver)) val mainEntries = Seq( - (basePath / s"$fullName-$ver.pom") -> Content.InMemory( + (basePath / s"$moduleName-$ver.pom") -> Content.InMemory( now, pomContent.getBytes(StandardCharsets.UTF_8) ), - (basePath / s"$fullName-$ver.jar") -> Content.File(mainJar.toNIO) + (basePath / s"$moduleName-$ver.jar") -> Content.File(mainJar.toNIO) ) val sourceJarEntries = sourceJarOpt .map { sourceJar => - (basePath / s"$fullName-$ver-sources.jar") -> Content.File(sourceJar.toNIO) + (basePath / s"$moduleName-$ver-sources.jar") -> Content.File(sourceJar.toNIO) } .toSeq val docJarEntries = docJarOpt .map { docJar => - (basePath / s"$fullName-$ver-javadoc.jar") -> Content.File(docJar.toNIO) + (basePath / s"$moduleName-$ver-javadoc.jar") -> Content.File(docJar.toNIO) } .toSeq diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala index b937e780a4..60af87e04a 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingPublishDirectiveHandler.scala @@ -12,23 +12,24 @@ case object UsingPublishDirectiveHandler extends UsingDirectiveHandler { def name = "Publish" def description = "Set parameters for publishing" - def usage = s"//> using $prefix(organization|moduleName|version) [value]" + def usage = s"//> using $prefix(organization|name|version) [value]" override def usageMd = s"""`//> using ${prefix}organization `"value" - |`//> using ${prefix}moduleName `"value" + |`//> using ${prefix}name `"value" |`//> using ${prefix}version `"value" |""".stripMargin private def q = "\"" override def examples = Seq( s"//> using ${prefix}organization ${q}io.github.myself$q", - s"//> using ${prefix}moduleName ${q}my-library$q", + s"//> using ${prefix}name ${q}my-library$q", s"//> using ${prefix}version ${q}0.1.1$q" ) def keys = Seq( "organization", "name", + "moduleName", "version", "computeVersion", "compute-version", @@ -77,8 +78,10 @@ case object UsingPublishDirectiveHandler extends UsingDirectiveHandler { val publishOptions = strippedKey match { case "organization" => PublishOptions(organization = Some(singleValue)) - case "name" | "moduleName" => + case "name" => PublishOptions(name = Some(singleValue)) + case "moduleName" | "module-name" => + PublishOptions(moduleName = Some(singleValue)) case "version" => PublishOptions(version = Some(singleValue)) case "computeVersion" | "compute-version" => diff --git a/modules/options/src/main/scala/scala/build/options/PublishOptions.scala b/modules/options/src/main/scala/scala/build/options/PublishOptions.scala index d41035364f..0401175c9f 100644 --- a/modules/options/src/main/scala/scala/build/options/PublishOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/PublishOptions.scala @@ -7,6 +7,7 @@ import scala.cli.signing.shared.PasswordOption final case class PublishOptions( organization: Option[Positioned[String]] = None, name: Option[Positioned[String]] = None, + moduleName: Option[Positioned[String]] = None, version: Option[Positioned[String]] = None, url: Option[Positioned[String]] = None, license: Option[Positioned[License]] = None, diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 5a482092dd..5094ba3970 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -922,9 +922,13 @@ Directory where temporary files for publishing should be written Organization to publish artifacts under +#### `--name` + +Name to publish artifacts as + #### `--module-name` -Module name to publish artifacts as +Final name to publish artifacts as, including Scala version and platform suffixes if any #### `--version` diff --git a/website/docs/reference/directives.md b/website/docs/reference/directives.md index f37a460200..501ed7714a 100644 --- a/website/docs/reference/directives.md +++ b/website/docs/reference/directives.md @@ -110,14 +110,14 @@ Set the default platform to Scala.js or Scala Native Set parameters for publishing `//> using publish.organization `"value" -`//> using publish.moduleName `"value" +`//> using publish.name `"value" `//> using publish.version `"value" #### Examples `//> using publish.organization "io.github.myself"` -`//> using publish.moduleName "my-library"` +`//> using publish.name "my-library"` `//> using publish.version "0.1.1"` From 975e97a8bb8b91dbeeff295fcb9b46b4563d98da Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:35:53 +0200 Subject: [PATCH 65/72] NIT Refacto in Runner --- .../scala/scala/build/internal/Runner.scala | 35 +++++++++++++++++-- .../main/scala/scala/cli/commands/Fmt.scala | 3 +- .../scala/scala/cli/commands/Metabrowse.scala | 2 +- .../cli/commands/pgp/PgpExternalCommand.scala | 5 ++- .../scala/cli/internal/ScalaJsLinker.scala | 8 ++--- .../scala/cli/packaging/NativeImage.scala | 4 +-- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/internal/Runner.scala b/modules/build/src/main/scala/scala/build/internal/Runner.scala index 2aec07f32d..2561b89c9d 100644 --- a/modules/build/src/main/scala/scala/build/internal/Runner.scala +++ b/modules/build/src/main/scala/scala/build/internal/Runner.scala @@ -23,12 +23,40 @@ import scala.util.Properties object Runner { - def run( + def maybeExec( commandName: String, command: Seq[String], logger: Logger, allowExecve: Boolean = false, cwd: Option[os.Path] = None + ): Process = + run0( + commandName, + command, + logger, + allowExecve = true, + cwd + ) + + def run( + command: Seq[String], + logger: Logger, + cwd: Option[os.Path] = None + ): Process = + run0( + "unused", + command, + logger, + allowExecve = false, + cwd + ) + + def run0( + commandName: String, + command: Seq[String], + logger: Logger, + allowExecve: Boolean, + cwd: Option[os.Path] ): Process = { import logger.{log, debug} @@ -83,7 +111,10 @@ object Runner { ) ++ args - run("java", command, logger, allowExecve, cwd = cwd) + if (allowExecve) + maybeExec("java", command, logger, cwd = cwd) + else + run(command, logger, cwd = cwd) } private def endsWithCaseInsensitive(s: String, suffix: String): Boolean = diff --git a/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala b/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala index 477453765f..a1a82c14e9 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala @@ -174,11 +174,10 @@ object Fmt extends ScalaCommand[FmtOptions] { sourceFiles.map(_.toString) ++ dialectArgs ++ options.scalafmtCliOptions - Runner.run( + Runner.maybeExec( "scalafmt", command, logger, - allowExecve = true, cwd = Some(workspace) ).waitFor() } diff --git a/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala b/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala index d13ae70bd8..55d17f9513 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala @@ -181,7 +181,7 @@ object Metabrowse extends ScalaCommand[MetabrowseOptions] { message ) - Runner.run("metabrowse", command, logger, allowExecve = true) + Runner.maybeExec("metabrowse", command, logger) } } diff --git a/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala b/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala index 40d33a2329..a142118eb6 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala @@ -35,11 +35,10 @@ abstract class PgpExternalCommand extends ExternalCommand { val command = Seq(launcher.toString) ++ externalCommand ++ remainingArgs - val retCode = Runner.run( + val retCode = Runner.maybeExec( progName, command, - logger, - allowExecve = true + logger ).waitFor() if (retCode != 0) diff --git a/modules/cli/src/main/scala/scala/cli/internal/ScalaJsLinker.scala b/modules/cli/src/main/scala/scala/cli/internal/ScalaJsLinker.scala index 43c61b6b3e..c66ae10810 100644 --- a/modules/cli/src/main/scala/scala/cli/internal/ScalaJsLinker.scala +++ b/modules/cli/src/main/scala/scala/cli/internal/ScalaJsLinker.scala @@ -132,12 +132,8 @@ object ScalaJsLinker { ) } - val cmd = command ++ allArgs.flatMap(_.value) - val res = Runner.run( - "unused", - cmd, - logger - ) + val cmd = command ++ allArgs.flatMap(_.value) + val res = Runner.run(cmd, logger) val retCode = res.waitFor() if (retCode == 0) diff --git a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala index c331bb3c17..bdcfd265c7 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala @@ -280,10 +280,10 @@ object NativeImage { case Some(vcvars) => runFromVcvarsBat(command, vcvars, nativeImageWorkDir, logger) case None => - Runner.run("unused", command, logger).waitFor() + Runner.run(command, logger).waitFor() } else - Runner.run("unused", command, logger).waitFor() + Runner.run(command, logger).waitFor() if (exitCode == 0) { val actualDest = if (Properties.isWin) From cecfa44967c4014585de8d1c9abb7d065708373b Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:36:12 +0200 Subject: [PATCH 66/72] NIT Refacto in SimpleScalaCompiler --- .../build/compiler/SimpleScalaCompiler.scala | 86 +++++++++++-------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala b/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala index fec9330151..601ec4b8e1 100644 --- a/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala +++ b/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala @@ -25,6 +25,53 @@ final case class SimpleScalaCompiler( override def usesClassDir: Boolean = !scaladoc + private def runScalacLike( + project: Project, + mainClass: String, + outputDir: os.Path, + logger: Logger + ): Boolean = { + + os.makeDir.all(outputDir) + + // initially adapted from https://github.com/VirtusLab/scala-cli/pull/103/files#diff-d13a7e6d602b8f84d9177e3138487872f0341d006accfe425886a561f029a9c3R120 and around + + val args = + project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ + Seq( + "-d", + outputDir.toString, + "-cp", + project.classPath.map(_.toString).mkString(File.pathSeparator) + ) ++ + project.sources.map(_.toString) + + val javaCommand = project.javaHomeOpt match { + case Some(javaHome) => + val ext = if (Properties.isWin) ".exe" else "" + val path = javaHome / "bin" / s"java$ext" + path.toString + case None => defaultJavaCommand + } + + val javaOptions = defaultJavaOptions ++ + project.javacOptions + .filter(_.startsWith("-J")) + .map(_.stripPrefix("-J")) + + val res = Runner.runJvm( + javaCommand, + javaOptions, + project.scalaCompiler.map(_.compilerClassPath.map(_.toIO)).getOrElse(Nil), + mainClass, + args, + logger, + cwd = Some(project.workspace) + ).waitFor() + + res == 0 + } + def compile( project: Project, logger: Logger @@ -49,44 +96,7 @@ final case class SimpleScalaCompiler( if (isScala2 && scaladoc) project.scaladocDir else project.classesDir - os.makeDir.all(outputDir) - - // initially adapted from https://github.com/VirtusLab/scala-cli/pull/103/files#diff-d13a7e6d602b8f84d9177e3138487872f0341d006accfe425886a561f029a9c3R120 and around - - val args = - project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ - Seq( - "-d", - outputDir.toString, - "-cp", - project.classPath.map(_.toString).mkString(File.pathSeparator) - ) ++ - project.sources.map(_.toString) - - val javaCommand = project.javaHomeOpt match { - case Some(javaHome) => - val ext = if (Properties.isWin) ".exe" else "" - val path = javaHome / "bin" / s"java$ext" - path.toString - case None => defaultJavaCommand - } - - val javaOptions = defaultJavaOptions ++ - project.javacOptions - .filter(_.startsWith("-J")) - .map(_.stripPrefix("-J")) - - val res = Runner.runJvm( - javaCommand, - javaOptions, - project.scalaCompiler.map(_.compilerClassPath.map(_.toIO)).getOrElse(Nil), - mainClass, - args, - logger, - cwd = Some(project.workspace) - ).waitFor() - - res == 0 + runScalacLike(project, mainClass, outputDir, logger) } } From 9f3ebad1a676f9c33631ccbf72a846dc58d81183 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Mon, 25 Apr 2022 23:36:27 +0200 Subject: [PATCH 67/72] Add pure Java projects support --- .../src/main/scala/scala/build/Bloop.scala | 2 +- .../src/main/scala/scala/build/Build.scala | 262 +++++++------ .../src/main/scala/scala/build/CrossKey.scala | 14 +- .../main/scala/scala/build/CrossSources.scala | 12 +- .../src/main/scala/scala/build/Inputs.scala | 2 + .../scala/scala/build/ReplArtifacts.scala | 6 +- .../src/main/scala/scala/build/Sources.scala | 7 + .../main/scala/scala/build/bsp/BspImpl.scala | 8 +- .../build/compiler/SimpleJavaCompiler.scala | 51 +++ .../build/compiler/SimpleScalaCompiler.scala | 51 ++- .../scala/build/tests/BuildOptionsTests.scala | 53 +-- .../scala/scala/build/tests/BuildTests.scala | 35 +- .../main/scala/scala/cli/commands/Fmt.scala | 4 +- .../scala/scala/cli/commands/Metabrowse.scala | 11 +- .../scala/scala/cli/commands/Package.scala | 168 +++++--- .../scala/scala/cli/commands/Publish.scala | 44 ++- .../main/scala/scala/cli/commands/Repl.scala | 9 +- .../main/scala/scala/cli/commands/Run.scala | 2 +- .../main/scala/scala/cli/commands/Test.scala | 5 +- .../cli/commands/util/SharedOptionsUtil.scala | 5 +- .../main/scala/scala/cli/exportCmd/Mill.scala | 4 +- .../main/scala/scala/cli/exportCmd/Sbt.scala | 4 +- .../scala/cli/launcher/LauncherCli.scala | 2 +- .../scala/scala/cli/packaging/Library.scala | 18 +- .../scala/cli/packaging/NativeImage.scala | 7 +- .../errors/NoScalaVersionProvidedError.scala | 10 +- .../UsingScalaVersionDirectiveHandler.scala | 6 +- .../integration/PublishTestDefinitions.scala | 2 +- .../cli/integration/PublishTestsDefault.scala | 67 +++- .../main/scala/scala/build/Artifacts.scala | 367 ++++++++++-------- .../scala/scala/build/ScalaArtifacts.scala | 17 + .../scala/scala/build/internals/Util.scala | 29 +- .../scala/build/options/BuildOptions.scala | 222 ++++++----- .../build/options/BuildRequirements.scala | 41 +- .../build/options/HasBuildRequirements.scala | 2 +- .../build/options/MaybeScalaVersion.scala | 23 ++ .../scala/build/options/ScalaOptions.scala | 4 +- 37 files changed, 1011 insertions(+), 565 deletions(-) create mode 100644 modules/build/src/main/scala/scala/build/compiler/SimpleJavaCompiler.scala create mode 100644 modules/options/src/main/scala/scala/build/ScalaArtifacts.scala rename modules/{core => options}/src/main/scala/scala/build/internals/Util.scala (70%) create mode 100644 modules/options/src/main/scala/scala/build/options/MaybeScalaVersion.scala diff --git a/modules/build/src/main/scala/scala/build/Bloop.scala b/modules/build/src/main/scala/scala/build/Bloop.scala index 21dea4b13f..92c7e1ae62 100644 --- a/modules/build/src/main/scala/scala/build/Bloop.scala +++ b/modules/build/src/main/scala/scala/build/Bloop.scala @@ -56,7 +56,7 @@ object Bloop { Artifacts.artifacts( Positioned.none(Seq(dep)), Nil, - params, + Some(params), logger, cache.withMessage(s"Downloading compilation server ${dep.version}") ) diff --git a/modules/build/src/main/scala/scala/build/Build.scala b/modules/build/src/main/scala/scala/build/Build.scala index 9de5743595..9fbfbdfe19 100644 --- a/modules/build/src/main/scala/scala/build/Build.scala +++ b/modules/build/src/main/scala/scala/build/Build.scala @@ -39,7 +39,7 @@ object Build { final case class Successful( inputs: Inputs, options: BuildOptions, - scalaParams: ScalaParameters, + scalaParams: Option[ScalaParameters], scope: Scope, sources: Sources, artifacts: Artifacts, @@ -76,14 +76,15 @@ object Build { } } - def crossKey: CrossKey = - CrossKey( + def crossKey: CrossKey = { + val optKey = scalaParams.map { params => BuildOptions.CrossKey( - scalaParams.scalaVersion, + params.scalaVersion, options.platform.value - ), - scope - ) + ) + } + CrossKey(optKey, scope) + } } final case class Failed( @@ -411,34 +412,37 @@ object Build { inputs: Inputs ): Either[BuildException, Option[ScalaNativeCompatibilityError]] = either { - val scalaVersion = value(options.scalaParams).scalaVersion - val nativeVersion = options.scalaNativeOptions.numeralVersion - val isCompatible = nativeVersion match { - case Some(snNumeralVer) => - if (snNumeralVer < SNNumeralVersion(0, 4, 1) && Properties.isWin) - false - else if (scalaVersion.startsWith("3.0")) - false - else if (scalaVersion.startsWith("3")) - snNumeralVer >= SNNumeralVersion(0, 4, 3) - else if (scalaVersion.startsWith("2.13")) - true - else if (scalaVersion.startsWith("2.12")) - inputs.sourceFiles().forall { - case _: Inputs.AnyScript => false - case _ => true - } - else false - case None => false - } - if (isCompatible) None - else - Some( - new ScalaNativeCompatibilityError( - scalaVersion, - options.scalaNativeOptions.finalVersion + val scalaParamsOpt = value(options.scalaParams) + scalaParamsOpt.flatMap { scalaParams => + val scalaVersion = scalaParams.scalaVersion + val nativeVersion = options.scalaNativeOptions.numeralVersion + val isCompatible = nativeVersion match { + case Some(snNumeralVer) => + if (snNumeralVer < SNNumeralVersion(0, 4, 1) && Properties.isWin) + false + else if (scalaVersion.startsWith("3.0")) + false + else if (scalaVersion.startsWith("3")) + snNumeralVer >= SNNumeralVersion(0, 4, 3) + else if (scalaVersion.startsWith("2.13")) + true + else if (scalaVersion.startsWith("2.12")) + inputs.sourceFiles().forall { + case _: Inputs.AnyScript => false + case _ => true + } + else false + case None => false + } + if (isCompatible) None + else + Some( + new ScalaNativeCompatibilityError( + scalaVersion, + options.scalaNativeOptions.finalVersion + ) ) - ) + } } def build( @@ -658,59 +662,67 @@ object Build { val releaseFlagVersion = releaseFlag(options, compilerJvmVersionOpt, logger).map(_.toString) - val scalaCompilerParams = { + val scalaCompilerParamsOpt = artifacts.scalaOpt match { + case Some(scalaArtifacts) => + val params = value(options.scalaParams).getOrElse { + sys.error( + "Should not happen (inconsistency between Scala parameters in BuildOptions and ScalaArtifacts)" + ) + } - val params = value(options.scalaParams) + val pluginScalacOptions = scalaArtifacts.compilerPlugins.distinct.map { + case (_, _, path) => + ScalacOpt(s"-Xplugin:$path") + } - val pluginScalacOptions = artifacts.compilerPlugins.distinct.map { - case (_, _, path) => - ScalacOpt(s"-Xplugin:$path") - } + val semanticDbScalacOptions = + if (generateSemanticDbs) + if (params.scalaVersion.startsWith("2.")) + Seq( + "-Yrangepos", + "-P:semanticdb:failures:warning", + "-P:semanticdb:synthetics:on", + s"-P:semanticdb:sourceroot:${inputs.workspace}" + ).map(ScalacOpt(_)) + else + Seq( + "-Xsemanticdb", + "-sourceroot", + inputs.workspace.toString + ).map(ScalacOpt(_)) + else Nil + + val sourceRootScalacOptions = + if (params.scalaVersion.startsWith("2.")) Nil + else Seq("-sourceroot", inputs.workspace.toString).map(ScalacOpt(_)) + + val scalaJsScalacOptions = + if (options.platform.value == Platform.JS && !params.scalaVersion.startsWith("2.")) + Seq(ScalacOpt("-scalajs")) + else Nil + + val scalacReleaseV = releaseFlagVersion + .map(v => List("-release", v).map(ScalacOpt(_))) + .getOrElse(Nil) + + val scalacOptions = + options.scalaOptions.scalacOptions.map(_.value) ++ + pluginScalacOptions ++ + semanticDbScalacOptions ++ + sourceRootScalacOptions ++ + scalaJsScalacOptions ++ + scalacReleaseV + + val compilerParams = ScalaCompilerParams( + scalaVersion = params.scalaVersion, + scalaBinaryVersion = params.scalaBinaryVersion, + scalacOptions = scalacOptions.toSeq.map(_.value), + compilerClassPath = scalaArtifacts.compilerClassPath + ) + Some(compilerParams) - val semanticDbScalacOptions = - if (generateSemanticDbs) - if (params.scalaVersion.startsWith("2.")) - Seq( - "-Yrangepos", - "-P:semanticdb:failures:warning", - "-P:semanticdb:synthetics:on", - s"-P:semanticdb:sourceroot:${inputs.workspace}" - ).map(ScalacOpt(_)) - else - Seq( - "-Xsemanticdb", - "-sourceroot", - inputs.workspace.toString - ).map(ScalacOpt(_)) - else Nil - - val sourceRootScalacOptions = - if (params.scalaVersion.startsWith("2.")) Nil - else Seq("-sourceroot", inputs.workspace.toString).map(ScalacOpt(_)) - - val scalaJsScalacOptions = - if (options.platform.value == Platform.JS && !params.scalaVersion.startsWith("2.")) - Seq(ScalacOpt("-scalajs")) - else Nil - - val scalacReleaseV = releaseFlagVersion - .map(v => List("-release", v).map(ScalacOpt(_))) - .getOrElse(Nil) - - val scalacOptions = - options.scalaOptions.scalacOptions.map(_.value) ++ - pluginScalacOptions ++ - semanticDbScalacOptions ++ - sourceRootScalacOptions ++ - scalaJsScalacOptions ++ - scalacReleaseV - - ScalaCompilerParams( - scalaVersion = params.scalaVersion, - scalaBinaryVersion = params.scalaBinaryVersion, - scalacOptions = scalacOptions.toSeq.map(_.value), - compilerClassPath = artifacts.compilerClassPath - ) + case None => + None } val javacOptions = { @@ -761,7 +773,7 @@ object Build { workspace = inputs.workspace, classesDir = classesDir0, scaladocDir = scaladocDir, - scalaCompiler = Some(scalaCompilerParams), + scalaCompiler = scalaCompilerParamsOpt, scalaJsOptions = if (options.platform.value == Platform.JS) Some(options.scalaJsOptions.config(logger)) else None, @@ -789,45 +801,64 @@ object Build { compilerJvmVersionOpt: Option[Positioned[Int]], scope: Scope, compiler: ScalaCompiler, - logger: Logger - ): Either[BuildException, (os.Path, ScalaParameters, Artifacts, Project, Boolean)] = either { + logger: Logger, + buildClient: BloopBuildClient + ): Either[BuildException, (os.Path, Option[ScalaParameters], Artifacts, Project, Boolean)] = + either { - val params = value(options.scalaParams) + val options0 = + if (sources.hasJava && !sources.hasScala) + options.copy( + scalaOptions = options.scalaOptions.copy( + scalaVersion = options.scalaOptions.scalaVersion.orElse { + Some(MaybeScalaVersion.none) + } + ) + ) + else + options + val params = value(options0.scalaParams) - val classesDir0 = classesDir(inputs.workspace, inputs.projectName, scope) + val scopeParams = + if (scope == Scope.Main) Nil + else Seq(scope.name) - val artifacts = value(options.artifacts(logger)) + buildClient.setProjectParams(scopeParams ++ value(options0.projectParams)) - value(validate(logger, options)) + val classesDir0 = classesDir(inputs.workspace, inputs.projectName, scope) - val project = value { - buildProject( - inputs, - sources, - generatedSources, - options, - compilerJvmVersionOpt, - scope, - logger - ) - } + val artifacts = value(options0.artifacts(logger)) - val projectChanged = compiler.prepareProject(project, logger) + value(validate(logger, options0)) - if (compiler.usesClassDir && projectChanged && os.isDir(classesDir0)) { - logger.debug(s"Clearing $classesDir0") - os.list(classesDir0).foreach { p => - logger.debug(s"Removing $p") - try os.remove.all(p) - catch { - case ex: FileSystemException => - logger.debug(s"Ignoring $ex while cleaning up $p") + val project = value { + buildProject( + inputs, + sources, + generatedSources, + options0, + compilerJvmVersionOpt, + scope, + logger + ) + } + + val projectChanged = compiler.prepareProject(project, logger) + + if (compiler.usesClassDir && projectChanged && os.isDir(classesDir0)) { + logger.debug(s"Clearing $classesDir0") + os.list(classesDir0).foreach { p => + logger.debug(s"Removing $p") + try os.remove.all(p) + catch { + case ex: FileSystemException => + logger.debug(s"Ignoring $ex while cleaning up $p") + } } } - } - (classesDir0, params, artifacts, project, projectChanged) - } + (classesDir0, params, artifacts, project, projectChanged) + } def buildOnce( inputs: Inputs, @@ -856,7 +887,8 @@ object Build { compiler.jvmVersion, scope, compiler, - logger + logger, + buildClient ) } diff --git a/modules/build/src/main/scala/scala/build/CrossKey.scala b/modules/build/src/main/scala/scala/build/CrossKey.scala index 65b57d12cc..4f04db5868 100644 --- a/modules/build/src/main/scala/scala/build/CrossKey.scala +++ b/modules/build/src/main/scala/scala/build/CrossKey.scala @@ -1,13 +1,15 @@ package scala.build -import scala.build.options.{BuildOptions, Platform, Scope} +import scala.build.options.{BuildOptions, MaybeScalaVersion, Platform, Scope} final case class CrossKey( - optionsKey: BuildOptions.CrossKey, + optionsKey: Option[BuildOptions.CrossKey], scope: Scope ) { - def scalaVersion: String = - optionsKey.scalaVersion - def platform: Platform = - optionsKey.platform + def scalaVersion: MaybeScalaVersion = + optionsKey + .map(k => MaybeScalaVersion(k.scalaVersion)) + .getOrElse(MaybeScalaVersion.none) + def platform: Option[Platform] = + optionsKey.map(_.platform) } diff --git a/modules/build/src/main/scala/scala/build/CrossSources.scala b/modules/build/src/main/scala/scala/build/CrossSources.scala index 1970b53097..b91b23fb02 100644 --- a/modules/build/src/main/scala/scala/build/CrossSources.scala +++ b/modules/build/src/main/scala/scala/build/CrossSources.scala @@ -3,7 +3,13 @@ package scala.build import scala.build.EitherCps.{either, value} import scala.build.Ops.* import scala.build.errors.{BuildException, CompositeBuildException} -import scala.build.options.{BuildOptions, BuildRequirements, HasBuildRequirements, Scope} +import scala.build.options.{ + BuildOptions, + BuildRequirements, + HasBuildRequirements, + MaybeScalaVersion, + Scope +} import scala.build.preprocessing.* final case class CrossSources( @@ -24,7 +30,9 @@ final case class CrossSources( val sharedOptions0 = sharedOptions(baseOptions) - val retainedScalaVersion = value(sharedOptions0.scalaParams).scalaVersion + val retainedScalaVersion = value(sharedOptions0.scalaParams) + .map(p => MaybeScalaVersion(p.scalaVersion)) + .getOrElse(MaybeScalaVersion.none) val buildOptionsWithScalaVersion = buildOptions .flatMap(_.withScalaVersion(retainedScalaVersion).toSeq) diff --git a/modules/build/src/main/scala/scala/build/Inputs.scala b/modules/build/src/main/scala/scala/build/Inputs.scala index 2cb2958753..cc60fcd23d 100644 --- a/modules/build/src/main/scala/scala/build/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/Inputs.scala @@ -148,6 +148,8 @@ final case class Inputs( workspace / Constants.workspaceDirName / projectName / "native" def nativeImageWorkDir: os.Path = workspace / Constants.workspaceDirName / projectName / "native-image" + def docJarWorkDir: os.Path = + workspace / Constants.workspaceDirName / projectName / "doc" } object Inputs { diff --git a/modules/build/src/main/scala/scala/build/ReplArtifacts.scala b/modules/build/src/main/scala/scala/build/ReplArtifacts.scala index 29212c865f..a182c9e5ba 100644 --- a/modules/build/src/main/scala/scala/build/ReplArtifacts.scala +++ b/modules/build/src/main/scala/scala/build/ReplArtifacts.scala @@ -48,14 +48,14 @@ object ReplArtifacts { val replArtifacts = Artifacts.artifacts( Positioned.none(allDeps), localRepoOpt.toSeq, - scalaParams, + Some(scalaParams), logger, cache.withMessage(s"Downloading Ammonite $ammoniteVersion") ) val replSourceArtifacts = Artifacts.artifacts( Positioned.none(allDeps), localRepoOpt.toSeq, - scalaParams, + Some(scalaParams), logger, cache.withMessage(s"Downloading Ammonite $ammoniteVersion sources"), classifiersOpt = Some(Set("sources")) @@ -87,7 +87,7 @@ object ReplArtifacts { Artifacts.artifacts( Positioned.none(allDeps), repositories, - scalaParams, + Some(scalaParams), logger, cache.withMessage(s"Downloading Scala compiler ${scalaParams.scalaVersion}") ) diff --git a/modules/build/src/main/scala/scala/build/Sources.scala b/modules/build/src/main/scala/scala/build/Sources.scala index d131cf8b0e..527e4354c3 100644 --- a/modules/build/src/main/scala/scala/build/Sources.scala +++ b/modules/build/src/main/scala/scala/build/Sources.scala @@ -51,6 +51,13 @@ final case class Sources( GeneratedSource(generatedSrcRoot / path, reportingPath, topWrapperLen) } } + + lazy val hasJava = + (paths.iterator.map(_._1.last) ++ inMemory.iterator.map(_.generatedRelPath.last)) + .exists(_.endsWith(".java")) + lazy val hasScala = + (paths.iterator.map(_._1.last) ++ inMemory.iterator.map(_.generatedRelPath.last)) + .exists(_.endsWith(".scala")) } object Sources { diff --git a/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala b/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala index 0d5b5144c8..a2a12ea167 100644 --- a/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala +++ b/modules/build/src/main/scala/scala/build/bsp/BspImpl.scala @@ -100,7 +100,8 @@ final class BspImpl( None, Scope.Main, currentBloopSession.remoteServer, - persistentLogger + persistentLogger, + localClient ) res.left.map((_, Scope.Main)) } @@ -114,7 +115,8 @@ final class BspImpl( None, Scope.Test, currentBloopSession.remoteServer, - persistentLogger + persistentLogger, + localClient ) res.left.map((_, Scope.Test)) } @@ -537,7 +539,7 @@ object BspImpl { sources: Sources, buildOptions: BuildOptions, classesDir: os.Path, - scalaParams: ScalaParameters, + scalaParams: Option[ScalaParameters], artifacts: Artifacts, project: Project, generatedSources: Seq[GeneratedSource], diff --git a/modules/build/src/main/scala/scala/build/compiler/SimpleJavaCompiler.scala b/modules/build/src/main/scala/scala/build/compiler/SimpleJavaCompiler.scala new file mode 100644 index 0000000000..c54f2a721a --- /dev/null +++ b/modules/build/src/main/scala/scala/build/compiler/SimpleJavaCompiler.scala @@ -0,0 +1,51 @@ +package scala.build.compiler + +import java.io.File + +import scala.build.internal.Runner +import scala.build.{Logger, Project} +import scala.util.Properties + +final case class SimpleJavaCompiler( + defaultJavaCommand: String, + defaultJavaOptions: Seq[String] +) { + + def compile( + project: Project, + logger: Logger + ): Boolean = + project.sources.isEmpty || { + val javacCommand = SimpleJavaCompiler.javaCommand(project, "javac") + .getOrElse(defaultJavaCommand) + + val args = project.javacOptions ++ + Seq( + "-d", + project.classesDir.toString, + "-cp", + project.classPath.map(_.toString).mkString(File.pathSeparator) + ) ++ + project.sources.map(_.toString) + + val proc = Runner.run( + Seq(javacCommand) ++ args, + logger, + cwd = Some(project.workspace) + ) + + val res = proc.waitFor() + + res == 0 + } +} + +object SimpleJavaCompiler { + + def javaCommand(project: Project, command: String = "java"): Option[String] = + project.javaHomeOpt.map { javaHome => + val ext = if (Properties.isWin) ".exe" else "" + val path = javaHome / "bin" / s"$command$ext" + path.toString + } +} diff --git a/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala b/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala index 601ec4b8e1..4a62448d83 100644 --- a/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala +++ b/modules/build/src/main/scala/scala/build/compiler/SimpleScalaCompiler.scala @@ -4,7 +4,6 @@ import java.io.File import scala.build.internal.Runner import scala.build.{Logger, Positioned, Project} -import scala.util.Properties final case class SimpleScalaCompiler( defaultJavaCommand: String, @@ -46,13 +45,7 @@ final case class SimpleScalaCompiler( ) ++ project.sources.map(_.toString) - val javaCommand = project.javaHomeOpt match { - case Some(javaHome) => - val ext = if (Properties.isWin) ".exe" else "" - val path = javaHome / "bin" / s"java$ext" - path.toString - case None => defaultJavaCommand - } + val javaCommand = SimpleJavaCompiler.javaCommand(project).getOrElse(defaultJavaCommand) val javaOptions = defaultJavaOptions ++ project.javacOptions @@ -77,28 +70,32 @@ final case class SimpleScalaCompiler( logger: Logger ): Boolean = if (project.sources.isEmpty) true - else { - - val isScala2 = project.scalaCompiler.exists(_.scalaVersion.startsWith("2.")) - - val mainClassOpt = - if (isScala2) - Some { - if (scaladoc) "scala.tools.nsc.ScalaDoc" - else "scala.tools.nsc.Main" + else + project.scalaCompiler match { + case Some(compiler) => + val isScala2 = compiler.scalaVersion.startsWith("2.") + val mainClassOpt = + if (isScala2) + Some { + if (scaladoc) "scala.tools.nsc.ScalaDoc" + else "scala.tools.nsc.Main" + } + else if (scaladoc) None + else Some("dotty.tools.dotc.Main") + + mainClassOpt.forall { mainClass => + + val outputDir = + if (isScala2 && scaladoc) project.scaladocDir + else project.classesDir + + runScalacLike(project, mainClass, outputDir, logger) } - else if (scaladoc) None - else Some("dotty.tools.dotc.Main") - - mainClassOpt.forall { mainClass => - - val outputDir = - if (isScala2 && scaladoc) project.scaladocDir - else project.classesDir - runScalacLike(project, mainClass, outputDir, logger) + case None => + scaladoc || + SimpleJavaCompiler(defaultJavaCommand, defaultJavaOptions).compile(project, logger) } - } def shutdown(): Unit = () diff --git a/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala b/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala index e29ebcb894..37e4af0234 100644 --- a/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/BuildOptionsTests.scala @@ -15,6 +15,7 @@ import scala.build.options.{ BuildOptions, BuildRequirements, InternalOptions, + MaybeScalaVersion, ScalaOptions, ShadowingSeq } @@ -37,12 +38,12 @@ class BuildOptionsTests extends munit.FunSuite { test("-S 3.nightly option works") { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("3.nightly"), + scalaVersion = Some(MaybeScalaVersion("3.nightly")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) assert( scalaParams.scalaVersion.startsWith("3") && scalaParams.scalaVersion.endsWith("-NIGHTLY"), "-S 3.nightly argument does not lead to scala3 nightly build option" @@ -51,10 +52,10 @@ class BuildOptionsTests extends munit.FunSuite { test("-S 3.1.nightly option works") { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("3.1.nightly") + scalaVersion = Some(MaybeScalaVersion("3.1.nightly")) ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) expect( scalaParams.scalaVersion.startsWith("3.1.") && scalaParams.scalaVersion.endsWith("-NIGHTLY"), "-S 3.1.nightly argument does not lead to scala 3.1. nightly build option" @@ -65,7 +66,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some(s"3.${Int.MaxValue}"), + scalaVersion = Some(MaybeScalaVersion(s"3.${Int.MaxValue}")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -82,7 +83,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.11.2"), + scalaVersion = Some(MaybeScalaVersion("2.11.2")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -99,7 +100,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.11"), + scalaVersion = Some(MaybeScalaVersion("2.11")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -116,7 +117,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some(s"3.${Int.MaxValue}.3"), + scalaVersion = Some(MaybeScalaVersion(s"3.${Int.MaxValue}.3")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -133,7 +134,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("3.1.3-RC1-bin-20220213-fd97eee-NIGHTLY"), + scalaVersion = Some(MaybeScalaVersion("3.1.3-RC1-bin-20220213-fd97eee-NIGHTLY")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -150,10 +151,10 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("3.1.2-RC1") + scalaVersion = Some(MaybeScalaVersion("3.1.2-RC1")) ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) assert( scalaParams.scalaVersion == "3.1.2-RC1", "-S 3.1.2-RC1 argument does not lead to 3.1.2-RC1 build option" @@ -164,7 +165,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.12.9-bin-1111111") + scalaVersion = Some(MaybeScalaVersion("2.12.9-bin-1111111")) ) ) assert( @@ -179,7 +180,7 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some(s"2.${Int.MaxValue}"), + scalaVersion = Some(MaybeScalaVersion(s"2.${Int.MaxValue}")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -195,12 +196,12 @@ class BuildOptionsTests extends munit.FunSuite { test("-S 2.nightly option works") { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.nightly"), + scalaVersion = Some(MaybeScalaVersion("2.nightly")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) assert( scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined, "-S 2.nightly argument does not lead to scala2 nightly build option" @@ -210,10 +211,10 @@ class BuildOptionsTests extends munit.FunSuite { test("-S 2.13.nightly option works") { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.13.nightly") + scalaVersion = Some(MaybeScalaVersion("2.13.nightly")) ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) assert( scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined, "-S 2.13.nightly argument does not lead to scala2 nightly build option" @@ -223,10 +224,10 @@ class BuildOptionsTests extends munit.FunSuite { test("-S 2.12.nightly option works") { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.12.nightly") + scalaVersion = Some(MaybeScalaVersion("2.12.nightly")) ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) assert( scala2NightlyRegex.unapplySeq(scalaParams.scalaVersion).isDefined, "-S 2.12.nightly argument does not lead to scala2 nightly build option" @@ -236,12 +237,12 @@ class BuildOptionsTests extends munit.FunSuite { test("-S 2.13.9-bin-4505094 option works without repo specification") { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = Some("2.13.9-bin-4505094"), + scalaVersion = Some(MaybeScalaVersion("2.13.9-bin-4505094")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) assert( scalaParams.scalaVersion == "2.13.9-bin-4505094", "-S 2.13.9-bin-4505094 argument does not lead to 2.13.9-bin-4505094 scala version in build option" @@ -274,12 +275,12 @@ class BuildOptionsTests extends munit.FunSuite { ) { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = prefix, + scalaVersion = prefix.map(MaybeScalaVersion(_)), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) val expectedScalaParams = ScalaParameters(expectedScalaVersion) @@ -312,13 +313,13 @@ class BuildOptionsTests extends munit.FunSuite { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = prefix, + scalaVersion = prefix.map(MaybeScalaVersion(_)), scalaBinaryVersion = None, supportedScalaVersionsUrl = Some(s"file://${confFilePath.toString()}") ) ) - val scalaParams = options.scalaParams.orThrow + val scalaParams = options.scalaParams.orThrow.getOrElse(???) val expectedScalaParams = ScalaParameters(expectedScalaVersion) expect(scalaParams == expectedScalaParams) @@ -345,7 +346,7 @@ class BuildOptionsTests extends munit.FunSuite { val extraScalacOpt = Seq("-sourceroot", newSourceRoot.toString) val options = defaultOptions.copy( scalaOptions = defaultOptions.scalaOptions.copy( - scalaVersion = Some("3.1.1"), + scalaVersion = Some(MaybeScalaVersion("3.1.1")), scalacOptions = ShadowingSeq.from( extraScalacOpt .map(ScalacOpt(_)) diff --git a/modules/build/src/test/scala/scala/build/tests/BuildTests.scala b/modules/build/src/test/scala/scala/build/tests/BuildTests.scala index 3893d632ff..8bd59bd575 100644 --- a/modules/build/src/test/scala/scala/build/tests/BuildTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/BuildTests.scala @@ -11,7 +11,14 @@ import scala.build.errors.{ InvalidBinaryScalaVersionError, ScalaNativeCompatibilityError } -import scala.build.options.{BuildOptions, InternalOptions, JavaOpt, ScalacOpt, ShadowingSeq} +import scala.build.options.{ + BuildOptions, + InternalOptions, + JavaOpt, + MaybeScalaVersion, + ScalacOpt, + ShadowingSeq +} import scala.build.tastylib.TastyData import scala.build.tests.TestUtil._ import scala.build.tests.util.BloopServer @@ -46,7 +53,7 @@ abstract class BuildTests(server: Boolean) extends munit.FunSuite { def sv2 = "2.13.5" val defaultOptions = baseOptions.copy( scalaOptions = baseOptions.scalaOptions.copy( - scalaVersion = Some(sv2), + scalaVersion = Some(MaybeScalaVersion(sv2)), scalaBinaryVersion = None ) ) @@ -54,7 +61,7 @@ abstract class BuildTests(server: Boolean) extends munit.FunSuite { def sv3 = "3.0.0" val defaultScala3Options = defaultOptions.copy( scalaOptions = defaultOptions.scalaOptions.copy( - scalaVersion = Some(sv3), + scalaVersion = Some(MaybeScalaVersion(sv3)), scalaBinaryVersion = None ) ) @@ -637,7 +644,7 @@ abstract class BuildTests(server: Boolean) extends munit.FunSuite { ) val buildOptions = baseOptions.copy( scalaOptions = baseOptions.scalaOptions.copy( - scalaVersion = Some(s"3.${Int.MaxValue}.3"), + scalaVersion = Some(MaybeScalaVersion(s"3.${Int.MaxValue}.3")), scalaBinaryVersion = None, supportedScalaVersionsUrl = None ) @@ -897,4 +904,24 @@ abstract class BuildTests(server: Boolean) extends munit.FunSuite { expect(scalaCompilerJarNameOpt.contains("scala3-compiler_3-3.1.0.jar")) } } + + test("Pure Java") { + val inputs = TestInputs( + os.rel / "Foo.java" -> + """package foo; + | + |public class Foo { + | public static void main(String[] args) { + | System.out.println("Hello"); + | } + |} + |""".stripMargin + ) + inputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) => + expect(maybeBuild.exists(_.success)) + val build = maybeBuild.toOption.flatMap(_.successfulOpt).getOrElse(sys.error("cannot happen")) + val cp = build.fullClassPath + expect(cp.length == 1) // no scala-library, only the class directory + } + } } diff --git a/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala b/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala index a1a82c14e9..718fe04b97 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Fmt.scala @@ -128,7 +128,7 @@ object Fmt extends ScalaCommand[FmtOptions] { logger.debug("No source files, not formatting anything") else { - def scalaVerOpt = inputsOpt.map { inputs => + def scalaVerOpt = inputsOpt.flatMap { inputs => val crossSources = CrossSources.forInputs( inputs, @@ -141,7 +141,7 @@ object Fmt extends ScalaCommand[FmtOptions] { sharedOptions .scalaParams .orExit(logger) - .scalaVersion + .map(_.scalaVersion) } def dialectOpt = options.dialect.map(_.trim).filter(_.nonEmpty).orElse { diff --git a/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala b/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala index 55d17f9513..0dbb3e5e9c 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Metabrowse.scala @@ -5,7 +5,7 @@ import caseapp._ import java.io.File import java.nio.file.Path -import scala.build.internal.{FetchExternalBinary, Runner} +import scala.build.internal.{Constants, FetchExternalBinary, Runner} import scala.build.{Build, BuildThreads, Logger} import scala.cli.CurrentParams import scala.cli.commands.util.SharedOptionsUtil._ @@ -97,8 +97,11 @@ object Metabrowse extends ScalaCommand[MetabrowseOptions] { .filter(_.nonEmpty) .map(os.Path(_, os.pwd)) .getOrElse { + val sv = successfulBuild.scalaParams + .map(_.scalaVersion) + .getOrElse(Constants.defaultScalaVersion) val (url, changing) = - metabrowseBinaryUrl(successfulBuild.scalaParams.scalaVersion, options) + metabrowseBinaryUrl(sv, options) FetchExternalBinary.fetch( url, changing, @@ -157,7 +160,9 @@ object Metabrowse extends ScalaCommand[MetabrowseOptions] { } def defaultDialect = { - val sv = successfulBuild.scalaParams.scalaVersion + val sv = successfulBuild.scalaParams + .map(_.scalaVersion) + .getOrElse(Constants.defaultScalaVersion) if (sv.startsWith("2.12.")) "Scala212" else if (sv.startsWith("2.13.")) "Scala213" else "Scala3" diff --git a/modules/cli/src/main/scala/scala/cli/commands/Package.scala b/modules/cli/src/main/scala/scala/cli/commands/Package.scala index d7b434c663..f109450dc5 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Package.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Package.scala @@ -11,7 +11,7 @@ import packager.mac.pkg.PkgPackage import packager.rpm.RedHatPackage import packager.windows.WindowsPackage -import java.io.{ByteArrayOutputStream, File} +import java.io.{ByteArrayOutputStream, File, OutputStream} import java.nio.charset.StandardCharsets import java.nio.file.attribute.FileTime import java.util.zip.{ZipEntry, ZipOutputStream} @@ -258,10 +258,10 @@ object Package extends ScalaCommand[PackageOptions] { else os.write(destPath, content) destPath case PackageType.DocJar => - val content = value(docJar(build, logger, extraArgs)) + val docJarPath = value(docJar(build, logger, extraArgs)) alreadyExistsCheck() - if (force) os.write.over(destPath, content) - else os.write(destPath, content) + if (force) os.copy.over(docJarPath, destPath) + else os.copy(docJarPath, destPath) destPath case PackageType.Assembly => assembly(build, destPath, value(mainClass), () => alreadyExistsCheck()) @@ -397,60 +397,114 @@ object Package extends ScalaCommand[PackageOptions] { build: Build.Successful, logger: Logger, extraArgs: Seq[String] - ): Either[BuildException, Array[Byte]] = either { - val isScala2 = build.scalaParams.scalaVersion.startsWith("2.") - if (isScala2) - Library.libraryJar( + ): Either[BuildException, os.Path] = either { + + val workDir = build.inputs.docJarWorkDir + val dest = workDir / "doc.jar" + val cacheData = + CachedBinary.getCacheData( build, - hasActualManifest = false, - contentDirOverride = Some(build.project.scaladocDir) + extraArgs.toList, + dest, + workDir ) - else { - val res = value { - Artifacts.fetch( - Positioned.none(Seq(dep"org.scala-lang::scaladoc:${build.scalaParams.scalaVersion}")), - build.options.finalRepositories, - build.scalaParams, - logger, - build.options.finalCache, - None - ) + + if (cacheData.changed) { + + val contentDir = build.scalaParams match { + case Some(scalaParams) if scalaParams.scalaVersion.startsWith("2.") => + build.project.scaladocDir + case Some(scalaParams) => + val res = value { + Artifacts.fetch( + Positioned.none(Seq(dep"org.scala-lang::scaladoc:${scalaParams.scalaVersion}")), + build.options.finalRepositories, + Some(scalaParams), + logger, + build.options.finalCache, + None + ) + } + val destDir = build.project.scaladocDir + os.makeDir.all(destDir) + val ext = if (Properties.isWin) ".exe" else "" + val baseArgs = Seq( + "-classpath", + build.fullClassPath.map(_.toString).mkString(File.pathSeparator), + "-d", + destDir.toString + ) + val defaultArgs = + if ( + build.options.notForBloopOptions.packageOptions.useDefaultScaladocOptions.getOrElse( + true + ) + ) + defaultScaladocArgs + else + Nil + val args = baseArgs ++ + build.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ + extraArgs ++ + defaultArgs ++ + Seq(build.output.toString) + val retCode = Runner.runJvm( + (build.options.javaHomeLocation().value / "bin" / s"java$ext").toString, + Nil, // FIXME Allow to customize that? + res.files, + "dotty.tools.scaladoc.Main", + args, + logger, + cwd = Some(build.inputs.workspace) + ).waitFor() + if (retCode == 0) + destDir + else + value(Left(new ScaladocGenerationFailedError(retCode))) + case None => + val destDir = build.project.scaladocDir + os.makeDir.all(destDir) + val ext = if (Properties.isWin) ".exe" else "" + val javaSources = + (build.sources.paths.map(_._1) ++ build.generatedSources.map(_.generated)) + .filter(_.last.endsWith(".java")) + val command = Seq( + (build.options.javaHomeLocation().value / "bin" / s"javadoc$ext").toString, + "-d", + destDir.toString, + "-classpath", + build.project.classesDir.toString + ) ++ + javaSources.map(_.toString) + val retCode = Runner.run( + command, + logger, + cwd = Some(build.inputs.workspace) + ).waitFor() + if (retCode == 0) + destDir + else + value(Left(new ScaladocGenerationFailedError(retCode))) } - val destDir = build.project.scaladocDir - os.makeDir.all(destDir) - val ext = if (Properties.isWin) ".exe" else "" - val baseArgs = Seq( - "-classpath", - build.fullClassPath.map(_.toString).mkString(File.pathSeparator), - "-d", - destDir.toString - ) - val defaultArgs = - if ( - build.options.notForBloopOptions.packageOptions.useDefaultScaladocOptions.getOrElse(true) + + var outputStream: OutputStream = null + try { + outputStream = os.write.outputStream(dest, createFolders = true) + Library.writeLibraryJarTo( + outputStream, + build, + hasActualManifest = false, + contentDirOverride = Some(contentDir) ) - defaultScaladocArgs - else - Nil - val args = baseArgs ++ - build.project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil) ++ - extraArgs ++ - defaultArgs ++ - Seq(build.output.toString) - val retCode = Runner.runJvm( - (build.options.javaHomeLocation().value / "bin" / s"java$ext").toString, - Nil, // FIXME Allow to customize that? - res.files, - "dotty.tools.scaladoc.Main", - args, - logger, - cwd = Some(build.inputs.workspace) - ).waitFor() - if (retCode == 0) - Library.libraryJar(build, hasActualManifest = false, contentDirOverride = Some(destDir)) - else - value(Left(new ScaladocGenerationFailedError(retCode))) + } + finally + if (outputStream != null) + outputStream.close() + + CachedBinary.updateProjectAndOutputSha(dest, workDir, cacheData.projectSha) } + + dest } private val generatedSourcesPrefix = os.rel / "META-INF" / "generated" @@ -785,11 +839,17 @@ object Package extends ScalaCommand[PackageOptions] { mainClass ) ++ classpath + val scalaNativeCli = build.artifacts.scalaOpt + .getOrElse { + sys.error("Expected Scala artifacts to be fetched") + } + .scalaNativeCli + val exitCode = Runner.runJvm( build.options.javaHome().value.javaCommand, build.options.javaOptions.javaOpts.toSeq.map(_.value.value), - build.artifacts.scalaNativeCli.map(_.toIO), + scalaNativeCli.map(_.toIO), "scala.scalanative.cli.ScalaNativeLd", args, logger diff --git a/modules/cli/src/main/scala/scala/cli/commands/Publish.scala b/modules/cli/src/main/scala/scala/cli/commands/Publish.scala index 618858675b..8126a544a0 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Publish.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Publish.scala @@ -229,15 +229,19 @@ object Publish extends ScalaCommand[PublishOptions] { case Some(name0) => name0.value case None => value(defaultName) } - val params = build.artifacts.params - val pf = publishOptions.scalaPlatformSuffix.getOrElse { - // FIXME Allow full cross version too - "_" + params.scalaBinaryVersion - } - val sv = publishOptions.scalaVersionSuffix.getOrElse { - params.platform.fold("")("_" + _) + build.artifacts.scalaOpt.map(_.params) match { + case Some(scalaParams) => + val pf = publishOptions.scalaPlatformSuffix.getOrElse { + // FIXME Allow full cross version too + "_" + scalaParams.scalaBinaryVersion + } + val sv = publishOptions.scalaVersionSuffix.getOrElse { + scalaParams.platform.fold("")("_" + _) + } + name + pf + sv + case None => + name } - name + pf + sv } val ver = publishOptions.version match { @@ -251,13 +255,17 @@ object Publish extends ScalaCommand[PublishOptions] { } } - val dependencies = build.artifacts.userDependencies.map { dep => - val dep0 = dep.toCs(build.artifacts.params) - val config = - if (build.scope == Scope.Main) None - else Some(Configuration(build.scope.name)) - (dep0.module.organization, dep0.module.name, dep0.version, config) - } + val dependencies = build.artifacts.userDependencies + .map(_.toCs(build.artifacts.scalaOpt.map(_.params))) + .sequence + .left.map(CompositeBuildException(_)) + .orExit(logger) + .map { dep0 => + val config = + if (build.scope == Scope.Main) None + else Some(Configuration(build.scope.name)) + (dep0.module.organization, dep0.module.name, dep0.version, config) + } val mainClassOpt = build.options.mainClass.orElse { build.retainedMainClass match { @@ -307,9 +315,9 @@ object Publish extends ScalaCommand[PublishOptions] { docBuildOpt match { case None => None case Some(docBuild) => - val content = value(Package.docJar(docBuild, logger, Nil)) - val docJar = workingDir / org / s"$moduleName-$ver-javadoc.jar" - os.write(docJar, content, createFolders = true) + val docJarPath = value(Package.docJar(docBuild, logger, Nil)) + val docJar = workingDir / org / s"$moduleName-$ver-javadoc.jar" + os.copy(docJarPath, docJar, createFolders = true) Some(docJar) } else diff --git a/modules/cli/src/main/scala/scala/cli/commands/Repl.scala b/modules/cli/src/main/scala/scala/cli/commands/Repl.scala index bab14fad44..45ebc32a6f 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Repl.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Repl.scala @@ -167,9 +167,14 @@ object Repl extends ScalaCommand[ReplOptions] { val cache = options.internal.cache.getOrElse(FileCache()) val replArtifacts = value { + val scalaParams = artifacts.scalaOpt + .getOrElse { + sys.error("Expected Scala artifacts to be fetched") + } + .params if (options.notForBloopOptions.replOptions.useAmmonite) ReplArtifacts.ammonite( - artifacts.params, + scalaParams, options.notForBloopOptions.replOptions.ammoniteVersion, artifacts.userDependencies, artifacts.extraClassPath, @@ -180,7 +185,7 @@ object Repl extends ScalaCommand[ReplOptions] { ) else ReplArtifacts.default( - artifacts.params, + scalaParams, artifacts.userDependencies, artifacts.extraClassPath, logger, diff --git a/modules/cli/src/main/scala/scala/cli/commands/Run.scala b/modules/cli/src/main/scala/scala/cli/commands/Run.scala index bc174b9a9a..7e69bd6129 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Run.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Run.scala @@ -72,7 +72,7 @@ object Run extends ScalaCommand[RunOptions] { programArgs, logger, allowExecve = allowTerminate, - jvmRunner = build.options.addRunnerDependency.getOrElse(true) + jvmRunner = build.artifacts.hasJvmRunner )) val onExitProcess = process.onExit().thenApply { p1 => diff --git a/modules/cli/src/main/scala/scala/cli/commands/Test.scala b/modules/cli/src/main/scala/scala/cli/commands/Test.scala index 189c7797e7..0290f7f0d6 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/Test.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/Test.scala @@ -73,9 +73,10 @@ object Test extends ScalaCommand[TestOptions] { val results = for ((s, idx) <- builds0.zipWithIndex) yield { if (printBeforeAfterMessages) { - val optionsKey = s.crossKey.optionsKey + val scalaStr = s.crossKey.scalaVersion.versionOpt.fold("")(v => s" for Scala $v") + val platformStr = s.crossKey.platform.fold("")(p => s", ${p.repr}") System.err.println( - s"${gray}Running tests for Scala ${optionsKey.scalaVersion}, ${optionsKey.platform.repr}$reset" + s"${gray}Running tests$scalaStr$platformStr$reset" ) System.err.println() } diff --git a/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala b/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala index 772ed8ceed..1d61584a5e 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala @@ -89,7 +89,10 @@ object SharedOptionsUtil { else None bo.BuildOptions( scalaOptions = bo.ScalaOptions( - scalaVersion = scalaVersion.map(_.trim).filter(_.nonEmpty), + scalaVersion = scalaVersion + .map(_.trim) + .filter(_.nonEmpty) + .map(bo.MaybeScalaVersion(_)), scalaBinaryVersion = scalaBinaryVersion.map(_.trim).filter(_.nonEmpty), addScalaLibrary = scalaLibrary.orElse(java.map(!_)), generateSemanticDbs = semanticDb, diff --git a/modules/cli/src/main/scala/scala/cli/exportCmd/Mill.scala b/modules/cli/src/main/scala/scala/cli/exportCmd/Mill.scala index 38dc1170cc..4c2b38e0bb 100644 --- a/modules/cli/src/main/scala/scala/cli/exportCmd/Mill.scala +++ b/modules/cli/src/main/scala/scala/cli/exportCmd/Mill.scala @@ -35,7 +35,9 @@ final case class Mill( options.classPathOptions.extraDependencies.toSeq .forall(_.value.nameAttributes == NoAttributes) - val sv = options.scalaOptions.scalaVersion.getOrElse(Constants.defaultScalaVersion) + val sv = options.scalaOptions.scalaVersion + .flatMap(_.versionOpt) // FIXME If versionOpt is empty, the project is pure Java + .getOrElse(Constants.defaultScalaVersion) if (pureJava) MillProject() diff --git a/modules/cli/src/main/scala/scala/cli/exportCmd/Sbt.scala b/modules/cli/src/main/scala/scala/cli/exportCmd/Sbt.scala index 83138f4c06..e5d4a6f459 100644 --- a/modules/cli/src/main/scala/scala/cli/exportCmd/Sbt.scala +++ b/modules/cli/src/main/scala/scala/cli/exportCmd/Sbt.scala @@ -112,7 +112,9 @@ final case class Sbt( private def scalaVersionSettings(options: BuildOptions): SbtProject = { val scalaVerSetting = { - val sv = options.scalaOptions.scalaVersion.getOrElse(Constants.defaultScalaVersion) + val sv = options.scalaOptions.scalaVersion + .flatMap(_.versionOpt) // FIXME If versionOpt is empty, the project is pure Java + .getOrElse(Constants.defaultScalaVersion) s"""scalaVersion := "$sv"""" } diff --git a/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala b/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala index 06d583fea7..b769e8397d 100644 --- a/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala +++ b/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala @@ -34,7 +34,7 @@ object LauncherCli { Artifacts.fetch( Positioned.none(scalaCliDependency), snapshotsRepo, - scalaParameters, + Some(scalaParameters), logger, cache.withMessage(s"Fetching Scala CLI $cliVersion"), None diff --git a/modules/cli/src/main/scala/scala/cli/packaging/Library.scala b/modules/cli/src/main/scala/scala/cli/packaging/Library.scala index 82998b6679..ac30e45037 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/Library.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/Library.scala @@ -1,6 +1,6 @@ package scala.cli.packaging -import java.io.ByteArrayOutputStream +import java.io.{ByteArrayOutputStream, OutputStream} import java.nio.file.attribute.FileTime import java.nio.file.{Files, Path} import java.util.jar.{Attributes => JarAttributes, JarOutputStream} @@ -26,8 +26,18 @@ object Library { hasActualManifest: Boolean = true, contentDirOverride: Option[os.Path] = None ): Array[Byte] = { - val baos = new ByteArrayOutputStream + writeLibraryJarTo(baos, build, mainClassOpt, hasActualManifest, contentDirOverride) + baos.toByteArray + } + + def writeLibraryJarTo( + outputStream: OutputStream, + build: Build.Successful, + mainClassOpt: Option[String] = None, + hasActualManifest: Boolean = true, + contentDirOverride: Option[os.Path] = None + ): Unit = { val manifest = new java.util.jar.Manifest manifest.getMainAttributes.put(JarAttributes.Name.MANIFEST_VERSION, "1.0") @@ -40,7 +50,7 @@ object Library { val contentDir = contentDirOverride.getOrElse(build.output) try { - zos = new JarOutputStream(baos, manifest) + zos = new JarOutputStream(outputStream, manifest) for (path <- os.walk(contentDir) if os.isFile(path)) { val name = path.relativeTo(contentDir).toString val lastModified = os.mtime(path) @@ -56,8 +66,6 @@ object Library { } } finally if (zos != null) zos.close() - - baos.toByteArray } } diff --git a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala index bdcfd265c7..7667ad1afa 100644 --- a/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala +++ b/modules/cli/src/main/scala/scala/cli/packaging/NativeImage.scala @@ -234,10 +234,9 @@ object NativeImage { createManifest = Properties.isWin, classPath = originalClasspath ) { processedClassPath => + val needsProcessing = build.scalaParams.exists(_.scalaVersion.startsWith("3.")) val (classPath, toClean, scala3extraOptions) = - if (!build.scalaParams.scalaBinaryVersion.startsWith("3")) - (processedClassPath, Seq[os.Path](), Seq[String]()) - else { + if (needsProcessing) { val cpString = processedClassPath.mkString(File.pathSeparator) val processed = BytecodeProcessor.processClassPath(cpString, TempCache).toSeq val nativeConfigFile = os.temp(suffix = ".json") @@ -259,6 +258,8 @@ object NativeImage { (cp, nativeConfigFile +: BytecodeProcessor.toClean(processed), options) } + else + (processedClassPath, Seq[os.Path](), Seq[String]()) try { val args = extraOptions ++ scala3extraOptions ++ Seq( diff --git a/modules/core/src/main/scala/scala/build/errors/NoScalaVersionProvidedError.scala b/modules/core/src/main/scala/scala/build/errors/NoScalaVersionProvidedError.scala index f3ab0e9517..7d3c4f90ea 100644 --- a/modules/core/src/main/scala/scala/build/errors/NoScalaVersionProvidedError.scala +++ b/modules/core/src/main/scala/scala/build/errors/NoScalaVersionProvidedError.scala @@ -1,5 +1,11 @@ package scala.build.errors -final class NoScalaVersionProvidedError extends BuildException( - "No Scala version provided to using scala directive" +import scala.build.Position + +final class NoScalaVersionProvidedError( + val dep: dependency.AnyDependency, + positions: Seq[Position] = Nil +) extends BuildException( + s"Got Scala dependency ${dep.render}, but no Scala version is provided", + positions = positions ) diff --git a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaVersionDirectiveHandler.scala b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaVersionDirectiveHandler.scala index a17b18a0d7..2a5f534d51 100644 --- a/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaVersionDirectiveHandler.scala +++ b/modules/directives/src/main/scala/scala/build/preprocessing/directives/UsingScalaVersionDirectiveHandler.scala @@ -1,7 +1,7 @@ package scala.build.preprocessing.directives import scala.build.Logger import scala.build.errors.BuildException -import scala.build.options.{BuildOptions, ScalaOptions} +import scala.build.options.{BuildOptions, MaybeScalaVersion, ScalaOptions} case object UsingScalaVersionDirectiveHandler extends UsingDirectiveHandler { def name = "Scala version" @@ -30,7 +30,9 @@ case object UsingScalaVersionDirectiveHandler extends UsingDirectiveHandler { val options = BuildOptions( scalaOptions = ScalaOptions( - scalaVersion = scalaVersions.headOption.map(_.positioned.value), + scalaVersion = scalaVersions.headOption + .map(_.positioned.value) + .map(MaybeScalaVersion(_)), extraScalaVersions = scalaVersions.drop(1).map(_.positioned.value).toSet ) ) diff --git a/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala index 926f652873..d9ff5dd796 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/PublishTestDefinitions.scala @@ -10,7 +10,7 @@ import scala.jdk.CollectionConverters._ abstract class PublishTestDefinitions(val scalaVersionOpt: Option[String]) extends munit.FunSuite with TestScalaVersionArgs { - private def extraOptions = scalaVersionArgs ++ TestUtil.extraOptions + protected def extraOptions = scalaVersionArgs ++ TestUtil.extraOptions test("simple") { val inputs = TestInputs( diff --git a/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala b/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala index 965b3758db..cf3c41134f 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala @@ -1,3 +1,68 @@ package scala.cli.integration -class PublishTestsDefault extends PublishTestDefinitions(scalaVersionOpt = None) +import com.eed3si9n.expecty.Expecty.expect + +class PublishTestsDefault extends PublishTestDefinitions(scalaVersionOpt = None) { + + test("Pure Java") { + val testOrg = "test-org.foo" + val testName = "foo" + val testVersion = "0.3.1" + val inputs = TestInputs( + Seq( + os.rel / "Foo.java" -> + s"""//> using publish.organization "$testOrg" + |//> using publish.name "$testName" + |//> using publish.version "$testVersion" + | + |package foo; + | + |public class Foo { + | private static boolean checkClass(String clsName) { + | try { + | Thread.currentThread().getContextClassLoader().loadClass(clsName); + | return true; + | } catch (ClassNotFoundException ex) { + | return false; + | } + | } + | + | public static void main(String[] args) { + | boolean hasJuList = checkClass("java.util.List"); + | boolean hasScalaArray = checkClass("scala.Array"); + | if (!hasJuList) { + | System.out.println("Error: java.util.List not found"); + | System.exit(1); + | } + | if (hasScalaArray) { + | System.out.println("Error: unexpectedly found scala.Array"); + | System.exit(1); + | } + | System.out.println("Hello from " + "foo"); + | } + |} + |""".stripMargin + ) + ) + + val repoRelPath = os.rel / "test-repo" + inputs.fromRoot { root => + os.proc(TestUtil.cli, "publish", extraOptions, ".", "-R", repoRelPath) + .call(stdin = os.Inherit, stdout = os.Inherit, cwd = root) + val repoRoot = root / repoRelPath + val baseDir = repoRoot / testOrg.split('.').toSeq / testName / testVersion + expect(os.isDir(baseDir)) + + val res = os.proc( + TestUtil.cs, + "launch", + s"$testOrg:$testName:$testVersion", + "-r", + repoRoot.toNIO.toUri.toASCIIString + ) + .call(stdin = os.Inherit, cwd = root) + val output = res.out.text().trim + expect(output == "Hello from foo") + } + } +} diff --git a/modules/options/src/main/scala/scala/build/Artifacts.scala b/modules/options/src/main/scala/scala/build/Artifacts.scala index 42329548bb..f83577a611 100644 --- a/modules/options/src/main/scala/scala/build/Artifacts.scala +++ b/modules/options/src/main/scala/scala/build/Artifacts.scala @@ -21,23 +21,19 @@ import scala.build.errors.{ import scala.build.internal.Constants import scala.build.internal.Constants.* import scala.build.internal.CsLoggerUtil.* -import scala.build.internal.Util.ScalaDependencyOps +import scala.build.internal.Util.PositionedScalaDependencyOps final case class Artifacts( - compilerDependencies: Seq[AnyDependency], - compilerArtifacts: Seq[(String, os.Path)], - compilerPlugins: Seq[(AnyDependency, String, os.Path)], javacPluginDependencies: Seq[(AnyDependency, String, os.Path)], extraJavacPlugins: Seq[os.Path], userDependencies: Seq[AnyDependency], internalDependencies: Seq[AnyDependency], - scalaJsCli: Seq[os.Path], - scalaNativeCli: Seq[os.Path], detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)], extraClassPath: Seq[os.Path], extraCompileOnlyJars: Seq[os.Path], extraSourceJars: Seq[os.Path], - params: ScalaParameters + scalaOpt: Option[ScalaArtifacts], + hasJvmRunner: Boolean ) { lazy val artifacts: Seq[(String, os.Path)] = detailedArtifacts @@ -55,8 +51,6 @@ final case class Artifacts( (a.url, f) } .toVector - lazy val compilerClassPath: Seq[os.Path] = - compilerArtifacts.map(_._2) lazy val classPath: Seq[os.Path] = artifacts.map(_._2) ++ extraClassPath lazy val compileClassPath: Seq[os.Path] = @@ -67,9 +61,17 @@ final case class Artifacts( object Artifacts { - def apply( + final case class ScalaArtifactsParams( params: ScalaParameters, compilerPlugins: Seq[Positioned[AnyDependency]], + addJsTestBridge: Option[String], + addNativeTestInterface: Option[String], + scalaJsCliVersion: Option[String], + scalaNativeCliVersion: Option[String] + ) + + def apply( + scalaArtifactsParamsOpt: Option[ScalaArtifactsParams], javacPluginDependencies: Seq[Positioned[AnyDependency]], extraJavacPlugins: Seq[os.Path], dependencies: Seq[Positioned[AnyDependency]], @@ -80,30 +82,15 @@ object Artifacts { addStubs: Boolean, addJvmRunner: Option[Boolean], addJvmTestRunner: Boolean, - addJsTestBridge: Option[String], - addNativeTestInterface: Option[String], addJmhDependencies: Option[String], - scalaJsCliVersion: Option[String], - scalaNativeCliVersion: Option[String], extraRepositories: Seq[String], cache: FileCache[Task], logger: Logger ): Either[BuildException, Artifacts] = either { - val compilerDependencies = - if (params.scalaVersion.startsWith("3.")) - Seq( - dep"org.scala-lang::scala3-compiler:${params.scalaVersion}" - ) - else - Seq( - dep"org.scala-lang:scala-compiler:${params.scalaVersion}" - ) - val compilerDependenciesMessage = - s"Downloading Scala ${params.scalaVersion} compiler" - + val addJvmRunner0 = addJvmRunner.getOrElse(true) val jvmRunnerDependencies = - if (addJvmRunner.getOrElse(true)) + if (addJvmRunner0) Seq(dep"$runnerOrganization::$runnerModuleName:$runnerVersion") else Nil @@ -112,15 +99,6 @@ object Artifacts { Seq(dep"$testRunnerOrganization::$testRunnerModuleName:$testRunnerVersion") else Nil - val jsTestBridgeDependencies = addJsTestBridge.toSeq.map { scalaJsVersion => - if (params.scalaVersion.startsWith("2.")) - dep"org.scala-js::scalajs-test-bridge:$scalaJsVersion" - else - dep"org.scala-js:scalajs-test-bridge_2.13:$scalaJsVersion" - } - val nativeTestInterfaceDependencies = addNativeTestInterface.toSeq.map { scalaNativeVersion => - dep"org.scala-native::test-interface::$scalaNativeVersion" - } val jmhDependencies = addJmhDependencies.toSeq.map { version => dep"org.openjdk.jmh:jmh-generator-bytecode:$version" @@ -129,37 +107,157 @@ object Artifacts { val maybeSnapshotRepo = { val hasSnapshots = (jvmRunnerDependencies ++ jvmTestRunnerDependencies) .exists(_.version.endsWith("SNAPSHOT")) || - scalaNativeCliVersion.exists(_.endsWith("SNAPSHOT")) - val runnerNeedsSonatypeSnapshots = Constants.runnerNeedsSonatypeSnapshots(params.scalaVersion) - val stubsNeedSonatypeSnapshots = addStubs && stubsVersion.endsWith("SNAPSHOT") + scalaArtifactsParamsOpt.flatMap(_.scalaNativeCliVersion).exists(_.endsWith("SNAPSHOT")) + val runnerNeedsSonatypeSnapshots = Constants.runnerNeedsSonatypeSnapshots( + scalaArtifactsParamsOpt.fold("")(_.params.scalaVersion) + ) + val stubsNeedSonatypeSnapshots = addStubs && stubsVersion.endsWith("SNAPSHOT") if (hasSnapshots || runnerNeedsSonatypeSnapshots || stubsNeedSonatypeSnapshots) Seq(coursier.Repositories.sonatype("snapshots").root) else Nil } - val scalaJsCliDependency = - scalaJsCliVersion.map { version => - val mod = - if (version.contains("-sc")) cmod"io.github.alexarchambault.tmp:scalajs-cli_2.13" - else cmod"org.scala-js:scalajs-cli_2.13" - Seq(coursier.Dependency(mod, version)) - } - - val scalaNativeCliDependency = - scalaNativeCliVersion.map { version => - val module = cmod"org.scala-native:scala-native-cli_2.12" - Seq(coursier.Dependency(module, version)) - } - val allExtraRepositories = maybeSnapshotRepo ++ extraRepositories + val scalaOpt = scalaArtifactsParamsOpt match { + case Some(scalaArtifactsParams) => + val compilerDependencies = + if (scalaArtifactsParams.params.scalaVersion.startsWith("3.")) + Seq( + dep"org.scala-lang::scala3-compiler:${scalaArtifactsParams.params.scalaVersion}" + ) + else + Seq( + dep"org.scala-lang:scala-compiler:${scalaArtifactsParams.params.scalaVersion}" + ) + val compilerDependenciesMessage = + s"Downloading Scala ${scalaArtifactsParams.params.scalaVersion} compiler" + + val compilerPlugins0 = value { + scalaArtifactsParams.compilerPlugins + .map { posDep => + val posDep0 = + posDep.map(dep => dep.copy(userParams = dep.userParams + ("intransitive" -> None))) + artifacts( + posDep0.map(Seq(_)), + allExtraRepositories, + Some(scalaArtifactsParams.params), + logger, + cache.withMessage(s"Downloading compiler plugin ${posDep.value.render}") + ).map(_.map { case (url, path) => (posDep0.value, url, path) }) + } + .sequence + .left.map(CompositeBuildException(_)) + .map(_.flatten) + } + + val compilerArtifacts = value { + artifacts( + Positioned.none(compilerDependencies), + allExtraRepositories, + Some(scalaArtifactsParams.params), + logger, + cache.withMessage(compilerDependenciesMessage) + ) + } + + def fetchedArtifactToPath(fetched: Fetch.Result): Seq[os.Path] = + fetched.fullDetailedArtifacts.collect { case (_, _, _, Some(f)) => os.Path(f, Os.pwd) } + + val scalaJsCliDependency = + scalaArtifactsParams.scalaJsCliVersion.map { version => + val mod = + if (version.contains("-sc")) cmod"io.github.alexarchambault.tmp:scalajs-cli_2.13" + else cmod"org.scala-js:scalajs-cli_2.13" + Seq(coursier.Dependency(mod, version)) + } + + val fetchedScalaJsCli = scalaJsCliDependency match { + case Some(dependency) => + val forcedVersions = Seq( + cmod"org.scala-js:scalajs-linker_2.13" -> scalaJsVersion + ) + Some( + value { + fetch0( + Positioned.none(dependency), + allExtraRepositories, + None, + forcedVersions, + logger, + cache.withMessage("Downloading Scala.js CLI"), + None + ) + } + ) + case None => + None + } + + val scalaJsCli = fetchedScalaJsCli.toSeq.flatMap(fetchedArtifactToPath) + + val scalaNativeCliDependency = + scalaArtifactsParams.scalaNativeCliVersion.map { version => + val module = cmod"org.scala-native:scala-native-cli_2.12" + Seq(coursier.Dependency(module, version)) + } + + val fetchedScalaNativeCli = scalaNativeCliDependency match { + case Some(dependency) => + Some( + value { + fetch0( + Positioned.none(dependency), + allExtraRepositories, + None, + Nil, + logger, + cache.withMessage("Downloading Scala Native CLI"), + None + ) + } + ) + case None => + None + } + + val scalaNativeCli = fetchedScalaNativeCli.toSeq.flatMap(fetchedArtifactToPath) + + val jsTestBridgeDependencies = + scalaArtifactsParams.addJsTestBridge.toSeq.map { scalaJsVersion => + if (scalaArtifactsParams.params.scalaVersion.startsWith("2.")) + dep"org.scala-js::scalajs-test-bridge:$scalaJsVersion" + else + dep"org.scala-js:scalajs-test-bridge_2.13:$scalaJsVersion" + } + val nativeTestInterfaceDependencies = + scalaArtifactsParams.addNativeTestInterface.toSeq.map { scalaNativeVersion => + dep"org.scala-native::test-interface::$scalaNativeVersion" + } + + val internalDependencies = jsTestBridgeDependencies ++ nativeTestInterfaceDependencies + + val scala = ScalaArtifacts( + compilerDependencies, + compilerArtifacts, + compilerPlugins0, + scalaJsCli, + scalaNativeCli, + internalDependencies, + scalaArtifactsParams.params + ) + Some(scala) + + case None => + None + } + val internalDependencies = jvmRunnerDependencies.map(Positioned.none) ++ jvmTestRunnerDependencies.map(Positioned.none) ++ - jsTestBridgeDependencies.map(Positioned.none) ++ - nativeTestInterfaceDependencies.map(Positioned.none) ++ + scalaOpt.toSeq.flatMap(_.internalDependencies).map(Positioned.none) ++ jmhDependencies.map(Positioned.none) val updatedDependencies = dependencies ++ internalDependencies @@ -185,82 +283,26 @@ object Artifacts { b.result() } - val compilerArtifacts = value { - artifacts( - Positioned.none(compilerDependencies), - allExtraRepositories, - params, - logger, - cache.withMessage(compilerDependenciesMessage) - ) - } - val fetchRes = value { fetch( Positioned.sequence(updatedDependencies), allExtraRepositories, - params, + scalaArtifactsParamsOpt.map(_.params), logger, cache.withMessage(updatedDependenciesMessage), classifiersOpt = Some(Set("_") ++ (if (fetchSources) Set("sources") else Set.empty)) ) } - val fetchedScalaNativeCli = scalaNativeCliDependency match { - case Some(dependency) => - Some( - value { - fetch0( - Positioned.none(dependency), - allExtraRepositories, - None, - Nil, - logger, - cache.withMessage("Downloading Scala Native CLI"), - None - ) - } - ) - case None => - None - } - - def fetchedArtifactToPath(fetched: Fetch.Result): Seq[os.Path] = - fetched.fullDetailedArtifacts.collect { case (_, _, _, Some(f)) => os.Path(f, Os.pwd) } - - val scalaNativeCli = fetchedScalaNativeCli.toSeq.flatMap(fetchedArtifactToPath) - - val fetchedScalaJsCli = scalaJsCliDependency match { - case Some(dependency) => - val forcedVersions = Seq( - cmod"org.scala-js:scalajs-linker_2.13" -> scalaJsVersion - ) - Some( - value { - fetch0( - Positioned.none(dependency), - allExtraRepositories, - None, - forcedVersions, - logger, - cache.withMessage("Downloading Scala.js CLI"), - None - ) - } - ) - case None => - None - } - - val scalaJsCli = fetchedScalaJsCli.toSeq.flatMap(fetchedArtifactToPath) - val extraStubsJars = - if (addStubs) + // stubs add classes for 'import $ivy' and 'import $dep' to work + // we only need those in Scala sources, not in pure Java projects + if (scalaOpt.nonEmpty && addStubs) value { artifacts( Positioned.none(Seq(dep"$stubsOrganization:$stubsModuleName:$stubsVersion")), allExtraRepositories, - params, + scalaArtifactsParamsOpt.map(_.params), logger, cache.withMessage("Downloading internal stub dependency") ).map(_.map(_._2)) @@ -268,29 +310,17 @@ object Artifacts { else Nil - val compilerPlugins0 = value { - compilerPlugins - .map { posDep => - val posDep0 = - posDep.map(dep => dep.copy(userParams = dep.userParams + ("intransitive" -> None))) - artifacts( - posDep0.map(Seq(_)), - allExtraRepositories, - params, - logger, - cache.withMessage(s"Downloading compiler plugin ${posDep.value.render}") - ).map(_.map { case (url, path) => (posDep0.value, url, path) }) - } - .sequence - .left.map(CompositeBuildException(_)) - .map(_.flatten) - } - val javacPlugins0 = value { javacPluginDependencies .map { posDep => val cache0 = cache.withMessage(s"Downloading javac plugin ${posDep.value.render}") - artifacts(posDep.map(Seq(_)), allExtraRepositories, params, logger, cache0) + artifacts( + posDep.map(Seq(_)), + allExtraRepositories, + scalaArtifactsParamsOpt.map(_.params), + logger, + cache0 + ) .map(_.map { case (url, path) => (posDep.value, url, path) }) } .sequence @@ -299,34 +329,31 @@ object Artifacts { } Artifacts( - compilerDependencies, - compilerArtifacts, - compilerPlugins0, javacPlugins0, extraJavacPlugins, dependencies.map(_.value), internalDependencies.map(_.value), - scalaJsCli, - scalaNativeCli, fetchRes.fullDetailedArtifacts.collect { case (d, p, a, Some(f)) => (d, p, a, os.Path(f, Os.pwd)) }, extraClassPath ++ extraStubsJars, extraCompileOnlyJars, extraSourceJars, - params + scalaOpt, + addJvmRunner0 ) } private[build] def artifacts( dependencies: Positioned[Seq[AnyDependency]], extraRepositories: Seq[String], - params: ScalaParameters, + paramsOpt: Option[ScalaParameters], logger: Logger, cache: FileCache[Task], classifiersOpt: Option[Set[String]] = None ): Either[BuildException, Seq[(String, os.Path)]] = either { - val res = value(fetch(dependencies, extraRepositories, params, logger, cache, classifiersOpt)) + val res = + value(fetch(dependencies, extraRepositories, paramsOpt, logger, cache, classifiersOpt)) val result = res .artifacts .iterator @@ -344,35 +371,43 @@ object Artifacts { def fetch( dependencies: Positioned[Seq[AnyDependency]], extraRepositories: Seq[String], - params: ScalaParameters, + paramsOpt: Option[ScalaParameters], logger: Logger, cache: FileCache[Task], classifiersOpt: Option[Set[String]] - ): Either[BuildException, Fetch.Result] = { - val coursierDependenciesWithFallbacks + ): Either[BuildException, Fetch.Result] = either { + val coursierDependenciesWithFallbacks = value { + dependencies.value + .map(Positioned(dependencies.positions, _)) + .map(dep => dep.toCs(paramsOpt).map(csDep => (dep.value, csDep.value))) + .sequence + .left.map(CompositeBuildException(_)) + .map(_.map { + case (dep, csDep) => + val maybeUrl = dep.userParams.get("url").flatten.map(new URL(_)) + val fallback = maybeUrl.map(url => (csDep.module -> csDep.version) -> (url -> true)) + (csDep, fallback) + }) + } + val coursierDependenciesWithFallbacks0 : Positioned[Seq[(CsDependency, Option[((Module, String), (URL, Boolean))])]] = - dependencies.map(positioned => - for { - dep <- positioned - csDep = dep.toCs(params) - maybeUrl = dep.userParams.get("url").flatten.map(new URL(_)) - fallback = maybeUrl.map(url => (csDep.module -> csDep.version) -> (url -> true)) - } yield csDep -> fallback - ) + dependencies.map(_ => coursierDependenciesWithFallbacks) val coursierDependencies: Positioned[Seq[CsDependency]] = - coursierDependenciesWithFallbacks.map(_.map(_._1)) + coursierDependenciesWithFallbacks0.map(_.map(_._1)) val fallbacks: Map[(Module, String), (URL, Boolean)] = - coursierDependenciesWithFallbacks.value.flatMap(_._2).toMap - fetch0( - coursierDependencies, - extraRepositories, - Some(params.scalaVersion), - Nil, - logger, - cache, - classifiersOpt, - fallbacks - ) + coursierDependenciesWithFallbacks0.value.flatMap(_._2).toMap + value { + fetch0( + coursierDependencies, + extraRepositories, + paramsOpt.map(_.scalaVersion), + Nil, + logger, + cache, + classifiersOpt, + fallbacks + ) + } } def fetch0( diff --git a/modules/options/src/main/scala/scala/build/ScalaArtifacts.scala b/modules/options/src/main/scala/scala/build/ScalaArtifacts.scala new file mode 100644 index 0000000000..1a3d072578 --- /dev/null +++ b/modules/options/src/main/scala/scala/build/ScalaArtifacts.scala @@ -0,0 +1,17 @@ +package scala.build + +import dependency.{AnyDependency, ScalaParameters} + +final case class ScalaArtifacts( + compilerDependencies: Seq[AnyDependency], + compilerArtifacts: Seq[(String, os.Path)], + compilerPlugins: Seq[(AnyDependency, String, os.Path)], + scalaJsCli: Seq[os.Path], + scalaNativeCli: Seq[os.Path], + internalDependencies: Seq[AnyDependency], + params: ScalaParameters +) { + + lazy val compilerClassPath: Seq[os.Path] = + compilerArtifacts.map(_._2) +} diff --git a/modules/core/src/main/scala/scala/build/internals/Util.scala b/modules/options/src/main/scala/scala/build/internals/Util.scala similarity index 70% rename from modules/core/src/main/scala/scala/build/internals/Util.scala rename to modules/options/src/main/scala/scala/build/internals/Util.scala index 2574ab044e..8d7f24a7b8 100644 --- a/modules/core/src/main/scala/scala/build/internals/Util.scala +++ b/modules/options/src/main/scala/scala/build/internals/Util.scala @@ -1,10 +1,13 @@ package scala.build.internal +import dependency.NoAttributes + import java.io.{File, PrintStream} import java.util.concurrent.ThreadFactory import java.util.concurrent.atomic.AtomicInteger -import scala.build.Os +import scala.build.errors.NoScalaVersionProvidedError +import scala.build.{Os, Positioned} object Util { @@ -58,6 +61,30 @@ object Util { implicit class ScalaDependencyOps(private val dep: dependency.AnyDependency) extends AnyVal { def toCs(params: dependency.ScalaParameters): coursier.Dependency = dep.applyParams(params).toCs + def toCs(paramsOpt: Option[dependency.ScalaParameters]) + : Either[NoScalaVersionProvidedError, coursier.Dependency] = + paramsOpt match { + case Some(params) => Right(toCs(params)) + case None => + val isJavaDep = dep.module.nameAttributes == NoAttributes && dep.exclude.forall( + _.nameAttributes == NoAttributes + ) + if (isJavaDep) + Right(dep.asInstanceOf[dependency.Dependency].toCs) + else + Left(new NoScalaVersionProvidedError(dep)) + } + } + implicit class PositionedScalaDependencyOps( + private val posDep: Positioned[dependency.AnyDependency] + ) extends AnyVal { + def toCs(paramsOpt: Option[dependency.ScalaParameters]) + : Either[NoScalaVersionProvidedError, Positioned[coursier.Dependency]] = { + val res = posDep.map(_.toCs(paramsOpt)) + res.value + .left.map(_ => new NoScalaVersionProvidedError(posDep.value, posDep.positions)) + .map(Positioned(res.positions, _)) + } } def isFullScalaVersion(sv: String): Boolean = diff --git a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala index ab2d68be40..b6b1ef6d51 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildOptions.scala @@ -42,15 +42,20 @@ final case class BuildOptions( scalaOptions.platform.getOrElse(Positioned(List(Position.Custom("DEFAULT")), Platform.JVM)) lazy val projectParams: Either[BuildException, Seq[String]] = either { - val platform0 = platform.value match { - case Platform.JVM => "JVM" - case Platform.JS => "Scala.js" - case Platform.Native => "Scala Native" + value(scalaParams) match { + case Some(scalaParams0) => + val platform0 = platform.value match { + case Platform.JVM => "JVM" + case Platform.JS => "Scala.js" + case Platform.Native => "Scala Native" + } + Seq(s"Scala ${scalaParams0.scalaVersion}", platform0) + case None => + Seq("Java") } - Seq(s"Scala ${value(scalaParams).scalaVersion}", platform0) } - lazy val scalaVersionIsExotic = scalaParams.exists { scalaParameters => + lazy val scalaVersionIsExotic = scalaParams.toOption.flatten.exists { scalaParameters => scalaParameters.scalaVersion.startsWith("2") && scalaParameters.scalaVersion.exists(_.isLetter) } @@ -62,26 +67,30 @@ final case class BuildOptions( } private def scalaLibraryDependencies: Either[BuildException, Seq[AnyDependency]] = either { - if (platform.value != Platform.Native && scalaOptions.addScalaLibrary.getOrElse(true)) { - val scalaParams0 = value(scalaParams) - val lib = - if (scalaParams0.scalaVersion.startsWith("3.")) - dep"org.scala-lang::scala3-library::${scalaParams0.scalaVersion}" - else - dep"org.scala-lang:scala-library:${scalaParams0.scalaVersion}" - Seq(lib) + value(scalaParams).toSeq.flatMap { scalaParams0 => + if (platform.value != Platform.Native && scalaOptions.addScalaLibrary.getOrElse(true)) + Seq( + if (scalaParams0.scalaVersion.startsWith("3.")) + dep"org.scala-lang::scala3-library::${scalaParams0.scalaVersion}" + else + dep"org.scala-lang:scala-library:${scalaParams0.scalaVersion}" + ) + else Nil } - else Nil } private def maybeJsDependencies: Either[BuildException, Seq[AnyDependency]] = either { if (platform.value == Platform.JS) - scalaJsOptions.jsDependencies(value(scalaParams).scalaVersion) + value(scalaParams).toSeq.flatMap { scalaParams0 => + scalaJsOptions.jsDependencies(scalaParams0.scalaVersion) + } else Nil } private def maybeNativeDependencies: Either[BuildException, Seq[AnyDependency]] = either { if (platform.value == Platform.Native) - scalaNativeOptions.nativeDependencies(value(scalaParams).scalaVersion) + value(scalaParams).toSeq.flatMap { scalaParams0 => + scalaNativeOptions.nativeDependencies(scalaParams0.scalaVersion) + } else Nil } private def dependencies: Either[BuildException, Seq[Positioned[AnyDependency]]] = either { @@ -93,7 +102,7 @@ final case class BuildOptions( private def semanticDbPlugins: Either[BuildException, Seq[AnyDependency]] = either { val generateSemDbs = scalaOptions.generateSemanticDbs.getOrElse(false) && - value(scalaParams).scalaVersion.startsWith("2.") + value(scalaParams).exists(_.scalaVersion.startsWith("2.")) if (generateSemDbs) Seq( dep"$semanticDbPluginOrganization:::$semanticDbPluginModuleName:$semanticDbPluginVersion" @@ -104,7 +113,9 @@ final case class BuildOptions( private def maybeJsCompilerPlugins: Either[BuildException, Seq[AnyDependency]] = either { if (platform.value == Platform.JS) - scalaJsOptions.compilerPlugins(value(scalaParams).scalaVersion) + value(scalaParams).toSeq.flatMap { scalaParams0 => + scalaJsOptions.compilerPlugins(scalaParams0.scalaVersion) + } else Nil } private def maybeNativeCompilerPlugins: Seq[AnyDependency] = @@ -294,7 +305,11 @@ final case class BuildOptions( def finalRepositories: Seq[String] = { val nightlyRepos = - if (scalaParams.exists(params => ScalaVersionUtil.isScala2Nightly(params.scalaVersion))) + if ( + scalaParams.toOption.flatten.exists(params => + ScalaVersionUtil.isScala2Nightly(params.scalaVersion) + ) + ) scala2NightlyRepo else Nil @@ -304,100 +319,129 @@ final case class BuildOptions( internal.localRepository.toSeq } - lazy val scalaParams: Either[BuildException, ScalaParameters] = either { + lazy val scalaParams: Either[BuildException, Option[ScalaParameters]] = either { lazy val maxSupportedStableScalaVersions = latestSupportedStableScalaVersion() lazy val latestSupportedStableVersions = maxSupportedStableScalaVersions.map(_.repr) - val scalaVersion = value { - scalaOptions.scalaVersion match { - case Some("3.nightly") => - ScalaVersionUtil.GetNightly.scala3(finalCache) - case Some(scala3NightlyNicknameRegex(threeSubBinaryNum)) => - ScalaVersionUtil.GetNightly.scala3X( - threeSubBinaryNum, - finalCache, - latestSupportedStableVersions - ) - case Some("2.nightly" | "2.13.nightly") => - ScalaVersionUtil.GetNightly.scala2("2.13", finalCache) - case Some("2.12.nightly") => - ScalaVersionUtil.GetNightly.scala2("2.12", finalCache) - case Some(versionString) if ScalaVersionUtil.isScala3Nightly(versionString) => - ScalaVersionUtil.CheckNightly.scala3( - versionString, - finalCache, - latestSupportedStableVersions - ) - .map(_ => versionString) - case Some(versionString) if ScalaVersionUtil.isScala2Nightly(versionString) => - ScalaVersionUtil.CheckNightly.scala2( - versionString, - finalCache, - latestSupportedStableVersions - ) - .map(_ => versionString) - case Some(versionString) if versionString.exists(_.isLetter) => - val allVersions = ScalaVersionUtil.allMatchingVersions(Some(versionString), finalCache) - ScalaVersionUtil.validateNonStable( - versionString, - allVersions, - latestSupportedStableVersions - ) - case Some(versionString) => - val allStableVersions = - ScalaVersionUtil.allMatchingVersions(Some(versionString), finalCache) - .filter(ScalaVersionUtil.isStable) - ScalaVersionUtil.validateStable( - versionString, - allStableVersions, - latestSupportedStableVersions, - maxSupportedStableScalaVersions - ) - case None => - val allStableVersions = ScalaVersionUtil.allMatchingVersions(None, finalCache) - .filter(ScalaVersionUtil.isStable) + val svOpt: Option[String] = scalaOptions.scalaVersion match { + case Some(MaybeScalaVersion(None)) => + None + case Some(MaybeScalaVersion(Some(svInput))) => + val sv = value { + svInput match { + case "3.nightly" => + ScalaVersionUtil.GetNightly.scala3(finalCache) + case scala3NightlyNicknameRegex(threeSubBinaryNum) => + ScalaVersionUtil.GetNightly.scala3X( + threeSubBinaryNum, + finalCache, + latestSupportedStableVersions + ) + case "2.nightly" | "2.13.nightly" => + ScalaVersionUtil.GetNightly.scala2("2.13", finalCache) + case "2.12.nightly" => + ScalaVersionUtil.GetNightly.scala2("2.12", finalCache) + case versionString if ScalaVersionUtil.isScala3Nightly(versionString) => + ScalaVersionUtil.CheckNightly.scala3( + versionString, + finalCache, + latestSupportedStableVersions + ) + .map(_ => versionString) + case versionString if ScalaVersionUtil.isScala2Nightly(versionString) => + ScalaVersionUtil.CheckNightly.scala2( + versionString, + finalCache, + latestSupportedStableVersions + ) + .map(_ => versionString) + case versionString if versionString.exists(_.isLetter) => + val allVersions = + ScalaVersionUtil.allMatchingVersions(Some(versionString), finalCache) + ScalaVersionUtil.validateNonStable( + versionString, + allVersions, + latestSupportedStableVersions + ) + case versionString => + val allStableVersions = + ScalaVersionUtil.allMatchingVersions(Some(versionString), finalCache) + .filter(ScalaVersionUtil.isStable) + ScalaVersionUtil.validateStable( + versionString, + allStableVersions, + latestSupportedStableVersions, + maxSupportedStableScalaVersions + ) + } + } + Some(sv) + + case None => + val allStableVersions = ScalaVersionUtil.allMatchingVersions(None, finalCache) + .filter(ScalaVersionUtil.isStable) + val sv = value { ScalaVersionUtil.default( allStableVersions, latestSupportedStableVersions, maxSupportedStableScalaVersions ) - } + } + Some(sv) } - val scalaBinaryVersion = scalaOptions.scalaBinaryVersion.getOrElse { - ScalaVersion.binary(scalaVersion) - } + svOpt match { + case Some(scalaVersion) => + val scalaBinaryVersion = scalaOptions.scalaBinaryVersion.getOrElse { + ScalaVersion.binary(scalaVersion) + } - val maybePlatformSuffix = platform.value match { - case Platform.JVM => None - case Platform.JS => Some(scalaJsOptions.platformSuffix) - case Platform.Native => Some(scalaNativeOptions.platformSuffix) - } + val maybePlatformSuffix = platform.value match { + case Platform.JVM => None + case Platform.JS => Some(scalaJsOptions.platformSuffix) + case Platform.Native => Some(scalaNativeOptions.platformSuffix) + } - ScalaParameters(scalaVersion, scalaBinaryVersion, maybePlatformSuffix) + Some(ScalaParameters(scalaVersion, scalaBinaryVersion, maybePlatformSuffix)) + case None => + None + } } def artifacts(logger: Logger): Either[BuildException, Artifacts] = either { + val scalaArtifactsParamsOpt = value(scalaParams) match { + case Some(scalaParams0) => + val params = Artifacts.ScalaArtifactsParams( + params = scalaParams0, + compilerPlugins = value(compilerPlugins), + addJsTestBridge = addJsTestBridge, + addNativeTestInterface = addNativeTestInterface, + scalaJsCliVersion = + if (platform.value == Platform.JS) Some(scalaJsCliVersion) else None, + scalaNativeCliVersion = + if (platform.value == Platform.Native) Some(scalaNativeOptions.finalVersion) else None + ) + Some(params) + case None => + None + } + val addRunnerDependency0 = addRunnerDependency.orElse { + if (scalaArtifactsParamsOpt.isDefined) None + else Some(false) // no runner in pure Java mode + } val maybeArtifacts = Artifacts( - params = value(scalaParams), - compilerPlugins = value(compilerPlugins), + scalaArtifactsParamsOpt, javacPluginDependencies = value(javacPluginDependencies), extraJavacPlugins = javaOptions.javacPlugins.map(_.value), dependencies = value(dependencies), extraClassPath = allExtraJars, - scalaJsCliVersion = - if (platform.value == Platform.JS) Some(scalaJsCliVersion) else None, - scalaNativeCliVersion = - if (platform.value == Platform.Native) Some(scalaNativeOptions.finalVersion) else None, extraCompileOnlyJars = allExtraCompileOnlyJars, extraSourceJars = allExtraSourceJars, fetchSources = classPathOptions.fetchSources.getOrElse(false), addStubs = internalDependencies.addStubsDependency, - addJvmRunner = addRunnerDependency, + addJvmRunner = addRunnerDependency0, addJvmTestRunner = addJvmTestRunner, - addJsTestBridge = addJsTestBridge, - addNativeTestInterface = addNativeTestInterface, addJmhDependencies = jmhOptions.addJmhDependencies, extraRepositories = finalRepositories, cache = finalCache, @@ -418,7 +462,7 @@ final case class BuildOptions( this +: sortedExtraScalaVersions.map { sv => copy( scalaOptions = scalaOptions0.copy( - scalaVersion = Some(sv), + scalaVersion = Some(MaybeScalaVersion(sv)), extraScalaVersions = Set.empty ) ) diff --git a/modules/options/src/main/scala/scala/build/options/BuildRequirements.scala b/modules/options/src/main/scala/scala/build/options/BuildRequirements.scala index 99cb85e170..68276fe439 100644 --- a/modules/options/src/main/scala/scala/build/options/BuildRequirements.scala +++ b/modules/options/src/main/scala/scala/build/options/BuildRequirements.scala @@ -5,7 +5,7 @@ final case class BuildRequirements( platform: Seq[BuildRequirements.PlatformRequirement] = Nil, scope: Option[BuildRequirements.ScopeRequirement] = None ) { - def withScalaVersion(sv: String): Either[String, BuildRequirements] = { + def withScalaVersion(sv: MaybeScalaVersion): Either[String, BuildRequirements] = { val dontPass = scalaVersion.filter(!_.valid(sv)) if (dontPass.isEmpty) Right(copy(scalaVersion = Nil)) @@ -28,40 +28,43 @@ final case class BuildRequirements( object BuildRequirements { sealed trait VersionRequirement extends Product with Serializable { - def valid(version: String): Boolean + def valid(version: MaybeScalaVersion): Boolean def failedMessage: String } final case class VersionEquals(requiredVersion: String, loose: Boolean) extends VersionRequirement { - def looselyValid(version: String): Boolean = - version == requiredVersion || - version.startsWith(requiredVersion + ".") || - version.startsWith(requiredVersion + "-") - def strictlyValid(version: String): Boolean = { - val cmp = coursier.core.Version(requiredVersion).compare(coursier.core.Version(version)) - cmp == 0 - } - def valid(version: String): Boolean = + def looselyValid(version: MaybeScalaVersion): Boolean = + version.versionOpt.contains(requiredVersion) || + version.versionOpt.exists(_.startsWith(requiredVersion + ".")) || + version.versionOpt.exists(_.startsWith(requiredVersion + "-")) + def strictlyValid(version: MaybeScalaVersion): Boolean = + version.versionOpt.exists { version => + val cmp = coursier.core.Version(requiredVersion).compare(coursier.core.Version(version)) + cmp == 0 + } + def valid(version: MaybeScalaVersion): Boolean = (loose && looselyValid(version)) || strictlyValid(version) def failedMessage: String = s"Expected version $requiredVersion" } final case class VersionLowerThan(maxVersion: String, orEqual: Boolean) extends VersionRequirement { - def valid(version: String): Boolean = { - val cmp = coursier.core.Version(version).compare(coursier.core.Version(maxVersion)) - cmp < 0 || (orEqual && cmp == 0) - } + def valid(version: MaybeScalaVersion): Boolean = + version.versionOpt.exists { version => + val cmp = coursier.core.Version(version).compare(coursier.core.Version(maxVersion)) + cmp < 0 || (orEqual && cmp == 0) + } def failedMessage: String = if (orEqual) s"Expected version lower than or equal to $maxVersion" else s"Expected version lower than $maxVersion" } final case class VersionHigherThan(minVersion: String, orEqual: Boolean) extends VersionRequirement { - def valid(version: String): Boolean = { - val cmp = coursier.core.Version(minVersion).compare(coursier.core.Version(version)) - cmp < 0 || (orEqual && cmp == 0) - } + def valid(version: MaybeScalaVersion): Boolean = + version.versionOpt.exists { version => + val cmp = coursier.core.Version(minVersion).compare(coursier.core.Version(version)) + cmp < 0 || (orEqual && cmp == 0) + } def failedMessage: String = if (orEqual) s"Expected version higher than or equal to $minVersion" else s"Expected version higher than $minVersion" diff --git a/modules/options/src/main/scala/scala/build/options/HasBuildRequirements.scala b/modules/options/src/main/scala/scala/build/options/HasBuildRequirements.scala index a18e92b2ab..64a913748d 100644 --- a/modules/options/src/main/scala/scala/build/options/HasBuildRequirements.scala +++ b/modules/options/src/main/scala/scala/build/options/HasBuildRequirements.scala @@ -4,7 +4,7 @@ final case class HasBuildRequirements[+T]( requirements: BuildRequirements, value: T ) { - def withScalaVersion(sv: String): Either[String, HasBuildRequirements[T]] = + def withScalaVersion(sv: MaybeScalaVersion): Either[String, HasBuildRequirements[T]] = requirements.withScalaVersion(sv).map { updatedRequirements => copy(requirements = updatedRequirements) } diff --git a/modules/options/src/main/scala/scala/build/options/MaybeScalaVersion.scala b/modules/options/src/main/scala/scala/build/options/MaybeScalaVersion.scala new file mode 100644 index 0000000000..9d3d108d4d --- /dev/null +++ b/modules/options/src/main/scala/scala/build/options/MaybeScalaVersion.scala @@ -0,0 +1,23 @@ +package scala.build.options + +final case class MaybeScalaVersion(versionOpt: Option[String] = None) { + def asString = versionOpt.getOrElse(MaybeScalaVersion.noneStr) +} + +object MaybeScalaVersion { + + private def noneStr = "none" + + def none: MaybeScalaVersion = + MaybeScalaVersion(noneStr) + + def apply(s: String): MaybeScalaVersion = + if (s == noneStr) MaybeScalaVersion(None) + else MaybeScalaVersion(Some(s)) + + implicit lazy val hashedType: HashedType[MaybeScalaVersion] = { v => + HashedType.string.hashedValue(v.versionOpt.getOrElse("no-scala-version")) + } + implicit lazy val hasHashData: HasHashData[MaybeScalaVersion] = + HasHashData.derive +} diff --git a/modules/options/src/main/scala/scala/build/options/ScalaOptions.scala b/modules/options/src/main/scala/scala/build/options/ScalaOptions.scala index 2c3c80e5bf..ef59c4bca7 100644 --- a/modules/options/src/main/scala/scala/build/options/ScalaOptions.scala +++ b/modules/options/src/main/scala/scala/build/options/ScalaOptions.scala @@ -5,7 +5,7 @@ import dependency.AnyDependency import scala.build.Positioned final case class ScalaOptions( - scalaVersion: Option[String] = None, + scalaVersion: Option[MaybeScalaVersion] = None, scalaBinaryVersion: Option[String] = None, addScalaLibrary: Option[Boolean] = None, generateSemanticDbs: Option[Boolean] = None, @@ -23,7 +23,7 @@ final case class ScalaOptions( def normalize: ScalaOptions = { var opt = this - for (sv <- opt.scalaVersion if opt.extraScalaVersions.contains(sv)) + for (sv <- opt.scalaVersion.map(_.asString) if opt.extraScalaVersions.contains(sv)) opt = opt.copy( extraScalaVersions = opt.extraScalaVersions - sv ) From 08aa9f27ee8ce599f5194417e7687d9731a7acb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= <46607934+lwronski@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:19:36 +0200 Subject: [PATCH 68/72] Use Constants.defaultScalaVersion instead of properties version (#955) --- .../cli/src/main/scala/scala/cli/launcher/LauncherCli.scala | 5 +++-- modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala | 4 ++-- .../src/main/scala-3.1/scala/build/CoursierUtils.scala | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala b/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala index 06d583fea7..7f9fdf68f6 100644 --- a/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala +++ b/modules/cli/src/main/scala/scala/cli/launcher/LauncherCli.scala @@ -71,9 +71,10 @@ object LauncherCli { } def scalaCliScalaVersion(cliVersion: String): String = - if (cliVersion == "nightly") Properties.versionNumberString + if (cliVersion == "nightly") Constants.defaultScalaVersion else if (Version(cliVersion) <= Version("0.1.2")) Constants.defaultScala212Version - else Properties.versionNumberString + else if (Version(cliVersion) <= Version("0.1.4")) Constants.defaultScala213Version + else Constants.defaultScalaVersion def resolveNightlyScalaCliVersion( cache: FileCache[Task], diff --git a/modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala b/modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala index 66f202711d..0ce96817a7 100644 --- a/modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala +++ b/modules/cli/src/test/scala/cli/tests/LauncherCliTest.scala @@ -14,7 +14,7 @@ class LauncherCliTest extends munit.FunSuite { test("resolve nightly version") { val logger = TestLogger() val cache = CoursierOptions().coursierCache(logger.coursierLogger("")) - val scalaParameters = ScalaParameters(Properties.versionNumberString) + val scalaParameters = ScalaParameters(Constants.defaultScalaVersion) val nightlyCliVersion = LauncherCli.resolveNightlyScalaCliVersion(cache, scalaParameters) expect(nightlyCliVersion.endsWith("-SNAPSHOT")) @@ -24,7 +24,7 @@ class LauncherCliTest extends munit.FunSuite { "0.1.2" -> Constants.defaultScala212Version, "0.1.1+43-g15666b67-SNAPSHOT" -> Constants.defaultScala212Version, "0.1.3" -> Constants.defaultScala213Version, - "nightly" -> Properties.versionNumberString + "nightly" -> Constants.defaultScalaVersion ) for ((cliVersion, expectedScalaVersion) <- expectedScalaCliVersions) diff --git a/modules/options/src/main/scala-3.1/scala/build/CoursierUtils.scala b/modules/options/src/main/scala-3.1/scala/build/CoursierUtils.scala index 20593ab37b..72e35a6611 100644 --- a/modules/options/src/main/scala-3.1/scala/build/CoursierUtils.scala +++ b/modules/options/src/main/scala-3.1/scala/build/CoursierUtils.scala @@ -6,6 +6,7 @@ import coursier.core.{Module, Dependency => CDependency} import coursier.parse.{DependencyParser, ModuleParser} import dependency.{DependencyLike, NameAttributes} +import scala.build.internal.Constants def noArgs(args: Expr[Seq[Any]])(using Quotes): Unit = {} // TODO @@ -23,7 +24,7 @@ object CoursierUtils { def parseModule(cs: Expr[StringContext], args: Expr[Seq[Any]])(using Quotes): Expr[Module] = noArgs(args) val modString = extractString(cs) - ModuleParser.module(modString, scala.util.Properties.versionNumberString) match { + ModuleParser.module(modString, Constants.defaultScalaVersion) match { case Left(error) => quotes.reflect.report.error(error) ??? @@ -31,7 +32,7 @@ object CoursierUtils { '{ ModuleParser.module( ${ Expr(modString) }, - scala.util.Properties.versionNumberString + Constants.defaultScalaVersion ).getOrElse(???) } } From db00f43ff37e82105d307749d9fde24bfc48d6e4 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 4 May 2022 10:08:33 +0200 Subject: [PATCH 69/72] Update GraalVM to 22.1.0 (#953) --- .../org.virtuslab/scala-cli-core/native-image.properties | 1 - project/deps.sc | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/native-image.properties b/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/native-image.properties index 83cea5c9d8..5561faf4e3 100644 --- a/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/native-image.properties +++ b/modules/cli/src/main/resources/META-INF/native-image/org.virtuslab/scala-cli-core/native-image.properties @@ -6,7 +6,6 @@ Args = --no-fallback \ -H:IncludeResources=coursier/coursier.properties \ -H:IncludeResources=coursier/launcher/coursier.properties \ -H:IncludeResources=coursier/launcher/.*.bat \ - --allow-incomplete-classpath \ --report-unsupported-elements-at-runtime \ -H:+ReportExceptionStackTraces \ -Djdk.http.auth.tunneling.disabledSchemes= diff --git a/project/deps.sc b/project/deps.sc index 7560e5ac24..8c41340216 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -125,13 +125,13 @@ object Deps { def snailgun(force213: Boolean = false) = if (force213) ivy"me.vican.jorge:snailgun-core_2.13:0.4.0" else ivy"me.vican.jorge::snailgun-core:0.4.0" - def svm = ivy"org.graalvm.nativeimage:svm:$graalVmVersion" + def svm = ivy"org.graalvm.nativeimage:svm:22.0.0.2" def swoval = ivy"com.swoval:file-tree-views:2.1.9" def testInterface = ivy"org.scala-sbt:test-interface:1.0" def usingDirectives = ivy"org.virtuslab:using_directives:0.0.8" } -def graalVmVersion = "22.0.0" +def graalVmVersion = "22.1.0" def graalVmJavaVersion = 17 def graalVmJvmId = s"graalvm-java$graalVmJavaVersion:$graalVmVersion" From fb57035139e435f6bd199f42b758f27a7d4601d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= <46607934+lwronski@users.noreply.github.com> Date: Wed, 4 May 2022 11:48:25 +0200 Subject: [PATCH 70/72] Remove pretty stacktraces (#954) --- build.sc | 22 --------- .../cli/integration/RunTestDefinitions.scala | 18 ++------ .../main/scala/scala/build/Artifacts.scala | 6 +-- .../scala-2/scala/cli/runner/Stacktrace.scala | 8 ---- .../runner/ClasspathDirectoriesLoader.scala | 36 --------------- .../scala/cli/runner/Stacktrace.scala | 29 ------------ .../scala/cli/runner/Stacktraces.scala | 45 ------------------- .../scala/cli/runner/Stacktrace.scala | 8 ---- .../scala/cli/runner/StackTracePrinter.scala | 12 ++--- project/deps.sc | 1 - website/docs/guides/internals.md | 5 +-- 11 files changed, 9 insertions(+), 181 deletions(-) delete mode 100644 modules/runner/src/main/scala-2/scala/cli/runner/Stacktrace.scala delete mode 100644 modules/runner/src/main/scala-3-stable/scala/cli/runner/ClasspathDirectoriesLoader.scala delete mode 100644 modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktrace.scala delete mode 100644 modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktraces.scala delete mode 100644 modules/runner/src/main/scala-3-unstable/scala/cli/runner/Stacktrace.scala diff --git a/build.sc b/build.sc index 433911a113..cd14f14612 100644 --- a/build.sc +++ b/build.sc @@ -349,11 +349,6 @@ class Core(val crossScalaVersion: String) extends BuildLikeModule { val runnerMainClass = runner(Scala.defaultInternal) .mainClass() .getOrElse(sys.error("No main class defined for runner")) - val runnerNeedsSonatypeSnapshots = - if (Deps.prettyStacktraces.dep.version.endsWith("SNAPSHOT")) - """ !sv.startsWith("2.") """ - else - "false" val detailedVersionValue = if (`local-repo`.developingOnStubModules) s"""Some("${vcsState()}")""" else "None" @@ -387,8 +382,6 @@ class Core(val crossScalaVersion: String) extends BuildLikeModule { | def runnerModuleName = "${runner(Scala.defaultInternal).artifactName()}" | def runnerVersion = "${runner(Scala.defaultInternal).publishVersion()}" | def runnerMainClass = "$runnerMainClass" - | def runnerNeedsSonatypeSnapshots(sv: String): Boolean = - | $runnerNeedsSonatypeSnapshots | | def semanticDbPluginOrganization = "${Deps.scalametaTrees.dep.module.organization.value}" | def semanticDbPluginModuleName = "semanticdb-scalac" @@ -851,21 +844,6 @@ class Runner(val crossScalaVersion: String) extends ScalaCliCrossSbtModule } def mainClass = Some("scala.cli.runner.Runner") - def ivyDeps = - if (crossScalaVersion.startsWith("3.") && !crossScalaVersion.contains("-RC")) - Agg(Deps.prettyStacktraces) - else - Agg.empty[Dep] - def repositories = { - val base = super.repositories - val extra = - if (Deps.prettyStacktraces.dep.version.endsWith("SNAPSHOT")) - Seq(coursier.Repositories.sonatype("snapshots")) - else - Nil - - base ++ extra - } def sources = T.sources { val scala3DirNames = if (crossScalaVersion.startsWith("3.")) { diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index 26ea65a1d5..eaa61aa0fd 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -516,7 +516,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) val exceptionLines = output.map(stripAnsi).dropWhile(!_.startsWith("Exception in thread ")) val tab = "\t" - val sp = " " + val expectedLines = if (actualScalaVersion.startsWith("2.12.")) s"""Exception in thread "main" java.lang.Exception: Caught exception during processing @@ -528,7 +528,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) |${tab}at Throws$$.main(Throws.scala:5) |$tab... 1 more |""".stripMargin.linesIterator.toVector - else if (actualScalaVersion.startsWith("2.13.")) + else if (actualScalaVersion.startsWith("3.") || actualScalaVersion.startsWith("2.13.")) s"""Exception in thread "main" java.lang.Exception: Caught exception during processing |${tab}at Throws$$.main(Throws.scala:8) |${tab}at Throws.main(Throws.scala) @@ -538,16 +538,6 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) |${tab}at Throws$$.main(Throws.scala:5) |$tab... 1 more |""".stripMargin.linesIterator.toVector - else if (actualScalaVersion.startsWith("3.")) - s"""Exception in thread main: java.lang.Exception: Caught exception during processing - | at method main in Throws.scala:8$sp - | - |Caused by: Exception in thread main: java.lang.RuntimeException: nope - | at method error in scala.sys.package$$:27$sp - | at method something in Throws.scala:3$sp - | at method main in Throws.scala:5$sp - | - |""".stripMargin.linesIterator.toVector else sys.error(s"Unexpected Scala version: $actualScalaVersion") if (exceptionLines != expectedLines) { @@ -649,9 +639,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) inputs.fromRoot { root => // format: off val cmd = Seq[os.Shellable]( - TestUtil.cli, "run", extraOptions, ".", - "--java-prop=scala.cli.runner.Stacktrace.disable=true" - ) + TestUtil.cli, "run", extraOptions, ".") // format: on val res = os.proc(cmd).call(cwd = root, check = false, mergeErrIntoOut = true) val output = res.out.lines() diff --git a/modules/options/src/main/scala/scala/build/Artifacts.scala b/modules/options/src/main/scala/scala/build/Artifacts.scala index f83577a611..392ea146a8 100644 --- a/modules/options/src/main/scala/scala/build/Artifacts.scala +++ b/modules/options/src/main/scala/scala/build/Artifacts.scala @@ -18,7 +18,6 @@ import scala.build.errors.{ FetchingDependenciesError, RepositoryFormatError } -import scala.build.internal.Constants import scala.build.internal.Constants.* import scala.build.internal.CsLoggerUtil.* import scala.build.internal.Util.PositionedScalaDependencyOps @@ -108,11 +107,8 @@ object Artifacts { val hasSnapshots = (jvmRunnerDependencies ++ jvmTestRunnerDependencies) .exists(_.version.endsWith("SNAPSHOT")) || scalaArtifactsParamsOpt.flatMap(_.scalaNativeCliVersion).exists(_.endsWith("SNAPSHOT")) - val runnerNeedsSonatypeSnapshots = Constants.runnerNeedsSonatypeSnapshots( - scalaArtifactsParamsOpt.fold("")(_.params.scalaVersion) - ) val stubsNeedSonatypeSnapshots = addStubs && stubsVersion.endsWith("SNAPSHOT") - if (hasSnapshots || runnerNeedsSonatypeSnapshots || stubsNeedSonatypeSnapshots) + if (hasSnapshots || stubsNeedSonatypeSnapshots) Seq(coursier.Repositories.sonatype("snapshots").root) else Nil diff --git a/modules/runner/src/main/scala-2/scala/cli/runner/Stacktrace.scala b/modules/runner/src/main/scala-2/scala/cli/runner/Stacktrace.scala deleted file mode 100644 index 76afeb4df8..0000000000 --- a/modules/runner/src/main/scala-2/scala/cli/runner/Stacktrace.scala +++ /dev/null @@ -1,8 +0,0 @@ -package scala.cli.runner - -object Stacktrace { - - def print(t: Throwable, prefix: String, verbosity: Int): Boolean = - false - -} diff --git a/modules/runner/src/main/scala-3-stable/scala/cli/runner/ClasspathDirectoriesLoader.scala b/modules/runner/src/main/scala-3-stable/scala/cli/runner/ClasspathDirectoriesLoader.scala deleted file mode 100644 index 3056487e2f..0000000000 --- a/modules/runner/src/main/scala-3-stable/scala/cli/runner/ClasspathDirectoriesLoader.scala +++ /dev/null @@ -1,36 +0,0 @@ -package scala.cli.runner - -import java.net.URLClassLoader -import java.net.URL -import java.io.File - -import org.virtuslab.stacktraces.io.Unzipper -import org.virtuslab.stacktraces.model.ClasspathWrapper - -object ClasspathDirectoriesLoader: - - private def getUrls(cl: ClassLoader): Array[File] = cl match - case null => Array() - case u: URLClassLoader => u.getURLs.map(u => new File(u.toURI)) ++ getUrls(cl.getParent) - case cl if cl.getClass.getName == "jdk.internal.loader.ClassLoaders$AppClassLoader" => - // Required with JDK-11 - sys.props.getOrElse("java.class.path", "") - .split(File.pathSeparator) - .filter(_.nonEmpty) - .map(new File(_)) - case _ => getUrls(cl.getParent) - - def getClasspath(loader: ClassLoader = Thread.currentThread().getContextClassLoader): List[File] = - getUrls(loader).toList - - def getClasspathDirectories(classPathFiles: List[File] = getClasspath()): List[ClasspathWrapper] = - val (directories, jars) = classPathFiles.partition(_.isDirectory) - val allDirectories = projectClassesToClasspathWrappers(directories) ++ - jarsToClasspathWrappers(jars) - allDirectories - - private def projectClassesToClasspathWrappers(directories: List[File]): List[ClasspathWrapper] = - directories.map(ClasspathWrapper(_, None)) - - private def jarsToClasspathWrappers(jars: List[File]): List[ClasspathWrapper] = - jars.map(j => ClasspathWrapper(Unzipper.unzipFile(j), Some(j.getName))) diff --git a/modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktrace.scala b/modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktrace.scala deleted file mode 100644 index 0f126ea36b..0000000000 --- a/modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktrace.scala +++ /dev/null @@ -1,29 +0,0 @@ -package scala.cli.runner - -import org.virtuslab.stacktraces.printer.PrettyExceptionPrinter - -object Stacktrace { - - private lazy val disable = java.lang.Boolean.getBoolean("scala.cli.runner.Stacktrace.disable") - - def print(t: Throwable, prefix: String, verbosity: Int): Boolean = !disable && { - val e = t match { - case e: Exception => e - case _ => new Exception(t) // meh - } - try - val prettyStackTrace = Stacktraces.convertToPrettyStackTrace(e) - Console.out.print(prefix) - PrettyExceptionPrinter.printStacktrace(prettyStackTrace) - true - catch - case e: Throwable => // meh meh - if (verbosity >= 1) { - Console.out.print("Failed to process exception, failure:") - e.printStackTrace() - Console.out.println("----\n") - } - false - } - -} diff --git a/modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktraces.scala b/modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktraces.scala deleted file mode 100644 index 95c45a9df0..0000000000 --- a/modules/runner/src/main/scala-3-stable/scala/cli/runner/Stacktraces.scala +++ /dev/null @@ -1,45 +0,0 @@ -package scala.cli.runner - -import org.virtuslab.stacktraces.core.StacktracesInspector -import org.virtuslab.stacktraces.model.TastyWrapper -import org.virtuslab.stacktraces.model.PrettyException -import org.virtuslab.stacktraces.model.PrettyStackTraceElement -import org.virtuslab.stacktraces.model.ElementType -import org.virtuslab.stacktraces.io.TastyFilesLocator - -import dotty.tools.dotc.util.NameTransformer -import dotty.tools.dotc.core.Names - -import scala.quoted.* -import scala.tasty.inspector.* -import scala.collection.JavaConverters.* - -import java.io.File -import java.nio.file.Paths - -object Stacktraces: - lazy val classpathDirectories = ClasspathDirectoriesLoader.getClasspathDirectories() - - def convertToPrettyStackTrace(e: Exception): PrettyException = - val st = filterInternalStackFrames(e.getStackTrace).flatMap { ste => - val tastyFilesLocator = TastyFilesLocator(classpathDirectories) - tastyFilesLocator.findTastyFile(ste.getClassName) match - case Some(TastyWrapper(tastyFile, opJarName)) => - StacktracesInspector.inspectStackTrace(ste, tastyFile).map(_.copy(jarName = opJarName)) - case None => - val elem = PrettyStackTraceElement( - ste, - ElementType.Method, - ste.getMethodName, - ste.getClassName, - ste.getLineNumber - ) - Some(elem) - }.toList - PrettyException(e, st) - - private def filterInternalStackFrames(st: Array[StackTraceElement]): List[StackTraceElement] = - st.sliding(2).toList.flatMap { - case Array(fs, sc) => - if sc.getMethodName.contains("$adapted") then Nil else List(fs) - } diff --git a/modules/runner/src/main/scala-3-unstable/scala/cli/runner/Stacktrace.scala b/modules/runner/src/main/scala-3-unstable/scala/cli/runner/Stacktrace.scala deleted file mode 100644 index 76afeb4df8..0000000000 --- a/modules/runner/src/main/scala-3-unstable/scala/cli/runner/Stacktrace.scala +++ /dev/null @@ -1,8 +0,0 @@ -package scala.cli.runner - -object Stacktrace { - - def print(t: Throwable, prefix: String, verbosity: Int): Boolean = - false - -} diff --git a/modules/runner/src/main/scala/scala/cli/runner/StackTracePrinter.scala b/modules/runner/src/main/scala/scala/cli/runner/StackTracePrinter.scala index 1049f1b65c..6bf7d69aac 100644 --- a/modules/runner/src/main/scala/scala/cli/runner/StackTracePrinter.scala +++ b/modules/runner/src/main/scala/scala/cli/runner/StackTracePrinter.scala @@ -49,10 +49,8 @@ final case class StackTracePrinter( ): Unit = if (ex != null) { truncateStackTrace(ex) - if (!Stacktrace.print(ex, "Caused by: ", verbosity)) { - System.err.println(s"Caused by: $ex") - printStackTrace(ex.getStackTrace, causedStackTrace) - } + System.err.println(s"Caused by: $ex") + printStackTrace(ex.getStackTrace, causedStackTrace) printCause(ex.getCause, ex.getStackTrace, verbosity) } private def printStackTrace(trace: Array[StackTraceElement]): Unit = @@ -92,10 +90,8 @@ final case class StackTracePrinter( val q = "\"" val threadName = Thread.currentThread().getName truncateStackTrace(ex) - if (!Stacktrace.print(ex, "", verbosity)) { - System.err.println(s"Exception in thread $q$threadName$q $ex") - printStackTrace(ex.getStackTrace) - } + System.err.println(s"Exception in thread $q$threadName$q $ex") + printStackTrace(ex.getStackTrace) printCause(ex.getCause, ex.getStackTrace, verbosity) } } diff --git a/project/deps.sc b/project/deps.sc index 8c41340216..c7e05bfff9 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -92,7 +92,6 @@ object Deps { def organizeImports = ivy"com.github.liancheng::organize-imports:0.5.0" def osLib = ivy"com.lihaoyi::os-lib:0.8.1" def pprint = ivy"com.lihaoyi::pprint:0.7.3" - def prettyStacktraces = ivy"org.virtuslab::pretty-stacktraces:0.0.1-M1" def scala3Compiler(sv: String) = ivy"org.scala-lang::scala3-compiler:$sv" def scalaAsync = ivy"org.scala-lang.modules::scala-async:1.0.1".exclude("*" -> "*") def scalac(sv: String) = ivy"org.scala-lang:scala-compiler:$sv" diff --git a/website/docs/guides/internals.md b/website/docs/guides/internals.md index 2a2216ef3d..64ff641379 100644 --- a/website/docs/guides/internals.md +++ b/website/docs/guides/internals.md @@ -98,10 +98,7 @@ That includes: When running your code, if the code crashes, Scala CLI processes the stack traces of the exception to make them more readable. This is achieved by adding a module (called `runner`) to the class path, and this module is actually used as the entry point of your application. The [`Runner` class](https://github.com/VirtusLab/scala-cli/blob/60eae701abc74bdd634efa5157740578bd6c4162/modules/runner/src/main/scala/scala/cli/runner/Runner.scala) -of the `runner` module starts your main class, catches any exceptions it might throw, and pretty-prints it. -(With Scala 3, pretty-printing of stack traces is handled by the -[pretty-stacktraces](https://github.com/Virtuslab/pretty-stacktraces) library.) - +of the `runner` module starts your main class, catches any exceptions it might throw, and prints it. ## Logging To get a glimpse at what Scala CLI is doing, increase its verbosity with `-v`. From d275b9ef4ab69a71c4738bbd2f2ad46c8a56358f Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Wed, 4 May 2022 11:48:47 +0200 Subject: [PATCH 71/72] Update trees_2.13 to 4.5.5 (#959) --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index c7e05bfff9..a93392c661 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -54,7 +54,7 @@ object Deps { // jni-utils version may need to be sync-ed when bumping the coursier version def coursier = "2.1.0-M5-18-gfebf9838c" def jsoniterScala = "2.13.18" - def scalaMeta = "4.5.4" + def scalaMeta = "4.5.5" def scalaNative = "0.4.4" def scalaPackager = "0.1.26" def signingCli = "0.1.3" From a86dd28c251b6ffc7ecc4ab97a40f674010c89a9 Mon Sep 17 00:00:00 2001 From: Piotr Chabelski Date: Wed, 4 May 2022 11:55:41 +0200 Subject: [PATCH 72/72] Update to Scala 0.1.5 (#965) --- website/docs/guides/scala-js.md | 1 + website/docs/reference/scala-versions.md | 1 + 2 files changed, 2 insertions(+) diff --git a/website/docs/guides/scala-js.md b/website/docs/guides/scala-js.md index 8cdcac8db4..c8ec83b3f9 100644 --- a/website/docs/guides/scala-js.md +++ b/website/docs/guides/scala-js.md @@ -174,3 +174,4 @@ The table below lists the last supported version of Scala.js in Scala CLI. If yo | 0.1.2 | 1.8.0 | | 0.1.3 | 1.9.0 | | 0.1.4 | 1.10.0 | +| 0.1.5 | 1.10.0 | diff --git a/website/docs/reference/scala-versions.md b/website/docs/reference/scala-versions.md index 2359f7e58d..3029325db2 100644 --- a/website/docs/reference/scala-versions.md +++ b/website/docs/reference/scala-versions.md @@ -13,4 +13,5 @@ Current Scala CLI versions support Scala 3, 2.13 and 2.12. The table below lists | 0.1.2 | 3.1.1 | 2.13.8 | 2.12.15 | | 0.1.3 | 3.1.1 | 2.13.8 | 2.12.15 | | 0.1.4 | 3.1.2 | 2.13.8 | 2.12.15 | +| 0.1.5 | 3.1.2 | 2.13.8 | 2.12.15 |