content: 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@UsageDirectory{}
    = hsep [text "AddDependentDirectory",
            doubleQuotes (ftext (usg_dir_path usage)),
            ppr (usg_dir_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 " -/ "