Skip to content

Commit

Permalink
Merge pull request #29 from tindzk/issue/16
Browse files Browse the repository at this point in the history
Fix inheritance of compiler plug-ins
  • Loading branch information
tindzk authored Jul 22, 2019
2 parents adf5fa6 + 494a21a commit 5c6ff6e
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 30 deletions.
29 changes: 22 additions & 7 deletions src/main/scala/seed/artefact/ArtefactResolution.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ object ArtefactResolution {
def compilerDeps(build: Build, module: Module): List[Set[JavaDep]] = {
def f(build: Build, module: Module, platform: Platform): Set[JavaDep] = {
import build.project.scalaOrganisation
val platformModule = BuildConfig.platformModule(module, platform)

val platformVer = BuildConfig.platformVersion(build, module, platform)
val compilerVer = BuildConfig.scalaVersion(build.project, List(module))
val compilerVer = BuildConfig.scalaVersion(build.project,
platformModule.toList :+ module)

val scalaDeps = Set(
Artefact.scalaCompiler(scalaOrganisation) -> compilerVer,
Expand All @@ -120,18 +123,17 @@ object ArtefactResolution {
else Set()
)

val dependencies = mergeDeps[ScalaDep](
module.compilerDeps ++ platformModule.toList.flatMap(_.compilerDeps))

scalaDeps.map { case (artefact, version) =>
javaDepFromArtefact(artefact, version, platform, platformVer,
compilerVer)
} ++ module.compilerDeps.map(dep =>
} ++ dependencies.map(dep =>
javaDepFromScalaDep(dep, platform, platformVer, compilerVer))
}

module.targets.map(target =>
f(build,
BuildConfig.platformModule(module, target).getOrElse(module),
target)
).filter(_.nonEmpty)
module.targets.map(target => f(build, module, target)).filter(_.nonEmpty)
}

def allCompilerDeps(build: Build): List[Set[JavaDep]] =
Expand Down Expand Up @@ -278,4 +280,17 @@ object ArtefactResolution {

(resolvedCachePath, platformResolution, compilerResolution)
}

/** @return If there are two dependencies with the same organisation and
* artefact name, only retain the last one, regardless of its version.
*/
def mergeDeps[T <: Dep](deps: List[T]): List[T] =
deps.foldLeft(List[T]()) { case (acc, cur) =>
acc.find(
d => d.organisation == cur.organisation && d.artefact == cur.artefact
) match {
case None => acc :+ cur
case Some(previous) => acc.diff(List(previous)) :+ cur
}
}
}
2 changes: 1 addition & 1 deletion src/main/scala/seed/artefact/Coursier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ object Coursier {
platformVersion, compilerVersion))

result.resolution.dependencyArtifacts().find { case (dep, attr, art) =>
dep.module.name.value == name
dep.module.name.value == name && dep.version == version
}.map(a => result.artefacts(a._3.url).toPath)
}
}
32 changes: 20 additions & 12 deletions src/main/scala/seed/generation/util/ScalaCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@ import java.nio.file.Path

import seed.artefact.Coursier
import seed.config.BuildConfig
import seed.artefact.ArtefactResolution
import seed.model.Build.Module
import seed.model.Platform.{JavaScript, Native}
import seed.model.{Artefact, Build, Platform}

