Make one sbt config depend on another - scala

The sbt documentation shows example of how to declare dependency only between projects. But I'm positive that there are ways to declare one config be dependent on another, just like the Test configuration uses classpath from the Compile configuration.
How can I declare my own configuration so that it would depend on the Compile config generated classpath?
I take a more close look to suggested solution and bunch of question arose again. So I reopen the question
I can not deduce sbt behavior solely from the delegate relation between Test and Compile config.
The source directories turn out to be completely different for Test and Compile config despite being delegated.
> show test:unmanagedSourceDirectories
[info] List(./src/test/scala, ./src/test/java)
> show test:scalaSource
[info] ./src/test/scala
> show test:javaSource
[info] ./src/test/java
> show compile:unmanagedSourceDirectories
[info] List(./src/main/scala, ./src/main/java)
> show compile:scalaSource
[info] ./src/main/scala
> show compile:javaSource
[info] ./src/main/java
And other significant keys such as unmanagementClasspath and fullClasspath are not SettingsKeys that may be naturally stacked with delegating. They are full-fledged TaskKeys that stores complex procedure for generating class-paths behind them.
So, the question is still actual: How can I mimic exporting classes from Compile to Test config for my custom defined config? And there is closely related optional question: how this is actually done for aforementioned pre-defined configs?

You can make one configuration extend another. You do that by using extend method.
lazy val MyConfig = config("myConfig") extend(Compile)
lazy val root = project.in(file(".")).
configs(MyConfig).
settings(inConfig(MyConfig)(Defaults.compileSettings ++ Defaults.compileInputsSettings): _*)
Extend will delegate to Compile in this case, for settings which are undefined in the MyConfig configuration.
You can check it by running SBT and executing for example show myConfig:managedClasspath, the output should be exactly the same as for show compile:managedClasspath.
If you inspect your new configuration's managedClasspath you'll see that it delegates to the compile.
[info] Delegates:
[info] myConfig:managedClasspath
[info] compile:managedClasspath
[info] *:managedClasspath
[info] {.}/myConfig:managedClasspath
[info] {.}/compile:managedClasspath
[info] {.}/*:managedClasspath
[info] */myConfig:managedClasspath
[info] */compile:managedClasspath
[info] */*:managedClasspath
As I've stated above, SBT will only delegate to the setting if it's not defined in the given configuration.
For example, if you do not define any specific compiler options for myConfig the settings will be taken from compile.
> show compile:scalacOptions
[info] List()
> show myConfig:scalacOptions
[info] List()
Changing setting in compile configuration will have an effect on myConfig:
> set scalacOptions in Compile += "-Xexperimental"
> show compile:scalacOptions
[info] List(-Xexperimental)
> show myConfig:scalacOptions
[info] List(-Xexperimental)
Overriding the setting in myConfig will make SBT to use the setting defined in that configuration, while Compile will have its own value:
> set scalacOptions in MyConfig := Seq("-Xcheck-null")
> show compile:scalacOptions
[info] List(-Xexperimental)
> show myConfig:scalacOptions
[info] List(-Xcheck-null)
Note the delegation is one way. Change to the MyConfig has no influence on the Compile configuration.
You can check the documentation for details.

Related

Scala 2.12 logging compilation too verbosely in SBT 0.13.16

Since updating to Scala 2.12, sbt logs immense amounts at info level on compilation, e.g.:
[info] /home/zoltan/workspace/reboot/juniqe/libs/db/src/main/scala/com/juniqe/db/slick/CmsBlockDao.scala:548: isomorphicType is not a valid implicit value for slick.ast.TypedType[slick.lifted.Rep[Short]] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter iso: slick.lifted.Isomorphism[slick.lifted.Rep[Short],B]
[info] block <- CmsBlock if block.blockId === blockId
[info] ^
This looks like an error, but there is no compilation error, so I would like to silence these messages, because sometimes there's so many of them, my terminal overflows and I can't see the actual compilation errors.
How can I lower the logging level just for compilation (including test and integration test compilation)?
I've tried setting logLevel in Compile := Level.Warn, and logLevel in Compile := Level.Warn, but the messages are still there.
Setting logLevel in Global := Level.Warn does the trick, but I don't want to set it for the Global scope.
Note: This is a multi-project build, and I'm running compile from the root project.
Well, turns out that all these errors appeared because a colleague had enabled the SBT flag -Xlog-implicits, which, according to SBT "Show[s] more info on why some implicits are not applicable". After removing this flag, the errors were gone.
Source: https://paulbutcher.com/2010/04/26/scala-compiler-advanced-options/

How to define build-scoped settings in multi-project .sbt builds?

As of sbt 0.13, the recommended way to define all configuration is multi-project .sbt build definition. It is absolutely clear how to define settings for each project there; however, how one should define build-wide settings, for example, in ThisBuild or Global scopes?
With Scala build definitions it is clear: we have settings key coming from Build trait:
object MyBuild extends Build {
override lazy val settings = super.settings ++ Seq(
someKey := someValue
)
}
Now someKey's value will be build-scoped by default. Alternatively, it can be defined directly in .sbt file with ThisBuild scope:
someKey in ThisBuild := someValue
However, there is no natural place to define these options in multi-project .sbt builds. I suspect that they should be defined in one of the projects with ThisBuild scope:
val someProject = project
.settings(someKey in ThisBuild := someValue)
but this is very counterintuitive and not really clear: are such settings really applied globally or do they really belong to the specific project? What if I define the same key in multiple projects?
val project1 = project.settings(someKey in ThisBuild := someValue1)
val project2 = project.settings(someKey in ThisBuild := someValue2)
The documentation does not give any answers on this, unfortunately.
Note that I explicitly do not ask how to share global settings between builds or projects. I ask specifically about defining build-scoped settings.
Well, I've performed some experiments and found out the following. Here is an example build.sbt:
val testKey = settingKey[String]("A key for test")
testKey := "value in build.sbt"
testKey in ThisBuild := "build-scoped value in build.sbt"
testKey in Global := "global value in build.sbt"
lazy val root = (project in file("."))
.settings(
testKey := "value in root project",
testKey in ThisBuild := "build-scoped value in root project",
testKey in Global := "global value in root project"
)
lazy val child = project
.settings(
testKey := "value in child",
testKey in ThisBuild := "build-scoped value in child project",
testKey in Global := "global value in child project"
)
and here's what I see in SBT console:
[info] Set current project to root (in build file:/private/tmp/sbtt/)
> inspect testKey
[info] Setting: java.lang.String = value in build.sbt
[info] Description:
[info] A key for test
[info] Provided by:
[info] {file:/private/tmp/sbtt/}root/*:testKey
[info] Defined at:
[info] /private/tmp/sbtt/build.sbt:4
[info] Delegates:
[info] root/*:testKey
[info] {.}/*:testKey
[info] */*:testKey
[info] Related:
[info] */*:testKey
[info] {.}/*:testKey
[info] child/*:testKey
> testKey
[info] value in build.sbt
> root/testKey
[info] value in build.sbt
> child/testKey
[info] value in child
> {.}/testKey
[info] global value in child
> reload
[info] Loading global plugins from /Users/netvl/.sbt/0.13/plugins
[info] Set current project to root (in build file:/private/tmp/sbtt/)
> testKey
[info] value in build.sbt
> root/testKey
[info] value in build.sbt
> child/testKey
[info] value in child
> {.}/testKey
[info] build-scoped value in child project
> */testKey
[info] global value in child project
So, the conclusion is as follows.
Values defined at the topmost level in a multi-project SBT build seem to be scoped to the project build.sbt belongs to. That is, here build.sbt resides in the root directory, and so defining testKey in the file directly is the same as defining it in (project in file(".")).settings(...) clause. According to SBT output, they also take priority over those defined in .settings(...).
Keys in larger scopes like ThisBuild or Global seem to be able to be defined in any project, but "later" projects will then take priority. This is probably related to the order the projects are processed. I have expected that the order of evaluation of lazy vals would affect this, and I've tried adding .aggregate(child) to the root project to force the child project to be evaluated first, but it didn't seem to have any effect: globally scoped values from child project still have priority.
Therefore, it seems fine to define build-scoped or globally scoped values inside the root project or inside common settings list which is then used in every project. However, assigning different values to a key in build or global scope in different projects will likely lead to a weird behavior - only one of these values will take effect, and which one I don't know.
It would be very nice if all this were explained in the documentation, but at the moment I can't find anything on this behavior there.
I'd gladly accept someone else's answer which would provide more insight on how SBT works in this regard.

