How to add examples to an SBT project? - scala

I want to add some examples to my Scala library. I prefer a directory layout like this:
examples/
example1/
Main.scala
...
example2/
Main.scala
...
src/
main/
scala/
...
test/
scala/
...
build.sbt
The examples use the packages in src/main/scala/. Is it possible to build and run the examples using SBT? Is it also possible to have dependencies specific to the examples?

It is possible, but you likely have to resort to complete .scala definition. I've put example project on bitbucket.
Here is the showcase:
// [info] Loading global plugins from /Users/omnomnom/.sbt/0.13/plugins
// [info] Loading project definition from /Users/omnomnom/projects/example-of-examples/project
// [info] Set current project to root-project (in build file:/Users/omnomnom/projects/example-of-examples/)
> projects
// [info] In file:/Users/omnomnom/projects/example-of-examples/
// [info] example1
// [info] example2
// [info] * root-project
> project example1
// [info] Set current project to example1 (in build file:/Users/omnomnom/projects/example-of-examples/)
> run
// [info] Updating {file:/Users/omnomnom/projects/example-of-examples/}root-project...
// [info] Resolving org.fusesource.jansi#jansi;1.4 ...
// [info] Done updating.
// [info] Updating {file:/Users/omnomnom/projects/example-of-examples/}example1...
// [info] Resolving org.fusesource.jansi#jansi;1.4 ...
// [info] Done updating.
// [info] Compiling 1 Scala source to /Users/omnomnom/projects/example-of-examples/target/scala-2.10/classes...
// [info] Running Main
I'm example #1
// [success] Total time: 4 s, completed Jul 4, 2014 10:17:54 PM
> project example2
// [info] Set current project to example2 (in build file:/Users/omnomnom/projects/example-of-examples/)
> run
// [info] Updating {file:/Users/omnomnom/projects/example-of-examples/}example2...
// [info] Resolving org.fusesource.jansi#jansi;1.4 ...
// [info] Done updating.
// [info] Compiling 1 Scala source to /Users/omnomnom/projects/example-of-examples/examples/example2/target/scala-2.10/classes...
// [info] Running Main
I'm example #2
// [success] Total time: 3 s, completed Jul 4, 2014 10:18:04 PM
And Build.scala, just for the case:
import sbt._
import Keys._
object Build extends Build {
override lazy val settings = super.settings ++ Seq(
scalacOptions ++= Seq("-unchecked", "-deprecation", "-Xlint"),
organization := "me.lazyval",
scalaVersion := "2.10.4",
initialCommands in console := "import me.lazyval._"
)
// altering source path, since you don't want to replicate usual src/main ... stuff
val exampleSourcePath = scalaSource in Compile := baseDirectory.value / "."
lazy val root = Project(id = "root-project", base = file("."), settings = Project.defaultSettings ++ settings)
lazy val example1 = Project(id = "example1", base = file("./examples/example1"), settings = Project.defaultSettings ++ settings ++ Seq(exampleSourcePath)) dependsOn root
// in `settings= ...` section you can set whatever dependencies you like
lazy val example2 = Project(id = "example2", base = file("./examples/example2"), settings = Project.defaultSettings ++ settings ++ Seq(exampleSourcePath)) dependsOn root
}

Related

