@@ -45,7 +45,7 @@ import qualified InterfaceFiles
4545import Control.Applicative
4646import Control.Concurrent.Async
4747import Control.Concurrent.MVar
48- import Control.Exception (throw ,catch ,displayException ,IOException ,ErrorCall )
48+ import Control.Exception (throw ,catch ,displayException ,IOException ,ErrorCall , try , SomeException )
4949import Control.Exception (bracketOnError )
5050import Control.Concurrent (forkIO )
5151import Control.Monad
@@ -810,17 +810,17 @@ doTask gopts opts paths env t@(ActonTask mn src m) = do
810810 timeStart <- getTime Monotonic
811811
812812 let outFiles = [tyFile, hFile, cFile]
813- ok <- checkUptoDate gopts opts paths actFile outFiles (importsOf t)
814- if C. only_build opts || (ok && not (mn == (modName paths) && (forceCompilation opts)))
815- then do
816- timeBeforeTy <- getTime Monotonic
817- (_,nmod) <- InterfaceFiles. readFile tyFile
813+ timeBeforeCheck <- getTime Monotonic
814+ uptoDateResult <- checkUptoDate gopts opts paths actFile outFiles (importsOf t)
815+ timeAfterCheck <- getTime Monotonic
816+ iff (C. timing gopts) $ putStrLn (" Check up-to-date & read .ty file: " ++ fmtTime(timeAfterCheck - timeBeforeCheck))
817+ case uptoDateResult of
818+ Just (ms, nmod) | C. only_build opts || not (mn == (modName paths) && (forceCompilation opts)) -> do
818819 timeEnd <- getTime Monotonic
819- iff (C. timing gopts) $ putStrLn (" Read .ty file " ++ makeRelative (projPath paths) tyFile ++ " : " ++ fmtTime(timeEnd - timeBeforeTy))
820820 iff (not (quiet gopts opts)) $ putStrLn (" Already up to date, in " ++ fmtTime(timeEnd - timeStart))
821821 let A. NModule te mdoc = nmod
822822 return (Acton.Env. addMod mn te mdoc env)
823- else do
823+ _ -> do
824824 createDirectoryIfMissing True (getModPath (projTypes paths) mn)
825825 env' <- runRestPasses gopts opts paths env m
826826 `catch` handle gopts opts " Compilation error" generalError src paths mn
@@ -839,7 +839,18 @@ doTask gopts opts paths env t@(ActonTask mn src m) = do
839839 || (C. norm args) || (C. deact args) || (C. cps args) || (C. llift args) || (C. hgen args) || (C. cgen args)
840840
841841
842- checkUptoDate :: C. GlobalOptions -> C. CompileOptions -> Paths -> FilePath -> [FilePath ] -> [A. ModName ] -> IO Bool
842+ -- | Check if a module is up-to-date and return its type interface if it is.
843+ -- Returns Nothing if the module needs recompilation (not up-to-date or .ty file unreadable).
844+ -- Returns Just (imports, nameInfo) if the module is up-to-date with a valid .ty file.
845+ --
846+ -- The function checks:
847+ -- 1. All output files exist
848+ -- 2. Output files are newer than source files
849+ -- 3. Import dependencies are up-to-date
850+ -- 4. The .ty file (first in outFiles) can be successfully read. Torn writes
851+ -- (actonc was killed while working) or compiler updates can cause .ty files
852+ -- to be unreadable and thus we should consider it out-of-date and recompile
853+ checkUptoDate :: C. GlobalOptions -> C. CompileOptions -> Paths -> FilePath -> [FilePath ] -> [A. ModName ] -> IO (Maybe ([A. ModName ], A. NameInfo ))
843854checkUptoDate gopts opts paths actFile outFiles imps = do
844855 iff (C. verbose gopts) (putStrLn (" Checking " ++ makeRelative (srcDir paths) actFile ++ " is up to date..." ))
845856 -- get the path to the actonc binary, i.e. ourself
@@ -853,13 +864,26 @@ checkUptoDate gopts opts paths actFile outFiles imps = do
853864 if not (and outExists)
854865 then do
855866 iff (C. verbose gopts) (putStrLn (" Missing output files: " ++ show outExists ++ " for " ++ show outFiles))
856- return False
867+ return Nothing
857868 else do
858869 -- get the time of the last modified source file
859870 srcTime <- head <$> sortBy (comparing Down ) <$> mapM System.Directory. getModificationTime srcFiles
860871 outTiming <- mapM System.Directory. getModificationTime outFiles
861872 impsOK <- mapM (impOK (head outTiming)) imps
862- return (all (srcTime < ) outTiming && and impsOK)
873+ if not (all (srcTime < ) outTiming && and impsOK)
874+ then do
875+ iff (C. verbose gopts) (putStrLn (" Source files are newer than output files" ))
876+ return Nothing
877+ else do
878+ -- All timestamps check out, now verify the .ty file is readable
879+ -- The .ty file is always the first in outFiles
880+ let tyFile = head outFiles
881+ tyResult <- (try :: IO a -> IO (Either SomeException a )) $ InterfaceFiles. readFile tyFile
882+ case tyResult of
883+ Left e -> do
884+ iff (C. verbose gopts) (putStrLn (" .ty file is unreadable: " ++ displayException e))
885+ return Nothing
886+ Right contents -> return (Just contents)
863887 where
864888 srcBase = joinPath [takeDirectory actFile, takeBaseName actFile]
865889 srcCFile = srcBase ++ " .c"
0 commit comments