mirror of
https://github.com/sbt/sbt.git
synced 2026-09-04 16:54:29 +02:00
The projects of a build mostly depend on the same libraries, and each project's report materializes its own copies of every coordinate it names, so one value exists once per project that mentions it: on a 302-module monorepo the cached reports hold 949,492 ModuleReports for 2,036 distinct values. UpdateReportInterner adds weak pools for ConfigRef, InclExclRule, File, Artifact, ModuleID, Caller and ModuleReport itself. Pooling the report is worth more than pooling its parts alone, because sharing it also shares its licenses vector, extraAttributes map, homepage string and artifacts vector. Reports carrying a publicationDate are canonicalized but never pooled, since that java.util.Calendar is mutable; everything else reachable from a ModuleReport is immutable, so sharing is semantically invisible. The pools are weak, so a value lives exactly as long as some report references it and nothing accumulates across a reload. Two sites cover a freshly resolved report. SbtUpdateReport interns each report as it builds it rather than sweeping the finished one, so only the module under construction is ever un-interned. That alone would not survive, though: coursier memoizes moduleReport on a key that includes the dependees, so each project builds its own instance of a shared coordinate, and transformDetails then rebuilt every report to drop the callers -- discarding the sharing and leaving a copy per module per configuration. Dropping the callers is what makes those reports value-equal in the first place, so transformDetails now re-interns what it rebuilds, and only rebuilds when there is something to drop. A scripted test pins it: two projects resolving one coordinate must end up holding one ModuleReport instance, on the fresh path and from the cache. Co-Authored-By: Claude Opus 5 <[email protected]>