mirror of https://github.com/sbt/sbt.git
336 lines
14 KiB
Markdown
336 lines
14 KiB
Markdown
|
|
[MavenScopes]:
|
||
|
|
http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope
|
||
|
|
"Maven scopes"
|
||
|
|
|
||
|
|
# Scopes
|
||
|
|
|
||
|
|
[[Previous|Getting Started Basic Def]] _Getting Started Guide page
|
||
|
|
7 of 14._ [[Next|Getting Started More About Settings]]
|
||
|
|
|
||
|
|
This page describes scopes. It assumes you've read and understood the
|
||
|
|
previous page, [[.sbt build definition|Getting Started Basic Def]].
|
||
|
|
|
||
|
|
## The whole story about keys
|
||
|
|
|
||
|
|
[[Previously|Getting Started Basic Def]] we pretended that a key like `name`
|
||
|
|
corresponded to one entry in sbt's map of key-value pairs. This was a
|
||
|
|
simplification.
|
||
|
|
|
||
|
|
In truth, each key can have an associated value in more than one context,
|
||
|
|
called a "scope."
|
||
|
|
|
||
|
|
Some concrete examples:
|
||
|
|
|
||
|
|
- if you have multiple projects in your build definition, a key can have
|
||
|
|
a different value in each project.
|
||
|
|
- the `compile` key may have a different value for your main sources and
|
||
|
|
your test sources, if you want to compile them differently.
|
||
|
|
- the `package-options` key (which contains options for creating jar
|
||
|
|
packages) may have different values when packaging class files
|
||
|
|
(`package-bin`) or packaging source code (`package-src`).
|
||
|
|
|
||
|
|
_There is no single value for a given key name_, because the value may differ
|
||
|
|
according to scope.
|
||
|
|
|
||
|
|
However, there is a single value for a given _scoped_ key.
|
||
|
|
|
||
|
|
If you think about sbt processing a list of settings to generate a key-value
|
||
|
|
map describing the project, as [[discussed earlier|Getting Started Basic Def]],
|
||
|
|
the keys in that key-value map are _scoped_ keys. Each setting defined in
|
||
|
|
the build definition (for example in `build.sbt`) applies to a scoped key as
|
||
|
|
well.
|
||
|
|
|
||
|
|
Often the scope is implied or has a default, but if the defaults are wrong,
|
||
|
|
you'll need to mention the desired scope in `build.sbt`.
|
||
|
|
|
||
|
|
## Scope axes
|
||
|
|
|
||
|
|
A _scope axis_ is a type, where each instance of the type can define its own
|
||
|
|
scope (that is, each instance can have its own unique values for keys).
|
||
|
|
|
||
|
|
There are three scope axes:
|
||
|
|
|
||
|
|
- Projects
|
||
|
|
- Configurations
|
||
|
|
- Tasks
|
||
|
|
|
||
|
|
### Scoping by project axis
|
||
|
|
|
||
|
|
If you [[put multiple projects in a single build|Getting Started Multi-Project]], each
|
||
|
|
project needs its own settings. That is, keys can be scoped according to the
|
||
|
|
project.
|
||
|
|
|
||
|
|
The project axis can also be set to "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.
|
||
|
|
|
||
|
|
### Scoping by configuration axis
|
||
|
|
|
||
|
|
A _configuration_ defines a flavor of build, potentially with its own
|
||
|
|
classpath, sources, generated packages, etc. The configuration concept comes
|
||
|
|
from Ivy, which sbt uses for [[managed dependencies|Getting Started Library Dependencies]], and
|
||
|
|
from [MavenScopes].
|
||
|
|
|
||
|
|
Some configurations you'll see in sbt:
|
||
|
|
|
||
|
|
- `Compile` which defines the main build (`src/main/scala`).
|
||
|
|
- `Test` which defines how to build tests (`src/test/scala`).
|
||
|
|
- `Runtime` which defines the classpath for the `run` task.
|
||
|
|
|
||
|
|
By default, all the keys associated with compiling, packaging, and running
|
||
|
|
are scoped to a configuration and therefore may work differently in each
|
||
|
|
configuration. The most obvious examples are the task keys `compile`,
|
||
|
|
`package`, and `run`; but all the keys which _affect_ those keys (such as
|
||
|
|
`source-directories` or `scalac-options` or `full-classpath`) are also
|
||
|
|
scoped to the configuration.
|
||
|
|
|
||
|
|
### Scoping by task axis
|
||
|
|
|
||
|
|
Settings can affect how a task works. For example, the `package-src` task is
|
||
|
|
affected by the `package-options` setting.
|
||
|
|
|
||
|
|
To support this, a task key (such as `package-src`) can be a scope for
|
||
|
|
another key (such as `package-options`).
|
||
|
|
|
||
|
|
The various tasks that build a package (`package-src`, `package-bin`,
|
||
|
|
`package-doc`) can share keys related to packaging, such as `artifact-name`
|
||
|
|
and `package-options`. Those keys can have distinct values for each
|
||
|
|
packaging task.
|
||
|
|
|
||
|
|
## Global scope
|
||
|
|
|
||
|
|
Each scope axis can be filled in with an instance of the axis type (for
|
||
|
|
example the task axis can be filled in with a task), or the axis can be
|
||
|
|
filled in with the special value `Global`.
|
||
|
|
|
||
|
|
`Global` means what you would expect: the setting's value applies to all
|
||
|
|
instances of that axis. For example if the task axis is `Global`, then the
|
||
|
|
setting would apply to all tasks.
|
||
|
|
|
||
|
|
## Delegation
|
||
|
|
|
||
|
|
A scoped key may be undefined, if it has no value associated with it in its scope.
|
||
|
|
|
||
|
|
For each scope, sbt has a fallback search path made up of other scopes.
|
||
|
|
Typically, if a key has no associated value in a more-specific scope, sbt
|
||
|
|
will try to get a value from a more general scope, such as the `Global`
|
||
|
|
scope or the entire-build scope.
|
||
|
|
|
||
|
|
This feature allows you to set a value once in a more general scope,
|
||
|
|
allowing multiple more-specific scopes to inherit the value.
|
||
|
|
|
||
|
|
You can see the fallback search path or "delegates" for a key using the
|
||
|
|
`inspect` command, as described below. Read on.
|
||
|
|
|
||
|
|
## Referring to scoped keys when running sbt
|
||
|
|
|
||
|
|
On the command line and in interactive mode, sbt displays (and parses)
|
||
|
|
scoped keys like this:
|
||
|
|
|
||
|
|
```text
|
||
|
|
{<build-uri>}<project-id>/config:intask::key
|
||
|
|
```
|
||
|
|
|
||
|
|
- `{<build-uri>}<project-id>` identifies the project axis. The `<project-id>`
|
||
|
|
part will be missing if the project axis has "entire build" scope.
|
||
|
|
- `config` identifies the configuration axis.
|
||
|
|
- `intask` identifies the task axis.
|
||
|
|
- `key` identifies the key being scoped.
|
||
|
|
|
||
|
|
`*` can appear for each axis, referring to the `Global` scope.
|
||
|
|
|
||
|
|
If you omit part of the scoped key, it will be inferred as follows:
|
||
|
|
|
||
|
|
- the current project will be used if you omit the project.
|
||
|
|
- a key-dependent configuration will be auto-detected if you omit the
|
||
|
|
configuration or task.
|
||
|
|
|
||
|
|
For more details, see [[Inspecting Settings]].
|
||
|
|
|
||
|
|
### Examples of scoped key notation
|
||
|
|
|
||
|
|
- `full-classpath`: just a key, so the default scopes are used: current project, a key-dependent configuration, and global task scope.
|
||
|
|
- `test:full-classpath`: specifies the configuration, so this is `full-classpath` in the `test` configuration, with defaults for the other two scope axes.
|
||
|
|
- `*:full-classpath`: specifies `Global` for the configuration, rather than the default configuration.
|
||
|
|
- `doc::full-classpath`: specifies the `full-classpath` key scoped to the `doc` task, with the defaults for the project and configuration axes.
|
||
|
|
- `{file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath` specifies a project, `{file:/home/hp/checkout/hello/}default-aea33a`, where the project is identified with the build `{file:/home/hp/checkout/hello/}` and then a project id inside that build `default-aea33a`. Also specifies configuration `test`, but leaves the default task axis.
|
||
|
|
- `{file:/home/hp/checkout/hello/}/test:full-classpath` sets the project axis to "entire build" where the build is `{file:/home/hp/checkout/hello/}`
|
||
|
|
- `{.}/test:full-classpath` sets the project axis to "entire build" where the build is `{.}`. `{.}` can be written `ThisBuild` in Scala code.
|
||
|
|
- `{file:/home/hp/checkout/hello/}/compile:doc::full-classpath` sets all three scope axes.
|
||
|
|
|
||
|
|
## Inspecting scopes
|
||
|
|
|
||
|
|
In sbt's interactive mode, you can use the `inspect` command to understand
|
||
|
|
keys and their scopes. Try `inspect test:full-classpath`:
|
||
|
|
|
||
|
|
```text
|
||
|
|
$ sbt
|
||
|
|
> inspect test:full-classpath
|
||
|
|
[info] Task: scala.collection.Seq[sbt.Attributed[java.io.File]]
|
||
|
|
[info] Description:
|
||
|
|
[info] The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies.
|
||
|
|
[info] Provided by:
|
||
|
|
[info] {file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath
|
||
|
|
[info] Dependencies:
|
||
|
|
[info] test:exported-products
|
||
|
|
[info] test:dependency-classpath
|
||
|
|
[info] Reverse dependencies:
|
||
|
|
[info] test:run-main
|
||
|
|
[info] test:run
|
||
|
|
[info] test:test-loader
|
||
|
|
[info] test:console
|
||
|
|
[info] Delegates:
|
||
|
|
[info] test:full-classpath
|
||
|
|
[info] runtime:full-classpath
|
||
|
|
[info] compile:full-classpath
|
||
|
|
[info] *:full-classpath
|
||
|
|
[info] {.}/test:full-classpath
|
||
|
|
[info] {.}/runtime:full-classpath
|
||
|
|
[info] {.}/compile:full-classpath
|
||
|
|
[info] {.}/*:full-classpath
|
||
|
|
[info] */test:full-classpath
|
||
|
|
[info] */runtime:full-classpath
|
||
|
|
[info] */compile:full-classpath
|
||
|
|
[info] */*:full-classpath
|
||
|
|
[info] Related:
|
||
|
|
[info] compile:full-classpath
|
||
|
|
[info] compile:full-classpath(for doc)
|
||
|
|
[info] test:full-classpath(for doc)
|
||
|
|
[info] runtime:full-classpath
|
||
|
|
```
|
||
|
|
|
||
|
|
On the first line, you can see this is a task (as opposed to a setting, as
|
||
|
|
explained in [[.sbt build definition|Getting Started Basic Def]]). The value resulting from the task
|
||
|
|
will have type `scala.collection.Seq[sbt.Attributed[java.io.File]]`.
|
||
|
|
|
||
|
|
"Provided by" points you to the scoped key that defines the value, in this
|
||
|
|
case `{file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath` (which
|
||
|
|
is the `full-classpath` key scoped to the `test` configuration and the
|
||
|
|
`{file:/home/hp/checkout/hello/}default-aea33a` project).
|
||
|
|
|
||
|
|
"Dependencies" may not make sense yet; stay tuned for the
|
||
|
|
[[next page|Getting Started More About Settings]].
|
||
|
|
|
||
|
|
You can also see the delegates; if the value were not defined, sbt would
|
||
|
|
search through:
|
||
|
|
|
||
|
|
- two other configurations (`runtime:full-classpath`,
|
||
|
|
`compile:full-classpath`). In these scoped keys, the project is unspecified meaning "current
|
||
|
|
project" and the task is unspecified meaning `Global`
|
||
|
|
- configuration set to `Global` (`*:full-classpath`), since project is
|
||
|
|
still unspecified it's "current project" and task is still unspecified so
|
||
|
|
`Global`
|
||
|
|
- project set to `{.}` or `ThisBuild` (meaning the entire build, no
|
||
|
|
specific project)
|
||
|
|
- project axis set to `Global` (`*/test:full-classpath`) (remember,
|
||
|
|
an unspecified project means current, so searching `Global` here is new;
|
||
|
|
i.e. `*` and "no project shown" are different for the project axis;
|
||
|
|
i.e. `*/test:full-classpath` is not the same as `test:full-classpath`)
|
||
|
|
- both project and configuration set to `Global` (`*/*:full-classpath`)
|
||
|
|
(remember that unspecified task means `Global` already, so
|
||
|
|
`*/*:full-classpath` uses `Global` for all three axes)
|
||
|
|
|
||
|
|
Try `inspect full-classpath` (as opposed to the above example, `inspect
|
||
|
|
test:full-classpath`) to get a sense of the difference. Because the
|
||
|
|
configuration is omitted, it is autodetected as `compile`.
|
||
|
|
`inspect compile:full-classpath` should therefore look the same as
|
||
|
|
`inspect full-classpath`.
|
||
|
|
|
||
|
|
Try `inspect *:full-classpath` for another contrast. `full-classpath`
|
||
|
|
is not defined in the `Global` configuration by default.
|
||
|
|
|
||
|
|
Again, for more details, see [[Inspecting Settings]].
|
||
|
|
|
||
|
|
## Referring to scopes in a build definition
|
||
|
|
|
||
|
|
If you create a setting in `build.sbt` with a bare key, it will be scoped to
|
||
|
|
the current project, configuration `Global` and task `Global`:
|
||
|
|
|
||
|
|
```scala
|
||
|
|
name := "hello"
|
||
|
|
```
|
||
|
|
|
||
|
|
Run sbt and `inspect name` to see that it's provided by
|
||
|
|
`{file:/home/hp/checkout/hello/}default-aea33a/*:name`, that is, the project is
|
||
|
|
`{file:/home/hp/checkout/hello/}default-aea33a`, the configuration is `*`
|
||
|
|
(meaning global), and the task is not shown (which also means global).
|
||
|
|
|
||
|
|
`build.sbt` always defines settings for a single project, so the "current
|
||
|
|
project" is the project you're defining in that particular `build.sbt`.
|
||
|
|
(For [[multi-project builds|Getting Started Multi-Project]], each project has its own
|
||
|
|
`build.sbt`.)
|
||
|
|
|
||
|
|
Keys have an overloaded method called `in` used to set the scope. The
|
||
|
|
argument to `in` can be an instance of any of the scope axes. So for
|
||
|
|
example, though there's no real reason to do this,
|
||
|
|
you could set the name scoped to the `Compile` configuration:
|
||
|
|
|
||
|
|
```scala
|
||
|
|
name in Compile := "hello"
|
||
|
|
```
|
||
|
|
|
||
|
|
or you could set the name scoped to the `package-bin` task (pointless! just
|
||
|
|
an example):
|
||
|
|
|
||
|
|
```scala
|
||
|
|
name in packageBin := "hello"
|
||
|
|
```
|
||
|
|
|
||
|
|
or you could set the name with multiple scope axes, for example in the
|
||
|
|
`packageBin` task in the `Compile` configuration:
|
||
|
|
|
||
|
|
```scala
|
||
|
|
name in (Compile, packageBin) := "hello"
|
||
|
|
```
|
||
|
|
|
||
|
|
or you could use `Global` for all axes:
|
||
|
|
|
||
|
|
```scala
|
||
|
|
name in Global := "hello"
|
||
|
|
```
|
||
|
|
|
||
|
|
(`name in Global` implicitly converts the scope axis `Global` to a scope
|
||
|
|
with all axes set to `Global`; the task and configuration are already
|
||
|
|
`Global` by default, so here the effect is to make the project `Global`,
|
||
|
|
that is, define `*/*:name` rather than `{file:/home/hp/checkout/hello/}default-aea33a/*:name`)
|
||
|
|
|
||
|
|
If you aren't used to Scala, a reminder: it's important to understand that
|
||
|
|
`in` and `:=` are just methods, not magic. Scala lets you write them in a
|
||
|
|
nicer way, but you could also use the Java style:
|
||
|
|
|
||
|
|
```scala
|
||
|
|
name.in(Compile).:=("hello")
|
||
|
|
```
|
||
|
|
|
||
|
|
There's no reason to use this ugly syntax, but it illustrates that these are
|
||
|
|
in fact methods.
|
||
|
|
|
||
|
|
## When to specify a scope
|
||
|
|
|
||
|
|
You need to specify the scope if the key in question is normally scoped.
|
||
|
|
For example, the `compile` task, by default, is scoped to `Compile` and
|
||
|
|
`Test` configurations, and does not exist outside of those scopes.
|
||
|
|
|
||
|
|
To change the value associated with the `compile` key, you need to write
|
||
|
|
`compile in Compile` or `compile in Test`. Using plain `compile` would
|
||
|
|
define a new compile task scoped to the current project, rather than
|
||
|
|
overriding the standard compile tasks which are scoped to a configuration.
|
||
|
|
|
||
|
|
If you get an error like _"Reference to undefined setting"_, often
|
||
|
|
you've failed to specify a scope, or you've specified the wrong
|
||
|
|
scope. The key you're using may be defined in some other
|
||
|
|
scope. sbt will try to suggest what you meant as part of the error
|
||
|
|
message; look for "Did you mean compile:compile?"
|
||
|
|
|
||
|
|
One way to think of it is that a name is only _part_ of a key. In reality,
|
||
|
|
all keys consist of both a name, and a scope (where the scope has three
|
||
|
|
axes). The entire expression `packageOptions in (Compile, packageBin)` is a
|
||
|
|
key name, in other words. Simply `packageOptions` is also a key name, but a
|
||
|
|
different one (for keys with no `in`, a scope is implicitly assumed: current
|
||
|
|
project, global config, global task).
|
||
|
|
|
||
|
|
## Next
|
||
|
|
|
||
|
|
Now that you understand scopes, you can [[learn more about settings|Getting Started More About Settings]].
|