diff --git a/.github/scripts/check-cross-version-deps.sc b/.github/scripts/check-cross-version-deps.sc index 64f16dcea5..64c23335ba 100755 --- a/.github/scripts/check-cross-version-deps.sc +++ b/.github/scripts/check-cross-version-deps.sc @@ -11,9 +11,9 @@ val modules = for { module <- modules } { println(s"Checking for $module...") - val depRegex = "\\[\\d+]\\s+[│└├─\\S\\s]+\\s([\\w.-]+):([\\w.-]+):([\\w\\s\\S.-]+)".r + val depRegex = "[│└├─\\S\\s]+\\s([\\w.-]+):([\\w.-]+):([\\w\\s\\S.-]+)".r val scalaDepSuffixRegex = "^(.+?)(_[23](?:\\.\\d{2})?)?$".r - val deps = os.proc(os.pwd / "mill", "-i", s"$module.ivyDepsTree") + val deps = os.proc(os.pwd / "mill", "-i", s"$module.showMvnDepsTree") .call(cwd = os.pwd) .out .lines() diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 740613b606..248a33f701 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1514,7 +1514,7 @@ jobs: - name: Check native-image config format run: ./mill -i __.checkNativeImageConfFormat - name: Check Ammonite availability - run: ./mill -i 'dummy.amm[_].resolvedRunIvyDeps' + run: ./mill -i 'dummy.amm[_].resolvedRunMvnDeps' - name: Check for cross Scala version conflicts run: .github/scripts/check-cross-version-deps.sc - name: Scalafix check diff --git a/.mill-version b/.mill-version index d0834a7f54..7dea76edb3 100644 --- a/.mill-version +++ b/.mill-version @@ -1 +1 @@ -0.12.14 +1.0.1 diff --git a/build.mill.scala b/build.mill.scala index 5fab48f9b9..e07217dbd6 100644 --- a/build.mill.scala +++ b/build.mill.scala @@ -1,9 +1,12 @@ +//| mvnDeps: +//| - io.github.alexarchambault.mill::mill-native-image::0.2.0 +//| - io.github.alexarchambault.mill::mill-native-image-upload:0.2.0 +//| - com.goyeau::mill-scalafix::0.6.0 +//| - com.lumidion::sonatype-central-client-requests:0.6.0 +//| - io.get-coursier:coursier-launcher_2.13:2.1.24 +//| - org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r package build -import $packages._ -import $ivy.`com.lihaoyi::mill-contrib-bloop:$MILL_VERSION` -import $ivy.`io.get-coursier::coursier-launcher:2.1.24` -import $ivy.`io.github.alexarchambault.mill::mill-native-image-upload:0.1.31-1` import build.ci.publishVersion import build.project.deps import deps.{Cli, Deps, Docker, InternalDeps, Java, Scala, TestDeps} @@ -15,6 +18,7 @@ import settings.{ FormatNativeImageConf, HasTests, LocalRepo, + LocatedInModules, PublishLocalNoFluff, ScalaCliCrossSbtModule, ScalaCliScalafixLegacyModule, @@ -34,58 +38,51 @@ import java.io.File import java.net.URL import java.nio.charset.Charset import java.util.Locale -import de.tobiasroeser.mill.vcs.version.VcsVersion import io.github.alexarchambault.millnativeimage.upload.Upload -import mill._ -import mill.api.Loose -import scalalib.{publish => _, _} -import mill.contrib.bloop.Bloop -import mill.testrunner.TestResult -import os.{CommandResult, Path} +import mill.* +import mill.api.{BuildCtx, ModuleCtx, Task} +import mill.scalalib.* +import scalalib.{publish as _, *} +import mill.javalib.testrunner.TestResult +import mill.util.{Tasks, VcsVersion} import _root_.scala.util.{Properties, Using} -// Tell mill modules are under modules/ -implicit def millModuleBasePath: define.Ctx.BasePath = - define.Ctx.BasePath(super.millModuleBasePath.value / "modules") - object cli extends Cross[Cli](Scala.scala3MainVersions) with CrossScalaDefaultToInternal -trait CrossScalaDefault { _: mill.define.Cross[_] => +trait CrossScalaDefault { self: Cross[?] => def crossScalaDefaultVersion: String override def defaultCrossSegments: Seq[String] = Seq(crossScalaDefaultVersion) } -trait CrossScalaDefaultToInternal extends CrossScalaDefault { _: mill.define.Cross[_] => +trait CrossScalaDefaultToInternal extends CrossScalaDefault { self: Cross[?] => def crossScalaDefaultVersion: String = Scala.defaultInternal } -trait CrossScalaDefaultToRunner extends CrossScalaDefault { _: mill.define.Cross[_] => +trait CrossScalaDefaultToRunner extends CrossScalaDefault { self: Cross[?] => def crossScalaDefaultVersion: String = Scala.runnerScala3 } // Publish a bootstrapped, executable jar for a restricted environments object cliBootstrapped extends ScalaCliPublishModule { - override def unmanagedClasspath: Target[Agg[PathRef]] = + override def unmanagedClasspath: T[Seq[PathRef]] = Task(cli(Scala.defaultInternal).nativeImageClassPath()) - override def jar: Target[PathRef] = assembly() + override def jar: T[PathRef] = assembly() import mill.scalalib.Assembly override def prependShellScript: T[String] = Task("") - override def mainClass: Target[Option[String]] = Some("scala.cli.ScalaCli") + override def mainClass: T[Option[String]] = Some("scala.cli.ScalaCli") override def assemblyRules: Seq[Assembly.Rule] = Seq( Assembly.Rule.ExcludePattern(".*\\.tasty"), Assembly.Rule.ExcludePattern(".*\\.semanticdb") ) ++ super.assemblyRules - override def resources: Target[Seq[PathRef]] = Task.Sources { - super.resources() ++ Seq(propertiesFilesResources()) - } + override def resources: T[Seq[PathRef]] = super.resources() ++ Seq(propertiesFilesResources()) - def propertiesFilesResources: Target[PathRef] = Task(persistent = true) { + def propertiesFilesResources: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "resources" val dest = dir / "java-properties" / "scala-cli-properties" @@ -120,30 +117,28 @@ object `scala3-graal` extends Cross[Scala3Graal](Scala.scala3MainVersions) object `scala3-graal-processor` extends Cross[Scala3GraalProcessor](Scala.scala3MainVersions) with CrossScalaDefaultToInternal -object `scala-cli-bsp` extends JavaModule with ScalaCliPublishModule { - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Seq( +object `scala-cli-bsp` extends JavaModule with ScalaCliPublishModule with LocatedInModules { + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.bsp4j ) - def javacOptions: Target[Seq[String]] = Task { + def javacOptions: T[Seq[String]] = Task { super.javacOptions() ++ Seq("-target", "8", "-source", "8") } } object integration extends CliIntegration { object test extends IntegrationScalaTests { - def ivyDeps: Target[Loose.Agg[Dep]] = super.ivyDeps() ++ Seq( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.jgit, Deps.jsoup ) } object docker extends CliIntegrationDocker { object test extends ScalaCliTests { - def sources: Target[Seq[PathRef]] = Task.Sources { - super.sources() ++ integration.sources() - } - def tmpDirBase: Target[PathRef] = Task(persistent = true) { + def sources: T[Seq[PathRef]] = super.sources() ++ integration.sources() + def tmpDirBase: T[PathRef] = Task(persistent = true) { PathRef(Task.dest / "working-dir") } - def forkEnv: Target[Map[String, String]] = super.forkEnv() ++ Seq( + def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq( "SCALA_CLI_TMP" -> tmpDirBase().path.toString, "SCALA_CLI_IMAGE" -> "scala-cli", "SCALA_CLI_PRINT_STACK_TRACES" -> "1" @@ -153,13 +148,11 @@ object integration extends CliIntegration { object `docker-slim` extends CliIntegrationDocker { object test extends ScalaCliTests { - def sources: Target[Seq[PathRef]] = Task.Sources { - integration.docker.test.sources() - } - def tmpDirBase: Target[PathRef] = Task(persistent = true) { + def sources: T[Seq[PathRef]] = integration.docker.test.sources() + def tmpDirBase: T[PathRef] = Task(persistent = true) { PathRef(Task.dest / "working-dir") } - def forkEnv: Target[Map[String, String]] = super.forkEnv() ++ Seq( + def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq( "SCALA_CLI_TMP" -> tmpDirBase().path.toString, "SCALA_CLI_IMAGE" -> "scala-cli-slim", "SCALA_CLI_PRINT_STACK_TRACES" -> "1" @@ -171,24 +164,25 @@ object integration extends CliIntegration { object `docs-tests` extends Cross[DocsTests](Scala.scala3MainVersions) with CrossScalaDefaultToInternal -trait DocsTests extends CrossSbtModule with ScalaCliScalafixModule with HasTests { main => - def ivyDeps: Target[Agg[Dep]] = Agg( +trait DocsTests extends CrossSbtModule with ScalaCliScalafixModule with HasTests + with LocatedInModules { main => + def mvnDeps: T[Seq[Dep]] = Seq( Deps.fansi, Deps.osLib, Deps.pprint ) - def tmpDirBase: Target[PathRef] = Task(persistent = true) { + def tmpDirBase: T[PathRef] = Task(persistent = true) { PathRef(Task.dest / "working-dir") } - def extraEnv: Target[Seq[(String, String)]] = Task { + def extraEnv: T[Seq[(String, String)]] = Task { Seq( "SCLICHECK_SCALA_CLI" -> cli(crossScalaVersion).standaloneLauncher().path.toString, "SCALA_CLI_CONFIG" -> (tmpDirBase().path / "config" / "config.json").toString ) } - def forkEnv: Target[Map[String, String]] = super.forkEnv() ++ extraEnv() + def forkEnv: T[Map[String, String]] = super.forkEnv() ++ extraEnv() - def constantsFile: Target[PathRef] = Task(persistent = true) { + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants.scala" val code = @@ -203,99 +197,104 @@ trait DocsTests extends CrossSbtModule with ScalaCliScalafixModule with HasTests | def alpineVersion = "$alpineVersion" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } - def generatedSources: Target[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) object test extends ScalaCliTests with ScalaCliScalafixModule { - def forkEnv: Target[Map[String, String]] = super.forkEnv() ++ extraEnv() ++ Seq( - "SCALA_CLI_EXAMPLES" -> (Task.workspace / "examples").toString, - "SCALA_CLI_GIF_SCENARIOS" -> (Task.workspace / "gifs" / "scenarios").toString, - "SCALA_CLI_WEBSITE_IMG" -> (Task.workspace / "website" / "static" / "img").toString, - "SCALA_CLI_GIF_RENDERER_DOCKER_DIR" -> (Task.workspace / "gifs").toString, - "SCALA_CLI_SVG_RENDERER_DOCKER_DIR" -> (Task.workspace / "gifs" / "svg_render").toString + def forkEnv: T[Map[String, String]] = super.forkEnv() ++ extraEnv() ++ Seq( + "SCALA_CLI_EXAMPLES" -> (BuildCtx.workspaceRoot / "examples").toString, + "SCALA_CLI_GIF_SCENARIOS" -> (BuildCtx.workspaceRoot / "gifs" / "scenarios").toString, + "SCALA_CLI_WEBSITE_IMG" -> (BuildCtx.workspaceRoot / "website" / "static" / "img").toString, + "SCALA_CLI_GIF_RENDERER_DOCKER_DIR" -> (BuildCtx.workspaceRoot / "gifs").toString, + "SCALA_CLI_SVG_RENDERER_DOCKER_DIR" -> (BuildCtx.workspaceRoot / "gifs" / "svg_render").toString ) - def resources: Target[Seq[PathRef]] = Task.Sources { - // Adding markdown directories here, so that they're watched for changes in watch mode - Seq( - PathRef(Task.workspace / "website" / "docs" / "commands"), - PathRef(Task.workspace / "website" / "docs" / "cookbooks") - ) ++ super.resources() + private def customResources: T[Seq[PathRef]] = { + val customPaths: Seq[os.Path] = Seq( + BuildCtx.workspaceRoot / "website" / "docs" / "commands", + BuildCtx.workspaceRoot / "website" / "docs" / "cookbooks" + ) + Task.Sources(customPaths*) } + def resources: T[Seq[PathRef]] = + // Adding markdown directories here, so that they're watched for changes in watch mode + super.resources() ++ customResources() } } -object packager extends ScalaModule with Bloop.Module { - def skipBloop = true - def scalaVersion: Target[String] = Scala.scala213 - def ivyDeps: Target[Agg[Dep]] = Agg( +object packager extends ScalaModule { + def skipBloop = true + def scalaVersion: T[String] = Scala.scala213 + def mvnDeps: T[Seq[Dep]] = Seq( Deps.scalaPackagerCli ) - def mainClass: Target[Option[String]] = Some("packager.cli.PackagerCli") + def mainClass: T[Option[String]] = Some("packager.cli.PackagerCli") } object `generate-reference-doc` extends Cross[GenerateReferenceDoc](Scala.scala3MainVersions) with CrossScalaDefaultToInternal -trait GenerateReferenceDoc extends CrossSbtModule with ScalaCliScalafixModule { +trait GenerateReferenceDoc extends CrossSbtModule with ScalaCliScalafixModule + with LocatedInModules { def moduleDeps: Seq[JavaModule] = Seq( cli(crossScalaVersion) ) def repositoriesTask: Task[Seq[Repository]] = Task.Anon(super.repositoriesTask() ++ customRepositories) - def ivyDeps: Target[Agg[Dep]] = Agg( + def mvnDeps: T[Seq[Dep]] = Seq( Deps.argonautShapeless, Deps.caseApp, Deps.munit ) - def mainClass: Target[Option[String]] = Some("scala.cli.doc.GenerateReferenceDoc") + def mainClass: T[Option[String]] = Some("scala.cli.doc.GenerateReferenceDoc") - def forkEnv: Target[Map[String, String]] = super.forkEnv() ++ Seq( + def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq( "SCALA_CLI_POWER" -> "true" ) } -object dummy extends Module { +object dummy extends LocatedInModules { // dummy projects to get scala steward updates for Ammonite and scalafmt, whose // versions are used in the fmt and repl commands, and ensure Ammonite is available // for all Scala versions we support. object amm extends Cross[Amm](Scala.listMaxAmmoniteScalaVersion) - trait Amm extends Cross.Module[String] with CrossScalaModule with Bloop.Module { + trait Amm extends Cross.Module[String] with CrossScalaModule { def crossScalaVersion: String = crossValue def skipBloop = true - def ivyDeps: Target[Agg[Dep]] = { + def mvnDeps: T[Seq[Dep]] = { val ammoniteDep = - if (crossValue == Scala.scala3Lts) Deps.ammoniteForScala3Lts + if crossValue == Scala.scala3Lts + then Deps.ammoniteForScala3Lts else Deps.ammonite - Agg(ammoniteDep) + Seq(ammoniteDep) } } - object scalafmt extends ScalaModule with Bloop.Module { - def skipBloop = true - def scalaVersion: Target[String] = Scala.defaultInternal - def ivyDeps: Target[Agg[Dep]] = Agg( + object scalafmt extends ScalaModule { + def skipBloop = true + def scalaVersion: T[String] = Scala.defaultInternal + def mvnDeps: T[Seq[Dep]] = Seq( Deps.scalafmtCli ) } - object pythonInterface extends JavaModule with Bloop.Module { - def skipBloop = true - def ivyDeps: Target[Agg[Dep]] = Agg( + object pythonInterface extends JavaModule { + def skipBloop = true + def mvnDeps: T[Seq[Dep]] = Seq( Deps.pythonInterface ) } - object scalaPy extends ScalaModule with Bloop.Module { - def skipBloop = true - def scalaVersion: Target[String] = Scala.defaultInternal - def ivyDeps: Target[Agg[Dep]] = Agg( + object scalaPy extends ScalaModule { + def skipBloop = true + def scalaVersion: T[String] = Scala.defaultInternal + def mvnDeps: T[Seq[Dep]] = Seq( Deps.scalaPy ) } - object scalafix extends ScalaModule with Bloop.Module { - def skipBloop = true - def scalaVersion: Target[String] = Scala.defaultInternal - def ivyDeps: Target[Agg[Dep]] = Agg( + object scalafix extends ScalaModule { + def skipBloop = true + def scalaVersion: T[String] = Scala.defaultInternal + def mvnDeps: T[Seq[Dep]] = Seq( Deps.scalafixInterfaces ) } @@ -304,20 +303,21 @@ object dummy extends Module { trait BuildMacros extends ScalaCliCrossSbtModule with ScalaCliPublishModule with ScalaCliScalafixModule - with HasTests { - def crossScalaVersion: String = crossValue - def compileIvyDeps: Target[Agg[Dep]] = Task { - if (crossScalaVersion.startsWith("3")) super.compileIvyDeps() - else super.compileIvyDeps() ++ Agg(Deps.scalaReflect(crossScalaVersion)) + with HasTests + with LocatedInModules { + def crossScalaVersion: String = crossValue + def compileMvnDeps: T[Seq[Dep]] = Task { + if crossScalaVersion.startsWith("3") then super.compileMvnDeps() + else super.compileMvnDeps() ++ Seq(Deps.scalaReflect(crossScalaVersion)) } object test extends ScalaCliTests { - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ asyncScalacOptions(scalaVersion()) } def testNegativeCompilation(): Command[Unit] = Task.Command(exclusive = true) { - val base = Task.workspace / "modules" / "build-macros" / "src" + val base = BuildCtx.workspaceRoot / "modules" / "build-macros" / "src" val negativeTests = Seq( "MismatchedLeft.scala" -> Seq( "Found: +EE1".r, @@ -328,24 +328,23 @@ trait BuildMacros extends ScalaCliCrossSbtModule val cpsSource = base / "main" / "scala" / "scala" / "build" / "EitherCps.scala" val cpsSourceExists = os.exists(cpsSource) - if (!cpsSourceExists) System.err.println(s"Expected source file $cpsSource does not exist") + if !cpsSourceExists then System.err.println(s"Expected source file $cpsSource does not exist") else System.err.println(s"Found source file $cpsSource") assert(cpsSourceExists) - val sv = scalaVersion() - def compile(extraSources: os.Path*): CommandResult = + val sv = scalaVersion() + def compile(extraSources: os.Path*): os.CommandResult = os.proc("scala-cli", "compile", "-S", sv, cpsSource, extraSources).call( check = false, mergeErrIntoOut = true, - cwd = Task.workspace + cwd = BuildCtx.workspaceRoot ) val compileResult = compile() - if (compileResult.exitCode != 0) { + if compileResult.exitCode != 0 then { System.err.println(s"Compilation failed: $cpsSource") System.err.println(compileResult.out.text()) } - else - System.err.println(s"Compiled $cpsSource successfully") + else System.err.println(s"Compiled $cpsSource successfully") assert(0 == compileResult.exitCode) val notPassed = negativeTests.filter { case (testName, expectedErrors) => @@ -354,12 +353,12 @@ trait BuildMacros extends ScalaCliCrossSbtModule println(s"Compiling $testName:") println(res.out.text()) val name = testFile.last - if (res.exitCode != 0) { + if res.exitCode != 0 then { println(s"Test case $name failed to compile as expected") val lines = res.out.lines() println(lines) expectedErrors.forall { expected => - if (lines.exists(expected.findFirstIn(_).nonEmpty)) false + if lines.exists(expected.findFirstIn(_).nonEmpty) then false else { println(s"ERROR: regex `$expected` not found in compilation output for $testName") true @@ -378,7 +377,7 @@ trait BuildMacros extends ScalaCliCrossSbtModule } def asyncScalacOptions(scalaVersion: String) = - if (scalaVersion.startsWith("3")) Nil else Seq("-Xasync") + if scalaVersion.startsWith("3") then Nil else Seq("-Xasync") trait ProtoBuildModule extends ScalaCliPublishModule with HasTests with ScalaCliScalafixModule @@ -386,7 +385,8 @@ trait ProtoBuildModule extends ScalaCliPublishModule with HasTests trait Core extends ScalaCliCrossSbtModule with ScalaCliPublishModule with HasTests - with ScalaCliScalafixModule { + with ScalaCliScalafixModule + with LocatedInModules { def crossScalaVersion: String = crossValue def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq( @@ -395,14 +395,14 @@ trait Core extends ScalaCliCrossSbtModule def compileModuleDeps: Seq[JavaModule] = Seq( `build-macros`(crossScalaVersion) ) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion) } def repositoriesTask: Task[Seq[Repository]] = Task.Anon(super.repositoriesTask() ++ deps.customRepositories) - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.bloopRifle.exclude(("org.scala-lang.modules", "scala-collection-compat_2.13")), Deps.collectionCompat, Deps.coursierJvm @@ -423,18 +423,17 @@ trait Core extends ScalaCliCrossSbtModule Deps.scalaJsLogging, Deps.swoval ) - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Seq( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacros ) - private def vcsState: Target[String] = Task(persistent = true) { + private def vcsState: T[String] = Task(persistent = true) { val isCI = System.getenv("CI") != null val state = VcsVersion.vcsState().format() - if (isCI) state - else state + "-maybe-stale" + if isCI then state else state + "-maybe-stale" } - def constantsFile: Target[PathRef] = Task(persistent = true) { + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants.scala" val testRunnerMainClass = `test-runner`(Scala.runnerScala3) @@ -444,7 +443,7 @@ trait Core extends ScalaCliCrossSbtModule .mainClass() .getOrElse(sys.error("No main class defined for runner")) val detailedVersionValue = - if (`local-repo`.developingOnStubModules) s"""Some("${vcsState()}")""" + if `local-repo`.developingOnStubModules then s"""Some("${vcsState()}")""" else "None" val testRunnerOrganization = `test-runner`(Scala.runnerScala3) .pomSettings() @@ -569,17 +568,18 @@ trait Core extends ScalaCliCrossSbtModule | def alpineVersion = "$alpineVersion" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } - def generatedSources: Target[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) } trait Directives extends ScalaCliCrossSbtModule with ScalaCliPublishModule with HasTests - with ScalaCliScalafixModule { + with ScalaCliScalafixModule + with LocatedInModules { def crossScalaVersion: String = crossValue def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq( options(crossScalaVersion), @@ -587,15 +587,15 @@ trait Directives extends ScalaCliCrossSbtModule `build-macros`(crossScalaVersion), `specification-level`(crossScalaVersion) ) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion) } - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Agg( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacros, Deps.svm ) - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( // Deps.asm, Deps.bloopConfig, Deps.jsoniterCore, @@ -607,16 +607,16 @@ trait Directives extends ScalaCliCrossSbtModule Task.Anon(super.repositoriesTask() ++ deps.customRepositories) object test extends ScalaCliTests { - def ivyDeps: Target[Loose.Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.pprint ) - def runClasspath: Target[Seq[PathRef]] = Task { + def runClasspath: T[Seq[PathRef]] = Task { super.runClasspath() ++ Seq(`local-repo`.localRepoJar()) } - def generatedSources: Target[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) - def constantsFile: Target[PathRef] = Task(persistent = true) { + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants2.scala" val code = @@ -627,7 +627,7 @@ trait Directives extends ScalaCliCrossSbtModule | def cs = "${settings.cs().replace("\\", "\\\\")}" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } @@ -641,21 +641,22 @@ trait Directives extends ScalaCliCrossSbtModule trait Config extends ScalaCliCrossSbtModule with ScalaCliPublishModule - with ScalaCliScalafixModule { + with ScalaCliScalafixModule + with LocatedInModules { def crossScalaVersion: String = crossValue def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(`specification-level`(crossScalaVersion)) - def ivyDeps: Target[Agg[Dep]] = { + def mvnDeps: T[Seq[Dep]] = { val maybeCollectionCompat = - if (crossScalaVersion.startsWith("2.12.")) Seq(Deps.collectionCompat) + if crossScalaVersion.startsWith("2.12.") then Seq(Deps.collectionCompat) else Nil - super.ivyDeps() ++ maybeCollectionCompat ++ Agg( + super.mvnDeps() ++ maybeCollectionCompat ++ Seq( Deps.jsoniterCoreJava8 ) } - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Agg( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacrosJava8 ) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ Seq("-release", "8") } @@ -663,12 +664,12 @@ trait Config extends ScalaCliCrossSbtModule // some compatibility-related imports, that are actually only used // in Scala 2.12. def fix(args: String*): Command[Unit] = - if (crossScalaVersion.startsWith("2.12.")) super.fix(args: _*) + if crossScalaVersion.startsWith("2.12.") then super.fix(args*) else Task.Command(()) } trait Options extends ScalaCliCrossSbtModule with ScalaCliPublishModule with HasTests - with ScalaCliScalafixModule { + with ScalaCliScalafixModule with LocatedInModules { def crossScalaVersion: String = crossValue def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq( core(crossScalaVersion) @@ -676,15 +677,15 @@ trait Options extends ScalaCliCrossSbtModule with ScalaCliPublishModule with Has def compileModuleDeps: Seq[JavaModule] = Seq( `build-macros`(crossScalaVersion) ) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion) } - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.bloopConfig, Deps.signingCliShared ) - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Seq( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacros ) @@ -699,20 +700,20 @@ trait Options extends ScalaCliCrossSbtModule with ScalaCliPublishModule with Has } } -trait Scala3Runtime extends CrossSbtModule with ScalaCliPublishModule { +trait Scala3Runtime extends CrossSbtModule with ScalaCliPublishModule with LocatedInModules { def crossScalaVersion: String = crossValue - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() } trait Scala3Graal extends ScalaCliCrossSbtModule - with ScalaCliPublishModule with ScalaCliScalafixModule { + with ScalaCliPublishModule with ScalaCliScalafixModule with LocatedInModules { def crossScalaVersion: String = crossValue - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.asm, Deps.osLib ) - def resources: Target[Seq[PathRef]] = Task.Sources { + def resources: T[Seq[PathRef]] = Task { val extraResourceDir = Task.dest / "extra" // scala3RuntimeFixes.jar is also used within // resource-config.json and BytecodeProcessor.scala @@ -725,17 +726,19 @@ trait Scala3Graal extends ScalaCliCrossSbtModule } } -trait Scala3GraalProcessor extends CrossScalaModule with ScalaCliPublishModule { +trait Scala3GraalProcessor extends CrossScalaModule with ScalaCliPublishModule + with LocatedInModules { def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq(`scala3-graal`(crossScalaVersion)) - def finalMainClass: Target[String] = "scala.cli.graal.CoursierCacheProcessor" + def finalMainClass: T[String] = "scala.cli.graal.CoursierCacheProcessor" } trait Build extends ScalaCliCrossSbtModule with ScalaCliPublishModule with HasTests - with ScalaCliScalafixModule { + with ScalaCliScalafixModule + with LocatedInModules { def crossScalaVersion: String = crossValue - def millSourcePath: os.Path = super.millSourcePath / os.up / "build" + def moduleDir: os.Path = super.moduleDir / os.up / "build" def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq( options(crossScalaVersion), directives(crossScalaVersion), @@ -743,15 +746,15 @@ trait Build extends ScalaCliCrossSbtModule `test-runner`(crossScalaVersion), `tasty-lib`(crossScalaVersion) ) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion) } - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Agg( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacros, Deps.svm ) - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.asm, Deps.collectionCompat, Deps.javaClassName, @@ -770,17 +773,17 @@ trait Build extends ScalaCliCrossSbtModule Task.Anon(super.repositoriesTask() ++ deps.customRepositories) object test extends ScalaCliTests { - def ivyDeps: Target[Loose.Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.pprint, Deps.slf4jNop ) - def runClasspath: Target[Seq[PathRef]] = Task { + def runClasspath: T[Seq[PathRef]] = Task { super.runClasspath() ++ Seq(`local-repo`.localRepoJar()) } - def generatedSources: Target[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) - def constantsFile: Target[PathRef] = Task(persistent = true) { + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants2.scala" val code = @@ -802,7 +805,7 @@ trait Build extends ScalaCliCrossSbtModule | def defaultScala213Version = "${Scala.scala213}" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } @@ -815,19 +818,17 @@ trait Build extends ScalaCliCrossSbtModule } trait SpecificationLevel extends ScalaCliCrossSbtModule - with ScalaCliPublishModule { - def crossScalaVersion: String = crossValue - def scalacOptions: Target[Seq[String]] = Task { + with ScalaCliPublishModule with LocatedInModules { + def crossScalaVersion: String = crossValue + def scalacOptions: T[Seq[String]] = Task { val isScala213 = crossScalaVersion.startsWith("2.13.") - val extraOptions = - if (isScala213) Seq("-Xsource:3") - else Nil + val extraOptions = if isScala213 then Seq("-Xsource:3") else Nil super.scalacOptions() ++ extraOptions ++ Seq("-release", "8") } } trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers - with FormatNativeImageConf { + with FormatNativeImageConf with LocatedInModules { // Copied from Mill: https://github.com/com-lihaoyi/mill/blob/ea367c09bd31a30464ca901cb29863edde5340be/scalalib/src/mill/scalalib/JavaModule.scala#L792 def debug(port: Int, args: Task[Args] = Task.Anon(Args())): Command[Unit] = Task.Command { try mill.api.Result.Success( @@ -846,7 +847,7 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers } } - def constantsFile: Target[PathRef] = Task(persistent = true) { + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants.scala" val code = @@ -863,11 +864,11 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers | def maxAmmoniteScala3LtsVersion = "${Scala.maxAmmoniteScala3LtsVersion}" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } - def optionsConstantsFile: Target[PathRef] = Task(persistent = true) { + def optionsConstantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants.scala" val code = @@ -890,14 +891,14 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers | def signingCliJvmVersion = ${Deps.Versions.signingCliJvmVersion} |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } - def generatedSources: Target[Seq[PathRef]] = + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile(), optionsConstantsFile()) - def defaultFilesResources: Target[PathRef] = Task(persistent = true) { + def defaultFilesResources: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "resources" def transformWorkflow(content: Array[Byte]): Array[Byte] = new String(content, "UTF-8") @@ -917,21 +918,19 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers ) for ((srcUrl, destRelPath, transform) <- resources) { val dest = dir / defaultFilesResourcePath / destRelPath - if (!os.isFile(dest)) { + if !os.isFile(dest) then { val content = Using.resource(new URL(srcUrl).openStream())(_.readAllBytes()) os.write(dest, transform(content), createFolders = true) } } PathRef(dir) } - override def resources: Target[Seq[PathRef]] = Task.Sources { - super.resources() ++ Seq(defaultFilesResources()) - } + override def resources: T[Seq[PathRef]] = super.resources() ++ Seq(defaultFilesResources()) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ asyncScalacOptions(crossScalaVersion) } - def javacOptions: Target[Seq[String]] = Task { + def javacOptions: T[Seq[String]] = Task { super.javacOptions() ++ Seq("--release", "16") } def moduleDeps: Seq[SonatypeCentralPublishModule] = Seq( @@ -944,7 +943,7 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers def repositoriesTask: Task[Seq[Repository]] = Task.Anon(super.repositoriesTask() ++ customRepositories) - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.caseApp, Deps.coursierLauncher, Deps.coursierProxySetup, @@ -961,13 +960,13 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers Deps.sttp, Deps.scalafixInterfaces ) - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Agg( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacros, Deps.svm ) - def mainClass: Target[Option[String]] = Some("scala.cli.ScalaCli") + def mainClass: T[Option[String]] = Some("scala.cli.ScalaCli") - override def nativeImageClassPath: Target[Seq[PathRef]] = Task { + override def nativeImageClassPath: T[Seq[PathRef]] = Task { val classpath = super.nativeImageClassPath().map(_.path).mkString(File.pathSeparator) val cache = Task.dest / "native-cp" // `scala3-graal-processor`.run() do not give me output and I cannot pass dynamically computed values like classpath @@ -980,17 +979,17 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers cp.split(File.pathSeparator).toSeq.map(p => PathRef(os.Path(p))) } - def localRepoJar: Target[PathRef] = `local-repo`.localRepoJar() + def localRepoJar: T[PathRef] = `local-repo`.localRepoJar() object test extends ScalaCliTests with ScalaCliScalafixModule { def moduleDeps: Seq[JavaModule] = super.moduleDeps ++ Seq( `build-module`(crossScalaVersion).test ) - def runClasspath: Target[Seq[PathRef]] = Task { + def runClasspath: T[Seq[PathRef]] = Task { super.runClasspath() ++ Seq(localRepoJar()) } - def compileIvyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def compileMvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.jsoniterMacros ) @@ -1002,24 +1001,24 @@ trait Cli extends CrossSbtModule with ProtoBuildModule with CliLaunchers } trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests - with ScalaCliScalafixModule { - def scalaVersion: Target[String] = sv + with ScalaCliScalafixModule with LocatedInModules { + def scalaVersion: T[String] = sv def sv: String = Scala.scala213 - def tmpDirBase: Target[PathRef] = Task(persistent = true) { + def tmpDirBase: T[PathRef] = Task(persistent = true) { PathRef(Task.dest / "working-dir") } - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ Seq("-Xasync", "-deprecation") } - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.osLib ) trait IntegrationScalaTests extends super.ScalaCliTests with ScalaCliScalafixModule { - def ivyDeps: Target[Loose.Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.bsp4j, Deps.coursier .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-macros")), @@ -1031,20 +1030,20 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests Deps.slf4jNop, Deps.usingDirectives ) - def compileIvyDeps: Target[Agg[Dep]] = super.compileIvyDeps() ++ Seq( + def compileMvnDeps: T[Seq[Dep]] = super.compileMvnDeps() ++ Seq( Deps.jsoniterMacros ) - def forkEnv: Target[Map[String, String]] = super.forkEnv() ++ Seq( + def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Seq( "SCALA_CLI_TMP" -> tmpDirBase().path.toString, "SCALA_CLI_PRINT_STACK_TRACES" -> "1", "SCALA_CLI_CONFIG" -> (tmpDirBase().path / "config" / "config.json").toString ) - def constantsFile: Target[PathRef] = Task(persistent = true) { + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants.scala" val mostlyStaticDockerfile = - Task.workspace / ".github" / "scripts" / "docker" / "ScalaCliSlimDockerFile" + BuildCtx.workspaceRoot / ".github" / "scripts" / "docker" / "ScalaCliSlimDockerFile" assert( os.exists(mostlyStaticDockerfile), s"Error: $mostlyStaticDockerfile not found" @@ -1122,75 +1121,88 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests | def jmhGeneratorBytecodeModule = "${Deps.jmhGeneratorBytecode.dep.module.name.value}" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } - def generatedSources: Target[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) - override def test(args: String*): Command[(String, Seq[TestResult])] = jvm(args: _*) + override def testForked(args: String*): Command[(msg: String, results: Seq[TestResult])] = + jvm(args*) - def forcedLauncher: Target[PathRef] = Task(persistent = true) { - val ext = if (Properties.isWin) ".exe" else "" + def forcedLauncher: T[PathRef] = Task(persistent = true) { + val ext = if Properties.isWin then ".exe" else "" val launcher = Task.dest / s"scala-cli$ext" - if (!os.exists(launcher)) { - val dir = Option(System.getenv("SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY")).getOrElse { - sys.error("SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY not set") + if !os.exists(launcher) then + BuildCtx.withFilesystemCheckerDisabled { + val dir = Option(System.getenv("SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY")).getOrElse { + sys.error("SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY not set") + } + System.err.println(s"SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY was set to $dir") + val content = importedLauncher(dir, BuildCtx.workspaceRoot) + System.err.println(s"writing launcher to $launcher") + os.write( + launcher, + content, + createFolders = true, + perms = if Properties.isWin then null else "rwxr-xr-x" + ) } - val content = importedLauncher(dir, Task.workspace) - os.write( - launcher, - content, - createFolders = true, - perms = if (Properties.isWin) null else "rwxr-xr-x" - ) - } PathRef(launcher) } - def forcedStaticLauncher: Target[PathRef] = Task(persistent = true) { + def forcedStaticLauncher: T[PathRef] = Task(persistent = true) { val launcher = Task.dest / "scala-cli" - if (!os.exists(launcher)) { - val dir = Option(System.getenv("SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY")).getOrElse { - sys.error("SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY not set") + if !os.exists(launcher) then + BuildCtx.withFilesystemCheckerDisabled { + val dir = + Option(System.getenv("SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY")).getOrElse { + sys.error("SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY not set") + } + System.err.println(s"SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY was set to $dir") + val content = importedLauncher(dir, BuildCtx.workspaceRoot) + System.err.println(s"writing launcher to $launcher") + os.write(launcher, content, createFolders = true) } - val content = importedLauncher(dir, Task.workspace) - os.write(launcher, content, createFolders = true) - } PathRef(launcher) } - def forcedMostlyStaticLauncher: Target[PathRef] = Task(persistent = true) { + def forcedMostlyStaticLauncher: T[PathRef] = Task(persistent = true) { val launcher = Task.dest / "scala-cli" - if (!os.exists(launcher)) { - val dir = - Option(System.getenv("SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY")).getOrElse { - sys.error("SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY not set") - } - val content = importedLauncher(dir, Task.workspace) - os.write(launcher, content, createFolders = true) - } + if !os.exists(launcher) then + BuildCtx.withFilesystemCheckerDisabled { + val dir = + Option(System.getenv("SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY")).getOrElse { + sys.error("SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY not set") + } + System.err.println( + s"SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY was set to $dir" + ) + val content = importedLauncher(dir, BuildCtx.workspaceRoot) + System.err.println(s"writing launcher to $launcher") + os.write(launcher, content, createFolders = true) + } PathRef(launcher) } private object Launchers { - def jvm: Target[PathRef] = cli(Scala.defaultInternal).standaloneLauncher + def jvm: T[PathRef] = cli(Scala.defaultInternal).standaloneLauncher - def jvmBootstrapped: Target[PathRef] = cliBootstrapped.jar + def jvmBootstrapped: T[PathRef] = cliBootstrapped.jar - def native: Target[PathRef] = + def native: T[PathRef] = Option(System.getenv("SCALA_CLI_IT_FORCED_LAUNCHER_DIRECTORY")) match { case Some(_) => forcedLauncher case None => cli(Scala.defaultInternal).nativeImage } - def nativeStatic: Target[PathRef] = + def nativeStatic: T[PathRef] = Option(System.getenv("SCALA_CLI_IT_FORCED_STATIC_LAUNCHER_DIRECTORY")) match { case Some(_) => forcedStaticLauncher case None => cli(Scala.defaultInternal).nativeImageStatic } - def nativeMostlyStatic: Target[PathRef] = + def nativeMostlyStatic: T[PathRef] = Option(System.getenv("SCALA_CLI_IT_FORCED_MOSTLY_STATIC_LAUNCHER_DIRECTORY")) match { case Some(_) => forcedMostlyStaticLauncher case None => cli(Scala.defaultInternal).nativeImageMostlyStatic @@ -1222,63 +1234,67 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests private def testArgs(args: Seq[String], launcher: os.Path, cliKind: String): Seq[String] = extraTestArgs(launcher, cliKind) ++ debugTestArgs(args) - def jvm(args: String*): Command[(String, Seq[TestResult])] = Task.Command { + def jvm(args: String*): Command[(msg: String, results: Seq[TestResult])] = Task.Command { testTask( Task.Anon(args ++ testArgs(args, Launchers.jvm().path, "jvm")), Task.Anon(Seq.empty[String]) )() } - def jvmBootstrapped(args: String*): Command[(String, Seq[TestResult])] = Task.Command { - testTask( - Task.Anon(args ++ testArgs(args, Launchers.jvmBootstrapped().path, "jvmBootstrapped")), - Task.Anon(Seq.empty[String]) - )() - } - def native(args: String*): Command[(String, Seq[TestResult])] = Task.Command { + def jvmBootstrapped(args: String*): Command[(msg: String, results: Seq[TestResult])] = + Task.Command { + testTask( + Task.Anon(args ++ testArgs(args, Launchers.jvmBootstrapped().path, "jvmBootstrapped")), + Task.Anon(Seq.empty[String]) + )() + } + def native(args: String*): Command[(msg: String, results: Seq[TestResult])] = Task.Command { testTask( Task.Anon(args ++ testArgs(args, Launchers.native().path, "native")), Task.Anon(Seq.empty[String]) )() } - def nativeStatic(args: String*): Command[(String, Seq[TestResult])] = Task.Command { - testTask( - Task.Anon(args ++ testArgs(args, Launchers.nativeStatic().path, "native-static")), - Task.Anon(Seq.empty[String]) - )() - } - def nativeMostlyStatic(args: String*): Command[(String, Seq[TestResult])] = Task.Command { - testTask( - Task.Anon(args ++ testArgs( - args, - Launchers.nativeMostlyStatic().path, - "native-mostly-static" - )), - Task.Anon(Seq.empty[String]) - )() - } + def nativeStatic(args: String*): Command[(msg: String, results: Seq[TestResult])] = + Task.Command { + testTask( + Task.Anon(args ++ testArgs(args, Launchers.nativeStatic().path, "native-static")), + Task.Anon(Seq.empty[String]) + )() + } + def nativeMostlyStatic(args: String*): Command[(msg: String, results: Seq[TestResult])] = + Task.Command { + testTask( + Task.Anon(args ++ testArgs( + args, + Launchers.nativeMostlyStatic().path, + "native-mostly-static" + )), + Task.Anon(Seq.empty[String]) + )() + } } } trait CliIntegrationDocker extends SbtModule with ScalaCliPublishModule with HasTests { def scalaVersion: T[String] = Scala.scala213 - def ivyDeps: T[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.osLib ) } trait Runner extends ScalaCliCrossSbtModule with ScalaCliPublishModule - with ScalaCliScalafixLegacyModule { - def crossScalaVersion: String = crossValue - def scalacOptions: Target[Seq[String]] = Task { + with ScalaCliScalafixLegacyModule + with LocatedInModules { + def crossScalaVersion: String = crossValue + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ Seq("-release", "8") } - def mainClass: Target[Option[String]] = Some("scala.cli.runner.Runner") - def sources: Target[Seq[PathRef]] = Task.Sources { + def mainClass: T[Option[String]] = Some("scala.cli.runner.Runner") + def sources: T[Seq[PathRef]] = { val scala3DirNames = - if (crossScalaVersion.startsWith("3.")) { + if crossScalaVersion.startsWith("3.") then { val name = - if (crossScalaVersion.contains("-RC")) "scala-3-unstable" + if crossScalaVersion.contains("-RC") then "scala-3-unstable" else "scala-3-stable" Seq(name) } @@ -1291,24 +1307,26 @@ trait Runner extends ScalaCliCrossSbtModule trait TestRunner extends ScalaCliCrossSbtModule with ScalaCliPublishModule - with ScalaCliScalafixLegacyModule { - def crossScalaVersion: String = crossValue - def scalacOptions: Target[Seq[String]] = Task { + with ScalaCliScalafixLegacyModule + with LocatedInModules { + def crossScalaVersion: String = crossValue + def scalacOptions: T[Seq[String]] = Task { super.scalacOptions() ++ Seq("-release", "8") } - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.asm, Deps.collectionCompat, Deps.testInterface ) - def mainClass: Target[Option[String]] = Some("scala.build.testrunner.DynamicTestRunner") + def mainClass: T[Option[String]] = Some("scala.build.testrunner.DynamicTestRunner") } trait TastyLib extends ScalaCliCrossSbtModule with ScalaCliPublishModule - with ScalaCliScalafixLegacyModule { - def crossScalaVersion: String = crossValue - def constantsFile: Target[PathRef] = Task(persistent = true) { + with ScalaCliScalafixLegacyModule + with LocatedInModules { + def crossScalaVersion: String = crossValue + def constantsFile: T[PathRef] = Task(persistent = true) { val dir = Task.dest / "constants" val dest = dir / "Constants.scala" val code = @@ -1319,12 +1337,12 @@ trait TastyLib extends ScalaCliCrossSbtModule | def defaultScalaVersion = "${Scala.defaultUser}" |} |""".stripMargin - if (!os.isFile(dest) || os.read(dest) != code) + if !os.isFile(dest) || os.read(dest) != code then os.write.over(dest, code, createFolders = true) PathRef(dir) } - def generatedSources: Target[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) + def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ Seq(constantsFile()) } object `local-repo` extends LocalRepo { @@ -1341,11 +1359,11 @@ object `local-repo` extends LocalRepo { sv <- Scala.runnerScalaVersions proj <- Seq(runner, `test-runner`) } yield proj(sv) - def version: Target[String] = runner(Scala.runnerScala3).publishVersion() + def version: T[String] = runner(Scala.runnerScala3).publishVersion() } // Helper CI commands -def publishSonatype(tasks: mill.main.Tasks[PublishModule.PublishData]) = Task.Command { +def publishSonatype(tasks: Tasks[PublishModule.PublishData]) = Task.Command { val taskNames = tasks.value.map(_.toString()) System.err.println( s"""Tasks producing artifacts to be included in the bundle: @@ -1356,17 +1374,17 @@ def publishSonatype(tasks: mill.main.Tasks[PublishModule.PublishData]) = Task.Co val bundleName = s"$organization-$ghName-$pv" System.err.println(s"Publishing bundle: $bundleName") publish.publishSonatype( - data = define.Target.sequence(tasks.value)(), + data = Task.sequence(tasks.value)(), log = Task.ctx().log, - workspace = Task.workspace, + workspace = BuildCtx.workspaceRoot, env = Task.env, bundleName = bundleName ) } -def copyTo(task: mill.main.Tasks[PathRef], dest: String): Command[Unit] = Task.Command { - val destPath = os.Path(dest, Task.workspace) - if (task.value.length > 1) +def copyTo(task: Tasks[PathRef], dest: String): Command[Unit] = Task.Command { + val destPath = os.Path(dest, BuildCtx.workspaceRoot) + if task.value.length > 1 then sys.error("Expected a single task") val ref = task.value.head() os.makeDir.all(destPath / os.up) @@ -1374,29 +1392,29 @@ def copyTo(task: mill.main.Tasks[PathRef], dest: String): Command[Unit] = Task.C } def writePackageVersionTo(dest: String): Command[Unit] = Task.Command { - val destPath = os.Path(dest, Task.workspace) + val destPath = os.Path(dest, BuildCtx.workspaceRoot) val rawVersion = cli(Scala.defaultInternal).publishVersion() val version = - if (rawVersion.contains("+")) rawVersion.stripSuffix("-SNAPSHOT") + if rawVersion.contains("+") then rawVersion.stripSuffix("-SNAPSHOT") else rawVersion os.write.over(destPath, version) } def writeShortPackageVersionTo(dest: String): Command[Unit] = Task.Command { - val destPath = os.Path(dest, Task.workspace) + val destPath = os.Path(dest, BuildCtx.workspaceRoot) val rawVersion = cli(Scala.defaultInternal).publishVersion() val version = rawVersion.takeWhile(c => c != '-' && c != '+') os.write.over(destPath, version) } def importedLauncher(directory: String = "artifacts", workspace: os.Path): Array[Byte] = { - val ext = if (Properties.isWin) ".zip" else ".gz" + val ext = if Properties.isWin then ".zip" else ".gz" val from = os.Path(directory, workspace) / s"scala-cli-${Upload.platformSuffix}$ext" System.err.println(s"Importing launcher from $from") - if (!os.exists(from)) + if !os.exists(from) then sys.error(s"$from not found") - if (Properties.isWin) { + if Properties.isWin then { import java.util.zip.ZipFile Using.resource(new ZipFile(from.toIO)) { zf => val ent = zf.getEntry("scala-cli.exe") @@ -1417,14 +1435,14 @@ def importedLauncher(directory: String = "artifacts", workspace: os.Path): Array } } -def copyLauncher(directory: String = "artifacts"): Command[Path] = Task.Command { +def copyLauncher(directory: String = "artifacts"): Command[os.Path] = Task.Command { val nativeLauncher = cli(Scala.defaultInternal).nativeImage().path Upload.copyLauncher0( nativeLauncher = nativeLauncher, directory = directory, name = "scala-cli", compress = true, - workspace = Task.workspace + workspace = BuildCtx.workspaceRoot ) } @@ -1432,7 +1450,7 @@ def copyJvmLauncher(directory: String = "artifacts"): Command[Unit] = Task.Comma val launcher = cli(Scala.defaultInternal).standaloneLauncher().path os.copy( launcher, - os.Path(directory, Task.workspace) / s"scala-cli$platformExecutableJarExtension", + os.Path(directory, BuildCtx.workspaceRoot) / s"scala-cli$platformExecutableJarExtension", createFolders = true, replaceExisting = true ) @@ -1441,7 +1459,7 @@ def copyJvmBootstrappedLauncher(directory: String = "artifacts"): Command[Unit] val launcher = cliBootstrapped.jar().path os.copy( launcher, - os.Path(directory, Task.workspace) / s"scala-cli.jar", + os.Path(directory, BuildCtx.workspaceRoot) / s"scala-cli.jar", createFolders = true, replaceExisting = true ) @@ -1450,25 +1468,25 @@ def copyJvmBootstrappedLauncher(directory: String = "artifacts"): Command[Unit] def uploadLaunchers(directory: String = "artifacts"): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val path = os.Path(directory, Task.workspace) + val path = os.Path(directory, BuildCtx.workspaceRoot) val launchers = os.list(path).filter(os.isFile(_)).map { path => path -> path.last } val (tag, overwriteAssets) = - if (version.endsWith("-SNAPSHOT")) ("nightly", true) + if version.endsWith("-SNAPSHOT") then ("nightly", true) else ("v" + version, false) System.err.println(s"Uploading to tag $tag (overwrite assets: $overwriteAssets)") Upload.upload(ghOrg, ghName, ghToken(), tag, dryRun = false, overwrite = overwriteAssets)( - launchers: _* + launchers* ) } -def unitTests(): Command[(String, Seq[TestResult])] = Task.Command { - `build-module`(Scala.defaultInternal).test.test()() - `build-macros`(Scala.defaultInternal).test.test()() - cli(Scala.defaultInternal).test.test()() - directives(Scala.defaultInternal).test.test()() - options(Scala.defaultInternal).test.test()() +def unitTests(): Command[(msg: String, results: Seq[TestResult])] = Task.Command { + `build-module`(Scala.defaultInternal).test.testForked()() + `build-macros`(Scala.defaultInternal).test.testForked()() + cli(Scala.defaultInternal).test.testForked()() + directives(Scala.defaultInternal).test.testForked()() + options(Scala.defaultInternal).test.testForked()() } def scala(args: Task[Args] = Task.Anon(Args())) = Task.Command { @@ -1484,45 +1502,43 @@ def defaultNativeImage(): Command[PathRef] = cli(Scala.defaultInternal).nativeImage() } -def nativeIntegrationTests(): Command[(String, Seq[TestResult])] = - Task.Command { - integration.test.native()() - } +def nativeIntegrationTests(): Command[(msg: String, results: Seq[TestResult])] = + integration.test.native() -def copyDefaultLauncher(directory: String = "artifacts"): Command[Path] = +def copyDefaultLauncher(directory: String = "artifacts"): Command[os.Path] = Task.Command { copyLauncher(directory)() } -def copyMostlyStaticLauncher(directory: String = "artifacts"): Command[Path] = Task.Command { +def copyMostlyStaticLauncher(directory: String = "artifacts"): Command[os.Path] = Task.Command { val nativeLauncher = cli(Scala.defaultInternal).nativeImageMostlyStatic().path Upload.copyLauncher0( nativeLauncher = nativeLauncher, directory = directory, name = "scala-cli", compress = true, - workspace = Task.workspace, + workspace = BuildCtx.workspaceRoot, suffix = "-mostly-static" ) } -def copyStaticLauncher(directory: String = "artifacts"): Command[Path] = Task.Command { +def copyStaticLauncher(directory: String = "artifacts"): Command[os.Path] = Task.Command { val nativeLauncher = cli(Scala.defaultInternal).nativeImageStatic().path Upload.copyLauncher0( nativeLauncher = nativeLauncher, directory = directory, name = "scala-cli", compress = true, - workspace = Task.workspace, + workspace = BuildCtx.workspaceRoot, suffix = "-static" ) } private def ghToken(): String = Option(System.getenv("UPLOAD_GH_TOKEN")).getOrElse { sys.error("UPLOAD_GH_TOKEN not set") } -private def gitClone(repo: String, branch: String, workDir: os.Path): CommandResult = +private def gitClone(repo: String, branch: String, workDir: os.Path): os.CommandResult = os.proc("git", "clone", repo, "-q", "-b", branch).call(cwd = workDir) -private def setupGithubRepo(repoDir: os.Path): CommandResult = { +private def setupGithubRepo(repoDir: os.Path): os.CommandResult = { val gitUserName = "gh-actions" val gitEmail = "actions@github.com" @@ -1536,13 +1552,13 @@ private def commitChanges( repoDir: os.Path, force: Boolean = false ): Unit = { - if (os.proc("git", "status").call(cwd = repoDir).out.trim().contains("nothing to commit")) + if os.proc("git", "status").call(cwd = repoDir).out.trim().contains("nothing to commit") then println("Nothing Changes") else { os.proc("git", "add", "-A").call(cwd = repoDir) os.proc("git", "commit", "-am", name).call(cwd = repoDir) println(s"Trying to push on $branch branch") - val pushExtraOptions = if (force) Seq("--force") else Seq.empty + val pushExtraOptions = if force then Seq("--force") else Seq.empty os.proc("git", "push", "origin", branch, pushExtraOptions).call(cwd = repoDir) println(s"Push successfully on $branch branch") } @@ -1556,12 +1572,12 @@ object ci extends Module { def updateScalaCliSetup(): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target-scala-cli-setup" + val targetDir = BuildCtx.workspaceRoot / "target-scala-cli-setup" val mainDir = targetDir / "scala-cli-setup" val setupScriptPath = mainDir / "src" / "main.ts" // clean target directory - if (os.exists(targetDir)) os.remove.all(targetDir) + if os.exists(targetDir) then os.remove.all(targetDir) os.makeDir.all(targetDir) @@ -1582,17 +1598,17 @@ object ci extends Module { os.proc("git", "switch", "-c", targetBranch).call(cwd = mainDir) commitChanges(s"Update scala-cli version to $version", targetBranch, mainDir, force = true) } - def updateStandaloneLauncher(): Command[CommandResult] = Task.Command { + def updateStandaloneLauncher(): Command[os.CommandResult] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target" + val targetDir = BuildCtx.workspaceRoot / "target" val scalaCliDir = targetDir / "scala-cli" val standaloneLauncherPath = scalaCliDir / "scala-cli.sh" val standaloneWindowsLauncherPath = scalaCliDir / "scala-cli.bat" // clean scala-cli directory - if (os.exists(scalaCliDir)) os.remove.all(scalaCliDir) - if (!os.exists(targetDir)) os.makeDir.all(targetDir) + if os.exists(scalaCliDir) then os.remove.all(scalaCliDir) + if !os.exists(targetDir) then os.makeDir.all(targetDir) val branch = "main" val targetBranch = s"update-standalone-launcher-$version" @@ -1654,11 +1670,11 @@ object ci extends Module { def updateScalaCliBrewFormula(): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target" + val targetDir = BuildCtx.workspaceRoot / "target" val homebrewFormulaDir = targetDir / "homebrew-scala-cli" // clean target directory - if (os.exists(targetDir)) os.remove.all(targetDir) + if os.exists(targetDir) then os.remove.all(targetDir) os.makeDir.all(targetDir) @@ -1674,13 +1690,15 @@ object ci extends Module { val arm64LauncherURL = s"https://github.com/Virtuslab/scala-cli/releases/download/v$version/scala-cli-aarch64-apple-darwin.gz" - val x86LauncherPath = os.Path("artifacts", Task.workspace) / "scala-cli-x86_64-apple-darwin.gz" + val x86LauncherPath = + os.Path("artifacts", BuildCtx.workspaceRoot) / "scala-cli-x86_64-apple-darwin.gz" val arm64LauncherPath = - os.Path("artifacts", Task.workspace) / "scala-cli-aarch64-apple-darwin.gz" + os.Path("artifacts", BuildCtx.workspaceRoot) / "scala-cli-aarch64-apple-darwin.gz" val (x86Sha256, arm64Sha256) = brewLaunchersSha(x86LauncherPath, arm64LauncherPath, targetDir) - val templateFormulaPath = Task.workspace / ".github" / "scripts" / "scala-cli.rb.template" - val template = os.read(templateFormulaPath) + val templateFormulaPath = + BuildCtx.workspaceRoot / ".github" / "scripts" / "scala-cli.rb.template" + val template = os.read(templateFormulaPath) val updatedFormula = template .replace("@X86_LAUNCHER_URL@", x86LauncherURL) @@ -1697,11 +1715,11 @@ object ci extends Module { def updateScalaExperimentalBrewFormula(): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target" + val targetDir = BuildCtx.workspaceRoot / "target" val homebrewFormulaDir = targetDir / "homebrew-scala-experimental" // clean homebrew-scala-experimental directory - if (os.exists(homebrewFormulaDir)) os.remove.all(homebrewFormulaDir) + if os.exists(homebrewFormulaDir) then os.remove.all(homebrewFormulaDir) os.makeDir.all(targetDir) @@ -1717,12 +1735,13 @@ object ci extends Module { val arm64LauncherURL = s"https://github.com/Virtuslab/scala-cli/releases/download/v$version/scala-cli-aarch64-apple-darwin.gz" - val x86LauncherPath = os.Path("artifacts", Task.workspace) / "scala-cli-x86_64-apple-darwin.gz" + val x86LauncherPath = + os.Path("artifacts", BuildCtx.workspaceRoot) / "scala-cli-x86_64-apple-darwin.gz" val arm64LauncherPath = - os.Path("artifacts", Task.workspace) / "scala-cli-aarch64-apple-darwin.gz" + os.Path("artifacts", BuildCtx.workspaceRoot) / "scala-cli-aarch64-apple-darwin.gz" val (x86Sha256, arm64Sha256) = brewLaunchersSha(x86LauncherPath, arm64LauncherPath, targetDir) - val templateFormulaPath = Task.workspace / ".github" / "scripts" / "scala.rb.template" + val templateFormulaPath = BuildCtx.workspaceRoot / ".github" / "scripts" / "scala.rb.template" val template = os.read(templateFormulaPath) val updatedFormula = template @@ -1740,12 +1759,12 @@ object ci extends Module { def updateInstallationScript(): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target" + val targetDir = BuildCtx.workspaceRoot / "target" val packagesDir = targetDir / "scala-cli-packages" val installationScriptPath = packagesDir / "scala-setup.sh" // clean target directory - if (os.exists(targetDir)) os.remove.all(targetDir) + if os.exists(targetDir) then os.remove.all(targetDir) os.makeDir.all(targetDir) @@ -1767,12 +1786,12 @@ object ci extends Module { def updateDebianPackages(): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target" + val targetDir = BuildCtx.workspaceRoot / "target" val packagesDir = targetDir / "scala-cli-packages" val debianDir = packagesDir / "debian" // clean target directory - if (os.exists(targetDir)) os.remove.all(targetDir) + if os.exists(targetDir) then os.remove.all(targetDir) os.makeDir.all(targetDir) @@ -1785,7 +1804,7 @@ object ci extends Module { // copy deb package to repository os.copy( - os.Path("artifacts", Task.workspace) / "scala-cli-x86_64-pc-linux.deb", + os.Path("artifacts", BuildCtx.workspaceRoot) / "scala-cli-x86_64-pc-linux.deb", debianDir / s"scala-cli_$version.deb" ) @@ -1832,15 +1851,15 @@ object ci extends Module { commitChanges(s"Update Debian packages for $version", branch, packagesDir) } - def updateChocolateyPackage(): Command[CommandResult] = Task.Command { + def updateChocolateyPackage(): Command[os.CommandResult] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val packagesDir = Task.workspace / "target" / "scala-cli-packages" - val chocoDir = Task.workspace / ".github" / "scripts" / "choco" + val packagesDir = BuildCtx.workspaceRoot / "target" / "scala-cli-packages" + val chocoDir = BuildCtx.workspaceRoot / ".github" / "scripts" / "choco" val msiPackagePath = packagesDir / s"scala-cli_$version.msi" os.copy( - Task.workspace / "artifacts" / "scala-cli-x86_64-pc-win32.msi", + BuildCtx.workspaceRoot / "artifacts" / "scala-cli-x86_64-pc-win32.msi", msiPackagePath, createFolders = true ) @@ -1879,12 +1898,12 @@ object ci extends Module { def updateCentOsPackages(): Command[Unit] = Task.Command { val version = cli(Scala.defaultInternal).publishVersion() - val targetDir = Task.workspace / "target" + val targetDir = BuildCtx.workspaceRoot / "target" val packagesDir = targetDir / "scala-cli-packages" val centOsDir = packagesDir / "CentOS" // clean target directory - if (os.exists(targetDir)) os.remove.all(targetDir) + if os.exists(targetDir) then os.remove.all(targetDir) os.makeDir.all(targetDir) @@ -1897,7 +1916,7 @@ object ci extends Module { // copy rpm package to repository os.copy( - os.Path("artifacts", Task.workspace) / "scala-cli-x86_64-pc-linux.rpm", + os.Path("artifacts", BuildCtx.workspaceRoot) / "scala-cli-x86_64-pc-linux.rpm", centOsDir / "Packages" / s"scala-cli_$version.rpm" ) @@ -1944,7 +1963,7 @@ object ci extends Module { edition <- vcEditions } yield vsBasePath / year / edition / "VC" / "Redist" / "MSVC" val baseDirs = candidateBaseDirs.filter(os.isDir(_)) - if (baseDirs.isEmpty) + if baseDirs.isEmpty then sys.error( s"No Visual Studio installation found, tried:" + System.lineSeparator() + candidateBaseDirs @@ -1968,7 +1987,7 @@ object ci extends Module { .mkString(System.lineSeparator()) ) } - val destDir = os.Path(directory, Task.workspace) + val destDir = os.Path(directory, BuildCtx.workspaceRoot) os.copy(orig, destDir / distName, createFolders = true, replaceExisting = true) } def writeWixConfigExtra(dest: String = "wix-visual-cpp-redist.xml"): Command[Unit] = @@ -2012,38 +2031,39 @@ object ci extends Module { | | |""".stripMargin - val dest0 = os.Path(dest, Task.workspace) + val dest0 = os.Path(dest, BuildCtx.workspaceRoot) os.write.over(dest0, content.getBytes(Charset.defaultCharset()), createFolders = true) } def setShouldPublish(): Command[Unit] = publish.setShouldPublish() def shouldPublish(): Command[Unit] = Task.Command { println(publish.shouldPublish()) } - def copyJvm(jvm: String = deps.graalVmJvmId, dest: String = "jvm"): Command[Path] = Task.Command { - import sys.process._ - val command = Seq( - settings.cs(), - "java-home", - "--jvm", - jvm, - "--update", - "--ttl", - "0" - ) - val baseJavaHome = os.Path(command.!!.trim, Task.workspace) - System.err.println(s"Initial Java home $baseJavaHome") - val destJavaHome = os.Path(dest, Task.workspace) - os.copy(baseJavaHome, destJavaHome, createFolders = true) - System.err.println(s"New Java home $destJavaHome") - destJavaHome - } + def copyJvm(jvm: String = deps.graalVmJvmId, dest: String = "jvm"): Command[os.Path] = + Task.Command { + import sys.process.* + val command = Seq( + settings.cs(), + "java-home", + "--jvm", + jvm, + "--update", + "--ttl", + "0" + ) + val baseJavaHome = os.Path(command.!!.trim, BuildCtx.workspaceRoot) + System.err.println(s"Initial Java home $baseJavaHome") + val destJavaHome = os.Path(dest, BuildCtx.workspaceRoot) + os.copy(baseJavaHome, destJavaHome, createFolders = true) + System.err.println(s"New Java home $destJavaHome") + destJavaHome + } def checkScalaVersions(): Command[Unit] = Task.Command { website.checkMainScalaVersions( - Task.workspace / "website" / "docs" / "reference" / "scala-versions.md" + BuildCtx.workspaceRoot / "website" / "docs" / "reference" / "scala-versions.md" ) website.checkScalaJsVersions( - Task.workspace / "website" / "docs" / "guides" / "advanced" / "scala-js.md" + BuildCtx.workspaceRoot / "website" / "docs" / "guides" / "advanced" / "scala-js.md" ) } } diff --git a/mill b/mill index 931612bef3..7b3ccead5a 100755 --- a/mill +++ b/mill @@ -84,7 +84,7 @@ export USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM=false DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" if [[ $IS_WINDOWS ]]; then - exec "$DIR/millw" "$@" + exec "$DIR/mill.bat" "$@" else exec "$DIR/millw" $COMMAND fi diff --git a/project/deps/package.mill.scala b/project/deps/package.mill.scala index 24beaeb801..7cf0fc3556 100644 --- a/project/deps/package.mill.scala +++ b/project/deps/package.mill.scala @@ -1,6 +1,7 @@ package build.project.deps -import mill._ -import scalalib._ +import mill.* +import mill.api.BuildInfo +import scalalib.* object Cli { def runnerLegacyVersion = "1.7.1" // last runner version to support pre-LTS Scala 3 versions @@ -99,7 +100,7 @@ object TestDeps { object InternalDeps { object Versions { - def mill: String = _root_.mill.main.BuildInfo.millVersion + def mill: String = BuildInfo.millVersion def lefouMillwRef = "166bcdf5741de8569e0630e18c3b2ef7e252cd96" } } @@ -145,93 +146,93 @@ object Deps { // DO NOT hardcode a Scala version in this dependency string // This dependency is used to ensure that Ammonite is available for Scala versions // that Scala CLI supports. - def ammonite = ivy"com.lihaoyi:::ammonite:${Versions.ammonite}" - def ammoniteForScala3Lts = ivy"com.lihaoyi:::ammonite:${Versions.ammoniteForScala3Lts}" + def ammonite = mvn"com.lihaoyi:::ammonite:${Versions.ammonite}" + def ammoniteForScala3Lts = mvn"com.lihaoyi:::ammonite:${Versions.ammoniteForScala3Lts}" def argonautShapeless = - ivy"com.github.alexarchambault:argonaut-shapeless_6.3_2.13:${Versions.argonautShapeless}" - def asm = ivy"org.ow2.asm:asm:9.8" + mvn"com.github.alexarchambault:argonaut-shapeless_6.3_2.13:${Versions.argonautShapeless}" + def asm = mvn"org.ow2.asm:asm:9.8" // Force using of 2.13 - is there a better way? - def bloopConfig = ivy"ch.epfl.scala:bloop-config_2.13:2.3.2" + def bloopConfig = mvn"ch.epfl.scala:bloop-config_2.13:2.3.2" .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-core_2.13")) - def bloopRifle = ivy"ch.epfl.scala:bloop-rifle_2.13:${Versions.bloop}" - def bsp4j = ivy"ch.epfl.scala:bsp4j:2.1.1" - def caseApp = ivy"com.github.alexarchambault::case-app:2.1.0-M30" - def collectionCompat = ivy"org.scala-lang.modules::scala-collection-compat:2.13.0" + def bloopRifle = mvn"ch.epfl.scala:bloop-rifle_2.13:${Versions.bloop}" + def bsp4j = mvn"ch.epfl.scala:bsp4j:2.1.1" + def caseApp = mvn"com.github.alexarchambault::case-app:2.1.0-M30" + def collectionCompat = mvn"org.scala-lang.modules::scala-collection-compat:2.13.0" // Force using of 2.13 - is there a better way? - def coursier = ivy"io.get-coursier:coursier_2.13:${Versions.coursier}" - def coursierCli = ivy"io.get-coursier:coursier-cli_2.13:${Versions.coursierCli}" - def coursierJvm = ivy"io.get-coursier:coursier-jvm_2.13:${Versions.coursier}" + def coursier = mvn"io.get-coursier:coursier_2.13:${Versions.coursier}" + def coursierCli = mvn"io.get-coursier:coursier-cli_2.13:${Versions.coursierCli}" + def coursierJvm = mvn"io.get-coursier:coursier-jvm_2.13:${Versions.coursier}" .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-core_2.13")) .exclude("io.get-coursier" -> "dependency_2.13") - def coursierLauncher = ivy"io.get-coursier:coursier-launcher_2.13:${Versions.coursier}" + def coursierLauncher = mvn"io.get-coursier:coursier-launcher_2.13:${Versions.coursier}" .exclude(("ai.kien", "python-native-libs_2.13")) .exclude(("org.scala-lang.modules", "scala-collection-compat_2.13")) - def coursierProxySetup = ivy"io.get-coursier:coursier-proxy-setup:${Versions.coursier}" - def coursierPublish = ivy"io.get-coursier.publish::publish:${Versions.coursierPublish}" + def coursierProxySetup = mvn"io.get-coursier:coursier-proxy-setup:${Versions.coursier}" + def coursierPublish = mvn"io.get-coursier.publish::publish:${Versions.coursierPublish}" .exclude(("org.scala-lang.modules", "scala-collection-compat_2.13")) .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-core_3")) - def dependency = ivy"io.get-coursier::dependency:0.3.2" - def dockerClient = ivy"com.spotify:docker-client:8.16.0" - def expecty = ivy"com.eed3si9n.expecty::expecty:0.17.0" - def fansi = ivy"com.lihaoyi::fansi:0.5.0" - def giter8 = ivy"org.foundweekends.giter8:giter8:0.16.2" - def guava = ivy"com.google.guava:guava:33.4.8-jre" + def dependency = mvn"io.get-coursier::dependency:0.3.2" + def dockerClient = mvn"com.spotify:docker-client:8.16.0" + def expecty = mvn"com.eed3si9n.expecty::expecty:0.17.0" + def fansi = mvn"com.lihaoyi::fansi:0.5.0" + def giter8 = mvn"org.foundweekends.giter8:giter8:0.16.2" + def guava = mvn"com.google.guava:guava:33.4.8-jre" def javaClassName = - ivy"org.virtuslab.scala-cli.java-class-name:java-class-name_3:${Versions.javaClassName}" + mvn"org.virtuslab.scala-cli.java-class-name:java-class-name_3:${Versions.javaClassName}" .exclude( "org.jline" -> "jline-reader", "org.jline" -> "jline-terminal", "org.jline" -> "jline-terminal-jna" ) - def jgit = ivy"org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r" - def jimfs = ivy"com.google.jimfs:jimfs:1.3.1" - def jmhGeneratorBytecode = ivy"org.openjdk.jmh:jmh-generator-bytecode:${Versions.jmh}" - def jmhCore = ivy"org.openjdk.jmh:jmh-core:${Versions.jmh}" - def jniUtils = ivy"io.get-coursier.jniutils:windows-jni-utils:0.3.3" + def jgit = mvn"org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r" + def jimfs = mvn"com.google.jimfs:jimfs:1.3.1" + def jmhGeneratorBytecode = mvn"org.openjdk.jmh:jmh-generator-bytecode:${Versions.jmh}" + def jmhCore = mvn"org.openjdk.jmh:jmh-core:${Versions.jmh}" + def jniUtils = mvn"io.get-coursier.jniutils:windows-jni-utils:0.3.3" def jsoniterCore = - ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${Versions.jsoniterScalaJava8}" + mvn"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${Versions.jsoniterScalaJava8}" def jsoniterCoreJava8 = - ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${Versions.jsoniterScalaJava8}" + mvn"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${Versions.jsoniterScalaJava8}" def jsoniterMacros = - ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${Versions.jsoniterScalaJava8}" + mvn"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${Versions.jsoniterScalaJava8}" def jsoniterMacrosJava8 = - ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${Versions.jsoniterScalaJava8}" - def jsoup = ivy"org.jsoup:jsoup:${Versions.jsoup}" - def libsodiumjni = ivy"org.virtuslab.scala-cli:libsodiumjni:0.0.4" + mvn"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${Versions.jsoniterScalaJava8}" + def jsoup = mvn"org.jsoup:jsoup:${Versions.jsoup}" + def libsodiumjni = mvn"org.virtuslab.scala-cli:libsodiumjni:0.0.4" def metaconfigTypesafe = - ivy"org.scalameta::metaconfig-typesafe-config:0.16.0" + mvn"org.scalameta::metaconfig-typesafe-config:0.16.0" .exclude(("org.scala-lang", "scala-compiler")) - def munit = ivy"org.scalameta::munit:1.1.1" - def nativeTestRunner = ivy"org.scala-native::test-runner:${Versions.scalaNative}" - def nativeTools = ivy"org.scala-native::tools:${Versions.scalaNative}" - def osLib = ivy"com.lihaoyi::os-lib:0.11.3" - def pprint = ivy"com.lihaoyi::pprint:0.9.0" - def pythonInterface = ivy"io.github.alexarchambault.python:interface:0.1.0" - def pythonNativeLibs = ivy"ai.kien::python-native-libs:0.2.4" - 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:${Versions.scalafmt}" + def munit = mvn"org.scalameta::munit:1.1.1" + def nativeTestRunner = mvn"org.scala-native::test-runner:${Versions.scalaNative}" + def nativeTools = mvn"org.scala-native::tools:${Versions.scalaNative}" + def osLib = mvn"com.lihaoyi::os-lib:0.11.3" + def pprint = mvn"com.lihaoyi::pprint:0.9.0" + def pythonInterface = mvn"io.github.alexarchambault.python:interface:0.1.0" + def pythonNativeLibs = mvn"ai.kien::python-native-libs:0.2.4" + def scalaAsync = mvn"org.scala-lang.modules::scala-async:1.0.1".exclude("*" -> "*") + def scalac(sv: String) = mvn"org.scala-lang:scala-compiler:$sv" + def scalafmtCli = mvn"org.scalameta:scalafmt-cli_2.13:${Versions.scalafmt}" // 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" + mvn"org.scala-js:scalajs-env-jsdom-nodejs_2.13:1.1.0" // Force using of 2.13 - is there a better way? - def scalaJsEnvNodeJs = ivy"org.scala-js:scalajs-env-nodejs_2.13:1.4.0" - def scalaJsLogging = ivy"org.scala-js:scalajs-logging_2.13:1.1.1" + def scalaJsEnvNodeJs = mvn"org.scala-js:scalajs-env-nodejs_2.13:1.4.0" + def scalaJsLogging = mvn"org.scala-js:scalajs-logging_2.13:1.1.1" // Force using of 2.13 - is there a better way? - def scalaJsTestAdapter = ivy"org.scala-js:scalajs-sbt-test-adapter_2.13:${Scala.scalaJs}" - def scalaPackager = ivy"org.virtuslab:scala-packager_2.13:${Versions.scalaPackager}" - def scalaPackagerCli = ivy"org.virtuslab:scala-packager-cli_2.13:${Versions.scalaPackager}" - def scalaPy = ivy"dev.scalapy::scalapy-core::0.5.3" - def scalaReflect(sv: String) = ivy"org.scala-lang:scala-reflect:$sv" - def semanticDbJavac = ivy"com.sourcegraph:semanticdb-javac:${Versions.javaSemanticdb}" - def semanticDbScalac = ivy"org.scalameta:::semanticdb-scalac:${Versions.scalaMeta}" + def scalaJsTestAdapter = mvn"org.scala-js:scalajs-sbt-test-adapter_2.13:${Scala.scalaJs}" + def scalaPackager = mvn"org.virtuslab:scala-packager_2.13:${Versions.scalaPackager}" + def scalaPackagerCli = mvn"org.virtuslab:scala-packager-cli_2.13:${Versions.scalaPackager}" + def scalaPy = mvn"dev.scalapy::scalapy-core::0.5.3" + def scalaReflect(sv: String) = mvn"org.scala-lang:scala-reflect:$sv" + def semanticDbJavac = mvn"com.sourcegraph:semanticdb-javac:${Versions.javaSemanticdb}" + def semanticDbScalac = mvn"org.scalameta:::semanticdb-scalac:${Versions.scalaMeta}" def scalametaSemanticDbShared = - ivy"org.scalameta:semanticdb-shared_2.13:${Versions.scalaMeta}" + mvn"org.scalameta:semanticdb-shared_2.13:${Versions.scalaMeta}" .exclude("org.jline" -> "jline") // to prevent incompatibilities with GraalVM <23 .exclude("com.lihaoyi" -> "sourcecode_2.13") .exclude("org.scala-lang.modules" -> "scala-collection-compat_2.13") def signingCliShared = - ivy"org.virtuslab.scala-cli-signing::shared:${Versions.signingCli}" + mvn"org.virtuslab.scala-cli-signing::shared:${Versions.signingCli}" // to prevent collisions with scala-cli's case-app version .exclude(("com.github.alexarchambault", "case-app_3")) .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-core_3")) @@ -239,7 +240,7 @@ object Deps { .exclude(("com.lihaoyi", "os-lib_3")) .exclude(("com.lihaoyi", "os-lib_2.13")) def signingCli = - ivy"org.virtuslab.scala-cli-signing::cli:${Versions.signingCli}" + mvn"org.virtuslab.scala-cli-signing::cli:${Versions.signingCli}" // to prevent collisions with scala-cli's case-app version .exclude(("com.github.alexarchambault", "case-app_3")) .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-core_3")) @@ -249,25 +250,25 @@ object Deps { .exclude(("org.scala-lang.modules", "scala-collection-compat_2.13")) .exclude(("com.lihaoyi", "os-lib_3")) .exclude(("com.lihaoyi", "os-lib_2.13")) - def slf4jNop = ivy"org.slf4j:slf4j-nop:2.0.17" - def sttp = ivy"com.softwaremill.sttp.client3::core:3.11.0" - def svm = ivy"org.graalvm.nativeimage:svm:$graalVmVersion" - def swoval = ivy"com.swoval:file-tree-views:2.1.12" - def testInterface = ivy"org.scala-sbt:test-interface:1.0" + def slf4jNop = mvn"org.slf4j:slf4j-nop:2.0.17" + def sttp = mvn"com.softwaremill.sttp.client3::core:3.11.0" + def svm = mvn"org.graalvm.nativeimage:svm:$graalVmVersion" + def swoval = mvn"com.swoval:file-tree-views:2.1.12" + def testInterface = mvn"org.scala-sbt:test-interface:1.0" val toolkitVersion = "0.7.0" val toolkitVersionForNative04 = "0.3.0" val toolkitVersionForNative05 = toolkitVersion - def toolkit = ivy"org.scala-lang:toolkit:$toolkitVersion" - def toolkitTest = ivy"org.scala-lang:toolkit-test:$toolkitVersion" + def toolkit = mvn"org.scala-lang:toolkit:$toolkitVersion" + def toolkitTest = mvn"org.scala-lang:toolkit-test:$toolkitVersion" val typelevelToolkitVersion = "0.1.29" - def typelevelToolkit = ivy"org.typelevel:toolkit:$typelevelToolkitVersion" - def usingDirectives = ivy"org.virtuslab:using_directives:1.1.4" + def typelevelToolkit = mvn"org.typelevel:toolkit:$typelevelToolkitVersion" + def usingDirectives = mvn"org.virtuslab:using_directives:1.1.4" // Lives at https://github.com/VirtusLab/no-crc32-zip-input-stream, see #865 // This provides a ZipInputStream that doesn't verify CRC32 checksums, that users // can enable by setting SCALA_CLI_VENDORED_ZIS=true in the environment, to workaround // some bad GraalVM / zlib issues (see #828 and linked issues for more details). - def zipInputStream = ivy"org.virtuslab.scala-cli.zip-input-stream:zip-input-stream:0.1.3" - def scalafixInterfaces = ivy"ch.epfl.scala:scalafix-interfaces:${Versions.scalafix}" + def zipInputStream = mvn"org.virtuslab.scala-cli.zip-input-stream:zip-input-stream:0.1.3" + def scalafixInterfaces = mvn"ch.epfl.scala:scalafix-interfaces:${Versions.scalafix}" } def graalVmVersion = "22.3.1" diff --git a/project/publish/package.mill.scala b/project/publish/package.mill.scala index 04d0e5abd2..fdc18f24cb 100644 --- a/project/publish/package.mill.scala +++ b/project/publish/package.mill.scala @@ -1,38 +1,35 @@ package build.project.publish -import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0` -import $ivy.`org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r` import build.project.settings import com.lumidion.sonatype.central.client.core.{PublishingType, SonatypeCredentials} import settings.{PublishLocalNoFluff, workspaceDirName} -import de.tobiasroeser.mill.vcs.version._ -import mill._ +import mill.* import mill.javalib.publish.Artifact -import scalalib._ +import mill.util.{Tasks, VcsVersion} +import scalalib.* import org.eclipse.jgit.api.Git +import mill.api.{BuildCtx, ModuleCtx, Task} import java.nio.charset.Charset -import scala.concurrent.duration._ -import scala.jdk.CollectionConverters._ +import scala.concurrent.duration.* +import scala.jdk.CollectionConverters.* -lazy val (ghOrg: String, ghName: String) = { - def default = ("VirtusLab", "scala-cli") - val isCI = System.getenv("CI") != null - if (isCI) { +def gh: (ghOrg: String, ghName: String) = { + lazy val default = ("VirtusLab", "scala-cli") + val isCI = System.getenv("CI") != null + if isCI then { val repos = { var git: Git = null try { git = Git.open(os.Path(sys.env("MILL_WORKSPACE_ROOT")).toIO) git.remoteList().call().asScala.toVector } - finally - if (git != null) - git.close() + finally if git != null then git.close() } val reposByName = repos.flatMap(r => r.getURIs.asScala.headOption.map(_.toASCIIString).toSeq.map((r.getName, _)) ) val repoUrlOpt = - if (reposByName.length == 1) + if reposByName.length == 1 then reposByName.headOption.map(_._2) else { val m = reposByName.toMap @@ -58,21 +55,22 @@ lazy val (ghOrg: String, ghName: String) = { default } } - else - default + else default } -private def computePublishVersion(state: VcsState, simple: Boolean): String = - if (state.commitsSinceLastTag > 0) - if (simple) { +lazy val (ghOrg: String, ghName: String) = gh + +private def computePublishVersion(state: VcsVersion.State, simple: Boolean): String = + if state.commitsSinceLastTag > 0 then + if simple then { val versionOrEmpty = state.lastTag .filter(_ != "latest") .filter(_ != "nightly") .map(_.stripPrefix("v")) .flatMap { tag => - if (simple) { + if simple then { val idx = tag.lastIndexOf(".") - if (idx >= 0) + if idx >= 0 then Some( tag.take(idx + 1) + (tag.drop(idx + 1).replaceAll("-RC.", "").toInt + 1).toString + @@ -83,7 +81,7 @@ private def computePublishVersion(state: VcsState, simple: Boolean): String = } else { val idx = tag.indexOf("-") - if (idx >= 0) Some(tag.take(idx) + "+" + tag.drop(idx + 1) + "-SNAPSHOT") + if idx >= 0 then Some(tag.take(idx) + "+" + tag.drop(idx + 1) + "-SNAPSHOT") else None } } @@ -98,7 +96,7 @@ private def computePublishVersion(state: VcsState, simple: Boolean): String = .replace("latest", "0.0.0") .replace("nightly", "0.0.0") val idx = rawVersion.indexOf("-") - if (idx >= 0) rawVersion.take(idx) + "-" + rawVersion.drop(idx + 1) + "-SNAPSHOT" + if idx >= 0 then rawVersion.take(idx) + "-" + rawVersion.drop(idx + 1) + "-SNAPSHOT" else rawVersion } else @@ -107,9 +105,9 @@ private def computePublishVersion(state: VcsState, simple: Boolean): String = .getOrElse(state.format()) .stripPrefix("v") -def finalPublishVersion: Target[String] = { +def finalPublishVersion: T[String] = { val isCI = System.getenv("CI") != null - if (isCI) + if isCI then Task(persistent = true) { val state = VcsVersion.vcsState() computePublishVersion(state, simple = false) @@ -124,8 +122,8 @@ def finalPublishVersion: Target[String] = { def organization = "org.virtuslab.scala-cli" trait ScalaCliPublishModule extends SonatypeCentralPublishModule with PublishLocalNoFluff { - import mill.scalalib.publish._ - def pomSettings: Target[PomSettings] = PomSettings( + import mill.scalalib.publish.* + def pomSettings: T[PomSettings] = PomSettings( description = artifactName(), organization = organization, url = s"https://github.com/$ghOrg/$ghName", @@ -139,17 +137,20 @@ trait ScalaCliPublishModule extends SonatypeCentralPublishModule with PublishLoc Developer("MaciejG604", "Maciej Gajek", "https://github.com/MaciejG604") ) ) - def publishVersion: Target[String] = finalPublishVersion() - override def sourceJar: Target[PathRef] = Task { - import mill.util.Jvm.createJar - val allSources0 = allSources().map(_.path).filter(os.exists).toSet - createJar( - allSources0 ++ resources().map(_.path).filter(os.exists), - manifest(), - (input, relPath) => - !allSources0(input) || - (!relPath.segments.contains(".scala") && !relPath.segments.contains(workspaceDirName)) - ) + def publishVersion: T[String] = finalPublishVersion() + override def sourceJar: T[PathRef] = Task { + PathRef { + import mill.util.Jvm.createJar + val allSources0 = allSources().map(_.path).filter(os.exists) + createJar( + jar = Task.dest / "out.jar", + inputPaths = allSources0 ++ resources().map(_.path).filter(os.exists), + manifest = manifest(), + fileFilter = (input, relPath) => + !allSources0.toSet(input) || + (!relPath.segments.contains(".scala") && !relPath.segments.contains(workspaceDirName)) + ) + } } } @@ -204,14 +205,14 @@ def publishSonatype( env = env, awaitTimeout = timeout.toMillis.toInt ) - val publishingType = if (isRelease) PublishingType.AUTOMATIC else PublishingType.USER_MANAGED + val publishingType = if isRelease then PublishingType.AUTOMATIC else PublishingType.USER_MANAGED System.err.println(s"Publishing type: $publishingType") - val finalBundleName = if (bundleName.nonEmpty) Some(bundleName) else None + val finalBundleName = if bundleName.nonEmpty then Some(bundleName) else None System.err.println(s"Final bundle name: $finalBundleName") publisher.publishAll( publishingType = publishingType, singleBundleName = finalBundleName, - artifacts = artifacts: _* + artifacts = artifacts* ) } @@ -230,12 +231,12 @@ def shouldPublish = Task(persistent = true) { } def setShouldPublish() = Task.Command { val envFile = System.getenv("GITHUB_ENV") - if (envFile == null) + if envFile == null then sys.error("GITHUB_ENV not set") val charSet = Charset.defaultCharset() val nl = System.lineSeparator() os.write.append( - os.Path(envFile, Task.workspace), + os.Path(envFile, BuildCtx.workspaceRoot), s"SHOULD_PUBLISH=${shouldPublish()}$nl".getBytes(charSet) ) } diff --git a/project/settings/package.mill.scala b/project/settings/package.mill.scala index c7c56bb376..701d4f385a 100644 --- a/project/settings/package.mill.scala +++ b/project/settings/package.mill.scala @@ -1,6 +1,4 @@ package build.project.settings -import $ivy.`com.goyeau::mill-scalafix::0.5.1` -import $ivy.`io.github.alexarchambault.mill::mill-native-image::0.1.31-1` import build.project.deps import deps.{ Deps, @@ -15,24 +13,23 @@ import build.project.utils import utils.isArmArchitecture import com.goyeau.mill.scalafix.ScalafixModule import coursier.Repository -import de.tobiasroeser.mill.vcs.version.{VcsState, VcsVersion} import io.github.alexarchambault.millnativeimage.NativeImage -import mill._ -import mill.api.Loose -import mill.scalalib._ -import os.{CommandResult, Path} -import upickle.default._ +import mill.* +import mill.scalalib.* +import upickle.default.* import java.io.File import java.nio.charset.StandardCharsets import java.util.Locale import scala.annotation.unused import scala.util.Properties +import mill.util.{Tasks, VcsVersion} +import mill.api.{BuildCtx, Task} private def isCI = System.getenv("CI") != null def fromPath(name: String): String = - if (Properties.isWin) { + if Properties.isWin then { val pathExt = Option(System.getenv("PATHEXT")) .toSeq .flatMap(_.split(File.pathSeparator).toSeq) @@ -61,34 +58,34 @@ def fromPath(name: String): String = else name -def cs: Target[String] = Task(persistent = true) { +def cs: T[String] = Task(persistent = true) { val arch = sys.props.getOrElse("os.arch", "").toLowerCase(Locale.ROOT) - val ext = if (Properties.isWin) ".exe" else "" - val csVersion = if (arch == "aarch64" && Properties.isMac) buildCsM1Version else buildCsVersion + val ext = if Properties.isWin then ".exe" else "" + val csVersion = if arch == "aarch64" && Properties.isMac then buildCsM1Version else buildCsVersion val dest = Task.dest / s"cs-$csVersion$ext" def downloadOpt(): Option[String] = { val urlOpt = arch match { case "x86_64" | "amd64" => - if (Properties.isWin) + if Properties.isWin then Some( s"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-x86_64-pc-win32.zip" ) - else if (Properties.isMac) + else if Properties.isMac then Some( s"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-x86_64-apple-darwin.gz" ) - else if (Properties.isLinux) + else if Properties.isLinux then Some( s"https://github.com/coursier/coursier/releases/download/v$csVersion/cs-x86_64-pc-linux.gz" ) else None case "aarch64" => - if (Properties.isLinux) + if Properties.isLinux then Some( s"https://github.com/VirtusLab/coursier-m1/releases/download/v$csVersion/cs-aarch64-pc-linux.gz" ) - else if (Properties.isMac) + else if Properties.isMac then Some( s"https://github.com/VirtusLab/coursier-m1/releases/download/v$csVersion/cs-aarch64-apple-darwin.gz" ) @@ -102,32 +99,30 @@ def cs: Target[String] = Task(persistent = true) { 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) + try task.unsafeRun()(using cache.ec) catch { case t: Throwable => throw new Exception(s"Error getting and extracting $url", t) } - val f = maybeFile.fold(ex => throw new Exception(ex), os.Path(_, Task.workspace)) + val f = maybeFile.fold(ex => throw new Exception(ex), os.Path(_, BuildCtx.workspaceRoot)) val exec = - 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 - f + if Properties.isWin && os.isDir(f) && f.last.endsWith(".zip") + then + os.list(f).find(_.last.endsWith(".exe")).getOrElse(sys.error(s"No .exe found under $f")) + else f - if (!Properties.isWin) - exec.toIO.setExecutable(true) + if !Properties.isWin then exec.toIO.setExecutable(true) exec.toString } } - if (os.isFile(dest)) dest.toString + if os.isFile(dest) + then dest.toString else downloadOpt().getOrElse(fromPath("cs")): String } -def platformExecutableJarExtension: String = if (Properties.isWin) ".bat" else "" +def platformExecutableJarExtension: String = if Properties.isWin then ".bat" else "" lazy val arch = sys.props("os.arch").toLowerCase(java.util.Locale.ROOT) match { case "amd64" => "x86_64" @@ -135,15 +130,22 @@ lazy val arch = sys.props("os.arch").toLowerCase(java.util.Locale.ROOT) match { } def platformSuffix: String = { val os = - if (Properties.isWin) "pc-win32" - else if (Properties.isLinux) "pc-linux" - else if (Properties.isMac) "apple-darwin" + if Properties.isWin then "pc-win32" + else if Properties.isLinux then "pc-linux" + else if Properties.isMac then "apple-darwin" else sys.error(s"Unrecognized OS: ${sys.props("os.name")}") s"$arch-$os" } def localRepoResourcePath = "local-repo.zip" +trait LocatedInModules extends Module { + override def moduleDir: os.Path = { + val oldModuleDir: os.Path = super.moduleDir + oldModuleDir / os.up / "modules" / oldModuleDir.last + } +} + trait CliLaunchers extends SbtModule { self => def launcherTypeResourcePath: os.RelPath = @@ -156,13 +158,14 @@ trait CliLaunchers extends SbtModule { self => Task.Command(super.writeNativeImageScript(scriptDest, "")()) def launcherKind: String - def nativeImageCsCommand: Target[Seq[String]] = Seq(cs()) - def nativeImagePersist: Boolean = System.getenv("CI") != null - def nativeImageGraalVmJvmId: Target[String] = deps.graalVmJvmId - def nativeImageOptions: Target[Seq[String]] = Task { + def nativeImageCsCommand: T[Seq[String]] = Seq(cs()) + def nativeImagePersist: Boolean = System.getenv("CI") != null + def nativeImageGraalVmJvmId: T[String] = deps.graalVmJvmId + def nativeImageOptions: T[Seq[String]] = Task { val usesDocker = nativeImageDockerParams().nonEmpty val cLibPath = - if (usesDocker) s"/data/$staticLibDirName" + if usesDocker + then s"/data/$staticLibDirName" else staticLibDir().path.toString super.nativeImageOptions() ++ Seq( s"-H:IncludeResources=$localRepoResourcePath", @@ -170,12 +173,12 @@ trait CliLaunchers extends SbtModule { self => s"-H:IncludeResources=$defaultFilesResourcePath/.*", "-H:-ParseRuntimeOptions", s"-H:CLibraryPath=$cLibPath" - ) ++ (if (Properties.isLinux && isArmArchitecture) // https://stackoverflow.com/a/61325264 - Seq("-Djdk.lang.Process.launchMechanism=vfork", "-H:PageSize=65536") + ) ++ (if Properties.isLinux && isArmArchitecture // https://stackoverflow.com/a/61325264 + then Seq("-Djdk.lang.Process.launchMechanism=vfork", "-H:PageSize=65536") else Nil) } - def nativeImageName: Target[String] = "scala-cli" - def nativeImageClassPath: Target[Seq[PathRef]] = Task { + def nativeImageName: T[String] = "scala-cli" + def nativeImageClassPath: T[Seq[PathRef]] = Task { val launcherKindResourceDir = Task.dest / "resources" os.write( launcherKindResourceDir / launcherTypeResourcePath, @@ -184,7 +187,7 @@ trait CliLaunchers extends SbtModule { self => ) PathRef(launcherKindResourceDir) +: self.nativeImageClassPath() } - def nativeImageMainClass: Target[String] = self.nativeImageMainClass() + def nativeImageMainClass: T[String] = self.nativeImageMainClass() private def staticLibDirName = "native-libs" @@ -205,13 +208,13 @@ trait CliLaunchers extends SbtModule { self => val libsodiumjniVersion = Deps.libsodiumjni.dep.versionConstraint.asString val (classifier, ext) = sys.props.get("os.arch") match { case Some("x86_64" | "amd64") => - if (Properties.isWin) ("x86_64-pc-win32", "lib") - else if (Properties.isLinux) ("x86_64-pc-linux", "a") - else if (Properties.isMac && isArmArchitecture) ("aarch64-apple-darwin", "a") - else if (Properties.isMac) ("x86_64-apple-darwin", "a") + if Properties.isWin then ("x86_64-pc-win32", "lib") + else if Properties.isLinux then ("x86_64-pc-linux", "a") + else if Properties.isMac && isArmArchitecture then ("aarch64-apple-darwin", "a") + else if Properties.isMac then ("x86_64-apple-darwin", "a") else sys.error(s"Unsupported OS for x86_64 platform: ${sys.props("os.name")}") case Some("aarch64") => - if (Properties.isLinux) ("aarch64-pc-linux", "a") + if Properties.isLinux then ("aarch64-pc-linux", "a") else sys.error(s"Unsupported OS for aarch64 platform: ${sys.props("os.name")}") case Some(arch) => sys.error(s"Unsupported architecture: $arch") @@ -227,9 +230,7 @@ trait CliLaunchers extends SbtModule { self => ext ).call() val libPath = os.Path(libRes.out.trim(), workspace) - val prefix = - if (Properties.isWin) "" - else "lib" + val prefix = if Properties.isWin then "" else "lib" os.copy.over(libPath, destDir / s"${prefix}sodiumjni.$ext") } private def copyLibsodiumStaticTo(cs: String, destDir: os.Path, workspace: os.Path): Unit = { @@ -274,22 +275,25 @@ trait CliLaunchers extends SbtModule { self => System.err.println(s"Calling ${proc.command.flatMap(_.value).mkString(" ")}") proc.call(stdin = os.Inherit, stdout = os.Inherit) } - def staticLibDir: Target[PathRef] = Task { - val dir = nativeImageDockerWorkingDir() / staticLibDirName - os.makeDir.all(dir) - - if (Properties.isWin) { - copyLibsodiumStaticTo(cs(), dir, Task.workspace) - copyLibsodiumjniTo(cs(), dir, Task.workspace) - copyCsjniutilTo(cs(), dir, Task.workspace) - } + def staticLibDir: T[PathRef] = Task { + // TODO: refactor so that the file system checker doesn't have to be disabled + BuildCtx.withFilesystemCheckerDisabled { + val dir: os.Path = nativeImageDockerWorkingDir() / staticLibDirName + os.makeDir.all(dir) + + if Properties.isWin then { + copyLibsodiumStaticTo(cs(), dir, BuildCtx.workspaceRoot) + copyLibsodiumjniTo(cs(), dir, BuildCtx.workspaceRoot) + copyCsjniutilTo(cs(), dir, BuildCtx.workspaceRoot) + } - if (launcherKind == "static") { - copyAlpineLibsodiumTo(cs(), dir, Task.workspace) - copyLibsodiumjniTo(cs(), dir, Task.workspace) - } + if launcherKind == "static" then { + copyAlpineLibsodiumTo(cs(), dir, BuildCtx.workspaceRoot) + copyLibsodiumjniTo(cs(), dir, BuildCtx.workspaceRoot) + } - PathRef(dir) + PathRef(dir) + } } } @@ -304,8 +308,8 @@ trait CliLaunchers extends SbtModule { self => } object `linux-docker-image` extends CliNativeImage { - def launcherKind: String = `base-image`.launcherKind - def nativeImageDockerParams: Target[Option[NativeImage.DockerParams]] = Some( + def launcherKind: String = `base-image`.launcherKind + def nativeImageDockerParams: T[Option[NativeImage.DockerParams]] = Some( NativeImage.DockerParams( imageName = s"ubuntu:$ubuntuVersion", prepareCommand = @@ -338,13 +342,13 @@ trait CliLaunchers extends SbtModule { self => ) object `static-image` extends CliNativeImage { - def launcherKind = "static" - def nativeImageOptions: Target[Seq[String]] = Task { + def launcherKind = "static" + def nativeImageOptions: T[Seq[String]] = Task { super.nativeImageOptions() ++ Seq( "-J-Dscala-cli.static-launcher=true" ) } - def nativeImageDockerParams: Target[Option[NativeImage.DockerParams]] = Task { + def nativeImageDockerParams: T[Option[NativeImage.DockerParams]] = Task { val baseDockerParams = NativeImage.linuxStaticParams( Docker.muslBuilder, s"https://github.com/coursier/coursier/releases/download/v${deps.csDockerVersion}/cs-x86_64-pc-linux.gz" @@ -353,9 +357,9 @@ trait CliLaunchers extends SbtModule { self => buildHelperImage() Some(dockerParams) } - def buildHelperImage: Target[Unit] = Task { + def buildHelperImage: T[Unit] = Task { os.proc("docker", "build", "-t", Docker.customMuslBuilderImageName, ".") - .call(cwd = Task.workspace / "project" / "musl-image", stdout = os.Inherit) + .call(cwd = BuildCtx.workspaceRoot / "project" / "musl-image", stdout = os.Inherit) () } override def writeDefaultNativeImageScript(scriptDest: String): Command[Unit] = @@ -366,8 +370,8 @@ trait CliLaunchers extends SbtModule { self => } object `mostly-static-image` extends CliNativeImage { - def launcherKind = "mostly-static" - def nativeImageDockerParams: Target[Option[NativeImage.DockerParams]] = Task { + def launcherKind = "mostly-static" + def nativeImageDockerParams: T[Option[NativeImage.DockerParams]] = Task { val baseDockerParams = NativeImage.linuxMostlyStaticParams( s"ubuntu:$ubuntuVersion", s"https://github.com/coursier/coursier/releases/download/v${deps.csDockerVersion}/cs-x86_64-pc-linux.gz" @@ -379,11 +383,11 @@ trait CliLaunchers extends SbtModule { self => def localRepoJar: T[PathRef] - def nativeImageMainClass: Target[String] = Task { + def nativeImageMainClass: T[String] = Task { mainClass().getOrElse(sys.error("Don't know what main class to use")) } - def transitiveJarsAgg: T[Agg[PathRef]] = { + def transitiveJarsSeq: T[Seq[PathRef]] = { def allModuleDeps(todo: List[JavaModule]): List[JavaModule] = todo match { case Nil => Nil @@ -392,33 +396,37 @@ trait CliLaunchers extends SbtModule { self => } Task { - mill.define.Target.traverse(allModuleDeps(this :: Nil).distinct)(m => + Task.traverse(allModuleDeps(this :: Nil).distinct)(m => Task.Anon(m.jar()) )() } } - def nativeImageClassPath: Target[Seq[PathRef]] = Task { + def nativeImageClassPath: T[Seq[PathRef]] = Task { val localRepoJar0 = localRepoJar() runClasspath() :+ localRepoJar0 // isn't localRepoJar already there? } - def nativeImage: Target[PathRef] = - if (Properties.isLinux && arch == "x86_64" && isCI) - `linux-docker-image`.nativeImage - else - `base-image`.nativeImage + def nativeImage: T[PathRef] = + // TODO: refactor so that the file system checker doesn't have to be disabled + BuildCtx.withFilesystemCheckerDisabled { + if Properties.isLinux && arch == "x86_64" && isCI + then `linux-docker-image`.nativeImage + else `base-image`.nativeImage + } - def nativeImageStatic: Target[PathRef] = - `static-image`.nativeImage - def nativeImageMostlyStatic: Target[PathRef] = - `mostly-static-image`.nativeImage + def nativeImageStatic: T[PathRef] = + // TODO: refactor so that the file system checker doesn't have to be disabled + BuildCtx.withFilesystemCheckerDisabled(`static-image`.nativeImage) + def nativeImageMostlyStatic: T[PathRef] = + // TODO: refactor so that the file system checker doesn't have to be disabled + BuildCtx.withFilesystemCheckerDisabled(`mostly-static-image`.nativeImage) def runWithAssistedConfig(args: String*): Command[Unit] = Task.Command { val cp = jarClassPath().map(_.path).mkString(File.pathSeparator) val mainClass0 = mainClass().getOrElse(sys.error("No main class")) val graalVmHome = Option(System.getenv("GRAALVM_HOME")).getOrElse { - import sys.process._ + import sys.process.* Seq(cs(), "java-home", "--jvm", deps.graalVmJvmId).!!.trim } val outputDir = Task.ctx().dest / "config" @@ -429,41 +437,43 @@ trait CliLaunchers extends SbtModule { self => cp, mainClass0 ) ++ args - os.proc(command.map(x => x: os.Shellable): _*).call( + os.proc(command.map(x => x: os.Shellable)*).call( stdin = os.Inherit, stdout = os.Inherit ) - Task.log.streams.out.println(s"Config generated in ${outputDir.relativeTo(Task.workspace)}") + Task.log.streams.out.println( + s"Config generated in ${outputDir.relativeTo(BuildCtx.workspaceRoot)}" + ) } @unused - def runFromJars(args: String*): Command[CommandResult] = Task.Command { + def runFromJars(args: String*): Task.Command[os.CommandResult] = Task.Command { val cp = jarClassPath().map(_.path).mkString(File.pathSeparator) val mainClass0 = mainClass().getOrElse(sys.error("No main class")) val command = Seq("java", "-cp", cp, mainClass0) ++ args - os.proc(command.map(x => x: os.Shellable): _*).call( + os.proc(command.map(x => x: os.Shellable)*).call( stdin = os.Inherit, stdout = os.Inherit ) } - def runClasspath: Target[Seq[PathRef]] = Task { + def runClasspath: T[Seq[PathRef]] = Task { super.runClasspath() ++ Seq(localRepoJar()) } - def jarClassPath: Target[Seq[PathRef]] = Task { - val cp = runClasspath() ++ transitiveJarsAgg() + def jarClassPath: T[Seq[PathRef]] = Task { + val cp = runClasspath() ++ transitiveJarsSeq() cp.filter(ref => os.exists(ref.path) && !os.isDir(ref.path)) } - def launcher: Target[PathRef] = Task { + def launcher: T[PathRef] = Task { import coursier.launcher.{BootstrapGenerator, ClassPathEntry, Parameters, Preamble} import scala.util.Properties.isWin val cp = jarClassPath().map(_.path) val mainClass0 = mainClass().getOrElse(sys.error("No main class")) - val dest = Task.ctx().dest / (if (isWin) "launcher.bat" else "launcher") + val dest = Task.ctx().dest / (if isWin then "launcher.bat" else "launcher") val preamble = Preamble() .withOsKind(isWin) @@ -479,10 +489,10 @@ trait CliLaunchers extends SbtModule { self => PathRef(dest) } - def standaloneLauncher: Target[PathRef] = Task { - val cachePath = os.Path(coursier.cache.FileCache().location, Task.workspace) + def standaloneLauncher: T[PathRef] = Task { + val cachePath = os.Path(coursier.cache.FileCache().location, BuildCtx.workspaceRoot) def urlOf(path: os.Path): Option[String] = - if (path.startsWith(cachePath)) { + if path.startsWith(cachePath) then { val segments = path.relativeTo(cachePath).segments val url = segments.head + "://" + segments.tail.mkString("/") Some(url) @@ -495,7 +505,7 @@ trait CliLaunchers extends SbtModule { self => val cp = jarClassPath().map(_.path) val mainClass0 = mainClass().getOrElse(sys.error("No main class")) - val dest = Task.ctx().dest / (if (isWin) "launcher.bat" else "launcher") + val dest = Task.ctx().dest / (if isWin then "launcher.bat" else "launcher") val preamble = Preamble() .withOsKind(isWin) @@ -523,20 +533,20 @@ trait CliLaunchers extends SbtModule { self => } trait HasTests extends SbtModule { - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { val sv = scalaVersion() val isScala213 = sv.startsWith("2.13.") val extraOptions = - if (isScala213) Seq("-Xsource:3") + if isScala213 then Seq("-Xsource:3") else Nil super.scalacOptions() ++ extraOptions } trait ScalaCliTests extends ScalaCliModule with super.SbtTests with TestModule.Munit { - def ivyDeps: Target[Agg[Dep]] = super.ivyDeps() ++ Agg( + def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ Seq( Deps.expecty, Deps.munit ) - def forkArgs: Target[Seq[String]] = super.forkArgs() ++ Seq("-Xmx512m", "-Xms128m") + def forkArgs: T[Seq[String]] = super.forkArgs() ++ Seq("-Xmx512m", "-Xms128m") def repositoriesTask: Task[Seq[Repository]] = Task.Anon(super.repositoriesTask() ++ deps.customRepositories) @@ -546,9 +556,9 @@ trait HasTests extends SbtModule { } trait PublishLocalNoFluff extends SonatypeCentralPublishModule { - def emptyZip: Target[PathRef] = Task { - import java.io._ - import java.util.zip._ + def emptyZip: T[PathRef] = Task { + import java.io.* + import java.util.zip.* val dest = Task.dest / "empty.zip" val baos = new ByteArrayOutputStream val zos = new ZipOutputStream(baos) @@ -559,23 +569,23 @@ trait PublishLocalNoFluff extends SonatypeCentralPublishModule { } // adapted from https://github.com/com-lihaoyi/mill/blob/fea79f0515dda1def83500f0f49993e93338c3de/scalalib/src/PublishModule.scala#L70-L85 // writes empty zips as source and doc JARs - def publishLocalNoFluff(localIvyRepo: String = null): define.Command[PathRef] = Task.Command { + def publishLocalNoFluff(localIvyRepo: String = null): Command[PathRef] = Task.Command { import mill.scalalib.publish.LocalIvyPublisher val publisher = localIvyRepo match { case null => LocalIvyPublisher case repo => - new LocalIvyPublisher(os.Path(repo.replace("{VERSION}", publishVersion()), Task.workspace)) + new LocalIvyPublisher(os.Path( + repo.replace("{VERSION}", publishVersion()), + BuildCtx.workspaceRoot + )) } publisher.publishLocal( - jar = jar().path, - sourcesJar = emptyZip().path, - docJar = emptyZip().path, pom = pom().path, - ivy = ivy().path, + ivy = Right(ivy().path), artifact = artifactMetadata(), - extras = extraPublish() + publishInfos = Seq.empty ) jar() @@ -586,14 +596,14 @@ trait LocalRepo extends Module { def stubsModules: Seq[PublishLocalNoFluff] def version: T[String] - def localRepo: Target[Seq[PathRef]] = Task { - val repoRoot = os.rel / "out" / "repo" / "{VERSION}" - val tasks = stubsModules.map(_.publishLocalNoFluff(repoRoot.toString)) - define.Target.sequence(tasks) + def localRepo: T[Seq[PathRef]] = { + val repoRoot = os.rel / "out" / "repo" / "{VERSION}" + val tasks: Seq[Command[PathRef]] = stubsModules.map(_.publishLocalNoFluff(repoRoot.toString)) + Task.sequence(tasks)() } - private def vcsState: Target[VcsState] = - if (isCI) + private def vcsState: T[VcsVersion.State] = + if isCI then Task(persistent = true) { VcsVersion.vcsState() } @@ -601,57 +611,59 @@ trait LocalRepo extends Module { Task { VcsVersion.vcsState() } - def localRepoZip: Target[PathRef] = Task { + def localRepoZip: T[PathRef] = Task { val repoVer = vcsState().format() val ver = version() localRepo() - val repoDir = Task.workspace / "out" / "repo" / ver + val repoDir = BuildCtx.workspaceRoot / "out" / "repo" / ver val destDir = Task.dest / ver / "repo.zip" val dest = destDir / "repo.zip" - - import java.io._ - import java.util.zip._ - os.makeDir.all(destDir) - var fos: FileOutputStream = null - var zos: ZipOutputStream = null - try { - fos = new FileOutputStream(dest.toIO) - zos = new ZipOutputStream(new BufferedOutputStream(fos)) - - val versionEntry = new ZipEntry("version") - versionEntry.setTime(0L) - zos.putNextEntry(versionEntry) - zos.write(repoVer.getBytes(StandardCharsets.UTF_8)) - zos.flush() - - os.walk(repoDir).filter(_ != repoDir).foreach { p => - val isDir = os.isDir(p) - val name = p.relativeTo(repoDir).toString + (if (isDir) "/" else "") - val entry = new ZipEntry(name) - entry.setTime(os.mtime(p)) - zos.putNextEntry(entry) - if (!isDir) { - zos.write(os.read.bytes(p)) - zos.flush() + // TODO: refactor so that the file system checker doesn't have to be disabled + BuildCtx.withFilesystemCheckerDisabled { + import java.io.* + import java.util.zip.* + os.makeDir.all(destDir) + var fos: FileOutputStream = null + var zos: ZipOutputStream = null + try { + fos = new FileOutputStream(dest.toIO) + zos = new ZipOutputStream(new BufferedOutputStream(fos)) + + val versionEntry = new ZipEntry("version") + versionEntry.setTime(0L) + zos.putNextEntry(versionEntry) + zos.write(repoVer.getBytes(StandardCharsets.UTF_8)) + zos.flush() + + os.walk(repoDir).filter(_ != repoDir).foreach { p => + val isDir = os.isDir(p) + val name = p.relativeTo(repoDir).toString + (if isDir then "/" else "") + val entry = new ZipEntry(name) + entry.setTime(os.mtime(p)) + zos.putNextEntry(entry) + if !isDir then { + zos.write(os.read.bytes(p)) + zos.flush() + } + zos.closeEntry() } - zos.closeEntry() + zos.finish() + } + finally { + if zos != null then zos.close() + if fos != null then fos.close() } - zos.finish() - } - finally { - if (zos != null) zos.close() - if (fos != null) fos.close() - } - PathRef(dest) + PathRef(dest) + } } - def localRepoJar: Target[PathRef] = Task { + def localRepoJar: T[PathRef] = Task { val zip = localRepoZip().path val dest = Task.dest / "repo.jar" - import java.io._ - import java.util.zip._ + import java.io.* + import java.util.zip.* var fos: FileOutputStream = null var zos: ZipOutputStream = null try { @@ -668,8 +680,8 @@ trait LocalRepo extends Module { zos.finish() } finally { - if (zos != null) zos.close() - if (fos != null) fos.close() + if zos != null then zos.close() + if fos != null then fos.close() } PathRef(dest) @@ -688,27 +700,27 @@ private def doFormatNativeImageConf(dir: os.Path, format: Boolean): List[os.Path var needsFormatting = List.empty[os.Path] for (name <- files) { val file = dir / name - if (os.isFile(file)) { + if os.isFile(file) then { val content = os.read(file) val json = ujson.read(content) val updatedJson = - if (name == "reflect-config.json") + if name == "reflect-config.json" then json.arrOpt.fold(json) { arr => val values = arr.toVector.groupBy(_("name").str).toVector.sortBy(_._1).map(_._2).map { t => val entries = t.map(_.obj).reduce(_ addAll _) - if (entries.get("allDeclaredFields").contains(ujson.Bool(true))) + if entries.get("allDeclaredFields").contains(ujson.Bool(true)) then entries -= "fields" - if (entries.get("allDeclaredMethods").contains(ujson.Bool(true))) + if entries.get("allDeclaredMethods").contains(ujson.Bool(true)) then entries -= "methods" ujson.Obj(entries) } - ujson.Arr(values: _*) + ujson.Arr(values*) } - else if (sortByName(name)) + else if sortByName(name) then json.arrOpt.fold(json) { arr => val values = arr.toVector.sortBy(_("name").str) - ujson.Arr(values: _*) + ujson.Arr(values*) } else json @@ -717,9 +729,9 @@ private def doFormatNativeImageConf(dir: os.Path, format: Boolean): List[os.Path .filter(_.trim.nonEmpty) .map(_ + "\n") .mkString - if (updatedContent != content) { + if updatedContent != content then { needsFormatting = file :: needsFormatting - if (format) + if format then os.write.over(file, updatedContent) } } @@ -728,7 +740,7 @@ private def doFormatNativeImageConf(dir: os.Path, format: Boolean): List[os.Path } trait FormatNativeImageConf extends JavaModule { - def nativeImageConfDirs: Target[Seq[Path]] = Task { + def nativeImageConfDirs: T[Seq[os.Path]] = Task { resources() .map(_.path / "META-INF" / "native-image") .filter(os.exists(_)) @@ -744,13 +756,14 @@ trait FormatNativeImageConf extends JavaModule { var needsFormatting = List.empty[os.Path] for (dir <- nativeImageConfDirs()) needsFormatting = doFormatNativeImageConf(dir, format = false) ::: needsFormatting - if (needsFormatting.nonEmpty) { + if needsFormatting.nonEmpty then { val msg = s"Error: ${needsFormatting.length} file(s) needs formatting" System.err.println(msg) for (f <- needsFormatting) System.err.println( s" ${ - if (f.startsWith(Task.workspace)) f.relativeTo(Task.workspace).toString + if f.startsWith(BuildCtx.workspaceRoot) + then f.relativeTo(BuildCtx.workspaceRoot).toString else f.toString }" ) @@ -774,42 +787,45 @@ trait FormatNativeImageConf extends JavaModule { trait ScalaCliScalafixModule extends ScalafixModule { - override def semanticDbVersion: Target[String] = Deps.Versions.scalaMeta + override def semanticDbVersion: T[String] = Deps.Versions.scalaMeta - def scalafixConfig: Target[Option[Path]] = Task { - if (scalaVersion().startsWith("2.")) super.scalafixConfig() - else Some(Task.workspace / ".scalafix3.conf") + def scalafixConfig: T[Option[os.Path]] = Task { + if scalaVersion().startsWith("2.") + then super.scalafixConfig() + else Some(BuildCtx.workspaceRoot / ".scalafix3.conf") } - def scalacPluginIvyDeps: Target[Loose.Agg[Dep]] = super.scalacPluginIvyDeps() ++ { - if (scalaVersion().startsWith("2.")) Seq(Deps.semanticDbScalac) + def scalacPluginMvnDeps: T[Seq[Dep]] = super.scalacPluginMvnDeps() ++ { + if scalaVersion().startsWith("2.") then Seq(Deps.semanticDbScalac) else Nil } // Explicitly setting sourceroot, so that Scala CLI doesn't use a wrong one. - // Using Task.workspace is more or less required, for scalafix stuff to work fine. - def scalacOptions: Target[Seq[String]] = Task { + // Using BuildCtx.workspaceRoot is more or less required, for scalafix stuff to work fine. + def scalacOptions: T[Seq[String]] = Task { val sv = scalaVersion() val isScala2 = sv.startsWith("2.") - val sourceRoot = Task.workspace + val sourceRoot = BuildCtx.workspaceRoot val parentOptions = { val l = super.scalacOptions() - if (isScala2) l.filterNot(_.startsWith("-P:semanticdb:sourceroot:")) + if isScala2 then l.filterNot(_.startsWith("-P:semanticdb:sourceroot:")) else { val len = l.length val idx = l.indexWhere(_.startsWith("-sourceroot")) - if (idx >= 0 && idx < len - 1) l.take(idx) ++ l.drop(idx + 2) + if idx >= 0 && idx < len - 1 + then l.take(idx) ++ l.drop(idx + 2) else l } } val semDbOptions = - if (isScala2) Seq(s"-P:semanticdb:sourceroot:$sourceRoot") + if isScala2 + then Seq(s"-P:semanticdb:sourceroot:$sourceRoot") else Seq(s"-sourceroot", sourceRoot.toString) val warnUnusedOptions = - if (!parentOptions.contains("-Ywarn-unused") && scalaVersion().startsWith("2.12")) - Seq("-Ywarn-unused") - else if (!parentOptions.contains("-Wunused") && scalaVersion().startsWith("2.13")) - Seq("-Wunused") - else if (!parentOptions.contains("-Wunused:all") && scalaVersion().startsWith("3")) - Seq("-Wunused:all") + if !parentOptions.contains("-Ywarn-unused") && scalaVersion().startsWith("2.12") + then Seq("-Ywarn-unused") + else if !parentOptions.contains("-Wunused") && scalaVersion().startsWith("2.13") + then Seq("-Wunused") + else if !parentOptions.contains("-Wunused:all") && scalaVersion().startsWith("3") + then Seq("-Wunused:all") else Nil parentOptions ++ semDbOptions ++ warnUnusedOptions } @@ -817,24 +833,22 @@ trait ScalaCliScalafixModule extends ScalafixModule { // meant to be used with modules which still have to be cross-compiled on Scala 2.12 trait ScalaCliScalafixLegacyModule extends ScalaCliScalafixModule { - override def scalafixConfig: Target[Option[Path]] = Task { - Some(Task.workspace / ".scalafix.legacy.conf") + override def scalafixConfig: T[Option[os.Path]] = Task { + Some(BuildCtx.workspaceRoot / ".scalafix.legacy.conf") } } trait ScalaCliCrossSbtModule extends CrossSbtModule with ScalaCliModule trait ScalaCliModule extends ScalaModule { - def javacOptions: Target[Seq[String]] = super.javacOptions() ++ Seq( + def javacOptions: T[Seq[String]] = super.javacOptions() ++ Seq( "--release", "16" ) - def scalacOptions: Target[Seq[String]] = Task { + def scalacOptions: T[Seq[String]] = Task { val sv = scalaVersion() val isScala213 = sv.startsWith("2.13.") - val extraOptions = - if (isScala213) Seq("-Xsource:3", "-Ytasty-reader") - else Nil + val extraOptions = if isScala213 then Seq("-Xsource:3", "-Ytasty-reader") else Nil super.scalacOptions() ++ Seq("-feature", "-deprecation") ++ extraOptions } } diff --git a/project/website/package.mill.scala b/project/website/package.mill.scala index 6ea54163a7..a30d524ffc 100644 --- a/project/website/package.mill.scala +++ b/project/website/package.mill.scala @@ -26,7 +26,7 @@ def checkMainScalaVersions(path: os.Path): Unit = { val lastCells = cells.drop(1) assert(lastCells.length == 3) val expectedLastCells = Seq(Scala.scala3Next, Scala.scala213, Scala.scala212) - if (lastCells != expectedLastCells) + if lastCells != expectedLastCells then sys.error( s"Unexpected last line in Scala version table in $path " + s"(expected ${expectedLastCells.mkString(", ")}, got ${lastCells.mkString(", ")})" @@ -38,7 +38,7 @@ def checkScalaJsVersions(path: os.Path): Unit = { val lastCells = cells.drop(1) assert(lastCells.length == 1) val expectedLastCells = Seq(Scala.scalaJs) - if (lastCells != expectedLastCells) + if lastCells != expectedLastCells then sys.error( s"Unexpected last line in Scala.js version table in $path " + s"(expected ${expectedLastCells.mkString(", ")}, got ${lastCells.mkString(", ")})"