From 8c74805831329a8252e015678afbdfa548809114 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Tue, 10 Sep 2024 07:22:35 +0200 Subject: [PATCH 01/13] Add Kotlin support by sourcing in lefou/mill-kotlin repo --- .github/workflows/run-tests.yml | 4 +- build.mill | 2 + kotlinlib/package.mill | 36 +++ .../src/mill/kotlinlib/KotlinModule.scala | 211 ++++++++++++++++++ .../mill/kotlinlib/KotlinModulePlatform.scala | 57 +++++ .../mill/kotlinlib/KotlinWorkerManager.scala | 8 + .../kotlinlib/KotlinWorkerManagerImpl.scala | 66 ++++++ .../mill/kotlinlib/KotlinWorkerModule.scala | 14 ++ .../hello-world-kotlin/main/src/Hello.kt | 10 + .../main/test/src/HelloTest.kt | 15 ++ .../main/src/hello/JavaHello.java | 14 ++ .../main/src/hello/KotlinHello.kt | 7 + .../main/test/src/HelloTest.java | 16 ++ .../src/mill/kotlinlib/HelloWorldTests.scala | 90 ++++++++ .../mill/kotlinlib/MixedHelloWorldTests.scala | 93 ++++++++ .../worker/impl/KotlinWorkerImpl.scala | 21 ++ .../kotlinlib/worker/api/KotlinWorker.scala | 9 + 17 files changed, 671 insertions(+), 2 deletions(-) create mode 100644 kotlinlib/package.mill create mode 100644 kotlinlib/src/mill/kotlinlib/KotlinModule.scala create mode 100644 kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala create mode 100644 kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala create mode 100644 kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala create mode 100644 kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala create mode 100644 kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt create mode 100644 kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt create mode 100644 kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java create mode 100644 kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt create mode 100644 kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java create mode 100644 kotlinlib/test/src/mill/kotlinlib/HelloWorldTests.scala create mode 100644 kotlinlib/test/src/mill/kotlinlib/MixedHelloWorldTests.scala create mode 100644 kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala create mode 100644 kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ed862f35a90..4de4fa7ad94 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -58,11 +58,11 @@ jobs: # For most tests, run them arbitrarily on Java 11 or Java 17 on Linux, and # on the opposite version on Windows below, so we get decent coverage of # each test on each Java version and each operating system - # We also try to group tests together to manuaully balance out the runtimes of each jobs + # We also try to group tests together to manually balance out the runtimes of each jobs - java-version: 17 millargs: "'{main,scalalib,testrunner,bsp,testkit}.__.testCached'" - java-version: '11' - millargs: "'{scalajslib,scalanativelib}.__.testCached'" + millargs: "'{scalajslib,scalanativelib,kotlinlib}.__.testCached'" - java-version: 17 millargs: "contrib.__.testCached" diff --git a/build.mill b/build.mill index 820784d4f1b..99a53841460 100644 --- a/build.mill +++ b/build.mill @@ -188,6 +188,7 @@ object Deps { val requests = ivy"com.lihaoyi::requests:0.9.0" val logback = ivy"ch.qos.logback:logback-classic:1.5.7" val sonatypeCentralClient = ivy"com.lumidion::sonatype-central-client-requests:0.3.0" + val kotlinCompiler = ivy"org.jetbrains.kotlin:kotlin-compiler:1.9.24" object RuntimeDeps { val errorProneCore = ivy"com.google.errorprone:error_prone_core:2.31.0" @@ -757,6 +758,7 @@ object dist extends MillPublishJavaModule { genTask(build.main.eval)() ++ genTask(build.main)() ++ genTask(build.scalalib)() ++ + genTask(build.kotlinlib)() ++ genTask(build.scalajslib)() ++ genTask(build.scalanativelib)() diff --git a/kotlinlib/package.mill b/kotlinlib/package.mill new file mode 100644 index 00000000000..0430fb87ead --- /dev/null +++ b/kotlinlib/package.mill @@ -0,0 +1,36 @@ +package build.kotlinlib + +// imports +import mill._ +import mill.scalalib._ + +object `package` extends RootModule with build.MillStableScalaModule { + + def moduleDeps = Seq(build.main, build.scalalib, build.testrunner, worker) + def testTransitiveDeps = super.testTransitiveDeps() ++ Seq(worker.impl.testDep()) + + trait MillKotlinModule extends build.MillStableScalaModule { + override def javacOptions = { + val release = + if (scala.util.Properties.isJavaAtLeast(11)) Seq("-release", "8") + else Seq("-source", "1.8", "-target", "1.8") + release ++ Seq("-encoding", "UTF-8", "-deprecation") + } + } + + object worker extends MillKotlinModule { + def moduleDeps = Seq(build.main, build.testrunner) + + override def compileIvyDeps: T[Agg[Dep]] = Agg( + build.Deps.osLib + ) + + object impl extends MillKotlinModule with build.MillPublishScalaModule { + override def moduleDeps: Seq[PublishModule] = Seq(worker) + override def compileIvyDeps: T[Agg[Dep]] = Agg( + build.Deps.osLib, + build.Deps.kotlinCompiler + ) + } + } +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala new file mode 100644 index 00000000000..af2ec09c871 --- /dev/null +++ b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala @@ -0,0 +1,211 @@ +package mill +package kotlinlib + +import mill.api.{PathRef, Result} +import mill.define.{Command, Task} +import mill.kotlinlib.worker.api.KotlinWorker +import mill.scalalib.api.CompilationResult +import mill.scalalib.{Dep, DepSyntax, JavaModule, Lib} +import mill.{Agg, T} + +import java.io.File + +trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => + + /** + * All individual source files fed into the compiler. + */ + override def allSourceFiles = T { + Lib.findSourceFiles(allSources(), Seq("kt", "kts", "java")).map(PathRef(_)) + } + + /** + * All individual Java source files fed into the compiler. + * Subset of [[allSourceFiles]]. + */ + def allJavaSourceFiles = T { + allSourceFiles().filter(_.path.ext.toLowerCase() == "java") + } + + /** + * All individual Kotlin source files fed into the compiler. + * Subset of [[allSourceFiles]]. + */ + def allKotlinSourceFiles = T { + allSourceFiles().filter(path => Seq("kt", "kts").exists(path.path.ext.toLowerCase() == _)) + } + + /** + * The Kotlin version to be used (for API and Language level settings). + */ + def kotlinVersion: T[String] + + /** + * The dependencies of this module. + * Defaults to add the kotlin-stdlib dependency matching the [[kotlinVersion]]. + */ + override def ivyDeps: T[Agg[Dep]] = T { + super.ivyDeps() ++ Agg( + ivy"org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion()}" + ) + } + + /** + * The version of the Kotlin compiler to be used. + * Default is derived from [[kotlinVersion]]. + */ + def kotlinCompilerVersion: T[String] = T { kotlinVersion() } + + /** + * The Ivy/Coursier dependencies resembling the Kotlin compiler. + * Default is derived from [[kotlinCompilerVersion]]. + */ + override def kotlinCompilerIvyDeps: T[Agg[Dep]] = T { + Agg(ivy"org.jetbrains.kotlin:kotlin-compiler:${kotlinCompilerVersion()}") ++ +// ( +// if (Seq("1.0.", "1.1.", "1.2").exists(prefix => kotlinVersion().startsWith(prefix))) +// Agg(ivy"org.jetbrains.kotlin:kotlin-runtime:${kotlinCompilerVersion()}") +// else Seq() +// ) ++ + ( + if ( + !Seq("1.0.", "1.1.", "1.2.0", "1.2.1", "1.2.2", "1.2.3", "1.2.4").exists(prefix => + kotlinVersion().startsWith(prefix) + ) + ) + Agg(ivy"org.jetbrains.kotlin:kotlin-scripting-compiler:${kotlinCompilerVersion()}") + else Seq() + ) +// ivy"org.jetbrains.kotlin:kotlin-scripting-compiler-impl:${kotlinCompilerVersion()}", +// ivy"org.jetbrains.kotlin:kotlin-scripting-common:${kotlinCompilerVersion()}", + } + +// @Deprecated("Use kotlinWorkerTask instead, as this does not need to be cached as Worker") +// def kotlinWorker: Worker[KotlinWorker] = T.worker { +// kotlinWorkerTask() +// } + + def kotlinWorkerTask: Task[KotlinWorker] = T.task { + kotlinWorkerRef().kotlinWorkerManager().get(kotlinCompilerClasspath()) + } + + /** + * Compiles all the sources to JVM class files. + */ + override def compile: T[CompilationResult] = T { + kotlinCompileTask()() + } + + /** + * Runs the Kotlin compiler with the `-help` argument to show you the built-in cmdline help. + * You might want to add additional arguments like `-X` to see extra help. + */ + def kotlincHelp(args: String*): Command[Unit] = T.command { + kotlinCompileTask(Seq("-help") ++ args)() + () + } + + protected def when(cond: Boolean)(args: String*): Seq[String] = if (cond) args else Seq() + + /** + * The actual Kotlin compile task (used by [[compile]] and [[kotlincHelp]]). + */ + protected def kotlinCompileTask(extraKotlinArgs: Seq[String] = Seq()): Task[CompilationResult] = T.task { + val ctx = T.ctx() + val dest = ctx.dest + val classes = dest / "classes" + os.makeDir.all(classes) + + val javaSourceFiles = allJavaSourceFiles().map(_.path) + val kotlinSourceFiles = allKotlinSourceFiles().map(_.path) + + val isKotlin = kotlinSourceFiles.nonEmpty + val isJava = javaSourceFiles.nonEmpty + val isMixed = isKotlin && isJava + + val compileCp = compileClasspath().map(_.path).filter(os.exists) + val updateCompileOutput = upstreamCompileOutput() + + def compileJava: Result[CompilationResult] = { + ctx.log.info( + s"Compiling ${javaSourceFiles.size} Java sources to ${classes} ..." + ) + // The compile step is lazy, but its dependencies are not! + internalCompileJavaFiles( + worker = zincWorkerRef().worker(), + upstreamCompileOutput = updateCompileOutput, + javaSourceFiles = javaSourceFiles, + compileCp = compileCp, + javacOptions = javacOptions(), + compileProblemReporter = ctx.reporter(hashCode), + reportOldProblems = internalReportOldProblems() + ) + } + + if (isMixed || isKotlin) { + ctx.log.info( + s"Compiling ${kotlinSourceFiles.size} Kotlin sources to ${classes} ..." + ) + val compilerArgs: Seq[String] = Seq( + // destdir + Seq("-d", classes.toIO.getAbsolutePath()), + // classpath + when(compileCp.iterator.nonEmpty)("-classpath", compileCp.iterator.mkString(File.pathSeparator)), + kotlincOptions(), + extraKotlinArgs, + // parameters + (kotlinSourceFiles ++ javaSourceFiles).map(_.toIO.getAbsolutePath()) + ).flatten + + val workerResult = kotlinWorkerTask().compile(compilerArgs: _*) + + val analysisFile = dest / "kotlin.analysis.dummy" + os.write(target = analysisFile, data = "", createFolders = true) + + workerResult match { + case Result.Success(_) => + val cr = CompilationResult(analysisFile, PathRef(classes)) + if (!isJava) { + // pure Kotlin project + cr + } else { + // also run Java compiler and use it's returned result + compileJava + } + case Result.Failure(reason, _) => + Result.Failure(reason, Some(CompilationResult(analysisFile, PathRef(classes)))) + case e: Result.Exception => e + case Result.Aborted => Result.Aborted + case Result.Skipped => Result.Skipped + // case x => x + } + } else { + // it's Java only + compileJava + } + } + + /** + * Additional Kotlin compiler options to be used by [[compile]]. + */ + def kotlincOptions: T[Seq[String]] = T { + Seq("-no-stdlib") ++ + when(!kotlinVersion().startsWith("1.0"))( + "-language-version", + kotlinVersion().split("[.]", 3).take(2).mkString("."), + "-api-version", + kotlinVersion().split("[.]", 3).take(2).mkString(".") + ) + } + + /** + * A test sub-module linked to its parent module best suited for unit-tests. + */ + trait KotlinModuleTests extends JavaModuleTests with KotlinModule { + override def kotlinVersion: T[String] = T { outer.kotlinVersion() } + override def kotlinCompilerVersion: T[String] = T { outer.kotlinCompilerVersion() } + override def kotlincOptions: T[Seq[String]] = T { outer.kotlincOptions() } + override def defaultCommandName(): String = super.defaultCommandName() + } + +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala new file mode 100644 index 00000000000..9d59319fedf --- /dev/null +++ b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala @@ -0,0 +1,57 @@ +package mill.kotlinlib + +import mill.api.{PathRef, Result} +import mill.define.{ModuleRef, Task} +import mill.scalalib.api.{CompilationResult, ZincWorkerApi} +import mill.scalalib.{Dep, JavaModule, ZincWorkerModule} +import mill.util.Util.millProjectModule +import mill.{Agg, T} + +trait KotlinModulePlatform extends JavaModule { + + type CompileProblemReporter = mill.api.CompileProblemReporter + + protected def zincWorkerRef: ModuleRef[ZincWorkerModule] = zincWorker + + protected def kotlinWorkerRef: ModuleRef[KotlinWorkerModule] = ModuleRef(KotlinWorkerModule) + + def kotlinCompilerIvyDeps: T[Agg[Dep]] + + private[kotlinlib] def kotlinWorkerClasspath = T { + millProjectModule( + "mill-kotlinlib-worker-impl", + repositoriesTask(), + resolveFilter = _.toString.contains("mill-kotlinlib-worker-impl") + ) + } + + /** + * The Java classpath resembling the Kotlin compiler. + * Default is derived from [[kotlinCompilerIvyDeps]]. + */ + def kotlinCompilerClasspath: T[Seq[PathRef]] = T { + resolveDeps(T.task { kotlinCompilerIvyDeps().map(bindDependency()) })().toSeq ++ kotlinWorkerClasspath() + } + + private[kotlinlib] def internalCompileJavaFiles( + worker: ZincWorkerApi, + upstreamCompileOutput: Seq[CompilationResult], + javaSourceFiles: Seq[os.Path], + compileCp: Agg[os.Path], + javacOptions: Seq[String], + compileProblemReporter: Option[CompileProblemReporter], + reportOldProblems: Boolean + )(implicit ctx: ZincWorkerApi.Ctx): Result[CompilationResult] = { + worker.compileJava( + upstreamCompileOutput = upstreamCompileOutput, + sources = javaSourceFiles, + compileClasspath = compileCp, + javacOptions = javacOptions, + reporter = compileProblemReporter, + reportCachedProblems = reportOldProblems + ) + } + + private[kotlinlib] def internalReportOldProblems: Task[Boolean] = zincReportCachedProblems + +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala new file mode 100644 index 00000000000..390491ba335 --- /dev/null +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala @@ -0,0 +1,8 @@ +package mill.kotlinlib + +import mill.api.{Ctx, PathRef} +import mill.kotlinlib.worker.api.KotlinWorker + +trait KotlinWorkerManager { + def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala new file mode 100644 index 00000000000..d4c6458b634 --- /dev/null +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala @@ -0,0 +1,66 @@ +package mill.kotlinlib + +import mill.PathRef +import mill.api.Ctx +import mill.kotlinlib.worker.api.KotlinWorker + +import java.net.{URL, URLClassLoader} + +class KotlinWorkerManagerImpl(ctx: Ctx) extends KotlinWorkerManager with AutoCloseable { + + private[this] var workerCache: Map[Seq[PathRef], (KotlinWorker, Int)] = Map.empty + + override def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker = { + val toolsCp = toolsClasspath.distinct + val (worker, count) = workerCache.get(toolsCp) match { + case Some((w, count)) => + ctx.log.debug(s"Reusing existing AspectjWorker for classpath: ${toolsCp}") + w -> count + case None => + ctx.log.debug(s"Creating Classloader with classpath: [${toolsCp}]") + val classLoader = new URLClassLoader( + toolsCp.map(_.path.toNIO.toUri().toURL()).toArray[URL], + getClass().getClassLoader() + ) + + val className = + classOf[KotlinWorker].getPackage().getName().split("\\.").dropRight(1).mkString( + "." + ) + ".impl." + classOf[KotlinWorker].getSimpleName() + "Impl" + ctx.log.debug(s"Creating ${className} from classpath: ${toolsCp}") + val impl = classLoader.loadClass(className) + val worker = impl.getConstructor().newInstance().asInstanceOf[KotlinWorker] + if (worker.getClass().getClassLoader() != classLoader) { + ctx.log.error( + """Worker not loaded from worker classloader. + |You should not add the mill-kotlin-worker JAR to the mill build classpath""".stripMargin + ) + } + if (worker.getClass().getClassLoader() == classOf[KotlinWorker].getClassLoader()) { + ctx.log.error("Worker classloader used to load interface and implementation") + } + worker -> 0 + } + workerCache += toolsCp -> (worker -> (1 + count)) + ctx.log.debug(stats()) + worker + } + + def stats(): String = { + s"""Cache statistics of ${this.toString()}: + |${ + workerCache.map { case (cp, (worker, count)) => + s"""- worker: ${worker.toString()} + | used: ${count} + |""".stripMargin + }.mkString + }""".stripMargin + } + + override def close(): Unit = { + ctx.log.debug(stats()) + + // We drop cached worker instances + workerCache = Map.empty + } +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala new file mode 100644 index 00000000000..4aa8cb3a469 --- /dev/null +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala @@ -0,0 +1,14 @@ +package mill.kotlinlib + +import mill.T +import mill.define.{Discover, ExternalModule, Module, Worker} + +trait KotlinWorkerModule extends Module { + def kotlinWorkerManager: Worker[KotlinWorkerManager] = T.worker { + new KotlinWorkerManagerImpl(T.ctx()) + } +} + +object KotlinWorkerModule extends ExternalModule with KotlinWorkerModule { + override def millDiscover: Discover = Discover[this.type] +} diff --git a/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt b/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt new file mode 100644 index 00000000000..be8ad86b551 --- /dev/null +++ b/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt @@ -0,0 +1,10 @@ +package hello + +fun getHelloString() : String { + return "Hello, world!" +} + +fun main(args : Array) { + println(getHelloString()) +} + diff --git a/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt b/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt new file mode 100644 index 00000000000..dfaa21d8757 --- /dev/null +++ b/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt @@ -0,0 +1,15 @@ +package hello.tests + +import hello.getHelloString +import kotlin.test.assertEquals +import org.junit.Test + +class HelloTest { + @Test fun testSuccess() : Unit { + assertEquals("Hello, world!", getHelloString()) + } + @Test fun testFailure() : Unit { + assertEquals("world!", getHelloString()) + } +} + diff --git a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java new file mode 100644 index 00000000000..7d5ccf96d1e --- /dev/null +++ b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java @@ -0,0 +1,14 @@ +package hello; + +public class JavaHello { + public static String JavaHelloString = "Hello from Java!"; + + public static String getHelloStringFromKotlin() { + return KotlinHelloKt.getKotlinHelloString(); + } + + public static void main(String[] args) { + System.out.println(getHelloStringFromKotlin()); + System.out.println(KotlinHelloKt.getHelloStringFromJava()); + } +} diff --git a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt new file mode 100644 index 00000000000..302e3df73b8 --- /dev/null +++ b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt @@ -0,0 +1,7 @@ +package hello + +val KotlinHelloString : String = "Hello from Kotlin!" + +fun getHelloStringFromJava() : String { + return JavaHello.JavaHelloString!!; +} \ No newline at end of file diff --git a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java new file mode 100644 index 00000000000..ae913627e5f --- /dev/null +++ b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java @@ -0,0 +1,16 @@ +package hello.tests; + +import hello.JavaHello; +import junit.framework.TestCase; + +public class HelloTest extends TestCase { + public void testSuccess() { + assertEquals("Hello from Kotlin!", JavaHello.getHelloStringFromKotlin()); + assertEquals("Hello from Java!", hello.KotlinHelloKt.getHelloStringFromJava()); + } + + public void testFailure() { + assertEquals("Hello from Java!", JavaHello.getHelloStringFromKotlin()); + assertEquals("Hello from Kotlin!", hello.KotlinHelloKt.getHelloStringFromJava()); + } +} diff --git a/kotlinlib/test/src/mill/kotlinlib/HelloWorldTests.scala b/kotlinlib/test/src/mill/kotlinlib/HelloWorldTests.scala new file mode 100644 index 00000000000..6b0d3476e19 --- /dev/null +++ b/kotlinlib/test/src/mill/kotlinlib/HelloWorldTests.scala @@ -0,0 +1,90 @@ +package mill +package kotlinlib + +import mill.scalalib.{DepSyntax, TestModule} +import mill.testkit.{TestBaseModule, UnitTester} +import mill.api.Result +import utest._ + +object HelloWorldTests extends TestSuite { + + val kotlinVersions = if (scala.util.Properties.isJavaAtLeast(9)) { + Seq("1.9.24", "2.0.20") + } else { + Seq("1.0.0", "1.9.24", "2.0.20") + } + + object HelloWorldKotlin extends TestBaseModule { + trait MainCross extends KotlinModule with Cross.Module[String] { + def kotlinVersion = crossValue + override def mainClass = Some("hello.HelloKt") + + object test extends KotlinModuleTests with TestModule.Junit4 { + override def ivyDeps = super.ivyDeps() ++ Agg( + ivy"org.jetbrains.kotlin:kotlin-test-junit:${this.kotlinVersion()}" + ) + } + } + object main extends Cross[MainCross](kotlinVersions) + } + + val resourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "hello-world-kotlin" + + def testEval() = UnitTester(HelloWorldKotlin, resourcePath) + def tests: Tests = Tests { + test("compile") { + val eval = testEval() + + HelloWorldKotlin.main.crossModules.foreach(m => { + val Right(result) = eval.apply(m.compile) + + assert( + os.walk(result.value.classes.path).exists(_.last == "HelloKt.class") + ) + }) + } + test("testCompile") { + val eval = testEval() + + HelloWorldKotlin.main.crossModules.foreach(m => { + val Right(result1) = eval.apply(m.test.compile) + + assert( + os.walk(result1.value.classes.path).exists(_.last == "HelloTest.class") + ) + }) + } + test("test") { + val eval = testEval() + + HelloWorldKotlin.main.crossModules.foreach(m => { + val Left(Result.Failure(_, Some(v1))) = eval.apply(m.test.test()) + + assert( + v1._2(0).fullyQualifiedName == "hello.tests.HelloTest.testFailure", + v1._2(0).status == "Failure", + v1._2(1).fullyQualifiedName == "hello.tests.HelloTest.testSuccess", + v1._2(1).status == "Success" + ) + }) + } + test("failures") { + val eval = testEval() + + val mainJava = HelloWorldKotlin.millSourcePath / "main" / "src" / "Hello.kt" + + HelloWorldKotlin.main.crossModules.foreach(m => { + + val Right(_) = eval.apply(m.compile) + + os.write.over(mainJava, os.read(mainJava) + "}") + + val Left(_) = eval.apply(m.compile) + + os.write.over(mainJava, os.read(mainJava).dropRight(1)) + + val Right(_) = eval.apply(m.compile) + }) + } + } +} diff --git a/kotlinlib/test/src/mill/kotlinlib/MixedHelloWorldTests.scala b/kotlinlib/test/src/mill/kotlinlib/MixedHelloWorldTests.scala new file mode 100644 index 00000000000..c3a67f82f60 --- /dev/null +++ b/kotlinlib/test/src/mill/kotlinlib/MixedHelloWorldTests.scala @@ -0,0 +1,93 @@ +package mill +package kotlinlib + +import mill.api.Result +import mill.scalalib.{DepSyntax, TestModule} +import mill.testkit.{TestBaseModule, UnitTester} +import utest._ + +object MixedHelloWorldTests extends TestSuite { + + val kotlinVersions = if (scala.util.Properties.isJavaAtLeast(9)) { + Seq("1.9.24", "2.0.20") + } else { + Seq("1.0.0", "1.9.24", "2.0.20") + } + + object MixedHelloWorldKotlin extends TestBaseModule { + trait MainCross extends KotlinModule with Cross.Module[String] { + def kotlinVersion = crossValue + override def mainClass = Some("hello.JavaHello") + + object test extends KotlinModuleTests with TestModule.Junit4 { + override def ivyDeps = super.ivyDeps() ++ Agg( + ivy"org.jetbrains.kotlin:kotlin-test-junit:${this.kotlinVersion()}" + ) + } + } + object main extends Cross[MainCross](kotlinVersions) + } + + val resourcePath = + os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "mixed-code-hello-world-kotlin" + + def testEval() = UnitTester(MixedHelloWorldKotlin, resourcePath) + def tests: Tests = Tests { + test("compile") { + val eval = testEval() + + MixedHelloWorldKotlin.main.crossModules.foreach(m => { + val Right(result) = eval.apply(m.compile) + + assert( + os.walk(result.value.classes.path).exists(_.last == "KotlinHelloKt.class"), + os.walk(result.value.classes.path).exists(_.last == "JavaHello.class") + ) + }) + } + test("testCompile") { + val eval = testEval() + + MixedHelloWorldKotlin.main.crossModules.foreach(m => { + val Right(result1) = eval.apply(m.test.compile) + + assert( + os.walk(result1.value.classes.path).exists(_.last == "HelloTest.class") + ) + }) + } + test("test") { + val eval = testEval() + MixedHelloWorldKotlin.main.crossModules.foreach(m => { + + val Left(Result.Failure(_, Some(v1))) = eval.apply(m.test.test()) + + assert( + v1._2(0).fullyQualifiedName == "hello.tests.HelloTest.testFailure", + v1._2(0).status == "Failure", + v1._2(1).fullyQualifiedName == "hello.tests.HelloTest.testSuccess", + v1._2(1).status == "Success" + ) + }) + } + test("failures") { + val eval = testEval() + + MixedHelloWorldKotlin.main.crossModules.foreach(m => { + + val mainJava = + MixedHelloWorldKotlin.millSourcePath / "main" / "src" / "hello" / "KotlinHello.kt" + + val Right(_) = eval.apply(m.compile) + + os.write.over(mainJava, os.read(mainJava) + "}") + + val Left(_) = eval.apply(m.compile) + + os.write.over(mainJava, os.read(mainJava).dropRight(1)) + + val Right(_) = eval.apply(m.compile) + }) + } + } +} diff --git a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala new file mode 100644 index 00000000000..c4e36e85af5 --- /dev/null +++ b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala @@ -0,0 +1,21 @@ +package mill.kotlinlib.worker.impl + +import mill.api.{Ctx, Result} +import mill.kotlinlib.worker.api.KotlinWorker +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler + +class KotlinWorkerImpl extends KotlinWorker { + + def compile(args: String*)(implicit ctx: Ctx): Result[Unit] = { + ctx.log.debug("Using kotlin compiler arguments: " + args.map(v => s"'${v}'").mkString(" ")) + + val compiler = new K2JVMCompiler() + val exitCode = compiler.exec(ctx.log.errorStream, args: _*) + if (exitCode.getCode() != 0) { + Result.Failure(s"Kotlin compiler failed with exit code ${exitCode.getCode()} (${exitCode})") + } else { + Result.Success(()) + } + } + +} diff --git a/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala b/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala new file mode 100644 index 00000000000..e8efbd01faa --- /dev/null +++ b/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala @@ -0,0 +1,9 @@ +package mill.kotlinlib.worker.api + +import mill.api.{Ctx, Result} + +trait KotlinWorker { + + def compile(args: String*)(implicit ctx: Ctx): Result[Unit] + +} From da994646269751f281b67f9b07a748f4ca806d53 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:23:41 +0200 Subject: [PATCH 02/13] Add basic examples for Kotlin integration --- .github/workflows/run-tests.yml | 2 + example/kotlinlib/basic/1-simple/build.mill | 101 ++++++++++++++++++ .../kotlinlib/basic/1-simple/src/foo/Foo.kt | 30 ++++++ .../basic/1-simple/test/src/foo/FooTest.kt | 18 ++++ .../basic/2-custom-build-logic/build.mill | 23 ++++ .../basic/2-custom-build-logic/src/foo/Foo.kt | 17 +++ .../test/src/foo/FooTests.kt | 15 +++ .../basic/3-multi-module/bar/src/bar/Bar.kt | 14 +++ .../bar/test/src/bar/BarTests.kt | 20 ++++ .../kotlinlib/basic/3-multi-module/build.mill | 54 ++++++++++ .../basic/3-multi-module/foo/src/foo/Foo.kt | 25 +++++ .../4-builtin-commands/bar/src/bar/Bar.kt | 14 +++ .../bar/test/src/bar/BarTests.kt | 20 ++++ .../basic/4-builtin-commands/build.mill | 26 +++++ .../4-builtin-commands/foo/src/foo/Foo.kt | 27 +++++ example/package.mill | 5 +- runner/package.mill | 1 + 17 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 example/kotlinlib/basic/1-simple/build.mill create mode 100644 example/kotlinlib/basic/1-simple/src/foo/Foo.kt create mode 100644 example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt create mode 100644 example/kotlinlib/basic/2-custom-build-logic/build.mill create mode 100644 example/kotlinlib/basic/2-custom-build-logic/src/foo/Foo.kt create mode 100644 example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt create mode 100644 example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt create mode 100644 example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt create mode 100644 example/kotlinlib/basic/3-multi-module/build.mill create mode 100644 example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt create mode 100644 example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt create mode 100644 example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt create mode 100644 example/kotlinlib/basic/4-builtin-commands/build.mill create mode 100644 example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 4de4fa7ad94..777268dbc1d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -70,6 +70,8 @@ jobs: millargs: "'example.javalib.__.local.testCached'" - java-version: 17 millargs: "'example.scalalib.__.local.testCached'" + - java-version: 17 + millargs: "'example.kotlinlib.__.local.testCached'" - java-version: '11' millargs: "'example.thirdparty[{mockito,acyclic,commons-io}].local.testCached'" - java-version: 17 diff --git a/example/kotlinlib/basic/1-simple/build.mill b/example/kotlinlib/basic/1-simple/build.mill new file mode 100644 index 00000000000..c3140150996 --- /dev/null +++ b/example/kotlinlib/basic/1-simple/build.mill @@ -0,0 +1,101 @@ +//// SNIPPET:BUILD +package build +import mill._, kotlinlib._, scalalib._ + +object `package` extends RootModule with KotlinModule { + + def kotlinVersion = "1.9.24" + + def mainClass = Some("foo.FooKt") + + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", + ivy"org.apache.commons:commons-text:1.12.0" + ) + + object test extends KotlinModuleTests with TestModule.Junit4{ + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"com.google.guava:guava:33.3.0-jre" + ) + } +} + +// This is a basic Mill build for a single `KotlinModule`, with two +// third-party dependencies and a test suite using the JUnit framework. As a +// single-module project, it `extends RootModule` to mark `object foo` as the +// top-level module in the build. This lets us directly perform operations +// `./mill compile` or `./mill run` without needing to prefix it as +// `foo.compile` or `foo.run`. +// +//// SNIPPET:DEPENDENCIES +// +// This example project uses two third-party dependencies - ArgParse4J for CLI +// argument parsing, Apache Commons Text for HTML escaping - and uses them to wrap a +// given input string in HTML templates with proper escaping. +// +// You can run `assembly` to generate a standalone executable jar, which then +// can be run from the command line or deployed to be run elsewhere. + +/** Usage + +> ./mill resolve _ # List what tasks are available to run +assembly +... +clean +... +compile +... +run +... +show +... +inspect +... + +> ./mill inspect compile # Show documentation and inputs of a task +compile(KotlinModule...) + Compiles all the sources to JVM class files. + Compiles the current module to generate compiled classfiles/bytecode. + When you override this, you probably also want/need to override [[bspCompileClassesPath]], + as that needs to point to the same compilation output path. + Keep in sync with [[bspCompileClassesPath]] +Inputs: + allJavaSourceFiles + allKotlinSourceFiles + compileClasspath + upstreamCompileOutput + javacOptions + zincReportCachedProblems + kotlincOptions + kotlinCompilerClasspath +... + +> ./mill compile # compile sources into classfiles +... +Compiling 1 Kotlin sources to... + +> ./mill run # run the main method, if any +error: argument -t/--text is required +... + +> ./mill run --text hello +

hello

+ +> ./mill test +... +Test foo.FooTest.testEscaping finished, ... +Test foo.FooTest.testSimple finished, ... +Test run foo.FooTest finished: 0 failed, 0 ignored, 2 total, ... + +> ./mill assembly # bundle classfiles and libraries into a jar for deployment + +> ./mill show assembly # show the output of the assembly task +".../out/assembly.dest/out.jar" + +> java -jar ./out/assembly.dest/out.jar --text hello +

hello

+ +> ./out/assembly.dest/out.jar --text hello # mac/linux +

hello

+ +*/ diff --git a/example/kotlinlib/basic/1-simple/src/foo/Foo.kt b/example/kotlinlib/basic/1-simple/src/foo/Foo.kt new file mode 100644 index 00000000000..b6ac5464d3d --- /dev/null +++ b/example/kotlinlib/basic/1-simple/src/foo/Foo.kt @@ -0,0 +1,30 @@ +package foo + +import org.apache.commons.text.StringEscapeUtils +import net.sourceforge.argparse4j.ArgumentParsers +import net.sourceforge.argparse4j.inf.ArgumentParser +import net.sourceforge.argparse4j.inf.ArgumentParserException +import net.sourceforge.argparse4j.inf.Namespace + +fun generateHtml(text: String): String { + return "

" + StringEscapeUtils.escapeHtml4(text) + "

" +} + +fun main(args: Array) { + val parser = ArgumentParsers.newFor("template").build() + .defaultHelp(true) + .description("Inserts text into a HTML template") + + parser.addArgument("-t", "--text") + .required(true) + .help("text to insert") + + try { + parser.parseArgs(args).let { + println(generateHtml(it.getString("text"))) + } + } catch (e: Exception) { + println(e.message) + System.exit(1) + } +} diff --git a/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt b/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt new file mode 100644 index 00000000000..012922af22c --- /dev/null +++ b/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt @@ -0,0 +1,18 @@ +package foo + +import com.google.common.html.HtmlEscapers.htmlEscaper +import foo.generateHtml +import org.junit.Assert.assertEquals +import org.junit.Test + +class FooTest { + @Test + fun testSimple() { + assertEquals(generateHtml("hello"), "

hello

") + } + + @Test + fun testEscaping() { + assertEquals(generateHtml(""), "

" + htmlEscaper().escape("") + "

") + } +} diff --git a/example/kotlinlib/basic/2-custom-build-logic/build.mill b/example/kotlinlib/basic/2-custom-build-logic/build.mill new file mode 100644 index 00000000000..1a8a86541c2 --- /dev/null +++ b/example/kotlinlib/basic/2-custom-build-logic/build.mill @@ -0,0 +1,23 @@ +//// SNIPPET:BUILD +package build +import mill._, kotlinlib._, scalalib._ + +object `package` extends RootModule with KotlinModule { + + def kotlinVersion = "1.9.24" + + def mainClass = Some("foo.FooKt") + + /** Total number of lines in module's source files */ + def lineCount = T{ + allSourceFiles().map(f => os.read.lines(f.path).size).sum + } + + /** Generate resources using lineCount of sources */ + override def resources = T{ + os.write(T.dest / "line-count.txt", "" + lineCount()) + Seq(PathRef(T.dest)) + } + + object test extends KotlinModuleTests with TestModule.Junit4 +} diff --git a/example/kotlinlib/basic/2-custom-build-logic/src/foo/Foo.kt b/example/kotlinlib/basic/2-custom-build-logic/src/foo/Foo.kt new file mode 100644 index 00000000000..f40a128ef3c --- /dev/null +++ b/example/kotlinlib/basic/2-custom-build-logic/src/foo/Foo.kt @@ -0,0 +1,17 @@ +package foo + +import java.io.IOException + +fun getLineCount(): String? { + return try { + String( + ::main.javaClass.classLoader.getResourceAsStream("line-count.txt").readAllBytes() + ) + } catch (e: IOException) { + null + } +} + +fun main() { + println("Line Count: " + getLineCount()) +} diff --git a/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt b/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt new file mode 100644 index 00000000000..82572ca6ac3 --- /dev/null +++ b/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt @@ -0,0 +1,15 @@ +package foo + +import org.junit.Assert.assertEquals +import org.junit.Test +import foo.getLineCount + +class FooTests { + + @Test + fun testSimple() { + val expectedLineCount = 12 + val actualLineCount = getLineCount()?.trim().let { Integer.parseInt(it) } + assertEquals(expectedLineCount, actualLineCount) + } +} diff --git a/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt b/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt new file mode 100644 index 00000000000..37008cc1b32 --- /dev/null +++ b/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt @@ -0,0 +1,14 @@ +package bar + +import org.apache.commons.text.StringEscapeUtils +import net.sourceforge.argparse4j.ArgumentParsers +import net.sourceforge.argparse4j.inf.ArgumentParser +import net.sourceforge.argparse4j.inf.Namespace + +fun generateHtml(text: String): String { + return "

" + StringEscapeUtils.escapeHtml4(text) + "

" +} + +fun main(args: Array) { + println("Bar.value: " + generateHtml(args[0])) +} diff --git a/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt b/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt new file mode 100644 index 00000000000..d36e2d2b08a --- /dev/null +++ b/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt @@ -0,0 +1,20 @@ +package bar + +import bar.generateHtml +import org.junit.Assert.assertEquals +import org.junit.Test + +class BarTests { + + @Test + fun simple() { + val result = generateHtml("hello") + assertEquals("

hello

", result) + } + + @Test + fun escaping() { + val result = generateHtml("") + assertEquals("

<hello>

", result) + } +} diff --git a/example/kotlinlib/basic/3-multi-module/build.mill b/example/kotlinlib/basic/3-multi-module/build.mill new file mode 100644 index 00000000000..3758e8b001e --- /dev/null +++ b/example/kotlinlib/basic/3-multi-module/build.mill @@ -0,0 +1,54 @@ +//// SNIPPET:BUILD +package build +import mill._, kotlinlib._, scalalib._ + +trait MyModule extends KotlinModule { + + def kotlinVersion = "1.9.24" + +} + +object foo extends MyModule { + def mainClass = Some("foo.FooKt") + def moduleDeps = Seq(bar) + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"net.sourceforge.argparse4j:argparse4j:0.9.0" + ) +} + +object bar extends MyModule { + def mainClass = Some("bar.BarKt") + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", + ivy"org.apache.commons:commons-text:1.12.0" + ) + + object test extends KotlinModuleTests with TestModule.Junit4 +} + +//// SNIPPET:TREE +// ---- +// build.mill +// foo/ +// src/ +// foo/ +// Foo.java +// resources/ +// ... +// bar/ +// src/ +// bar/ +// Bar.java +// resources/ +// ... +// out/ +// foo/ +// compile.json +// compile.dest/ +// ... +// bar/ +// compile.json +// compile.dest/ +// ... +// ---- +// diff --git a/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt b/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt new file mode 100644 index 00000000000..d985a2ce933 --- /dev/null +++ b/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt @@ -0,0 +1,25 @@ +package foo + +import net.sourceforge.argparse4j.ArgumentParsers +import net.sourceforge.argparse4j.inf.ArgumentParser +import net.sourceforge.argparse4j.inf.Namespace + +const val VALUE: String = "hello" + +fun mainFunction(fooText: String, barText: String) { + println("Foo.value: " + VALUE) + println("Bar.value: " + bar.generateHtml(barText)) +} + +fun main(args: Array) { + val parser = ArgumentParsers.newFor("Foo").build() + parser.addArgument("--foo-text").required(true) + parser.addArgument("--bar-text").required(true) + + val res = parser.parseArgs(args) + + val fooText = res.getString("foo_text") + val barText = res.getString("bar_text") + + mainFunction(fooText, barText) +} diff --git a/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt b/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt new file mode 100644 index 00000000000..9d14172709a --- /dev/null +++ b/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt @@ -0,0 +1,14 @@ +package bar + +import org.apache.commons.text.StringEscapeUtils +import net.sourceforge.argparse4j.ArgumentParsers +import net.sourceforge.argparse4j.inf.ArgumentParser +import net.sourceforge.argparse4j.inf.Namespace + +fun generateHtml(text: String): String { + return "

