summaryrefslogtreecommitdiff
path: root/scalaplugin
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-12-10 14:43:12 -0800
committerLi Haoyi <haoyi.sg@gmail.com>2017-12-10 14:43:12 -0800
commit4a7e9f6da30f3997fcf6b3a41db07ff837708e67 (patch)
treeeeb0938dfe9f5e1435040736c12ecbea62d4ce70 /scalaplugin
parentbcbc6d2c36bfb4a3dfaa0f5572e2914fec69297d (diff)
downloadmill-4a7e9f6da30f3997fcf6b3a41db07ff837708e67.tar.gz
mill-4a7e9f6da30f3997fcf6b3a41db07ff837708e67.tar.bz2
mill-4a7e9f6da30f3997fcf6b3a41db07ff837708e67.zip
Implement a `SbtScalaModule` helper trait, to conveniently set up the default source/test locations for builds using SBT project layout
Also split out plain-old-Scala-logic in `Lib` from inheritable traits in `ScalaModule`, since `ScalaModule` was getting a bit unwieldy
Diffstat (limited to 'scalaplugin')
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala194
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala201
-rw-r--r--scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala5
-rw-r--r--scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala5
4 files changed, 208 insertions, 197 deletions
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala b/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala
new file mode 100644
index 00000000..055b3206
--- /dev/null
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala
@@ -0,0 +1,194 @@
+package mill
+package scalaplugin
+
+import java.io.File
+import java.net.URLClassLoader
+import java.util.Optional
+
+import ammonite.ops._
+import coursier.{Cache, Fetch, MavenRepository, Repository, Resolution}
+import mill.eval.{PathRef, Result}
+import mill.util.Ctx
+import sbt.internal.inc._
+import sbt.internal.util.{ConsoleOut, MainAppender}
+import sbt.util.LogExchange
+import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _}
+
+object CompilationResult {
+ implicit val jsonFormatter: upickle.default.ReadWriter[CompilationResult] = upickle.default.macroRW
+}
+
+// analysisFile is represented by Path, so we won't break caches after file changes
+case class CompilationResult(analysisFile: Path, classes: PathRef)
+
+object Lib{
+ case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup {
+ override def analysis(classpathEntry: File): Optional[CompileAnalysis] =
+ am(classpathEntry)
+
+ override def definesClass(classpathEntry: File): DefinesClass =
+ Locate.definesClass(classpathEntry)
+ }
+
+ var scalaInstanceCache = Option.empty[(Long, ScalaInstance)]
+
+ def compileScala(scalaVersion: String,
+ sources: Seq[Path],
+ compileClasspath: Seq[Path],
+ compilerClasspath: Seq[Path],
+ compilerBridge: Seq[Path],
+ scalacOptions: Seq[String],
+ scalacPluginClasspath: Seq[Path],
+ javacOptions: Seq[String],
+ upstreamCompileOutput: Seq[CompilationResult])
+ (implicit ctx: Ctx): CompilationResult = {
+ val compileClasspathFiles = compileClasspath.map(_.toIO).toArray
+
+ def grepJar(classPath: Seq[Path], s: String) = {
+ classPath
+ .find(_.toString.endsWith(s))
+ .getOrElse(throw new Exception("Cannot find " + s))
+ .toIO
+ }
+
+ val compilerJars = compilerClasspath.toArray.map(_.toIO)
+ val compilerBridgeKey = "MILL_COMPILER_BRIDGE_"+scalaVersion.replace('.', '_')
+ val compilerBridgePath = sys.props(compilerBridgeKey)
+ assert(compilerBridgePath != null, "Cannot find compiler bridge " + compilerBridgeKey)
+ val compilerBridgeJar = new java.io.File(compilerBridgePath)
+
+ val classloaderSig = compilerClasspath.map(p => p.toString().hashCode + p.mtime.toMillis).sum
+
+ 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
+ }
+
+ mkdir(ctx.dest)
+
+ val ic = new sbt.internal.inc.IncrementalCompilerImpl()
+
+ val logger = {
+ 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)
+ l
+ }
+
+ def analysisMap(f: File): Optional[CompileAnalysis] = {
+ if (f.isFile) {
+ Optional.empty[CompileAnalysis]
+ } else {
+ upstreamCompileOutput.collectFirst {
+ case CompilationResult(zincPath, classFiles) if classFiles.path.toNIO == f.toPath =>
+ FileAnalysisStore.binary(zincPath.toIO).get().map[CompileAnalysis](_.getAnalysis)
+ }.getOrElse(Optional.empty[CompileAnalysis])
+ }
+ }
+
+ val lookup = MockedLookup(analysisMap)
+
+ val zincFile = ctx.dest / 'zinc
+ val classesDir = ctx.dest / 'classes
+
+ val zincIOFile = zincFile.toIO
+ val classesIODir = classesDir.toIO
+
+ val store = FileAnalysisStore.binary(zincIOFile)
+
+ val newResult = ic.compile(
+ ic.inputs(
+ classpath = classesIODir +: compileClasspathFiles,
+ sources = sources.flatMap(ls.rec).filter(x => x.isFile && x.ext == "scala").map(_.toIO).toArray,
+ classesDirectory = classesIODir,
+ scalacOptions = (scalacPluginClasspath.map(jar => s"-Xplugin:${jar}") ++ scalacOptions).toArray,
+ javacOptions = javacOptions.toArray,
+ maxErrors = 10,
+ sourcePositionMappers = Array(),
+ order = CompileOrder.Mixed,
+ compilers = ic.compilers(
+ scalaInstance,
+ ClasspathOptionsUtil.boot,
+ None,
+ ZincUtil.scalaCompiler(scalaInstance, compilerBridgeJar)
+ ),
+ setup = ic.setup(
+ lookup,
+ skip = false,
+ zincIOFile,
+ new FreshCompilerCache,
+ IncOptions.of(),
+ new ManagedLoggedReporter(10, logger),
+ None,
+ Array()
+ ),
+ pr = {
+ val prev = store.get()
+ PreviousResult.of(prev.map(_.getAnalysis), prev.map(_.getMiniSetup))
+ }
+ ),
+ logger = logger
+ )
+
+ store.set(
+ AnalysisContents.create(
+ newResult.analysis(),
+ newResult.setup()
+ )
+ )
+
+ CompilationResult(zincFile, PathRef(classesDir))
+ }
+
+ def resolveDependencies(repositories: Seq[Repository],
+ scalaVersion: String,
+ scalaBinaryVersion: String,
+ deps: Seq[Dep],
+ sources: Boolean = false): Seq[PathRef] = {
+ val flattened = deps.map{
+ case Dep.Java(dep) => dep
+ case Dep.Scala(dep) =>
+ dep.copy(module = dep.module.copy(name = dep.module.name + "_" + scalaBinaryVersion))
+ case Dep.Point(dep) =>
+ dep.copy(module = dep.module.copy(name = dep.module.name + "_" + scalaVersion))
+ }.toSet
+ val start = Resolution(flattened)
+
+ val fetch = Fetch.from(repositories, Cache.fetch())
+ val resolution = start.process.run(fetch).unsafePerformSync
+ val sourceOrJar =
+ if (sources) resolution.classifiersArtifacts(Seq("sources"))
+ else resolution.artifacts
+ val localArtifacts: Seq[File] = scalaz.concurrent.Task
+ .gatherUnordered(sourceOrJar.map(Cache.file(_).run))
+ .unsafePerformSync
+ .flatMap(_.toOption)
+
+ localArtifacts.map(p => PathRef(Path(p), quick = true))
+ }
+ def scalaCompilerIvyDeps(scalaVersion: String) = Seq(
+ Dep.Java("org.scala-lang", "scala-compiler", scalaVersion),
+ Dep.Java("org.scala-lang", "scala-reflect", scalaVersion)
+ )
+ def scalaRuntimeIvyDeps(scalaVersion: String) = Seq[Dep](
+ Dep.Java("org.scala-lang", "scala-library", scalaVersion)
+ )
+
+ val DefaultShellScript: Seq[String] = Seq(
+ "#!/usr/bin/env sh",
+ "exec java -jar \"$0\" \"$@\""
+ )
+} \ No newline at end of file
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
index b193a8c3..8167a4f1 100644
--- a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
@@ -1,202 +1,15 @@
package mill
package scalaplugin
-import java.io.File
-import java.net.URLClassLoader
-import java.util.Optional
-
import ammonite.ops._
-import coursier.{Cache, Fetch, MavenRepository, Repository, Resolution}
+import coursier.{Cache, MavenRepository, Repository, Resolution}
import mill.define.Task
import mill.define.Task.{Module, TaskModule}
import mill.eval.{PathRef, Result}
import mill.modules.Jvm
import mill.modules.Jvm.{createAssembly, createJar, interactiveSubprocess, 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 => _, _}
-
-object CompilationResult {
- implicit val jsonFormatter: upickle.default.ReadWriter[CompilationResult] = upickle.default.macroRW
-}
-
-// analysisFile is represented by Path, so we won't break caches after file changes
-case class CompilationResult(analysisFile: Path, classes: PathRef)
-
-object ScalaModule{
- case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup {
- override def analysis(classpathEntry: File): Optional[CompileAnalysis] =
- am(classpathEntry)
-
- override def definesClass(classpathEntry: File): DefinesClass =
- Locate.definesClass(classpathEntry)
- }
-
- var scalaInstanceCache = Option.empty[(Long, ScalaInstance)]
-
- def compileScala(scalaVersion: String,
- sources: Seq[Path],
- compileClasspath: Seq[Path],
- compilerClasspath: Seq[Path],
- compilerBridge: Seq[Path],
- scalacOptions: Seq[String],
- scalacPluginClasspath: Seq[Path],
- javacOptions: Seq[String],
- upstreamCompileOutput: Seq[CompilationResult])
- (implicit ctx: Ctx): CompilationResult = {
- val compileClasspathFiles = compileClasspath.map(_.toIO).toArray
-
- def grepJar(classPath: Seq[Path], s: String) = {
- classPath
- .find(_.toString.endsWith(s))
- .getOrElse(throw new Exception("Cannot find " + s))
- .toIO
- }
-
- val compilerJars = compilerClasspath.toArray.map(_.toIO)
- val compilerBridgeKey = "MILL_COMPILER_BRIDGE_"+scalaVersion.replace('.', '_')
- val compilerBridgePath = sys.props(compilerBridgeKey)
- assert(compilerBridgePath != null, "Cannot find compiler bridge " + compilerBridgeKey)
- val compilerBridgeJar = new java.io.File(compilerBridgePath)
-
- val classloaderSig = compilerClasspath.map(p => p.toString().hashCode + p.mtime.toMillis).sum
-
- 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
- }
-
- mkdir(ctx.dest)
-
- val ic = new sbt.internal.inc.IncrementalCompilerImpl()
-
- val logger = {
- 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)
- l
- }
-
- def analysisMap(f: File): Optional[CompileAnalysis] = {
- if (f.isFile) {
- Optional.empty[CompileAnalysis]
- } else {
- upstreamCompileOutput.collectFirst {
- case CompilationResult(zincPath, classFiles) if classFiles.path.toNIO == f.toPath =>
- FileAnalysisStore.binary(zincPath.toIO).get().map[CompileAnalysis](_.getAnalysis)
- }.getOrElse(Optional.empty[CompileAnalysis])
- }
- }
- val lookup = MockedLookup(analysisMap)
-
- val zincFile = ctx.dest / 'zinc
- val classesDir = ctx.dest / 'classes
-
- val zincIOFile = zincFile.toIO
- val classesIODir = classesDir.toIO
-
- val store = FileAnalysisStore.binary(zincIOFile)
-
- val newResult = ic.compile(
- ic.inputs(
- classpath = classesIODir +: compileClasspathFiles,
- sources = sources.flatMap(ls.rec).filter(x => x.isFile && x.ext == "scala").map(_.toIO).toArray,
- classesDirectory = classesIODir,
- scalacOptions = (scalacPluginClasspath.map(jar => s"-Xplugin:${jar}") ++ scalacOptions).toArray,
- javacOptions = javacOptions.toArray,
- maxErrors = 10,
- sourcePositionMappers = Array(),
- order = CompileOrder.Mixed,
- compilers = ic.compilers(
- scalaInstance,
- ClasspathOptionsUtil.boot,
- None,
- ZincUtil.scalaCompiler(scalaInstance, compilerBridgeJar)
- ),
- setup = ic.setup(
- lookup,
- skip = false,
- zincIOFile,
- new FreshCompilerCache,
- IncOptions.of(),
- new ManagedLoggedReporter(10, logger),
- None,
- Array()
- ),
- pr = {
- val prev = store.get()
- PreviousResult.of(prev.map(_.getAnalysis), prev.map(_.getMiniSetup))
- }
- ),
- logger = logger
- )
-
- store.set(
- AnalysisContents.create(
- newResult.analysis(),
- newResult.setup()
- )
- )
-
- CompilationResult(zincFile, PathRef(classesDir))
- }
-
- def resolveDependencies(repositories: Seq[Repository],
- scalaVersion: String,
- scalaBinaryVersion: String,
- deps: Seq[Dep],
- sources: Boolean = false): Seq[PathRef] = {
- val flattened = deps.map{
- case Dep.Java(dep) => dep
- case Dep.Scala(dep) =>
- dep.copy(module = dep.module.copy(name = dep.module.name + "_" + scalaBinaryVersion))
- case Dep.Point(dep) =>
- dep.copy(module = dep.module.copy(name = dep.module.name + "_" + scalaVersion))
- }.toSet
- val start = Resolution(flattened)
-
- val fetch = Fetch.from(repositories, Cache.fetch())
- val resolution = start.process.run(fetch).unsafePerformSync
- val sourceOrJar =
- if (sources) resolution.classifiersArtifacts(Seq("sources"))
- else resolution.artifacts
- val localArtifacts: Seq[File] = scalaz.concurrent.Task
- .gatherUnordered(sourceOrJar.map(Cache.file(_).run))
- .unsafePerformSync
- .flatMap(_.toOption)
-
- localArtifacts.map(p => PathRef(Path(p), quick = true))
- }
- def scalaCompilerIvyDeps(scalaVersion: String) = Seq(
- Dep.Java("org.scala-lang", "scala-compiler", scalaVersion),
- Dep.Java("org.scala-lang", "scala-reflect", scalaVersion)
- )
- def scalaRuntimeIvyDeps(scalaVersion: String) = Seq[Dep](
- Dep.Java("org.scala-lang", "scala-library", scalaVersion)
- )
-
- val DefaultShellScript: Seq[String] = Seq(
- "#!/usr/bin/env sh",
- "exec java -jar \"$0\" \"$@\""
- )
-}
-import ScalaModule._
+import Lib._
trait TestScalaModule extends ScalaModule with TaskModule {
override def defaultCommandName() = "test"
def testFramework: T[String]
@@ -396,3 +209,13 @@ trait ScalaModule extends Module with TaskModule{ outer =>
)
}
}
+trait SbtScalaModule extends ScalaModule { outer =>
+ def basePath: Path
+ override def sources = T.source{ basePath / 'src / 'main / 'scala }
+ override def resources = T.source{ basePath / 'src / 'main / 'resources }
+ trait Tests extends super.Tests{
+ def basePath = outer.basePath
+ override def sources = T.source{ basePath / 'src / 'test / 'scala }
+ override def resources = T.source{ basePath / 'src / 'test / 'resources }
+ }
+} \ No newline at end of file
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
index 7c10cb6b..f233fdf1 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
+++ b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
@@ -10,24 +10,21 @@ import mill.util.JsonFormatters._
object AcyclicBuild{
val acyclic =
for(crossVersion <- Cross("2.10.6", "2.11.8", "2.12.3", "2.12.4"))
- yield new ScalaModule{outer =>
+ yield new SbtScalaModule{outer =>
def basePath = AcyclicTests.workspacePath
def organization = "com.lihaoyi"
def name = "acyclic"
def version = "0.1.7"
- override def sources = basePath/'src/'main/'scala
def scalaVersion = crossVersion
override def ivyDeps = Seq(
Dep.Java("org.scala-lang", "scala-compiler", scalaVersion())
)
object test extends this.Tests{
- def basePath = AcyclicTests.workspacePath
override def forkWorkingDir = pwd/'scalaplugin/'src/'test/'resource/'acyclic
override def ivyDeps = Seq(
Dep("com.lihaoyi", "utest", "0.6.0")
)
- override def sources = basePath/'src/'test/'scala
def testFramework = "utest.runner.Framework"
}
}
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala
index f2f3a832..c59beca4 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala
+++ b/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala
@@ -9,9 +9,8 @@ import utest._
import mill.util.JsonFormatters._
object BetterFilesBuild{
- trait BetterFilesModule extends ScalaModule{ outer =>
+ trait BetterFilesModule extends SbtScalaModule{ outer =>
def scalaVersion = "2.12.4"
- override def sources = basePath/'src/'main/'scala
override def scalacOptions = Seq(
"-deprecation", // Emit warning and location for usages of deprecated APIs.
"-encoding", "utf-8", // Specify character encoding used by source files.
@@ -64,9 +63,7 @@ object BetterFilesBuild{
override def projectDeps =
if (this == Core.test) Seq(Core)
else Seq(outer, Core.test)
- def basePath = outer.basePath
override def ivyDeps = Seq(Dep("org.scalatest", "scalatest", "3.0.4"))
- override def sources = basePath/'src/'test/'scala
def testFramework = "org.scalatest.tools.Framework"
}
}