Reputation: 1469
I've read a reference to understand how the scope delegation works in SBT.
From the above reference page, I excerpted an Exercise D.
ThisBuild / scalacOptions += "-Ywarn-unused-import"
lazy val projD = (project in file("d"))
.settings(
test := {
println((Compile / console / scalacOptions).value)
},
console / scalacOptions -= "-Ywarn-unused-import",
Compile / scalacOptions := scalacOptions.value // added by sbt
)
What would you see if you ran projD/test?
List()
List (-Ywarn-unused-import)
something else?
And their reasoning was
The answer is List(-Ywarn-unused-import). Rule 2 finds projD / Compile / Zero, Rule 3 finds projD / Zero / console, and Rule 4 finds ThisBuild / Zero / Zero. Rule 1 selects projD / Compile / Zero because it has the subproject axis projD, and the configuration axis has higher precedence over the task axis.
So far so good, I could understand the reason why Compile/console/sclacOptions will be scoped to projD/Compile/zero/scalacOptions. It's because the configuration axis of projD/Compile/zero is more concrete than the projD/zero/console compared to the key that we want to know (Compile/console/scalacOptions).
Next, Compile / scalacOptions refers to scalacOptions.value, we next need to find a delegate for projD / Zero / Zero. Rule 4 finds ThisBuild / Zero / Zero and thus it resolves to List(-Ywarn-unused-import).
Here, I couldn't understand why ThisBuild / Zero/ Zero wins the projD/ Compile/ Zero. Because the scope of the key that we want to find is projD/ zero/ zero, so projD/Compile/zero has more specific value compared to the Thisbuild [Following the Rule 1 in the reference].
I think the reason is Compile/ scalacOptions has the value of scalacOptions.value, which generates recursive definition. Then, we could have used the projD/ Zero/ console.
Upvotes: 2
Views: 194
Reputation: 6460
I think you're just a bit confused about the references to the difference scopes in the second part of the explanation. Let's write down all scopes in the example explicitly:
ThisBuild / Zero / Zero / scalacOptions += "-Ywarn-unused-import"
lazy val projD = (project in file("d"))
.settings(
test := { println((Compile / console / scalacOptions).value) }
)
projD / Zero / console / scalacOptions -= "-Ywarn-unused-import"
projD / Compile / Zero / scalacOptions := (projD / Zero / Zero / scalacOptions).value
I had to take out these two settings, to be able to refer to projD
, but otherwise it's the same definition.
So from the first part you understood that projD / Compile / Zero / scalacOptions
wins over projD / Zero / console / scalacOptions
and ThisBuild / ...
.
Now let's just take its value and use in our task. But what is its value? It refers to projD / Zero / Zero / scalacOptions
which is not defined explicitly. So we need to find a delegate for it. At this moment we're already not competing with projD / Compile
and projD / console
and the only fitting key in scope is in the scope ThisBuild / Zero / Zero
.
I tried to illustrate it here (ommitting the / scalacOptions
to save space):
projD / Compile / console // the one we need in the task
projD / Zero / console // could be applied by rule 3, but looses to the next one:
projD / Compile / Zero // applies by rule 2 (task scope delegation), defined with next one:
projD / Zero / Zero // not defined explicitly, so is delegated:
ThisBuild / Zero / Zero // applies by rule 4 (project scope delegation)
Upvotes: 2