Setting a third-party plugin setting in sbt AutoPlugin

I have an AutoPlugin which aggregates several third-party plugins and customizes their settings for our company. For most of the plugins, this works just fine by putting them in the projectSettings:
override lazy val projectSettings = Seq( somePluginSetting := "whatever" )
I tried to do this for ScalaStyle as well:
import org.scalastyle.sbt.ScalastylePlugin.scalastyleConfigUrl
override lazy val projectSettings = Seq(
scalastyleConfigUrl := Some(url("http://git.repo/scalastyle-config.xml"))
)
This setting is never visible in projects using my plugin, instead sbt uses the plugin-provided default value:
> inspect scalastyleConfigUrl
[info] Setting: scala.Option[java.net.URL] = None
[info] Description:
[info] Scalastyle configuration file as a URL
[info] Provided by:
[info] {file:/Users/kaeser/Documents/workspace/ci-test-project/}root/*:scalastyleConfigUrl
[info] Defined at:
[info] (org.scalastyle.sbt.ScalastylePlugin) Plugin.scala:101
[info] Delegates:
[info] *:scalastyleConfigUrl
[info] {.}/*:scalastyleConfigUrl
[info] */*:scalastyleConfigUrl
[info] Related:
[info] test:scalastyleConfigUrl
When I put the setting into build.sbt directly, it works as expected.
I made a simple example sbt plugin that shows the problem: https://github.com/jastice/sbt-customsettings
What might the issue be?
This problem is most likely caused by the order in which settings are applied. If your plugin and the plugin you are depending on are both autoplugins, the requires value determines the order in which project settings are included.
Scalastyle still uses the old plugin format. It also does not follow the best practices. It should not set projectSettings as that makes it difficult to disable it in multi project builds. And it also prevents you from easily extending it from your custom plugin. I am not sure if the order in which project settings are applied is defined, or is determined by the order in which the plugins are loaded.
The easiest fix would be to make Scalastyle an AutoPlugin and depend on it using the requires value. Otherwise it's probably quite tricky to find out what determines the order.

