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

fix false positive cache invalidation caused by unscoped lookup #192

Merged
merged 1 commit into from
Dec 23, 2021

Conversation

bjaglin
Copy link
Member

@bjaglin bjaglin commented Dec 21, 2021

Fix regression introduced in bb36031, effectively disabling the cache for projects with files in several configuration. This is particularly annoying with scalafmtOnCompile := true.

The problem is visible by looking at a stamp file on a project with 4 configurations after running sbt clean scalafixAll

# 2.4.3
bjaglin@x1:~/project/target/streams$ find -name output-diff
./it/scalafmt/_global/streams/output-diff
./uit/scalafmt/_global/streams/output-diff
./compile/scalafmt/_global/streams/output-diff
./test/scalafmt/_global/streams/output-diff
# 2.4.4
bjaglin@x1:~/project/target/streams$ find -name output-diff
./_global/_global/_global/streams/output-diff
# 2.4.5
bjaglin@x1:~/project/target/streams$ find -name output-diff
./_global/_global/_global/streams/output-diff
# 2.4.5+9-d5456570-SNAPSHOT
bjaglin@x1:~/project/target/streams$ find -name output-diff
./it/scalafmt/_global/streams/output-diff
./uit/scalafmt/_global/streams/output-diff
./compile/scalafmt/_global/streams/output-diff
./test/scalafmt/_global/streams/output-diff

@bjaglin bjaglin changed the title fix false positive cache invalidation caused by unscoped lookup WIP fix false positive cache invalidation caused by unscoped lookup Dec 21, 2021
@bjaglin bjaglin marked this pull request as ready for review December 22, 2021 00:16
@bjaglin bjaglin changed the title WIP fix false positive cache invalidation caused by unscoped lookup fix false positive cache invalidation caused by unscoped lookup Dec 22, 2021
@@ -393,15 +388,12 @@ object ScalafmtPlugin extends AutoPlugin {

private def scalafmtTask(sources: Seq[File], session: FormatSession) =
Def.task {
session.formatTrackedSources(streams.value.cacheStoreFactory, sources)
Copy link
Contributor

Choose a reason for hiding this comment

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

would changing this to (scalafmt / streams).value.cacheStoreFactory be an equivalent single-line fix? if not, can you explain how this change actually works?

Copy link
Contributor

Choose a reason for hiding this comment

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

i tested this, doesn't work. mystery...

Copy link
Member Author

Choose a reason for hiding this comment

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

It wouldn't since we would still be missing the configuration axis (not propagated from the taskDyn, see #192 (comment)) when resolving the .value lookup.

However, an equivalent fix would be to pass the config as an argument of the method, and use (config / scalafmt / streams).value.cacheStoreFactory, but I felt like capturing the cache directory in the session was simpler.

@kitbellew
Copy link
Contributor

Fix regression introduced in bb36031, effectively disabling the cache for projects with files in several configuration. This is particularly annoying with scalafmtOnCompile := true.

i am not an expert (in fact, could barely navigate through sbt dynamic tasks to get them to compile)... which part in the linked change had this effect (and how)?

@bjaglin
Copy link
Member Author

bjaglin commented Dec 23, 2021

@kitbellew here is an attempt at clarifying what's going on (based on my own understanding obviously). This assumes the reader is familiar with the task graph & scopes.

# build.sbt

lazy val taskLookup = taskKey[File]("")
lazy val taskDynLookup = taskKey[File]("")

inConfig(Compile)(
  Seq(
    taskLookup := Def.task(streams.value.cacheDirectory).value,
    taskDynLookup := Def.taskDyn(Def.task(streams.value.cacheDirectory)).value,
  )
)
-$ sbt "show taskLookup" | tail -n2  | head -n1
+$ sbt "show taskDynLookup" | tail -n2  | head -n1
-[info] /tmp/sbt/target/streams/compile/taskLookup/_global/streams
+[info] /tmp/sbt/target/streams/_global/_global/_global/streams

We can see that the cache directory is not resolved in the same scope depending on where the lookup happens.

  • Within a task, the lookup is implicitly scoped to the enclosing config/task axes.
  • Within a task referenced in a taskDyn, the enclosing scope is lost (not propagated), so we fallback to the global cache directory no matter on which config/task it's evaluated.

It seems that keys are not scoped when looked up from
Def.Initialize[] referenced within Def.taskDyn(), resulting in
streams.value.cacheStoreFactory defaulting to a directory shared
across configurations.
@kitbellew
Copy link
Contributor

@kitbellew here is an attempt at clarifying what's going on (based on my own understanding obviously). This assumes the reader is familiar with the task graph & scopes.

thank you for the explanation, Brice. while bug in the macro is a more palatable explanation for the regression, it is true that i am more of a bull in a china shop when it comes to sbt/scopes.

@kitbellew kitbellew merged commit 912ba57 into scalameta:master Dec 23, 2021
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.

2 participants