From 4ed7ea8f9b7e82204063b68495e3ec120a846a83 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sat, 13 Mar 2021 10:15:01 +0100 Subject: [PATCH] Annotated more function types and some cleanups --- scalalib/src/CoursierModule.scala | 2 +- scalalib/src/JavaModule.scala | 674 ++++++++++++++++-------------- scalalib/src/ScalaModule.scala | 47 ++- 3 files changed, 384 insertions(+), 339 deletions(-) diff --git a/scalalib/src/CoursierModule.scala b/scalalib/src/CoursierModule.scala index 2138571b382..12dba1adafe 100644 --- a/scalalib/src/CoursierModule.scala +++ b/scalalib/src/CoursierModule.scala @@ -9,7 +9,7 @@ import mill.eval.PathRef * This module provides the capability to resolve (transitive) dependencies from (remote) repositories. * * It's mainly used in [[JavaModule]], but can also be used stand-alone, - * in which case you must provide repositories by overriding [[CoursierModule.repositories]]. + * in which case you must provide repositories by overriding [[CoursierModule.repositoriesTask]]. */ trait CoursierModule extends mill.Module { diff --git a/scalalib/src/JavaModule.scala b/scalalib/src/JavaModule.scala index c14cfeb5f28..3a8d865fcfe 100644 --- a/scalalib/src/JavaModule.scala +++ b/scalalib/src/JavaModule.scala @@ -1,33 +1,34 @@ package mill package scalalib -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream - import coursier.Repository -import mill.define.{Command, Target, Task, TaskModule} +import mill.define.{Command, Sources, Target, Task, TaskModule} import mill.eval.{PathRef, Result} import mill.modules.{Assembly, Jvm} import mill.modules.Jvm.{createAssembly, createJar} -import Lib._ -import mill.scalalib.publish.{Artifact, Scope} +import mill.scalalib.publish.Artifact import mill.api.Loose.Agg +import mill.scalalib.api.CompilationResult +import os.Path /** - * Core configuration required to compile a single Scala compilation target - */ -trait JavaModule extends mill.Module - with TaskModule - with GenIdeaModule - with CoursierModule - with OfflineSupportModule { outer => + * Core configuration required to compile a single Scala compilation target + */ +trait JavaModule + extends mill.Module + with TaskModule + with GenIdeaModule + with CoursierModule + with OfflineSupportModule { outer => def zincWorker: ZincWorkerModule = mill.scalalib.ZincWorkerModule trait JavaModuleTests extends TestModule { override def moduleDeps: Seq[JavaModule] = Seq(outer) override def repositories: Seq[Repository] = outer.repositories - override def repositoriesTask: Task[Seq[Repository]] = T.task { outer.repositoriesTask() } + override def repositoriesTask: Task[Seq[Repository]] = T.task { + outer.repositoriesTask() + } override def javacOptions: T[Seq[String]] = outer.javacOptions override def zincWorker: ZincWorkerModule = outer.zincWorker override def skipIdea: Boolean = outer.skipIdea @@ -36,23 +37,23 @@ trait JavaModule extends mill.Module def defaultCommandName() = "run" - def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{ + def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task { Artifact.fromDepJava(_: Dep) } /** - * Allows you to specify an explicit main class to use for the `run` command. - * If none is specified, the classpath is searched for an appropriate main - * class to use if one exists - */ + * Allows you to specify an explicit main class to use for the `run` command. + * If none is specified, the classpath is searched for an appropriate main + * class to use if one exists + */ def mainClass: T[Option[String]] = None - def finalMainClassOpt: T[Either[String, String]] = T{ - mainClass() match{ + def finalMainClassOpt: T[Either[String, String]] = T { + mainClass() match { case Some(m) => Right(m) case None => - zincWorker.worker().discoverMainClasses(compile())match { - case Seq() => Left("No main class specified or found") + zincWorker.worker().discoverMainClasses(compile()) match { + case Seq() => Left("No main class specified or found") case Seq(main) => Right(main) case mains => Left( @@ -63,7 +64,7 @@ trait JavaModule extends mill.Module } } - def finalMainClass: T[String] = T{ + def finalMainClass: T[String] = T { finalMainClassOpt() match { case Right(main) => Result.Success(main) case Left(msg) => Result.Failure(msg) @@ -71,56 +72,63 @@ trait JavaModule extends mill.Module } /** - * Any ivy dependencies you want to add to this Module, in the format - * ivy"org::name:version" for Scala dependencies or ivy"org:name:version" - * for Java dependencies - */ - def ivyDeps = T{ Agg.empty[Dep] } + * Any ivy dependencies you want to add to this Module, in the format + * ivy"org::name:version" for Scala dependencies or ivy"org:name:version" + * for Java dependencies + */ + def ivyDeps: T[Agg[Dep]] = T { Agg.empty[Dep] } /** - * Same as `ivyDeps`, but only present at compile time. Useful for e.g. - * macro-related dependencies like `scala-reflect` that doesn't need to be - * present at runtime - */ - def compileIvyDeps = T{ Agg.empty[Dep] } + * Same as `ivyDeps`, but only present at compile time. Useful for e.g. + * macro-related dependencies like `scala-reflect` that doesn't need to be + * present at runtime + */ + def compileIvyDeps = T { Agg.empty[Dep] } + /** - * Same as `ivyDeps`, but only present at runtime. Useful for e.g. - * selecting different versions of a dependency to use at runtime after your - * code has already been compiled - */ - def runIvyDeps = T{ Agg.empty[Dep] } + * Same as `ivyDeps`, but only present at runtime. Useful for e.g. + * selecting different versions of a dependency to use at runtime after your + * code has already been compiled + */ + def runIvyDeps: T[Agg[Dep]] = T { Agg.empty[Dep] } /** - * Options to pass to the java compiler - */ - def javacOptions = T{ Seq.empty[String] } + * Options to pass to the java compiler + */ + def javacOptions: T[Seq[String]] = T { Seq.empty[String] } /** The direct dependencies of this module */ - def moduleDeps = Seq.empty[JavaModule] + def moduleDeps: Seq[JavaModule] = Seq.empty /** The compile-only direct dependencies of this module. */ - def compileModuleDeps = Seq.empty[JavaModule] + def compileModuleDeps: Seq[JavaModule] = Seq.empty /** The compile-only transitive ivy dependencies of this module and all it's upstream compile-only modules. */ - def transitiveCompileIvyDeps: T[Agg[Dep]] = T{ + def transitiveCompileIvyDeps: T[Agg[Dep]] = T { // We never include compile-only dependencies transitively, but we must include normal transitive dependencies! - compileIvyDeps() ++ T.traverse(compileModuleDeps)(_.transitiveIvyDeps)().flatten + compileIvyDeps() ++ T + .traverse(compileModuleDeps)(_.transitiveIvyDeps)() + .flatten } /** - * Show the module dependencies. - * @param recursive If `true` include all recursive module dependencies, else only show direct dependencies. - */ - def showModuleDeps(recursive: Boolean = false) = T.command { + * Show the module dependencies. + * @param recursive If `true` include all recursive module dependencies, else only show direct dependencies. + */ + def showModuleDeps(recursive: Boolean = false): Command[Unit] = T.command { val normalDeps = if (recursive) recursiveModuleDeps else moduleDeps - val compileDeps = if(recursive) compileModuleDeps.flatMap(_.transitiveModuleDeps).distinct else compileModuleDeps + val compileDeps = + if (recursive) compileModuleDeps.flatMap(_.transitiveModuleDeps).distinct + else compileModuleDeps val deps = (normalDeps ++ compileDeps).distinct - val asString = s"${if(recursive) "Recursive module" else "Module"} dependencies of ${millModuleSegments.render}:\n\t${ - deps.map { dep => - dep.millModuleSegments.render ++ - (if (compileModuleDeps.contains(dep) || !normalDeps.contains(dep)) " (compile)" else "") - }.mkString("\n\t") - }" + val asString = + s"${if (recursive) "Recursive module" else "Module"} dependencies of ${millModuleSegments.render}:\n\t${deps + .map { dep => + dep.millModuleSegments.render ++ + (if (compileModuleDeps.contains(dep) || !normalDeps.contains(dep)) " (compile)" + else "") + } + .mkString("\n\t")}" T.log.outputStream.println(asString) } @@ -135,56 +143,56 @@ trait JavaModule extends mill.Module } /** - * Additional jars, classfiles or resources to add to the classpath directly - * from disk rather than being downloaded from Maven Central or other package - * repositories - */ - def unmanagedClasspath = T{ Agg.empty[PathRef] } + * Additional jars, classfiles or resources to add to the classpath directly + * from disk rather than being downloaded from Maven Central or other package + * repositories + */ + def unmanagedClasspath: T[Agg[PathRef]] = T { Agg.empty[PathRef] } /** - * The transitive ivy dependencies of this module and all it's upstream modules - */ - def transitiveIvyDeps: T[Agg[Dep]] = T{ + * The transitive ivy dependencies of this module and all it's upstream modules + */ + def transitiveIvyDeps: T[Agg[Dep]] = T { ivyDeps() ++ T.traverse(moduleDeps)(_.transitiveIvyDeps)().flatten } /** - * The upstream compilation output of all this module's upstream modules - */ - def upstreamCompileOutput = T{ - T.traverse((recursiveModuleDeps ++ compileModuleDeps.flatMap(_.transitiveModuleDeps)).distinct)(_.compile) + * The upstream compilation output of all this module's upstream modules + */ + def upstreamCompileOutput: T[Seq[CompilationResult]] = T { + T.traverse((recursiveModuleDeps ++ compileModuleDeps.flatMap( + _.transitiveModuleDeps)).distinct)(_.compile) } /** - * The transitive version of `localClasspath` - */ - def transitiveLocalClasspath: T[Agg[PathRef]] = T{ + * The transitive version of `localClasspath` + */ + def transitiveLocalClasspath: T[Agg[PathRef]] = T { T.traverse(moduleDeps ++ compileModuleDeps)(m => - T.task{m.localClasspath() ++ m.transitiveLocalClasspath()} - )().flatten + T.task { m.localClasspath() ++ m.transitiveLocalClasspath() })() + .flatten } /** - * What platform suffix to use for publishing, e.g. `_sjs` for Scala.js - * projects - */ - def platformSuffix: T[String] = T{ "" } - - private val Milestone213 = raw"""2.13.(\d+)-M(\d+)""".r + * What platform suffix to use for publishing, e.g. `_sjs` for Scala.js + * projects + */ + def platformSuffix: T[String] = T { "" } /** - * What shell script to use to launch the executable generated by `assembly`. - * Defaults to a generic "universal" launcher that should work for Windows, - * OS-X and Linux - */ - def prependShellScript: T[String] = T{ - finalMainClassOpt().toOption match{ + * What shell script to use to launch the executable generated by `assembly`. + * Defaults to a generic "universal" launcher that should work for Windows, + * OS-X and Linux + */ + def prependShellScript: T[String] = T { + finalMainClassOpt().toOption match { case None => "" case Some(cls) => mill.modules.Jvm.launcherUniversalScript( - cls, - Agg("$0"), Agg("%~dpnx0"), - forkArgs() + mainClass = cls, + shellClassPath = Agg("$0"), + cmdClassPath = Agg("%~dpnx0"), + jvmArgs = forkArgs() ) } } @@ -192,29 +200,31 @@ trait JavaModule extends mill.Module def assemblyRules: Seq[Assembly.Rule] = Assembly.defaultRules /** - * The folders where the source files for this module live - */ - def sources = T.sources{ millSourcePath / 'src } + * The folders where the source files for this module live + */ + def sources = T.sources { millSourcePath / "src" } + /** - * The folders where the resource files for this module live - */ - def resources = T.sources{ millSourcePath / 'resources } + * The folders where the resource files for this module live + */ + def resources: Sources = T.sources { millSourcePath / "resources" } + /** - * Folders containing source files that are generated rather than - * hand-written; these files can be generated in this target itself, - * or can refer to files generated from other targets - */ - def generatedSources = T{ Seq.empty[PathRef] } + * Folders containing source files that are generated rather than + * hand-written; these files can be generated in this target itself, + * or can refer to files generated from other targets + */ + def generatedSources: T[Seq[PathRef]] = T { Seq.empty[PathRef] } /** - * The folders containing all source files fed into the compiler - */ - def allSources = T{ sources() ++ generatedSources() } + * The folders containing all source files fed into the compiler + */ + def allSources: T[Seq[PathRef]] = T { sources() ++ generatedSources() } /** - * All individual source files fed into the Java compiler - */ - def allSourceFiles = T{ + * All individual source files fed into the Java compiler + */ + def allSourceFiles: T[Seq[PathRef]] = T { def isHiddenFile(path: os.Path) = path.last.startsWith(".") for { root <- allSources() @@ -224,82 +234,83 @@ trait JavaModule extends mill.Module } yield PathRef(path) } - /** - * Compiles the current module to generate compiled classfiles/bytecode - */ + * Compiles the current module to generate compiled classfiles/bytecode + */ def compile: T[mill.scalalib.api.CompilationResult] = T.persistent { - zincWorker.worker().compileJava( - upstreamCompileOutput(), - allSourceFiles().map(_.path), - compileClasspath().map(_.path), - javacOptions(), - T.reporter.apply(hashCode) - ) + zincWorker + .worker() + .compileJava( + upstreamCompileOutput(), + allSourceFiles().map(_.path), + compileClasspath().map(_.path), + javacOptions(), + T.reporter.apply(hashCode) + ) } /** - * The output classfiles/resources from this module, excluding upstream - * modules and third-party dependencies - */ - def localClasspath = T{ + * The output classfiles/resources from this module, excluding upstream + * modules and third-party dependencies + */ + def localClasspath: T[Seq[PathRef]] = T { resources() ++ Agg(compile().classes) } /** - * All classfiles and resources from upstream modules and dependencies - * necessary to compile this module - */ - def compileClasspath = T { + * All classfiles and resources from upstream modules and dependencies + * necessary to compile this module + */ + def compileClasspath: T[Agg[PathRef]] = T { transitiveLocalClasspath() ++ - resources() ++ - unmanagedClasspath() ++ - resolvedIvyDeps() + resources() ++ + unmanagedClasspath() ++ + resolvedIvyDeps() } def resolvedIvyDeps: T[Agg[PathRef]] = T { - resolveDeps(T.task{transitiveCompileIvyDeps() ++ transitiveIvyDeps()})() + resolveDeps(T.task { transitiveCompileIvyDeps() ++ transitiveIvyDeps() })() } /** - * All upstream classfiles and resources necessary to build and executable - * assembly, but without this module's contribution - */ - def upstreamAssemblyClasspath = T { + * All upstream classfiles and resources necessary to build and executable + * assembly, but without this module's contribution + */ + def upstreamAssemblyClasspath: T[Agg[PathRef]] = T { transitiveLocalClasspath() ++ - unmanagedClasspath() ++ - resolvedRunIvyDeps() + unmanagedClasspath() ++ + resolvedRunIvyDeps() } def resolvedRunIvyDeps: T[Agg[PathRef]] = T { - resolveDeps(T.task{runIvyDeps() ++ transitiveIvyDeps()})() + resolveDeps(T.task { runIvyDeps() ++ transitiveIvyDeps() })() } /** - * All classfiles and resources from upstream modules and dependencies - * necessary to run this module's code after compilation - */ - def runClasspath = T{ + * All classfiles and resources from upstream modules and dependencies + * necessary to run this module's code after compilation + */ + def runClasspath: T[Seq[PathRef]] = T { localClasspath() ++ - upstreamAssemblyClasspath() + upstreamAssemblyClasspath() } /** - * Creates a manifest representation which can be modified or replaced - * The default implementation just adds the `Manifest-Version`, `Main-Class` and `Created-By` attributes - */ - def manifest: T[Jvm.JarManifest] = T{ + * Creates a manifest representation which can be modified or replaced + * The default implementation just adds the `Manifest-Version`, `Main-Class` and `Created-By` attributes + */ + def manifest: T[Jvm.JarManifest] = T { Jvm.createManifest(finalMainClassOpt().toOption) } /** - * Build the assembly for upstream dependencies separate from the current - * classpath - * - * This should allow much faster assembly creation in the common case where - * upstream dependencies do not change - */ - def upstreamAssembly = T{ + * Build the assembly for upstream dependencies separate from the current + * classpath + * + * This should allow much faster assembly creation in the common case where + * upstream dependencies do not change + */ + def upstreamAssembly: T[PathRef] = T { createAssembly( upstreamAssemblyClasspath().map(_.path), manifest(), @@ -308,10 +319,10 @@ trait JavaModule extends mill.Module } /** - * An executable uber-jar/assembly containing all the resources and compiled - * classfiles from this module and all it's upstream modules and dependencies - */ - def assembly = T{ + * An executable uber-jar/assembly containing all the resources and compiled + * classfiles from this module and all it's upstream modules and dependencies + */ + def assembly: T[PathRef] = T { createAssembly( Agg.from(localClasspath().map(_.path)), manifest(), @@ -322,10 +333,10 @@ trait JavaModule extends mill.Module } /** - * A jar containing only this module's resources and compiled classfiles, - * without those from upstream modules and dependencies - */ - def jar = T{ + * A jar containing only this module's resources and compiled classfiles, + * without those from upstream modules and dependencies + */ + def jar: T[PathRef] = T { createJar( localClasspath().map(_.path).filter(os.exists), manifest() @@ -340,18 +351,18 @@ trait JavaModule extends mill.Module def javadocOptions: T[Seq[String]] = T { Seq[String]() } /** - * Extra directories to be processed by the API documentation tool. - * - * Typically includes static files such as html and markdown, but depends - * on the doc tool that is actually used. - */ - def docSources = T.sources(millSourcePath / 'docs) + * Extra directories to be processed by the API documentation tool. + * + * Typically includes static files such as html and markdown, but depends + * on the doc tool that is actually used. + */ + def docSources: Sources = T.sources(millSourcePath / 'docs) /** * The documentation jar, containing all the Javadoc/Scaladoc HTML files, for * publishing to Maven Central */ - def docJar = T[PathRef] { + def docJar: T[PathRef] = T[PathRef] { val outDir = T.dest val javadocDir = outDir / 'javadoc @@ -366,21 +377,22 @@ trait JavaModule extends mill.Module val options = javadocOptions() ++ Seq("-d", javadocDir.toNIO.toString) - if (files.nonEmpty) Jvm.runSubprocess( - commandArgs = Seq( - "javadoc" - ) ++ options ++ - Seq( - "-classpath", - compileClasspath() - .map(_.path) - .filter(_.ext != "pom") - .mkString(java.io.File.pathSeparator) - ) ++ + if (files.nonEmpty) + Jvm.runSubprocess( + commandArgs = Seq( + "javadoc" + ) ++ options ++ + Seq( + "-classpath", + compileClasspath() + .map(_.path) + .filter(_.ext != "pom") + .mkString(java.io.File.pathSeparator) + ) ++ files.map(_.toString), - envArgs = Map(), - workingDir = T.dest - ) + envArgs = Map(), + workingDir = T.dest + ) createJar(Agg(javadocDir))(outDir) } @@ -407,11 +419,11 @@ trait JavaModule extends mill.Module def forkEnv: T[Map[String, String]] = T { sys.env.toMap } /** - * Builds a command-line "launcher" file that can be used to run this module's - * code, without the Mill process. Useful for deployment & other places where - * you do not want a build tool running - */ - def launcher = T{ + * Builds a command-line "launcher" file that can be used to run this module's + * code, without the Mill process. Useful for deployment & other places where + * you do not want a build tool running + */ + def launcher = T { Result.Success( Jvm.createLauncher( finalMainClass(), @@ -426,25 +438,27 @@ trait JavaModule extends mill.Module * @param inverse Invert the tree representation, so that the root is on the bottom. * @param additionalDeps Additional dependency to be included into the tree. */ - protected def printDepsTree(inverse: Boolean, additionalDeps: Task[Agg[Dep]]) = T.task { - val (flattened, resolution) = Lib.resolveDependenciesMetadata( - repositoriesTask(), - resolveCoursierDependency().apply(_), - additionalDeps() ++ transitiveIvyDeps(), - Some(mapDependencies()) - ) + protected def printDepsTree(inverse: Boolean, + additionalDeps: Task[Agg[Dep]]): Task[Unit] = + T.task { + val (flattened, resolution) = Lib.resolveDependenciesMetadata( + repositoriesTask(), + resolveCoursierDependency().apply(_), + additionalDeps() ++ transitiveIvyDeps(), + Some(mapDependencies()) + ) - println( - coursier.util.Print.dependencyTree( - roots = flattened, - resolution = resolution, - printExclusions = false, - reverse = inverse + println( + coursier.util.Print.dependencyTree( + roots = flattened, + resolution = resolution, + printExclusions = false, + reverse = inverse + ) ) - ) - Result.Success() - } + Result.Success() + } /** * Command to print the transitive dependency tree to STDOUT. @@ -453,29 +467,37 @@ trait JavaModule extends mill.Module * @param withCompile Include the compile-time only dependencies (`compileIvyDeps`, provided scope) into the tree. * @param withRuntime Include the runtime dependencies (`runIvyDeps`, runtime scope) into the tree. */ - def ivyDepsTree(inverse: Boolean = false, withCompile: Boolean = false, withRuntime: Boolean = false): Command[Unit] = + def ivyDepsTree(inverse: Boolean = false, + withCompile: Boolean = false, + withRuntime: Boolean = false): Command[Unit] = (withCompile, withRuntime) match { - case (true, true) => T.command { - printDepsTree(inverse, T.task{ transitiveCompileIvyDeps() ++ runIvyDeps() }) + case (true, true) => + T.command { + printDepsTree(inverse, T.task { + transitiveCompileIvyDeps() ++ runIvyDeps() + }) } - case (true, false) => T.command { + case (true, false) => + T.command { printDepsTree(inverse, transitiveCompileIvyDeps) } - case (false, true) => T.command { + case (false, true) => + T.command { printDepsTree(inverse, runIvyDeps) } - case _ => T.command { + case _ => + T.command { printDepsTree(inverse, T.task { Agg.empty[Dep] }) } } /** - * Runs this module's code in-process within an isolated classloader. This is - * faster than `run`, but in exchange you have less isolation between runs - * since the code can dirty the parent Mill process and potentially leave it - * in a bad state. - */ - def runLocal(args: String*) = T.command { + * Runs this module's code in-process within an isolated classloader. This is + * faster than `run`, but in exchange you have less isolation between runs + * since the code can dirty the parent Mill process and potentially leave it + * in a bad state. + */ + def runLocal(args: String*): Command[Unit] = T.command { Jvm.runLocal( finalMainClass(), runClasspath().map(_.path), @@ -484,22 +506,25 @@ trait JavaModule extends mill.Module } /** - * Runs this module's code in a subprocess and waits for it to finish - */ - def run(args: String*) = T.command{ - try Result.Success(Jvm.runSubprocess( - finalMainClass(), - runClasspath().map(_.path), - forkArgs(), - forkEnv(), - args, - workingDir = forkWorkingDir() - )) catch { case e: Exception => - Result.Failure("subprocess failed") + * Runs this module's code in a subprocess and waits for it to finish + */ + def run(args: String*): Command[Unit] = T.command { + try Result.Success( + Jvm.runSubprocess( + finalMainClass(), + runClasspath().map(_.path), + forkArgs(), + forkEnv(), + args, + workingDir = forkWorkingDir() + )) + catch { + case e: Exception => + Result.Failure("subprocess failed") } } - private[this] def backgroundSetup(dest: os.Path) = { + private[this] def backgroundSetup(dest: os.Path): (Path, Path, String) = { val token = java.util.UUID.randomUUID().toString val procId = dest / ".mill-background-process-id" val procTombstone = dest / ".mill-background-process-tombstone" @@ -516,7 +541,7 @@ trait JavaModule extends mill.Module // appear in a short amount of time, we assume the subprocess exited or was // killed via some other means, and continue anyway. val start = System.currentTimeMillis() - while({ + while ({ if (os.exists(procTombstone)) { Thread.sleep(10) os.remove.all(procTombstone) @@ -525,7 +550,7 @@ trait JavaModule extends mill.Module Thread.sleep(10) System.currentTimeMillis() - start < 100 } - })() + }) () os.write(procId, token) os.write(procTombstone, token) @@ -533,91 +558,103 @@ trait JavaModule extends mill.Module } /** - * Runs this module's code in a background process, until it dies or - * `runBackground` is used again. This lets you continue using Mill while - * the process is running in the background: editing files, compiling, and - * only re-starting the background process when you're ready. - * - * You can also use `-w foo.runBackground` to make Mill watch for changes - * and automatically recompile your code & restart the background process - * when ready. This is useful when working on long-running server processes - * that would otherwise run forever - */ - def runBackground(args: String*) = T.command{ + * Runs this module's code in a background process, until it dies or + * `runBackground` is used again. This lets you continue using Mill while + * the process is running in the background: editing files, compiling, and + * only re-starting the background process when you're ready. + * + * You can also use `-w foo.runBackground` to make Mill watch for changes + * and automatically recompile your code & restart the background process + * when ready. This is useful when working on long-running server processes + * that would otherwise run forever + */ + def runBackground(args: String*): Command[Unit] = T.command { val (procId, procTombstone, token) = backgroundSetup(T.dest) - try Result.Success(Jvm.runSubprocess( - "mill.scalalib.backgroundwrapper.BackgroundWrapper", - (runClasspath() ++ zincWorker.backgroundWrapperClasspath()).map(_.path), - forkArgs(), - forkEnv(), - Seq(procId.toString, procTombstone.toString, token, finalMainClass()) ++ args, - workingDir = forkWorkingDir(), - background = true - )) catch { case e: Exception => - Result.Failure("subprocess failed") + try Result.Success( + Jvm.runSubprocess( + "mill.scalalib.backgroundwrapper.BackgroundWrapper", + (runClasspath() ++ zincWorker.backgroundWrapperClasspath()).map(_.path), + forkArgs(), + forkEnv(), + Seq(procId.toString, procTombstone.toString, token, finalMainClass()) ++ args, + workingDir = forkWorkingDir(), + background = true + )) + catch { + case e: Exception => + Result.Failure("subprocess failed") } } /** - * Same as `runBackground`, but lets you specify a main class to run - */ - def runMainBackground(mainClass: String, args: String*) = T.command{ - val (procId, procTombstone, token) = backgroundSetup(T.dest) - try Result.Success(Jvm.runSubprocess( - "mill.scalalib.backgroundwrapper.BackgroundWrapper", - (runClasspath() ++ zincWorker.backgroundWrapperClasspath()).map(_.path), - forkArgs(), - forkEnv(), - Seq(procId.toString, procTombstone.toString, token, mainClass) ++ args, - workingDir = forkWorkingDir(), - background = true - )) catch { case e: Exception => - Result.Failure("subprocess failed") + * Same as `runBackground`, but lets you specify a main class to run + */ + def runMainBackground(mainClass: String, args: String*): Command[Unit] = + T.command { + val (procId, procTombstone, token) = backgroundSetup(T.dest) + try Result.Success( + Jvm.runSubprocess( + "mill.scalalib.backgroundwrapper.BackgroundWrapper", + (runClasspath() ++ zincWorker.backgroundWrapperClasspath()) + .map(_.path), + forkArgs(), + forkEnv(), + Seq(procId.toString, procTombstone.toString, token, mainClass) ++ args, + workingDir = forkWorkingDir(), + background = true + )) + catch { + case e: Exception => + Result.Failure("subprocess failed") + } } - } /** - * Same as `runLocal`, but lets you specify a main class to run - */ - def runMainLocal(mainClass: String, args: String*) = T.command { - Jvm.runLocal( - mainClass, - runClasspath().map(_.path), - args - ) - } + * Same as `runLocal`, but lets you specify a main class to run + */ + def runMainLocal(mainClass: String, args: String*): Command[Unit] = + T.command { + Jvm.runLocal( + mainClass, + runClasspath().map(_.path), + args + ) + } /** - * Same as `run`, but lets you specify a main class to run - */ - def runMain(mainClass: String, args: String*) = T.command{ - try Result.Success(Jvm.runSubprocess( - mainClass, - runClasspath().map(_.path), - forkArgs(), - forkEnv(), - args, - workingDir = forkWorkingDir() - )) catch { case e: Exception => - Result.Failure("subprocess failed") + * Same as `run`, but lets you specify a main class to run + */ + def runMain(mainClass: String, args: String*): Command[Unit] = T.command { + try Result.Success( + Jvm.runSubprocess( + mainClass, + runClasspath().map(_.path), + forkArgs(), + forkEnv(), + args, + workingDir = forkWorkingDir() + )) + catch { + case e: Exception => + Result.Failure("subprocess failed") } } /** - * Override this to change the published artifact id. - * For example, by default a scala module foo.baz might be published as foo-baz_2.12 and a java module would be foo-baz. - * Setting this to baz would result in a scala artifact baz_2.12 or a java artifact baz. - */ + * Override this to change the published artifact id. + * For example, by default a scala module foo.baz might be published as foo-baz_2.12 and a java module would be foo-baz. + * Setting this to baz would result in a scala artifact baz_2.12 or a java artifact baz. + */ def artifactName: T[String] = millModuleSegments.parts.mkString("-") /** - * The exact id of the artifact to be published. You probably don't want to override this. - * If you want to customize the name of the artifact, override artifactName instead. - * If you want to customize the scala version in the artifact id, see ScalaModule.artifactScalaVersion - */ + * The exact id of the artifact to be published. You probably don't want to override this. + * If you want to customize the name of the artifact, override artifactName instead. + * If you want to customize the scala version in the artifact id, see ScalaModule.artifactScalaVersion + */ def artifactId: T[String] = artifactName() - def forkWorkingDir = T{ ammonite.ops.pwd } + def forkWorkingDir: Target[Path] = T { os.pwd } override def prepareOffline(): Command[Unit] = T.command { super.prepareOffline() @@ -630,30 +667,33 @@ trait JavaModule extends mill.Module trait TestModule extends JavaModule with TaskModule { override def defaultCommandName() = "test" + /** - * What test frameworks to use. - */ + * What test frameworks to use. + */ def testFrameworks: T[Seq[String]] + /** - * Discovers and runs the module's tests in a subprocess, reporting the - * results to the console. - * @see [[testCached]] - */ - def test(args: String*): Command[(String, Seq[TestRunner.Result])] = T.command { - testTask(T.task{args})() - } + * Discovers and runs the module's tests in a subprocess, reporting the + * results to the console. + * @see [[testCached]] + */ + def test(args: String*): Command[(String, Seq[TestRunner.Result])] = + T.command { + testTask(T.task { args })() + } /** - * Args to be used by [[testCached]]. - */ - def testCachedArgs: T[Seq[String]] = T{ Seq[String]() } + * Args to be used by [[testCached]]. + */ + def testCachedArgs: T[Seq[String]] = T { Seq[String]() } /** - * Discovers and runs the module's tests in a subprocess, reporting the - * results to the console. - * If no input has changed since the last run, no test were executed. - * @see [[test()]] - */ + * Discovers and runs the module's tests in a subprocess, reporting the + * results to the console. + * If no input has changed since the last run, no test were executed. + * @see [[test()]] + */ def testCached: T[(String, Seq[TestRunner.Result])] = T { testTask(testCachedArgs)() } @@ -744,13 +784,17 @@ trait TestModule extends JavaModule with TaskModule { } } -object TestModule{ - def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) = { +object TestModule { + def handleResults(doneMsg: String, results: Seq[TestRunner.Result]) + : Result[(String, Seq[TestRunner.Result])] = { - val badTests = results.filter(x => Set("Error", "Failure").contains(x.status)) + val badTests = + results.filter(x => Set("Error", "Failure").contains(x.status)) if (badTests.isEmpty) Result.Success((doneMsg, results)) else { - val suffix = if (badTests.length == 1) "" else " and " + (badTests.length-1) + " more" + val suffix = + if (badTests.length == 1) "" + else " and " + (badTests.length - 1) + " more" Result.Failure( badTests.head.fullyQualifiedName + " " + badTests.head.selector + suffix, diff --git a/scalalib/src/ScalaModule.scala b/scalalib/src/ScalaModule.scala index e1c5c8147da..07dbbdae8c4 100644 --- a/scalalib/src/ScalaModule.scala +++ b/scalalib/src/ScalaModule.scala @@ -1,7 +1,7 @@ package mill package scalalib -import coursier.Repository +import coursier.{Dependency, Repository} import mill.define.{Command, Target, Task, TaskModule} import mill.eval.{PathRef, Result} import mill.modules.Jvm @@ -22,8 +22,8 @@ import mill.api.DummyInputStream trait ScalaModule extends JavaModule { outer => trait ScalaModuleTests extends JavaModuleTests with ScalaModule { - override def scalaOrganization = outer.scalaOrganization() - def scalaVersion = outer.scalaVersion() + override def scalaOrganization: T[String] = outer.scalaOrganization() + override def scalaVersion: T[String] = outer.scalaVersion() override def scalacPluginIvyDeps = outer.scalacPluginIvyDeps override def scalacPluginClasspath = outer.scalacPluginClasspath override def scalacOptions = outer.scalacOptions @@ -44,7 +44,7 @@ trait ScalaModule extends JavaModule { outer => /** * All individual source files fed into the Zinc compiler. */ - override def allSourceFiles = T { + override def allSourceFiles: T[Seq[PathRef]] = T { def isHiddenFile(path: os.Path) = path.last.startsWith(".") for { root <- allSources() @@ -61,22 +61,23 @@ trait ScalaModule extends JavaModule { outer => */ def scalaVersion: T[String] - override def mapDependencies = T.task { d: coursier.Dependency => - val artifacts = - if (isDotty(scalaVersion())) - Set("dotty-library", "dotty-compiler") - else if (isScala3(scalaVersion())) - Set("scala3-library", "scala3-compiler") + override def mapDependencies: Task[coursier.Dependency => coursier.Dependency] = T.task { + d: coursier.Dependency => + val artifacts = + if (isDotty(scalaVersion())) + Set("dotty-library", "dotty-compiler") + else if (isScala3(scalaVersion())) + Set("scala3-library", "scala3-compiler") + else + Set("scala-library", "scala-compiler", "scala-reflect") + if (!artifacts(d.module.name.value)) d else - Set("scala-library", "scala-compiler", "scala-reflect") - if (!artifacts(d.module.name.value)) d - else - d.withModule( - d.module.withOrganization( - coursier.Organization(scalaOrganization()) + d.withModule( + d.module.withOrganization( + coursier.Organization(scalaOrganization()) + ) ) - ) - .withVersion(scalaVersion()) + .withVersion(scalaVersion()) } override def resolveCoursierDependency: Task[Dep => coursier.Dependency] = @@ -188,7 +189,7 @@ trait ScalaModule extends JavaModule { outer => ) } - override def docJar = T { + override def docJar: T[PathRef] = T { val pluginOptions = scalaDocPluginClasspath().map(pluginPathRef => s"-Xplugin:${pluginPathRef.path}") val compileCp = Seq( @@ -295,9 +296,9 @@ trait ScalaModule extends JavaModule { outer => /** * Opens up a Scala console with your module and all dependencies present, - * for you to test and operate your code interactively + * for you to test and operate your code interactively. */ - def console() = T.command { + def console(): Command[Unit] = T.command { if (T.log.inStream == DummyInputStream) { Result.Failure("console needs to be run with the -i/--interactive flag") } else { @@ -322,12 +323,12 @@ trait ScalaModule extends JavaModule { outer => * Ammonite's version used in the `repl` command is by default * set to the one Mill is built against. */ - def ammoniteVersion = T(Versions.ammonite) + def ammoniteVersion: T[String] = T(Versions.ammonite) /** * Dependencies that are necessary to run the Ammonite Scala REPL */ - def ammoniteReplClasspath = T { + def ammoniteReplClasspath: T[Seq[PathRef]] = T { localClasspath() ++ transitiveLocalClasspath() ++ unmanagedClasspath() ++