How to force SBT to use Javadoc instead of Scaladoc?

I have Java based a Play & Akka project that is built with SBT, is there any way to tell SBT to run Javadoc instead of Scaladoc when building the project? The SBT documentation says that
“sbt will run javadoc if there are only Java sources in the project. If there are any Scala sources, sbt will run scaladoc.”
and there are no Scala files in the project but Scaladoc is still run. I assume that this is because the Play plugin is converting the view templates and conf/routes file into Scala code before compilation happens.
tl;dr Change sources in (Compile, doc) to exclude *.scala files so scaladoc doesn't kick in.
As you can read below sources in (Compile, doc) holds all sources, both managed and unmanaged in a project:
> inspect compile:doc::sources
[info] Task: scala.collection.Seq[java.io.File]
[info] Description:
[info] All sources, both managed and unmanaged.
With some filtering you can achieve your goal quite easily - use the following in build.sbt:
sources in (Compile, doc) <<= sources in (Compile, doc) map { _.filterNot(_.getName endsWith ".scala") }
A sample project's session (comments with // are mine to enhance reading):
// display current setting's value
> show compile:doc::sources
[info] ArrayBuffer(/Users/jacek/sandbox/sbt-learning-space/src/main/scala/x.scala, /Users/jacek/sandbox/sbt-learning-space/src/main/java/Hello.java)
[success] Total time: 0 s, completed Aug 24, 2014 9:31:34 PM
// generate docs - scaladoc kicks in since there are scala files
> doc
[info] Updating {file:/Users/jacek/sandbox/sbt-learning-space/}sbt-learning-space...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Main Scala API documentation to /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api...
model contains 3 documentable templates
[info] Main Scala API documentation successful.
Hello
[success] Total time: 1 s, completed Aug 24, 2014 9:32:15 PM
// change the value of sources in the current session only - use session save to retain it
> set sources in (Compile, doc) <<= sources in (Compile, doc) map { _.filterNot(_.getName endsWith ".scala") }
[info] Defining compile:doc::sources
[info] The new value will be used by compile:doc
[info] Reapplying settings...
[info] Set current project to hello (in build file:/Users/jacek/sandbox/sbt-learning-space/)
// display current setting's value - it only holds the single java file
> show compile:doc::sources
[info] ArrayBuffer(/Users/jacek/sandbox/sbt-learning-space/src/main/java/Hello.java)
[success] Total time: 0 s, completed Aug 24, 2014 9:33:23 PM
// generate docs - javadoc is properly generated
> doc
[info] Main Java API documentation to /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api...
[info] Loading source file /Users/jacek/sandbox/sbt-learning-space/src/main/java/Hello.java...
[info] Constructing Javadoc information...
[info] Standard Doclet version 1.7.0_65
[info] Building tree for all the packages and classes...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/Hello.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/package-frame.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/package-summary.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/package-tree.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/constant-values.html...
[info] Building index for all the packages and classes...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/overview-tree.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/index-all.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/deprecated-list.html...
[info] Building index for all classes...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/allclasses-frame.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/allclasses-noframe.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/index.html...
[info] Generating /Users/jacek/sandbox/sbt-learning-space/target/scala-2.10/api/help-doc.html...
[info] Main Java API documentation successful.
Hello
[success] Total time: 1 s, completed Aug 24, 2014 9:34:01 PM
You could try and integrate genjavadoc into sbt, in order to generate a javadoc.
The ScalaDoc can still be generated using the normal doc task,
whereas the JavaDoc can be generated using genjavadoc:doc.
Another approach, allowed with scala 2.12.0-RC1 (Sept. 2016) and SI 4826 is to let scaladoc run! It will process javadoc comments!
Scaladoc already processes Java sources and produces Scaladoc for them.
It just ignores the doc comments.
Many of our Scala codebases have at least some Java in them for various reasons.
It'd be nice if Scaladoc could generate full documentation for these mixed codebases, including all doc comments.

How to set multiple settings in sbt console?

I know I can use set to set a setting, but is there a way to add a bunch of settings from the sbt console?
You can simply use set as well with a Seq[Setting[_]]. set accepts either a single Setting[_] or a Seq[Setting[_]].
> show name
[info] set-seq
> show version
[info] 1.0
> set Seq(name := "abc", version := "2.0")
[info] Defining *:name, *:version
[info] The new values will be used by *:description, *:isSnapshot and 8 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] Set current project to abc (in build file:/Users/jacek/sandbox/set-seq/)
> show name
[info] abc
> show version
[info] 2.0
This is similar to build.sbt files, in which separate lines can be either Setting[_] or a Seq[Setting[_]].