summaryrefslogtreecommitdiff
path: root/integration
diff options
context:
space:
mode:
authorNikolay Tatarinov <5min4eq.unity@gmail.com>2018-03-28 01:33:11 +0300
committerGitHub <noreply@github.com>2018-03-28 01:33:11 +0300
commit83aeb8976d90ad1637243953a9af37e6d4206c52 (patch)
tree346e52db0d80c7fb7bdab8013acb38ef8be55fbb /integration
parenta796f0a1381632560b6251a93466957319065966 (diff)
downloadmill-83aeb8976d90ad1637243953a9af37e6d4206c52.tar.gz
mill-83aeb8976d90ad1637243953a9af37e6d4206c52.tar.bz2
mill-83aeb8976d90ad1637243953a9af37e6d4206c52.zip
WIP: Play json build (#182)
* play json build * build progress * add check task * try to make play json js build * scalariform and mima plugins in separate files. check mima * better error message for mima compatibility check * fix scala 2.10 compilation * license headers support * add jmh support * fix reformat on compile; fix code validation; extract base module * remove scala 2.13 from cross versions * include play-json in integration tests * add example .travis.yml * bring back scala 2.13 support * make reformat target, not command * add release task * update mill version in travis.yml * update release script * update release process * add README.md for play json build
Diffstat (limited to 'integration')
-rw-r--r--integration/test/resources/play-json/.travis.yml29
-rw-r--r--integration/test/resources/play-json/README.md30
-rw-r--r--integration/test/resources/play-json/build.sc371
-rw-r--r--integration/test/resources/play-json/headers.sc63
-rw-r--r--integration/test/resources/play-json/jmh.sc61
-rw-r--r--integration/test/resources/play-json/mima.sc58
-rw-r--r--integration/test/resources/play-json/reformat.sc71
-rwxr-xr-xintegration/test/resources/play-json/release.sh14
-rw-r--r--integration/test/resources/play-json/version.sc1
-rw-r--r--integration/test/src/mill/integration/IntegrationTestSuite.scala8
-rw-r--r--integration/test/src/mill/integration/PlayJsonTests.scala57
-rw-r--r--integration/test/src/mill/integration/forked/Tests.scala3
-rw-r--r--integration/test/src/mill/integration/local/Tests.scala3
13 files changed, 766 insertions, 3 deletions
diff --git a/integration/test/resources/play-json/.travis.yml b/integration/test/resources/play-json/.travis.yml
new file mode 100644
index 00000000..e045541b
--- /dev/null
+++ b/integration/test/resources/play-json/.travis.yml
@@ -0,0 +1,29 @@
+language: scala
+sudo: required
+dist: trusty
+
+scala:
+- 2.13.0-M3
+- 2.12.4
+- 2.11.12
+- 2.10.7
+
+jdk:
+- oraclejdk8
+- oraclejdk9
+
+matrix:
+ exclude:
+ - scala: 2.10.7
+ jdk: oraclejdk9
+ - scala: 2.11.12
+ jdk: oraclejdk9
+
+script:
+ - curl -L -o ~/bin/mill https://github.com/lihaoyi/mill/releases/download/0.1.7/0.1.7-8-b913c6 && chmod +x ~/bin/mill
+ - export PATH=~/bin/mill:$PATH
+ - mill all validateCode reportBinaryIssues __[$TRAVIS_SCALA_VERSION].test
+
+cache:
+ directories:
+ - $HOME/.coursier
diff --git a/integration/test/resources/play-json/README.md b/integration/test/resources/play-json/README.md
new file mode 100644
index 00000000..d71acae9
--- /dev/null
+++ b/integration/test/resources/play-json/README.md
@@ -0,0 +1,30 @@
+## Play JSON build
+
+This is a port of sbt build for [Play JSON](https://github.com/playframework/play-json) library. It has roughly the same features as sbt version, for instance it cross-builds Play JSON under 4 major scala versions both js and JVM. It cam compile sources, run tests, publish artifacts to Sonatype, run benchmarks, reformat code, add license headers, use mima to diagnose binary compatibility issues.
+
+The main build file is [build.sc](/build.sc), other `.sc` files in root directory provide additional "plugin"-like features.
+
+### Example commands
+
+Compile play json for JVM on scala 2.12.4:
+```bash
+mill playJsonJvm[2.12.4].compile
+```
+
+Run test on all modules:
+```bash
+mill __.test
+```
+
+Run benchmarks on scala 2.12.4:
+```bash
+mill benchmarks[2.12.4].runJmh
+```
+
+### CI
+
+Example CI configuration is in [.travis.yml](/.travis.yml) . It does the same thing as [original one](https://github.com/playframework/play-json/blob/master/.travis.yml) . You can check status of Play JSON built with this `.travis.yml` file [here](travis-ci.org/rockjam/play-json)
+
+### Release
+
+To make release run `./release.sh`. Don't forget to export `SONATYPE_CREDENTIALS` and `GPG_PASSPHRASE` env variables.
diff --git a/integration/test/resources/play-json/build.sc b/integration/test/resources/play-json/build.sc
new file mode 100644
index 00000000..e5222ae1
--- /dev/null
+++ b/integration/test/resources/play-json/build.sc
@@ -0,0 +1,371 @@
+import mill._, mill.scalalib._, mill.scalalib.publish._, mill.scalajslib._
+import $file.version
+import $file.reformat
+import reformat.Scalariform
+import $file.mima
+import mima.MiMa
+import $file.headers
+import $file.jmh
+import jmh.Jmh
+import headers.Headers
+import com.typesafe.tools.mima.core._
+
+import ammonite.ops._
+import mill.define.Task
+
+val ScalaVersions = Seq("2.10.7", "2.11.12", "2.12.4", "2.13.0-M3")
+
+trait BaseModule extends CrossSbtModule with Scalariform with Headers
+
+trait PlayJsonModule extends BaseModule with PublishModule with MiMa {
+
+ def pomSettings = PomSettings(
+ description = artifactName(),
+ organization = "com.typesafe.play",
+ url = "https://github.com/playframework/play-json",
+ licenses = Seq(License.`Apache-2.0`),
+ versionControl = VersionControl.github("playframework", "play-json"),
+ developers = Seq(
+ Developer(
+ id = "playframework",
+ name = "Play Framework Team",
+ url = "https://github.com/playframework"
+ )
+ )
+ )
+
+ trait Tests extends super.Tests with Scalariform with Headers {
+ val specs2Core = T {
+ val v = Lib.scalaBinaryVersion(scalaVersion()) match {
+ case "2.10" => "3.9.1"
+ case _ => "4.0.2"
+ }
+ ivy"org.specs2::specs2-core:$v"
+ }
+ }
+
+ def scalacOptions = Seq("-deprecation", "-feature", "-unchecked", "-encoding", "utf8")
+ def javacOptions = Seq("-encoding", "UTF-8", "-Xlint:-options")
+
+ def publishVersion = version.current
+}
+
+abstract class PlayJson(val platformSegment: String) extends PlayJsonModule {
+ def crossScalaVersion: String
+ def millSourcePath = pwd / "play-json"
+ def artifactName = "play-json"
+
+ def sources = T.sources(
+ millSourcePath / platformSegment / "src" / "main",
+ millSourcePath / "shared" / "src" / "main"
+ )
+
+ def ivyDeps = Agg(
+ ivy"org.scala-lang: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()}"
+ )
+
+ def scalacPluginIvyDeps = Agg(macroParadise)
+
+ def mimaBinaryIssueFilters = Seq(
+ // AbstractFunction1 is in scala.runtime and isn't meant to be used by end users
+ ProblemFilters.exclude[MissingTypesProblem]("play.api.libs.json.JsArray$"),
+ ProblemFilters.exclude[MissingTypesProblem]("play.api.libs.json.JsObject$"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultWrites.BigIntWrites"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultWrites.BigIntegerWrites"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultReads.BigIntReads"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultReads.BigIntegerReads"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultWrites.BigIntWrites"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultWrites.BigIntegerWrites"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultReads.BigIntReads"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.DefaultReads.BigIntegerReads"),
+ ProblemFilters.exclude[ReversedMissingMethodProblem]("play.api.libs.json.JsonConfiguration.optionHandlers")
+ )
+
+ def generatedSources = T {
+ import ammonite.ops._
+
+ val dir = T.ctx().dest
+ mkdir(dir / "play-json")
+ val file = dir / "play-json" / "Generated.scala"
+
+ val (writes, reads) = (1 to 22).map { i =>
+ def commaSeparated(s: Int => String) = (1 to i).map(s).mkString(", ")
+
+ def newlineSeparated(s: Int => String) = (1 to i).map(s).mkString("\n")
+
+ val writerTypes = commaSeparated(j => s"T$j: Writes")
+ val readerTypes = commaSeparated(j => s"T$j: Reads")
+ val typeTuple = commaSeparated(j => s"T$j")
+ val written = commaSeparated(j => s"implicitly[Writes[T$j]].writes(x._$j)")
+ val readValues = commaSeparated(j => s"t$j")
+ val readGenerators = newlineSeparated(j => s"t$j <- implicitly[Reads[T$j]].reads(arr(${j - 1}))")
+ (
+ s"""
+ implicit def Tuple${i}W[$writerTypes]: Writes[Tuple${i}[$typeTuple]] = Writes[Tuple${i}[$typeTuple]](
+ x => JsArray(Array($written))
+ )
+ """,
+ s"""
+ implicit def Tuple${i}R[$readerTypes]: Reads[Tuple${i}[$typeTuple]] = Reads[Tuple${i}[$typeTuple]]{
+ case JsArray(arr) if arr.size == $i =>
+ for{
+ $readGenerators
+ } yield Tuple$i($readValues)
+
+ case _ =>
+ JsError(Seq(JsPath() -> Seq(JsonValidationError("Expected array of $i elements"))))
+ }
+ """)
+ }.unzip
+
+ write(file, s"""
+ package play.api.libs.json
+
+ trait GeneratedReads {
+ ${reads.mkString("\n")}
+ }
+
+ trait GeneratedWrites{
+ ${writes.mkString("\n")}
+ }
+ """)
+
+ Seq(PathRef(dir))
+ }
+}
+
+object playJsonJvm extends Cross[PlayJsonJvm](ScalaVersions:_*)
+class PlayJsonJvm(val crossScalaVersion: String) extends PlayJson("jvm") {
+ def moduleDeps = Seq(playFunctionalJvm(crossScalaVersion))
+
+ val jacksonVersion = "2.9.3"
+
+ def ivyDeps = super.ivyDeps() ++ Agg(
+ ivy"joda-time:joda-time:2.9.9",
+ ivy"com.fasterxml.jackson.core:jackson-core:$jacksonVersion",
+ ivy"com.fasterxml.jackson.core:jackson-annotations:$jacksonVersion",
+ ivy"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion",
+ ivy"com.fasterxml.jackson.datatype:jackson-datatype-jdk8:$jacksonVersion",
+ ivy"com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion"
+ )
+
+ object test extends Tests {
+ def ivyDeps =
+ Agg(
+ ivy"org.scalatest::scalatest:3.0.5-M1",
+ ivy"org.scalacheck::scalacheck:1.13.5",
+ ivy"com.chuusai::shapeless:2.3.3",
+ ivy"ch.qos.logback:logback-classic:1.2.3",
+ specs2Core()
+ )
+
+ def sources = {
+ val docSpecs = ls.rec(millSourcePath / up / "docs" / "manual" / "working" / "scalaGuide").filter(_.isDir).filter(_.last=="code").map(PathRef(_))
+ T.sources(
+ docSpecs ++
+ Seq(
+ PathRef(millSourcePath / platformSegment / "src" / "test"),
+ PathRef(millSourcePath / "shared" / "src" / "test")
+ )
+ )
+ }
+
+ def testFrameworks = Seq(
+ "org.scalatest.tools.Framework",
+ "org.specs2.runner.Specs2Framework"
+ )
+ }
+
+}
+
+object playJsonJs extends Cross[PlayJsonJs](ScalaVersions:_*)
+class PlayJsonJs(val crossScalaVersion: String) extends PlayJson("js") with ScalaJSModule {
+ def moduleDeps = Seq(playFunctionalJs(crossScalaVersion))
+
+ def scalaJSVersion = "0.6.22"
+
+ // TODO: remove super[PlayJson].Tests with super[ScalaJSModule].Tests hack
+ object test extends super[PlayJson].Tests with super[ScalaJSModule].Tests with Scalariform with Headers {
+ def ivyDeps =
+ Agg(
+ ivy"org.scalatest::scalatest::3.0.5-M1",
+ ivy"org.scalacheck::scalacheck::1.13.5",
+ ivy"com.chuusai::shapeless::2.3.3"
+ )
+
+ def sources = T.sources(
+ millSourcePath / platformSegment / "src" / "test",
+ millSourcePath / "shared" / "src" / "test"
+ )
+
+ def testFrameworks = Seq(
+ "org.scalatest.tools.Framework"
+ )
+ }
+}
+
+trait PlayFunctional extends PlayJsonModule {
+ def millSourcePath = pwd / "play-functional"
+ def artifactName = "play-functional"
+}
+
+object playFunctionalJvm extends Cross[PlayFunctionalJvm](ScalaVersions:_*)
+class PlayFunctionalJvm(val crossScalaVersion: String) extends PlayFunctional
+
+object playFunctionalJs extends Cross[PlayFunctionalJs](ScalaVersions:_*)
+class PlayFunctionalJs(val crossScalaVersion: String) extends PlayFunctional with ScalaJSModule {
+ def scalaJSVersion = "0.6.22"
+}
+
+object playJoda extends Cross[PlayJoda](ScalaVersions:_*)
+class PlayJoda(val crossScalaVersion: String) extends PlayJsonModule {
+ def moduleDeps = Seq(playJsonJvm(crossScalaVersion))
+
+ def millSourcePath = pwd / "play-json-joda"
+ def artifactName = "play-json-joda"
+
+ def ivyDeps = Agg(
+ ivy"joda-time:joda-time:2.9.9"
+ )
+
+ object test extends Tests {
+ def ivyDeps = Agg(specs2Core())
+
+ def testFrameworks = Seq(
+ "org.specs2.runner.Specs2Framework"
+ )
+ }
+
+}
+
+object benchmarks extends Cross[Benchmarks](ScalaVersions:_*)
+class Benchmarks(val crossScalaVersion: String) extends BaseModule with Jmh {
+ def moduleDeps = Seq(playJsonJvm(crossScalaVersion))
+
+ def millSourcePath = pwd / "benchmarks"
+}
+
+// TODO: we should have a way to "take all modules in this build"
+val testModules = Seq(
+ playJsonJvm("2.12.4").test,
+ playJsonJs("2.12.4").test,
+ playJoda("2.12.4").test
+)
+
+val sourceModules = Seq(
+ playJsonJvm("2.12.4"),
+ playJsonJs("2.12.4"),
+ playJoda("2.12.4"),
+)
+
+val allModules = testModules ++ sourceModules
+
+def reportBinaryIssues() = T.command {
+ Task.traverse(sourceModules) { module =>
+ module.mimaReportBinaryIssues.map(issues => module.millModuleSegments.render -> issues)
+ }.map { issues =>
+ val issuesByModules = issues.flatMap { case (moduleName, issues) =>
+ val messageForModule = issues.foldLeft(Seq.empty[String]) { case (acc, (artifact, problems)) =>
+ val elem = if(problems.nonEmpty) {
+ Some(
+ s"""Compared to artifact: ${artifact}
+ |found ${problems.size} binary incompatibilities:
+ |${problems.mkString("\n")}
+ """.stripMargin
+ )
+ } else {
+ None
+ }
+ acc ++ elem
+ }
+ if(messageForModule.nonEmpty) {
+ Some(
+ s"""
+ |For module: ${moduleName}:
+ |${messageForModule.mkString("\n")}
+ """.stripMargin
+ )
+ } else {
+ None
+ }
+ }
+
+ if(issuesByModules.nonEmpty) {
+ sys.error(issuesByModules.mkString("\n"))
+ }
+ }
+}
+
+def validateCode() = T.command {
+ Task.traverse(allModules)(_.checkCodeFormat()).zip(Task.traverse(allModules)(_.headerCheck()))
+}
+
+/**
+ * Release steps are:
+ * 1) clean
+ * 2) run tests
+ * 3) set release version
+ * 4) commit release version
+ * 5) make release tag
+ * 6) publish all modules to sonatype
+ * 7) set next version
+ * 8) commit next version
+ * 9) push everything to git
+ */
+object release extends Module {
+
+ implicit val wd = pwd
+
+ val versionFile = wd / "version.sc"
+
+ private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r
+ private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.(\d+)-SNAPSHOT""".r
+
+ private val releaseVersion = version.current match {
+ case MinorSnapshotVersion(major, minor, patch) =>
+ s"${major}.${minor}.${patch.toInt}"
+ case ReleaseVersion(major, minor, patch) =>
+ s"${major}.${minor}.${patch.toInt}"
+ }
+
+ private val nextVersion = version.current match {
+ case v@MinorSnapshotVersion(major, minor, patch) => v
+ case ReleaseVersion(major, minor, patch) =>
+ s"${major}.${minor}.${patch.toInt + 1}-SNAPSHOT"
+ }
+
+ def setReleaseVersion = T {
+ T.ctx.log.info(s"Setting release version to ${releaseVersion}")
+
+ write.over(
+ versionFile,
+ s"""def current = "${releaseVersion}"
+ |
+ """.stripMargin
+ )
+
+ %%("git", "commit", "-am", s"Setting release version to ${releaseVersion}")
+ %%("git", "tag", s"$releaseVersion")
+ }
+
+ def setNextVersion = T {
+ T.ctx.log.info(s"Setting next version to ${nextVersion}")
+
+ write.over(
+ versionFile,
+ s"""def current = "${nextVersion}""""
+ )
+
+ %%("git", "commit", "-am", s"Setting next version to ${nextVersion}")
+ %%("git", "push", "origin", "master", "--tags")
+ }
+
+}
diff --git a/integration/test/resources/play-json/headers.sc b/integration/test/resources/play-json/headers.sc
new file mode 100644
index 00000000..a66eaedd
--- /dev/null
+++ b/integration/test/resources/play-json/headers.sc
@@ -0,0 +1,63 @@
+import $ivy.`com.github.rockjam::license-headers:0.0.1`
+
+import ammonite.ops._
+import mill._, scalalib._
+import mill.eval.PathRef
+import de.heikoseeberger.sbtheader.{CommentStyle, FileType, HeaderCreator, License}
+
+trait Headers extends ScalaModule {
+
+ val headerMappings = Map(
+ FileType.scala.extension -> (FileType.scala, CommentStyle.cStyleBlockComment),
+ FileType.java.extension -> (FileType.java, CommentStyle.cStyleBlockComment)
+ )
+
+ val license = {
+ val currentYear = java.time.Year.now(java.time.Clock.systemUTC).getValue
+ License.Custom(
+ s"Copyright (C) 2009-$currentYear Lightbend Inc. <https://www.lightbend.com>"
+ )
+ }
+
+ def headerCreate() = T.command {
+ val withoutHeaders = filesWithoutHeaders(sources() ++ resources())
+
+ val updatedFiles = withoutHeaders.map {
+ case (file, updated) =>
+ write.over(file, updated)
+ file
+ }
+
+ if (updatedFiles.nonEmpty) {
+ T.ctx.log.info(
+ s"Headers created for ${updatedFiles.size} files:\n${updatedFiles.mkString("\n")}")
+ }
+ updatedFiles
+ }
+
+ def headerCheck() = T.command {
+ val withoutHeaders = filesWithoutHeaders(sources() ++ resources()).map(_._1)
+
+ if (withoutHeaders.nonEmpty) {
+ sys.error(
+ s"There are files without headers!\n${withoutHeaders.mkString("\n")}"
+ )
+ }
+ }
+
+ private def filesWithoutHeaders(input: Seq[PathRef]) = {
+ for {
+ ref <- input if exists(ref.path)
+ file <- ls.rec(ref.path) if file.isFile
+ (fileType, commentStyle) <- headerMappings.get(file.ext)
+ updatedContent <- HeaderCreator(
+ fileType,
+ commentStyle,
+ license,
+ log = _ => (),
+ read.getInputStream(file)
+ ).createText
+ } yield file -> updatedContent
+ }
+
+}
diff --git a/integration/test/resources/play-json/jmh.sc b/integration/test/resources/play-json/jmh.sc
new file mode 100644
index 00000000..76ed43fc
--- /dev/null
+++ b/integration/test/resources/play-json/jmh.sc
@@ -0,0 +1,61 @@
+import ammonite.ops._
+import mill._, scalalib._, modules._
+
+trait Jmh extends ScalaModule {
+
+ def ivyDeps = super.ivyDeps() ++ Agg(ivy"org.openjdk.jmh:jmh-core:1.19")
+
+ def runJmh(args: String*) = T.command {
+ val (_, resources) = generateBenchmarkSources()
+ Jvm.interactiveSubprocess(
+ "org.openjdk.jmh.Main",
+ classPath = (runClasspath() ++ generatorDeps()).map(_.path) ++
+ Seq(compileGeneratedSources().path, resources),
+ mainArgs = args,
+ workingDir = T.ctx.dest
+ )
+ }
+
+ def compileGeneratedSources = T {
+ val dest = T.ctx.dest
+ val (sourcesDir, _) = generateBenchmarkSources()
+ val sources = ls.rec(sourcesDir).filter(_.isFile)
+ %%("javac",
+ sources.map(_.toString),
+ "-cp",
+ (runClasspath() ++ generatorDeps()).map(_.path.toString).mkString(":"),
+ "-d",
+ dest)(wd = dest)
+ PathRef(dest)
+ }
+
+ // returns sources and resources directories
+ def generateBenchmarkSources = T {
+ val dest = T.ctx().dest
+
+ val sourcesDir = dest / 'jmh_sources
+ val resourcesDir = dest / 'jmh_resources
+
+ rm(sourcesDir)
+ mkdir(sourcesDir)
+ rm(resourcesDir)
+ mkdir(resourcesDir)
+
+ Jvm.subprocess(
+ "org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator",
+ (runClasspath() ++ generatorDeps()).map(_.path),
+ mainArgs = Array(
+ compile().classes.path,
+ sourcesDir,
+ resourcesDir,
+ "default"
+ ).map(_.toString)
+ )
+
+ (sourcesDir, resourcesDir)
+ }
+
+ def generatorDeps = resolveDeps(
+ T { Agg(ivy"org.openjdk.jmh:jmh-generator-bytecode:1.19") }
+ )
+}
diff --git a/integration/test/resources/play-json/mima.sc b/integration/test/resources/play-json/mima.sc
new file mode 100644
index 00000000..ebab2c72
--- /dev/null
+++ b/integration/test/resources/play-json/mima.sc
@@ -0,0 +1,58 @@
+import ammonite.ops._
+import $ivy.`com.typesafe::mima-reporter:0.1.18`
+import com.typesafe.tools.mima.lib.MiMaLib
+import com.typesafe.tools.mima.core._
+import mill._, scalalib._
+
+trait MiMa extends ScalaModule with PublishModule {
+ def previousVersions = T {
+ scalaVersion().split('.')(1) match {
+ case "10" => Seq("2.6.0")
+ case "11" => Seq("2.6.0")
+ case "12" => Seq("2.6.0")
+ case _ => Nil
+ }
+ }
+
+ def mimaBinaryIssueFilters: Seq[ProblemFilter] = Seq.empty
+
+ def previousDeps = T {
+ Agg.from(previousVersions().map { version =>
+ Dep.Java(
+ pomSettings().organization,
+ artifactId(),
+ version,
+ cross = false
+ )
+ })
+ }
+
+ def previousArtifacts = T {
+ resolveDeps(previousDeps)().filter(_.path.segments.contains(artifactId()))
+ }
+
+ def mimaReportBinaryIssues: T[List[(String, List[String])]] = T {
+ val currentClassfiles = compile().classes.path
+ val classpath = runClasspath()
+
+ val lib = {
+ com.typesafe.tools.mima.core.Config.setup("sbt-mima-plugin", Array.empty)
+ val cpstring = classpath
+ .map(_.path)
+ .filter(exists)
+ .mkString(System.getProperty("path.separator"))
+ new MiMaLib(
+ com.typesafe.tools.mima.core.reporterClassPath(cpstring)
+ )
+ }
+
+ previousArtifacts().toList.map { path =>
+ val problems =
+ lib.collectProblems(path.path.toString, currentClassfiles.toString)
+ path.path.toString -> problems.filter { problem =>
+ mimaBinaryIssueFilters.forall(_.apply(problem))
+ }.map(_.description("current"))
+ }
+ }
+
+}
diff --git a/integration/test/resources/play-json/reformat.sc b/integration/test/resources/play-json/reformat.sc
new file mode 100644
index 00000000..c44b2f97
--- /dev/null
+++ b/integration/test/resources/play-json/reformat.sc
@@ -0,0 +1,71 @@
+import $ivy.`org.scalariform::scalariform:0.2.5`
+
+import mill._, scalalib._
+import ammonite.ops._
+import scalariform.formatter._
+import scalariform.formatter.preferences._
+import scalariform.parser.ScalaParserException
+
+trait Scalariform extends ScalaModule {
+ val playJsonPreferences = FormattingPreferences()
+ .setPreference(SpacesAroundMultiImports, true)
+ .setPreference(SpaceInsideParentheses, false)
+ .setPreference(DanglingCloseParenthesis, Preserve)
+ .setPreference(PreserveSpaceBeforeArguments, true)
+ .setPreference(DoubleIndentConstructorArguments, false)
+
+ def compile = T {
+ reformat()
+ super.compile()
+ }
+
+ def reformat = T {
+ val files = filesToFormat(sources())
+ T.ctx.log.info(s"Formatting ${files.size} Scala sources")
+ files.map { path =>
+ try {
+ val formatted = ScalaFormatter.format(
+ read(path),
+ playJsonPreferences,
+ scalaVersion = scalaVersion()
+ )
+ write.over(path, formatted)
+ } catch {
+ case ex: ScalaParserException =>
+ T.ctx.log.error(s"Failed to format file: ${path}. Error: ${ex.getMessage}")
+ }
+ path
+ }
+ }
+
+ def checkCodeFormat() = T.command {
+ filesToFormat(sources()).foreach { path =>
+ try {
+ val input = read(path)
+ val formatted = ScalaFormatter.format(
+ input,
+ playJsonPreferences,
+ scalaVersion = scalaVersion()
+ )
+ if (input != formatted) sys.error(
+ s"""
+ |ERROR: Scalariform check failed at file: ${path}
+ |To fix, format your sources using `mill __.reformat` before submitting a pull request.
+ |Additionally, please squash your commits (eg, use git commit --amend) if you're going to update this pull request.
+ """.stripMargin
+ )
+ } catch {
+ case ex: ScalaParserException =>
+ T.ctx.log.error(s"Failed to format file: ${path}. Error: ${ex.getMessage}")
+ }
+ }
+ }
+
+ private def filesToFormat(sources: Seq[PathRef]) = {
+ for {
+ pathRef <- sources if exists(pathRef.path)
+ file <- ls.rec(pathRef.path) if file.isFile && file.ext == "scala"
+ } yield file
+ }
+
+}
diff --git a/integration/test/resources/play-json/release.sh b/integration/test/resources/play-json/release.sh
new file mode 100755
index 00000000..372518ab
--- /dev/null
+++ b/integration/test/resources/play-json/release.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+set -eux
+
+rm -rf out
+mill __.test
+mill release.setReleaseVersion
+mill mill.scalalib.PublishModule/publishAll \
+ "$SONATYPE_CREDENTIALS" \
+ "$GPG_PASSPHRASE" \
+ __.publishArtifacts \
+ --release \
+ true \
+mill release.setNextVersion
diff --git a/integration/test/resources/play-json/version.sc b/integration/test/resources/play-json/version.sc
new file mode 100644
index 00000000..e9dbb12f
--- /dev/null
+++ b/integration/test/resources/play-json/version.sc
@@ -0,0 +1 @@
+def current = "2.7.0-SNAPSHOT"
diff --git a/integration/test/src/mill/integration/IntegrationTestSuite.scala b/integration/test/src/mill/integration/IntegrationTestSuite.scala
index 756b724a..d24261f3 100644
--- a/integration/test/src/mill/integration/IntegrationTestSuite.scala
+++ b/integration/test/src/mill/integration/IntegrationTestSuite.scala
@@ -17,9 +17,15 @@ abstract class IntegrationTestSuite(repoKey: String, val workspaceSlug: String,
wrapper
}
+ def buildFiles: Seq[Path] = {
+ Seq(buildFilePath / "build.sc")
+ }
+
override def initWorkspace() = {
super.initWorkspace()
- cp.over(buildFilePath / "build.sc", workspacePath / "build.sc")
+ buildFiles.foreach { file =>
+ cp.over(file, workspacePath / file.name)
+ }
assert(!ls.rec(workspacePath).exists(_.ext == "class"))
}
}
diff --git a/integration/test/src/mill/integration/PlayJsonTests.scala b/integration/test/src/mill/integration/PlayJsonTests.scala
new file mode 100644
index 00000000..c78a256e
--- /dev/null
+++ b/integration/test/src/mill/integration/PlayJsonTests.scala
@@ -0,0 +1,57 @@
+package mill.integration
+
+import ammonite.ops._
+import utest._
+
+class PlayJsonTests(fork: Boolean) extends IntegrationTestSuite("MILL_PLAY_JSON_REPO", "play-json", fork) {
+
+ override def buildFiles: Seq[Path] = {
+ ls(buildFilePath).filter(_.ext == "sc")
+ }
+
+ val tests = Tests{
+ initWorkspace()
+
+ 'jvm - {
+ assert(eval("playJsonJvm[2.12.4].test"))
+ val jvmMeta = meta("playJsonJvm[2.12.4].test.test")
+
+ assert(
+ jvmMeta.contains("play.api.libs.json.JsonSharedSpec"),
+ jvmMeta.contains("JSON should support basic array operations")
+ )
+
+ assert(
+ jvmMeta.contains("play.api.libs.json.JsonValidSpec"),
+ jvmMeta.contains("JSON reads should::validate Dates")
+ )
+ }
+ 'js - {
+ assert(eval("playJsonJs[2.12.4].test"))
+ val jsMeta = meta("playJsonJs[2.12.4].test.test")
+
+ assert(
+ jsMeta.contains("play.api.libs.json.JsonSharedSpec"),
+ jsMeta.contains("JSON should support basic array operations")
+ )
+
+ assert(
+ jsMeta.contains("play.api.libs.json.JsonSpec"),
+ jsMeta.contains("Complete JSON should create full object when lose precision when parsing BigDecimals")
+ )
+ }
+ 'playJoda - {
+ assert(eval("playJoda[2.12.4].test"))
+ val metaFile = meta("playJoda[2.12.4].test.test")
+
+ assert(
+ metaFile.contains("play.api.libs.json.JsonJodaValidSpec"),
+ metaFile.contains("JSON reads should::validate Dates")
+ )
+ }
+
+ 'benchmarks - {
+// "benchmarks[2.12.4].runJmh" -i 1 -wi 1 -f1 -t1
+ }
+ }
+}
diff --git a/integration/test/src/mill/integration/forked/Tests.scala b/integration/test/src/mill/integration/forked/Tests.scala
index 6933bbbe..4a2e9f53 100644
--- a/integration/test/src/mill/integration/forked/Tests.scala
+++ b/integration/test/src/mill/integration/forked/Tests.scala
@@ -4,4 +4,5 @@ object AcyclicTests extends mill.integration.AcyclicTests(fork = true)
object AmmoniteTests extends mill.integration.AmmoniteTests(fork = true)
object BetterFilesTests extends mill.integration.BetterFilesTests(fork = true)
object JawnTests extends mill.integration.JawnTests(fork = true)
-object UpickleTests extends mill.integration.UpickleTests(fork = true) \ No newline at end of file
+object UpickleTests extends mill.integration.UpickleTests(fork = true)
+object PlayJsonTests extends mill.integration.PlayJsonTests(fork = true)
diff --git a/integration/test/src/mill/integration/local/Tests.scala b/integration/test/src/mill/integration/local/Tests.scala
index 21b9581c..b4f78ee4 100644
--- a/integration/test/src/mill/integration/local/Tests.scala
+++ b/integration/test/src/mill/integration/local/Tests.scala
@@ -4,4 +4,5 @@ object AcyclicTests extends mill.integration.AcyclicTests(fork = false)
object AmmoniteTests extends mill.integration.AmmoniteTests(fork = false)
object BetterFilesTests extends mill.integration.BetterFilesTests(fork = false)
object JawnTests extends mill.integration.JawnTests(fork = false)
-object UpickleTests extends mill.integration.UpickleTests(fork = false) \ No newline at end of file
+object UpickleTests extends mill.integration.UpickleTests(fork = false)
+object PlayJsonTests extends mill.integration.PlayJsonTests(fork = false)