(rootProject/*:bintrayEnsureLicenses) you must define at least one license for this project

I am trying to publish my project on bintray for the first time. I read the documentation here
https://github.com/sbt/sbt-bintray
My plugins.sbt file is
addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1")
This is my build.sbt. You can see that I have defined the license for my project
lazy val publishSettings = Seq(
licenses += ("MIT", url("http://opensource.org/licenses/MIT")),
bintrayOrganization := Some("abhishes"),
bintrayRepository := "AbhiTestRepo",
bintrayPackageLabels := Seq("foo", "bar")
)
lazy val commonSettings = Seq(
"organization" := "abhishes",
version := "1.0",
scalaVersion := "2.12.3"
)
lazy val project1 = (project in file("SubProject1")).settings(commonSettings)
.settings(publishSettings)
.settings(
name := "SubProject1"
)
lazy val project2 = (project in file("SubProject2")).settings(commonSettings)
.settings(publishSettings)
.settings(
name := "SubProject2"
)
lazy val rootProject = (project in file("."))
.settings(commonSettings)
.settings(publishSettings)
.settings(
name := "MyScalaProject"
).dependsOn(project1, project2)
when I say sbt publish I still get this error
> publish
[trace] Stack trace suppressed: run last rootProject/*:bintrayEnsureLicenses for the full output.
[error] (rootProject/*:bintrayEnsureLicenses) you must define at least one license for this project. Please choose one or more of
[error] AFL-3.0, AGPL-V3, APL-1.0, APSL-2.0, Apache-1.0, Apache-1.1, Apache-2.0, Artistic-License-2.0, Attribution, BSD, BSD New, BSD Simplified, BSL-1.0, Bouncy-Castle, CA-TOSL-1.1, CDDL-1.0, CPAL-1.0, CPL-1.0, CPOL-1.02, CUAOFFICE-1.0, Codehaus, Day, Day-Addendum, ECL2, EUDATAGRID, EUPL-1.1, Eclipse-1.0, Eiffel-2.0, Entessa-1.0, Fair, Frameworx-1.0, GPL-2.0, GPL-2.0+CE, GPL-3.0, HSQLDB, Historical, IBMPL-1.0, IPAFont-1.0, ISC, IU-Extreme-1.1.1, JA-SIG, JSON, JTidy, LGPL-2.1, LGPL-3.0, Lucent-1.02, MIT, MPL-2.0, MS-PL, MS-RL, MirOS, Motosoto-0.9.1, Mozilla-1.1, Multics, NASA-1.3, NAUMEN, NOSL-3.0, NTP, Nethack, Nokia-1.0a, OCLC-2.0, OSL-3.0, Openfont-1.1, Opengroup, PHP-3.0, PostgreSQL, Public Domain, Public Domain - SUN, PythonPL, PythonSoftFoundation, QTPL-1.0, RPL-1.5, Real-1.0, RicohPL, SUNPublic-1.0, SimPL-2.0, Sleepycat, Sybase-1.0, TMate, Unlicense, UoI-NCSA, VovidaPL-1.0, W3C, WTFPL, Xnet, ZLIB, ZPL-2.0, wxWindows
[error] Total time: 1 s, completed Aug 26, 2017 1:00:10 PM
>
EDIT: This is the output of inspect licenses
> inspect licenses
[info] Setting: scala.collection.Seq[scala.Tuple2[java.lang.String, java.net.URL]] = List()
[info] Description:
[info] Project licenses as (name, url) pairs.
[info] Provided by:
[info] */*:licenses
[info] Defined at:
[info] (sbt.Classpaths) Defaults.scala:1186
[info] Reverse dependencies:
[info] rootProject/*:bintrayEnsureBintrayPackageExists
[info] rootProject/*:projectInfo
[info] rootProject/*:bintrayEnsureLicenses
[info] Delegates:
[info] rootProject/*:licenses
[info] {.}/*:licenses
[info] */*:licenses
[info] Related:
[info] */*:licenses
>
Edit2: Here is the output of inspect rootProject/licenses
> inspect rootProject/licenses
[info] Setting: scala.collection.Seq[scala.Tuple2[java.lang.String, java.net.URL]] = List()
[info] Description:
[info] Project licenses as (name, url) pairs.
[info] Provided by:
[info] */*:licenses
[info] Defined at:
[info] (sbt.Classpaths) Defaults.scala:1186
[info] Reverse dependencies:
[info] rootProject/*:bintrayEnsureBintrayPackageExists
[info] rootProject/*:projectInfo
[info] rootProject/*:bintrayEnsureLicenses
[info] Delegates:
[info] rootProject/*:licenses
[info] {.}/*:licenses
[info] */*:licenses
[info] Related:
[info] */*:licenses
I was able to resolve the problem myself. If you look in the source code of the plugin
https://github.com/sbt/sbt-bintray/blob/7a14108bd273a8bb469ad118ccd7cce5b4042099/src/main/scala/Bintray.scala#L33
The data type of of licenses is Seq[(String, url)]
Based on this I changed my code accordingly by making the licenses as a sequence of tuples of type (String, url) and the problem got resolved.
lazy val publishSettings = Seq(
licenses ++= Seq(("MIT", url("http://opensource.org/licenses/MIT"))),
bintrayOrganization := Some("abhishes"),
bintrayRepository := "AbhiTestRepo",
bintrayPackageLabels := Seq("foo", "bar")
)

Can't add compile dependencies using sbt.AutoPlugins

I am trying to build a plugin that automatically sets up a set of scalariform preferences.
My plugin's build.sbt:
name := "my-scalariform"
organization := "com.my"
version := "1.0-SNAPSHOT"
sbtPlugin := true
scalacOptions ++= Seq("-feature", "-deprecation", "-unchecked")
addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0")
My initial plugin design:
package com.my.plugins
import com.typesafe.sbt.SbtScalariform
import com.typesafe.sbt.SbtScalariform.{
ScalariformKeys,
scalariformSettings
}
import sbt.AutoPlugin
import sbt.{ Compile, Test }
import sbt.Keys.{ compile, compileInputs }
import scalariform.formatter.preferences.{
DoubleIndentClassDeclaration,
FormattingPreferences,
IndentSpaces,
IndentWithTabs,
PreserveDanglingCloseParenthesis
}
object MyScalariformPlugin extends AutoPlugin {
override def trigger = allRequirements
lazy val formattingPreferences = {
import scalariform.formatter.preferences._
Seq(
ScalariformKeys.preferences := FormattingPreferences()
.setPreference(DoubleIndentClassDeclaration, true)
.setPreference(IndentSpaces, 2)
.setPreference(IndentWithTabs, false)
.setPreference(PreserveDanglingCloseParenthesis, true)
)
}
override lazy val projectSettings = scalariformSettings ++ formattingPreferences
}
When I added this plugin to my project I can see my scalariform settings:
> scalariform-preferences
[info] FormattingPreferences(Map(DoubleIndentClassDeclaration -> true, IndentSpaces -> 2, IndentWithTabs -> false, PreserveDanglingCloseParenthesis -> true))
However the compileInputs is missing the scalariform-format dependency:
> inspect compile:compile::compileInputs
...
[info] Defined at:
[info] (sbt.Defaults) Defaults.scala:792
[info] Dependencies:
[info] compile:compile::incCompileSetup
[info] compile:compile::streams
[info] compile:compile::dependencyClasspath
[info] compile:compile::compileOrder
[info] compile:compile::scalacOptions
[info] compile:compile::classDirectory
[info] compile:compile::javacOptions
[info] compile:compile::sourcePositionMappers
[info] compile:compile::compilers
[info] compile:compile::sources
[info] compile:compile::maxErrors
[info] Reverse dependencies:
[info] compile:compile
...
If I explicitly add the scalariform command overrides as a value in my plugin and then explicitly add it to my project I get the correct dependencies:
lazy val commandSettings = Seq(
compileInputs in (Compile, compile) <<= (compileInputs in (Compile, compile)) dependsOn (ScalariformKeys.format in Compile),
compileInputs in (Test, compile) <<= (compileInputs in (Test, compile)) dependsOn (ScalariformKeys.format in Test)
)
Dependencies:
> inspect compile:compile::compileInputs
...
[info] Defined at:
[info] (sbt.Defaults) Defaults.scala:792
[info] (com.my.plugins.MyScalariformPlugin) MyScalariformPlugin.scala:22
[info] Dependencies:
[info] compile:compile::incCompileSetup
[info] compile:compile::streams
[info] compile:compile::dependencyClasspath
[info] compile:scalariformFormat
[info] compile:compile::compileOrder
[info] compile:compile::scalacOptions
[info] compile:compile::classDirectory
[info] compile:compile::javacOptions
[info] compile:compile::sourcePositionMappers
[info] compile:compile::compilers
[info] compile:compile::sources
[info] compile:compile::maxErrors
[info] Reverse dependencies:
[info] compile:compile
...
I have tried specifying this dependency myself using an autoImport but that results in an error:
object autoImport {
lazy val commandSettings = Seq(
compileInputs in (Compile, compile) <<= (compileInputs in (Compile, compile)) dependsOn (ScalariformKeys.format in Compile),
compileInputs in (Test, compile) <<= (compileInputs in (Test, compile)) dependsOn (ScalariformKeys.format in Test)
)
}
import autoImport._
Errors:
[error] References to undefined settings:
[error]
[error] */test:compile::compileInputs from */test:compile::compileInputs ((com.my.plugins.MyScalariformPlugin.autoImport) MyScalariformPlugin.scala:24)
[error] Did you mean test:compile::compileInputs ?
[error]
[error] */test:scalariformFormat from */test:compile::compileInputs ((com.my.plugins.MyScalariformPlugin.autoImport) MyScalariformPlugin.scala:24)
[error] Did you mean test:scalariformFormat ?
[error]
[error] */compile:compile::compileInputs from */compile:compile::compileInputs ((com.my.plugins.MyScalariformPlugin.autoImport) MyScalariformPlugin.scala:23)
[error] Did you mean compile:compile::compileInputs ?
[error]
[error] */compile:scalariformFormat from */compile:compile::compileInputs ((com.my.plugins.MyScalariformPlugin.autoImport) MyScalariformPlugin.scala:23)
[error] Did you mean compile:scalariformFormat ?
[error]
Add this line to MyScalariformPlugin (don't ask me why :))
override def requires = plugins.JvmPlugin

sbt - defining custom configuration for sequential tests

Some of my tests require that they are sequentially executed. I read about custom configurations on http://www.scala-sbt.org/0.13.0/docs/Detailed-Topics/Testing.html but I am missing something because my configuration is not working properly.
Here it is:
import sbt._
import Keys._
object SchedulingBackendBuild extends Build {
lazy val SequentialTest = config("sequentialTest") extend(Test)
def sequentialTestFilter(name: String): Boolean = {
println("===seq test filter")
name endsWith "SeqSpec"
}
def unitTestFilter(name: String): Boolean = {
println("===unit test filter")
!sequentialTestFilter(name)
}
lazy val root = Project(id = "scheduling-backend",
base = file("."),
settings = Project.defaultSettings
).configs(SequentialTest)
.settings(inConfig(SequentialTest)(Defaults.testTasks): _*)
.settings(
testOptions in Test ++= Seq(Tests.Filter(unitTestFilter)),
testOptions in SequentialTest ++= Seq(Tests.Filter(sequentialTestFilter))
)
}
I want test to only execute tests that are not ending with SeqSpec and this is working, but when I try to execute sequentialTest:test no tests are executed. I added println to my filters and I can see that even if I execute sequentialTest:test I am getting
===unit test filter
===seq test filter
===seq test filter
so both filters are executed.
When I type inspect sequentialTest:testOptions I am getting
[info] Task: scala.collection.Seq[sbt.TestOption]
[info] Description:
[info] Options for running tests.
[info] Provided by:
[info] {file:/path/to/project/scheduling-backend/}scheduling-backend/sequentialTest:testOptions
[info] Defined at:
[info] /path/to/project/scheduling-backend/project/Build.scala:22
[info] Reverse dependencies:
[info] sequentialTest:testOnly::testOptions
[info] sequentialTest:testQuick::testOptions
[info] sequentialTest:test::testOptions
[info] Delegates:
[info] sequentialTest:testOptions
[info] test:testOptions
[info] runtime:testOptions
[info] compile:testOptions
[info] *:testOptions
[info] {.}/sequentialTest:testOptions
[info] {.}/test:testOptions
[info] {.}/runtime:testOptions
[info] {.}/compile:testOptions
[info] {.}/*:testOptions
[info] */sequentialTest:testOptions
[info] */test:testOptions
[info] */runtime:testOptions
[info] */compile:testOptions
[info] */*:testOptions
[info] Related:
[info] sequentialTest:testOnly::testOptions
[info] sequentialTest:testQuick::testOptions
[info] test:testOptions
[info] test:testQuick::testOptions
[info] */*:testOptions
[info] test:testOnly::testOptions
[info] test:test::testOptions
[info] sequentialTest:test::testOptions
so for me it looks ok, line 22 is testOptions in SequentialTest ++= Seq(Tests.Filter(sequentialTestFilter))
Partial answer: if I change ++= operator (appending to the sequence) to := (replacing the sequence) everything works fine (and := operators are used in the documentation). Even so I would want to know why ++= in this case is bad choice.

scala-sbt change javacOptions in task

I'm trying to create a simple ant task, which depends on compile. The problem I'm having is that I can't seem to set the javacOptions from within my task (only in the global scope).
val metamodelSettings = TaskKey[Unit]("metamodelSettings")
val metamodel = TaskKey[Unit]("metamodel")
metamodelSettings := {
print("Metamodel generation started")
javacOptions := Seq(
"-verbose",
"-g",
"-processor", "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor",
"-s", "app",
"-proc:only"
)
}
metamodel := {
print("Metamodel generation complete")
}
metamodel <<= metamodel.dependsOn(metamodelSettings, compile in Compile)
How can I change the javacOptions just for my metamodel task?
Edit:
[info] This is sbt 0.13.0
$ inspect metamodelSettings:javacOptions
[info] Task: scala.collection.Seq[java.lang.String]
[info] Description:
[info] Options for the Java compiler.
[info] Provided by:
[info] */*:javacOptions
[info] Defined at:
[info] (sbt.Defaults) Defaults.scala:209
[info] Delegates:
[info] metamodelSettings:javacOptions
[info] *:javacOptions
[info] {.}/metamodelSettings:javacOptions
[info] {.}/*:javacOptions
[info] */metamodelSettings:javacOptions
[info] */*:javacOptions
[info] Related:
[info] *:metamodelSettings::javacOptions
[info] compile:javacOptions
[info] *:metamodel::javacOptions
[info] */*:javacOptions
[info] compile:doc::javacOptions
show metamodelSettings::javacOptions is showing the expected values, but still not using them.
[test] $ show metamodelSettings::javacOptions
[info] List(-verbose, -g, -processor, org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor, -s, app, -proc:only)
[success] Total time: 0 s, completed 04-Feb-2014 11:29:56
If you want to set javacOptions only for metamodelSettings then use in to provide a scope for settings:
javacOptions in metamodelSettings := Seq(...)
You can also use different style whith inTask;
inTask(metamodelSettings) {
// your settings
}
As I was unable to override the settings, I resorted to invoking the compiler directly. This seems like a nasty solution to me, but maybe somebody will find it useful.
val metamodel = TaskKey[Unit]("metamodel")
javacOptions := Seq("-proc:none")
cleanFiles <++= baseDirectory (_ ** "*_.java" get)
metamodel := {
managedClasspath in Compile <<= (classpathTypes, update) map {
(ct, report) =>
Classpaths.managedJars(Compile, Set("jar", "zip"), report)
}
val javac = (Keys.compileInputs in Keys.compile in Compile).value.compilers.javac
val src = (file("app") / "models") ** "*.java"
val cp = (managedClasspath in Compile).value.map(x => x.data)
val out = file("app/models")
val args = Seq(
"-processor", "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor",
"-s", "app",
"-proc:only"
)
javac(sources=src.get, classpath=cp, outputDirectory=out, options=args)(ConsoleLogger())
}

Make tests in multi project sbt projects run only if tests pass in dependent projects

Assuming a multiproject SBT project with a foo-project and bar-project, such that foo-project depends on bar-project for code etc.
I would like tests in foo-project to run iff the tests in bar-project pass.
How?
You may provide explicit dependencies between projects. For example root -> A -> B
Test case on GitHub. Project definition:
val commonSettings = Seq(libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1")
lazy val a: Project = (project in file("a")) settings(commonSettings: _*) settings(
name := "a",
test in Test <<= test in Test dependsOn (test in Test in b)
)
lazy val b: Project = (project in file("b")) settings(commonSettings: _*) settings(
name := "b"
)
lazy val root: Project = (project in file(".")) settings(commonSettings: _*) settings(
name := "root",
test in Test <<= test in Test dependsOn (test in Test in a)
)
Beginning from B and complete them successful:
ezh#mobile ZZZZZZ % sbt-0.13
[info] Set current project to root (in build file:/home/ezh/ZZZZZZ/)
> root/test
[info] Compiling 1 Scala source to /home/ezh/ZZZZZZ/b/target/scala-2.10/test-classes...
[info] TestSpecB:
[info] This test
[info] - should fail
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[info] Compiling 1 Scala source to /home/ezh/ZZZZZZ/a/target/scala-2.10/test-classes...
[info] TestSpecA:
[info] This test
[info] - should succeed
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[info] Passed: Total 0, Failed 0, Errors 0, Passed 0
[info] No tests to run for root/test:test
[success] Total time: 5 s, completed 28.11.2013 16:20:12
Beginning from B but fail:
ezh#mobile ZZZZZZ % sbt-0.13
[info] Set current project to root (in build file:/home/ezh/ZZZZZZ/)
> test
[info] Compiling 1 Scala source to /home/ezh/ZZZZZZ/b/target/scala-2.10/test-classes...
[info] TestSpecB:
[info] This test
[info] - should fail *** FAILED ***
[info] 2 did not equal 3 (Test.scala:5)
[error] Failed: Total 1, Failed 1, Errors 0, Passed 0
[error] Failed tests:
[error] TestSpecB
[error] (b/test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 3 s, completed 28.11.2013 16:20:35
>
as already noted this is probably evil, however this should probably work:
import sbt._
import sbt.Keys._
object Build extends Build {
lazy val projectA = project
lazy val myTest = taskKey[Seq[Option[Tests.Output]]]("my test")
lazy val root: Project = project in file(".") settings (myTest <<= myTestTask) dependsOn projectA
def myTestTask = Def.task {
val state: State = Keys.state.value
val log: Logger = streams.value.log
val extracted = Project.extract(state)
import extracted._
def noTestsMessage(scoped: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): String =
"No tests to run for " + display(scoped)
def f(ref: ProjectReference) = for {
state Pair Value(r) <- Project.runTask(executeTests in(ref, Test), state)
_ = Tests.showResults(log, r, noTestsMessage(test in ref))
} yield r
val depsTests = currentProject.dependencies.map(_.project).map(f)
val passed = depsTests.forall(_.forall(_.overall == TestResult.Passed))
if (passed) depsTests :+ f(ThisProject) else depsTests
}
}
http://scastie.org/3319