Resolving the dependency of Scala Macros and Compiler Framework in SBT - scala

I am trying to write a framework to make writing Scala compiler plugins easier, what I am doing is writing a framework on top of the Scala quasiquotes. So my project depends on macros from macro-paradise and both scala-compiler and scala-reflect libraries.
I wrote an SBT build script by following the instructions mentioned here: https://github.com/scalamacros/sbt-example-paradise/blob/master/project/Build.scala
And used scalaVersion 2.11.0-SNAPSHOT, 2.10.3-SNAPSHOT, 2.10.3-RC1, 2.10.2 to compile my project, but neither of them worked. Here is my sbt build script:
import sbt._
import Keys._
object LombrelloBuildSettings {
val sversion = "2.10.3-SNAPSHOT"
val buildSettings = Defaults.defaultSettings ++ Seq(
name := "lombrello",
organization := "ch.usi.inf.l3",
version := "0.1-SNAPSHOT",
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"),
scalaVersion := sversion,
scalaOrganization := "org.scala-lang.macro-paradise",
resolvers += Resolver.sonatypeRepo("snapshots"),
licenses := ("BSD 3-Clause", new java.net.URL("http://opensource.org/licenses/BSD-3-Clause")) :: Nil,
libraryDependencies ++= Seq("org.scala-lang.macro-paradise" % "scala-reflect" % sversion,
"org.scala-lang" % "scala-compiler" % sversion),
addCompilerPlugin("org.scala-lang.plugins" % "macro-paradise" % "2.0.0-SNAPSHOT" cross CrossVersion.full))
}
object LombrelloBuild extends Build {
import LombrelloBuildSettings._
lazy val root: Project = Project(
"root",
file("."),
settings = buildSettings ++ Seq(
run <<= run in Compile in tests
)
) aggregate (main, tests)
lazy val main: Project = Project(
"main",
file("src/main"),
settings = buildSettings
)
lazy val tests: Project = Project(
"tests",
file("src/test"),
settings = buildSettings ++ Seq(name := "tests")) dependsOn (main)
}
Using the scalaVersion 2.10-3-RC1, I get the following error:
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: UNRESOLVED DEPENDENCIES ::
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: org.scala-lang.macro-paradise#scala-library;2.10.3-RC1: not found
[warn] :: org.scala-lang.macro-paradise#scala-reflect;2.10.3-RC1: not found
[warn] :: org.scala-lang.macro-paradise#scala-compiler;2.10.3-RC1: not found
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
sbt.ResolveException: unresolved dependency: org.scala-lang.macro-paradise#scala-library;2.10.3-RC1: not found
unresolved dependency: org.scala-lang.macro-paradise#scala-reflect;2.10.3-RC1: not found
unresolved dependency: org.scala-lang.macro-paradise#scala-compiler;2.10.3-RC1: not found
at sbt.IvyActions$.sbt$IvyActions$$resolve(IvyActions.scala:213)
at sbt.IvyActions$$anonfun$update$1.apply(IvyActions.scala:122)
Using, scalaVersion 2.11.0-SNAPSHOT, I got the following error:
java.lang.NoClassDefFoundError: scala/tools/nsc/typechecker/TypersTracking$class
at org.scalalang.macroparadise.Plugin$$anon$1.<init>(Plugin.scala:20)
at org.scalalang.macroparadise.Plugin.<init>(Plugin.scala:20)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
While using the version 2.10.3-SNAPSHOT I got the following:
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: UNRESOLVED DEPENDENCIES ::
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: org.scala-lang.plugins#macro-paradise_2.10.3-SNAPSHOT;2.0.0-SNAPSHOT: not found
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
sbt.ResolveException: unresolved dependency: org.scala-lang.plugins#macro-paradise_2.10.3-SNAPSHOT;2.0.0-SNAPSHOT: not found
And version 2.10.2 couldn't resolve the dependencies of scala-library, scala-reflect and 2.10.2 at all (like 2.10.3-RC1)!
My question is, is it at all possible to mix both compiler API and Macro API and make them work under SBT, if yes what exactly is wrong with my build script?

It appeared that I used some wrong settings in my SBT configuration. I didn't need to change the scalaOrganization, neither needed to add macro-paradise to my library dependencies. so the settings should become like:
val sversion = "2.10.2"
val buildSettings = Defaults.defaultSettings ++ Seq(
name := "lombrello",
organization := "ch.usi.inf.l3",
version := "0.1-SNAPSHOT",
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"),
scalaVersion := sversion,
resolvers += Resolver.sonatypeRepo("snapshots"),
licenses := ("BSD 3-Clause", new java.net.URL("http://opensource.org/licenses/BSD-3-Clause")) :: Nil,
libraryDependencies ++= Seq("org.scala-lang" % "scala-reflect" % sversion,
"org.scala-lang" % "scala-compiler" % sversion),
addCompilerPlugin("org.scala-lang.plugins" % "macro-paradise" % "2.0.0-SNAPSHOT" cross CrossVersion.full)
)
All credits go to Eugene Burmako in this comment.

