From 9a3c110d3cb62f15ea370a30b436135724b4b219 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 10 Oct 2024 11:43:07 +0200 Subject: [PATCH 01/13] Update coursier to 2.1.14 (#3705) Fixes https://github.com/com-lihaoyi/mill/issues/3695 along with https://github.com/com-lihaoyi/mill/pull/3701 (the two PRs are needed) --- build.mill | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.mill b/build.mill index f2691ab2b41..f4a9b0f2e24 100644 --- a/build.mill +++ b/build.mill @@ -120,7 +120,7 @@ object Deps { val asmTree = ivy"org.ow2.asm:asm-tree:9.7" val bloopConfig = ivy"ch.epfl.scala::bloop-config:1.5.5" - val coursier = ivy"io.get-coursier::coursier:2.1.13" + val coursier = ivy"io.get-coursier::coursier:2.1.14" val coursierInterface = ivy"io.get-coursier:interface:1.0.19" val cask = ivy"com.lihaoyi::cask:0.9.4" From 6b8316948af6c0cf93f8e1c3c653e19aa7070255 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 10 Oct 2024 11:44:29 +0200 Subject: [PATCH 02/13] Drop unnecessary filter on artifacts (#3701) Artifacts should already be filtered by `coursier.Artifacts` (via the default classifiers and artifact types stuff), no need to filter them further --- main/util/src/mill/util/CoursierSupport.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/util/src/mill/util/CoursierSupport.scala b/main/util/src/mill/util/CoursierSupport.scala index 2b37781bb81..0ab43fc6b3d 100644 --- a/main/util/src/mill/util/CoursierSupport.scala +++ b/main/util/src/mill/util/CoursierSupport.scala @@ -104,7 +104,7 @@ trait CoursierSupport { Agg.from( res.files .map(os.Path(_)) - .filter(path => path.ext == "jar" && resolveFilter(path)) + .filter(resolveFilter) .map(PathRef(_, quick = true)) ) ++ localTestDeps.flatten ) From c10c2585ca6d0e3109cafa8a6c081bc0f6e2d40a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 10 Oct 2024 11:45:21 +0200 Subject: [PATCH 03/13] Make use of new `os.zip.open` operation to create assemblies (#3707) Should be basically the same logic, just now we use the version bundled within `os.zip` --- scalalib/src/mill/scalalib/Assembly.scala | 56 +++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/scalalib/src/mill/scalalib/Assembly.scala b/scalalib/src/mill/scalalib/Assembly.scala index 8b389863b2d..13801a86ef9 100644 --- a/scalalib/src/mill/scalalib/Assembly.scala +++ b/scalalib/src/mill/scalalib/Assembly.scala @@ -6,9 +6,8 @@ import mill.api.{Ctx, IO, PathRef} import os.Generator import java.io.{ByteArrayInputStream, InputStream, SequenceInputStream} -import java.net.URI import java.nio.file.attribute.PosixFilePermission -import java.nio.file.{FileSystems, Files, StandardOpenOption} +import java.nio.file.StandardOpenOption import java.util.Collections import java.util.jar.JarFile import java.util.regex.Pattern @@ -222,44 +221,46 @@ object Assembly { os.remove(rawJar) // use the `base` (the upstream assembly) as a start - val baseUri = "jar:" + rawJar.toIO.getCanonicalFile.toURI.toASCIIString - val hm = base.fold(Map("create" -> "true")) { b => - os.copy(b, rawJar) - Map.empty - } + base.foreach(os.copy.over(_, rawJar)) var addedEntryCount = 0 // Add more files by copying files to a JAR file system - Using.resource(FileSystems.newFileSystem(URI.create(baseUri), hm.asJava)) { zipFs => - val manifestPath = zipFs.getPath(JarFile.MANIFEST_NAME) - Files.createDirectories(manifestPath.getParent) - val manifestOut = Files.newOutputStream( + Using.resource(os.zip.open(rawJar)) { zipRoot => + val manifestPath = zipRoot / os.SubPath(JarFile.MANIFEST_NAME) + os.makeDir.all(manifestPath / os.up) + Using.resource(os.write.outputStream( manifestPath, - StandardOpenOption.TRUNCATE_EXISTING, - StandardOpenOption.CREATE - ) - manifest.build.write(manifestOut) - manifestOut.close() + openOptions = Seq( + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE + ) + )) { manifestOut => + manifest.build.write(manifestOut) + } val (mappings, resourceCleaner) = Assembly.loadShadedClasspath(inputPaths, assemblyRules) try { Assembly.groupAssemblyEntries(mappings, assemblyRules).foreach { case (mapping, entry) => - val path = zipFs.getPath(mapping).toAbsolutePath + val path = zipRoot / os.SubPath(mapping) entry match { case entry: AppendEntry => val separated = entry.inputStreams .flatMap(inputStream => Seq(new ByteArrayInputStream(entry.separator.getBytes), inputStream()) ) - val cleaned = if (Files.exists(path)) separated else separated.drop(1) - val concatenated = new SequenceInputStream(Collections.enumeration(cleaned.asJava)) - addedEntryCount += 1 - writeEntry(path, concatenated, append = true) + val cleaned = if (os.exists(path)) separated else separated.drop(1) + Using.resource(new SequenceInputStream(Collections.enumeration(cleaned.asJava))) { + concatenated => + addedEntryCount += 1 + writeEntry(path, concatenated, append = true) + } case entry: WriteOnceEntry => addedEntryCount += 1 - writeEntry(path, entry.inputStream(), append = false) + Using.resource(entry.inputStream()) { stream => + writeEntry(path, stream, append = false) + } } } } finally { @@ -297,15 +298,14 @@ object Assembly { Assembly(PathRef(destJar), addedEntryCount) } - private def writeEntry(p: java.nio.file.Path, inputStream: InputStream, append: Boolean): Unit = { - if (p.getParent != null) Files.createDirectories(p.getParent) + private def writeEntry(p: os.Path, inputStream: InputStream, append: Boolean): Unit = { + if (p.segmentCount != 0) os.makeDir.all(p / os.up) val options = if (append) Seq(StandardOpenOption.APPEND, StandardOpenOption.CREATE) else Seq(StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE) - val outputStream = java.nio.file.Files.newOutputStream(p, options: _*) - IO.stream(inputStream, outputStream) - outputStream.close() - inputStream.close() + Using.resource(os.write.outputStream(p, openOptions = options)) { outputStream => + IO.stream(inputStream, outputStream) + } } } From 463547bc8a61eca29f4f21e7d8515b5268d28bda Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 10 Oct 2024 12:07:24 +0200 Subject: [PATCH 04/13] use case object in KotlinJSModule (#3708) These appear to be ADTs - and they need to generate upickle readwriters makes it easier for the Scala 3 upgrade to get this in now --- .../mill/kotlinlib/js/KotlinJSModule.scala | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala b/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala index 3540c099642..07f284a4a37 100644 --- a/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala +++ b/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala @@ -453,45 +453,45 @@ trait KotlinJSModule extends KotlinModule { outer => sealed trait ModuleKind { def extension: String } object ModuleKind { - object NoModule extends ModuleKind { val extension = "js" } + case object NoModule extends ModuleKind { val extension = "js" } implicit val rwNoModule: RW[NoModule.type] = macroRW - object UMDModule extends ModuleKind { val extension = "js" } + case object UMDModule extends ModuleKind { val extension = "js" } implicit val rwUMDModule: RW[UMDModule.type] = macroRW - object CommonJSModule extends ModuleKind { val extension = "js" } + case object CommonJSModule extends ModuleKind { val extension = "js" } implicit val rwCommonJSModule: RW[CommonJSModule.type] = macroRW - object AMDModule extends ModuleKind { val extension = "js" } + case object AMDModule extends ModuleKind { val extension = "js" } implicit val rwAMDModule: RW[AMDModule.type] = macroRW - object ESModule extends ModuleKind { val extension = "mjs" } + case object ESModule extends ModuleKind { val extension = "mjs" } implicit val rwESModule: RW[ESModule.type] = macroRW - object PlainModule extends ModuleKind { val extension = "js" } + case object PlainModule extends ModuleKind { val extension = "js" } implicit val rwPlainModule: RW[PlainModule.type] = macroRW } sealed trait SourceMapEmbedSourcesKind object SourceMapEmbedSourcesKind { - object Always extends SourceMapEmbedSourcesKind + case object Always extends SourceMapEmbedSourcesKind implicit val rwAlways: RW[Always.type] = macroRW - object Never extends SourceMapEmbedSourcesKind + case object Never extends SourceMapEmbedSourcesKind implicit val rwNever: RW[Never.type] = macroRW - object Inlining extends SourceMapEmbedSourcesKind + case object Inlining extends SourceMapEmbedSourcesKind implicit val rwInlining: RW[Inlining.type] = macroRW } sealed trait SourceMapNamesPolicy object SourceMapNamesPolicy { - object SimpleNames extends SourceMapNamesPolicy + case object SimpleNames extends SourceMapNamesPolicy implicit val rwSimpleNames: RW[SimpleNames.type] = macroRW - object FullyQualifiedNames extends SourceMapNamesPolicy + case object FullyQualifiedNames extends SourceMapNamesPolicy implicit val rwFullyQualifiedNames: RW[FullyQualifiedNames.type] = macroRW - object No extends SourceMapNamesPolicy + case object No extends SourceMapNamesPolicy implicit val rwNo: RW[No.type] = macroRW } sealed trait BinaryKind object BinaryKind { - object Library extends BinaryKind + case object Library extends BinaryKind implicit val rwLibrary: RW[Library.type] = macroRW - object Executable extends BinaryKind + case object Executable extends BinaryKind implicit val rwExecutable: RW[Executable.type] = macroRW implicit val rw: RW[BinaryKind] = macroRW } @@ -499,14 +499,14 @@ object BinaryKind { sealed trait RunTarget object RunTarget { // TODO rely on the node version installed in the env or fetch a specific one? - object Node extends RunTarget + case object Node extends RunTarget implicit val rwNode: RW[Node.type] = macroRW implicit val rw: RW[RunTarget] = macroRW } private[kotlinlib] sealed trait OutputMode private[kotlinlib] object OutputMode { - object Js extends OutputMode - object KlibDir extends OutputMode - object KlibFile extends OutputMode + case object Js extends OutputMode + case object KlibDir extends OutputMode + case object KlibFile extends OutputMode } From 365635e0129aed933eaadb0d678aaf7c6b480e0e Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 10 Oct 2024 13:07:03 +0200 Subject: [PATCH 05/13] Add kotlinx-html to `3-hello-kotlinjs` example (#3709) Takes advantage of the new klib support pulled in by https://github.com/com-lihaoyi/mill/pull/3705 We don't support the `module` file indirection, so for now we have to reference the `-js` version of the artifact directly --- .../kotlinlib/web/3-hello-kotlinjs/build.mill | 19 +++++++++++-------- .../web/3-hello-kotlinjs/foo/src/foo/Hello.kt | 9 +++++++-- .../foo/test/src/foo/HelloTests.kt | 6 ++++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/example/kotlinlib/web/3-hello-kotlinjs/build.mill b/example/kotlinlib/web/3-hello-kotlinjs/build.mill index 950fec1eb2a..cd69d50d31c 100644 --- a/example/kotlinlib/web/3-hello-kotlinjs/build.mill +++ b/example/kotlinlib/web/3-hello-kotlinjs/build.mill @@ -1,9 +1,9 @@ // KotlinJS support on Mill is still Work In Progress (WIP). As of time of writing it -// does not support third-party dependencies, Kotlin 2.x with KMP KLIB files, Node.js/Webpack -// test runners and reporting, etc. +// Node.js/Webpack test runners and reporting, etc. // -// The example below demonstrates only the minimal compilation, running, and testing of a single KotlinJS -// module. For more details in fully developing KotlinJS support, see the following ticket: +// The example below demonstrates only the minimal compilation, running, and testing of +// a single KotlinJS module using a single third-party dependency. For more details in +// fully developing KotlinJS support, see the following ticket: // // * https://github.com/com-lihaoyi/mill/issues/3611 @@ -14,6 +14,9 @@ object foo extends KotlinJSModule { def moduleKind = ModuleKind.ESModule def kotlinVersion = "1.9.25" def kotlinJSRunTarget = Some(RunTarget.Node) + def ivyDeps = Agg( + ivy"org.jetbrains.kotlinx:kotlinx-html-js:0.11.0", + ) object test extends KotlinJSModule with KotlinJSKotlinXTests } @@ -22,7 +25,7 @@ object foo extends KotlinJSModule { > mill foo.run Compiling 1 Kotlin sources to .../out/foo/compile.dest/classes... -Hello, world +

Hello World

stringifiedJsObject: ["hello","world","!"] > mill foo.test # Test is incorrect, `foo.test`` fails @@ -30,13 +33,13 @@ Compiling 1 Kotlin sources to .../out/foo/test/compile.dest/classes... Linking IR to .../out/foo/test/linkBinary.dest/binaries produce executable: .../out/foo/test/linkBinary.dest/binaries ... -error: AssertionError: Expected , actual . +error: AssertionError: Expected <

Hello World

>, actual <

Hello World Wrong

>. > cat out/foo/test/linkBinary.dest/binaries/test.js # Generated javascript on disk -...assertEquals_0(getString(), 'Not hello, world');... +...assertEquals_0(..., '

Hello World Wrong<\/h1>');... ... -> sed -i.bak 's/Not hello, world/Hello, world/g' foo/test/src/foo/HelloTests.kt +> sed -i.bak 's/Hello World Wrong/Hello World/g' foo/test/src/foo/HelloTests.kt > mill foo.test # passes after fixing test diff --git a/example/kotlinlib/web/3-hello-kotlinjs/foo/src/foo/Hello.kt b/example/kotlinlib/web/3-hello-kotlinjs/foo/src/foo/Hello.kt index 09f3ccd16af..b3348c98139 100644 --- a/example/kotlinlib/web/3-hello-kotlinjs/foo/src/foo/Hello.kt +++ b/example/kotlinlib/web/3-hello-kotlinjs/foo/src/foo/Hello.kt @@ -1,11 +1,16 @@ package foo -fun getString() = "Hello, world" +import kotlinx.html.* +import kotlinx.html.stream.createHTML fun main() { - println(getString()) + println(hello()) val parsedJsonStr: dynamic = JSON.parse("""{"helloworld": ["hello", "world", "!"]}""") val stringifiedJsObject = JSON.stringify(parsedJsonStr.helloworld) println("stringifiedJsObject: " + stringifiedJsObject) } + +fun hello(): String { + return createHTML().h1 { +"Hello World" }.toString() +} \ No newline at end of file diff --git a/example/kotlinlib/web/3-hello-kotlinjs/foo/test/src/foo/HelloTests.kt b/example/kotlinlib/web/3-hello-kotlinjs/foo/test/src/foo/HelloTests.kt index 7526f739947..fc33731c87a 100644 --- a/example/kotlinlib/web/3-hello-kotlinjs/foo/test/src/foo/HelloTests.kt +++ b/example/kotlinlib/web/3-hello-kotlinjs/foo/test/src/foo/HelloTests.kt @@ -6,8 +6,10 @@ import kotlin.test.assertEquals class HelloTests { @Test - fun failure() { - assertEquals(getString(), "Not hello, world") + fun testHello() { + val result = hello() + assertEquals(result.trim(), "

Hello World Wrong

") + result } } From a4d3e944037359cd7869e98068ed1774892e8847 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 10 Oct 2024 15:31:02 +0200 Subject: [PATCH 06/13] Try to fix deprecated discover warning (#3711) fixes https://github.com/com-lihaoyi/mill/issues/3385 Somehow the `@nowarn` annotations don't seem to work after we moved the `millDiscover` definition into the main module body (???) so instead of relying on those I try to fix the deprecated warning in the `Discover` macro itself by identifying type annotations and explicitly asking not to dereference them Manually tested by running the commands below, observed that no deprecation warning was printed ``` rm -rf /Users/lihaoyi/Github/mill/example/javalib/basic/1-simple/out ./mill -i dist.run example/javalib/basic/1-simple -i run -t hello ``` --- main/define/src/mill/define/Discover.scala | 11 ++++++++++- runner/src/mill/runner/CodeGen.scala | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/main/define/src/mill/define/Discover.scala b/main/define/src/mill/define/Discover.scala index c3136c2a0a0..03316412f80 100644 --- a/main/define/src/mill/define/Discover.scala +++ b/main/define/src/mill/define/Discover.scala @@ -141,11 +141,20 @@ object Discover { } if overridesRoutes._1.nonEmpty || overridesRoutes._2.nonEmpty || overridesRoutes._3.nonEmpty } yield { + val lhs0 = discoveredModuleType match { + // Explicitly do not de-alias type refs, so type aliases to deprecated + // types do not result in spurious deprecation warnings appearing + case tr: TypeRef => tr + // Other types are fine + case _ => discoveredModuleType.typeSymbol.asClass.toType + } + + val lhs = q"classOf[$lhs0]" + // by wrapping the `overridesRoutes` in a lambda function we kind of work around // the problem of generating a *huge* macro method body that finally exceeds the // JVM's maximum allowed method size val overridesLambda = q"(() => $overridesRoutes)()" - val lhs = q"classOf[${discoveredModuleType.typeSymbol.asClass}]" q"$lhs -> $overridesLambda" } diff --git a/runner/src/mill/runner/CodeGen.scala b/runner/src/mill/runner/CodeGen.scala index fa75337cb4c..58d549d69a5 100644 --- a/runner/src/mill/runner/CodeGen.scala +++ b/runner/src/mill/runner/CodeGen.scala @@ -161,11 +161,7 @@ object CodeGen { newScriptCode = objectData.name.applyTo(newScriptCode, wrapperObjectName) newScriptCode = objectData.obj.applyTo(newScriptCode, "abstract class") - val millDiscover = - if (segments.nonEmpty) "" - else - """@_root_.scala.annotation.nowarn - | override lazy val millDiscover: _root_.mill.define.Discover = _root_.mill.define.Discover[this.type]""".stripMargin + val millDiscover = discoverSnippet(segments) s"""$pkgLine |$aliasImports @@ -224,11 +220,7 @@ object CodeGen { s"extends _root_.mill.main.RootModule.Subfolder " } - val millDiscover = - if (segments.nonEmpty) "" - else - """@_root_.scala.annotation.nowarn - | override lazy val millDiscover: _root_.mill.define.Discover = _root_.mill.define.Discover[this.type]""".stripMargin + val millDiscover = discoverSnippet(segments) // User code needs to be put in a separate class for proper submodule // object initialization due to https://github.com/scala/scala3/issues/21444 @@ -240,6 +232,14 @@ object CodeGen { } + def discoverSnippet(segments: Seq[String]): String = { + if (segments.nonEmpty) "" + else + """override lazy val millDiscover: _root_.mill.define.Discover = _root_.mill.define.Discover[this.type] + |""".stripMargin + + } + private case class Snippet(var text: String = null, var start: Int = -1, var end: Int = -1) { def applyTo(s: String, replacement: String): String = s.patch(start, replacement.padTo(end - start, ' '), end - start) From fe6ac679df18410fd953ee11bc58bf847b60e624 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 10 Oct 2024 16:19:03 +0200 Subject: [PATCH 07/13] Bump OutputDirectoryLockTests wait timeout to try and mitigate flakiness Seems the 30s `retryMax` is timing out sometimes. Not sure if it's actually stuck or not, but if not bumping it to 60s should help --- .../feature/output-directory/src/OutputDirectoryLockTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/feature/output-directory/src/OutputDirectoryLockTests.scala b/integration/feature/output-directory/src/OutputDirectoryLockTests.scala index bcfce43b82f..13799e273aa 100644 --- a/integration/feature/output-directory/src/OutputDirectoryLockTests.scala +++ b/integration/feature/output-directory/src/OutputDirectoryLockTests.scala @@ -16,7 +16,7 @@ object OutputDirectoryLockTests extends UtestIntegrationTestSuite { override def utestAfterAll(): Unit = { pool.shutdown() } - implicit val retryMax: RetryMax = RetryMax(30000.millis) + implicit val retryMax: RetryMax = RetryMax(60000.millis) implicit val retryInterval: RetryInterval = RetryInterval(50.millis) def tests: Tests = Tests { test("basic") - integrationTest { tester => From 9a0ffc21acb56f8c7604f4e5c37a495fba863baf Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 10 Oct 2024 16:19:57 +0200 Subject: [PATCH 08/13] Try and speed up proguard contrib module/tests by re-using global java runtime jar (#3713) Proguard is a long pole in the windows contrib test job, hopefully this makes it a bit faster Seems to cut a few minutes; previously it ran in 12-17min, with this PR it ran in 10+ min --- .../src/mill/contrib/proguard/Proguard.scala | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/contrib/proguard/src/mill/contrib/proguard/Proguard.scala b/contrib/proguard/src/mill/contrib/proguard/Proguard.scala index 7c577ff2549..662ab690b09 100644 --- a/contrib/proguard/src/mill/contrib/proguard/Proguard.scala +++ b/contrib/proguard/src/mill/contrib/proguard/Proguard.scala @@ -65,18 +65,8 @@ trait Proguard extends ScalaModule { * Keep in sync with [[javaHome]]. */ def java9RtJar: T[Seq[PathRef]] = Task { - if (mill.main.client.Util.isJava9OrAbove) { - val rt = T.dest / Export.rtJarName - if (!os.exists(rt)) { - T.log.outputStream.println( - s"Preparing Java runtime JAR; this may take a minute or two ..." - ) - Export.rtTo(rt.toIO, false) - } - Seq(PathRef(rt)) - } else { - Seq() - } + if (mill.main.client.Util.isJava9OrAbove) Seq(PathRef(T.home / Export.rtJarName)) + else Seq() } /** From 01ab379ec98b0aa70711ae5dae7a207f4d9760f3 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 10 Oct 2024 16:32:15 +0200 Subject: [PATCH 09/13] More cleanups for PromptLogger (#3712) * Move more pure logic into `PromptLoggerUtil` static methods * Make prompt line replacement logic more aggressive, such that even after dead lines finish transitioning they can be replaced, fixing an issue where the prompt height sometimes grew unnecessarily due to blank lines --- main/api/src/mill/api/Logger.scala | 2 +- main/eval/src/mill/eval/EvaluatorCore.scala | 2 +- main/util/src/mill/util/MultiLogger.scala | 6 +- main/util/src/mill/util/PrefixLogger.scala | 3 +- main/util/src/mill/util/PromptLogger.scala | 61 +++++-------------- .../util/src/mill/util/PromptLoggerUtil.scala | 35 +++++++++++ main/util/src/mill/util/ProxyLogger.scala | 3 +- .../src/mill/util/PromptLoggerTests.scala | 8 +-- 8 files changed, 63 insertions(+), 57 deletions(-) diff --git a/main/api/src/mill/api/Logger.scala b/main/api/src/mill/api/Logger.scala index 430b6352f99..b9a5dd64ddf 100644 --- a/main/api/src/mill/api/Logger.scala +++ b/main/api/src/mill/api/Logger.scala @@ -58,7 +58,7 @@ trait Logger extends AutoCloseable { ): Unit = ticker(s"${key.mkString("-")} $message") private[mill] def setPromptLine(): Unit = () - private[mill] def setPromptLeftHeader(s: String): Unit = () + private[mill] def setPromptHeaderPrefix(s: String): Unit = () private[mill] def clearPromptStatuses(): Unit = () private[mill] def removePromptLine(key: Seq[String]): Unit = () private[mill] def removePromptLine(): Unit = () diff --git a/main/eval/src/mill/eval/EvaluatorCore.scala b/main/eval/src/mill/eval/EvaluatorCore.scala index bfc25731686..932d73092bd 100644 --- a/main/eval/src/mill/eval/EvaluatorCore.scala +++ b/main/eval/src/mill/eval/EvaluatorCore.scala @@ -111,7 +111,7 @@ private[mill] trait EvaluatorCore extends GroupEvaluator { ) val verboseKeySuffix = s"/${terminals0.size}" - logger.setPromptLeftHeader(s"$countMsg$verboseKeySuffix") + logger.setPromptHeaderPrefix(s"$countMsg$verboseKeySuffix") if (failed.get()) None else { val upstreamResults = upstreamValues diff --git a/main/util/src/mill/util/MultiLogger.scala b/main/util/src/mill/util/MultiLogger.scala index 9dda1ea4e4b..357763657b3 100644 --- a/main/util/src/mill/util/MultiLogger.scala +++ b/main/util/src/mill/util/MultiLogger.scala @@ -81,9 +81,9 @@ class MultiLogger( logger1.removePromptLine() logger2.removePromptLine() } - private[mill] override def setPromptLeftHeader(s: String): Unit = { - logger1.setPromptLeftHeader(s) - logger2.setPromptLeftHeader(s) + private[mill] override def setPromptHeaderPrefix(s: String): Unit = { + logger1.setPromptHeaderPrefix(s) + logger2.setPromptHeaderPrefix(s) } private[mill] override def withPromptPaused[T](t: => T): T = { diff --git a/main/util/src/mill/util/PrefixLogger.scala b/main/util/src/mill/util/PrefixLogger.scala index 68cd02e7321..7ee393516a9 100644 --- a/main/util/src/mill/util/PrefixLogger.scala +++ b/main/util/src/mill/util/PrefixLogger.scala @@ -107,7 +107,8 @@ class PrefixLogger( private[mill] override def removePromptLine(callKey: Seq[String]): Unit = logger0.removePromptLine(callKey) private[mill] override def removePromptLine(): Unit = removePromptLine(logPrefixKey) - private[mill] override def setPromptLeftHeader(s: String): Unit = logger0.setPromptLeftHeader(s) + private[mill] override def setPromptHeaderPrefix(s: String): Unit = + logger0.setPromptHeaderPrefix(s) override def enableTicker = logger0.enableTicker private[mill] override def subLogger( diff --git a/main/util/src/mill/util/PromptLogger.scala b/main/util/src/mill/util/PromptLogger.scala index 8846355f433..1a08d6cefcc 100644 --- a/main/util/src/mill/util/PromptLogger.scala +++ b/main/util/src/mill/util/PromptLogger.scala @@ -88,16 +88,16 @@ private[mill] class PromptLogger( def error(s: String): Unit = synchronized { systemStreams.err.println(s) } - override def setPromptLeftHeader(s: String): Unit = - synchronized { promptLineState.updateGlobal(s) } + override def setPromptHeaderPrefix(s: String): Unit = + synchronized { promptLineState.setHeaderPrefix(s) } override def clearPromptStatuses(): Unit = synchronized { promptLineState.clearStatuses() } override def removePromptLine(key: Seq[String]): Unit = synchronized { - promptLineState.updateCurrent(key, None) + promptLineState.setCurrent(key, None) } def ticker(s: String): Unit = () override def setPromptDetail(key: Seq[String], s: String): Unit = synchronized { - promptLineState.updateDetail(key, s) + promptLineState.setDetail(key, s) } override def reportKey(key: Seq[String]): Unit = synchronized { @@ -116,7 +116,7 @@ private[mill] class PromptLogger( private val reportedIdentifiers = collection.mutable.Set.empty[Seq[String]] override def setPromptLine(key: Seq[String], verboseKeySuffix: String, message: String): Unit = synchronized { - promptLineState.updateCurrent(key, Some(s"[${key.mkString("-")}] $message")) + promptLineState.setCurrent(key, Some(s"[${key.mkString("-")}] $message")) seenIdentifiers(key) = (verboseKeySuffix, message) } @@ -273,19 +273,8 @@ private[mill] object PromptLogger { ) { private var lastRenderedPromptHash = 0 - private implicit def seqOrdering = new Ordering[Seq[String]] { - def compare(xs: Seq[String], ys: Seq[String]): Int = { - val iter = xs.iterator.zip(ys) - while (iter.nonEmpty) { - val (x, y) = iter.next() - if (x > y) return 1 - else if (y > x) return -1 - } - - return xs.lengthCompare(ys) - } - } - private val statuses = collection.mutable.SortedMap.empty[Seq[String], Status] + private val statuses = collection.mutable.SortedMap + .empty[Seq[String], Status](PromptLoggerUtil.seqStringOrdering) private var headerPrefix = "" // Pre-compute the prelude and current prompt as byte arrays so that @@ -308,6 +297,7 @@ private[mill] object PromptLogger { if (ending) statuses.clear() val (termWidth0, termHeight0) = consoleDims() + val interactive = consoleDims()._1.nonEmpty // don't show prompt for non-interactive terminal val currentPromptLines = renderPrompt( termWidth0.getOrElse(defaultTermWidth), @@ -317,38 +307,23 @@ private[mill] object PromptLogger { s"[$headerPrefix]", titleText, statuses.toSeq.map { case (k, v) => (k.mkString("-"), v) }, - interactive = consoleDims()._1.nonEmpty, + interactive = interactive, infoColor = infoColor, ending = ending ) - val currentPromptStr = - if (termWidth0.isEmpty) currentPromptLines.mkString("\n") + "\n" - else { - // For the ending prompt, leave the cursor at the bottom on a new line rather than - // scrolling back left/up. We do not want further output to overwrite the header as - // it will no longer re-render - val backUp = - if (ending) "\n" - else AnsiNav.left(9999) + AnsiNav.up(currentPromptLines.length - 1) - - AnsiNav.clearScreen(0) + - currentPromptLines.mkString("\n") + - backUp - } - - currentPromptBytes = currentPromptStr.getBytes + currentPromptBytes = renderPromptWrapped(currentPromptLines, interactive, ending).getBytes } def clearStatuses(): Unit = synchronized { statuses.clear() } - def updateGlobal(s: String): Unit = synchronized { headerPrefix = s } + def setHeaderPrefix(s: String): Unit = synchronized { headerPrefix = s } - def updateDetail(key: Seq[String], detail: String): Unit = synchronized { + def setDetail(key: Seq[String], detail: String): Unit = synchronized { statuses.updateWith(key)(_.map(se => se.copy(next = se.next.map(_.copy(detail = detail))))) } - def updateCurrent(key: Seq[String], sOpt: Option[String]): Unit = synchronized { + def setCurrent(key: Seq[String], sOpt: Option[String]): Unit = synchronized { val now = currentTimeMillis() def stillTransitioning(status: Status) = { @@ -357,7 +332,7 @@ private[mill] object PromptLogger { val sOptEntry = sOpt.map(StatusEntry(_, now, "")) statuses.updateWith(key) { case None => - statuses.find { case (k, v) => v.next.isEmpty && stillTransitioning(v) } match { + statuses.find { case (k, v) => v.next.isEmpty } match { case Some((reusableKey, reusableValue)) => statuses.remove(reusableKey) Some(reusableValue.copy(next = sOptEntry)) @@ -369,13 +344,7 @@ private[mill] object PromptLogger { // If still performing a transition, do not update the `prevTransitionTime` // since we do not want to delay the transition that is already in progress if (stillTransitioning(existing)) existing.copy(next = sOptEntry) - else { - existing.copy( - next = sOptEntry, - beginTransitionTime = now, - prev = existing.next - ) - } + else existing.copy(next = sOptEntry, beginTransitionTime = now, prev = existing.next) ) } } diff --git a/main/util/src/mill/util/PromptLoggerUtil.scala b/main/util/src/mill/util/PromptLoggerUtil.scala index fd261ed1b70..128e93e158b 100644 --- a/main/util/src/mill/util/PromptLoggerUtil.scala +++ b/main/util/src/mill/util/PromptLoggerUtil.scala @@ -149,6 +149,28 @@ private object PromptLoggerUtil { header :: body ::: footer } + // Wrap the prompt in the necessary clear-screens/newlines/move-cursors + // according to whether it is interactive or ending + def renderPromptWrapped( + currentPromptLines: Seq[String], + interactive: Boolean, + ending: Boolean + ): String = { + if (!interactive) currentPromptLines.mkString("\n") + "\n" + else { + // For the ending prompt, leave the cursor at the bottom on a new line rather than + // scrolling back left/up. We do not want further output to overwrite the header as + // it will no longer re-render + val backUp = + if (ending) "\n" + else AnsiNav.left(9999) + AnsiNav.up(currentPromptLines.length - 1) + + AnsiNav.clearScreen(0) + + currentPromptLines.mkString("\n") + + backUp + } + } + def renderHeader( headerPrefix0: String, titleText0: String, @@ -204,4 +226,17 @@ private object PromptLoggerUtil { } ??? } + + private[mill] val seqStringOrdering = new Ordering[Seq[String]] { + def compare(xs: Seq[String], ys: Seq[String]): Int = { + val iter = xs.iterator.zip(ys) + while (iter.nonEmpty) { + val (x, y) = iter.next() + if (x > y) return 1 + else if (y > x) return -1 + } + + return xs.lengthCompare(ys) + } + } } diff --git a/main/util/src/mill/util/ProxyLogger.scala b/main/util/src/mill/util/ProxyLogger.scala index 240fcabd685..bb1f1d1d27d 100644 --- a/main/util/src/mill/util/ProxyLogger.scala +++ b/main/util/src/mill/util/ProxyLogger.scala @@ -35,7 +35,8 @@ class ProxyLogger(logger: Logger) extends Logger { override def rawOutputStream: PrintStream = logger.rawOutputStream private[mill] override def removePromptLine(key: Seq[String]): Unit = logger.removePromptLine(key) private[mill] override def removePromptLine(): Unit = logger.removePromptLine() - private[mill] override def setPromptLeftHeader(s: String): Unit = logger.setPromptLeftHeader(s) + private[mill] override def setPromptHeaderPrefix(s: String): Unit = + logger.setPromptHeaderPrefix(s) private[mill] override def withPromptPaused[T](t: => T): T = logger.withPromptPaused(t) private[mill] override def withPromptUnpaused[T](t: => T): T = logger.withPromptUnpaused(t) diff --git a/main/util/test/src/mill/util/PromptLoggerTests.scala b/main/util/test/src/mill/util/PromptLoggerTests.scala index b9f95ef7b4d..044247339f6 100644 --- a/main/util/test/src/mill/util/PromptLoggerTests.scala +++ b/main/util/test/src/mill/util/PromptLoggerTests.scala @@ -59,7 +59,7 @@ object PromptLoggerTests extends TestSuite { val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp()) - promptLogger.setPromptLeftHeader("123/456") + promptLogger.setPromptHeaderPrefix("123/456") promptLogger.setPromptLine(Seq("1"), "/456", "my-task") now += 10000 @@ -108,7 +108,7 @@ object PromptLoggerTests extends TestSuite { var now = 0L val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp("80 40")) - promptLogger.setPromptLeftHeader("123/456") + promptLogger.setPromptHeaderPrefix("123/456") promptLogger.refreshPrompt() check(promptLogger, baos)( " [123/456] ========================== TITLE ==================================" @@ -278,7 +278,7 @@ object PromptLoggerTests extends TestSuite { @volatile var now = 0L val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp("80 40")) - promptLogger.setPromptLeftHeader("123/456") + promptLogger.setPromptHeaderPrefix("123/456") promptLogger.refreshPrompt() check(promptLogger, baos)( " [123/456] ========================== TITLE ==================================" @@ -329,7 +329,7 @@ object PromptLoggerTests extends TestSuite { @volatile var now = 0L val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp("80 40")) - promptLogger.setPromptLeftHeader("123/456") + promptLogger.setPromptHeaderPrefix("123/456") promptLogger.refreshPrompt() promptLogger.setPromptLine(Seq("1"), "/456", "my-task") From 80d1640658dae984888be2eac6b47d830bab93b2 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Thu, 10 Oct 2024 16:44:11 +0200 Subject: [PATCH 10/13] Add JavaModule.artifactTypes (#3703) Allowing users to ask for more artifact types to be fetched and put in the classpath --- .../src/DocAnnotationsTests.scala | 2 +- main/util/src/mill/util/CoursierSupport.scala | 30 +++++++++++++++++-- .../src/mill/scalalib/CoursierModule.scala | 28 +++++++++++++++-- scalalib/src/mill/scalalib/JavaModule.scala | 13 ++++++-- scalalib/src/mill/scalalib/Lib.scala | 29 ++++++++++++++++-- .../src/mill/scalalib/ZincWorkerModule.scala | 4 +-- 6 files changed, 94 insertions(+), 12 deletions(-) diff --git a/integration/feature/docannotations/src/DocAnnotationsTests.scala b/integration/feature/docannotations/src/DocAnnotationsTests.scala index 77bdb767d1e..02d27311844 100644 --- a/integration/feature/docannotations/src/DocAnnotationsTests.scala +++ b/integration/feature/docannotations/src/DocAnnotationsTests.scala @@ -93,7 +93,7 @@ object DocAnnotationsTests extends UtestIntegrationTestSuite { assert( globMatches( - """core.ivyDepsTree(JavaModule.scala:884) + """core.ivyDepsTree(JavaModule.scala:893) | Command to print the transitive dependency tree to STDOUT. | | --inverse Invert the tree representation, so that the root is on the bottom val diff --git a/main/util/src/mill/util/CoursierSupport.scala b/main/util/src/mill/util/CoursierSupport.scala index 0ab43fc6b3d..ba09b7bfb0b 100644 --- a/main/util/src/mill/util/CoursierSupport.scala +++ b/main/util/src/mill/util/CoursierSupport.scala @@ -6,7 +6,7 @@ import coursier.error.ResolutionError.CantDownloadModule import coursier.params.ResolutionParams import coursier.parse.RepositoryParser import coursier.util.Task -import coursier.{Artifacts, Classifier, Dependency, Repository, Resolution, Resolve} +import coursier.{Artifacts, Classifier, Dependency, Repository, Resolution, Resolve, Type} import mill.api.Loose.Agg import mill.api.{Ctx, PathRef, Result} @@ -44,7 +44,8 @@ trait CoursierSupport { customizer: Option[Resolution => Resolution] = None, ctx: Option[mill.api.Ctx.Log] = None, coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None, - resolveFilter: os.Path => Boolean = _ => true + resolveFilter: os.Path => Boolean = _ => true, + artifactTypes: Option[Set[Type]] = None ): Result[Agg[PathRef]] = { def isLocalTestDep(dep: Dependency): Option[Seq[PathRef]] = { val org = dep.module.organization.value @@ -86,6 +87,7 @@ trait CoursierSupport { if (sources) Set(Classifier("sources")) else Set.empty ) + .withArtifactTypesOpt(artifactTypes) .eitherResult() artifactsResultOrError match { @@ -112,6 +114,30 @@ trait CoursierSupport { } } + @deprecated("Use the override accepting artifactTypes", "Mill after 0.12.0-RC3") + def resolveDependencies( + repositories: Seq[Repository], + deps: IterableOnce[Dependency], + force: IterableOnce[Dependency], + sources: Boolean, + mapDependencies: Option[Dependency => Dependency], + customizer: Option[Resolution => Resolution], + ctx: Option[mill.api.Ctx.Log], + coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]], + resolveFilter: os.Path => Boolean + ): Result[Agg[PathRef]] = + resolveDependencies( + repositories, + deps, + force, + sources, + mapDependencies, + customizer, + ctx, + coursierCacheCustomizer, + resolveFilter + ) + @deprecated( "Prefer resolveDependenciesMetadataSafe instead, which returns a Result instead of throwing exceptions", "0.12.0" diff --git a/scalalib/src/mill/scalalib/CoursierModule.scala b/scalalib/src/mill/scalalib/CoursierModule.scala index 9ecdfcbfeeb..4594916e4a0 100644 --- a/scalalib/src/mill/scalalib/CoursierModule.scala +++ b/scalalib/src/mill/scalalib/CoursierModule.scala @@ -1,7 +1,7 @@ package mill.scalalib import coursier.cache.FileCache -import coursier.{Dependency, Repository, Resolve} +import coursier.{Dependency, Repository, Resolve, Type} import coursier.core.Resolution import mill.define.Task import mill.api.PathRef @@ -48,14 +48,20 @@ trait CoursierModule extends mill.Module { * * @param deps The dependencies to resolve. * @param sources If `true`, resolve source dependencies instead of binary dependencies (JARs). + * @param artifactTypes If non-empty, pull the passed artifact types rather than the default ones from coursier * @return The [[PathRef]]s to the resolved files. */ - def resolveDeps(deps: Task[Agg[BoundDep]], sources: Boolean = false): Task[Agg[PathRef]] = + def resolveDeps( + deps: Task[Agg[BoundDep]], + sources: Boolean = false, + artifactTypes: Option[Set[Type]] = None + ): Task[Agg[PathRef]] = Task.Anon { Lib.resolveDependencies( repositories = repositoriesTask(), deps = deps(), sources = sources, + artifactTypes = artifactTypes, mapDependencies = Some(mapDependencies()), customizer = resolutionCustomizer(), coursierCacheCustomizer = coursierCacheCustomizer(), @@ -63,6 +69,13 @@ trait CoursierModule extends mill.Module { ) } + @deprecated("Use the override accepting artifactTypes", "Mill after 0.12.0-RC3") + def resolveDeps( + deps: Task[Agg[BoundDep]], + sources: Boolean + ): Task[Agg[PathRef]] = + resolveDeps(deps, sources, None) + /** * Map dependencies before resolving them. * Override this to customize the set of dependencies. @@ -134,18 +147,27 @@ object CoursierModule { def resolveDeps[T: CoursierModule.Resolvable]( deps: IterableOnce[T], - sources: Boolean = false + sources: Boolean = false, + artifactTypes: Option[Set[coursier.Type]] = None ): Agg[PathRef] = { Lib.resolveDependencies( repositories = repositories, deps = deps.map(implicitly[CoursierModule.Resolvable[T]].bind(_, bind)), sources = sources, + artifactTypes = artifactTypes, mapDependencies = mapDependencies, customizer = customizer, coursierCacheCustomizer = coursierCacheCustomizer, ctx = ctx ).getOrThrow } + + @deprecated("Use the override accepting artifactTypes", "Mill after 0.12.0-RC3") + def resolveDeps[T: CoursierModule.Resolvable]( + deps: IterableOnce[T], + sources: Boolean + ): Agg[PathRef] = + resolveDeps(deps, sources, None) } sealed trait Resolvable[T] { diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index 90d72e7b62b..f72bc67c743 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -1,11 +1,11 @@ package mill package scalalib -import coursier.Repository import coursier.core.Resolution import coursier.parse.JavaOrScalaModule import coursier.parse.ModuleParser import coursier.util.ModuleMatcher +import coursier.{Repository, Type} import mainargs.{Flag, arg} import mill.Agg import mill.api.{Ctx, JarManifest, MillException, PathRef, Result, internal} @@ -155,6 +155,12 @@ trait JavaModule */ def runIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] } + /** + * Default artifact types to fetch and put in the classpath. Add extra types + * here if you'd like fancy artifact extensions to be fetched. + */ + def artifactTypes: T[Set[Type]] = Task { coursier.core.Resolution.defaultTypes } + /** * Options to pass to the java compiler */ @@ -551,7 +557,10 @@ trait JavaModule } def resolvedRunIvyDeps: T[Agg[PathRef]] = Task { - defaultResolver().resolveDeps(runIvyDeps().map(bindDependency()) ++ transitiveIvyDeps()) + defaultResolver().resolveDeps( + runIvyDeps().map(bindDependency()) ++ transitiveIvyDeps(), + artifactTypes = Some(artifactTypes()) + ) } /** diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index c606a519c4a..07a10461524 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -2,7 +2,7 @@ package mill package scalalib import coursier.util.Task -import coursier.{Dependency, Repository, Resolution} +import coursier.{Dependency, Repository, Resolution, Type} import mill.api.{Ctx, Loose, PathRef, Result} import mill.main.BuildInfo import mill.main.client.EnvVars @@ -89,7 +89,8 @@ object Lib { ctx: Option[Ctx.Log] = None, coursierCacheCustomizer: Option[ coursier.cache.FileCache[Task] => coursier.cache.FileCache[Task] - ] = None + ] = None, + artifactTypes: Option[Set[Type]] = None ): Result[Agg[PathRef]] = { val depSeq = deps.iterator.toSeq mill.util.Jvm.resolveDependencies( @@ -97,6 +98,7 @@ object Lib { deps = depSeq.map(_.dep), force = depSeq.filter(_.force).map(_.dep), sources = sources, + artifactTypes = artifactTypes, mapDependencies = mapDependencies, customizer = customizer, ctx = ctx, @@ -104,6 +106,29 @@ object Lib { ).map(_.map(_.withRevalidateOnce)) } + @deprecated("Use the override accepting artifactTypes", "Mill after 0.12.0-RC3") + def resolveDependencies( + repositories: Seq[Repository], + deps: IterableOnce[BoundDep], + sources: Boolean, + mapDependencies: Option[Dependency => Dependency], + customizer: Option[coursier.core.Resolution => coursier.core.Resolution], + ctx: Option[Ctx.Log], + coursierCacheCustomizer: Option[ + coursier.cache.FileCache[Task] => coursier.cache.FileCache[Task] + ] + ): Result[Agg[PathRef]] = + resolveDependencies( + repositories, + deps, + sources, + mapDependencies, + customizer, + ctx, + coursierCacheCustomizer, + None + ) + def scalaCompilerIvyDeps(scalaOrganization: String, scalaVersion: String): Loose.Agg[Dep] = if (ZincWorkerUtil.isDotty(scalaVersion)) Agg( diff --git a/scalalib/src/mill/scalalib/ZincWorkerModule.scala b/scalalib/src/mill/scalalib/ZincWorkerModule.scala index 6861fdcacf0..611e2482ef3 100644 --- a/scalalib/src/mill/scalalib/ZincWorkerModule.scala +++ b/scalalib/src/mill/scalalib/ZincWorkerModule.scala @@ -127,8 +127,8 @@ trait ZincWorkerModule extends mill.Module with OfflineSupportModule { self: Cou val bridgeJar = resolveDependencies( repositories, Seq(bridgeDep.bindDep("", "", "")), - useSources, - Some(overrideScalaLibrary(scalaVersion, scalaOrganization)) + sources = useSources, + mapDependencies = Some(overrideScalaLibrary(scalaVersion, scalaOrganization)) ).map(deps => ZincWorkerUtil.grepJar(deps, bridgeName, bridgeVersion, useSources) ) From fba6c262a97ade99fcf9ae64b1da14ffad3b8a6f Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 11 Oct 2024 11:51:32 +0200 Subject: [PATCH 11/13] Update mill-version (#3718) --- .config/mill-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/mill-version b/.config/mill-version index b30ea878bb5..825bbb0a69e 100644 --- a/.config/mill-version +++ b/.config/mill-version @@ -1 +1 @@ -0.12.0-RC3-32-b4a0bf \ No newline at end of file +0.12.0-RC3-46-80d164 From cc1aa6f1f3dc85da1e3c9ae2ffeb869804664c1c Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 11 Oct 2024 13:04:12 +0200 Subject: [PATCH 12/13] upgrade moduledefs and mainargs (#3720) pre-prep for scala 3 port --- build.mill | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.mill b/build.mill index f4a9b0f2e24..60b3afa4be7 100644 --- a/build.mill +++ b/build.mill @@ -151,8 +151,8 @@ object Deps { val log4j2Core = ivy"org.apache.logging.log4j:log4j-core:2.23.1" val osLib = ivy"com.lihaoyi::os-lib:0.11.1" val pprint = ivy"com.lihaoyi::pprint:0.9.0" - val mainargs = ivy"com.lihaoyi::mainargs:0.7.4" - val millModuledefsVersion = "0.11.0" + val mainargs = ivy"com.lihaoyi::mainargs:0.7.6" + val millModuledefsVersion = "0.11.1" val millModuledefsString = s"com.lihaoyi::mill-moduledefs:${millModuledefsVersion}" val millModuledefs = ivy"${millModuledefsString}" val millModuledefsPlugin = From 7221788fc4f9bb2cfabbbb9e3a3782751d2751ff Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 11 Oct 2024 14:02:51 +0200 Subject: [PATCH 13/13] Fix JavaModule.artifactTypes (#3719) Follow-up of https://github.com/com-lihaoyi/mill/pull/3703 that added `JavaModule.artifactTypes`. `JavaModule` performs two dependency resolutions: one for `compileClasspath`, the other for `runClasspath`. https://github.com/com-lihaoyi/mill/pull/3703 used `artifactTypes` in the `runClasspath` one, but not in the `compileClasspath` one. The PR here fixes that, and adds unit tests for both paths. This addresses the issues found around https://github.com/com-lihaoyi/mill/pull/3696#issuecomment-2405891608. --- .../src/DocAnnotationsTests.scala | 2 +- scalalib/src/mill/scalalib/JavaModule.scala | 5 +++- .../resources/pomArtifactType/.placeholder | 0 .../src/mill/scalalib/ResolveDepsTests.scala | 29 +++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 scalalib/test/resources/pomArtifactType/.placeholder diff --git a/integration/feature/docannotations/src/DocAnnotationsTests.scala b/integration/feature/docannotations/src/DocAnnotationsTests.scala index 02d27311844..4ef2900e86c 100644 --- a/integration/feature/docannotations/src/DocAnnotationsTests.scala +++ b/integration/feature/docannotations/src/DocAnnotationsTests.scala @@ -93,7 +93,7 @@ object DocAnnotationsTests extends UtestIntegrationTestSuite { assert( globMatches( - """core.ivyDepsTree(JavaModule.scala:893) + """core.ivyDepsTree(JavaModule.scala:896) | Command to print the transitive dependency tree to STDOUT. | | --inverse Invert the tree representation, so that the root is on the bottom val diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index f72bc67c743..4d92101bcb4 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -545,7 +545,10 @@ trait JavaModule * Resolved dependencies based on [[transitiveIvyDeps]] and [[transitiveCompileIvyDeps]]. */ def resolvedIvyDeps: T[Agg[PathRef]] = Task { - defaultResolver().resolveDeps(transitiveCompileIvyDeps() ++ transitiveIvyDeps()) + defaultResolver().resolveDeps( + transitiveCompileIvyDeps() ++ transitiveIvyDeps(), + artifactTypes = Some(artifactTypes()) + ) } /** diff --git a/scalalib/test/resources/pomArtifactType/.placeholder b/scalalib/test/resources/pomArtifactType/.placeholder new file mode 100644 index 00000000000..e69de29bb2d diff --git a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala index cd55d1b14b5..a54a2d023f9 100644 --- a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala +++ b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala @@ -4,6 +4,7 @@ import coursier.maven.MavenRepository import mill.api.Result.{Failure, Success} import mill.api.{PathRef, Result} import mill.api.Loose.Agg +import mill.testkit.{UnitTester, TestBaseModule} import utest._ object ResolveDepsTests extends TestSuite { @@ -28,6 +29,21 @@ object ResolveDepsTests extends TestSuite { assert(upickle.default.read[Dep](upickle.default.write(dep)) == dep) } } + + object TestCase extends TestBaseModule { + object pomStuff extends JavaModule { + def ivyDeps = Agg( + // Dependency whose packaging is "pom", as it's meant to be used + // as a "parent dependency" by other dependencies, rather than be pulled + // as we do here. We do it anyway, to check that pulling the "pom" artifact + // type brings that dependency POM file in the class path. We need a dependency + // that has a "pom" packaging for that. + ivy"org.apache.hadoop:hadoop-yarn-server:3.4.0" + ) + def artifactTypes = super.artifactTypes() + coursier.Type("pom") + } + } + val tests = Tests { test("resolveValidDeps") { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3") @@ -95,5 +111,18 @@ object ResolveDepsTests extends TestSuite { val Failure(errMsg, _) = evalDeps(deps) assert(errMsg.contains("fake")) } + + test("pomArtifactType") { + val sources = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) / "pomArtifactType" + UnitTester(TestCase, sourceRoot = sources).scoped { eval => + val Right(compileResult) = eval(TestCase.pomStuff.compileClasspath) + val compileCp = compileResult.value.toSeq.map(_.path) + assert(compileCp.exists(_.lastOpt.contains("hadoop-yarn-server-3.4.0.pom"))) + + val Right(runResult) = eval(TestCase.pomStuff.runClasspath) + val runCp = runResult.value.toSeq.map(_.path) + assert(runCp.exists(_.lastOpt.contains("hadoop-yarn-server-3.4.0.pom"))) + } + } } }