Why my usage of `ThisBuild` is not working? - plugins

I wrote a simple SBT plugin, which defined a helloMessage, and the default value is None:
lazy val helloMessage = settingKey[Option[String]]("the message for hello")
override def projectSettings = Seq(
helloMessage in ThisBuild := None,
hello := println("Hello from my plugin: " + helloMessage.value)
)
Then in a testing project, I added this plugin, and defined in build.sbt:
helloMessage in ThisBuild := Some("hello from this build")
lazy val root = project in file(".")
lazy val core = project in file("core")
You may notice I used ThisBuild for helloMessage.
But the problem is, when I run ./sbt helloMessage in the testing project, it just outputs a None! Not the message Some("hello from this build") I defined!
But if I delete the two lines:
lazy val root = project in file(".")
lazy val core = project in file("core")
or just the core line, it will output the expected message Some("hello from this build").
Where is wrong? If I have to keep the multi projects, how to fix it?

Start by using buildSettings for in ThisBuild settings, as suggested in the docs.

A possible solution, change:
helloMessage in ThisBuild := None
to
helloMessage in Global := None

I believe the issue is with the initialization order. In your build.sbt file the line helloMessage in ThisBuild := Some("hello from this build") will be executed first. This will set the helloMessage setting in the global (build) scope.
Now, when any of your projects gets initialized it will run the new setting helloMessage in ThisBuild := None. This will overwrite the helloMessage setting in the global scope to be None.
One way to go around this is to remove the ThisBuild from inside your project settings. That is, you keep the ThisBuild scope in your main build.sbt file but keep local scope (helloMessage := None) inside the project settings in your plugin definition.

Related

Can I create a proto jar for scalaVersion 2.11/2.12 and use it within the same sbt under different sub-project?