Related

Adding SBT as a dependency in SBT file

I am writing few sbt tasks in a scala file. These SBT tasks will be imported into many other projects.
lazy val root = (project in file(".")).
settings(
inThisBuild(List(
organization := "com.example",
scalaVersion := "2.11.8",
version := "1.0.0"
)),
name := "sbttasks",
libraryDependencies ++= Seq(
"org.scala-sbt" % "sbt" % "1.0.0" % "provided"
)
)
I get a compilation error
error] java.lang.RuntimeException: Conflicting cross-version suffixes in: org.scala-lang.modules:scala-xml, org.scala-lang.modules:scala-parser-combinators
[error] at scala.sys.package$.error(package.scala:27)
[error] at sbt.librarymanagement.ConflictWarning$.processCrossVersioned(ConflictWarning.scala:39)
[error] at sbt.librarymanagement.ConflictWarning$.apply(ConflictWarning.scala:19)
[error] at sbt.Classpaths$.$anonfun$ivyBaseSettings$64(Defaults.scala:1995)
[error] at scala.Function1.$anonfun$compose$1(Function1.scala:44)
[error] at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:39)
[error] at sbt.std.Transform$$anon$4.work(System.scala:66)
[error] at sbt.Execute.$anonfun$submit$2(Execute.scala:262)
[error] at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error] at sbt.Execute.work(Execute.scala:271)
[error] at sbt.Execute.$anonfun$submit$1(Execute.scala:262)
[error] at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:174)
[error] at sbt.Completion
I don't want to write the custom tasks in build.sbt itself (as the SBT documentation shows) because then I won't be able to import my custom tasks into other projects.
To write reusable tasks that you can "import" in different projects, you need to make an sbt plugin.
If you have a multi-project build and want to reuse your tasks in the subprojects, you can create a file project/MyPlugin.scala with
import sbt._
import sbt.Keys._
object MyPlugin extends AutoPlugin {
override def trigger = noTrigger
object autoImport {
val fooTask = taskKey[Foo]("Foo description")
val barTask = taskKey[Bar]("Bar description")
}
import autoImport._
override lazy val projectSettings = Seq(
fooTask := { ??? },
barTask := { ??? }
)
}
Then to enable this plugin (i.e. make those tasks available) in a subproject, you can write this in your build.sbt:
lazy val subproject = (project in file("subproject"))
.enablePlugins(MyPlugin)
On the contrast, if you want to reuse these tasks in other unrelated projects, you need to make this plugin a separate project and publish it. It's a normal sbt project, but instead of an explicit sbt dependency, you write in its build.sbt:
sbtPlugin := true
And the code defining tasks goes to src/main/scala/ (like in a normal project).
You can read in detail about writing plugins in the sbt documentation.
Change version of "org.scala-sbt" to "1.0.0-M4"
lazy val root = (project in file(".")).
settings(
inThisBuild(List(
organization := "com.example",
scalaVersion := "2.11.8",
version := "1.0.0",
name := "sbttasks"
)),
libraryDependencies ++= Seq(
"org.scala-sbt" % "sbt" % "1.0.0-M4" % "provided"
)
)
For entire compatibility matrix check
https://mvnrepository.com/artifact/org.scala-sbt/main

sbt cross project, shared dependencies for test example

