intitial import

This commit is contained in:
Eugene Yokota 2018-06-23 22:35:09 -04:00
parent 0307478990
commit 42ff91f4cc
15 changed files with 481 additions and 0 deletions

20
.travis.yml Normal file
View File

@ -0,0 +1,20 @@
dist: trusty
group: stable
language: scala
jdk: oraclejdk8
script: sbt scripted
# Undo _JAVA_OPTIONS environment variable
before_script:
- unset _JAVA_OPTIONS
cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt
before_cache:
- find $HOME/.ivy2/cache -name "ivydata-*.properties" -delete
- find $HOME/.sbt -name "*.lock" -delete

46
README.markdown Normal file
View File

@ -0,0 +1,46 @@
sbt-projectmatrix
=================
cross building using subprojects.
This is an experimental plugin that implements better cross building.
setup
-----
**Requirements**: Requires sbt 1.2.0-M1 or above.
In `project/plugins.sbt`:
```scala
addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.1.0")
```
usage
-----
To use `projectMatrix`:
```scala
lazy val core = (projectMatrix in file("core"))
.scalaVersions("2.12.6", "2.11.12")
.settings(
name := "core"
)
.jvmPlatform()
lazy val app = (projectMatrix in file("app"))
.dependsOn(core)
.scalaVersions("2.12.6")
.settings(
name := "app"
)
.jvmPlatform()
```
This sets up basic project matrices one supporting both 2.11 and 2.12, and the other supporting only 2.12.
license
-------
MIT License

19
build.sbt Normal file
View File

@ -0,0 +1,19 @@
ThisBuild / organization := "com.eed3si9n"
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / description := "sbt plugin to define project matrix for cross building"
ThisBuild / licenses := Seq("MIT License" -> url("https://github.com/sbt/sbt-projectmatrix/blob/master/LICENSE"))
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)
.settings(
sbtPlugin := true,
name := "sbt-projectmatrix",
scalacOptions := Seq("-deprecation", "-unchecked"),
publishMavenStyle := false,
bintrayOrganization in bintray := None,
bintrayRepository := "sbt-plugins",
scriptedLaunchOpts := { scriptedLaunchOpts.value ++
Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
scriptedBufferLog := false,
)

1
project/build.properties Normal file
View File

@ -0,0 +1 @@
sbt.version=1.2.0-M1

2
project/plugins.sbt Normal file
View File

@ -0,0 +1,2 @@
addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.4")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1")

View File

