Hassan Al-Awwadi pushed to branch wip/haanss/depdir at Glasgow Haskell Compiler / GHC
Commits:
-
01d3154e
by Wen Kokke at 2025-07-10T17:06:36+01:00
-
ac259c48
by Wen Kokke at 2025-07-10T17:06:38+01:00
-
2b4db9ba
by Pi Delport at 2025-07-11T16:40:52-04:00
-
0bfcaf03
by Hassan Al-Awwadi at 2025-07-14T18:54:33+02:00
25 changed files:
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Make.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Recomp/Types.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/Finder/Types.hs
- compiler/GHC/Unit/Module/Deps.hs
- docs/users_guide/9.14.1-notes.rst
- docs/users_guide/eventlog-formats.rst
- docs/users_guide/separate_compilation.rst
- libraries/ghc-internal/src/GHC/Internal/Base.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/TH.hs
- libraries/template-haskell/Language/Haskell/TH/Syntax.hs
- testsuite/.gitignore
- testsuite/tests/interface-stability/template-haskell-exports.stdout
- testsuite/tests/th/Makefile
- + testsuite/tests/th/TH_Depends_Dir.hs
- + testsuite/tests/th/TH_Depends_Dir.stdout
- + testsuite/tests/th/TH_Depends_Dir_External.hs
- testsuite/tests/th/all.T
Changes:
... | ... | @@ -75,14 +75,15 @@ data UsageConfig = UsageConfig |
75 | 75 | |
76 | 76 | mkUsageInfo :: UsageConfig -> Plugins -> FinderCache -> UnitEnv
|
77 | 77 | -> Module -> ImportedMods -> [ImportUserSpec] -> NameSet
|
78 | - -> [FilePath] -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded
|
|
78 | + -> [FilePath] -> [FilePath] -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded
|
|
79 | 79 | -> IfG [Usage]
|
80 | 80 | mkUsageInfo uc plugins fc unit_env
|
81 | 81 | this_mod dir_imp_mods imp_decls used_names
|
82 | - dependent_files merged needed_links needed_pkgs
|
|
82 | + dependent_files dependent_dirs merged needed_links needed_pkgs
|
|
83 | 83 | = do
|
84 | 84 | eps <- liftIO $ readIORef (euc_eps (ue_eps unit_env))
|
85 | - hashes <- liftIO $ mapM getFileHash dependent_files
|
|
85 | + file_hashes <- liftIO $ mapM getFileHash dependent_files
|
|
86 | + dirs_hashes <- liftIO $ mapM getDirHash dependent_dirs
|
|
86 | 87 | let hu = ue_unsafeHomeUnit unit_env
|
87 | 88 | hug = ue_home_unit_graph unit_env
|
88 | 89 | -- Dependencies on object files due to TH and plugins
|
... | ... | @@ -93,7 +94,11 @@ mkUsageInfo uc plugins fc unit_env |
93 | 94 | let usages = mod_usages ++ [ UsageFile { usg_file_path = mkFastString f
|
94 | 95 | , usg_file_hash = hash
|
95 | 96 | , usg_file_label = Nothing }
|
96 | - | (f, hash) <- zip dependent_files hashes ]
|
|
97 | + | (f, hash) <- zip dependent_files file_hashes ]
|
|
98 | + ++ [ UsageDirectory { usg_dir_path = mkFastString d
|
|
99 | + , usg_dir_hash = hash
|
|
100 | + , usg_dir_label = Nothing }
|
|
101 | + | (d, hash) <- zip dependent_dirs dirs_hashes]
|
|
97 | 102 | ++ [ UsageMergedRequirement
|
98 | 103 | { usg_mod = mod,
|
99 | 104 | usg_mod_hash = hash
|
... | ... | @@ -269,6 +269,7 @@ mkRecompUsageInfo hsc_env tc_result = do |
269 | 269 | else do
|
270 | 270 | let used_names = mkUsedNames tc_result
|
271 | 271 | dep_files <- (readIORef (tcg_dependent_files tc_result))
|
272 | + dep_dirs <- (readIORef (tcg_dependent_dirs tc_result))
|
|
272 | 273 | (needed_links, needed_pkgs) <- readIORef (tcg_th_needed_deps tc_result)
|
273 | 274 | let uc = initUsageConfig hsc_env
|
274 | 275 | plugins = hsc_plugins hsc_env
|
... | ... | @@ -289,6 +290,7 @@ mkRecompUsageInfo hsc_env tc_result = do |
289 | 290 | (tcg_import_decls tc_result)
|
290 | 291 | used_names
|
291 | 292 | dep_files
|
293 | + dep_dirs
|
|
292 | 294 | (tcg_merged tc_result)
|
293 | 295 | needed_links
|
294 | 296 | needed_pkgs
|
... | ... | @@ -194,6 +194,7 @@ data RecompReason |
194 | 194 | | ModuleChangedRaw ModuleName
|
195 | 195 | | ModuleChangedIface ModuleName
|
196 | 196 | | FileChanged FilePath
|
197 | + | DirChanged FilePath
|
|
197 | 198 | | CustomReason String
|
198 | 199 | | FlagsChanged
|
199 | 200 | | LinkFlagsChanged
|
... | ... | @@ -230,6 +231,7 @@ instance Outputable RecompReason where |
230 | 231 | ModuleRemoved (_st, _uid, m) -> ppr m <+> text "removed"
|
231 | 232 | ModuleAdded (_st, _uid, m) -> ppr m <+> text "added"
|
232 | 233 | FileChanged fp -> text fp <+> text "changed"
|
234 | + DirChanged dp -> text dp <+> text "changed"
|
|
233 | 235 | CustomReason s -> text s
|
234 | 236 | FlagsChanged -> text "Flags changed"
|
235 | 237 | LinkFlagsChanged -> text "Flags changed"
|
... | ... | @@ -815,6 +817,22 @@ checkModUsage fc UsageFile{ usg_file_path = file, |
815 | 817 | then \e -> pprTrace "UsageFile" (text (show e)) $ return recomp
|
816 | 818 | else \_ -> return recomp -- if we can't find the file, just recompile, don't fail
|
817 | 819 | |
820 | +checkModUsage fc UsageDirectory{ usg_dir_path = dir,
|
|
821 | + usg_dir_hash = old_hash,
|
|
822 | + usg_dir_label = mlabel } =
|
|
823 | + liftIO $
|
|
824 | + handleIO handler $ do
|
|
825 | + new_hash <- lookupDirCache fc $ unpackFS dir
|
|
826 | + if (old_hash /= new_hash)
|
|
827 | + then return recomp
|
|
828 | + else return UpToDate
|
|
829 | + where
|
|
830 | + reason = DirChanged $ unpackFS dir
|
|
831 | + recomp = needsRecompileBecause $ fromMaybe reason $ fmap CustomReason mlabel
|
|
832 | + handler = if debugIsOn
|
|
833 | + then \e -> pprTrace "UsageDirectory" (text (show e)) $ return recomp
|
|
834 | + else \_ -> return recomp -- if we can't find the dir, just recompile, don't fail
|
|
835 | + |
|
818 | 836 | -- | We are importing a module whose exports have changed.
|
819 | 837 | -- Does this require recompilation?
|
820 | 838 | --
|
... | ... | @@ -140,6 +140,10 @@ pprUsage usage@UsageFile{} |
140 | 140 | = hsep [text "addDependentFile",
|
141 | 141 | doubleQuotes (ftext (usg_file_path usage)),
|
142 | 142 | ppr (usg_file_hash usage)]
|
143 | +pprUsage usage@UsageDirectory{}
|
|
144 | + = hsep [text "AddDependentDirectory",
|
|
145 | + doubleQuotes (ftext (usg_dir_path usage)),
|
|
146 | + ppr (usg_dir_hash usage)]
|
|
143 | 147 | pprUsage usage@UsageMergedRequirement{}
|
144 | 148 | = hsep [text "merged", ppr (usg_mod usage), ppr (usg_mod_hash usage)]
|
145 | 149 | pprUsage usage@UsageHomeModuleInterface{}
|
... | ... | @@ -173,8 +173,6 @@ import GHC.Parser.HaddockLex (lexHsDoc) |
173 | 173 | import GHC.Parser (parseIdentifier)
|
174 | 174 | import GHC.Rename.Doc (rnHsDoc)
|
175 | 175 | |
176 | - |
|
177 | - |
|
178 | 176 | {-
|
179 | 177 | Note [Template Haskell state diagram]
|
180 | 178 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
... | ... | @@ -1524,6 +1522,11 @@ instance TH.Quasi TcM where |
1524 | 1522 | dep_files <- readTcRef ref
|
1525 | 1523 | writeTcRef ref (fp:dep_files)
|
1526 | 1524 | |
1525 | + qAddDependentDirectory dp = do
|
|
1526 | + ref <- fmap tcg_dependent_dirs getGblEnv
|
|
1527 | + dep_dirs <- readTcRef ref
|
|
1528 | + writeTcRef ref (dp:dep_dirs)
|
|
1529 | + |
|
1527 | 1530 | qAddTempFile suffix = do
|
1528 | 1531 | dflags <- getDynFlags
|
1529 | 1532 | logger <- getLogger
|
... | ... | @@ -1928,6 +1931,7 @@ handleTHMessage msg = case msg of |
1928 | 1931 | ReifyConStrictness nm -> wrapTHResult $ TH.qReifyConStrictness nm
|
1929 | 1932 | GetPackageRoot -> wrapTHResult $ TH.qGetPackageRoot
|
1930 | 1933 | AddDependentFile f -> wrapTHResult $ TH.qAddDependentFile f
|
1934 | + AddDependentDirectory d -> wrapTHResult $ TH.qAddDependentDirectory d
|
|
1931 | 1935 | AddTempFile s -> wrapTHResult $ TH.qAddTempFile s
|
1932 | 1936 | AddModFinalizer r -> do
|
1933 | 1937 | interp <- hscInterp <$> getTopEnv
|
... | ... | @@ -603,6 +603,7 @@ data TcGblEnv |
603 | 603 | -- decls.
|
604 | 604 | |
605 | 605 | tcg_dependent_files :: TcRef [FilePath], -- ^ dependencies from addDependentFile
|
606 | + tcg_dependent_dirs :: TcRef [FilePath], -- ^ dependencies from addDependentDirectory
|
|
606 | 607 | |
607 | 608 | tcg_th_topdecls :: TcRef [LHsDecl GhcPs],
|
608 | 609 | -- ^ Top-level declarations from addTopDecls
|
... | ... | @@ -55,7 +55,7 @@ module GHC.Tc.Utils.Monad( |
55 | 55 | getRdrEnvs, getImports,
|
56 | 56 | getFixityEnv, extendFixityEnv,
|
57 | 57 | getDeclaredDefaultTys,
|
58 | - addDependentFiles,
|
|
58 | + addDependentFiles, addDependentDirectories,
|
|
59 | 59 | |
60 | 60 | -- * Error management
|
61 | 61 | getSrcSpanM, setSrcSpan, setSrcSpanA, addLocM,
|
... | ... | @@ -274,6 +274,7 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this |
274 | 274 | let { type_env_var = hsc_type_env_vars hsc_env };
|
275 | 275 | |
276 | 276 | dependent_files_var <- newIORef [] ;
|
277 | + dependent_dirs_var <- newIORef [] ;
|
|
277 | 278 | static_wc_var <- newIORef emptyWC ;
|
278 | 279 | cc_st_var <- newIORef newCostCentreState ;
|
279 | 280 | th_topdecls_var <- newIORef [] ;
|
... | ... | @@ -369,6 +370,7 @@ initTc hsc_env hsc_src keep_rn_syntax mod loc do_this |
369 | 370 | tcg_safe_infer = infer_var,
|
370 | 371 | tcg_safe_infer_reasons = infer_reasons_var,
|
371 | 372 | tcg_dependent_files = dependent_files_var,
|
373 | + tcg_dependent_dirs = dependent_dirs_var,
|
|
372 | 374 | tcg_tc_plugin_solvers = [],
|
373 | 375 | tcg_tc_plugin_rewriters = emptyUFM,
|
374 | 376 | tcg_defaulting_plugins = [],
|
... | ... | @@ -957,6 +959,12 @@ addDependentFiles fs = do |
957 | 959 | dep_files <- readTcRef ref
|
958 | 960 | writeTcRef ref (fs ++ dep_files)
|
959 | 961 | |
962 | +addDependentDirectories :: [FilePath] -> TcRn ()
|
|
963 | +addDependentDirectories ds = do
|
|
964 | + ref <- fmap tcg_dependent_dirs getGblEnv
|
|
965 | + dep_dirs <- readTcRef ref
|
|
966 | + writeTcRef ref (ds ++ dep_dirs)
|
|
967 | + |
|
960 | 968 | {-
|
961 | 969 | ************************************************************************
|
962 | 970 | * *
|
... | ... | @@ -31,6 +31,9 @@ module GHC.Unit.Finder ( |
31 | 31 | |
32 | 32 | findObjectLinkableMaybe,
|
33 | 33 | findObjectLinkable,
|
34 | + |
|
35 | + -- important that GHC.HsToCore.Usage uses the same hashing method for usage dirs as is used here.
|
|
36 | + getDirHash,
|
|
34 | 37 | ) where
|
35 | 38 | |
36 | 39 | import GHC.Prelude
|
... | ... | @@ -68,7 +71,9 @@ import qualified Data.Map as M |
68 | 71 | import GHC.Driver.Env
|
69 | 72 | import GHC.Driver.Config.Finder
|
70 | 73 | import qualified Data.Set as Set
|
74 | +import qualified Data.List as L(sort)
|
|
71 | 75 | import Data.List.NonEmpty ( NonEmpty (..) )
|
76 | +import qualified System.Directory as SD
|
|
72 | 77 | import qualified System.OsPath as OsPath
|
73 | 78 | import qualified Data.List.NonEmpty as NE
|
74 | 79 | |
... | ... | @@ -107,10 +112,12 @@ initFinderCache :: IO FinderCache |
107 | 112 | initFinderCache = do
|
108 | 113 | mod_cache <- newIORef emptyInstalledModuleEnv
|
109 | 114 | file_cache <- newIORef M.empty
|
115 | + dir_cache <- newIORef M.empty
|
|
110 | 116 | let flushFinderCaches :: UnitEnv -> IO ()
|
111 | 117 | flushFinderCaches ue = do
|
112 | 118 | atomicModifyIORef' mod_cache $ \fm -> (filterInstalledModuleEnv is_ext fm, ())
|
113 | 119 | atomicModifyIORef' file_cache $ \_ -> (M.empty, ())
|
120 | + atomicModifyIORef' dir_cache $ \_ -> (M.empty, ())
|
|
114 | 121 | where
|
115 | 122 | is_ext mod _ = not (isUnitEnvInstalledModule ue mod)
|
116 | 123 | |
... | ... | @@ -137,8 +144,29 @@ initFinderCache = do |
137 | 144 | atomicModifyIORef' file_cache $ \c -> (M.insert key hash c, ())
|
138 | 145 | return hash
|
139 | 146 | Just fp -> return fp
|
147 | + lookupDirCache :: FilePath -> IO Fingerprint
|
|
148 | + lookupDirCache key = do
|
|
149 | + c <- readIORef dir_cache
|
|
150 | + case M.lookup key c of
|
|
151 | + Nothing -> do
|
|
152 | + hash <- getDirHash key
|
|
153 | + atomicModifyIORef' dir_cache $ \c -> (M.insert key hash c, ())
|
|
154 | + return hash
|
|
155 | + Just fp -> return fp
|
|
140 | 156 | return FinderCache{..}
|
141 | 157 | |
158 | +-- | This function computes a shallow hash of a directory, so really just what files and directories are directly inside it.
|
|
159 | +-- It does not look at the contents of the files, or the contents of the directories it contains.
|
|
160 | +getDirHash :: FilePath -> IO Fingerprint
|
|
161 | +getDirHash dir = do
|
|
162 | + contents <- SD.listDirectory dir
|
|
163 | + -- The documentation of Fingerprints describes this as an easy naive implementation
|
|
164 | + -- I wonder if we should do something more sophisticated here?
|
|
165 | + let hashes = fingerprintString <$> contents
|
|
166 | + let s_hashes = L.sort hashes
|
|
167 | + let hash = fingerprintFingerprints s_hashes
|
|
168 | + return hash
|
|
169 | + |
|
142 | 170 | -- -----------------------------------------------------------------------------
|
143 | 171 | -- The three external entry points
|
144 | 172 |
... | ... | @@ -37,6 +37,7 @@ data FinderCache = FinderCache { flushFinderCaches :: UnitEnv -> IO () |
37 | 37 | , lookupFileCache :: FilePath -> IO Fingerprint
|
38 | 38 | -- ^ Look for the hash of a file in the cache. This should add it to the
|
39 | 39 | -- cache. If the file doesn't exist, raise an IOException.
|
40 | + , lookupDirCache :: FilePath -> IO Fingerprint
|
|
40 | 41 | }
|
41 | 42 | |
42 | 43 | data InstalledFindResult
|
... | ... | @@ -357,6 +357,23 @@ data Usage |
357 | 357 | -- contents don't change. This previously lead to odd
|
358 | 358 | -- recompilation behaviors; see #8114
|
359 | 359 | }
|
360 | + | UsageDirectory {
|
|
361 | + usg_dir_path :: FastString,
|
|
362 | + -- ^ External dir dependency. From TH addDependentFile.
|
|
363 | + -- Should be absolute.
|
|
364 | + usg_dir_hash :: Fingerprint,
|
|
365 | + -- ^ 'Fingerprint' of the directories contents.
|
|
366 | + |
|
367 | + usg_dir_label :: Maybe String
|
|
368 | + -- ^ An optional string which is used in recompilation messages if
|
|
369 | + -- dir in question has changed.
|
|
370 | + |
|
371 | + -- Note: We do a very shallow check indeed, just what the contents of
|
|
372 | + -- the directory are, aka what files and directories are within it.
|
|
373 | + -- If those files/directories have their own contents changed...
|
|
374 | + -- We won't spot it here, better recursive add them to your usage
|
|
375 | + -- seperately.
|
|
376 | + }
|
|
360 | 377 | | UsageHomeModuleInterface {
|
361 | 378 | usg_mod_name :: ModuleName
|
362 | 379 | -- ^ Name of the module
|
... | ... | @@ -395,6 +412,7 @@ instance NFData Usage where |
395 | 412 | rnf (UsagePackageModule mod hash safe) = rnf mod `seq` rnf hash `seq` rnf safe `seq` ()
|
396 | 413 | rnf (UsageHomeModule mod uid hash entities exports safe) = rnf mod `seq` rnf uid `seq` rnf hash `seq` rnf entities `seq` rnf exports `seq` rnf safe `seq` ()
|
397 | 414 | rnf (UsageFile file hash label) = rnf file `seq` rnf hash `seq` rnf label `seq` ()
|
415 | + rnf (UsageDirectory dir hash label) = rnf dir `seq` rnf hash `seq` rnf label `seq` ()
|
|
398 | 416 | rnf (UsageMergedRequirement mod hash) = rnf mod `seq` rnf hash `seq` ()
|
399 | 417 | rnf (UsageHomeModuleInterface mod uid hash) = rnf mod `seq` rnf uid `seq` rnf hash `seq` ()
|
400 | 418 | |
... | ... | @@ -431,6 +449,12 @@ instance Binary Usage where |
431 | 449 | put_ bh (usg_unit_id usg)
|
432 | 450 | put_ bh (usg_iface_hash usg)
|
433 | 451 | |
452 | + put_ bh usg@UsageDirectory{} = do
|
|
453 | + putByte bh 5
|
|
454 | + put_ bh (usg_dir_path usg)
|
|
455 | + put_ bh (usg_dir_hash usg)
|
|
456 | + put_ bh (usg_dir_label usg)
|
|
457 | + |
|
434 | 458 | get bh = do
|
435 | 459 | h <- getByte bh
|
436 | 460 | case h of
|
... | ... | @@ -462,6 +486,12 @@ instance Binary Usage where |
462 | 486 | uid <- get bh
|
463 | 487 | hash <- get bh
|
464 | 488 | return UsageHomeModuleInterface { usg_mod_name = mod, usg_unit_id = uid, usg_iface_hash = hash }
|
489 | + 5 -> do
|
|
490 | + dp <- get bh
|
|
491 | + hash <- get bh
|
|
492 | + label <- get bh
|
|
493 | + return UsageDirectory { usg_dir_path = dp, usg_dir_hash = hash, usg_dir_label = label }
|
|
494 | + |
|
465 | 495 | i -> error ("Binary.get(Usage): " ++ show i)
|
466 | 496 | |
467 | 497 | -- | Records the imports that we depend on from a home module,
|
... | ... | @@ -232,6 +232,11 @@ Cmm |
232 | 232 | They are replaced, respectively, by ``SpecialiseEP``, ``pragSpecED`` and
|
233 | 233 | ``pragSpecInlED``.
|
234 | 234 | |
235 | +- We have added the ``addDependentDirectory`` function to match
|
|
236 | + ``addDependentFile``, which adds a directory to the list of dependencies that
|
|
237 | + the recompilation checker will look at to determine if a module needs to be
|
|
238 | + recompiled.
|
|
239 | + |
|
235 | 240 | Included libraries
|
236 | 241 | ~~~~~~~~~~~~~~~~~~
|
237 | 242 |
... | ... | @@ -779,9 +779,9 @@ the total time spent profiling. |
779 | 779 | Cost-centre break-down
|
780 | 780 | ^^^^^^^^^^^^^^^^^^^^^^
|
781 | 781 | |
782 | -A variable-length packet encoding a heap profile sample broken down by,
|
|
783 | - * cost-centre (:rts-flag:`-hc`)
|
|
784 | - |
|
782 | +A variable-length packet encoding a heap profile sample.
|
|
783 | +This event is only emitted when the heap profile type is set to :rts-flag:`-hc` or :rts-flag:`-hb`.
|
|
784 | +Otherwise, a :event-type:`HEAP_PROF_SAMPLE_STRING` event is emitted instead.
|
|
785 | 785 | |
786 | 786 | .. event-type:: HEAP_PROF_SAMPLE_COST_CENTRE
|
787 | 787 | |
... | ... | @@ -796,11 +796,19 @@ A variable-length packet encoding a heap profile sample broken down by, |
796 | 796 | String break-down
|
797 | 797 | ^^^^^^^^^^^^^^^^^
|
798 | 798 | |
799 | -A variable-length event encoding a heap sample broken down by,
|
|
799 | +A variable-length event encoding a heap sample.
|
|
800 | +The content of the sample label varies depending on the heap profile type:
|
|
801 | + |
|
802 | + * :rts-flag:`-hT` The sample label contains a closure type, e.g., ``"ghc-bignum:GHC.Num.Integer.IS"``.
|
|
803 | + * :rts-flag:`-hm` The sample label contains a module name, e.g., ``"GHC.Num.Integer"``.
|
|
804 | + * :rts-flag:`-hd` The sample label contains a closure description, e.g., ``"IS"``.
|
|
805 | + * :rts-flag:`-hy` The sample label contains a type description, e.g., ``"Integer"``.
|
|
806 | + * :rts-flag:`-he` The sample label contains a stringified era, e.g., ``"1"``.
|
|
807 | + * :rts-flag:`-hr` The sample label contains a retainer set description, e.g., ``"(184)$stoIntegralSized1"``.
|
|
808 | + * :rts-flag:`-hi` The sample label contains a stringified pointer, e.g., ``"0x1008b7588"``,
|
|
809 | + which can be matched to an info table description emitted by the :event-type:`IPE` event.
|
|
800 | 810 | |
801 | - * type description (:rts-flag:`-hy`)
|
|
802 | - * closure description (:rts-flag:`-hd`)
|
|
803 | - * module (:rts-flag:`-hm`)
|
|
811 | +If the heap profile type is set to :rts-flag:`-hc` or :rts-flag:`-hb`, a :event-type:`HEAP_PROF_SAMPLE_COST_CENTRE` event is emitted instead.
|
|
804 | 812 | |
805 | 813 | .. event-type:: HEAP_PROF_SAMPLE_STRING
|
806 | 814 | |
... | ... | @@ -808,7 +816,7 @@ A variable-length event encoding a heap sample broken down by, |
808 | 816 | :length: variable
|
809 | 817 | :field Word8: profile ID
|
810 | 818 | :field Word64: heap residency in bytes
|
811 | - :field String: type or closure description, or module name
|
|
819 | + :field String: sample label
|
|
812 | 820 | |
813 | 821 | .. _time-profiler-events:
|
814 | 822 |
... | ... | @@ -710,7 +710,7 @@ beautiful sight! |
710 | 710 | You can read about :ghc-wiki:`how all this works <commentary/compiler/recompilation-avoidance>` in the GHC commentary.
|
711 | 711 | |
712 | 712 | Recompilation for Template Haskell and Plugins
|
713 | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
713 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
714 | 714 | |
715 | 715 | Recompilation checking gets a bit more complicated when using Template Haskell or
|
716 | 716 | plugins. Both these features execute code at compile time and so if any of the
|
... | ... | @@ -727,6 +727,19 @@ if ``foo`` is from module ``A`` and ``bar`` is from module ``B``, the module wil |
727 | 727 | now depend on ``A.o`` and ``B.o``, if either of these change then the module will
|
728 | 728 | be recompiled.
|
729 | 729 | |
730 | +``addDependentFile`` and ``addDependentDirectory``
|
|
731 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
732 | + |
|
733 | +When using Template Haskell or plugins, you can use the functions
|
|
734 | +``addDependentFile`` and ``addDependentDirectory`` to add additional
|
|
735 | +dependencies to the module being compiled.
|
|
736 | + |
|
737 | +- When adding a file, this means that the contents of the file changing between
|
|
738 | + compilations will trigger a recompilation of the module.
|
|
739 | +- When adding a directory, this means that any file or subdirectory *added* to or
|
|
740 | + *removed* from the directory will trigger recompilation of the module, so
|
|
741 | + it is not a recursive dependency.
|
|
742 | + |
|
730 | 743 | .. _mutual-recursion:
|
731 | 744 | |
732 | 745 | Mutually recursive modules and hs-boot files
|
... | ... | @@ -1047,7 +1047,7 @@ class Functor f where |
1047 | 1047 | -- * sequence computations and combine their results ('<*>' and 'liftA2').
|
1048 | 1048 | --
|
1049 | 1049 | -- A minimal complete definition must include implementations of 'pure'
|
1050 | +-- and one of either '<*>' or 'liftA2'. If it defines both, then they must behave
|
|
1050 | 1051 | -- the same as their default definitions:
|
1051 | 1052 | --
|
1052 | 1053 | -- @('<*>') = 'liftA2' 'id'@
|
... | ... | @@ -135,6 +135,9 @@ class (MonadIO m, MonadFail m) => Quasi m where |
135 | 135 | -- | See 'addDependentFile'.
|
136 | 136 | qAddDependentFile :: FilePath -> m ()
|
137 | 137 | |
138 | + -- | See 'addDependentDirectory'.
|
|
139 | + qAddDependentDirectory :: FilePath -> m ()
|
|
140 | + |
|
138 | 141 | -- | See 'addTempFile'.
|
139 | 142 | qAddTempFile :: String -> m FilePath
|
140 | 143 | |
... | ... | @@ -181,30 +184,31 @@ instance Quasi IO where |
181 | 184 | qReport True msg = hPutStrLn stderr ("Template Haskell error: " ++ msg)
|
182 | 185 | qReport False msg = hPutStrLn stderr ("Template Haskell error: " ++ msg)
|
183 | 186 | |
184 | - qLookupName _ _ = badIO "lookupName"
|
|
185 | - qReify _ = badIO "reify"
|
|
186 | - qReifyFixity _ = badIO "reifyFixity"
|
|
187 | - qReifyType _ = badIO "reifyFixity"
|
|
188 | - qReifyInstances _ _ = badIO "reifyInstances"
|
|
189 | - qReifyRoles _ = badIO "reifyRoles"
|
|
190 | - qReifyAnnotations _ = badIO "reifyAnnotations"
|
|
191 | - qReifyModule _ = badIO "reifyModule"
|
|
192 | - qReifyConStrictness _ = badIO "reifyConStrictness"
|
|
193 | - qLocation = badIO "currentLocation"
|
|
194 | - qRecover _ _ = badIO "recover" -- Maybe we could fix this?
|
|
195 | - qGetPackageRoot = badIO "getProjectRoot"
|
|
196 | - qAddDependentFile _ = badIO "addDependentFile"
|
|
197 | - qAddTempFile _ = badIO "addTempFile"
|
|
198 | - qAddTopDecls _ = badIO "addTopDecls"
|
|
199 | - qAddForeignFilePath _ _ = badIO "addForeignFilePath"
|
|
200 | - qAddModFinalizer _ = badIO "addModFinalizer"
|
|
201 | - qAddCorePlugin _ = badIO "addCorePlugin"
|
|
202 | - qGetQ = badIO "getQ"
|
|
203 | - qPutQ _ = badIO "putQ"
|
|
204 | - qIsExtEnabled _ = badIO "isExtEnabled"
|
|
205 | - qExtsEnabled = badIO "extsEnabled"
|
|
206 | - qPutDoc _ _ = badIO "putDoc"
|
|
207 | - qGetDoc _ = badIO "getDoc"
|
|
187 | + qLookupName _ _ = badIO "lookupName"
|
|
188 | + qReify _ = badIO "reify"
|
|
189 | + qReifyFixity _ = badIO "reifyFixity"
|
|
190 | + qReifyType _ = badIO "reifyFixity"
|
|
191 | + qReifyInstances _ _ = badIO "reifyInstances"
|
|
192 | + qReifyRoles _ = badIO "reifyRoles"
|
|
193 | + qReifyAnnotations _ = badIO "reifyAnnotations"
|
|
194 | + qReifyModule _ = badIO "reifyModule"
|
|
195 | + qReifyConStrictness _ = badIO "reifyConStrictness"
|
|
196 | + qLocation = badIO "currentLocation"
|
|
197 | + qRecover _ _ = badIO "recover" -- Maybe we could fix this?
|
|
198 | + qGetPackageRoot = badIO "getProjectRoot"
|
|
199 | + qAddDependentFile _ = badIO "addDependentFile"
|
|
200 | + qAddDependentDirectory _ = badIO "AddDependentDirectory"
|
|
201 | + qAddTempFile _ = badIO "addTempFile"
|
|
202 | + qAddTopDecls _ = badIO "addTopDecls"
|
|
203 | + qAddForeignFilePath _ _ = badIO "addForeignFilePath"
|
|
204 | + qAddModFinalizer _ = badIO "addModFinalizer"
|
|
205 | + qAddCorePlugin _ = badIO "addCorePlugin"
|
|
206 | + qGetQ = badIO "getQ"
|
|
207 | + qPutQ _ = badIO "putQ"
|
|
208 | + qIsExtEnabled _ = badIO "isExtEnabled"
|
|
209 | + qExtsEnabled = badIO "extsEnabled"
|
|
210 | + qPutDoc _ _ = badIO "putDoc"
|
|
211 | + qGetDoc _ = badIO "getDoc"
|
|
208 | 212 | |
209 | 213 | instance Quote IO where
|
210 | 214 | newName = newNameIO
|
... | ... | @@ -822,6 +826,26 @@ getPackageRoot :: Q FilePath |
822 | 826 | getPackageRoot = Q qGetPackageRoot
|
823 | 827 | |
824 | 828 | |
829 | +-- | Record external directories that runIO is using (dependent upon).
|
|
830 | +-- The compiler can then recognize that it should re-compile the Haskell file
|
|
831 | +-- when a directory changes.
|
|
832 | +--
|
|
833 | +-- Expects an absolute directory path.
|
|
834 | +--
|
|
835 | +-- Notes:
|
|
836 | +--
|
|
837 | +-- * ghc -M does not know about these dependencies - it does not execute TH.
|
|
838 | +--
|
|
839 | +-- * The dependency is shallow, based only on the direct content.
|
|
840 | +-- Basically, it only sees a list of names. It does not look at directory
|
|
841 | +-- metadata, recurse into subdirectories, or look at file contents. As
|
|
842 | +-- long as the list of names remains the same, the directory is considered
|
|
843 | +-- unchanged.
|
|
844 | +--
|
|
845 | +-- * The state of the directory is read at the interface generation time,
|
|
846 | +-- not at the time of the function call.
|
|
847 | +addDependentDirectory :: FilePath -> Q ()
|
|
848 | +addDependentDirectory dp = Q (qAddDependentDirectory dp)
|
|
825 | 849 | |
826 | 850 | -- | Record external files that runIO is using (dependent upon).
|
827 | 851 | -- The compiler can then recognize that it should re-compile the Haskell file
|
... | ... | @@ -833,7 +857,11 @@ getPackageRoot = Q qGetPackageRoot |
833 | 857 | --
|
834 | 858 | -- * ghc -M does not know about these dependencies - it does not execute TH.
|
835 | 859 | --
|
836 | --- * The dependency is based on file content, not a modification time
|
|
860 | +-- * The dependency is based on file content, not a modification time or
|
|
861 | +-- any other metadata associated with the file (e.g. permissions).
|
|
862 | +--
|
|
863 | +-- * The state of the file is read at the interface generation time,
|
|
864 | +-- not at the time of the function call.
|
|
837 | 865 | addDependentFile :: FilePath -> Q ()
|
838 | 866 | addDependentFile fp = Q (qAddDependentFile fp)
|
839 | 867 | |
... | ... | @@ -961,32 +989,33 @@ instance MonadIO Q where |
961 | 989 | liftIO = runIO
|
962 | 990 | |
963 | 991 | instance Quasi Q where
|
964 | - qNewName = newName
|
|
965 | - qReport = report
|
|
966 | - qRecover = recover
|
|
967 | - qReify = reify
|
|
968 | - qReifyFixity = reifyFixity
|
|
969 | - qReifyType = reifyType
|
|
970 | - qReifyInstances = reifyInstances
|
|
971 | - qReifyRoles = reifyRoles
|
|
972 | - qReifyAnnotations = reifyAnnotations
|
|
973 | - qReifyModule = reifyModule
|
|
974 | - qReifyConStrictness = reifyConStrictness
|
|
975 | - qLookupName = lookupName
|
|
976 | - qLocation = location
|
|
977 | - qGetPackageRoot = getPackageRoot
|
|
978 | - qAddDependentFile = addDependentFile
|
|
979 | - qAddTempFile = addTempFile
|
|
980 | - qAddTopDecls = addTopDecls
|
|
981 | - qAddForeignFilePath = addForeignFilePath
|
|
982 | - qAddModFinalizer = addModFinalizer
|
|
983 | - qAddCorePlugin = addCorePlugin
|
|
984 | - qGetQ = getQ
|
|
985 | - qPutQ = putQ
|
|
986 | - qIsExtEnabled = isExtEnabled
|
|
987 | - qExtsEnabled = extsEnabled
|
|
988 | - qPutDoc = putDoc
|
|
989 | - qGetDoc = getDoc
|
|
992 | + qNewName = newName
|
|
993 | + qReport = report
|
|
994 | + qRecover = recover
|
|
995 | + qReify = reify
|
|
996 | + qReifyFixity = reifyFixity
|
|
997 | + qReifyType = reifyType
|
|
998 | + qReifyInstances = reifyInstances
|
|
999 | + qReifyRoles = reifyRoles
|
|
1000 | + qReifyAnnotations = reifyAnnotations
|
|
1001 | + qReifyModule = reifyModule
|
|
1002 | + qReifyConStrictness = reifyConStrictness
|
|
1003 | + qLookupName = lookupName
|
|
1004 | + qLocation = location
|
|
1005 | + qGetPackageRoot = getPackageRoot
|
|
1006 | + qAddDependentFile = addDependentFile
|
|
1007 | + qAddDependentDirectory = addDependentDirectory
|
|
1008 | + qAddTempFile = addTempFile
|
|
1009 | + qAddTopDecls = addTopDecls
|
|
1010 | + qAddForeignFilePath = addForeignFilePath
|
|
1011 | + qAddModFinalizer = addModFinalizer
|
|
1012 | + qAddCorePlugin = addCorePlugin
|
|
1013 | + qGetQ = getQ
|
|
1014 | + qPutQ = putQ
|
|
1015 | + qIsExtEnabled = isExtEnabled
|
|
1016 | + qExtsEnabled = extsEnabled
|
|
1017 | + qPutDoc = putDoc
|
|
1018 | + qGetDoc = getDoc
|
|
990 | 1019 | |
991 | 1020 | |
992 | 1021 | ----------------------------------------------------
|
... | ... | @@ -291,6 +291,7 @@ data THMessage a where |
291 | 291 | |
292 | 292 | GetPackageRoot :: THMessage (THResult FilePath)
|
293 | 293 | AddDependentFile :: FilePath -> THMessage (THResult ())
|
294 | + AddDependentDirectory :: FilePath -> THMessage (THResult ())
|
|
294 | 295 | AddTempFile :: String -> THMessage (THResult FilePath)
|
295 | 296 | AddModFinalizer :: RemoteRef (TH.Q ()) -> THMessage (THResult ())
|
296 | 297 | AddCorePlugin :: String -> THMessage (THResult ())
|
... | ... | @@ -343,6 +344,7 @@ getTHMessage = do |
343 | 344 | 23 -> THMsg <$> (PutDoc <$> get <*> get)
|
344 | 345 | 24 -> THMsg <$> GetDoc <$> get
|
345 | 346 | 25 -> THMsg <$> return GetPackageRoot
|
347 | + 26 -> THMsg <$> AddDependentDirectory <$> get
|
|
346 | 348 | n -> error ("getTHMessage: unknown message " ++ show n)
|
347 | 349 | |
348 | 350 | putTHMessage :: THMessage a -> Put
|
... | ... | @@ -373,7 +375,7 @@ putTHMessage m = case m of |
373 | 375 | PutDoc l s -> putWord8 23 >> put l >> put s
|
374 | 376 | GetDoc l -> putWord8 24 >> put l
|
375 | 377 | GetPackageRoot -> putWord8 25
|
376 | - |
|
378 | + AddDependentDirectory a -> putWord8 26 >> put a
|
|
377 | 379 | |
378 | 380 | data EvalOpts = EvalOpts
|
379 | 381 | { useSandboxThread :: Bool
|
... | ... | @@ -198,6 +198,7 @@ instance TH.Quasi GHCiQ where |
198 | 198 | qLocation = fromMaybe noLoc . qsLocation <$> getState
|
199 | 199 | qGetPackageRoot = ghcCmd GetPackageRoot
|
200 | 200 | qAddDependentFile file = ghcCmd (AddDependentFile file)
|
201 | + qAddDependentDirectory dir = ghcCmd (AddDependentDirectory dir)
|
|
201 | 202 | qAddTempFile suffix = ghcCmd (AddTempFile suffix)
|
202 | 203 | qAddTopDecls decls = ghcCmd (AddTopDecls decls)
|
203 | 204 | qAddForeignFilePath lang fp = ghcCmd (AddForeignFilePath lang fp)
|
... | ... | @@ -32,6 +32,7 @@ module Language.Haskell.TH.Syntax ( |
32 | 32 | ModName (..),
|
33 | 33 | addCorePlugin,
|
34 | 34 | addDependentFile,
|
35 | + addDependentDirectory,
|
|
35 | 36 | addForeignFile,
|
36 | 37 | addForeignFilePath,
|
37 | 38 | addForeignSource,
|
... | ... | @@ -1523,6 +1523,7 @@ mk/ghcconfig*_test___spaces_ghc*.exe.mk |
1523 | 1523 | /tests/th/T8633
|
1524 | 1524 | /tests/th/TH_Depends
|
1525 | 1525 | /tests/th/TH_Depends_external.txt
|
1526 | +/tests/th/TH_Depends_external/dummy.txt
|
|
1526 | 1527 | /tests/th/TH_StringPrimL
|
1527 | 1528 | /tests/th/TH_import_loop/ModuleA.hi-boot
|
1528 | 1529 | /tests/th/TH_import_loop/ModuleA.o-boot
|
... | ... | @@ -1715,6 +1715,7 @@ module Language.Haskell.TH.Syntax where |
1715 | 1715 | qRunIO :: forall a. GHC.Internal.Types.IO a -> m a
|
1716 | 1716 | qGetPackageRoot :: m GHC.Internal.IO.FilePath
|
1717 | 1717 | qAddDependentFile :: GHC.Internal.IO.FilePath -> m ()
|
1718 | + qAddDependentDirectory :: GHC.Internal.IO.FilePath -> m ()
|
|
1718 | 1719 | qAddTempFile :: GHC.Internal.Base.String -> m GHC.Internal.IO.FilePath
|
1719 | 1720 | qAddTopDecls :: [Dec] -> m ()
|
1720 | 1721 | qAddForeignFilePath :: ForeignSrcLang -> GHC.Internal.Base.String -> m ()
|
... | ... | @@ -1726,7 +1727,7 @@ module Language.Haskell.TH.Syntax where |
1726 | 1727 | qExtsEnabled :: m [Extension]
|
1727 | 1728 | qPutDoc :: DocLoc -> GHC.Internal.Base.String -> m ()
|
1728 | 1729 | qGetDoc :: DocLoc -> m (GHC.Internal.Maybe.Maybe GHC.Internal.Base.String)
|
1729 | - {-# MINIMAL qNewName, qReport, qRecover, qLookupName, qReify, qReifyFixity, qReifyType, qReifyInstances, qReifyRoles, qReifyAnnotations, qReifyModule, qReifyConStrictness, qLocation, qGetPackageRoot, qAddDependentFile, qAddTempFile, qAddTopDecls, qAddForeignFilePath, qAddModFinalizer, qAddCorePlugin, qGetQ, qPutQ, qIsExtEnabled, qExtsEnabled, qPutDoc, qGetDoc #-}
|
|
1730 | + {-# MINIMAL qNewName, qReport, qRecover, qLookupName, qReify, qReifyFixity, qReifyType, qReifyInstances, qReifyRoles, qReifyAnnotations, qReifyModule, qReifyConStrictness, qLocation, qGetPackageRoot, qAddDependentFile, qAddDependentDirectory, qAddTempFile, qAddTopDecls, qAddForeignFilePath, qAddModFinalizer, qAddCorePlugin, qGetQ, qPutQ, qIsExtEnabled, qExtsEnabled, qPutDoc, qGetDoc #-}
|
|
1730 | 1731 | type Quote :: (* -> *) -> Constraint
|
1731 | 1732 | class GHC.Internal.Base.Monad m => Quote m where
|
1732 | 1733 | newName :: GHC.Internal.Base.String -> m Name
|
... | ... | @@ -1779,6 +1780,7 @@ module Language.Haskell.TH.Syntax where |
1779 | 1780 | type VarStrictType :: *
|
1780 | 1781 | type VarStrictType = VarBangType
|
1781 | 1782 | addCorePlugin :: GHC.Internal.Base.String -> Q ()
|
1783 | + addDependentDirectory :: GHC.Internal.IO.FilePath -> Q ()
|
|
1782 | 1784 | addDependentFile :: GHC.Internal.IO.FilePath -> Q ()
|
1783 | 1785 | addForeignFile :: ForeignSrcLang -> GHC.Internal.Base.String -> Q ()
|
1784 | 1786 | addForeignFilePath :: ForeignSrcLang -> GHC.Internal.IO.FilePath -> Q ()
|
... | ... | @@ -43,6 +43,20 @@ TH_Depends: |
43 | 43 | '$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends
|
44 | 44 | ./TH_Depends
|
45 | 45 | |
46 | +.PHONY: TH_Depends_Dir
|
|
47 | +TH_Depends_Dir:
|
|
48 | + rm -rf TH_Depends_external
|
|
49 | + $(RM) TH_Depends_Dir TH_Depends_Dir.exe
|
|
50 | + $(RM) TH_Depends_Dir.o TH_Depends_Dir.hi
|
|
51 | + $(RM) TH_Depends_Dir_External.o TH_Depends_Dir_External.hi
|
|
52 | + |
|
53 | + mkdir TH_Depends_external
|
|
54 | + '$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends_Dir
|
|
55 | + ./TH_Depends_Dir
|
|
56 | + sleep 2
|
|
57 | + echo "dummy" > TH_Depends_external/dummy.txt
|
|
58 | + '$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends_Dir
|
|
59 | + ./TH_Depends_Dir
|
|
46 | 60 | |
47 | 61 | T8333:
|
48 | 62 | '$(TEST_HC)' $(TEST_HC_OPTS_INTERACTIVE) $(ghcThWayFlags) T8333.hs < /dev/null
|
1 | + |
|
2 | +{-# LANGUAGE TemplateHaskell #-}
|
|
3 | + |
|
4 | +module Main where
|
|
5 | + |
|
6 | +import TH_Depends_Dir_External (checkDirectoryContent)
|
|
7 | + |
|
8 | +main :: IO ()
|
|
9 | +main = putStrLn $checkDirectoryContent |
|
\ No newline at end of file |
1 | +no files?
|
|
2 | +yes files! |
|
\ No newline at end of file |
1 | + |
|
2 | +module TH_Depends_Dir_External where
|
|
3 | + |
|
4 | +import Language.Haskell.TH.Syntax
|
|
5 | +import Language.Haskell.TH.Lib
|
|
6 | +import System.Directory (listDirectory)
|
|
7 | + |
|
8 | +checkDirectoryContent :: Q Exp
|
|
9 | +checkDirectoryContent = do
|
|
10 | + qAddDependentDirectory "TH_Depends_external"
|
|
11 | + l <- qRunIO $ listDirectory "TH_Depends_external"
|
|
12 | + let s = case l of
|
|
13 | + [] -> "no files?"
|
|
14 | + _ -> "yes files!"
|
|
15 | + stringE s |
|
\ No newline at end of file |
... | ... | @@ -214,6 +214,7 @@ test('T5434', [], multimod_compile, |
214 | 214 | ['T5434', '-v0 -Wall ' + config.ghc_th_way_flags])
|
215 | 215 | test('T5508', normal, compile, ['-v0 -ddump-splices -dsuppress-uniques'])
|
216 | 216 | test('TH_Depends', [only_ways(['normal'])], makefile_test, ['TH_Depends'])
|
217 | +test('TH_Depends_Dir', [only_ways(['normal'])], makefile_test, ['TH_Depends_Dir'])
|
|
217 | 218 | test('T5597', [], multimod_compile, ['T5597', '-v0 ' + config.ghc_th_way_flags])
|
218 | 219 | test('T5665', [], multimod_compile, ['T5665', '-v0 ' + config.ghc_th_way_flags])
|
219 | 220 | test('T5700', [], multimod_compile,
|