on master help sign in
Rebuild cache reliably when upgrading transitive dependencies

Fix https://github.com/elm/compiler/issues/1710 Before I decided if the summary was valid based on a solution.dat file. It contained a (Map Pkg.Name Pkg.Version) of the exact packages represented in the summary. That was not enough information though. The ExposedModules in the Summary need to change if something moves from “transitive-dependencies” into “dependencies”. So the new version stores the full description of the dependencies right in the summary.dat file. The idea is that: - If it is valid, you need to read the whole file anyway. - If it is invalid, you have to read a bunch of files in packages anyway. So the common path (if it is valid) should be one file read faster, and the uncommon path (if it is invalid) should be one large file read slower.

commit9bac5d473423da3d48c0f2ef16438d8ee0754813 authorEvan Czaplicki <evancz@users.noreply.github.com> dateSat 2 Jun 2018, 16:00 (8 years ago) parenteb39dd7
4 files changed+107−61
builder/src/Deps/Verify.hs+2−3 builder/src/Elm/Project/Constraint.hs+26 builder/src/Stuff/Paths.hs−6 builder/src/Stuff/Verify.hs+79−52
builder/src/Deps/Verify.hs+2−3
@@ -48,12 +48,11 @@ import qualified Stuff.Paths as Paths
4848-- VERIFY
4949
5050
51−verify :: FilePath -> Project -> Task.Task (Map Name Version, Summary.Summary)
51+verify :: FilePath -> Project -> Task.Task Summary.Summary
5252verify root project =
5353 do solution <- Project.get verifyApp verifyPkg project
5454 (RawInfo infos ifaces) <- verifyArtifacts solution
55− let summary = Summary.init root project infos ifaces
56− return (solution, summary)
55+ return (Summary.init root project infos ifaces)
5756
5857
5958throw :: E.Exit -> Task.Task a
builder/src/Elm/Project/Constraint.hs+26
@@ -18,6 +18,9 @@ module Elm.Project.Constraint
1818 )
1919 where
2020
21+
22+import Control.Monad (liftM4)
23+import Data.Binary
2124import qualified Data.Text as Text
2225import Data.Text (Text)
2326
@@ -232,3 +235,26 @@ expand constraint@(Range lower lowerOp upperOp upper) version
232235
233236 | otherwise =
234237 constraint
238+
239+
240+
241+-- BINARY
242+
243+
244+instance Binary Constraint where
245+ get = liftM4 Range get get get get
246+ put (Range a b c d) = put a >> put b >> put c >> put d
247+
248+
249+instance Binary Op where
250+ put op =
251+ case op of
252+ Less -> putWord8 0
253+ LessOrEqual -> putWord8 1
254+
255+ get =
256+ do n <- getWord8
257+ case n of
258+ 0 -> return Less
259+ 1 -> return LessOrEqual
260+ _ -> error "binary encoding of Op was corrupted"
builder/src/Stuff/Paths.hs−6
@@ -1,7 +1,6 @@
11{-# OPTIONS_GHC -Wall #-}
22module Stuff.Paths
33 ( docs
4− , solution
54 , summary
65 , prepublishDir
76 , removeStuff
@@ -37,11 +36,6 @@ docs =
3736 stuff </> "docs.json"
3837
3938
40−solution :: FilePath
41−solution =
42− stuff </> "solution.dat"
43−
44−
4539summary :: FilePath
4640summary =
4741 stuff </> "summary.dat"
builder/src/Stuff/Verify.hs+79−52
@@ -5,14 +5,14 @@ module Stuff.Verify
55 where
66
77
8−import Prelude hiding (read)
9−import qualified Data.Map as Map
8+import Control.Monad (liftM2, liftM3, liftM4)
9+import Data.Binary
1010import Data.Map (Map)
1111
12+import qualified Elm.Compiler.Module as Module
1213import Elm.Package (Name, Version)
1314
1415import qualified Deps.Verify as Verify
15−import Elm.Project.Json (Project(..), PkgInfo(..))
1616import qualified Elm.Project.Json as Project
1717import qualified Elm.Project.Constraint as Con
1818import qualified Elm.Project.Summary as Summary
@@ -25,72 +25,99 @@ import qualified Stuff.Paths as Path
2525-- VERIFY
2626
2727
28−verify :: FilePath -> Project -> Task.Task Summary.Summary
28+verify :: FilePath -> Project.Project -> Task.Task Summary.Summary
2929verify root project =
30− do fresh <-
31− IO.andM
32− [ IO.exists Path.solution
33− , IO.exists Path.summary
34− , checkSolution project
35− ]
30+ do exists <- IO.exists Path.summary
31+ if exists
32+ then
33+ do (MiniSummary deps exposed ifaces graph) <- IO.readBinary Path.summary
34+ if isValidMiniSummary deps project
35+ then
36+ return (Summary.Summary root project exposed ifaces graph)
37+ else
38+ do IO.remove Path.summary
39+ cacheSummary root project
40+ else
41+ cacheSummary root project
42+
43+
44+cacheSummary :: FilePath -> Project.Project -> Task.Task Summary.Summary
45+cacheSummary root project =
46+ do summary@(Summary.Summary _ _ exposed ifaces graph) <- Verify.verify root project
47+ IO.writeBinary Path.summary (MiniSummary (getDeps project) exposed ifaces graph)
48+ return summary
3649
37− if fresh
38− then read root project
39− else rebuildCache root project
4050
4151
42−rebuildCache :: FilePath -> Project -> Task.Task Summary.Summary
43−rebuildCache root project =
44− do IO.remove Path.solution
45− IO.remove Path.summary
52+-- MINI SUMMARY
4653
47− (solution, summary) <- Verify.verify root project
4854
49− IO.writeBinary Path.solution solution
50− write summary
51− return summary
55+data MiniSummary =
56+ MiniSummary
57+ { _deps :: ProjectDeps
58+ , _exposed :: Summary.ExposedModules
59+ , _ifaces :: Module.Interfaces
60+ , _depsGraph :: Summary.DepsGraph
61+ }
5262
5363
64+data ProjectDeps
65+ = App (Map Name Version) (Map Name Version) (Map Name Version)
66+ | Pkg (Map Name Con.Constraint) (Map Name Con.Constraint)
5467
55−-- READ / WRITE
5668
69+getDeps :: Project.Project -> ProjectDeps
70+getDeps project =
71+ case project of
72+ Project.App (Project.AppInfo _ _ direct test trans) ->
73+ App direct test trans
5774
58−read :: FilePath -> Project -> Task.Task Summary.Summary
59−read root project =
60− do (exposed, ifaces, graph) <- IO.readBinary Path.summary
61− return (Summary.Summary root project exposed ifaces graph)
75+ Project.Pkg (Project.PkgInfo _ _ _ _ _ direct test _) ->
76+ Pkg direct test
6277
6378
64−write :: Summary.Summary -> Task.Task ()
65−write (Summary.Summary _ _ exposed ifaces graph) =
66− IO.writeBinary Path.summary ( exposed, ifaces, graph )
79+isValidMiniSummary :: ProjectDeps -> Project.Project -> Bool
80+isValidMiniSummary deps project =
81+ case deps of
82+ App cachedDirect cachedTest cachedTrans ->
83+ case project of
84+ Project.Pkg _ ->
85+ False
6786
87+ Project.App (Project.AppInfo _ _ direct test trans) ->
88+ cachedDirect == direct
89+ && cachedTest == test
90+ && cachedTrans == trans
6891
92+ Pkg cachedDirect cachedTest ->
93+ case project of
94+ Project.Pkg (Project.PkgInfo _ _ _ _ _ direct test _) ->
95+ cachedDirect == direct
96+ && cachedTest == test
6997
70−-- CHECK SOLUTION
98+ Project.App _ ->
99+ False
71100
72101
73−checkSolution :: Project -> Task.Task Bool
74−checkSolution project =
75− checkSolutionHelp project <$> IO.readBinary Path.solution
76102
103+-- BINARY
77104
78−checkSolutionHelp :: Project -> Map Name Version -> Bool
79−checkSolutionHelp project solution =
80− case project of
81− App info ->
82− solution == Project.appSolution info
83−
84− Pkg info ->
85− allGood (_pkg_test_deps info) solution
86− && allGood (_pkg_deps info) solution
87−
88−
89−allGood :: Map Name Con.Constraint -> Map Name Version -> Bool
90−allGood cons vsns =
91− let
92− bools =
93− Map.intersectionWith Con.satisfies cons vsns
94− in
95− Map.size cons == Map.size bools
96− && Map.foldr (&&) True bools
105+
106+instance Binary MiniSummary where
107+ get = liftM4 MiniSummary get get get get
108+ put (MiniSummary a b c d) = put a >> put b >> put c >> put d
109+
110+
111+
112+instance Binary ProjectDeps where
113+ put deps =
114+ case deps of
115+ App a b c -> putWord8 0 >> put a >> put b >> put c
116+ Pkg a b -> putWord8 1 >> put a >> put b
117+
118+ get =
119+ do n <- getWord8
120+ case n of
121+ 0 -> liftM3 App get get get
122+ 1 -> liftM2 Pkg get get
123+ _ -> error "binary encoding of ProjectDeps was corrupted"