@ -0,0 +1,289 @@
package sbt
import java.util.Locale
import scala.collection.immutable.ListMap
import Keys._
import sbt.librarymanagement.CrossVersion.partialVersion
/**
* A project matrix is an implementation of a composite project
* that represents cross building across some axis (such as platform)
* and Scala version.
*
* {{{
* lazy val core = (projectMatrix in file("core"))
* .scalaVersions("2.12.6", "2.11.12")
* .settings(
* name := "core"
* )
* .jvmPlatform()
* }}}
*/
sealed trait ProjectMatrix extends CompositeProject {
def scalaVersions(sv: String*): ProjectMatrix
def id: String
/** The base directory for the project matrix.*/
def base: sbt.File
def withId(id: String): ProjectMatrix
/** Sets the base directory for this project matrix.*/
def in(dir: sbt.File): ProjectMatrix
/** Adds new configurations directly to this project. To override an existing configuration, use `overrideConfigs`. */
def configs(cs: Configuration*): ProjectMatrix
/** Adds classpath dependencies on internal or external projects. */
def dependsOn(deps: MatrixClasspathDep[ProjectMatrixReference]*): ProjectMatrix
/**
* Adds projects to be aggregated. When a user requests a task to run on this project from the command line,
* the task will also be run in aggregated projects.
*/
def aggregate(refs: ProjectMatrixReference*): ProjectMatrix
/** Appends settings to the current settings sequence for this project. */
def settings(ss: Def.SettingsDefinition*): ProjectMatrix
/**
* Sets the [[AutoPlugin]]s of this project.
* A [[AutoPlugin]] is a common label that is used by plugins to determine what settings, if any, to enable on a project.
*/
def enablePlugins(ns: Plugins*): ProjectMatrix
/** Disable the given plugins on this project. */
def disablePlugins(ps: AutoPlugin*): ProjectMatrix
def custom(
idSuffix: String,
directorySuffix: String,
scalaVersions: Seq[String],
process: Project => Project
): ProjectMatrix
def jvmPlatform(settings: Setting[_]*): ProjectMatrix
def jvm: ProjectFinder
def projectRefs: Seq[ProjectReference]
}
/** Represents a reference to a project matrix with an optional configuration string.
*/
sealed trait MatrixClasspathDep[MR <: ProjectMatrixReference] {
def matrix: MR; def configuration: Option[String]
}
trait ProjectFinder {
def apply(scalaVersion: String): Project
def get: Seq[Project]
}
object ProjectMatrix {
import sbt.io.syntax._
val jvmIdSuffix: String = "JVM"
val jvmDirectorySuffix: String = "-jvm"
/** A row in the project matrix, typically representing a platform.
*/
final class ProjectRow(
val idSuffix: String,
val directorySuffix: String,
val scalaVersions: Seq[String],
val process: Project => Project
) {}
final case class MatrixClasspathDependency(
matrix: ProjectMatrixReference,
configuration: Option[String]
) extends MatrixClasspathDep[ProjectMatrixReference]
private final class ProjectMatrixDef(
val id: String,
val base: sbt.File,
val scalaVersions: Seq[String],
val rows: Seq[ProjectRow],
val aggregate: Seq[ProjectMatrixReference],
val dependencies: Seq[MatrixClasspathDep[ProjectMatrixReference]],
val settings: Seq[Def.Setting[_]],
val configurations: Seq[Configuration],
val plugins: Plugins
) extends ProjectMatrix { self =>
lazy val projectMatrix: ListMap[(ProjectRow, String), Project] = {
ListMap((for {
r <- rows
svs = if (r.scalaVersions.nonEmpty) r.scalaVersions
else if (scalaVersions.nonEmpty) scalaVersions
else sys.error(s"project matrix $id must specify scalaVersions.")
sv <- svs
} yield {
val idSuffix = r.idSuffix + scalaVersionIdSuffix(sv)
val svDirSuffix = r.directorySuffix + "-" + scalaVersionDirSuffix(sv)
val childId = self.id + idSuffix
val deps = dependencies map {
case MatrixClasspathDependency(matrix: LocalProjectMatrix, configuration) =>
ClasspathDependency(LocalProject(matrix.id + idSuffix), configuration)
}
val aggs = aggregate map {
case ref: LocalProjectMatrix => LocalProject(ref.id + idSuffix)
}
val p = Project(childId, new sbt.File(childId).getAbsoluteFile)
.dependsOn(deps: _*)
.aggregate(aggs: _*)
.setPlugins(plugins)
.configs(configurations: _*)
.settings(
Keys.scalaVersion := sv,
target := base.getAbsoluteFile / "target" / svDirSuffix.dropWhile(_ == '-'),
crossTarget := Keys.target.value,
sourceDirectory := base.getAbsoluteFile / "src",
inConfig(Compile)(makeSources(r.directorySuffix, svDirSuffix)),
inConfig(Test)(makeSources(r.directorySuffix, svDirSuffix))
)
.settings(self.settings)
(r, sv) -> r.process(p)
}): _*)
}
override lazy val componentProjects: Seq[Project] = projectMatrix.values.toList
private def makeSources(dirSuffix: String, svDirSuffix: String): Setting[_] = {
unmanagedSourceDirectories ++= Seq(
scalaSource.value.getParentFile / s"scala${dirSuffix}",
scalaSource.value.getParentFile / s"scala$svDirSuffix"
)
}
private def scalaVersionIdSuffix(sv: String): String = {
scalaVersionDirSuffix(sv).toLowerCase(Locale.ENGLISH).replaceAll("""\W+""", "_")
}
private def scalaVersionDirSuffix(sv: String): String =
partialVersion(sv) match {
case Some((m, n)) => s"$m.$n"
case _ => sv
}
override def withId(id: String): ProjectMatrix = copy(id = id)
override def in(dir: sbt.File): ProjectMatrix = copy(base = dir)
override def configs(cs: Configuration*): ProjectMatrix =
copy(configurations = configurations ++ cs)
override def scalaVersions(sv: String*): ProjectMatrix =
copy(scalaVersions = sv)
override def aggregate(refs: ProjectMatrixReference*): ProjectMatrix =
copy(aggregate = (aggregate: Seq[ProjectMatrixReference]) ++ refs)
override def dependsOn(deps: MatrixClasspathDep[ProjectMatrixReference]*): ProjectMatrix =
copy(dependencies = dependencies ++ deps)
/** Appends settings to the current settings sequence for this project. */
override def settings(ss: Def.SettingsDefinition*): ProjectMatrix =
copy(settings = (settings: Seq[Def.Setting[_]]) ++ Def.settings(ss: _*))
override def enablePlugins(ns: Plugins*): ProjectMatrix =
setPlugins(ns.foldLeft(plugins)(Plugins.and))
override def disablePlugins(ps: AutoPlugin*): ProjectMatrix =
setPlugins(Plugins.and(plugins, Plugins.And(ps.map(p => Plugins.Exclude(p)).toList)))
def setPlugins(ns: Plugins): ProjectMatrix = copy(plugins = ns)
override def jvmPlatform(settings: Setting[_]*): ProjectMatrix =
custom(jvmIdSuffix, jvmDirectorySuffix, Nil, { _.settings(settings) })
override def jvm: ProjectFinder = new ProjectFinder {
def get: Seq[Project] = projectMatrix.toSeq collect {
case ((r, sv), v) if r.idSuffix == jvmIdSuffix => v
}
def apply(sv: String): Project =
(projectMatrix.toSeq collect {
case ((r, `sv`), v) if r.idSuffix == jvmIdSuffix => v
}).headOption.getOrElse(sys.error(s"$sv was not found"))
}
override def projectRefs: Seq[ProjectReference] =
componentProjects map { case p => (p: ProjectReference) }
override def custom(
idSuffix: String,
directorySuffix: String,
scalaVersions: Seq[String],
process: Project => Project
): ProjectMatrix =
copy(rows = rows :+ new ProjectRow(idSuffix, directorySuffix, scalaVersions, process))
def copy(
id: String = id,
base: sbt.File = base,
scalaVersions: Seq[String] = scalaVersions,
rows: Seq[ProjectRow] = rows,
aggregate: Seq[ProjectMatrixReference] = aggregate,
dependencies: Seq[MatrixClasspathDep[ProjectMatrixReference]] = dependencies,
settings: Seq[Setting[_]] = settings,
configurations: Seq[Configuration] = configurations,
plugins: Plugins = plugins
): ProjectMatrix =
unresolved(
id,
base,
scalaVersions,
rows,
aggregate,
dependencies,
settings,
configurations,
plugins
)
}
def apply(id: String, base: sbt.File): ProjectMatrix = {
unresolved(id, base, Nil, Nil, Nil, Nil, Nil, Nil, Plugins.Empty)
}
private[sbt] def unresolved(
id: String,
base: sbt.File,
scalaVersions: Seq[String],
rows: Seq[ProjectRow],
aggregate: Seq[ProjectMatrixReference],
dependencies: Seq[MatrixClasspathDep[ProjectMatrixReference]],
settings: Seq[Def.Setting[_]],
configurations: Seq[Configuration],
plugins: Plugins
): ProjectMatrix =
new ProjectMatrixDef(
id,
base,
scalaVersions,
rows,
aggregate,
dependencies,
settings,
configurations,
plugins
)
implicit def projectMatrixToLocalProjectMatrix(m: ProjectMatrix): LocalProjectMatrix =
LocalProjectMatrix(m.id)
import scala.reflect.macros._
def projectMatrixMacroImpl(c: blackbox.Context): c.Expr[ProjectMatrix] = {
import c.universe._
val enclosingValName = std.KeyMacro.definingValName(
c,
methodName =>
s"""$methodName must be directly assigned to a val, such as `val x = $methodName`. Alternatively, you can use `sbt.ProjectMatrix.apply`"""
)
val name = c.Expr[String](Literal(Constant(enclosingValName)))
reify { ProjectMatrix(name.splice, new sbt.File(name.splice)) }
}
}