I have a small project.
Where I have the following problem:
scalaTest needs to be added to all three dependency project (client, server, shared), otherwise the scalatest library is not accessible from all projects.
In other words, if I write
val jvmDependencies = Def.setting(Seq(
"org.scalaz" %% "scalaz-core" % "7.2.8"
)++scalaTest)
then things work fine.
But if I don't write ++scalaTest into each three dependencies then it fails like this:
> test
[info] Compiling 1 Scala source to /Users/joco/tmp3/server/target/scala-2.11/test-classes...
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:1: object specs2 is not a member of package org
[error] import org.specs2.mutable.Specification
[error] ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:3: not found: type Specification
[error] class Test extends Specification {
[error] ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:5: value should is not a member of String
[error] "Test" should {
[error] ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:6: value in is not a member of String
[error] "one is one" in {
[error] ^
[error] /Users/joco/tmp3/server/src/test/scala/Test.scala:8: value === is not a member of Int
[error] 1===one
[error] ^
[error] 5 errors found
[error] (server/test:compileIncremental) Compilation failed
[error] Total time: 4 s, completed Mar 18, 2017 1:56:54 PM
However for production(not test) code everything works just fine: I don't have to add 3 times the same dependencies (in this example autowire) to all three projects if I want to use a library in all three projects, it is enough to add it to only the shared project and then I can use that library from all three projects.
For test code, however, as I mentioned above, currently I have to add the same library dependency (scalaTest - below) to all three projects.
Question: Is there a way to avoid this ?
Settings.scala:
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
import sbt.Keys._
import sbt._
object Settings {
val scalacOptions = Seq(
"-Xlint",
"-unchecked",
"-deprecation",
"-feature",
"-Yrangepos"
)
object versions {
val scala = "2.11.8"
}
val scalaTest=Seq(
"org.scalatest" %% "scalatest" % "3.0.1" % "test",
"org.specs2" %% "specs2" % "3.7" % "test")
val sharedDependencies = Def.setting(Seq(
"com.lihaoyi" %%% "autowire" % "0.2.6"
)++scalaTest)
val jvmDependencies = Def.setting(Seq(
"org.scalaz" %% "scalaz-core" % "7.2.8"
))
/** Dependencies only used by the JS project (note the use of %%% instead of %%) */
val scalajsDependencies = Def.setting(Seq(
"org.scala-js" %%% "scalajs-dom" % "0.9.1"
)++scalaTest)
}
build.sbt:
import sbt.Keys._
import sbt.Project.projectToRef
import webscalajs.SourceMappings
lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared")) .settings(
scalaVersion := Settings.versions.scala,
libraryDependencies ++= Settings.sharedDependencies.value,
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
) .jsConfigure(_ enablePlugins ScalaJSWeb)
lazy val sharedJVM = shared.jvm.settings(name := "sharedJVM")
lazy val sharedJS = shared.js.settings(name := "sharedJS")
lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable functions")
lazy val client: Project = (project in file("client"))
.settings(
scalaVersion := Settings.versions.scala,
scalacOptions ++= Settings.scalacOptions,
libraryDependencies ++= Settings.scalajsDependencies.value,
testFrameworks += new TestFramework("utest.runner.Framework")
)
.enablePlugins(ScalaJSPlugin)
.disablePlugins(RevolverPlugin)
.dependsOn(sharedJS)
lazy val clients = Seq(client)
lazy val server = (project in file("server")) .settings(
scalaVersion := Settings.versions.scala,
scalacOptions ++= Settings.scalacOptions,
libraryDependencies ++= Settings.jvmDependencies.value
)
.enablePlugins(SbtLess,SbtWeb)
.aggregate(clients.map(projectToRef): _*)
.dependsOn(sharedJVM)
onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value
fork in run := true
cancelable in Global := true
For test code, however, as I mentioned above, currently I have to add the same library dependency (scalaTest - below) to all three projects.
That is expected. test dependencies are not inherited along dependency chains. That makes sense, because you don't want to depend on JUnit just because you depend on a library that happens to be tested using JUnit.
Although yes, that calls for a bit of duplication when you have several projects in the same build, all using the same testing framework. This is why we often find some commonSettings that are added to all projects of an sbt build. This is also where we typically put things like organization, scalaVersion, and many other settings that usually apply to all projects inside one build.

Is it possible to reject publish if SNAPSHOT dependencies are used in SBT?

I keep accidentally publishing my internal project still referencing internal SNAPSHOTs, but it would be very helpful if there was an SBT plugin that would fail to publish if you are relying on any SNAPSHOT dependencies. Is anyone aware of such a plugin or feature in SBT?
Here's how you can write such plugin.
output
> publish
[info] :: delivering :: com.example#b_2.10;0.1.0 :: 0.1.0 :: release :: Fri Jan 13 15:50:53 EST 2017
[info] delivering ivy file to /xxx/b/target/scala-2.10/ivy-0.1.0.xml
[info] Wrote /xxx/b/target/scala-2.10/b_2.10-0.1.0.pom
[info] Wrote /xxx/a/target/scala-2.10/a_2.10-0.1.0.pom
[info] :: delivering :: com.example#a_2.10;0.1.0 :: 0.1.0 :: release :: Fri Jan 13 15:50:53 EST 2017
[info] delivering ivy file to /xxx/a/target/scala-2.10/ivy-0.1.0.xml
[trace] Stack trace suppressed: run last b/*:publishConfiguration for the full output.
[trace] Stack trace suppressed: run last a/*:publishConfiguration for the full output.
[error] (b/*:publishConfiguration) SNAPSHOT found in classpath:
[error] com.eed3si9n:treehugger_2.10:0.2.4-SNAPSHOT:compile->default;compile->compile;compile->runtime;compile->default(compile);compile->master
[error] (a/*:publishConfiguration) SNAPSHOT found in classpath:
[error] com.eed3si9n:treehugger_2.10:0.2.4-SNAPSHOT:compile->default;compile->compile;compile->runtime;compile->default(compile);compile->master
[error] com.example:c_2.10:0.1.0-SNAPSHOT:compile->compile;compile->default(compile)
[error] io.netty:netty-all:4.1.8.Final-SNAPSHOT:compile->default;compile->compile;compile->runtime;compile->default(compile);compile->master
[error] Total time: 0 s, completed Jan 13, 2017 3:50:53 PM
project/build.properties
sbt.version = 0.13.13
project/DepsVerifyPlugin.scala
import sbt._
import Keys._
object DepsVerifyPlugin extends sbt.AutoPlugin {
override def requires = plugins.JvmPlugin
override def trigger = allRequirements
override def projectSettings = Seq(
publishConfiguration := {
val old = publishConfiguration.value
val ur = update.value
ur.configuration("compile") foreach { compileReport =>
val allModules = compileReport.allModules
val snapshotDeps = allModules filter { _.revision contains "SNAPSHOT" }
if (snapshotDeps.nonEmpty) {
sys.error(
"SNAPSHOT found in classpath:\n" +
snapshotDeps.mkString("\n")
)
}
}
old
}
)
}
build.sbt
val commonSettings: Seq[Setting[_]] = Seq(
organization in ThisBuild := "com.example",
scalaVersion in ThisBuild := "2.10.6",
version in ThisBuild := "0.1.0",
resolvers += Resolver.sonatypeRepo("public"),
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/test-repo")))
)
val netty = "io.netty" % "netty-all" % "4.1.8.Final-SNAPSHOT"
val treehugger = "com.eed3si9n" %% "treehugger" % "0.2.4-SNAPSHOT"
lazy val root = (project in file("."))
.aggregate(a, b, c)
.settings(
commonSettings,
name := "Hello",
publish := ()
)
lazy val a = (project in file("a"))
.dependsOn(b, c)
.settings(
commonSettings,
libraryDependencies += netty
)
lazy val b = (project in file("b"))
.settings(
commonSettings,
libraryDependencies += treehugger
)
lazy val c = (project in file("c"))
.settings(
commonSettings,
version := "0.1.0-SNAPSHOT",
publish := ()
)
You could consider adopting sbt-release.
This is a more high-level 'workflow' plugin: 'publish' is used as one of the steps in a release (after 'check that there's no SNAPSHOT dependencies').
It will not prevent you from running 'sbt publish', but when you make a habit of using 'sbt release' instead of 'sbt publish' it accomplishes what you're looking for.

SBT setup for sbt-scalabuff

Can someone give me a minimal working sbt setup for sbt-scalabuff? The information that's out there seems incomplete. I'm currently trying to use addSbtPlugin("com.github.sbt" % "sbt-scalabuff" % "0.2"), but I get sbt.ResolveException: unresolved dependency: net.sandrogrzicic#scalabuff-runtime_2.9.2;1.3.6: not found. I guess I'm missing a repository.
Why is it using 2.9.2, though? I have scalaVersion := 2.10.3.
build.sbt
organization := "com.confabulous"
name := "protobuf"
version := "0.0.1-SNAPSHOT"
scalaVersion := "2.10.4"
scalacOptions += "-deprecation"
resolvers ++= Seq(
"sonatype releases" at "https://oss.sonatype.org/content/repositories/releas
"sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapsh
"typesafe repo" at "http://repo.typesafe.com/typesafe/releases/"
)
libraryDependencies += "net.sandrogrzicic" %% "scalabuff-runtime" % "1.3.6"
plugins/plugins.sbt
addSbtPlugin("com.github.sbt" % "sbt-scalabuff" % "0.2")
project/Build.scala
import sbt._
import scalabuff.ScalaBuffPlugin._
object build extends Build {
lazy val root = Project("main", file("."), settings = Defaults.defaultSetting
}
Output
$ sbt compile
Loading /usr/share/sbt/bin/sbt-launch-lib.bash
[info] Loading project definition from /home/dan/projects/confabulous/protobuf/project
[info] Updating {file:/home/dan/projects/confabulous/protobuf/project/}default-6a3ff1...
[info] Resolving org.scala-sbt#precompiled-2_10_1;0.12.4 ...
[info] Done updating.
[info] Set current project to protobuf (in build file:/home/dan/projects/confabulous/protobuf/)
[info] Compiling 1 Scala source to /home/dan/projects/confabulous/protobuf/target/scala-2.10/classes...
[error] /home/dan/projects/confabulous/protobuf/target/scala-2.10/src_managed/scala/com/confabulous/protobuf/ConfabulousProtobuf.scala:11: not found: value net
[error] with net.sandrogrzicic.scalabuff.Message[Pair] {
[error] ^
[error] /home/dan/projects/confabulous/protobuf/target/scala-2.10/src_managed/scala/com/confabulous/protobuf/ConfabulousProtobuf.scala:76: not found: value net
[error] with net.sandrogrzicic.scalabuff.Message[Notice] {
[error] ^
Add https://dl.bintray.com/actor/maven to your SBT resolver
resolvers ++= Seq("sonatype releases" at "https://oss.sonatype.org/content/repositories/releas
"sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapsh
"typesafe repo" at "http://repo.typesafe.com/typesafe/releases/"
"bintrayRepo" at "https://dl.bintray.com/actor/maven"
)

sbt compile yields "object casbah is not a member of package com.mongodb"

My directory structure:
-build.sbt
-src
--main
---scala
----MongoConnect.scala
-lib
My build.sbt:
name := "mongodb-experiments"
version := "0.1"
libraryDependencies ++= Seq(
"com.mongodb.casbah" %% "casbah" % "3.0.0-SNAPSHOT"
)
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
My MongoConnect.scala program:
import com.mongodb.casbah.Imports._
object MongoConnect{
def main(args: Array[String]){
println("Hello Mongo")
}
}
Why does sbt compile result in
object casbah is not a member of package com.mongodb
?
sbt compile
[info] Set current project to mongodb-experiments (in build file:/Users/hrishikeshparanjape/git-public/mongodb-experiments/)
[info] Updating {file:/Users/hrishikeshparanjape/git-public/mongodb-experiments/}default-fc358e...
[info] Resolving org.scala-lang#scala-library;2.9.1 ...
[info] Resolving com.mongodb.casbah#casbah_2.9.1;3.0.0-SNAPSHOT ...
[info] Resolving com.mongodb.casbah#casbah-util_2.9.1;3.0.0-SNAPSHOT ...
[info] Resolving org.slf4j#slf4j-api;1.6.0 ...
[info] Resolving org.mongodb#mongo-java-driver;2.7.2 ...
[info] Resolving org.scalaj#scalaj-collection_2.9.1;1.2 ...
[info] Resolving org.scala-tools.time#time_2.8.0;0.2 ...
[info] Resolving joda-time#joda-time;1.6 ...
[info] Resolving com.mongodb.casbah#casbah-commons_2.9.1;3.0.0-SNAPSHOT ...
[info] Resolving com.mongodb.casbah#casbah-core_2.9.1;3.0.0-SNAPSHOT ...
[info] Resolving com.mongodb.casbah#casbah-query_2.9.1;3.0.0-SNAPSHOT ...
[info] Resolving com.mongodb.casbah#casbah-gridfs_2.9.1;3.0.0-SNAPSHOT ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/hrishikeshparanjape/git-public/mongodb-experiments/target/scala-2.9.1/classes...
[error] /Users/hrishikeshparanjape/git-public/mongodb-experiments/src/main/scala/MongoConnect.scala:1: object casbah is not a member of package com.mongodb
[error] import com.mongodb.casbah.Imports._
[error] ^
[error] one error found
[error] {file:/Users/hrishikeshparanjape/git-public/mongodb-experiments/}default-fc358e/compile:compile: Compilation failed
[error] Total time: 7 s, completed Jul 26, 2012 11:53:35 PM
Why do you use snapshot repository with and old versions of casbah?
libraryDependencies ++= Seq(
"org.mongodb" %% "casbah" % "2.4.1"
)
resolvers += "typesafe" at "http://repo.typesafe.com/typesafe/releases/"
%% sign in dependency will choose configured in sbt scala version
For 3.x version there is a milestone
libraryDependencies ++= Seq(
"org.mongodb" %% "casbah" % "3.0.0-M2"
)
And as I remember in 3.x import should be changed to:
import com.mongodb.casbah._
modify your build.sbt file as:
name := "mongodb-experiments"
version := "0.1"
libraryDependencies ++= Seq(
"com.mongodb.casbah" % "casbah_2.9.0" % "2.2.0-SNAPSHOT"
)
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
For some reason, 3.0.0 does not work.