ghc-9.14.1: GHC/Iface/Recomp/Types.hs
module GHC.Iface.Recomp.Types (
IfaceSelfRecomp(..),
IfaceDynFlags(..),
pprIfaceDynFlags,
missingExtraFlagInfo,
) where
import GHC.Prelude
import GHC.Fingerprint
import GHC.Utils.Outputable
import GHC.Iface.Flags
import GHC.Types.SafeHaskell
import GHC.Unit.Module.Deps
import GHC.Unit.Module
import GHC.Utils.Binary
import Control.DeepSeq
{-
Note [Self recompilation information in interface files]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The flag -fwrite-if-self-recomp controls whether
interface files contain the information necessary to answer the
question:
Is the interface file up-to-date, relative to:
* the source file it corresponds to,
* the flags passed to the GHC invocation to compile it,
* its dependencies (e.g. imported items, watched files added by addDependentFile, ...)
If there is no self-recompilation information stored, then we always re-generate
the interface file from scratch.
Why? Most packages are only built once either by a distribution or cabal
and then placed into an immutable store, after which we will never ask
this question. Therefore we can derive two benefits from omitting this
information.
* Primary motivation: It vastly reduces the surface area for creating
non-deterministic interface files. See issue #10424 which motivated a
proper fix to that issue. Distributions have long contained versions
of GHC which just have broken self-recompilation checking (in order to
get deterministic interface files).
* Secondary motivation: This reduces the size of interface files
slightly.. the `mi_usages` field can be quite big but probably this
isn't such a great benefit.
* Third motivation: Conceptually clarity about which parts of an
interface file are used in order to **communicate** with subsequent
packages about the **interface** for a module. And which parts are
used to self-communicate during recompilation checking.
The main tracking issue is #22188 but fixes issues such as #10424 in a
proper way.
-}
-- | The information for a module which is only used when deciding whether to recompile
-- itself.
--
-- See Note [Self recompilation information in interface files]
data IfaceSelfRecomp =
IfaceSelfRecomp { mi_sr_src_hash :: !Fingerprint
-- ^ Hash of the .hs source, used for recompilation checking.
, mi_sr_usages :: [Usage]
-- ^ Usages; kept sorted so that it's easy to decide
-- whether to write a new iface file (changing usages
-- doesn't affect the hash of this module)
-- NOT STRICT! we read this field lazily from the interface file
-- It is *only* consulted by the recompilation checker
, mi_sr_flag_hash :: !(FingerprintWithValue IfaceDynFlags)
-- ^ Hash of the important flags used when compiling the module, excluding
-- optimisation flags
, mi_sr_opt_hash :: !Fingerprint
-- ^ Hash of optimisation flags
, mi_sr_hpc_hash :: !Fingerprint
-- ^ Hash of hpc flags
, mi_sr_plugin_hash :: !Fingerprint
-- ^ Hash of plugins
}
instance Binary IfaceSelfRecomp where
put_ bh (IfaceSelfRecomp{mi_sr_src_hash, mi_sr_usages, mi_sr_flag_hash, mi_sr_opt_hash, mi_sr_hpc_hash, mi_sr_plugin_hash}) = do
put_ bh mi_sr_src_hash
lazyPut bh mi_sr_usages
put_ bh mi_sr_flag_hash
put_ bh mi_sr_opt_hash
put_ bh mi_sr_hpc_hash
put_ bh mi_sr_plugin_hash
get bh = do
src_hash <- get bh
usages <- lazyGet bh
flag_hash <- get bh
opt_hash <- get bh
hpc_hash <- get bh
plugin_hash <- get bh
return $ IfaceSelfRecomp { mi_sr_src_hash = src_hash, mi_sr_usages = usages, mi_sr_flag_hash = flag_hash, mi_sr_opt_hash = opt_hash, mi_sr_hpc_hash = hpc_hash, mi_sr_plugin_hash = plugin_hash }
instance Outputable IfaceSelfRecomp where
ppr (IfaceSelfRecomp{mi_sr_src_hash, mi_sr_usages, mi_sr_flag_hash, mi_sr_opt_hash, mi_sr_hpc_hash, mi_sr_plugin_hash})
= vcat [text "Self-Recomp"
, nest 2 (vcat [ text "src hash:" <+> ppr mi_sr_src_hash
, text "flags:" <+> pprFingerprintWithValue missingExtraFlagInfo (fmap pprIfaceDynFlags mi_sr_flag_hash)
, text "opt hash:" <+> ppr mi_sr_opt_hash
, text "hpc hash:" <+> ppr mi_sr_hpc_hash
, text "plugin hash:" <+> ppr mi_sr_plugin_hash
, text "usages:" <+> ppr (map pprUsage mi_sr_usages)
])]
instance NFData IfaceSelfRecomp where
rnf (IfaceSelfRecomp src_hash usages flag_hash opt_hash hpc_hash plugin_hash)
= rnf src_hash `seq` rnf usages `seq` rnf flag_hash `seq` rnf opt_hash `seq` rnf hpc_hash `seq` rnf plugin_hash `seq` ()
pprFingerprintWithValue :: SDoc -> FingerprintWithValue SDoc -> SDoc
pprFingerprintWithValue missingInfo (FingerprintWithValue fp mflags)
= vcat $
[ text "fingerprint:" <+> (ppr fp)
]
++ case mflags of
Nothing -> [missingInfo]
Just doc -> [doc]
pprUsage :: Usage -> SDoc
pprUsage UsagePackageModule{ usg_mod = mod, usg_mod_hash = hash, usg_safe = safe }
= pprUsageImport mod hash safe
pprUsage UsageHomeModule{ usg_unit_id = unit_id, usg_mod_name = mod_name
, usg_mod_hash = hash, usg_safe = safe
, usg_exports = exports, usg_entities = entities }
= pprUsageImport (mkModule unit_id mod_name) hash safe $$
nest 2 (
maybe empty (\v -> text "exports: " <> ppr v) exports $$
vcat [ ppr n <+> ppr v | (n,v) <- entities ]
)
pprUsage usage@UsageFile{}
= hsep [text "addDependentFile",
doubleQuotes (ftext (usg_file_path usage)),
ppr (usg_file_hash usage)]
pprUsage usage@UsageMergedRequirement{}
= hsep [text "merged", ppr (usg_mod usage), ppr (usg_mod_hash usage)]
pprUsage usage@UsageHomeModuleInterface{}
= hsep [text "implementation", ppr (usg_mod_name usage)
, ppr (usg_unit_id usage)
, ppr (usg_iface_hash usage)]
pprUsageImport :: Outputable mod => mod -> Fingerprint -> IsSafeImport -> SDoc
pprUsageImport mod hash safe
= hsep [ text "import", pp_safe, ppr mod
, ppr hash ]
where
pp_safe | safe = text "safe"
| otherwise = text " -/ "