What is the difference between ThisBuild and Global scopes? - sbt

Can someone explain me the difference between writing these 2 lines:
resolvers in ThisBuild ++= appResolvers
resolvers in Global ++= appResolvers

Read Scopes for the full explanation.
I'll quote relevant parts:
There are three scope axes:
The subproject axis
The dependency configuration axis
The task axis
Scoping by project axis
If you put multiple projects in a single build, each project needs its own settings. That is, keys can be scoped according to the project.
The project axis can also be set to ThisBuild, which means the “entire build”, so a setting applies to the entire build rather than a single project. Build-level settings are often used as a fallback when a project doesn’t define a project-specific setting.
Zero scope component
Each scope axis can be filled in with an instance of the axis type (analogous to Some(_)), or the axis can be filled in with the special value Zero. So we can think of Zero as None.
Zero is a universal fallback for all scope axes, but its direct use should be reserved to sbt and plugin authors in most cases.
Global is a scope that sets Zero to all axes: Zero / Zero / Zero. In other words, Global / someKey is a shorthand for Zero / Zero / Zero / someKey.
Referring to scopes in a build definition
Global / concurrentRestrictions := Seq(
Tags.limitAll(1)
)
(Global / concurrentRestrictions implicitly converts to Zero / Zero / Zero / concurrentRestrictions, setting all axes to Zero scope component; the task and configuration are already Zero by default, so here the effect is to make the project Zero, that is, define Zero / Zero / Zero / concurrentRestrictions rather than ProjectRef(uri("file:/tmp/hello/"), "root") / Zero / Zero / concurrentRestrictions)
So as written above, Global sets all three axes to Zero whereas ThisBuild sets only the subproject axis to ThisBuild. This might make sense if you combine ThisBuild with other axis like configuration:
> set ThisBuild / Test / name := "test-name"
[info] Defining ThisBuild / Test / name
Update February 2020: As Stefan K noted in the comment scope delegation rule is a key fact that I didn't include in the above explanation.
Rule 4: Given a scope, delegate scopes are searched by substituting the subproject axis in the following order: the given subproject, ThisBuild, and then Zero.
For example, if publishing configuration refers to projFoo / version, it would look in the order of:
projFoo / version
ThisBuild / version
Global / version
If the default setting is scoped to Global, like in the case of version setting (see inspect version), using either ThisBuild / version or Global / version would be able to set a version number for all projects in the build. The choice of ThisBuild here is almost by convention.
There are times where one might want to distinguish the two scoping. First is for source dependencies. sbt has a built-in support to depend on subprojects across multiple builds. In these cases, using ThisBuild would prevent one setting from spilling over to other builds.
There are also cases where some feature specifically refers to a globally scoped setting, often to configure the behavior of a command and/or the behavior of sbt itself. Global / concurrentRestrictions is an example of that. In this case, one must use Global / concurrentRestrictions.

This would probably be used in a plugin:
resolvers in Global ++= appResolvers
Whereas this could appear in your build definition:
resolvers in ThisBuild ++= appResolvers
thereby letting you override global default offered by plugin.
Within the same build definition, using either one will most likely have equivalent effect, because they are the bottom two in the delegates list.

Related

Should the longer synonyms for local variables be used in a Makefile?

In make (I am using OpenBSD’s implementation, but I suppose the question is relevant for GNU make as well), we have the following so called local variables
# The name of the target
% The name of the archive member (for library rules)
! The name of the archive file (for library rules)
? The list of prerequisites for this target that were deemed out of date
< The name of the prerequisite from which this target is to be built (for inference rules)
* The file prefix of the file, containing only the file portion, no suffix or preceding directory components
(roughly from make(1) on OpenBSD)
These local variables have synonyms: for exampe, .IMPSRC for < or .TARGET for #. The manual in FreeBSD says these longer versions are preferred. OpenBSD’s man page mentions no such thing, but says these longer names are an extension.
Is it better to use to longer names? Which is better for compatibility? Are both POSIX?
Those variables are called automatic variables in GNU make and internal variables in the POSIX standard.
The long names for these are purely BSD make inventions, they do not exist in any other version of make (such as GNU make) and they are not mentioned in the POSIX standard for make.
It's up to you whether you want to use them, but they are completely non-portable. Of course you could always define them yourself if you wanted to implement a compatibility layer.