object ScalaCompiler {
def resolveCompiler(build: Build,
module: Module,
compilerResolution: List[Coursier.ResolutionResult],
def resolveCompiler(compilerResolution: List[Coursier.ResolutionResult],
artefact: Artefact,
artefactVersion: String,
platform: Platform,
platformVer: String,
compilerVer: String
): Path = {
val platformVer = BuildConfig.platformVersion(build, module, platform)

compilerResolution.iterator.flatMap(resolution =>
Coursier.artefactPath(resolution, artefact, platform,
platformVer, compilerVer, platformVer)
platformVer, compilerVer, artefactVersion)
).toList.headOption.getOrElse(
throw new Exception(s"Artefact '$artefact' missing"))
}
Expand All @@ -30,16 +29,25 @@ object ScalaCompiler {
compilerResolution: List[Coursier.ResolutionResult],
platform: Platform,
compilerVer: String): List[String] = {
import ArtefactResolution.mergeDeps

val platformVer = BuildConfig.platformVersion(build, module, platform)
val moduleDeps = BuildConfig.collectModuleDeps(build, module, platform)
val modules = moduleDeps.map(build.module) :+ module
val artefacts =
(if (platform == JavaScript) List(Artefact.ScalaJsCompiler)
else if (platform == Native) List(Artefact.ScalaNativePlugin)
(if (platform == JavaScript) List(Artefact.ScalaJsCompiler -> platformVer)
else if (platform == Native) List(Artefact.ScalaNativePlugin -> platformVer)
else List()
) ++ modules.flatMap(_.compilerDeps.map(Artefact.fromDep))
) ++ mergeDeps(modules.flatMap { m =>
val platformModule = BuildConfig.platformModule(m, platform)
val dependencies =
m.compilerDeps ++ platformModule.toList.flatMap(_.compilerDeps)
mergeDeps(dependencies)
}).map(d => Artefact.fromDep(d) -> d.version)

artefacts.map(a => resolveCompiler(
build, module, compilerResolution, a, platform, compilerVer)
).map(p => "-Xplugin:" + p)
artefacts.map { case (artefact, version) =>
resolveCompiler(compilerResolution, artefact, version, platform,
platformVer, compilerVer)
}.map(p => "-Xplugin:" + p)
}
}
42 changes: 41 additions & 1 deletion src/test/scala/seed/artefact/ArtefactResolutionSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object ArtefactResolutionSpec extends SimpleTestSuite {
JavaDep("net.java.dev.jna", "jna", "4.5.1")))
}

test("compilerDeps()") {
test("Inherit compiler dependencies") {
val build = Build(
project = Project("2.12.8", scalaJsVersion = Some("0.6.26")),
module = Map())
Expand All @@ -80,6 +80,46 @@ object ArtefactResolutionSpec extends SimpleTestSuite {
JavaDep("org.scala-lang", "scala-library", "2.12.8"),
JavaDep("org.scala-lang", "scala-reflect", "2.12.8"),
JavaDep("org.scala-js", "scalajs-compiler_2.12.8", "0.6.26"),
JavaDep("org.scalamacros", "paradise_2.12.8", "2.1.1"),
JavaDep("com.softwaremill.clippy", "plugin_2.12", "0.6.0"))))
}

test("Compiler dependency with overridden version in platform module") {
val build = Build(
project = Project("2.12.8", scalaJsVersion = Some("0.6.26")),
module = Map())
val module = Module(
targets = List(JVM, JavaScript),
compilerDeps = List(
ScalaDep("org.scalamacros", "paradise", "2.1.0", VersionTag.Full)),
js = Some(Module(
compilerDeps = List(
ScalaDep("org.scalamacros", "paradise", "2.1.1", VersionTag.Full)))))
val deps = ArtefactResolution.compilerDeps(build, module)

assertEquals(deps,
List(
Set(
JavaDep("org.scala-lang", "scala-compiler", "2.12.8"),
JavaDep("org.scala-lang", "scala-library", "2.12.8"),
JavaDep("org.scala-lang", "scala-reflect", "2.12.8"),
JavaDep("org.scalamacros", "paradise_2.12.8", "2.1.0")),
Set(
JavaDep("org.scala-lang", "scala-compiler", "2.12.8"),
JavaDep("org.scala-lang", "scala-library", "2.12.8"),
JavaDep("org.scala-lang", "scala-reflect", "2.12.8"),
JavaDep("org.scala-js", "scalajs-compiler_2.12.8", "0.6.26"),
JavaDep("org.scalamacros", "paradise_2.12.8", "2.1.1"))))
}

test("mergeDeps()") {
val deps =
List(
ScalaDep("org.scalamacros", "paradise", "2.1.0", VersionTag.Full),
ScalaDep("org.scalamacros", "paradise", "2.1.1", VersionTag.Full))

assertEquals(
ArtefactResolution.mergeDeps(deps),
List(ScalaDep("org.scalamacros", "paradise", "2.1.1", VersionTag.Full)))
}
}
20 changes: 18 additions & 2 deletions src/test/scala/seed/artefact/CoursierSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package seed.artefact

