What is the difference between ThisBuild and Global scopes?

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.


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:

  1. projFoo / version
  2. ThisBuild / version
  3. 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.

Tags:

Sbt