Pass in version number when building in sbt

I have a semi-complicated SBT process because I need to conditionally include a different config file based on what kind of build is needed. I solved this problem through sub-projects:
lazy val app = project
.in(file("."))
.enablePlugins(JavaAppPackaging)
.settings(
commonSettings // Seq() of settings to be shared between projects
,sourceGenerators in Compile += (avroScalaGenerateSpecific in Compile).taskValue
,(avroSpecificSourceDirectory in Compile) := new java.io.File("src/main/resources/com/coolCompany/folderName/avro")
)
lazy val localPackage = project
.in(file("build/local"))
.enablePlugins(JavaAppPackaging)
.settings(
organization := "com.coolCompany",
version := "0.1.0-SNAPSHOT",
scalaVersion := "2.11.8",
name := "my-neat-project",
scalacOptions := compilerOptions, //Seq() of compiler flags
sourceDirectory in Compile := (sourceDirectory in (app, Compile)).value,
mappings in Universal += {
((sourceDirectory in Compile).value / "../../conf/local/config.properties") -> "lib/config.properties"
}
)
.dependsOn(app)
val buildNumber = inputKey[String]("The version number of the artifact.")
lazy val deployedPackage = project
.in(file("build/deployed"))
.enablePlugins(JavaAppPackaging)
.settings(
organization := "com.coolCompany",
buildNumber := {
val args : Seq[String] = spaceDelimited("<arg>").parsed
println(s"Input version number is ${args.head}")
args.head
},
version := buildNumber.inputTaskValue + "-SNAPSHOT", //"0.1.0-SNAPSHOT",
scalaVersion := "2.11.8",
name := "my-cool-project",
scalacOptions := compilerOptions,
sourceDirectory in Compile := (sourceDirectory in (app, Compile)).value,
mappings in Universal += {
((sourceDirectory in Compile).value / "../../conf/deployed/config.properties") -> "lib/config.properties"
}
)
.dependsOn(app)
Now I need to allow the version number to be passed in by a build tool when building. You can see what I've attempted to do already: I created an inputKey task called buildNumber, then tried to access that in the version := definition. I can run the buildNumber task itself just fine:
$ sbt 'deployedPackage/buildNumber 0.1.2'
Input version number is 0.1.2
So I can at least verify that my input task works as expected. The issue is that I can't figure out how I actually get to that input value when running the actual packageBin step that I want.
I've tried the following:
$ sbt 'deployedPackage/universal:packageBin 0.1.2'
[error] Expected key
[error] Expected '::'
[error] Expected end of input.
[error] deployedPackage/universal:packageBin 0.1.2
So it clearly doesn't understand what to do with the version number. I've tried a bunch of different input variations, such as [...]packageBin buildNumber::0.1.2, [...]packageBin -- buildNumber 0.1.2, or [...]packageBin -- 0.1.2, and all of them give that error or something similar indicating it doesn't understand what I'm trying to pass in.
Now, ultimately, these errors make sense. buildNumber, the task, is what knows what to do with the command line values, but packageBin does not. How do I set up this task or these set of tasks to allow the version number to be passed in?
I have seen this question but the answers link to an sbt plugin that seems to do about 100 more things than I want it to do, including quite a few that I would need to find a way to explicitly disable. I only want the version number to be able to be passed in & used in the artifact.
Edit/Update: I resolved this issue by switching back to Maven.
I think you're a bit confused about the way sbt settings work. Settings are set when sbt loads and then cannot be changed until you reload the session. So whatever you set version to, it will be fixed, it cannot be dynamic and depend on user input or a task (which on the contrast is dynamic and is evaluated every time you call it).
This is true. However you can still compute a SettingKey value in the first place. We use environment variables to set our build version.
version := "1." + sys.env.getOrElse("BUILD_NUMBER", "0-SNAPSHOT")
Explanation
1. is the major version. Increment this manually as you like
BUILD_NUMBER is an environment variable that is typically set by a CI, e.g. Jenkins. If not set, use 0-SNAPSHOT as a version suffix.
We use this in our company for our continuous deployment pipeline.
Hope that helps,
Muki
I think you're a bit confused about the way sbt settings work. Settings are set when sbt loads and then cannot be changed until you reload the session. So whatever you set version to, it will be fixed, it cannot be dynamic and depend on user input or a task (which on the contrast is dynamic and is evaluated every time you call it).
You have an input task buildNumber which depends on user input and is evaluated every time you call it:
> show buildNumber 123
Input version number is 123
[info] 123
[success] Total time: 0 s, completed Dec 24, 2017 3:41:43 PM
> show buildNumber 456
Input version number is 456
[info] 456
[success] Total time: 0 s, completed Dec 24, 2017 3:41:45 PM
It returns whatever you give it and doesn't do anything (besides println). More importantly, it doesn't affect version setting anyhow (and couldn't even in theory).
When you use buildNumber.inputTaskValue, you refer to the input task itself, not its value (which is unknown because it depends on the user input). You can see it by checking the version setting value:
> show version
[info] sbt.InputTask#6c996907-SNAPSHOT
So it's definitely not what you want.
I suggest you to review your approach in general and read a bit more sbt docs, for example about Task graph (and the whole Getting started chapter).
If you still really need to set version on sbt load according to your input, you can do it with a command. It would look like this:
commands += Command.single("pkg") { (state0, buildNumber) =>
val state1 = Project.extract(state0).append(Seq(version := buildNumber + "-SNAPSHOT"), state0)
val (state2, result) = Project.extract(state1).runTask(packageBin in (deployedPackage, universal), state1)
state2
}
But you should be really careful dealing with the state manually. Again, I recommend you to review your approach and change it to a more sbt-idiomatic one.
I suggest you just set the version setting only for the project deployedPackage just before you call the task needing the version. This is the simplest way of setting the version, and as settings keys have scopes, this should work as intended.
I used something like this for a big multi module project, where one of the projects had a separate versioning history than the other projects.

What does "provided->default" mean in an sbt build file?

An example of this comes from a sample github project:
libraryDependencies ++= Seq(
"javax.servlet" % "servlet-api" % "2.5" % "provided->default",
...
}
I'm only vaguely clear on what the 'fourth column' in these configurations mean, but this is the first time I've seen either provided or provided->default, and it's unclear how I can go about finding what should be expected here in the documentation. Can anyone help explain this construct?
It means that your provided configuration depends on the default configuration of "java.servlet" % "servlet-api" % "2.5".
Maven scopes describe what these configurations or scopes mean.
For instance, if you're using a library to write your tests, you've probably come across something like "org.scalacheck" %% "scalacheck" % "1.13.2" % "test" or similar. Here, the second part of the configuration is omitted and refers to the default configuration (usually compile). Equivalently, you could write "org.scalacheck" %% "scalacheck" % "1.13.2" % "test->compile". It means that your test configuration depends on the default configuration of ScalaCheck: your tests need ScalaCheck on the class path to compile and run.
You may find more details in the Ivy documentation.

Define configuration with compiler plugin

I'd like to create a compile configuration which is the same as the default one but adds a compiler plugin. In my particular case, I want to have a "dev" configuration but with the linter plugin (https://github.com/HairyFotr/linter) because it slows down compile times and there's no need to run it in production or continuous integration.
Now this is what I tried:
lazy val Dev = config("dev") extend Compile
lazy val root = (project in file(".")).configs(Dev).settings(
inConfig(Dev)(addCompilerPlugin("org.psywerx.hairyfotr" %% "linter" % "0.1.12")): _*)
and it should work, since when I inspect dev:libraryDependencies, it's what I expect it to be- it has org.psywerx.hairyfotr:linter:0.1.12:plugin->default(compile). Normally if I add the library with a "plugin" scope, it does work for the default settings:
libraryDependencies += ("org.psywerx.hairyfotr" %% "linter" % "0.1.12" % "plugin"
It just does not work if I add this under a different configuration, so there must be something else going on here.
This solves the problem, but not exactly in a way was asked. Here's the full build.sbt:
libraryDependencies ++= Seq(
"org.psywerx.hairyfotr" %% "linter" % "0.1.14" % "test")
val linter = Command.command("linter")(state => {
val linterJar = for {
(newState, result) <- Project.runTask(fullClasspath in Test, state)
cp <- result.toEither.right.toOption
linter <- cp.find(
_.get(moduleID.key).exists(mId =>
mId.organization == "org.psywerx.hairyfotr" &&
mId.name == "linter_2.11"))
} yield linter.data.absolutePath
val res = Project.runTask(scalacOptions, state)
res match {
case Some((newState, result)) =>
result.toEither.right.foreach { defaultScalacOptions =>
Project.runTask(compile in Test,
Project.extract(state).append(
scalacOptions := defaultScalacOptions ++ linterJar.map(p => Seq(s"-Xplugin:$p")).getOrElse(Seq.empty),
newState))
}
case None => sys.error("Couldn't get defaultScalacOptions")
}
state
})
lazy val root = (project in file(".")).configs(Test).settings(commands ++= Seq(linter))
The fact that you return unmodified state means you don't change the project settings. So if you run sbt linter, you should get your project compiled with the additional scalacOptions, but if you run compile in the same sbt session, it will not use those additional settings.
The tricky thing here is that scalacOptions is actually a TaskKey, not a SettingKey. I don't know why is that, but to get its value, you have to run that task. One reason might be that in sbt you cannot make setting depending on a task, but you can make a task depending on a task. In other words, scalacOptions can depend on the other task value, and maybe internally it does, I haven't checked. If current answer will work for you, I can try and think about more elegant way of achieving the same result.
EDIT: modified the code to specify the scalacOptions for the linter plugin proper. Please note the plugin has to be a managed dependency, not just a downloaded jar, for this solution to work. If you want to have it unmanaged, there's a way, but I won't go into it for now. Additionally, I've taken a freedom of making it also work for testing code, for illustration purposes.
Looking at Defaults.scala in the source, it seems like the compile command is always taking the options from the compile scope. So if I'm correct, you can have only one set of compilation options!
This seems to be confirmed by the fact that scalacOptions behaves the same way, and this is also why I don't see a non-hacky answer for these similar questions:
Different scalac options for different scopes or tasks?
Different compile options for tests and release in SBT?
I'd be happy to be proven wrong.
EDIT: FWIW, one might not be able to define another scalac options profile in the same project, but you could do so in a "different" project:
lazy val dev = (project in file(".")).
settings(target := baseDirectory.value / "target" / "dev").
settings(addCompilerPlugin("org.psywerx.hairyfotr" %% "linter" % "0.1.12"): _*)
This has the disadvantage that it has a separate output directory, so it will take more space and, more importantly, will not get incremental compiles between the two projects. However, after spending some time thinking about it, this may be by design. After all, even though linters don't, some scalac compilation options could conceivably change the output. This would make it meaningless to try to keep the metadata for incremental compilation from one set of scalac options to another. Thus different scalac options would indeed require different target directories.

How to define custom attributes for nginx cookbook?

For my current problem I'm using the nginxcookbook. What I want to do is to install it from source (no problem to do that using nginx::source) and prefix it to /opt/nginx/<version>/, but it will always be prefixed (by default) to /opt/nginx-<version>/.
This is my ./attributes/default.rb:
node.override['nginx']['source']['prefix'] = "/opt/nginx/#{node['nginx']['source']['version']}"
#also tested
override['nginx']['source']['prefix'] = "/opt/nginx/#{node['nginx']['source']['version']}"
#and also
default['nginx']['source']['prefix'] = "/opt/nginx/#{node['nginx']['source']['version']}"
What am I not understanding?
Thanks
An additional thing would be:
- How may I prefix it like above and create a sym-link to /opt/nginx/current (and do that even after update)?
So, this is a shortcoming in the cookbook. Many variables are being built based on the value of that one attribute, and if you haven't set your override value FIRST, then the values in the cookbook will still win for the derived attributes.
If you set an override on the node or environment for nginx['source']['prefix'] that should work, but it's not ideal. Alternately, also override the derived values.
https://github.com/miketheman/nginx/blob/master/attributes/source.rb
default['nginx']['source']['sbin_path'] = "#{node['nginx']['source']['prefix']}/sbin/nginx"
default['nginx']['source']['default_configure_flags'] = %W(
--prefix=#{node['nginx']['source']['prefix']} # <= this one needs overridden
--conf-path=#{node['nginx']['dir']}/nginx.conf
--sbin-path=#{node['nginx']['source']['sbin_path']}
)
There may be others, but those are the two I know of, and that stand out.

Resources