From ceb1777fa075d329d6d2cb5c20201749591e0ee4 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 25 Jul 2018 17:30:16 +0200 Subject: [PATCH 1/6] Abstract over the scala compiler organization --- docs/pages/2 - Configuring Mill.md | 2 +- integration/test/resources/acyclic/build.sc | 2 +- integration/test/resources/ammonite/build.sc | 8 ++++---- integration/test/resources/play-json/build.sc | 4 ++-- integration/test/resources/upickle/build.sc | 4 ++-- scalajslib/src/mill/scalajslib/ScalaJSModule.scala | 1 + scalalib/src/mill/scalalib/Lib.scala | 10 +++++----- scalalib/src/mill/scalalib/ScalaModule.scala | 11 +++++++---- scalalib/test/src/mill/scalalib/HelloWorldTests.scala | 6 +----- .../src/mill/scalanativelib/ScalaNativeModule.scala | 2 ++ 10 files changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/pages/2 - Configuring Mill.md b/docs/pages/2 - Configuring Mill.md index 7ac20f99a39..a48e158fc47 100644 --- a/docs/pages/2 - Configuring Mill.md +++ b/docs/pages/2 - Configuring Mill.md @@ -48,7 +48,7 @@ object foo extends ScalaModule { ivy"com.lihaoyi::upickle:0.5.1", ivy"com.lihaoyi::pprint:0.5.2", ivy"com.lihaoyi::fansi:0.2.4", - ivy"org.scala-lang:scala-reflect:${scalaVersion()}" + ivy"${scalaOrganization()}:scala-reflect:${scalaVersion()}" ) } ``` diff --git a/integration/test/resources/acyclic/build.sc b/integration/test/resources/acyclic/build.sc index ce69f05d392..73a3cb99cbb 100644 --- a/integration/test/resources/acyclic/build.sc +++ b/integration/test/resources/acyclic/build.sc @@ -20,7 +20,7 @@ class AcyclicModule(val crossScalaVersion: String) extends CrossSbtModule with P ) def ivyDeps = Agg( - ivy"org.scala-lang:scala-compiler:${scalaVersion()}" + ivy"${scalaOrganization()}:scala-compiler:${scalaVersion()}" ) object test extends Tests{ def forkWorkingDir = ammonite.ops.pwd / 'target / 'workspace / 'acyclic diff --git a/integration/test/resources/ammonite/build.sc b/integration/test/resources/ammonite/build.sc index 39134e3ded5..c26d5050314 100644 --- a/integration/test/resources/ammonite/build.sc +++ b/integration/test/resources/ammonite/build.sc @@ -33,7 +33,7 @@ class TerminalModule(val crossScalaVersion: String) extends AmmModule{ ivy"com.lihaoyi::fansi:0.2.4" ) def compileIvyDeps = Agg( - ivy"org.scala-lang:scala-reflect:$crossScalaVersion" + ivy"${scalaOrganization()}:scala-reflect:$crossScalaVersion" ) object test extends Tests @@ -49,7 +49,7 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){ ivy"com.lihaoyi::fansi:0.2.4" ) def compileIvyDeps = Agg( - ivy"org.scala-lang:scala-reflect:$crossScalaVersion" + ivy"${scalaOrganization()}:scala-reflect:$crossScalaVersion" ) } @@ -75,8 +75,8 @@ object amm extends Cross[MainModule](fullCrossScalaVersions:_*){ class InterpModule(val crossScalaVersion: String) extends AmmModule{ def moduleDeps = Seq(ops(), amm.util(), amm.runtime()) def ivyDeps = Agg( - ivy"org.scala-lang:scala-compiler:$crossScalaVersion", - ivy"org.scala-lang:scala-reflect:$crossScalaVersion", + ivy"${scalaOrganization()}:scala-compiler:$crossScalaVersion", + ivy"${scalaOrganization()}:scala-reflect:$crossScalaVersion", ivy"com.lihaoyi::scalaparse:1.0.0", ivy"org.javassist:javassist:3.21.0-GA" ) diff --git a/integration/test/resources/play-json/build.sc b/integration/test/resources/play-json/build.sc index c60eefbac01..d4557011a24 100644 --- a/integration/test/resources/play-json/build.sc +++ b/integration/test/resources/play-json/build.sc @@ -61,7 +61,7 @@ abstract class PlayJson(val platformSegment: String) extends PlayJsonModule { ) def ivyDeps = Agg( - ivy"org.scala-lang:scala-reflect:${scalaVersion()}", + ivy"${scalaOrganization()}:scala-reflect:${scalaVersion()}", ivy"org.typelevel::macro-compat::1.1.1" ) @@ -69,7 +69,7 @@ abstract class PlayJson(val platformSegment: String) extends PlayJsonModule { def compileIvyDeps = Agg( macroParadise, - ivy"org.scala-lang:scala-compiler:${scalaVersion()}" + ivy"${scalaOrganization()}:scala-compiler:${scalaVersion()}" ) def scalacPluginIvyDeps = Agg(macroParadise) diff --git a/integration/test/resources/upickle/build.sc b/integration/test/resources/upickle/build.sc index 2ea7668daa0..1f5f2bedce1 100644 --- a/integration/test/resources/upickle/build.sc +++ b/integration/test/resources/upickle/build.sc @@ -23,8 +23,8 @@ trait UpickleModule extends CrossSbtModule with PublishModule{ ) def compileIvyDeps = Agg( ivy"com.lihaoyi::acyclic:0.1.5", - ivy"org.scala-lang:scala-reflect:${scalaVersion()}", - ivy"org.scala-lang:scala-compiler:${scalaVersion()}" + ivy"${scalaOrganization()}:scala-reflect:${scalaVersion()}", + ivy"${scalaOrganization()}:scala-compiler:${scalaVersion()}" ) def ivyDeps = Agg( ivy"com.lihaoyi::sourcecode::0.1.3" diff --git a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala index 1b99f1427b1..00963f00418 100644 --- a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala +++ b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala @@ -16,6 +16,7 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer => trait Tests extends TestScalaJSModule { override def scalaWorker = outer.scalaWorker + override def scalaOrganization = outer.scalaOrganization() override def scalaVersion = outer.scalaVersion() override def scalaJSVersion = outer.scalaJSVersion() override def moduleDeps = Seq(outer) diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index b69409a653b..7be9b3a7848 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -142,12 +142,12 @@ object Lib{ mapDependencies ) } - def scalaCompilerIvyDeps(scalaVersion: String) = Agg[Dep]( - ivy"org.scala-lang:scala-compiler:$scalaVersion".forceVersion(), - ivy"org.scala-lang:scala-reflect:$scalaVersion".forceVersion() + def scalaCompilerIvyDeps(scalaOrganization: String, scalaVersion: String) = Agg[Dep]( + ivy"$scalaOrganization:scala-compiler:$scalaVersion".forceVersion(), + ivy"$scalaOrganization:scala-reflect:$scalaVersion".forceVersion() ) - def scalaRuntimeIvyDeps(scalaVersion: String) = Agg[Dep]( - ivy"org.scala-lang:scala-library:$scalaVersion".forceVersion() + def scalaRuntimeIvyDeps(scalaOrganization: String, scalaVersion: String) = Agg[Dep]( + ivy"$scalaOrganization:scala-library:$scalaVersion".forceVersion() ) def listClassFiles(base: Path): Iterator[String] = { diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 3cea7fab50d..50eb81d52c0 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -17,6 +17,7 @@ import mill.util.DummyInputStream */ trait ScalaModule extends JavaModule { outer => trait Tests extends TestModule with ScalaModule{ + override def scalaOrganization = outer.scalaOrganization() def scalaVersion = outer.scalaVersion() override def repositories = outer.repositories override def scalacPluginIvyDeps = outer.scalacPluginIvyDeps @@ -25,12 +26,13 @@ trait ScalaModule extends JavaModule { outer => override def scalaWorker = outer.scalaWorker override def moduleDeps: Seq[JavaModule] = Seq(outer) } + def scalaOrganization: T[String] = "org.scala-lang" def scalaVersion: T[String] override def mapDependencies = T.task{ d: coursier.Dependency => val artifacts = Set("scala-library", "scala-compiler", "scala-reflect") - if (d.module.organization != "org.scala-lang" || !artifacts(d.module.name)) d - else d.copy(version = scalaVersion()) + if (!artifacts(d.module.name)) d + else d.copy(module = d.module.copy(organization = scalaOrganization()), version = scalaVersion()) } override def resolveCoursierDependency: Task[Dep => coursier.Dependency] = T.task{ @@ -89,13 +91,14 @@ trait ScalaModule extends JavaModule { outer => resolveDeps(scalacPluginIvyDeps)() } - def scalaLibraryIvyDeps = T{ scalaRuntimeIvyDeps(scalaVersion()) } + def scalaLibraryIvyDeps = T{ scalaRuntimeIvyDeps(scalaOrganization(), scalaVersion()) } /** * Classpath of the Scala Compiler & any compiler plugins */ def scalaCompilerClasspath: T[Agg[PathRef]] = T{ resolveDeps( - T.task{scalaCompilerIvyDeps(scalaVersion()) ++ scalaRuntimeIvyDeps(scalaVersion())} + T.task{scalaCompilerIvyDeps(scalaOrganization(), scalaVersion()) ++ + scalaRuntimeIvyDeps(scalaOrganization(), scalaVersion())} )() } override def compileClasspath = T{ diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index 7a1dd6dff8b..f603869800b 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -190,11 +190,7 @@ object HelloWorldTests extends TestSuite { object HelloWorldTypeLevel extends HelloBase{ object foo extends ScalaModule { def scalaVersion = "2.11.8" - override def mapDependencies = T.task{ d: coursier.Dependency => - val artifacts = Set("scala-library", "scala-compiler", "scala-reflect") - if (d.module.organization != "org.scala-lang" || !artifacts(d.module.name)) d - else d.copy(module = d.module.copy(organization = "org.typelevel")) - } + override def scalaOrganization = "org.typelevel" def ivyDeps = Agg( ivy"com.github.julien-truffaut::monocle-macro::1.4.0" diff --git a/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala b/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala index 8613c42f2fc..d6064564c13 100644 --- a/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala +++ b/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala @@ -47,6 +47,7 @@ trait ScalaNativeModule extends ScalaModule { outer => trait Tests extends TestScalaNativeModule { override def scalaWorker = outer.scalaWorker + override def scalaOrganization = outer.scalaOrganization() override def scalaVersion = outer.scalaVersion() override def scalaNativeVersion = outer.scalaNativeVersion() override def releaseMode = outer.releaseMode() @@ -238,6 +239,7 @@ trait TestScalaNativeModule extends ScalaNativeModule with TestModule { testOute // which knows the names of all the tests and references to invoke them object testRunnerNative extends ScalaNativeModule { override def scalaWorker = testOuter.scalaWorker + override def scalaOrganization = testOuter.scalaOrganization() override def scalaVersion = testOuter.scalaVersion() override def scalaNativeVersion = testOuter.scalaNativeVersion() override def moduleDeps = Seq(testOuter) From 41b68a1d289a3d2cf8207e86f5896409bb9243ba Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 30 Jul 2018 14:30:54 +0200 Subject: [PATCH 2/6] Support using a locally published compiler Publishing locally with sbt means publishing ivy-style, which uses a different naming convention than maven, we now handle both cases. --- scalalib/src/mill/scalalib/Lib.scala | 10 ++++++---- scalalib/src/mill/scalalib/ScalaModule.scala | 7 ++++++- .../worker/src/mill/scalalib/worker/ScalaWorker.scala | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index 7be9b3a7848..2404ab73eaa 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -65,11 +65,13 @@ object Lib{ } } - def grepJar(classPath: Agg[Path], s: String) = { + def grepJar(classPath: Agg[Path], name: String, version: String) = { + val mavenStylePath = s"$name-$version.jar" + val ivyStylePath = s"$version/$name.jar" + classPath - .find(_.toString.endsWith(s)) - .getOrElse(throw new Exception("Cannot find " + s)) - .toIO + .find(p => p.toString.endsWith(mavenStylePath) || p.toString.endsWith(ivyStylePath)) + .getOrElse(throw new Exception(s"Cannot find $mavenStylePath or $ivyStylePath")) } diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 50eb81d52c0..bbadb962c3b 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -84,7 +84,12 @@ trait ScalaModule extends JavaModule { outer => Lib.depToDependency(_, scalaVersion0, platformSuffix()), Seq(ivy"org.scala-sbt::compiler-bridge:${Versions.zinc}"), sources = true - ).map(_.find(_.path.last == s"compiler-bridge_${scalaBinaryVersion0}-${Versions.zinc}-sources.jar").map(_.path).get) + ).map(deps => + grepJar( + deps.map(_.path), + s"compiler-bridge_${scalaBinaryVersion0}", s"${Versions.zinc}-sources" + ) + ) } def scalacPluginClasspath: T[Agg[PathRef]] = T { diff --git a/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala b/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala index 76f185c53ca..dcd8fcb8036 100644 --- a/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala +++ b/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala @@ -100,8 +100,8 @@ class ScalaWorker(ctx0: mill.util.Ctx, val scalaInstance = new ScalaInstance( version = scalaVersion, loader = mill.util.ClassLoader.create(compilerJars.map(_.toURI.toURL), null), - libraryJar = grepJar(compilerClasspath, s"scala-library-$scalaVersion.jar"), - compilerJar = grepJar(compilerClasspath, s"scala-compiler-$scalaVersion.jar"), + libraryJar = grepJar(compilerClasspath, "scala-library", scalaVersion).toIO, + compilerJar = grepJar(compilerClasspath, "scala-compiler", scalaVersion).toIO, allJars = compilerJars, explicitActual = None ) From a219acfa55b7b64192eadcc04ca82e67f0b65329 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 30 Jul 2018 15:01:42 +0200 Subject: [PATCH 3/6] Add minimal support for Dotty projects --- scalalib/src/mill/scalalib/Lib.scala | 18 ++++++-- scalalib/src/mill/scalalib/ScalaModule.scala | 41 +++++++++++++++---- .../resources/hello-dotty/foo/src/Main.scala | 13 ++++++ .../src/mill/scalalib/HelloWorldTests.scala | 13 ++++++ .../mill/scalalib/worker/ScalaWorker.scala | 20 ++++++--- 5 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 scalalib/test/resources/hello-dotty/foo/src/Main.scala diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index 2404ab73eaa..3d581e92c6d 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -56,15 +56,20 @@ object Lib{ private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r + private val DottyVersion = raw"""0\.(\d+)\.(\d+).*""".r def scalaBinaryVersion(scalaVersion: String) = { scalaVersion match { case ReleaseVersion(major, minor, _) => s"$major.$minor" case MinorSnapshotVersion(major, minor, _) => s"$major.$minor" + case DottyVersion(minor, _) => s"0.$minor" case _ => scalaVersion } } + def isDotty(scalaVersion: String) = + scalaVersion.startsWith("0.") + def grepJar(classPath: Agg[Path], name: String, version: String) = { val mavenStylePath = s"$name-$version.jar" val ivyStylePath = s"$version/$name.jar" @@ -144,10 +149,15 @@ object Lib{ mapDependencies ) } - def scalaCompilerIvyDeps(scalaOrganization: String, scalaVersion: String) = Agg[Dep]( - ivy"$scalaOrganization:scala-compiler:$scalaVersion".forceVersion(), - ivy"$scalaOrganization:scala-reflect:$scalaVersion".forceVersion() - ) + def scalaCompilerIvyDeps(scalaOrganization: String, scalaVersion: String) = + if (isDotty(scalaVersion)) + Agg(ivy"$scalaOrganization::dotty-compiler:$scalaVersion".forceVersion()) + else + Agg( + ivy"$scalaOrganization:scala-compiler:$scalaVersion".forceVersion(), + ivy"$scalaOrganization:scala-reflect:$scalaVersion".forceVersion() + ) + def scalaRuntimeIvyDeps(scalaOrganization: String, scalaVersion: String) = Agg[Dep]( ivy"$scalaOrganization:scala-library:$scalaVersion".forceVersion() ) diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index bbadb962c3b..ad3e8e36b92 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -26,11 +26,22 @@ trait ScalaModule extends JavaModule { outer => override def scalaWorker = outer.scalaWorker override def moduleDeps: Seq[JavaModule] = Seq(outer) } - def scalaOrganization: T[String] = "org.scala-lang" + + def scalaOrganization: T[String] = T { + if (isDotty(scalaVersion())) + "ch.epfl.lamp" + else + "org.scala-lang" + } + def scalaVersion: T[String] override def mapDependencies = T.task{ d: coursier.Dependency => - val artifacts = Set("scala-library", "scala-compiler", "scala-reflect") + val artifacts = + if (isDotty(scalaVersion())) + Set("dotty-library", "dotty-compiler") + else + Set("scala-library", "scala-compiler", "scala-reflect") if (!artifacts(d.module.name)) d else d.copy(module = d.module.copy(organization = scalaOrganization()), version = scalaVersion()) } @@ -79,16 +90,26 @@ trait ScalaModule extends JavaModule { outer => case _ => (scalaVersion(), Lib.scalaBinaryVersion(scalaVersion())) } + val (bridgeDep, bridgeName, bridgeVersion) = + if (isDotty(scalaVersion0)) { + val org = scalaOrganization() + val name = "dotty-sbt-bridge" + val version = scalaVersion() + (ivy"$org:$name:$version", name, version) + } else { + val org = "org.scala-sbt" + val name = "compiler-bridge" + val version = Versions.zinc + (ivy"$org::$name:$version", s"${name}_$scalaBinaryVersion0", version) + } + resolveDependencies( repositories, Lib.depToDependency(_, scalaVersion0, platformSuffix()), - Seq(ivy"org.scala-sbt::compiler-bridge:${Versions.zinc}"), + Seq(bridgeDep), sources = true ).map(deps => - grepJar( - deps.map(_.path), - s"compiler-bridge_${scalaBinaryVersion0}", s"${Versions.zinc}-sources" - ) + grepJar(deps.map(_.path), bridgeName, s"$bridgeVersion-sources") ) } @@ -163,7 +184,11 @@ trait ScalaModule extends JavaModule { outer => Result.Failure("repl needs to be run with the -i/--interactive flag") }else{ Jvm.interactiveSubprocess( - mainClass = "scala.tools.nsc.MainGenericRunner", + mainClass = + if (isDotty(scalaVersion())) + "dotty.tools.repl.Main" + else + "scala.tools.nsc.MainGenericRunner", classPath = runClasspath().map(_.path) ++ scalaCompilerClasspath().map(_.path), mainArgs = Seq("-usejavacp"), workingDir = pwd diff --git a/scalalib/test/resources/hello-dotty/foo/src/Main.scala b/scalalib/test/resources/hello-dotty/foo/src/Main.scala new file mode 100644 index 00000000000..b6fc468d519 --- /dev/null +++ b/scalalib/test/resources/hello-dotty/foo/src/Main.scala @@ -0,0 +1,13 @@ +trait Context + +object Main { + def foo(f: implicit Int => Int): Int = { + implicit val x: Int = 1 + f + } + + def main(args: Array[String]): Unit = { + val value = foo(implicit x => x + 1) + assert(value == 2) + } +} diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index f603869800b..f13c4e037a0 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -234,6 +234,12 @@ object HelloWorldTests extends TestSuite { } } + object HelloDotty extends HelloBase{ + object foo extends ScalaModule { + def scalaVersion = "0.9.0-RC1" + } + } + val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world" def jarMainClass(jar: JarFile): Option[String] = { @@ -811,5 +817,12 @@ object HelloWorldTests extends TestSuite { ) } + 'dotty - workspaceTest( + HelloDotty, + resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-dotty" + ){ eval => + val Right((_, evalCount)) = eval.apply(HelloDotty.foo.run()) + assert(evalCount > 0) + } } } diff --git a/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala b/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala index dcd8fcb8036..c9f78fd0342 100644 --- a/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala +++ b/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala @@ -9,7 +9,7 @@ import mill.Agg import mill.eval.PathRef import mill.scalalib.{CompilationResult, Lib, TestRunner} import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} -import mill.scalalib.Lib.grepJar +import mill.scalalib.Lib.{grepJar, isDotty, scalaBinaryVersion} import mill.util.{Ctx, PrintLogger} import sbt.internal.inc._ import sbt.internal.util.{ConsoleOut, MainAppender} @@ -44,15 +44,18 @@ class ScalaWorker(ctx0: mill.util.Ctx, val sourceFolder = mill.modules.Util.unpackZip(sourcesJar)(workingDir) val classloader = mill.util.ClassLoader.create(compilerJars.map(_.toURI.toURL), null)(ctx0) - val scalacMain = classloader.loadClass("scala.tools.nsc.Main") + val compilerMain = classloader.loadClass( + if (isDotty(scalaVersion)) + "dotty.tools.dotc.Main" + else + "scala.tools.nsc.Main" + ) val argsArray = Array[String]( "-d", compiledDest.toString, "-classpath", (compilerJars ++ compilerBridgeClasspath).mkString(File.pathSeparator) ) ++ ls.rec(sourceFolder.path).filter(_.ext == "scala").map(_.toString) - scalacMain.getMethods - .find(_.getName == "process") - .get + compilerMain.getMethod("process", classOf[Array[String]]) .invoke(null, argsArray) } compiledDest @@ -97,11 +100,16 @@ class ScalaWorker(ctx0: mill.util.Ctx, val compilers = compilersCache match { case Some((k, v)) if k == compilersSig => v case _ => + val compilerName = + if (isDotty(scalaVersion)) + s"dotty-compiler_${scalaBinaryVersion(scalaVersion)}" + else + "scala-compiler" val scalaInstance = new ScalaInstance( version = scalaVersion, loader = mill.util.ClassLoader.create(compilerJars.map(_.toURI.toURL), null), libraryJar = grepJar(compilerClasspath, "scala-library", scalaVersion).toIO, - compilerJar = grepJar(compilerClasspath, "scala-compiler", scalaVersion).toIO, + compilerJar = grepJar(compilerClasspath, compilerName, scalaVersion).toIO, allJars = compilerJars, explicitActual = None ) From 334617e5efd354d7d2a04ed9f33112b8d3130033 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 31 Jul 2018 16:23:48 +0200 Subject: [PATCH 4/6] Rewrite scalalib.Dep, introduce scalalib.CrossVersion Instead of Dep being a trait with three cases (Java/Scala/Point), it is now a case class where the cross field is an instance of the CrossVersion trait which has three cases (Constant/Binary/Full). This is more versatile since it allows for non-empty constant suffixes which will be used to implement withDottyCompat in the next commit. It's also a cleaner separation of concerns. We also deduplicate various pieces of codes that computed the artifact name: this is now always handled in Dep and CrossVersion. --- scalalib/src/mill/scalalib/Dep.scala | 117 ++++++++++-------- scalalib/src/mill/scalalib/Lib.scala | 41 ++---- .../src/mill/scalalib/publish/settings.scala | 52 +++----- .../scalanativelib/ScalaNativeModule.scala | 2 +- 4 files changed, 90 insertions(+), 122 deletions(-) diff --git a/scalalib/src/mill/scalalib/Dep.scala b/scalalib/src/mill/scalalib/Dep.scala index 9719bd2d4be..22824de31ba 100644 --- a/scalalib/src/mill/scalalib/Dep.scala +++ b/scalalib/src/mill/scalalib/Dep.scala @@ -1,32 +1,29 @@ package mill.scalalib import mill.util.JsonFormatters._ import upickle.default.{macroRW, ReadWriter => RW} -sealed trait Dep { - def configure(attributes: coursier.Attributes): Dep - def force: Boolean - def forceVersion(): Dep = this match { - case dep : Dep.Java => dep.copy(force = true) - case dep : Dep.Scala => dep.copy(force = true) - case dep : Dep.Point => dep.copy(force = true) - } - def exclude(exclusions: (String, String)*): Dep = this match { - case dep : Dep.Java => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) - case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) - case dep : Dep.Point => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions)) + +import CrossVersion._ + +case class Dep(dep: coursier.Dependency, cross: CrossVersion, force: Boolean) { + def artifactName(binaryVersion: String, fullVersion: String, platformSuffix: String) = { + val suffix = cross.suffixString(binaryVersion, fullVersion, platformSuffix) + dep.module.name + suffix } + def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) + def forceVersion(): Dep = copy(force = true) + def exclude(exclusions: (String, String)*) = copy(dep = dep.copy(exclusions = dep.exclusions ++ exclusions)) def excludeOrg(organizations: String*): Dep = exclude(organizations.map(_ -> "*"): _*) def excludeName(names: String*): Dep = exclude(names.map("*" -> _): _*) - def withConfiguration(configuration: String): Dep = this match { - case dep : Dep.Java => dep.copy(dep = dep.dep.copy(configuration = configuration)) - case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(configuration = configuration)) - case dep : Dep.Point => dep.copy(dep = dep.dep.copy(configuration = configuration)) - } + def toDependency(binaryVersion: String, fullVersion: String, platformSuffix: String) = + dep.copy(module = dep.module.copy(name = artifactName(binaryVersion, fullVersion, platformSuffix))) + def withConfiguration(configuration: String): Dep = copy(dep = dep.copy(configuration = configuration)) } -object Dep{ + +object Dep { val DefaultConfiguration = "default(compile)" - implicit def parse(signature: String) = { + implicit def parse(signature: String): Dep = { val parts = signature.split(';') val module = parts.head val attributes = parts.tail.foldLeft(coursier.Attributes()) { (as, s) => @@ -37,48 +34,60 @@ object Dep{ } } (module.split(':') match { - case Array(a, b, c) => Dep.Java(a, b, c, cross = false, force = false) - case Array(a, b, "", c) => Dep.Java(a, b, c, cross = true, force = false) - case Array(a, "", b, c) => Dep.Scala(a, b, c, cross = false, force = false) - case Array(a, "", b, "", c) => Dep.Scala(a, b, c, cross = true, force = false) - case Array(a, "", "", b, c) => Dep.Point(a, b, c, cross = false, force = false) - case Array(a, "", "", b, "", c) => Dep.Point(a, b, c, cross = true, force = false) + case Array(a, b, c) => Dep(a, b, c, cross = empty(platformed = false)) + case Array(a, b, "", c) => Dep(a, b, c, cross = empty(platformed = true)) + case Array(a, "", b, c) => Dep(a, b, c, cross = Binary(platformed = false)) + case Array(a, "", b, "", c) => Dep(a, b, c, cross = Binary(platformed = true)) + case Array(a, "", "", b, c) => Dep(a, b, c, cross = Full(platformed = false)) + case Array(a, "", "", b, "", c) => Dep(a, b, c, cross = Full(platformed = true)) case _ => throw new Exception(s"Unable to parse signature: [$signature]") }).configure(attributes = attributes) } - def apply(org: String, name: String, version: String, cross: Boolean): Dep = { - this(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross) - } - case class Java(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep { - def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) + def apply(org: String, name: String, version: String, cross: CrossVersion, force: Boolean = false): Dep = { + apply(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) } - object Java{ - implicit def rw: RW[Java] = macroRW - def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = { - Java(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) + implicit def rw: RW[Dep] = macroRW +} + +sealed trait CrossVersion { + /** If true, the cross-version suffix should start with a platform suffix if it exists */ + def platformed: Boolean + + def isBinary: Boolean = + this.isInstanceOf[Binary] + def isConstant: Boolean = + this.isInstanceOf[Constant] + def isFull: Boolean = + this.isInstanceOf[Full] + + /** The string that should be appended to the module name to get the artifact name */ + def suffixString(binaryVersion: String, fullVersion: String, platformSuffix: String): String = { + val firstSuffix = if (platformed) platformSuffix else "" + this match { + case cross: Constant => + s"${firstSuffix}${cross.value}" + case cross: Binary => + s"${firstSuffix}_${binaryVersion}" + case cross: Full => + s"${firstSuffix}_${fullVersion}" } } - implicit def default(dep: coursier.Dependency): Dep = new Java(dep, false, false) - def apply(dep: coursier.Dependency, cross: Boolean) = Scala(dep, cross, false) - case class Scala(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep { - def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) - } - object Scala{ - implicit def rw: RW[Scala] = macroRW - def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = { - Scala(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) - } +} +object CrossVersion { + case class Constant(value: String, platformed: Boolean) extends CrossVersion + object Constant { + implicit def rw: RW[Constant] = macroRW } - case class Point(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep { - def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes)) + case class Binary(platformed: Boolean) extends CrossVersion + object Binary { + implicit def rw: RW[Binary] = macroRW } - object Point{ - implicit def rw: RW[Point] = macroRW - def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = { - Point(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force) - } + case class Full(platformed: Boolean) extends CrossVersion + object Full { + implicit def rw: RW[Full] = macroRW } - implicit def rw = RW.merge[Dep]( - Java.rw, Scala.rw, Point.rw - ) + + def empty(platformed: Boolean) = Constant(value = "", platformed) + + implicit def rw: RW[CrossVersion] = RW.merge(Constant.rw, Binary.rw, Full.rw) } diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index 3d581e92c6d..4ce5cf8366c 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -79,42 +79,17 @@ object Lib{ .getOrElse(throw new Exception(s"Cannot find $mavenStylePath or $ivyStylePath")) } - def depToDependencyJava(dep: Dep, platformSuffix: String = ""): Dependency = { - dep match { - case Dep.Java(dep, cross, force) => - dep.copy( - module = dep.module.copy( - name = - dep.module.name + - (if (!cross) "" else platformSuffix) - ) - ) - } + assert(dep.cross.isConstant, s"Not a Java dependency: $dep") + depToDependency(dep, "", platformSuffix) } - def depToDependency(dep: Dep, scalaVersion: String, platformSuffix: String = ""): Dependency = - dep match { - case d: Dep.Java => depToDependencyJava(dep) - case Dep.Scala(dep, cross, force) => - dep.copy( - module = dep.module.copy( - name = - dep.module.name + - (if (!cross) "" else platformSuffix) + - "_" + scalaBinaryVersion(scalaVersion) - ) - ) - case Dep.Point(dep, cross, force) => - dep.copy( - module = dep.module.copy( - name = - dep.module.name + - (if (!cross) "" else platformSuffix) + - "_" + scalaVersion - ) - ) - } + def depToDependency(dep: Dep, scalaVersion: String, platformSuffix: String = ""): Dependency = + dep.toDependency( + binaryVersion = scalaBinaryVersion(scalaVersion), + fullVersion = scalaVersion, + platformSuffix = platformSuffix + ) def resolveDependenciesMetadata(repositories: Seq[Repository], depToDependency: Dep => coursier.Dependency, diff --git a/scalalib/src/mill/scalalib/publish/settings.scala b/scalalib/src/mill/scalalib/publish/settings.scala index 9a59f09d8c2..bca81cf018a 100644 --- a/scalalib/src/mill/scalalib/publish/settings.scala +++ b/scalalib/src/mill/scalalib/publish/settings.scala @@ -8,45 +8,29 @@ case class Artifact(group: String, id: String, version: String) { object Artifact { def fromDepJava(dep: Dep) = { - dep match { - case Dep.Java(dep, cross, force) => - Dependency( - Artifact(dep.module.organization, dep.module.name, dep.version), - Scope.Compile, - if (dep.configuration == "") None else Some(dep.configuration), - dep.exclusions.toList - ) - } + assert(dep.cross.isConstant, s"Not a Java dependency: $dep") + fromDep(dep, "", "", "") } + def fromDep(dep: Dep, scalaFull: String, scalaBin: String, platformSuffix: String): Dependency = { - dep match { - case d: Dep.Java => fromDepJava(d) - case Dep.Scala(dep, cross, force) => - Dependency( - Artifact( - dep.module.organization, - s"${dep.module.name}${platformSuffix}_${scalaBin}", - dep.version - ), - Scope.Compile, - if (dep.configuration == "") None else Some(dep.configuration), - dep.exclusions.toList - ) - case Dep.Point(dep, cross, force) => - Dependency( - Artifact( - dep.module.organization, - s"${dep.module.name}${platformSuffix}_${scalaFull}", - dep.version - ), - Scope.Compile, - if (dep.configuration == "") None else Some(dep.configuration), - dep.exclusions.toList - ) - } + val name = dep.artifactName( + binaryVersion = scalaBin, + fullVersion = scalaFull, + platformSuffix = platformSuffix + ) + Dependency( + Artifact( + dep.dep.module.organization, + name, + dep.dep.version + ), + Scope.Compile, + if (dep.dep.configuration == "") None else Some(dep.dep.configuration), + dep.dep.exclusions.toList + ) } } diff --git a/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala b/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala index d6064564c13..d3cde4d1747 100644 --- a/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala +++ b/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala @@ -224,7 +224,7 @@ trait TestScalaNativeModule extends ScalaNativeModule with TestModule { testOute Lib.resolveDependencies( repositories, Lib.depToDependency(_, scalaVersion(), ""), - transitiveIvyDeps().collect{ case x: Dep.Scala => x }.filter(d => supportedTestFrameworks(d.dep.module.name)) + transitiveIvyDeps().filter(d => d.cross.isBinary && supportedTestFrameworks(d.dep.module.name)) ) } From a6b7886cf439f632ce4a085d6174361ab8452a1f Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 31 Jul 2018 21:50:34 +0200 Subject: [PATCH 5/6] Add simple way to use Scala 2 deps in a Dotty project This is similar to the withDottyCompat method in the sbt-dotty plugin. --- scalalib/src/mill/scalalib/Dep.scala | 31 +++++++++++++++++++ scalalib/src/mill/scalalib/Lib.scala | 4 +-- scalalib/src/mill/scalalib/ScalaModule.scala | 1 + .../resources/hello-dotty/foo/src/Main.scala | 4 +++ .../src/mill/scalalib/HelloWorldTests.scala | 1 + .../mill/scalalib/worker/ScalaWorker.scala | 3 +- 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/scalalib/src/mill/scalalib/Dep.scala b/scalalib/src/mill/scalalib/Dep.scala index 22824de31ba..b419462c980 100644 --- a/scalalib/src/mill/scalalib/Dep.scala +++ b/scalalib/src/mill/scalalib/Dep.scala @@ -5,6 +5,8 @@ import upickle.default.{macroRW, ReadWriter => RW} import CrossVersion._ case class Dep(dep: coursier.Dependency, cross: CrossVersion, force: Boolean) { + import Dep.isDotty + def artifactName(binaryVersion: String, fullVersion: String, platformSuffix: String) = { val suffix = cross.suffixString(binaryVersion, fullVersion, platformSuffix) dep.module.name + suffix @@ -17,12 +19,41 @@ case class Dep(dep: coursier.Dependency, cross: CrossVersion, force: Boolean) { def toDependency(binaryVersion: String, fullVersion: String, platformSuffix: String) = dep.copy(module = dep.module.copy(name = artifactName(binaryVersion, fullVersion, platformSuffix))) def withConfiguration(configuration: String): Dep = copy(dep = dep.copy(configuration = configuration)) + + /** + * If scalaVersion is a Dotty version, replace the cross-version suffix + * by the Scala 2.x version that the Dotty version is retro-compatible with, + * otherwise do nothing. + * + * This setting is useful when your build contains dependencies that have only + * been published with Scala 2.x, if you have: + * {{{ + * def ivyDeps = Agg(ivy"a::b:c") + * }}} + * you can replace it by: + * {{{ + * def ivyDeps = Agg(ivy"a::b:c".withDottyCompat(scalaVersion())) + * }}} + * This will have no effect when compiling with Scala 2.x, but when compiling + * with Dotty this will change the cross-version to a Scala 2.x one. This + * works because Dotty is currently retro-compatible with Scala 2.x. + */ + def withDottyCompat(scalaVersion: String): Dep = + cross match { + case cross: Binary if isDotty(scalaVersion) => + copy(cross = Constant(value = "_2.12", platformed = cross.platformed)) + case _ => + this + } } object Dep { val DefaultConfiguration = "default(compile)" + def isDotty(scalaVersion: String) = + scalaVersion.startsWith("0.") + implicit def parse(signature: String): Dep = { val parts = signature.split(';') val module = parts.head diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index 4ce5cf8366c..e890caaf59d 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -10,6 +10,7 @@ import javax.tools.ToolProvider import ammonite.ops._ import ammonite.util.Util import coursier.{Cache, Dependency, Fetch, Repository, Resolution} +import Dep.isDotty import mill.Agg import mill.eval.{PathRef, Result} import mill.modules.Jvm @@ -67,9 +68,6 @@ object Lib{ } } - def isDotty(scalaVersion: String) = - scalaVersion.startsWith("0.") - def grepJar(classPath: Agg[Path], name: String, version: String) = { val mavenStylePath = s"$name-$version.jar" val ivyStylePath = s"$version/$name.jar" diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index ad3e8e36b92..1899ee1477c 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -8,6 +8,7 @@ import mill.define.TaskModule import mill.eval.{PathRef, Result} import mill.modules.Jvm import mill.modules.Jvm.{createJar, subprocess} +import Dep.isDotty import Lib._ import mill.util.Loose.Agg import mill.util.DummyInputStream diff --git a/scalalib/test/resources/hello-dotty/foo/src/Main.scala b/scalalib/test/resources/hello-dotty/foo/src/Main.scala index b6fc468d519..3fe803422a0 100644 --- a/scalalib/test/resources/hello-dotty/foo/src/Main.scala +++ b/scalalib/test/resources/hello-dotty/foo/src/Main.scala @@ -1,3 +1,5 @@ +import cats._, cats.data._, cats.implicits._ + trait Context object Main { @@ -7,6 +9,8 @@ object Main { } def main(args: Array[String]): Unit = { + val x = Applicative[List].pure(1) + assert(x == List(1)) val value = foo(implicit x => x + 1) assert(value == 2) } diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index f13c4e037a0..9c2556d4305 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -237,6 +237,7 @@ object HelloWorldTests extends TestSuite { object HelloDotty extends HelloBase{ object foo extends ScalaModule { def scalaVersion = "0.9.0-RC1" + def ivyDeps = Agg(ivy"org.typelevel::cats-core:1.2.0".withDottyCompat(scalaVersion())) } } diff --git a/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala b/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala index c9f78fd0342..98b408894b3 100644 --- a/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala +++ b/scalalib/worker/src/mill/scalalib/worker/ScalaWorker.scala @@ -9,7 +9,8 @@ import mill.Agg import mill.eval.PathRef import mill.scalalib.{CompilationResult, Lib, TestRunner} import xsbti.compile.{CompilerCache => _, FileAnalysisStore => _, ScalaInstance => _, _} -import mill.scalalib.Lib.{grepJar, isDotty, scalaBinaryVersion} +import mill.scalalib.Dep.isDotty +import mill.scalalib.Lib.{grepJar, scalaBinaryVersion} import mill.util.{Ctx, PrintLogger} import sbt.internal.inc._ import sbt.internal.util.{ConsoleOut, MainAppender} From 66af1e2cb67cab0dd72aec7437b4bc0e816cf05e Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sun, 5 Aug 2018 21:23:40 +0900 Subject: [PATCH 6/6] Turn off the Dotty test on Java >= 9 --- scalalib/test/src/mill/scalalib/HelloWorldTests.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index 9c2556d4305..1a89a0e3849 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -14,6 +14,7 @@ import utest._ import utest.framework.TestPath import scala.collection.JavaConverters._ +import scala.util.Properties.isJavaAtLeast object HelloWorldTests extends TestSuite { @@ -822,8 +823,13 @@ object HelloWorldTests extends TestSuite { HelloDotty, resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-dotty" ){ eval => - val Right((_, evalCount)) = eval.apply(HelloDotty.foo.run()) - assert(evalCount > 0) + if (isJavaAtLeast("9")) { + // Skip the test because Dotty does not support Java >= 9 yet + // (see https://github.com/lampepfl/dotty/pull/3138) + } else { + val Right((_, evalCount)) = eval.apply(HelloDotty.foo.run()) + assert(evalCount > 0) + } } } }