View File

@ -0,0 +1,7 @@
package sbt
/** Identifies a project matrix. */
sealed trait ProjectMatrixReference
/** Identifies a project in the current build context. */
final case class LocalProjectMatrix(id: String) extends ProjectMatrixReference

View File

@ -0,0 +1,18 @@
package sbtprojectmatrix
import sbt._
import java.util.concurrent.atomic.AtomicBoolean
import scala.language.experimental.macros
object ProjectMatrixPlugin extends AutoPlugin {
override val requires = sbt.plugins.CorePlugin
override val trigger = allRequirements
object autoImport {
def projectMatrix: ProjectMatrix = macro ProjectMatrix.projectMatrixMacroImpl
implicit def matrixClasspathDependency[T](
m: T
)(implicit ev: T => ProjectMatrixReference): ProjectMatrix.MatrixClasspathDependency =
ProjectMatrix.MatrixClasspathDependency(m, None)
}
}

View File

@ -0,0 +1,34 @@
ThisBuild / organization := "com.example"
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / publishMavenStyle := true
ThisBuild / ivyPaths := {
val base = (ThisBuild / baseDirectory).value
IvyPaths(base, Some(base / "ivy-cache"))
}
publish / skip := true
lazy val core = (projectMatrix in file("core"))
.scalaVersions("2.12.6", "2.11.12")
.settings(
name := "core",
ivyPaths := (ThisBuild / ivyPaths).value
)
.custom(
idSuffix = "config1_2_",
directorySuffix = "-config1.2",
scalaVersions = Nil,
_.settings(
moduleName := name.value + "_config1.2",
libraryDependencies += "com.typesafe" % "config" % "1.2.1"
)
)
.custom(
idSuffix = "config1_3_",
directorySuffix = "-config1.3",
scalaVersions = Nil,
_.settings(
moduleName := name.value + "_config1.3",
libraryDependencies += "com.typesafe" % "config" % "1.3.3"
)
)