import minitest.SimpleTestSuite
import seed.Log
import seed.model.Build
import seed.model.{Artefact, Build, Platform}
import seed.model.Build.JavaDep

object CoursierSpec extends SimpleTestSuite {
var resolution: Coursier.ResolutionResult = _

test("Resolve dependency") {
val dep = JavaDep("org.scala-js", "scalajs-dom_sjs0.6_2.12", "0.9.6")
val resolution = Coursier.resolveAndDownload(Set(dep),
resolution = Coursier.resolveAndDownload(Set(dep),
Build.Resolvers(), Coursier.DefaultIvyPath,
Coursier.DefaultCachePath, optionalArtefacts = true, silent = true,
Log.urgent)
Expand All @@ -19,4 +21,18 @@ object CoursierSpec extends SimpleTestSuite {
result.exists(_.javaDocJar.nonEmpty) &&
result.exists(_.sourcesJar.nonEmpty))
}

test("Get artefact path") {
val artefact = Artefact("org.scala-js", "scalajs-dom_sjs0.6_2.12", None)
val version = "0.9.6"
val unresolvedVersion = "0.0.1"

val path = Coursier.artefactPath(resolution, artefact, Platform.JavaScript,
"0.6.26", "2.12.8", version)
assert(path.exists(_.toString.endsWith("scalajs-dom_sjs0.6_2.12-0.9.6.jar")))

val path2 = Coursier.artefactPath(resolution, artefact, Platform.JavaScript,
"0.6.26", "2.12.8", unresolvedVersion)
assert(path2.isEmpty)
}
}
70 changes: 63 additions & 7 deletions src/test/scala/seed/generation/BloopIntegrationSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
override def setup(): Unit = ()
override def tearDown(env: Unit): Unit = ()

def readBloopJson(path: Path): bloop.config.Config.File = {
val content = FileUtils.readFileToString(path.toFile, "UTF-8")

import io.circe.parser._
decode[bloop.config.Config.File](content)(
ConfigEncoderDecoders.allDecoder
).right.get
}

def compileAndRun(projectPath: Path) = {
def compile =
TestProcessHelper.runBloop(projectPath)("compile", "example").map { x =>
Expand All @@ -47,7 +56,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
compileAndRun(projectPath)
}

testAsync("Build project with compiler plug-in") { _ =>
testAsync("Build project with compiler plug-in defined on cross-platform module") { _ =>
val BuildConfig.Result(build, projectPath, _) = BuildConfig.load(
Paths.get("test/example-paradise"), Log.urgent).get
val buildPath = projectPath.resolve("build")
Expand All @@ -59,6 +68,58 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
compileAndRun(projectPath)
}

testAsync("Build project with compiler plug-in defined on platform modules") { _ =>
val BuildConfig.Result(build, projectPath, _) = BuildConfig.load(
Paths.get("test/example-paradise-platform"), Log.urgent).get
val buildPath = projectPath.resolve("build")
if (Files.exists(buildPath)) FileUtils.deleteDirectory(buildPath.toFile)
val packageConfig = PackageConfig(tmpfs = false, silent = false,
ivyPath = None, cachePath = None)
cli.Generate.ui(Config(), projectPath, build, Command.Bloop(packageConfig),
Log.urgent)
compileAndRun(projectPath)
}

testAsync("Build project with overridden compiler plug-in version") { _ =>
val projectPath = Paths.get("test/example-paradise-versions")
val BuildConfig.Result(build, _, _) =
BuildConfig.load(projectPath, Log.urgent).get
val buildPath = projectPath.resolve("build")
if (Files.exists(buildPath)) FileUtils.deleteDirectory(buildPath.toFile)
val packageConfig = PackageConfig(tmpfs = false, silent = false,
ivyPath = None, cachePath = None)
cli.Generate.ui(Config(), projectPath, build, Command.Bloop(packageConfig),
Log.urgent)

val bloopPath = projectPath.resolve(".bloop")

val macrosJvm = readBloopJson(bloopPath.resolve("macros-jvm.json"))
val macrosJs = readBloopJson(bloopPath.resolve("macros-js.json"))
val exampleJvm = readBloopJson(bloopPath.resolve("example-jvm.json"))
val exampleJs = readBloopJson(bloopPath.resolve("example-js.json"))

def getFileName(path: String): String = path.drop(path.lastIndexOf('/') + 1)

assertEquals(
macrosJvm.project.scala.get.options.filter(_.contains("paradise"))
.map(getFileName),
List("paradise_2.11.12-2.1.0.jar"))
assertEquals(
macrosJs.project.scala.get.options.filter(_.contains("paradise"))
.map(getFileName),
List("paradise_2.11.12-2.1.1.jar"))
assertEquals(
exampleJvm.project.scala.get.options.filter(_.contains("paradise"))
.map(getFileName),
List("paradise_2.11.12-2.1.0.jar"))
assertEquals(
exampleJs.project.scala.get.options.filter(_.contains("paradise"))
.map(getFileName),
List("paradise_2.11.12-2.1.1.jar"))

compileAndRun(projectPath)
}

testAsync("Build modules with different Scala versions") { _ =>
val BuildConfig.Result(build, projectPath, _) = BuildConfig.load(
Paths.get("test/multiple-scala-versions"), Log.urgent).get
Expand Down Expand Up @@ -120,12 +181,7 @@ object BloopIntegrationSpec extends TestSuite[Unit] {
testAsync("Build project with custom command target") { _ =>
buildCustomTarget("custom-command-target").map { _ =>
val path = Paths.get(s"test/custom-command-target/.bloop/demo.json")
val content = FileUtils.readFileToString(path.toFile, "UTF-8")

import io.circe.parser._
val result = decode[bloop.config.Config.File](content)(
ConfigEncoderDecoders.allDecoder
).right.get
val result = readBloopJson(path)

// Should not include "utils" dependency since it does not have any
// Scala sources and no Bloop module.
Expand Down
26 changes: 26 additions & 0 deletions test/example-paradise-platform/build.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# See https://github.com/tindzk/seed/issues/16
[project]
scalaVersion = "2.11.12"
scalaJsVersion = "0.6.26"
scalaOptions = ["-encoding", "UTF-8", "-unchecked", "-deprecation", "-Xfuture"]

[module.macros]
root = "macros"
sources = ["macros"]
targets = ["jvm", "js"]

[module.macros.jvm]
compilerDeps = [
["org.scalamacros", "paradise", "2.1.1", "full"]
]

[module.macros.js]
compilerDeps = [
["org.scalamacros", "paradise", "2.1.1", "full"]
]

[module.example]
root = "example"
sources = ["example"]
targets = ["jvm", "js"]
moduleDeps = ["macros"]
4 changes: 4 additions & 0 deletions test/example-paradise-platform/example/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@hello
object Test extends App {
println(this.hello)
}
26 changes: 26 additions & 0 deletions test/example-paradise-platform/macros/Macros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation

object helloMacro {
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
import Flag._
val result = {
annottees.map(_.tree).toList match {
case q"object $name extends ..$parents { ..$body }" :: Nil =>
q"""
object $name extends ..$parents {
def hello: ${typeOf[String]} = "hello"
..$body
}
"""
}
}
c.Expr[Any](result)
}
}

class hello extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro helloMacro.impl
}
23 changes: 23 additions & 0 deletions test/example-paradise-versions/build.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[project]
scalaVersion = "2.11.12"
scalaJsVersion = "0.6.26"
scalaOptions = ["-encoding", "UTF-8", "-unchecked", "-deprecation", "-Xfuture"]

[module.macros]
root = "macros"
sources = ["macros"]
targets = ["jvm", "js"]
compilerDeps = [
["org.scalamacros", "paradise", "2.1.0", "full"]
]

[module.macros.js]
compilerDeps = [
["org.scalamacros", "paradise", "2.1.1", "full"]
]

[module.example]
root = "example"
sources = ["example"]
targets = ["jvm", "js"]
moduleDeps = ["macros"]
Loading

0 comments on commit 5c6ff6e

Please sign in to comment.