diff options
Diffstat (limited to 'scalaplugin')
6 files changed, 95 insertions, 82 deletions
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala b/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala index 87a42d04..4ccb71cd 100644 --- a/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala +++ b/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala @@ -4,7 +4,7 @@ import ammonite.ops._ import mill.discover.Mirror.Segment import mill.discover.{Discovered, Mirror} import mill.eval.{Evaluator, PathRef} -import mill.util.OSet +import mill.util.{OSet, PrintLogger} object GenIdea { @@ -22,7 +22,7 @@ object GenIdea { val discovered = implicitly[Discovered[T]] val mapping = Discovered.mapping(obj)(discovered) val workspacePath = pwd / 'out - val evaluator = new Evaluator(workspacePath, mapping, _ => ()) + val evaluator = new Evaluator(workspacePath, mapping, new PrintLogger(true)) val modules = Mirror .traverse(obj, discovered.mirror){ (h, p) => diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala index c9cf4a49..001f5844 100644 --- a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala +++ b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala @@ -1,9 +1,10 @@ package mill package scalaplugin -import java.io.File +import java.io.{File, PrintStream, PrintWriter, Writer} import java.net.URLClassLoader import java.util.Optional +import java.util.concurrent.Callable import ammonite.ops._ import coursier.{Cache, Fetch, MavenRepository, Repository, Resolution} @@ -12,11 +13,14 @@ import mill.define.Task.{Module, TaskModule} import mill.eval.{PathRef, Result} import mill.modules.Jvm import mill.modules.Jvm.{createAssembly, createJar, subprocess} +import mill.util.Ctx import sbt.internal.inc._ import sbt.internal.util.{ConsoleOut, MainAppender} import sbt.util.{InterfaceUtil, LogExchange} import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} import mill.util.JsonFormatters._ +import sbt.librarymanagement.DependencyResolution +import xsbti.GlobalLock @@ -29,17 +33,17 @@ object ScalaModule{ Locate.definesClass(classpathEntry) } - val compilerCache = new CompilerCache(2) + var scalaInstanceCache = Option.empty[(Long, ScalaInstance)] def compileScala(scalaVersion: String, - sources: Path, + sources: Seq[Path], compileClasspath: Seq[Path], compilerClasspath: Seq[Path], compilerBridge: Seq[Path], scalacOptions: Seq[String], - javacOptions: Seq[String], - outputPath: Path): PathRef = { + javacOptions: Seq[String]) + (implicit ctx: Ctx): PathRef = { val compileClasspathFiles = compileClasspath.map(_.toIO).toArray - val binaryScalaVersion = scalaVersion.split('.').dropRight(1).mkString(".") + def grepJar(classPath: Seq[Path], s: String) = { classPath .find(_.toString.endsWith(s)) @@ -47,37 +51,44 @@ object ScalaModule{ .toIO } - val outerClassLoader = getClass.getClassLoader + val compilerJars = compilerClasspath.toArray.map(_.toIO) - val compilerBridgeJar = grepJar(compilerBridge, s"compiler-bridge_$binaryScalaVersion-1.0.5.jar") - val zincClassLoader = new URLClassLoader(compilerJars.map(_.toURI.toURL), null){ - override def loadClass(name: String): Class[_] = { - Option(findLoadedClass(name)) orElse - (try Some(findClass(name)) catch {case e: ClassNotFoundException => None}) getOrElse { - // Try to limit `outerClassLoader` to only stuff from the compiler-bridge jar - if (name.startsWith("xsbt.")) outerClassLoader.loadClass(name) - else super.loadClass(name) - } - } - } - val scalaInstance = new ScalaInstance( - version = scalaVersion, - loader = zincClassLoader, - libraryJar = grepJar(compilerClasspath, s"scala-library-$scalaVersion.jar"), - compilerJar = grepJar(compilerClasspath, s"scala-compiler-$scalaVersion.jar"), - allJars = compilerJars, - explicitActual = None + def binaryScalaVersion = scalaVersion.split('.').dropRight(1).mkString(".") + val compilerBridgeJar = new java.io.File( + s"bridge/${scalaVersion.replace('.', '_')}/target/scala-$binaryScalaVersion/mill-bridge_$scalaVersion-0.1-SNAPSHOT.jar" +// s"out/bridges/$scalaVersion/compile/classes" ) + val classloaderSig = compilerClasspath.map(p => p.toString().hashCode + p.mtime.toMillis).sum + println("classloaderSig: " + classloaderSig) + val scalaInstance = scalaInstanceCache match{ + case Some((k, v)) if k == classloaderSig => v + case _ => + val scalaInstance = new ScalaInstance( + version = scalaVersion, + loader = new URLClassLoader(compilerJars.map(_.toURI.toURL), null), + libraryJar = grepJar(compilerClasspath, s"scala-library-$scalaVersion.jar"), + compilerJar = grepJar(compilerClasspath, s"scala-compiler-$scalaVersion.jar"), + allJars = compilerJars, + explicitActual = None + ) + scalaInstanceCache = Some((classloaderSig, scalaInstance)) + scalaInstance + } + + + val scalac = ZincUtil.scalaCompiler(scalaInstance, compilerBridgeJar) - mkdir(outputPath) + mkdir(ctx.dest) val ic = new sbt.internal.inc.IncrementalCompilerImpl() val logger = { - val console = ConsoleOut.systemOut - val consoleAppender = MainAppender.defaultScreen(console) + + val consoleAppender = MainAppender.defaultScreen(ConsoleOut.printStreamOut( + ctx.log.outputStream + )) val l = LogExchange.logger("Hello") LogExchange.unbindLoggerAppenders("Hello") LogExchange.bindLoggerAppenders("Hello", (consoleAppender -> sbt.util.Level.Info) :: Nil) @@ -97,13 +108,13 @@ object ScalaModule{ override def startUnit(phase: String, unitPath: String): Unit = () } - val zincFile = (outputPath/'zinc).toIO + val zincFile = (ctx.dest/'zinc).toIO val store = FileAnalysisStore.binary(zincFile) - val classesDir = (outputPath / 'classes).toIO + val classesDir = (ctx.dest / 'classes).toIO val newResult = ic.compile( ic.inputs( classpath = classesDir +: compileClasspathFiles, - sources = ls.rec(sources).filter(_.isFile).map(_.toIO).toArray, + sources = sources.flatMap(ls.rec).filter(x => x.isFile && x.ext == "scala").map(_.toIO).toArray, classesDirectory = classesDir, scalacOptions = scalacOptions.toArray, javacOptions = javacOptions.toArray, @@ -115,7 +126,7 @@ object ScalaModule{ lookup, skip = false, zincFile, - compilerCache, + new FreshCompilerCache, IncOptions.of(), reporter, Some(ignoreProgress), @@ -136,7 +147,7 @@ object ScalaModule{ ) ) - PathRef(outputPath/'classes) + PathRef(ctx.dest/'classes) } def resolveDependencies(repositories: Seq[Repository], @@ -325,33 +336,29 @@ trait ScalaModule extends Module with TaskModule{ outer => def sources = T.source{ basePath / 'src } def resources = T.source{ basePath / 'resources } + def allSources = T{ Seq(sources()) } def compile = T.persistent{ compileScala( scalaVersion(), - sources().path, + allSources().map(_.path), compileDepClasspath().map(_.path), scalaCompilerClasspath().map(_.path), compilerBridgeClasspath().map(_.path), scalacOptions(), - javacOptions(), - T.ctx().dest + javacOptions() ) } def assembly = T{ - val dest = T.ctx().dest createAssembly( - dest, - (runDepClasspath().filter(_.path.ext != "pom") ++ Seq(resources(), compile())).map(_.path).filter(exists), + (runDepClasspath().filter(_.path.ext != "pom") ++ + Seq(resources(), compile())).map(_.path).filter(exists), prependShellScript = prependShellScript() ) - PathRef(dest) } def classpath = T{ Seq(resources(), compile()) } def jar = T{ - val dest = T.ctx().dest - createJar(dest, Seq(resources(), compile()).map(_.path).filter(exists), mainClass()) - PathRef(dest) + createJar(Seq(resources(), compile()).map(_.path).filter(exists), mainClass()) } def run() = T.command{ diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala b/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala index 03292b68..43e15974 100644 --- a/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala +++ b/scalaplugin/src/main/scala/mill/scalaplugin/TestRunner.scala @@ -6,6 +6,8 @@ import java.net.URLClassLoader import java.util.zip.ZipInputStream import ammonite.ops.{Path, ls, pwd} +import mill.util.Ctx.LogCtx +import mill.util.PrintLogger import sbt.testing._ import scala.collection.mutable @@ -43,14 +45,17 @@ object TestRunner { entireClasspath = args(1).split(" ").map(Path(_)), testClassfilePath = args(2).split(" ").map(Path(_)), args = args(3) match{ case "" => Nil case x => x.split(" ").toList } - ) + )(new LogCtx { + def log = new PrintLogger(true) + }) val outputPath = args(4) ammonite.ops.write(Path(outputPath), upickle.default.write(result)) } def apply(frameworkName: String, entireClasspath: Seq[Path], testClassfilePath: Seq[Path], - args: Seq[String]): Option[String] = { + args: Seq[String]) + (implicit ctx: LogCtx): Option[String] = { val outerClassLoader = getClass.getClassLoader val cl = new URLClassLoader( entireClasspath.map(_.toIO.toURI.toURL).toArray, @@ -86,17 +91,17 @@ object TestRunner { }, Array( new Logger { - def debug(msg: String) = println(msg) + def debug(msg: String) = ctx.log.info(msg) - def error(msg: String) = println(msg) + def error(msg: String) = ctx.log.error(msg) def ansiCodesSupported() = true - def warn(msg: String) = println(msg) + def warn(msg: String) = ctx.log.info(msg) - def trace(t: Throwable) = println(t) + def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream) - def info(msg: String) = println(msg) + def info(msg: String) = ctx.log.info(msg) }) ) } @@ -107,7 +112,7 @@ object TestRunner { val grouped = events.groupBy(x => x).mapValues(_.length).filter(_._2 != 0).toList.sorted grouped.map{case (k, v) => k + ": " + v}.mkString(",") } - println(msg) + ctx.log.info(msg) if (events.count(Set(Status.Error, Status.Failure)) == 0) None else Some(msg) } diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala index 7ba8b267..856e9508 100644 --- a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala +++ b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala @@ -9,7 +9,7 @@ import utest._ import mill.util.JsonFormatters._ object AcyclicBuild{ val acyclic = - for(crossVersion <- Cross("2.10.6", "2.11.8", "2.12.4")) + for(crossVersion <- Cross("2.10.6", "2.11.8", "2.12.3", "2.12.4")) yield new ScalaModule{outer => def basePath = AcyclicTests.workspacePath def organization = "com.lihaoyi" @@ -44,12 +44,13 @@ object AcyclicTests extends TestSuite{ val packageScala = workspacePath/'src/'main/'scala/'acyclic/"package.scala" - 'scala210 - check("2.10.6") - 'scala211 - check("2.11.8") - 'scala212 - check("2.12.4") + 'scala210 - check("2.10.6", full = false) + 'scala211 - check("2.11.8", full = false) + 'scala2123 - check("2.12.3", full = true) + 'scala2124 - check("2.12.4", full = false) val allBinaryVersions = Seq("2.10", "2.11", "2.12") - def check(scalaVersion: String) = { + def check(scalaVersion: String, full: Boolean) = { // Dependencies are right; make sure every dependency is of the correct // binary Scala version, except for the compiler-bridge which is of the // same version as the host classpath. @@ -74,30 +75,31 @@ object AcyclicTests extends TestSuite{ val Right((_, evalCount2)) = eval(AcyclicBuild.acyclic(scalaVersion).compile) assert(evalCount2 == 0) - write.append(packageScala, "\n") + if (full){ + // Caches are invalidated if code is changed + write.append(packageScala, "\n") + val Right((_, evalCount3)) = eval(AcyclicBuild.acyclic(scalaVersion).compile) + assert(evalCount3 > 0) - // Caches are invalidated if code is changed - val Right((_, evalCount3)) = eval(AcyclicBuild.acyclic(scalaVersion).compile) - assert(evalCount3 > 0) + // Compilation can fail on broken code, and work when fixed + write.append(packageScala, "\n}}") + val Left(Result.Exception(ex)) = eval(AcyclicBuild.acyclic(scalaVersion).compile) + assert(ex.isInstanceOf[sbt.internal.inc.CompileFailed]) - // Compilation can fail on broken code, and work when fixed - write.append(packageScala, "\n}}") - val Left(Result.Exception(ex)) = eval(AcyclicBuild.acyclic(scalaVersion).compile) - assert(ex.isInstanceOf[sbt.internal.inc.CompileFailed]) + write.write(packageScala, read(packageScala).dropRight(3)) + val Right(_) = eval(AcyclicBuild.acyclic(scalaVersion).compile) - write.write(packageScala, read(packageScala).dropRight(3)) - val Right(_) = eval(AcyclicBuild.acyclic(scalaVersion).compile) + // Tests compile & run + val Right(_) = eval(AcyclicBuild.acyclic(scalaVersion).test.forkTest()) - // Tests compile & run - val Right(_) = eval(AcyclicBuild.acyclic(scalaVersion).test.forkTest()) + // Tests can be broken + write.append(packageScala, "\n}}") + val Left(_) = eval(AcyclicBuild.acyclic(scalaVersion).test.forkTest()) - // Tests can be broken - write.append(packageScala, "\n}}") - val Left(_) = eval(AcyclicBuild.acyclic(scalaVersion).test.forkTest()) - - // Tests can be fixed - write.write(packageScala, read(packageScala).dropRight(3)) - val Right(_) = eval(AcyclicBuild.acyclic(scalaVersion).test.forkTest()) + // Tests can be fixed + write.write(packageScala, read(packageScala).dropRight(3)) + val Right(_) = eval(AcyclicBuild.acyclic(scalaVersion).test.forkTest()) + } } } diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala index c2f5a7b8..916dd7ca 100644 --- a/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala +++ b/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala @@ -119,7 +119,7 @@ object HelloWorldTests extends TestSuite { val Right((_, incCompileCount)) = eval(HelloWorld.compile, helloWorldMapping) - assert(incCompileCount == 1) + assert(incCompileCount > 0, incCompileCount < freshCount) } 'failOnError - { write.append(mainObject, "val x: ") diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala b/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala index e54480c9..242b4e8b 100644 --- a/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala +++ b/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala @@ -4,20 +4,19 @@ import ammonite.ops.Path import mill.define.{Target, Task} import mill.discover.Mirror import mill.eval.{Evaluator, Result} -import mill.util.OSet +import mill.util.{DummyLogger, OSet, PrintLogger} object TestEvaluator { - private val noopLogger: String => Unit = _ => () def resolveDestPaths(workspacePath: Path)(t: Mirror.LabelledTarget[_]): (Path, Path) = { - new Evaluator(workspacePath, Map.empty, noopLogger).resolveDestPaths(t) + new Evaluator(workspacePath, Map.empty, DummyLogger).resolveDestPaths(t) } def eval[T]( mapping: Map[Target[_], Mirror.LabelledTarget[_]], workspacePath: Path)(t: Task[T]): Either[Result.Failing, (T, Int)] = { - val evaluator = new Evaluator(workspacePath, mapping, noopLogger) + val evaluator = new Evaluator(workspacePath, mapping, new PrintLogger(true)) val evaluated = evaluator.evaluate(OSet(t)) if (evaluated.failing.keyCount == 0) { |