I have a set of .proto files (protobuf) which I generate java from using scalapb. I also have in the same sbt 2 sub-projects, one is scalaVersion 2.11 compatible (can't upgrade it to 2.12 due to missing packages) and the other one is scala 2.12.
I created a sub-project to hold my proto, and by default 2.12 is used and my 2.12 sub-project can use it, but my 2.11 can't.
I set the crossScalaVersions to 2.11/2.12, I compiled my project with both, which passed, but then even then I was unable to get the 2.11 sub-project to find that code.
I am "wondering" if that is something supported, or if there is a track I could use a single location to hold my .proto yet have my 2 sub-projects using the same sbt file use those.
lazy val scala212 = "2.12.13"
lazy val scala211 = "2.11.12"
lazy val supportedScalaVersion = List(scala212, scala211)
ThisBuild / scalaVersion := scala212
lazy val root = (project in file("."))
.aggregate(proto, subproject1, subproject2)
.settigns(
crossScalaVersions := Nil,
publish / skip := true
)
lazy val proto = project
.settings(
crossScalaVersions := supportedScalaVersions,
name := "proto",
libraryDependencies += "com.trueaccord.scalapb" %% "scalapb-runtime" % com.trueaccord.scalapb.compiler.Version.scalapbVersion % "protobuf",
PB.targets in Compile := Seq(
scalapb.gen(grpc = false) -> (sourceManaged in Compile).value / "protobuf"
)
)
lazy val subproject1 = project
.dependsOn(proto)
lazy val subproject2 = project
.settings(
scalaVersion := scala211
)
.dependsOn(proto)
So, from the above, if I do sbt "+ proto" I can compile both versions. If I do sbt subproject1/compile it works too. Using sbt subproject2/compile fails indicating that it cannot find the 2.11:proto jar file.
Either, I would like the above somehow to work nicely, or any other trick that I could generate the code from the same proto location but within subproject1/subproject2 would be appreciated.
You could try the sbt-projectmatrix plugin:
https://github.com/sbt/sbt-projectmatrix
The idea is to have separate sbt subprojects for the different Scala versions, so you can simply reference the relevant subproject when calling dependsOn.
I think this plugin is going to end up in sbt some day as it's a much better solution in general than the current built-in stateful cross compilation support, and it's developed by Eugene Yokota, who is also an sbt developer.

SBT: How to set common scala version for multiproject

I have a multi-SBT-project in IntellJ Idea. My SBT file in the root dir looks like this:
name := "PlayRoot"
version := "1.0"
lazy val shapeless_learn = project.in(file("shapeless_learn")).dependsOn(common)
lazy val scalaz_learn = project.in(file("scalaz_learn")).dependsOn(common)
lazy val common = project.in(file("common"))
lazy val root = project.in(file(".")).aggregate(common, shapeless_learn, scalaz_learn)
scalaVersion := "2.11.7"
Then I have folders for each of the projects: ./common, ./shapeless_learn, ./scalaz_learn and each has its own build.sbt there. But for some reason I require to put in each of the subproject build.sbt files the line scalaVersion := "2.11.7".
If I forget to do that, the build fails with the message:
Error:Unresolved dependencies: common#common_2.10;0.1-SNAPSHOT: not found
See complete log in ...
For some reason if I do not specify that my scala version is 2.11.7, sbt falls back to 2.10 and tries to find common project that is built for 2.10 which I do not have.
I always keep forgetting adding scalaVersion := "2.11.7" to the newly created project and it keeps bugging me. I also would prefer sbt generating build.sbt with some default data, but instead it requires me not to forget to create it manually.
Is there any way I could set the single scala version for all projects and sub-projects in a single place? I figured that I could add a separate lazy val commonSettings = Seq { scalaVersion := "2.11.7" } in a root definition. And for each lazy val project definition I should add in the end .settings(commonSettings). This is nice, but still doesn't look beautiful enough - I should do this for every project definition. Is there a better way?
Is there any way I could create a template for a newly created project, so when I just put line lazy val newProject = ..., it will put an appropriate build.sbt file there with the contents I want?
Use
scalaVersion in ThisBuild := "2.11.7"
in the root build.sbt.

hot swap in sbt project without play-plugin

When I am using play framework, every time I've changed the code, it will take effect automatically by re-compile the code.
However, when I'm using sbt to run a project without play-plugin, it won't take effect.
I'm wondering if there were a way to make sbt project hot swap the changed code.
My build.sbt is as below:
version in ThisBuild := "1.0-SNAPSHOT"
scalaVersion in ThisBuild := "2.11.6"
lazy val `frontend` = (project in file("frontend")).
enablePlugins(PlayScala).
enablePlugins(DockerPlugin).
settings(
name := "frontend",
libraryDependencies ++= Dependencies.frontend
).dependsOn(`api`).aggregate(`api`)
lazy val `backend` = (project in file("backend")).
enablePlugins(JavaAppPackaging).
enablePlugins(DockerPlugin).
settings(
name := "backend",
libraryDependencies ++= Dependencies.backend ++ Seq(cache, ws)
).dependsOn(`api`).aggregate(`api`)
lazy val `api` = (project in file("api")).
settings(
name := "api",
libraryDependencies += ws
)
And what I have configured in intellij idea is like below as a sbt task(I can't post images by now):
"project backend" ~run
However, every time I've changed the code in backend, It won't take effect after I've call backend from the frontend.
I'm wondering how I can solve the problem. Thanks for your guys' help.
You can have sbt automatically recompiling any changes by invoking it like this:
sbt ~compile
If you use ~run, on every change the changeed classes will be compiled and project rerun again.
If it does not work, you might explain more about your project and structure.
Open two SBT window.
The one run ~compile, and another run ~run.
Hope it will be help.

How to enable SbtWeb in not-play project?

I have a single-project build, implemented in Build.scala file with the following settings:
scala
lazy val root = Project(
id = ProjectInfo.name,
base = file("."),
settings = Project.defaultSettings
++ Revolver.settings
++ Revolver.enableDebugging(port = 5050)
++ Twirl.settings
++ // more tasks omitted
++ Seq(
mainClass in Compile := Some(launcherClassName),
mainClass in Revolver.reStart := Some(launcherClassName),
javaOptions in Revolver.reStart ++= List(
"-XX:PermSize=256M",
"-XX:MaxPermSize=512M",
"-Dlogback.debug=false",
"-Dlogback.configurationFile=src/main/resources/logback.xml"
),
resolvers ++= projectResolvers,
libraryDependencies ++= Dependencies.all,
parallelExecution in Test := false,
)
)
I would like to add sbt-web managed assets processing for the project, as I want to handle coffeescript, less and so on.
I added sbt-coffeescript plugin straight to plugins.sbt file in project folder and actually got it working. So now when I run web-assets:assets I have a coffeescript sample file in /src/main/coffeescript/foo.coffee and it gets compiled to target/web/coffeescript/main/coffeescript/foo.js.
Unfortunately, nothing gets processed when I simply run compile or run task. How do I enable processing of assets during compile in development workflow?
The issue you're having is that the old-style of specifying dependencies in projects does not work with AutoPlugins (which is what the WebPlugin is).
Specifically:
val foo = Project(
id = "ok"
base = file("ok")
settings = defaultSettings // BAD!
)
i.e. if you manually place settings on the Project, you're telling sbt "I Know EVERY setting I want on this project, and I want to completely override the defaults."
The load order of sbt settings is:
AutoPlugins (Core settings now come from AutoPlugins)
Settings defined in Project instances
Settings defined in build.sbt files in the base directory of a project.
The above code is re-applying ALL of the sbt default settings from 0.13.x series, which will overwrite anything that the AutoPlugins previously enabled. This is by design, as any other mechanism wouldn't be "correct".
If you're migrating to using AutoPlugins, simply modify your build to be:
lazy val root = Project(
id = ProjectInfo.name,
base = file("."))
settings =
// NOTICE we dropped the defaultSettings
Revolver.settings
++ Revolver.enableDebugging(port = 5050)
++ Twirl.settings
++ // more tasks omitted
++ Seq(
mainClass in Compile := Some(launcherClassName),
mainClass in Revolver.reStart := Some(launcherClassName),
javaOptions in Revolver.reStart ++= List(
"-XX:PermSize=256M",
"-XX:MaxPermSize=512M",
"-Dlogback.debug=false",
"-Dlogback.configurationFile=src/main/resources/logback.xml"
),
resolvers ++= projectResolvers,
libraryDependencies ++= Dependencies.all,
parallelExecution in Test := false,
)
)
To run assets generation on compilation I did this:
settings = ... ++ Seq(
pipelineStages := Seq(rjs),
(compile in Compile) <<= compile in Compile dependsOn (stage in Assets),
// ...
)
Than when I run compile, stage command is also executed, thus running sbt-web's pipeline.
The question for me is how to make generated assets to become available as part of managed resources (I'm trying to get sbt-web working with xsbt-web-plugin and liftweb)

How to (automatically) inherit settings/tasks from an sbt plugin?

I have an sbt plugin defining tasks that I would like to have available in a Play project, or another sbt project in general. While it might not be best practice, I'd prefer to have these tasks automatically available in the Play project, so that all I need to do is add the sbt plugin via plugins.sbt. But before I can even get that far, I'm having trouble importing tasks at all.
If the plugin's build.sbt is as follows:
name := "sbt-task-test"
version := "1.0.0-SNAPSHOT"
scalaVersion := "2.10.3"
scalaBinaryVersion := "2.10"
organization := "com.example"
sbtPlugin := true
lazy val testTask = taskKey[Unit]("Run a test task.")
testTask := {
println("Running test task..")
}
How can I make testTask available in another sbt project's build.sbt or Build.scala? I've tried following this example to no avail.
My end goal is to use tasks defined like in this blog post, but I'd like to at least get some simpler examples working first. In this case, I'd be adding something like registerTask("testTask", "com.example.tasks.Test", "Run a test task") to build.sbt, however I have the same problem as above.
First, you should put your task definition in the source of the plugin, not the build.sbt. So try this:
build.sbt of the plugin (it defines only how to build the plugin):
name := "sbt-task-test"
version := "1.0.0-SNAPSHOT"
scalaVersion := "2.10.3"
// scalaBinaryVersion := "2.10" // better not to play with this
organization := "com.example"
sbtPlugin := true
src/main/scala/MyPlugin.scala (in the plugin project)
import sbt._
object MyPlugin extends Plugin {
lazy val testTask = taskKey[Unit]("Run a test task.")
override def settings = Seq(
testTask := { println("Running test task..") }
)
}
Overriding settings helps to add the definition of this task to the project scope.
Now you should build and publish the plugin (locally for example) using sbt publishLocal.
Then in the project, where you want to use this plugin:
project/plugins.sbt should contain:
addSbtPlugin("com.example" % "sbt-task-test" % "1.0.0-SNAPSHOT")
This will add testTask key and definition to the scope automatically, so that you can do in the project's directory:
sbt testTask
and it will print Running test task..