View File

@ -0,0 +1,5 @@
sys.props.get("plugin.version") match {
case Some(x) => addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % x)
case _ => sys.error("""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
}

View File

@ -0,0 +1,6 @@
> publishLocal
$ exists ivy-cache/local/com.example/core_config1.2_2.11/0.1.0-SNAPSHOT/poms/core_config1.2_2.11.pom
$ exists ivy-cache/local/com.example/core_config1.2_2.12/0.1.0-SNAPSHOT/poms/core_config1.2_2.12.pom
$ exists ivy-cache/local/com.example/core_config1.3_2.11/0.1.0-SNAPSHOT/poms/core_config1.3_2.11.pom
$ exists ivy-cache/local/com.example/core_config1.3_2.12/0.1.0-SNAPSHOT/poms/core_config1.3_2.12.pom

View File

@ -0,0 +1,19 @@
// lazy val root = (project in file("."))
// .aggregate(core.projectRefs ++ app.projectRefs: _*)
// .settings(
// )
lazy val core = (projectMatrix in file("core"))
.scalaVersions("2.12.6", "2.11.12")
.settings(
name := "core"
)
.jvmPlatform()
lazy val app = (projectMatrix in file("app"))
.dependsOn(core)
.scalaVersions("2.12.6")
.settings(
name := "app"
)
.jvmPlatform()

View File

@ -0,0 +1,6 @@
package a
class Core {
}
object Core extends Core

View File

@ -0,0 +1,5 @@
sys.props.get("plugin.version") match {
case Some(x) => addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % x)
case _ => sys.error("""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
}

View File

@ -0,0 +1,4 @@
> compile
$ exists core/target/jvm-2.12/classes/a/Core.class
$ exists core/target/jvm-2.11/classes/a/Core.class