" + StringEscapeUtils.escapeHtml4("world") + "

" +} + +fun main(args: Array) { + println("Bar.value: " + generateHtml(args[0])) +} \ No newline at end of file diff --git a/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt b/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt new file mode 100644 index 00000000000..4897ffed908 --- /dev/null +++ b/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt @@ -0,0 +1,20 @@ +package bar + +import bar.generateHtml +import org.junit.Assert.assertEquals +import org.junit.Test + +public class BarTests { + + @Test + fun testSimple() { + val result = generateHtml("hello") + assertEquals("

hello

", result) + } + + @Test + fun testEscaping() { + val result = generateHtml("") + assertEquals("

<hello>

", result) + } +} \ No newline at end of file diff --git a/example/kotlinlib/basic/4-builtin-commands/build.mill b/example/kotlinlib/basic/4-builtin-commands/build.mill new file mode 100644 index 00000000000..d118ec168ad --- /dev/null +++ b/example/kotlinlib/basic/4-builtin-commands/build.mill @@ -0,0 +1,26 @@ +//// SNIPPET:BUILD +package build +import mill._, kotlinlib._, scalalib._ + +trait MyModule extends KotlinModule { + + def kotlinVersion = "1.9.24" +} + +object foo extends MyModule { + def mainClass = Some("foo.FooKt") + def moduleDeps = Seq(bar) + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"net.sourceforge.argparse4j:argparse4j:0.9.0" + ) +} + +object bar extends MyModule { + def mainClass = Some("bar.BarKt") + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", + ivy"org.apache.commons:commons-text:1.12.0" + ) + + object test extends KotlinModuleTests with TestModule.Junit4 +} diff --git a/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt b/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt new file mode 100644 index 00000000000..1f9c8f10cb9 --- /dev/null +++ b/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt @@ -0,0 +1,27 @@ +package foo + +import net.sourceforge.argparse4j.ArgumentParsers +import net.sourceforge.argparse4j.inf.ArgumentParser +import net.sourceforge.argparse4j.inf.Namespace + + +const val VALUE: String = "hello" + +fun mainFunction(fooText: String, barText: String) { + println("Foo.value: " + VALUE) + println("Bar.value: " + bar.generateHtml(barText)) +} + +fun main(args: Array) { + val parser = ArgumentParsers.newFor("Foo").build() + parser.addArgument("--foo-text").required(true) + parser.addArgument("--bar-text").required(true) + + val res = parser.parseArgs(args) + + val fooText = res.getString("foo_text") + val barText = res.getString("bar_text") + + mainFunction(fooText, barText) +} + diff --git a/example/package.mill b/example/package.mill index 0ab4f788c8b..89e94bf7a31 100644 --- a/example/package.mill +++ b/example/package.mill @@ -35,6 +35,9 @@ object `package` extends RootModule with Module { object module extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "module")) object web extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "web")) } + object kotlinlib extends Module { + object basic extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "basic")) + } object scalalib extends Module { object basic extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "basic")) object builds extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "builds")) @@ -79,7 +82,7 @@ object `package` extends RootModule with Module { case Some(upstream) => T{ os.copy.over(super.testRepoRoot().path, T.dest) val upstreamRoot = upstream.testRepoRoot().path - val suffix = Seq("build.mill", "build.mill").find(s => os.exists(upstreamRoot / s)).head + val suffix = Seq("build.sc", "build.mill").find(s => os.exists(upstreamRoot / s)).head for(lines <- buildScLines()) { os.write.over(T.dest / suffix, lines.mkString("\n")) } diff --git a/runner/package.mill b/runner/package.mill index f27e1a26b0a..c30b9ebd2d9 100644 --- a/runner/package.mill +++ b/runner/package.mill @@ -11,6 +11,7 @@ object `package` extends RootModule with build.MillPublishScalaModule { def moduleDeps = Seq( build.scalalib, + build.kotlinlib, build.scalajslib, build.scalanativelib, build.bsp, From 772484a07f1ef8164417ee180f85325e02e5936a Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Wed, 11 Sep 2024 21:18:26 +0200 Subject: [PATCH 03/13] Use mandatoryIvyDeps for hosting Kotlin stblib --- example/kotlinlib/basic/1-simple/build.mill | 2 +- example/kotlinlib/basic/3-multi-module/build.mill | 4 ++-- example/kotlinlib/basic/4-builtin-commands/build.mill | 4 ++-- kotlinlib/src/mill/kotlinlib/KotlinModule.scala | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/kotlinlib/basic/1-simple/build.mill b/example/kotlinlib/basic/1-simple/build.mill index c3140150996..dcc985ff611 100644 --- a/example/kotlinlib/basic/1-simple/build.mill +++ b/example/kotlinlib/basic/1-simple/build.mill @@ -8,7 +8,7 @@ object `package` extends RootModule with KotlinModule { def mainClass = Some("foo.FooKt") - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = Agg( ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", ivy"org.apache.commons:commons-text:1.12.0" ) diff --git a/example/kotlinlib/basic/3-multi-module/build.mill b/example/kotlinlib/basic/3-multi-module/build.mill index 3758e8b001e..8b188da2fe8 100644 --- a/example/kotlinlib/basic/3-multi-module/build.mill +++ b/example/kotlinlib/basic/3-multi-module/build.mill @@ -11,14 +11,14 @@ trait MyModule extends KotlinModule { object foo extends MyModule { def mainClass = Some("foo.FooKt") def moduleDeps = Seq(bar) - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = Agg( ivy"net.sourceforge.argparse4j:argparse4j:0.9.0" ) } object bar extends MyModule { def mainClass = Some("bar.BarKt") - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = Agg( ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", ivy"org.apache.commons:commons-text:1.12.0" ) diff --git a/example/kotlinlib/basic/4-builtin-commands/build.mill b/example/kotlinlib/basic/4-builtin-commands/build.mill index d118ec168ad..feb86c3e0e6 100644 --- a/example/kotlinlib/basic/4-builtin-commands/build.mill +++ b/example/kotlinlib/basic/4-builtin-commands/build.mill @@ -10,14 +10,14 @@ trait MyModule extends KotlinModule { object foo extends MyModule { def mainClass = Some("foo.FooKt") def moduleDeps = Seq(bar) - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = Agg( ivy"net.sourceforge.argparse4j:argparse4j:0.9.0" ) } object bar extends MyModule { def mainClass = Some("bar.BarKt") - def ivyDeps = super.ivyDeps() ++ Agg( + def ivyDeps = Agg( ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", ivy"org.apache.commons:commons-text:1.12.0" ) diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala index af2ec09c871..92941789c2b 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala @@ -44,8 +44,8 @@ trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => * The dependencies of this module. * Defaults to add the kotlin-stdlib dependency matching the [[kotlinVersion]]. */ - override def ivyDeps: T[Agg[Dep]] = T { - super.ivyDeps() ++ Agg( + override def mandatoryIvyDeps: T[Agg[Dep]] = T { + super.mandatoryIvyDeps() ++ Agg( ivy"org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion()}" ) } From 27628510223196c9c58c87bcb697c69ca26afa7b Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Wed, 11 Sep 2024 22:57:33 +0200 Subject: [PATCH 04/13] Use Clikt for agrument parsing in Kotlin examples --- example/kotlinlib/basic/1-simple/build.mill | 6 ++-- .../kotlinlib/basic/1-simple/src/foo/Foo.kt | 34 +++++++------------ .../basic/3-multi-module/bar/src/bar/Bar.kt | 3 -- .../kotlinlib/basic/3-multi-module/build.mill | 3 +- .../basic/3-multi-module/foo/src/foo/Foo.kt | 28 +++++++-------- .../4-builtin-commands/bar/src/bar/Bar.kt | 3 -- .../basic/4-builtin-commands/build.mill | 3 +- .../4-builtin-commands/foo/src/foo/Foo.kt | 29 +++++++--------- 8 files changed, 43 insertions(+), 66 deletions(-) diff --git a/example/kotlinlib/basic/1-simple/build.mill b/example/kotlinlib/basic/1-simple/build.mill index dcc985ff611..18854865068 100644 --- a/example/kotlinlib/basic/1-simple/build.mill +++ b/example/kotlinlib/basic/1-simple/build.mill @@ -9,7 +9,7 @@ object `package` extends RootModule with KotlinModule { def mainClass = Some("foo.FooKt") def ivyDeps = Agg( - ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", + ivy"com.github.ajalt.clikt:clikt-jvm:4.4.0", ivy"org.apache.commons:commons-text:1.12.0" ) @@ -29,7 +29,7 @@ object `package` extends RootModule with KotlinModule { // //// SNIPPET:DEPENDENCIES // -// This example project uses two third-party dependencies - ArgParse4J for CLI +// This example project uses two third-party dependencies - Clikt for CLI // argument parsing, Apache Commons Text for HTML escaping - and uses them to wrap a // given input string in HTML templates with proper escaping. // @@ -75,7 +75,7 @@ Inputs: Compiling 1 Kotlin sources to... > ./mill run # run the main method, if any -error: argument -t/--text is required +error: Error: missing option --text ... > ./mill run --text hello diff --git a/example/kotlinlib/basic/1-simple/src/foo/Foo.kt b/example/kotlinlib/basic/1-simple/src/foo/Foo.kt index b6ac5464d3d..4d605d4f9ad 100644 --- a/example/kotlinlib/basic/1-simple/src/foo/Foo.kt +++ b/example/kotlinlib/basic/1-simple/src/foo/Foo.kt @@ -1,30 +1,20 @@ package foo import org.apache.commons.text.StringEscapeUtils -import net.sourceforge.argparse4j.ArgumentParsers -import net.sourceforge.argparse4j.inf.ArgumentParser -import net.sourceforge.argparse4j.inf.ArgumentParserException -import net.sourceforge.argparse4j.inf.Namespace +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required + +class Foo: CliktCommand() { + val text by option("-t", "--text", help="text to insert").required() + + override fun run() { + echo(generateHtml(text)) + } +} fun generateHtml(text: String): String { return "

" + StringEscapeUtils.escapeHtml4(text) + "

" } -fun main(args: Array) { - val parser = ArgumentParsers.newFor("template").build() - .defaultHelp(true) - .description("Inserts text into a HTML template") - - parser.addArgument("-t", "--text") - .required(true) - .help("text to insert") - - try { - parser.parseArgs(args).let { - println(generateHtml(it.getString("text"))) - } - } catch (e: Exception) { - println(e.message) - System.exit(1) - } -} +fun main(args: Array) = Foo().main(args) diff --git a/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt b/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt index 37008cc1b32..ea5d1cec31c 100644 --- a/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt +++ b/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt @@ -1,9 +1,6 @@ package bar import org.apache.commons.text.StringEscapeUtils -import net.sourceforge.argparse4j.ArgumentParsers -import net.sourceforge.argparse4j.inf.ArgumentParser -import net.sourceforge.argparse4j.inf.Namespace fun generateHtml(text: String): String { return "

" + StringEscapeUtils.escapeHtml4(text) + "

" diff --git a/example/kotlinlib/basic/3-multi-module/build.mill b/example/kotlinlib/basic/3-multi-module/build.mill index 8b188da2fe8..fd33cb9f60b 100644 --- a/example/kotlinlib/basic/3-multi-module/build.mill +++ b/example/kotlinlib/basic/3-multi-module/build.mill @@ -12,14 +12,13 @@ object foo extends MyModule { def mainClass = Some("foo.FooKt") def moduleDeps = Seq(bar) def ivyDeps = Agg( - ivy"net.sourceforge.argparse4j:argparse4j:0.9.0" + ivy"com.github.ajalt.clikt:clikt-jvm:4.4.0" ) } object bar extends MyModule { def mainClass = Some("bar.BarKt") def ivyDeps = Agg( - ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", ivy"org.apache.commons:commons-text:1.12.0" ) diff --git a/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt b/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt index d985a2ce933..bc022e40c49 100644 --- a/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt +++ b/example/kotlinlib/basic/3-multi-module/foo/src/foo/Foo.kt @@ -1,25 +1,23 @@ package foo -import net.sourceforge.argparse4j.ArgumentParsers -import net.sourceforge.argparse4j.inf.ArgumentParser -import net.sourceforge.argparse4j.inf.Namespace +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required const val VALUE: String = "hello" +class Foo: CliktCommand() { + val fooText by option("--foo-text").required() + val barText by option("--bar-text").required() + + override fun run() { + mainFunction(fooText, barText) + } +} + fun mainFunction(fooText: String, barText: String) { println("Foo.value: " + VALUE) println("Bar.value: " + bar.generateHtml(barText)) } -fun main(args: Array) { - val parser = ArgumentParsers.newFor("Foo").build() - parser.addArgument("--foo-text").required(true) - parser.addArgument("--bar-text").required(true) - - val res = parser.parseArgs(args) - - val fooText = res.getString("foo_text") - val barText = res.getString("bar_text") - - mainFunction(fooText, barText) -} +fun main(args: Array) = Foo().main(args) diff --git a/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt b/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt index 9d14172709a..7e5fa110bef 100644 --- a/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt +++ b/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt @@ -1,9 +1,6 @@ package bar import org.apache.commons.text.StringEscapeUtils -import net.sourceforge.argparse4j.ArgumentParsers -import net.sourceforge.argparse4j.inf.ArgumentParser -import net.sourceforge.argparse4j.inf.Namespace fun generateHtml(text: String): String { return "

" + StringEscapeUtils.escapeHtml4("world") + "

" diff --git a/example/kotlinlib/basic/4-builtin-commands/build.mill b/example/kotlinlib/basic/4-builtin-commands/build.mill index feb86c3e0e6..82183f4cc8d 100644 --- a/example/kotlinlib/basic/4-builtin-commands/build.mill +++ b/example/kotlinlib/basic/4-builtin-commands/build.mill @@ -11,14 +11,13 @@ object foo extends MyModule { def mainClass = Some("foo.FooKt") def moduleDeps = Seq(bar) def ivyDeps = Agg( - ivy"net.sourceforge.argparse4j:argparse4j:0.9.0" + ivy"com.github.ajalt.clikt:clikt-jvm:4.4.0" ) } object bar extends MyModule { def mainClass = Some("bar.BarKt") def ivyDeps = Agg( - ivy"net.sourceforge.argparse4j:argparse4j:0.9.0", ivy"org.apache.commons:commons-text:1.12.0" ) diff --git a/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt b/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt index 1f9c8f10cb9..abc8fe7492c 100644 --- a/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt +++ b/example/kotlinlib/basic/4-builtin-commands/foo/src/foo/Foo.kt @@ -1,27 +1,24 @@ package foo -import net.sourceforge.argparse4j.ArgumentParsers -import net.sourceforge.argparse4j.inf.ArgumentParser -import net.sourceforge.argparse4j.inf.Namespace - +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required const val VALUE: String = "hello" +class Foo: CliktCommand() { + val fooText by option("--foo-text").required() + val barText by option("--bar-text").required() + + override fun run() { + mainFunction(fooText, barText) + } +} + fun mainFunction(fooText: String, barText: String) { println("Foo.value: " + VALUE) println("Bar.value: " + bar.generateHtml(barText)) } -fun main(args: Array) { - val parser = ArgumentParsers.newFor("Foo").build() - parser.addArgument("--foo-text").required(true) - parser.addArgument("--bar-text").required(true) - - val res = parser.parseArgs(args) - - val fooText = res.getString("foo_text") - val barText = res.getString("bar_text") - - mainFunction(fooText, barText) -} +fun main(args: Array) = Foo().main(args) From d3041c71914f42cf6e445cf12803a21d68e06dcb Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:39:14 +0200 Subject: [PATCH 05/13] Use kotlinx.html in Kotlin samples --- example/kotlinlib/basic/1-simple/build.mill | 2 +- example/kotlinlib/basic/1-simple/src/foo/Foo.kt | 5 +++-- example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt | 5 +++-- example/kotlinlib/basic/3-multi-module/build.mill | 2 +- .../kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt | 5 +++-- example/kotlinlib/basic/4-builtin-commands/build.mill | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/example/kotlinlib/basic/1-simple/build.mill b/example/kotlinlib/basic/1-simple/build.mill index 18854865068..1d9b7fe3aa9 100644 --- a/example/kotlinlib/basic/1-simple/build.mill +++ b/example/kotlinlib/basic/1-simple/build.mill @@ -10,7 +10,7 @@ object `package` extends RootModule with KotlinModule { def ivyDeps = Agg( ivy"com.github.ajalt.clikt:clikt-jvm:4.4.0", - ivy"org.apache.commons:commons-text:1.12.0" + ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) object test extends KotlinModuleTests with TestModule.Junit4{ diff --git a/example/kotlinlib/basic/1-simple/src/foo/Foo.kt b/example/kotlinlib/basic/1-simple/src/foo/Foo.kt index 4d605d4f9ad..1f9f6931105 100644 --- a/example/kotlinlib/basic/1-simple/src/foo/Foo.kt +++ b/example/kotlinlib/basic/1-simple/src/foo/Foo.kt @@ -1,6 +1,7 @@ package foo -import org.apache.commons.text.StringEscapeUtils +import kotlinx.html.h1 +import kotlinx.html.stream.createHTML import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.required @@ -14,7 +15,7 @@ class Foo: CliktCommand() { } fun generateHtml(text: String): String { - return "

" + StringEscapeUtils.escapeHtml4(text) + "

" + return createHTML(prettyPrint = false).h1 { text(text) }.toString() } fun main(args: Array) = Foo().main(args) diff --git a/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt b/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt index ea5d1cec31c..7d764903605 100644 --- a/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt +++ b/example/kotlinlib/basic/3-multi-module/bar/src/bar/Bar.kt @@ -1,9 +1,10 @@ package bar -import org.apache.commons.text.StringEscapeUtils +import kotlinx.html.h1 +import kotlinx.html.stream.createHTML fun generateHtml(text: String): String { - return "

" + StringEscapeUtils.escapeHtml4(text) + "

" + return createHTML(prettyPrint = false).h1 { text(text) }.toString() } fun main(args: Array) { diff --git a/example/kotlinlib/basic/3-multi-module/build.mill b/example/kotlinlib/basic/3-multi-module/build.mill index fd33cb9f60b..945da3262a1 100644 --- a/example/kotlinlib/basic/3-multi-module/build.mill +++ b/example/kotlinlib/basic/3-multi-module/build.mill @@ -19,7 +19,7 @@ object foo extends MyModule { object bar extends MyModule { def mainClass = Some("bar.BarKt") def ivyDeps = Agg( - ivy"org.apache.commons:commons-text:1.12.0" + ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) object test extends KotlinModuleTests with TestModule.Junit4 diff --git a/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt b/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt index 7e5fa110bef..be74135c57b 100644 --- a/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt +++ b/example/kotlinlib/basic/4-builtin-commands/bar/src/bar/Bar.kt @@ -1,9 +1,10 @@ package bar -import org.apache.commons.text.StringEscapeUtils +import kotlinx.html.h1 +import kotlinx.html.stream.createHTML fun generateHtml(text: String): String { - return "

" + StringEscapeUtils.escapeHtml4("world") + "

" + return createHTML(prettyPrint = false).h1 { text("world") }.toString() } fun main(args: Array) { diff --git a/example/kotlinlib/basic/4-builtin-commands/build.mill b/example/kotlinlib/basic/4-builtin-commands/build.mill index 82183f4cc8d..73ff33ff2ec 100644 --- a/example/kotlinlib/basic/4-builtin-commands/build.mill +++ b/example/kotlinlib/basic/4-builtin-commands/build.mill @@ -18,7 +18,7 @@ object foo extends MyModule { object bar extends MyModule { def mainClass = Some("bar.BarKt") def ivyDeps = Agg( - ivy"org.apache.commons:commons-text:1.12.0" + ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) object test extends KotlinModuleTests with TestModule.Junit4 From 7ad442cd6cb4a64455a9bdcf4a8e68625fd6d7b6 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:02:44 +0200 Subject: [PATCH 06/13] Format files --- .../src/mill/kotlinlib/KotlinModule.scala | 426 +++++++++--------- .../mill/kotlinlib/KotlinModulePlatform.scala | 20 +- .../mill/kotlinlib/KotlinWorkerManager.scala | 16 +- .../kotlinlib/KotlinWorkerManagerImpl.scala | 132 +++--- .../mill/kotlinlib/KotlinWorkerModule.scala | 28 +- .../worker/impl/KotlinWorkerImpl.scala | 42 +- .../kotlinlib/worker/api/KotlinWorker.scala | 18 +- 7 files changed, 344 insertions(+), 338 deletions(-) diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala index 92941789c2b..20f356f2a90 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala @@ -1,211 +1,215 @@ -package mill -package kotlinlib - -import mill.api.{PathRef, Result} -import mill.define.{Command, Task} -import mill.kotlinlib.worker.api.KotlinWorker -import mill.scalalib.api.CompilationResult -import mill.scalalib.{Dep, DepSyntax, JavaModule, Lib} -import mill.{Agg, T} - -import java.io.File - -trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => - - /** - * All individual source files fed into the compiler. - */ - override def allSourceFiles = T { - Lib.findSourceFiles(allSources(), Seq("kt", "kts", "java")).map(PathRef(_)) - } - - /** - * All individual Java source files fed into the compiler. - * Subset of [[allSourceFiles]]. - */ - def allJavaSourceFiles = T { - allSourceFiles().filter(_.path.ext.toLowerCase() == "java") - } - - /** - * All individual Kotlin source files fed into the compiler. - * Subset of [[allSourceFiles]]. - */ - def allKotlinSourceFiles = T { - allSourceFiles().filter(path => Seq("kt", "kts").exists(path.path.ext.toLowerCase() == _)) - } - - /** - * The Kotlin version to be used (for API and Language level settings). - */ - def kotlinVersion: T[String] - - /** - * The dependencies of this module. - * Defaults to add the kotlin-stdlib dependency matching the [[kotlinVersion]]. - */ - override def mandatoryIvyDeps: T[Agg[Dep]] = T { - super.mandatoryIvyDeps() ++ Agg( - ivy"org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion()}" - ) - } - - /** - * The version of the Kotlin compiler to be used. - * Default is derived from [[kotlinVersion]]. - */ - def kotlinCompilerVersion: T[String] = T { kotlinVersion() } - - /** - * The Ivy/Coursier dependencies resembling the Kotlin compiler. - * Default is derived from [[kotlinCompilerVersion]]. - */ - override def kotlinCompilerIvyDeps: T[Agg[Dep]] = T { - Agg(ivy"org.jetbrains.kotlin:kotlin-compiler:${kotlinCompilerVersion()}") ++ -// ( -// if (Seq("1.0.", "1.1.", "1.2").exists(prefix => kotlinVersion().startsWith(prefix))) -// Agg(ivy"org.jetbrains.kotlin:kotlin-runtime:${kotlinCompilerVersion()}") -// else Seq() -// ) ++ - ( - if ( - !Seq("1.0.", "1.1.", "1.2.0", "1.2.1", "1.2.2", "1.2.3", "1.2.4").exists(prefix => - kotlinVersion().startsWith(prefix) - ) - ) - Agg(ivy"org.jetbrains.kotlin:kotlin-scripting-compiler:${kotlinCompilerVersion()}") - else Seq() - ) -// ivy"org.jetbrains.kotlin:kotlin-scripting-compiler-impl:${kotlinCompilerVersion()}", -// ivy"org.jetbrains.kotlin:kotlin-scripting-common:${kotlinCompilerVersion()}", - } - -// @Deprecated("Use kotlinWorkerTask instead, as this does not need to be cached as Worker") -// def kotlinWorker: Worker[KotlinWorker] = T.worker { -// kotlinWorkerTask() -// } - - def kotlinWorkerTask: Task[KotlinWorker] = T.task { - kotlinWorkerRef().kotlinWorkerManager().get(kotlinCompilerClasspath()) - } - - /** - * Compiles all the sources to JVM class files. - */ - override def compile: T[CompilationResult] = T { - kotlinCompileTask()() - } - - /** - * Runs the Kotlin compiler with the `-help` argument to show you the built-in cmdline help. - * You might want to add additional arguments like `-X` to see extra help. - */ - def kotlincHelp(args: String*): Command[Unit] = T.command { - kotlinCompileTask(Seq("-help") ++ args)() - () - } - - protected def when(cond: Boolean)(args: String*): Seq[String] = if (cond) args else Seq() - - /** - * The actual Kotlin compile task (used by [[compile]] and [[kotlincHelp]]). - */ - protected def kotlinCompileTask(extraKotlinArgs: Seq[String] = Seq()): Task[CompilationResult] = T.task { - val ctx = T.ctx() - val dest = ctx.dest - val classes = dest / "classes" - os.makeDir.all(classes) - - val javaSourceFiles = allJavaSourceFiles().map(_.path) - val kotlinSourceFiles = allKotlinSourceFiles().map(_.path) - - val isKotlin = kotlinSourceFiles.nonEmpty - val isJava = javaSourceFiles.nonEmpty - val isMixed = isKotlin && isJava - - val compileCp = compileClasspath().map(_.path).filter(os.exists) - val updateCompileOutput = upstreamCompileOutput() - - def compileJava: Result[CompilationResult] = { - ctx.log.info( - s"Compiling ${javaSourceFiles.size} Java sources to ${classes} ..." - ) - // The compile step is lazy, but its dependencies are not! - internalCompileJavaFiles( - worker = zincWorkerRef().worker(), - upstreamCompileOutput = updateCompileOutput, - javaSourceFiles = javaSourceFiles, - compileCp = compileCp, - javacOptions = javacOptions(), - compileProblemReporter = ctx.reporter(hashCode), - reportOldProblems = internalReportOldProblems() - ) - } - - if (isMixed || isKotlin) { - ctx.log.info( - s"Compiling ${kotlinSourceFiles.size} Kotlin sources to ${classes} ..." - ) - val compilerArgs: Seq[String] = Seq( - // destdir - Seq("-d", classes.toIO.getAbsolutePath()), - // classpath - when(compileCp.iterator.nonEmpty)("-classpath", compileCp.iterator.mkString(File.pathSeparator)), - kotlincOptions(), - extraKotlinArgs, - // parameters - (kotlinSourceFiles ++ javaSourceFiles).map(_.toIO.getAbsolutePath()) - ).flatten - - val workerResult = kotlinWorkerTask().compile(compilerArgs: _*) - - val analysisFile = dest / "kotlin.analysis.dummy" - os.write(target = analysisFile, data = "", createFolders = true) - - workerResult match { - case Result.Success(_) => - val cr = CompilationResult(analysisFile, PathRef(classes)) - if (!isJava) { - // pure Kotlin project - cr - } else { - // also run Java compiler and use it's returned result - compileJava - } - case Result.Failure(reason, _) => - Result.Failure(reason, Some(CompilationResult(analysisFile, PathRef(classes)))) - case e: Result.Exception => e - case Result.Aborted => Result.Aborted - case Result.Skipped => Result.Skipped - // case x => x - } - } else { - // it's Java only - compileJava - } - } - - /** - * Additional Kotlin compiler options to be used by [[compile]]. - */ - def kotlincOptions: T[Seq[String]] = T { - Seq("-no-stdlib") ++ - when(!kotlinVersion().startsWith("1.0"))( - "-language-version", - kotlinVersion().split("[.]", 3).take(2).mkString("."), - "-api-version", - kotlinVersion().split("[.]", 3).take(2).mkString(".") - ) - } - - /** - * A test sub-module linked to its parent module best suited for unit-tests. - */ - trait KotlinModuleTests extends JavaModuleTests with KotlinModule { - override def kotlinVersion: T[String] = T { outer.kotlinVersion() } - override def kotlinCompilerVersion: T[String] = T { outer.kotlinCompilerVersion() } - override def kotlincOptions: T[Seq[String]] = T { outer.kotlincOptions() } - override def defaultCommandName(): String = super.defaultCommandName() - } - -} +package mill +package kotlinlib + +import mill.api.{PathRef, Result} +import mill.define.{Command, Task} +import mill.kotlinlib.worker.api.KotlinWorker +import mill.scalalib.api.CompilationResult +import mill.scalalib.{Dep, DepSyntax, JavaModule, Lib} +import mill.{Agg, T} + +import java.io.File + +trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => + + /** + * All individual source files fed into the compiler. + */ + override def allSourceFiles = T { + Lib.findSourceFiles(allSources(), Seq("kt", "kts", "java")).map(PathRef(_)) + } + + /** + * All individual Java source files fed into the compiler. + * Subset of [[allSourceFiles]]. + */ + def allJavaSourceFiles = T { + allSourceFiles().filter(_.path.ext.toLowerCase() == "java") + } + + /** + * All individual Kotlin source files fed into the compiler. + * Subset of [[allSourceFiles]]. + */ + def allKotlinSourceFiles = T { + allSourceFiles().filter(path => Seq("kt", "kts").exists(path.path.ext.toLowerCase() == _)) + } + + /** + * The Kotlin version to be used (for API and Language level settings). + */ + def kotlinVersion: T[String] + + /** + * The dependencies of this module. + * Defaults to add the kotlin-stdlib dependency matching the [[kotlinVersion]]. + */ + override def mandatoryIvyDeps: T[Agg[Dep]] = T { + super.mandatoryIvyDeps() ++ Agg( + ivy"org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion()}" + ) + } + + /** + * The version of the Kotlin compiler to be used. + * Default is derived from [[kotlinVersion]]. + */ + def kotlinCompilerVersion: T[String] = T { kotlinVersion() } + + /** + * The Ivy/Coursier dependencies resembling the Kotlin compiler. + * Default is derived from [[kotlinCompilerVersion]]. + */ + override def kotlinCompilerIvyDeps: T[Agg[Dep]] = T { + Agg(ivy"org.jetbrains.kotlin:kotlin-compiler:${kotlinCompilerVersion()}") ++ +// ( +// if (Seq("1.0.", "1.1.", "1.2").exists(prefix => kotlinVersion().startsWith(prefix))) +// Agg(ivy"org.jetbrains.kotlin:kotlin-runtime:${kotlinCompilerVersion()}") +// else Seq() +// ) ++ + ( + if ( + !Seq("1.0.", "1.1.", "1.2.0", "1.2.1", "1.2.2", "1.2.3", "1.2.4").exists(prefix => + kotlinVersion().startsWith(prefix) + ) + ) + Agg(ivy"org.jetbrains.kotlin:kotlin-scripting-compiler:${kotlinCompilerVersion()}") + else Seq() + ) +// ivy"org.jetbrains.kotlin:kotlin-scripting-compiler-impl:${kotlinCompilerVersion()}", +// ivy"org.jetbrains.kotlin:kotlin-scripting-common:${kotlinCompilerVersion()}", + } + +// @Deprecated("Use kotlinWorkerTask instead, as this does not need to be cached as Worker") +// def kotlinWorker: Worker[KotlinWorker] = T.worker { +// kotlinWorkerTask() +// } + + def kotlinWorkerTask: Task[KotlinWorker] = T.task { + kotlinWorkerRef().kotlinWorkerManager().get(kotlinCompilerClasspath()) + } + + /** + * Compiles all the sources to JVM class files. + */ + override def compile: T[CompilationResult] = T { + kotlinCompileTask()() + } + + /** + * Runs the Kotlin compiler with the `-help` argument to show you the built-in cmdline help. + * You might want to add additional arguments like `-X` to see extra help. + */ + def kotlincHelp(args: String*): Command[Unit] = T.command { + kotlinCompileTask(Seq("-help") ++ args)() + () + } + + protected def when(cond: Boolean)(args: String*): Seq[String] = if (cond) args else Seq() + + /** + * The actual Kotlin compile task (used by [[compile]] and [[kotlincHelp]]). + */ + protected def kotlinCompileTask(extraKotlinArgs: Seq[String] = Seq()): Task[CompilationResult] = + T.task { + val ctx = T.ctx() + val dest = ctx.dest + val classes = dest / "classes" + os.makeDir.all(classes) + + val javaSourceFiles = allJavaSourceFiles().map(_.path) + val kotlinSourceFiles = allKotlinSourceFiles().map(_.path) + + val isKotlin = kotlinSourceFiles.nonEmpty + val isJava = javaSourceFiles.nonEmpty + val isMixed = isKotlin && isJava + + val compileCp = compileClasspath().map(_.path).filter(os.exists) + val updateCompileOutput = upstreamCompileOutput() + + def compileJava: Result[CompilationResult] = { + ctx.log.info( + s"Compiling ${javaSourceFiles.size} Java sources to ${classes} ..." + ) + // The compile step is lazy, but its dependencies are not! + internalCompileJavaFiles( + worker = zincWorkerRef().worker(), + upstreamCompileOutput = updateCompileOutput, + javaSourceFiles = javaSourceFiles, + compileCp = compileCp, + javacOptions = javacOptions(), + compileProblemReporter = ctx.reporter(hashCode), + reportOldProblems = internalReportOldProblems() + ) + } + + if (isMixed || isKotlin) { + ctx.log.info( + s"Compiling ${kotlinSourceFiles.size} Kotlin sources to ${classes} ..." + ) + val compilerArgs: Seq[String] = Seq( + // destdir + Seq("-d", classes.toIO.getAbsolutePath()), + // classpath + when(compileCp.iterator.nonEmpty)( + "-classpath", + compileCp.iterator.mkString(File.pathSeparator) + ), + kotlincOptions(), + extraKotlinArgs, + // parameters + (kotlinSourceFiles ++ javaSourceFiles).map(_.toIO.getAbsolutePath()) + ).flatten + + val workerResult = kotlinWorkerTask().compile(compilerArgs: _*) + + val analysisFile = dest / "kotlin.analysis.dummy" + os.write(target = analysisFile, data = "", createFolders = true) + + workerResult match { + case Result.Success(_) => + val cr = CompilationResult(analysisFile, PathRef(classes)) + if (!isJava) { + // pure Kotlin project + cr + } else { + // also run Java compiler and use it's returned result + compileJava + } + case Result.Failure(reason, _) => + Result.Failure(reason, Some(CompilationResult(analysisFile, PathRef(classes)))) + case e: Result.Exception => e + case Result.Aborted => Result.Aborted + case Result.Skipped => Result.Skipped + // case x => x + } + } else { + // it's Java only + compileJava + } + } + + /** + * Additional Kotlin compiler options to be used by [[compile]]. + */ + def kotlincOptions: T[Seq[String]] = T { + Seq("-no-stdlib") ++ + when(!kotlinVersion().startsWith("1.0"))( + "-language-version", + kotlinVersion().split("[.]", 3).take(2).mkString("."), + "-api-version", + kotlinVersion().split("[.]", 3).take(2).mkString(".") + ) + } + + /** + * A test sub-module linked to its parent module best suited for unit-tests. + */ + trait KotlinModuleTests extends JavaModuleTests with KotlinModule { + override def kotlinVersion: T[String] = T { outer.kotlinVersion() } + override def kotlinCompilerVersion: T[String] = T { outer.kotlinCompilerVersion() } + override def kotlincOptions: T[Seq[String]] = T { outer.kotlincOptions() } + override def defaultCommandName(): String = super.defaultCommandName() + } + +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala index 9d59319fedf..c06bbf8f7cb 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala @@ -30,18 +30,20 @@ trait KotlinModulePlatform extends JavaModule { * Default is derived from [[kotlinCompilerIvyDeps]]. */ def kotlinCompilerClasspath: T[Seq[PathRef]] = T { - resolveDeps(T.task { kotlinCompilerIvyDeps().map(bindDependency()) })().toSeq ++ kotlinWorkerClasspath() + resolveDeps( + T.task { kotlinCompilerIvyDeps().map(bindDependency()) } + )().toSeq ++ kotlinWorkerClasspath() } private[kotlinlib] def internalCompileJavaFiles( - worker: ZincWorkerApi, - upstreamCompileOutput: Seq[CompilationResult], - javaSourceFiles: Seq[os.Path], - compileCp: Agg[os.Path], - javacOptions: Seq[String], - compileProblemReporter: Option[CompileProblemReporter], - reportOldProblems: Boolean - )(implicit ctx: ZincWorkerApi.Ctx): Result[CompilationResult] = { + worker: ZincWorkerApi, + upstreamCompileOutput: Seq[CompilationResult], + javaSourceFiles: Seq[os.Path], + compileCp: Agg[os.Path], + javacOptions: Seq[String], + compileProblemReporter: Option[CompileProblemReporter], + reportOldProblems: Boolean + )(implicit ctx: ZincWorkerApi.Ctx): Result[CompilationResult] = { worker.compileJava( upstreamCompileOutput = upstreamCompileOutput, sources = javaSourceFiles, diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala index 390491ba335..6e82d1edfbe 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala @@ -1,8 +1,8 @@ -package mill.kotlinlib - -import mill.api.{Ctx, PathRef} -import mill.kotlinlib.worker.api.KotlinWorker - -trait KotlinWorkerManager { - def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker -} +package mill.kotlinlib + +import mill.api.{Ctx, PathRef} +import mill.kotlinlib.worker.api.KotlinWorker + +trait KotlinWorkerManager { + def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala index d4c6458b634..77dd667a95f 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala @@ -1,66 +1,66 @@ -package mill.kotlinlib - -import mill.PathRef -import mill.api.Ctx -import mill.kotlinlib.worker.api.KotlinWorker - -import java.net.{URL, URLClassLoader} - -class KotlinWorkerManagerImpl(ctx: Ctx) extends KotlinWorkerManager with AutoCloseable { - - private[this] var workerCache: Map[Seq[PathRef], (KotlinWorker, Int)] = Map.empty - - override def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker = { - val toolsCp = toolsClasspath.distinct - val (worker, count) = workerCache.get(toolsCp) match { - case Some((w, count)) => - ctx.log.debug(s"Reusing existing AspectjWorker for classpath: ${toolsCp}") - w -> count - case None => - ctx.log.debug(s"Creating Classloader with classpath: [${toolsCp}]") - val classLoader = new URLClassLoader( - toolsCp.map(_.path.toNIO.toUri().toURL()).toArray[URL], - getClass().getClassLoader() - ) - - val className = - classOf[KotlinWorker].getPackage().getName().split("\\.").dropRight(1).mkString( - "." - ) + ".impl." + classOf[KotlinWorker].getSimpleName() + "Impl" - ctx.log.debug(s"Creating ${className} from classpath: ${toolsCp}") - val impl = classLoader.loadClass(className) - val worker = impl.getConstructor().newInstance().asInstanceOf[KotlinWorker] - if (worker.getClass().getClassLoader() != classLoader) { - ctx.log.error( - """Worker not loaded from worker classloader. - |You should not add the mill-kotlin-worker JAR to the mill build classpath""".stripMargin - ) - } - if (worker.getClass().getClassLoader() == classOf[KotlinWorker].getClassLoader()) { - ctx.log.error("Worker classloader used to load interface and implementation") - } - worker -> 0 - } - workerCache += toolsCp -> (worker -> (1 + count)) - ctx.log.debug(stats()) - worker - } - - def stats(): String = { - s"""Cache statistics of ${this.toString()}: - |${ - workerCache.map { case (cp, (worker, count)) => - s"""- worker: ${worker.toString()} - | used: ${count} - |""".stripMargin - }.mkString - }""".stripMargin - } - - override def close(): Unit = { - ctx.log.debug(stats()) - - // We drop cached worker instances - workerCache = Map.empty - } -} +package mill.kotlinlib + +import mill.PathRef +import mill.api.Ctx +import mill.kotlinlib.worker.api.KotlinWorker + +import java.net.{URL, URLClassLoader} + +class KotlinWorkerManagerImpl(ctx: Ctx) extends KotlinWorkerManager with AutoCloseable { + + private[this] var workerCache: Map[Seq[PathRef], (KotlinWorker, Int)] = Map.empty + + override def get(toolsClasspath: Seq[PathRef])(implicit ctx: Ctx): KotlinWorker = { + val toolsCp = toolsClasspath.distinct + val (worker, count) = workerCache.get(toolsCp) match { + case Some((w, count)) => + ctx.log.debug(s"Reusing existing AspectjWorker for classpath: ${toolsCp}") + w -> count + case None => + ctx.log.debug(s"Creating Classloader with classpath: [${toolsCp}]") + val classLoader = new URLClassLoader( + toolsCp.map(_.path.toNIO.toUri().toURL()).toArray[URL], + getClass().getClassLoader() + ) + + val className = + classOf[KotlinWorker].getPackage().getName().split("\\.").dropRight(1).mkString( + "." + ) + ".impl." + classOf[KotlinWorker].getSimpleName() + "Impl" + ctx.log.debug(s"Creating ${className} from classpath: ${toolsCp}") + val impl = classLoader.loadClass(className) + val worker = impl.getConstructor().newInstance().asInstanceOf[KotlinWorker] + if (worker.getClass().getClassLoader() != classLoader) { + ctx.log.error( + """Worker not loaded from worker classloader. + |You should not add the mill-kotlin-worker JAR to the mill build classpath""".stripMargin + ) + } + if (worker.getClass().getClassLoader() == classOf[KotlinWorker].getClassLoader()) { + ctx.log.error("Worker classloader used to load interface and implementation") + } + worker -> 0 + } + workerCache += toolsCp -> (worker -> (1 + count)) + ctx.log.debug(stats()) + worker + } + + def stats(): String = { + s"""Cache statistics of ${this.toString()}: + |${ + workerCache.map { case (cp, (worker, count)) => + s"""- worker: ${worker.toString()} + | used: ${count} + |""".stripMargin + }.mkString + }""".stripMargin + } + + override def close(): Unit = { + ctx.log.debug(stats()) + + // We drop cached worker instances + workerCache = Map.empty + } +} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala index 4aa8cb3a469..832b4dd328e 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala @@ -1,14 +1,14 @@ -package mill.kotlinlib - -import mill.T -import mill.define.{Discover, ExternalModule, Module, Worker} - -trait KotlinWorkerModule extends Module { - def kotlinWorkerManager: Worker[KotlinWorkerManager] = T.worker { - new KotlinWorkerManagerImpl(T.ctx()) - } -} - -object KotlinWorkerModule extends ExternalModule with KotlinWorkerModule { - override def millDiscover: Discover = Discover[this.type] -} +package mill.kotlinlib + +import mill.T +import mill.define.{Discover, ExternalModule, Module, Worker} + +trait KotlinWorkerModule extends Module { + def kotlinWorkerManager: Worker[KotlinWorkerManager] = T.worker { + new KotlinWorkerManagerImpl(T.ctx()) + } +} + +object KotlinWorkerModule extends ExternalModule with KotlinWorkerModule { + override def millDiscover: Discover = Discover[this.type] +} diff --git a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala index c4e36e85af5..71940b4510d 100644 --- a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala +++ b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala @@ -1,21 +1,21 @@ -package mill.kotlinlib.worker.impl - -import mill.api.{Ctx, Result} -import mill.kotlinlib.worker.api.KotlinWorker -import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler - -class KotlinWorkerImpl extends KotlinWorker { - - def compile(args: String*)(implicit ctx: Ctx): Result[Unit] = { - ctx.log.debug("Using kotlin compiler arguments: " + args.map(v => s"'${v}'").mkString(" ")) - - val compiler = new K2JVMCompiler() - val exitCode = compiler.exec(ctx.log.errorStream, args: _*) - if (exitCode.getCode() != 0) { - Result.Failure(s"Kotlin compiler failed with exit code ${exitCode.getCode()} (${exitCode})") - } else { - Result.Success(()) - } - } - -} +package mill.kotlinlib.worker.impl + +import mill.api.{Ctx, Result} +import mill.kotlinlib.worker.api.KotlinWorker +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler + +class KotlinWorkerImpl extends KotlinWorker { + + def compile(args: String*)(implicit ctx: Ctx): Result[Unit] = { + ctx.log.debug("Using kotlin compiler arguments: " + args.map(v => s"'${v}'").mkString(" ")) + + val compiler = new K2JVMCompiler() + val exitCode = compiler.exec(ctx.log.errorStream, args: _*) + if (exitCode.getCode() != 0) { + Result.Failure(s"Kotlin compiler failed with exit code ${exitCode.getCode()} (${exitCode})") + } else { + Result.Success(()) + } + } + +} diff --git a/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala b/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala index e8efbd01faa..6626f25b6a9 100644 --- a/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala +++ b/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala @@ -1,9 +1,9 @@ -package mill.kotlinlib.worker.api - -import mill.api.{Ctx, Result} - -trait KotlinWorker { - - def compile(args: String*)(implicit ctx: Ctx): Result[Unit] - -} +package mill.kotlinlib.worker.api + +import mill.api.{Ctx, Result} + +trait KotlinWorker { + + def compile(args: String*)(implicit ctx: Ctx): Result[Unit] + +} From 524b56930ebbc6733c6702cb53a3e392a48c0867 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:15:23 +0200 Subject: [PATCH 07/13] Change MillStableScalaModule to MillPublishScalaModule as base module for kotlin modules --- kotlinlib/package.mill | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kotlinlib/package.mill b/kotlinlib/package.mill index 0430fb87ead..8f7c88c10cc 100644 --- a/kotlinlib/package.mill +++ b/kotlinlib/package.mill @@ -4,12 +4,14 @@ package build.kotlinlib import mill._ import mill.scalalib._ -object `package` extends RootModule with build.MillStableScalaModule { +// TODO change MillPublishScalaModule to MillStableScalaModule after mill version with kotlinlib is released, +// because currently there is no previous artifact version +object `package` extends RootModule with build.MillPublishScalaModule { def moduleDeps = Seq(build.main, build.scalalib, build.testrunner, worker) def testTransitiveDeps = super.testTransitiveDeps() ++ Seq(worker.impl.testDep()) - trait MillKotlinModule extends build.MillStableScalaModule { + trait MillKotlinModule extends build.MillPublishScalaModule { override def javacOptions = { val release = if (scala.util.Properties.isJavaAtLeast(11)) Seq("-release", "8") @@ -25,7 +27,7 @@ object `package` extends RootModule with build.MillStableScalaModule { build.Deps.osLib ) - object impl extends MillKotlinModule with build.MillPublishScalaModule { + object impl extends MillKotlinModule { override def moduleDeps: Seq[PublishModule] = Seq(worker) override def compileIvyDeps: T[Agg[Dep]] = Agg( build.Deps.osLib, From cfc673c4c20b1426e44ed5cf5b94a32b4eff53b5 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:24:23 +0200 Subject: [PATCH 08/13] Fix 4-builtin-commands Kotlin example assertions --- example/package.mill | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/example/package.mill b/example/package.mill index 89e94bf7a31..94ca06829c6 100644 --- a/example/package.mill +++ b/example/package.mill @@ -36,7 +36,7 @@ object `package` extends RootModule with Module { object web extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "web")) } object kotlinlib extends Module { - object basic extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "basic")) + object basic extends Cross[ExampleCrossModuleKotlin](build.listIn(millSourcePath / "basic")) } object scalalib extends Module { object basic extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "basic")) @@ -64,6 +64,13 @@ object `package` extends RootModule with Module { object libraries extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "libraries")) } + trait ExampleCrossModuleKotlin extends ExampleCrossModuleJava { + override def lineTransform(line: String) = this.millModuleSegments.parts.last match { + case "4-builtin-commands" => line.replace("compile.dest/zinc", "compile.dest/kotlin.analysis.dummy") + case _ => line + } + } + trait ExampleCrossModuleJava extends ExampleCrossModule { def upstreamCross(s: String) = s match { @@ -105,7 +112,7 @@ object `package` extends RootModule with Module { case s"//// SNIPPET:$name" => current = Some(name) groupedLines(name) = mutable.Buffer() - case s => current.foreach(groupedLines(_).append(s)) + case s => current.foreach(groupedLines(_).append(lineTransform(s))) } current = None @@ -119,11 +126,13 @@ object `package` extends RootModule with Module { Nil } - case s => if (current.nonEmpty) None else Some(s) + case s => if (current.nonEmpty) None else Some(lineTransform(s)) } } } } + + def lineTransform(line: String) = line } trait ExampleCrossModule extends build.integration.IntegrationTestModule { From b415b00ba2752980448977fe0c7d3d6d757d754a Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Thu, 12 Sep 2024 23:33:59 +0200 Subject: [PATCH 09/13] Use Kotest in Kotlin samples --- example/kotlinlib/basic/1-simple/build.mill | 4 ++-- .../basic/1-simple/test/src/foo/FooTest.kt | 19 ++++++++----------- .../basic/2-custom-build-logic/build.mill | 6 +++++- .../test/src/foo/FooTests.kt | 13 ++++++------- .../bar/test/src/bar/BarTests.kt | 18 ++++++++---------- .../kotlinlib/basic/3-multi-module/build.mill | 6 +++++- .../bar/test/src/bar/BarTests.kt | 18 ++++++++---------- .../basic/4-builtin-commands/build.mill | 6 +++++- 8 files changed, 47 insertions(+), 43 deletions(-) diff --git a/example/kotlinlib/basic/1-simple/build.mill b/example/kotlinlib/basic/1-simple/build.mill index 1d9b7fe3aa9..3f248de44a2 100644 --- a/example/kotlinlib/basic/1-simple/build.mill +++ b/example/kotlinlib/basic/1-simple/build.mill @@ -13,9 +13,9 @@ object `package` extends RootModule with KotlinModule { ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) - object test extends KotlinModuleTests with TestModule.Junit4{ + object test extends KotlinModuleTests with TestModule.Junit5 { def ivyDeps = super.ivyDeps() ++ Agg( - ivy"com.google.guava:guava:33.3.0-jre" + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" ) } } diff --git a/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt b/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt index 012922af22c..c978e934c1b 100644 --- a/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt +++ b/example/kotlinlib/basic/1-simple/test/src/foo/FooTest.kt @@ -1,18 +1,15 @@ package foo -import com.google.common.html.HtmlEscapers.htmlEscaper import foo.generateHtml -import org.junit.Assert.assertEquals -import org.junit.Test +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe -class FooTest { - @Test - fun testSimple() { - assertEquals(generateHtml("hello"), "

hello

") +class FooTest : FunSpec({ + test("testSimple") { + generateHtml("hello") shouldBe "

hello

" } - @Test - fun testEscaping() { - assertEquals(generateHtml(""), "

" + htmlEscaper().escape("") + "

") + test("testEscaping") { + generateHtml("") shouldBe "

<hello>

" } -} +}) diff --git a/example/kotlinlib/basic/2-custom-build-logic/build.mill b/example/kotlinlib/basic/2-custom-build-logic/build.mill index 1a8a86541c2..e6f3b5c62dc 100644 --- a/example/kotlinlib/basic/2-custom-build-logic/build.mill +++ b/example/kotlinlib/basic/2-custom-build-logic/build.mill @@ -19,5 +19,9 @@ object `package` extends RootModule with KotlinModule { Seq(PathRef(T.dest)) } - object test extends KotlinModuleTests with TestModule.Junit4 + object test extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" + ) + } } diff --git a/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt b/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt index 82572ca6ac3..aa0d089a440 100644 --- a/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt +++ b/example/kotlinlib/basic/2-custom-build-logic/test/src/foo/FooTests.kt @@ -1,15 +1,14 @@ package foo -import org.junit.Assert.assertEquals -import org.junit.Test import foo.getLineCount +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe -class FooTests { +class FooTests : FunSpec({ - @Test - fun testSimple() { + test("testSimple") { val expectedLineCount = 12 val actualLineCount = getLineCount()?.trim().let { Integer.parseInt(it) } - assertEquals(expectedLineCount, actualLineCount) + actualLineCount shouldBe expectedLineCount } -} +}) diff --git a/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt b/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt index d36e2d2b08a..aeb26106298 100644 --- a/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt +++ b/example/kotlinlib/basic/3-multi-module/bar/test/src/bar/BarTests.kt @@ -1,20 +1,18 @@ package bar import bar.generateHtml -import org.junit.Assert.assertEquals -import org.junit.Test +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe -class BarTests { +class BarTests : FunSpec({ - @Test - fun simple() { + test("simple") { val result = generateHtml("hello") - assertEquals("

hello

", result) + result shouldBe "

hello

" } - @Test - fun escaping() { + test("escaping") { val result = generateHtml("") - assertEquals("

<hello>

", result) + result shouldBe "

<hello>

" } -} +}) diff --git a/example/kotlinlib/basic/3-multi-module/build.mill b/example/kotlinlib/basic/3-multi-module/build.mill index 945da3262a1..e8eeb597ccc 100644 --- a/example/kotlinlib/basic/3-multi-module/build.mill +++ b/example/kotlinlib/basic/3-multi-module/build.mill @@ -22,7 +22,11 @@ object bar extends MyModule { ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) - object test extends KotlinModuleTests with TestModule.Junit4 + object test extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" + ) + } } //// SNIPPET:TREE diff --git a/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt b/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt index 4897ffed908..f47be7c3b6b 100644 --- a/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt +++ b/example/kotlinlib/basic/4-builtin-commands/bar/test/src/bar/BarTests.kt @@ -1,20 +1,18 @@ package bar import bar.generateHtml -import org.junit.Assert.assertEquals -import org.junit.Test +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe -public class BarTests { +class BarTests : FunSpec({ - @Test - fun testSimple() { + test("simple") { val result = generateHtml("hello") - assertEquals("

hello

", result) + result shouldBe "

hello

" } - @Test - fun testEscaping() { + test("escaping") { val result = generateHtml("") - assertEquals("

<hello>

", result) + result shouldBe "

<hello>

" } -} \ No newline at end of file +}) \ No newline at end of file diff --git a/example/kotlinlib/basic/4-builtin-commands/build.mill b/example/kotlinlib/basic/4-builtin-commands/build.mill index 73ff33ff2ec..5918a33db9c 100644 --- a/example/kotlinlib/basic/4-builtin-commands/build.mill +++ b/example/kotlinlib/basic/4-builtin-commands/build.mill @@ -21,5 +21,9 @@ object bar extends MyModule { ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) - object test extends KotlinModuleTests with TestModule.Junit4 + object test extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" + ) + } } From abfd8afac9c584c2f6ee095b4c7db2c394e32481 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Fri, 13 Sep 2024 20:55:44 +0200 Subject: [PATCH 10/13] Fix test output for basic/1-simple Kotlin example --- example/kotlinlib/basic/1-simple/build.mill | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/example/kotlinlib/basic/1-simple/build.mill b/example/kotlinlib/basic/1-simple/build.mill index 3f248de44a2..8361f78a117 100644 --- a/example/kotlinlib/basic/1-simple/build.mill +++ b/example/kotlinlib/basic/1-simple/build.mill @@ -83,9 +83,10 @@ error: Error: missing option --text > ./mill test ... -Test foo.FooTest.testEscaping finished, ... -Test foo.FooTest.testSimple finished, ... -Test run foo.FooTest finished: 0 failed, 0 ignored, 2 total, ... +Test foo.FooTesttestSimple finished, ... +Test foo.FooTesttestEscaping finished, ... +Test foo.FooTest finished, ... +Test run finished: 0 failed, 0 ignored, 2 total, ... > ./mill assembly # bundle classfiles and libraries into a jar for deployment From 85c1f30650682a68e2bd609eca49566e93b7b020 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Fri, 13 Sep 2024 21:12:24 +0200 Subject: [PATCH 11/13] Add copyright for the imported code --- kotlinlib/src/mill/kotlinlib/KotlinModule.scala | 4 ++++ kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala | 4 ++++ kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala | 4 ++++ kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala | 4 ++++ kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala | 4 ++++ kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt | 4 ++++ .../resources/hello-world-kotlin/main/test/src/HelloTest.kt | 4 ++++ .../main/src/hello/JavaHello.java | 4 ++++ .../main/src/hello/KotlinHello.kt | 4 ++++ .../main/test/src/HelloTest.java | 4 ++++ .../src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala | 4 ++++ .../worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala | 4 ++++ 12 files changed, 48 insertions(+) diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala index 20f356f2a90..84fee02a688 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill package kotlinlib diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala index c06bbf8f7cb..a5225eabe21 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill.kotlinlib import mill.api.{PathRef, Result} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala index 6e82d1edfbe..53296127d8b 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManager.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill.kotlinlib import mill.api.{Ctx, PathRef} diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala index 77dd667a95f..7e8fadcdc27 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill.kotlinlib import mill.PathRef diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala index 832b4dd328e..c701a9eb53d 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerModule.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill.kotlinlib import mill.T diff --git a/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt b/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt index be8ad86b551..76ccba8ed2a 100644 --- a/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt +++ b/kotlinlib/test/resources/hello-world-kotlin/main/src/Hello.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package hello fun getHelloString() : String { diff --git a/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt b/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt index dfaa21d8757..acd822d848c 100644 --- a/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt +++ b/kotlinlib/test/resources/hello-world-kotlin/main/test/src/HelloTest.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package hello.tests import hello.getHelloString diff --git a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java index 7d5ccf96d1e..7397d53095b 100644 --- a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java +++ b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/JavaHello.java @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package hello; public class JavaHello { diff --git a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt index 302e3df73b8..5bc04d3278a 100644 --- a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt +++ b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/src/hello/KotlinHello.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package hello val KotlinHelloString : String = "Hello from Kotlin!" diff --git a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java index ae913627e5f..e0e173995b9 100644 --- a/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java +++ b/kotlinlib/test/resources/mixed-code-hello-world-kotlin/main/test/src/HelloTest.java @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package hello.tests; import hello.JavaHello; diff --git a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala index 71940b4510d..99fcb41f6e7 100644 --- a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala +++ b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill.kotlinlib.worker.impl import mill.api.{Ctx, Result} diff --git a/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala b/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala index 6626f25b6a9..0c323d1e88b 100644 --- a/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala +++ b/kotlinlib/worker/src/mill/kotlinlib/worker/api/KotlinWorker.scala @@ -1,3 +1,7 @@ +/* + * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. + */ + package mill.kotlinlib.worker.api import mill.api.{Ctx, Result} From 3385f948245ebd30e7c0f16e63e6f1ee41e5e2d1 Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Fri, 13 Sep 2024 21:17:35 +0200 Subject: [PATCH 12/13] Remove KotlinModulePlatform --- .../src/mill/kotlinlib/KotlinModule.scala | 56 +++++++++++++++-- .../mill/kotlinlib/KotlinModulePlatform.scala | 63 ------------------- 2 files changed, 51 insertions(+), 68 deletions(-) delete mode 100644 kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala index 84fee02a688..4269424f104 100644 --- a/kotlinlib/src/mill/kotlinlib/KotlinModule.scala +++ b/kotlinlib/src/mill/kotlinlib/KotlinModule.scala @@ -6,15 +6,16 @@ package mill package kotlinlib import mill.api.{PathRef, Result} -import mill.define.{Command, Task} +import mill.define.{Command, ModuleRef, Task} import mill.kotlinlib.worker.api.KotlinWorker -import mill.scalalib.api.CompilationResult -import mill.scalalib.{Dep, DepSyntax, JavaModule, Lib} +import mill.scalalib.api.{CompilationResult, ZincWorkerApi} +import mill.scalalib.{Dep, DepSyntax, JavaModule, Lib, ZincWorkerModule} +import mill.util.Util.millProjectModule import mill.{Agg, T} import java.io.File -trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => +trait KotlinModule extends JavaModule { outer => /** * All individual source files fed into the compiler. @@ -60,11 +61,35 @@ trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => */ def kotlinCompilerVersion: T[String] = T { kotlinVersion() } + type CompileProblemReporter = mill.api.CompileProblemReporter + + protected def zincWorkerRef: ModuleRef[ZincWorkerModule] = zincWorker + + protected def kotlinWorkerRef: ModuleRef[KotlinWorkerModule] = ModuleRef(KotlinWorkerModule) + + private[kotlinlib] def kotlinWorkerClasspath = T { + millProjectModule( + "mill-kotlinlib-worker-impl", + repositoriesTask(), + resolveFilter = _.toString.contains("mill-kotlinlib-worker-impl") + ) + } + + /** + * The Java classpath resembling the Kotlin compiler. + * Default is derived from [[kotlinCompilerIvyDeps]]. + */ + def kotlinCompilerClasspath: T[Seq[PathRef]] = T { + resolveDeps( + T.task { kotlinCompilerIvyDeps().map(bindDependency()) } + )().toSeq ++ kotlinWorkerClasspath() + } + /** * The Ivy/Coursier dependencies resembling the Kotlin compiler. * Default is derived from [[kotlinCompilerVersion]]. */ - override def kotlinCompilerIvyDeps: T[Agg[Dep]] = T { + def kotlinCompilerIvyDeps: T[Agg[Dep]] = T { Agg(ivy"org.jetbrains.kotlin:kotlin-compiler:${kotlinCompilerVersion()}") ++ // ( // if (Seq("1.0.", "1.1.", "1.2").exists(prefix => kotlinVersion().startsWith(prefix))) @@ -206,6 +231,27 @@ trait KotlinModule extends JavaModule with KotlinModulePlatform { outer => ) } + private[kotlinlib] def internalCompileJavaFiles( + worker: ZincWorkerApi, + upstreamCompileOutput: Seq[CompilationResult], + javaSourceFiles: Seq[os.Path], + compileCp: Agg[os.Path], + javacOptions: Seq[String], + compileProblemReporter: Option[CompileProblemReporter], + reportOldProblems: Boolean + )(implicit ctx: ZincWorkerApi.Ctx): Result[CompilationResult] = { + worker.compileJava( + upstreamCompileOutput = upstreamCompileOutput, + sources = javaSourceFiles, + compileClasspath = compileCp, + javacOptions = javacOptions, + reporter = compileProblemReporter, + reportCachedProblems = reportOldProblems + ) + } + + private[kotlinlib] def internalReportOldProblems: Task[Boolean] = zincReportCachedProblems + /** * A test sub-module linked to its parent module best suited for unit-tests. */ diff --git a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala b/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala deleted file mode 100644 index a5225eabe21..00000000000 --- a/kotlinlib/src/mill/kotlinlib/KotlinModulePlatform.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020-Present Original lefou/mill-kotlin repository contributors. - */ - -package mill.kotlinlib - -import mill.api.{PathRef, Result} -import mill.define.{ModuleRef, Task} -import mill.scalalib.api.{CompilationResult, ZincWorkerApi} -import mill.scalalib.{Dep, JavaModule, ZincWorkerModule} -import mill.util.Util.millProjectModule -import mill.{Agg, T} - -trait KotlinModulePlatform extends JavaModule { - - type CompileProblemReporter = mill.api.CompileProblemReporter - - protected def zincWorkerRef: ModuleRef[ZincWorkerModule] = zincWorker - - protected def kotlinWorkerRef: ModuleRef[KotlinWorkerModule] = ModuleRef(KotlinWorkerModule) - - def kotlinCompilerIvyDeps: T[Agg[Dep]] - - private[kotlinlib] def kotlinWorkerClasspath = T { - millProjectModule( - "mill-kotlinlib-worker-impl", - repositoriesTask(), - resolveFilter = _.toString.contains("mill-kotlinlib-worker-impl") - ) - } - - /** - * The Java classpath resembling the Kotlin compiler. - * Default is derived from [[kotlinCompilerIvyDeps]]. - */ - def kotlinCompilerClasspath: T[Seq[PathRef]] = T { - resolveDeps( - T.task { kotlinCompilerIvyDeps().map(bindDependency()) } - )().toSeq ++ kotlinWorkerClasspath() - } - - private[kotlinlib] def internalCompileJavaFiles( - worker: ZincWorkerApi, - upstreamCompileOutput: Seq[CompilationResult], - javaSourceFiles: Seq[os.Path], - compileCp: Agg[os.Path], - javacOptions: Seq[String], - compileProblemReporter: Option[CompileProblemReporter], - reportOldProblems: Boolean - )(implicit ctx: ZincWorkerApi.Ctx): Result[CompilationResult] = { - worker.compileJava( - upstreamCompileOutput = upstreamCompileOutput, - sources = javaSourceFiles, - compileClasspath = compileCp, - javacOptions = javacOptions, - reporter = compileProblemReporter, - reportCachedProblems = reportOldProblems - ) - } - - private[kotlinlib] def internalReportOldProblems: Task[Boolean] = zincReportCachedProblems - -} From c29f28ad45997d376b21e507f7389b65369a76ea Mon Sep 17 00:00:00 2001 From: 0xnm <0xnm@users.noreply.github.com> Date: Fri, 13 Sep 2024 21:43:47 +0200 Subject: [PATCH 13/13] Fix expected test output for basic/3-multi-module test in Kotlin --- example/package.mill | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/package.mill b/example/package.mill index 94ca06829c6..a6c712752c6 100644 --- a/example/package.mill +++ b/example/package.mill @@ -66,6 +66,8 @@ object `package` extends RootModule with Module { trait ExampleCrossModuleKotlin extends ExampleCrossModuleJava { override def lineTransform(line: String) = this.millModuleSegments.parts.last match { + case "3-multi-module" => line.replace("bar.BarTests.simple", "bar.BarTestssimple") + .replace("bar.BarTests.escaping", "bar.BarTestsescaping") case "4-builtin-commands" => line.replace("compile.dest/zinc", "compile.dest/kotlin.analysis.dummy") case _ => line }