Skip to content

Commit

Permalink
Add sbt 0.13 support
Browse files Browse the repository at this point in the history
This commit refactors the build to reflectively invoke scalafix instead
of calling the API directly. This has the following benefits

- works for 0.13 and 1.0
- avoids binary compatibility issues with sbt classpath (scalapb,
  fastparse, ...). We should shade scalafix dependencies but that would
  make it impossible support running custom rules that are published to
  Maven Central.
  • Loading branch information
olafurpg committed Aug 2, 2018
1 parent 53c0cfe commit 72c5815
Show file tree
Hide file tree
Showing 32 changed files with 199 additions and 115 deletions.
7 changes: 5 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ jobs:
- env: TEST="scalafmt"
script: ./bin/scalafmt --test
- env: TEST="compile"
script: sbt plugin/test plugin/scripted
script: sbt test-all
- stage: release
script: travis_wait 60 sbt ci-release
env:
- CI_RELEASE="release-all"
- CI_SONATYPE_RELEASE="release-snapshot-all"
script: sbt ci-release

cache:
directories:
Expand Down
37 changes: 25 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ inThisBuild(
else old
},
onLoadMessage := s"Welcome to sbt-scalafix ${version.value}",
scalaVersion := "2.12.6",
scalaVersion := {
if (sbtVersion.in(pluginCrossBuild).value.startsWith("0.13")) "2.10.6"
else "2.12.6"
},
crossSbtVersions := List("0.13.17", "1.2.0"),
resolvers += Resolver.sonatypeRepo("releases"),
organization := "ch.epfl.scala",
homepage := Some(url("https://github.com/scalacenter/sbt-scalafix")),
Expand All @@ -25,6 +29,24 @@ inThisBuild(

skip in publish := true

// For some reason I'm not motivated to debug, ^publishSigned did not work
// as intended when I tried it.
commands += Command.command("release-all") { s =>
"^^ 1.2.0 publishSigned" ::
"^^ 0.13.17 publishSigned" ::
s
}
commands += Command.command("release-snapshot-all") { s =>
"^^ 1.2.0 publish" ::
"^^ 0.13.17 publish" ::
s
}
commands += Command.command("test-all") { s =>
"^^ 1.2.0 test scripted" ::
"^^ 0.13.17 test scripted" ::
s
}

lazy val plugin = project
.settings(
moduleName := "sbt-scalafix",
Expand All @@ -35,17 +57,8 @@ lazy val plugin = project
s"-Dplugin.version=${version.value}"
),
libraryDependencies ++= List(
"ch.epfl.scala" % "scalafix-cli" % {
val buildVersion = version.in(ThisBuild).value
if (CiReleasePlugin.isTravisTag) {
println(
s"Automatically picking scalafmt version $buildVersion. TRAVIS_TAG=${System.getenv("TRAVIS_TAG")}"
)
buildVersion
} else {
"0.6.0-M12"
}
} cross CrossVersion.full,
"org.eclipse.jgit" % "org.eclipse.jgit" % "4.5.4.201711221230-r",
"com.geirsson" %% "coursier-small" % "1.0.0-M2",
"org.scalatest" %% "scalatest" % "3.0.5" % Test
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package sbt.internal.sbtscalafix

/** Dummy for sbt 0.13 */
trait JLineAccess {
def terminalWidth: Int = 80
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sbt.scalafixsbt
package sbt.internal.sbtscalafix

/** Helper class to access sbt's JLine instance */
trait JLineAccess {
Expand Down
7 changes: 0 additions & 7 deletions plugin/src/main/scala/org/scalameta/BuildInfo.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scala.util.control.NonFatal

import sbt.complete._
import sbt.complete.DefaultParsers._
import sbt.scalafixsbt.JLineAccess
import sbt.internal.sbtscalafix.JLineAccess

object ScalafixCompletions extends ScalafixCompletionsComponent with JLineAccess

Expand Down
9 changes: 9 additions & 0 deletions plugin/src/main/scala/scalafix/sbt/BuildInfo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package scalafix.sbt

object BuildInfo {
def scala212: String = "2.12.6"
def scala211: String = "2.11.12"
def scalameta: String = "4.0.0-M6"
def scalafix: String = "0.6.0-M12"
def supportedScalaVersions: List[String] = List("2.12.6", "2.11.12")
}
4 changes: 2 additions & 2 deletions plugin/src/main/scala/scalafix/sbt/ScalafixEnable.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package scalafix.sbt

import org.scalameta.BuildInfo
import sbt._, Keys._
import sbt._
import sbt.Keys._

/** Command to automatically enable semanticdb-scalac for shell session */
object ScalafixEnable {
Expand Down
43 changes: 43 additions & 0 deletions plugin/src/main/scala/scalafix/sbt/ScalafixInterface.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package scalafix.sbt

import com.geirsson.coursiersmall._
import java.net.URLClassLoader
import scala.language.reflectiveCalls

trait ScalafixInterface {
def main(args: Array[String]): Unit
}

object ScalafixInterface {

def classloadInstance(): ScalafixInterface = {
val dep = new Dependency(
"ch.epfl.scala",
s"scalafix-cli_${BuildInfo.scala212}",
BuildInfo.scalafix
)
val settings = new Settings()
.withDependencies(List(dep))
.withRepositories(
List(
Repository.MavenCentral,
Repository.SonatypeSnapshots
)
)
val jars = CoursierSmall.fetch(settings)
type Main = {
def main(args: Array[String]): Unit
}
val urls = jars.iterator.map(_.toUri.toURL).toArray
val classloader = new URLClassLoader(urls, null)
val cls = classloader.loadClass("scalafix.v1.Main$")
val ctor = cls.getDeclaredConstructor()
ctor.setAccessible(true)
val cli = ctor.newInstance().asInstanceOf[Main]
new ScalafixInterface {
override def main(args: Array[String]): Unit = {
cli.main(args)
}
}
}
}
35 changes: 25 additions & 10 deletions plugin/src/main/scala/scalafix/sbt/ScalafixPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package scalafix.sbt

import scalafix.Versions
import sbt.File
import sbt.Keys._
import sbt._
import sbt.complete.Parser
import sbt.plugins.JvmPlugin
import scalafix.internal.sbt.ScalafixCompletions
import sbt.Def
import scala.util.control.NonFatal

object ScalafixPlugin extends AutoPlugin {
override def trigger: PluginTrigger = allRequirements
Expand Down Expand Up @@ -54,10 +54,10 @@ object ScalafixPlugin extends AutoPlugin {
"Defaults to the build base directory if a .scalafix.conf file exists."
)
val scalafixSemanticdbVersion: SettingKey[String] = settingKey[String](
s"Which version of semanticdb to use. Default is ${Versions.scalameta}."
s"Which version of semanticdb to use. Default is ${BuildInfo.scalameta}."
)
val scalafixSemanticdb: ModuleID =
scalafixSemanticdb(Versions.scalameta)
scalafixSemanticdb(BuildInfo.scalameta)
def scalafixSemanticdb(scalametaVersion: String): ModuleID =
"org.scalameta" % "semanticdb-scalac" % scalametaVersion cross CrossVersion.full
val scalafixVerbose: SettingKey[Boolean] =
Expand All @@ -67,22 +67,22 @@ object ScalafixPlugin extends AutoPlugin {
scalafix := scalafixTaskImpl(
scalafixParserCompat,
compat = true,
Seq("--format", "sbt")
Seq()
).tag(Scalafix).evaluated,
scalafixTest := scalafixTaskImpl(
scalafixParserCompat,
compat = true,
Seq("--test", "--format", "sbt")
Seq("--test")
).tag(Scalafix).evaluated,
scalafixCli := scalafixTaskImpl(
scalafixParser,
compat = false,
Seq("--format", "sbt")
Seq()
).tag(Scalafix).evaluated,
scalafixAutoSuppressLinterErrors := scalafixTaskImpl(
scalafixParser,
compat = true,
Seq("--auto-suppress-linter-errors", "--format", "sbt")
Seq("--auto-suppress-linter-errors")
).tag(Scalafix).evaluated
)

Expand All @@ -107,6 +107,15 @@ object ScalafixPlugin extends AutoPlugin {
}
import autoImport._

lazy val cli: Either[Throwable, ScalafixInterface] = {
try {
Right(ScalafixInterface.classloadInstance())
} catch {
case NonFatal(e) =>
Left(e)
}
}

override def projectSettings: Seq[Def.Setting[_]] =
Seq(Compile, Test).flatMap(inConfig(_)(scalafixConfigSettings))

Expand All @@ -118,7 +127,7 @@ object ScalafixPlugin extends AutoPlugin {
sbtfixTest := sbtfixImpl(compat = true, extraOptions = Seq("--test")).evaluated,
aggregate.in(sbtfix) := false,
aggregate.in(sbtfixTest) := false,
scalafixSemanticdbVersion := Versions.scalameta
scalafixSemanticdbVersion := BuildInfo.scalameta
)

private def sbtfixImpl(compat: Boolean, extraOptions: Seq[String] = Seq()) = {
Expand Down Expand Up @@ -193,7 +202,11 @@ object ScalafixPlugin extends AutoPlugin {
streams: TaskStreams
): Def.Initialize[Task[Unit]] = {
if (files.isEmpty) Def.task(())
else {
else if (cli.isLeft) {
Def.task {
throw cli.left.get
}
} else {
Def.task {
val args = Array.newBuilder[String]

Expand Down Expand Up @@ -224,10 +237,12 @@ object ScalafixPlugin extends AutoPlugin {
}

args += "--no-sys-exit"
args += "--format"
args += "sbt"

args ++= files.iterator.map(_.getAbsolutePath)

_root_.scalafix.v1.Main.main(args.result())
cli.right.get.main(args.result())
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions plugin/src/sbt-test/sbt-scalafix/basic/.scalafix.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rules = [
ExplicitResultTypes
]
20 changes: 20 additions & 0 deletions plugin/src/sbt-test/sbt-scalafix/basic/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
inThisBuild(
List(
scalaVersion := "2.12.6",
libraryDependencies ++= List(
"org.scalameta" %% "testkit" % "4.0.0-M6" % Test,
"org.scalacheck" %% "scalacheck" % "1.13.5" % Test,
"org.scalatest" %% "scalatest" % "3.0.5" % Test
)
)
)

lazy val example = project
.settings(
Defaults.itSettings,
inConfig(IntegrationTest)(scalafixConfigSettings),
addCompilerPlugin(scalafixSemanticdb),
scalacOptions += "-Yrangepos"
)

lazy val tests = project
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package example

object Example {
implicit val str = null.asInstanceOf[java.util.Map.Entry[Int, String]]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
libraryDependencies += "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0"

resolvers += Resolver.sonatypeRepo("releases")

libraryDependencies += "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0"
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.1.0-M6")
4 changes: 4 additions & 0 deletions plugin/src/sbt-test/sbt-scalafix/basic/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-> scalafixTest
> scalafix
> scalafixTest
> tests/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package tests

import org.scalatest._
import scala.meta.internal.io._
import scala.meta.testkit._

class CheckSuite extends FunSuite with DiffAssertions {

test("> scalafix") {
val root = PathIO.workingDirectory.resolve("example").resolve("src")
val obtained = StringFS.asString(root)
val expected =
"""
|/main/scala/example/Example.scala
|package example
|
|object Example {
| implicit val str: _root_.java.util.Map.Entry[_root_.scala.Int, _root_.scala.Predef.String] = null.asInstanceOf[java.util.Map.Entry[Int, String]]
|}
|""".stripMargin
assertNoDiff(obtained, expected)
}

}
2 changes: 1 addition & 1 deletion plugin/src/sbt-test/sbt-scalafix/cross-build/build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _root_.scalafix.Versions
import _root_.scalafix.sbt.{BuildInfo => Versions}
inThisBuild(
List(
updateOptions := updateOptions.value.withLatestSnapshots(false),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
resolvers += Resolver.sonatypeRepo("releases")
libraryDependencies += "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0"

addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))
Original file line number Diff line number Diff line change
@@ -1 +1 @@
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.1.0-M6")
2 changes: 1 addition & 1 deletion plugin/src/sbt-test/sbt-scalafix/scalafixEnable/build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
val V = _root_.scalafix.Versions
val V = _root_.scalafix.sbt.BuildInfo

// 2.10 is not supported, scalafix is not enabled
lazy val scala210 = project.settings(
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
resolvers += Resolver.sonatypeRepo("releases")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))

This file was deleted.

2 changes: 1 addition & 1 deletion plugin/src/sbt-test/sbt-scalafix/suppress/build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _root_.scalafix.Versions
import _root_.scalafix.sbt.{BuildInfo => Versions}

val suppress = project.settings(
scalaVersion := Versions.scala212
Expand Down
4 changes: 2 additions & 2 deletions plugin/src/sbt-test/sbt-scalafix/suppress/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
libraryDependencies += "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0"

resolvers += Resolver.sonatypeRepo("releases")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))
libraryDependencies += "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0"
Original file line number Diff line number Diff line change
@@ -1 +1 @@
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.1.0-M6")
Loading

0 comments on commit 72c5815

Please sign in to comment.