Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document scope delegation in detail #357

Merged
merged 2 commits into from May 6, 2017
Merged

Conversation

eed3si9n
Copy link
Member

@eed3si9n eed3si9n commented May 4, 2017

Scope delegation (.value lookup) is one of more trickier aspects of build.sbt DSL. This writeup builds on top of the existing Scopes page, and breaks down the notion of scope delegation into rules and a few realistic looking examples.

Inside of `foo`'s setting body the dependency a scoped key `(bar in Test)` is declared.
However, as you see, `bar in Test` is undefined in `projA`.
sbt is able to still resolve `(bar in Test)` to another scoped key,
and initialize `foo` as `2`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it sounds obvious, but it would be a good idea to explain that this is because (if I understand correctly) the Test scope extends the Compile scope. It might also be worth explaining what it means for a scope to extend another scope.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just came here to write that

@dwijnand
Copy link
Member

dwijnand commented May 4, 2017

eed3si9n#1

organization := "com.example",
scalaVersion := "2.12.2",
version := scalaVersion.value + "_0.1.0"
)),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to suggest that inThisBuild works/makes sense only in a settings() call, while (IIRC) it works anywhere

#### Delegation in detail (.value lookup)

Now that we've covered all the details of scoping, we can explain the `.value`
lookup in detail. It's ok to skip this section at in the beginning.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove at

lazy val foo = settingKey[Int]("")
lazy val bar = settingKey[Int]("")

lazy val projA = (project in file("a"))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this in method the same as the one below?

@sjrd
Copy link

sjrd commented May 5, 2017

Very nice write-up! I knew most of this, but not as clearly as I do now.

It also made be realize I had misunderstood the difference between ThisBuild and Global. Trouble is: now I don't know anymore what's the difference between them! Could that be clearly explained, further than just "they're at different levels of precedence"?

@dwijnand
Copy link
Member

dwijnand commented May 5, 2017

@sjrd Having a good reference to link to people to explain the difference between ThisBuild and Global has been on my list for a while.

Basically sbt doesn't just have multi-project support, it also has multi-build support. It's not a widely used feature. For example if you add a dependency on another project using ProjectRef (where you specify the path of some other project) that project belongs to another build that isn't ThisBuild.

As a rule of thumb I would suggest to always use ThisBuild over Global, using Global only when:

  • defining the default value of a brand new key, or
  • modifying an sbt engine generic key, like cancelable in Global

@sjrd
Copy link

sjrd commented May 5, 2017

Huh! Wow. TIL.

Looks like this multi-build feature could be a huge can of worms. I kind of always assumed that ProjectRefs were causing sbt to always have a "separate world" for the other build. But now I understand they use the same world, and they share their Global scopes (but not their ThisBuild scopes). I'll probably have to revisit all my in Globals now ...

@dwijnand
Copy link
Member

dwijnand commented May 5, 2017

Yeah, I migrated away from my in Global usage too.

Have a look at this little session playing with how projects and project work when you add another build:

> projects
[info] In file:/d/sbt-lm/
[info] 	   lm
[info] 	 * lmRoot
[info] In file:/d/sbt-io/
[info] 	   io
[info] 	   ioRoot
> lm/compile
.. snip
> io/compile
[error] Expected ID character
[error] Not a valid command: io
[error] Expected project ID
[error] Expected configuration
[error] Expected ':' (if selecting a configuration)
[error] Expected key
[error] Not a valid key: io
[error] io/compile
[error]   ^
> {file:/d/sbt-io}io/compile
[success] Total time: 0 s, completed 05-May-2017 11:04:51
> project {file:/d/sbt-io}
[info] Set current project to IO Root (in build file:/d/sbt-io/)
> io/compile
[success] Total time: 1 s, completed 05-May-2017 11:05:51
> project {file:/d/sbt-lm}
[info] Set current project to LM Root (in build file:/d/sbt-lm/)
> lm/compile
.. snip

@metasim
Copy link
Member

metasim commented May 5, 2017

How about a variant of inspect, say inspect precedence <key>, that behaves like inspect uses <key>, but shows all shadowed resolutions?

@jvican
Copy link
Member

jvican commented May 5, 2017

Yeah, I migrated away from my in Global usage too.

Perhaps I can do a rewrite for this. I could detect if people use the project refs for external project, and if not rewrite Global into ThisBuild. Does this sound like a good idea?

@jvican
Copy link
Member

jvican commented May 5, 2017

How about a variant of inspect, say inspect precedence , that behaves like inspect uses , but shows all shadowed resolutions?

What do you mean by shadowed "resolutions"? Do you mean that you would like to see all the scopes that were eligible but were not selected because of the higher precedence of one?

Copy link
Member

@jvican jvican left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This writeup looks nice @eed3si9n. Thanks for your time on it. I have done a suggestion to improve a tricky part of the text.

are listed first, then falls back to `Global` (`*`) configuration.
- Finally, Task axis scoping lists the given task value `console::` and the one without.

Note that scope delegation feels similar to class inheritance in an object-oriented language,
Copy link
Member

@jvican jvican May 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest three things:

  1. Make this a top-level section.
  2. Avoid the term "dynamic dispatch" and describe what it really means. The example given is great, but it's difficult to parse for people because it involves understanding what dynamic dispatch. I would assume lots of people don't know what it is.
  3. Write a sbt example that shows this behaviour.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forget about the sbt example. I missed that there's already one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I adopted your suggestion 1. I might work on 2 later.

Scope delegation (`.value` lookup) is one of more trickier aspect of build.sbt DSL. Based on the material covered in Scopes page, this adds a new page called Scope Delegation which breaks down delegation into rules and exercises.
@eed3si9n
Copy link
Member Author

eed3si9n commented May 6, 2017

Thanks everyone for all the suggestions and comments.
Special credit to @dwijnand for eed3si9n#1, which I've mostly hand merged here - 9675fdb

@eed3si9n eed3si9n merged commit b251e3b into sbt:master May 6, 2017
@eed3si9n eed3si9n deleted the wip/scope_doc branch May 6, 2017 05:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants