summaryrefslogtreecommitdiff
path: root/compiler/GHC/Iface/Recomp.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Iface/Recomp.hs')
-rw-r--r--compiler/GHC/Iface/Recomp.hs183
1 files changed, 107 insertions, 76 deletions
diff --git a/compiler/GHC/Iface/Recomp.hs b/compiler/GHC/Iface/Recomp.hs
index 392085f309..ee47ec97ee 100644
--- a/compiler/GHC/Iface/Recomp.hs
+++ b/compiler/GHC/Iface/Recomp.hs
@@ -1,4 +1,5 @@
{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE TupleSections #-}
-- | Module for detecting if recompilation is required
module GHC.Iface.Recomp
@@ -49,7 +50,6 @@ import GHC.Types.SrcLoc
import GHC.Types.Unique
import GHC.Types.Unique.Set
import GHC.Types.Fixity.Env
-import GHC.Types.SourceFile
import GHC.Unit.External
import GHC.Unit.Finder
@@ -66,10 +66,13 @@ import Data.Function
import Data.List (sortBy, sort)
import qualified Data.Map as Map
import Data.Word (Word64)
+import Data.Either
--Qualified import so we can define a Semigroup instance
-- but it doesn't clash with Outputable.<>
import qualified Data.Semigroup
+import GHC.List (uncons)
+import Data.Ord
{-
-----------------------------------------------
@@ -107,11 +110,11 @@ data RecompileRequired
= UpToDate
-- ^ everything is up to date, recompilation is not required
| MustCompile
- -- ^ The .hs file has been touched, or the .o/.hi file does not exist
+ -- ^ The .hs file has been modified, or the .o/.hi file does not exist
| RecompBecause String
-- ^ The .o/.hi files are up to date, but something else has changed
-- to force recompilation; the String says what (one-line summary)
- deriving Eq
+ deriving (Eq, Show)
instance Semigroup RecompileRequired where
UpToDate <> r = r
@@ -133,11 +136,10 @@ recompileRequired _ = True
checkOldIface
:: HscEnv
-> ModSummary
- -> SourceModified
-> Maybe ModIface -- Old interface from compilation manager, if any
-> IO (RecompileRequired, Maybe ModIface)
-checkOldIface hsc_env mod_summary source_modified maybe_iface
+checkOldIface hsc_env mod_summary maybe_iface
= do let dflags = hsc_dflags hsc_env
let logger = hsc_logger hsc_env
showPass logger dflags $
@@ -145,16 +147,15 @@ checkOldIface hsc_env mod_summary source_modified maybe_iface
(showPpr dflags $ ms_mod mod_summary) ++
" (use -ddump-hi-diffs for more details)"
initIfaceCheck (text "checkOldIface") hsc_env $
- check_old_iface hsc_env mod_summary source_modified maybe_iface
+ check_old_iface hsc_env mod_summary maybe_iface
check_old_iface
:: HscEnv
-> ModSummary
- -> SourceModified
-> Maybe ModIface
-> IfG (RecompileRequired, Maybe ModIface)
-check_old_iface hsc_env mod_summary src_modified maybe_iface
+check_old_iface hsc_env mod_summary maybe_iface
= let dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
getIface =
@@ -180,11 +181,10 @@ check_old_iface hsc_env mod_summary src_modified maybe_iface
src_changed
| gopt Opt_ForceRecomp dflags = True
- | SourceModified <- src_modified = True
| otherwise = False
in do
when src_changed $
- liftIO $ trace_hi_diffs logger dflags (nest 4 $ text "Source file changed or recompilation check turned off")
+ liftIO $ trace_hi_diffs logger dflags (nest 4 $ text "Recompilation check turned off")
case src_changed of
-- If the source has changed and we're in interactive mode,
@@ -209,31 +209,8 @@ check_old_iface hsc_env mod_summary src_modified maybe_iface
-- even in the SourceUnmodifiedAndStable case we
-- should check versions because some packages
-- might have changed or gone away.
- Just iface -> do
- (recomp_reqd, mb_checked_iface) <-
- checkVersions hsc_env mod_summary iface
- return $ case mb_checked_iface of
- Just iface | not (recompileRequired recomp_reqd) ->
- -- If the module used TH splices when it was last
- -- compiled, then the recompilation check is not
- -- accurate enough (#481) and we must ignore
- -- it. However, if the module is stable (none of
- -- the modules it depends on, directly or
- -- indirectly, changed), then we *can* skip
- -- recompilation. This is why the SourceModified
- -- type contains SourceUnmodifiedAndStable, and
- -- it's pretty important: otherwise ghc --make
- -- would always recompile TH modules, even if
- -- nothing at all has changed. Stability is just
- -- the same check that make is doing for us in
- -- one-shot mode.
- let stable = case src_modified of
- SourceUnmodifiedAndStable -> True
- _ -> False
- in if mi_used_th iface && not stable
- then (RecompBecause "TH", mb_checked_iface)
- else (recomp_reqd, mb_checked_iface)
- _ -> (recomp_reqd, mb_checked_iface)
+ Just iface ->
+ checkVersions hsc_env mod_summary iface
-- | Check if a module is still the same 'version'.
--
@@ -259,6 +236,8 @@ checkVersions hsc_env mod_summary iface
-- but we ALSO must make sure the instantiation matches up. See
-- test case bkpcabal04!
; hsc_env <- getTopEnv
+ ; if mi_src_hash iface /= ms_hs_hash mod_summary
+ then return (RecompBecause "Source file changed", Nothing) else do {
; if not (isHomeModule home_unit (mi_module iface))
then return (RecompBecause "-this-unit-id changed", Nothing) else do {
; recomp <- liftIO $ checkFlagHash hsc_env iface
@@ -295,7 +274,7 @@ checkVersions hsc_env mod_summary iface
; recomp <- checkList [checkModUsage (homeUnitAsUnit home_unit) u
| u <- mi_usages iface]
; return (recomp, Just iface)
- }}}}}}}}}}
+ }}}}}}}}}}}
where
logger = hsc_logger hsc_env
dflags = hsc_dflags hsc_env
@@ -389,16 +368,15 @@ checkHsig logger home_unit dflags mod_summary iface = do
checkHie :: DynFlags -> ModSummary -> RecompileRequired
checkHie dflags mod_summary =
let hie_date_opt = ms_hie_date mod_summary
- hs_date = ms_hs_date mod_summary
+ hi_date = ms_iface_date mod_summary
in if not (gopt Opt_WriteHie dflags)
then UpToDate
- else case hie_date_opt of
- Nothing -> RecompBecause "HIE file is missing"
- Just hie_date
- | hie_date < hs_date
+ else case (hie_date_opt, hi_date) of
+ (Nothing, _) -> RecompBecause "HIE file is missing"
+ (Just hie_date, Just hi_date)
+ | hie_date < hi_date
-> RecompBecause "HIE file is out of date"
- | otherwise
- -> UpToDate
+ _ -> UpToDate
-- | Check the flags haven't changed
checkFlagHash :: HscEnv -> ModIface -> IO RecompileRequired
@@ -475,41 +453,69 @@ checkMergedSignatures hsc_env mod_summary iface = do
-- Returns (RecompBecause <textual reason>) if recompilation is required.
checkDependencies :: HscEnv -> ModSummary -> ModIface -> IfG RecompileRequired
checkDependencies hsc_env summary iface
- = liftIO $ checkList (map dep_missing (ms_imps summary ++ ms_srcimps summary))
+ = do
+ res <- liftIO $ fmap sequence $ traverse (\(mb_pkg, L _ mod) ->
+ let reason = moduleNameString mod ++ " changed"
+ in classify reason <$> findImportedModule fc units home_unit dflags mod (mb_pkg))
+ (ms_imps summary ++ ms_srcimps summary)
+ case res of
+ Left recomp -> return recomp
+ Right es -> do
+ let (hs, ps) = partitionEithers es
+ res1 <- liftIO $ check_mods (sort hs) prev_dep_mods
+
+ let allPkgDeps = sortBy (comparing snd) (ps ++ bkpk_units)
+ res2 <- liftIO $ check_packages allPkgDeps prev_dep_pkgs
+ return (res1 `mappend` res2)
where
dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
fc = hsc_FC hsc_env
home_unit = hsc_home_unit hsc_env
units = hsc_units hsc_env
- prev_dep_mods = dep_direct_mods (mi_deps iface)
- prev_dep_plgn = dep_plgins (mi_deps iface)
- prev_dep_pkgs = dep_direct_pkgs (mi_deps iface)
-
- dep_missing (mb_pkg, L _ mod) = do
- find_res <- findImportedModule fc units home_unit dflags mod (mb_pkg)
- let reason = moduleNameString mod ++ " changed"
- case find_res of
- Found _ mod
- | isHomeUnit home_unit pkg
- -> if moduleName mod `notElem` map gwib_mod prev_dep_mods ++ prev_dep_plgn
- then do trace_hi_diffs logger dflags $
- text "imported module " <> quotes (ppr mod) <>
- text " not among previous dependencies"
- return (RecompBecause reason)
- else
- return UpToDate
- | otherwise
- -> if toUnitId pkg `notElem` prev_dep_pkgs
- then do trace_hi_diffs logger dflags $
- text "imported module " <> quotes (ppr mod) <>
- text " is from package " <> quotes (ppr pkg) <>
- text ", which is not among previous dependencies"
- return (RecompBecause reason)
- else
- return UpToDate
- where pkg = moduleUnit mod
- _otherwise -> return (RecompBecause reason)
+ prev_dep_mods = map gwib_mod $ dep_direct_mods (mi_deps iface)
+ prev_dep_pkgs = sort (dep_direct_pkgs (mi_deps iface))
+ bkpk_units = map (("Signature",) . indefUnit . instUnitInstanceOf . moduleUnit) (requirementMerges units (moduleName (mi_module iface)))
+
+
+
+ classify _ (Found _ mod)
+ | isHomeUnit home_unit (moduleUnit mod) = Right (Left (moduleName mod))
+ | otherwise = Right (Right (moduleNameString (moduleName mod), toUnitId $ moduleUnit mod))
+ classify reason _ = Left (RecompBecause reason)
+
+ check_mods [] [] = return UpToDate
+ check_mods [] (old:_) = do
+ -- This case can happen when a module is change from HPT to package import
+ trace_hi_diffs logger dflags $
+ text "module no longer " <> quotes (ppr old) <>
+ text "in dependencies"
+ return (RecompBecause (moduleNameString old ++ " removed"))
+ check_mods (new:news) olds
+ | Just (old, olds') <- uncons olds
+ , new == old = check_mods (dropWhile (== new) news) olds'
+ | otherwise = do
+ trace_hi_diffs logger dflags $
+ text "imported module " <> quotes (ppr new) <>
+ text " not among previous dependencies"
+ return (RecompBecause (moduleNameString new ++ " added"))
+
+ check_packages :: [(String, UnitId)] -> [UnitId] -> IO RecompileRequired
+ check_packages [] [] = return UpToDate
+ check_packages [] (old:_) = do
+ trace_hi_diffs logger dflags $
+ text "package " <> quotes (ppr old) <>
+ text "no longer in dependencies"
+ return (RecompBecause (unitString old ++ " removed"))
+ check_packages (new:news) olds
+ | Just (old, olds') <- uncons olds
+ , snd new == old = check_packages (dropWhile ((== (snd new)) . snd) news) olds'
+ | otherwise = do
+ trace_hi_diffs logger dflags $
+ text "imported package " <> quotes (ppr new) <>
+ text " not among previous dependencies"
+ return (RecompBecause ((fst new) ++ " package changed"))
+
needInterface :: Module -> (ModIface -> IO RecompileRequired)
-> IfG RecompileRequired
@@ -569,6 +575,13 @@ checkModUsage _ UsageMergedRequirement{ usg_mod = mod, usg_mod_hash = old_mod_ha
needInterface mod $ \iface -> do
let reason = moduleNameString (moduleName mod) ++ " changed (raw)"
checkModuleFingerprint logger dflags reason old_mod_hash (mi_mod_hash (mi_final_exts iface))
+checkModUsage this_pkg UsageHomeModuleInterface{ usg_mod_name = mod_name, usg_iface_hash = old_mod_hash } = do
+ let mod = mkModule this_pkg mod_name
+ dflags <- getDynFlags
+ logger <- getLogger
+ needInterface mod $ \iface -> do
+ let reason = moduleNameString (moduleName mod) ++ " changed (interface)"
+ checkIfaceFingerprint logger dflags reason old_mod_hash (mi_iface_hash (mi_final_exts iface))
checkModUsage this_pkg UsageHomeModule{
usg_mod_name = mod_name,
@@ -606,7 +619,8 @@ checkModUsage this_pkg UsageHomeModule{
checkModUsage _this_pkg UsageFile{ usg_file_path = file,
- usg_file_hash = old_hash } =
+ usg_file_hash = old_hash,
+ usg_file_label = mlabel } =
liftIO $
handleIO handler $ do
new_hash <- getFileHash file
@@ -614,7 +628,8 @@ checkModUsage _this_pkg UsageFile{ usg_file_path = file,
then return recomp
else return UpToDate
where
- recomp = RecompBecause (file ++ " changed")
+ reason = file ++ " changed"
+ recomp = RecompBecause (fromMaybe reason mlabel)
handler = if debugIsOn
then \e -> pprTrace "UsageFile" (text (show e)) $ return recomp
else \_ -> return recomp -- if we can't find the file, just recompile, don't fail
@@ -635,6 +650,21 @@ checkModuleFingerprint logger dflags reason old_mod_hash new_mod_hash
= out_of_date_hash logger dflags reason (text " Module fingerprint has changed")
old_mod_hash new_mod_hash
+checkIfaceFingerprint
+ :: Logger
+ -> DynFlags
+ -> String
+ -> Fingerprint
+ -> Fingerprint
+ -> IO RecompileRequired
+checkIfaceFingerprint logger dflags reason old_mod_hash new_mod_hash
+ | new_mod_hash == old_mod_hash
+ = up_to_date logger dflags (text "Iface fingerprint unchanged")
+
+ | otherwise
+ = out_of_date_hash logger dflags reason (text " Iface fingerprint has changed")
+ old_mod_hash new_mod_hash
+
------------------------
checkMaybeHash
:: Logger
@@ -1071,12 +1101,14 @@ addFingerprints hsc_env iface0
-- The interface hash depends on:
-- - the ABI hash, plus
+ -- - the source file hash,
-- - the module level annotations,
-- - usages
-- - deps (home and external packages, dependent files)
-- - hpc
iface_hash <- computeFingerprint putNameLiterally
(mod_hash,
+ mi_src_hash iface0,
ann_fn (mkVarOcc "module"), -- See mkIfaceAnnCache
mi_usages iface0,
sorted_deps,
@@ -1171,8 +1203,7 @@ sortDependencies d
dep_trusted_pkgs = sort (dep_trusted_pkgs d),
dep_boot_mods = sort (dep_boot_mods d),
dep_orphs = sortBy stableModuleCmp (dep_orphs d),
- dep_finsts = sortBy stableModuleCmp (dep_finsts d),
- dep_plgins = sortBy (lexicalCompareFS `on` moduleNameFS) (dep_plgins d) }
+ dep_finsts = sortBy stableModuleCmp (dep_finsts d) }
{-
************************************************************************