Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Dotty projects #397

Merged
merged 6 commits into from
Aug 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/pages/2 - Configuring Mill.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()}"
)
}
```
Expand Down
2 changes: 1 addition & 1 deletion integration/test/resources/acyclic/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions integration/test/resources/ammonite/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
)

}
Expand All @@ -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"
)
Expand Down
4 changes: 2 additions & 2 deletions integration/test/resources/play-json/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ 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"
)

private val macroParadise = ivy"org.scalamacros:::paradise:2.1.0"

def compileIvyDeps = Agg(
macroParadise,
ivy"org.scala-lang:scala-compiler:${scalaVersion()}"
ivy"${scalaOrganization()}:scala-compiler:${scalaVersion()}"
)

def scalacPluginIvyDeps = Agg(macroParadise)
Expand Down
4 changes: 2 additions & 2 deletions integration/test/resources/upickle/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions scalajslib/src/mill/scalajslib/ScalaJSModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
148 changes: 94 additions & 54 deletions scalalib/src/mill/scalalib/Dep.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,60 @@
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) {
import Dep.isDotty

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))

/**
* 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{

object Dep {

val DefaultConfiguration = "default(compile)"

implicit def parse(signature: String) = {
def isDotty(scalaVersion: String) =
scalaVersion.startsWith("0.")

implicit def parse(signature: String): Dep = {
val parts = signature.split(';')
val module = parts.head
val attributes = parts.tail.foldLeft(coursier.Attributes()) { (as, s) =>
Expand All @@ -37,48 +65,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)
}
71 changes: 28 additions & 43 deletions scalalib/src/mill/scalalib/Lib.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -56,58 +57,37 @@ 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 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"))
}


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,
Expand Down Expand Up @@ -142,12 +122,17 @@ 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 scalaRuntimeIvyDeps(scalaVersion: String) = Agg[Dep](
ivy"org.scala-lang:scala-library:$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()
)

def listClassFiles(base: Path): Iterator[String] = {
Expand Down