diff options
| author | aarne <unknown> | 2004-02-26 14:49:16 +0000 |
|---|---|---|
| committer | aarne <unknown> | 2004-02-26 14:49:16 +0000 |
| commit | 2e1b57878329eb6a1822ef43c190f8a2aaaa82b7 (patch) | |
| tree | d7a85d0755a7c651e355f3315acd35f7d1a2feb5 /src/GF/Infra | |
| parent | 13be0d6356a2f198e2ad5929c5896939da8e168f (diff) | |
Improved make facility.
Diffstat (limited to 'src/GF/Infra')
| -rw-r--r-- | src/GF/Infra/ReadFiles.hs | 218 | ||||
| -rw-r--r-- | src/GF/Infra/UseIO.hs | 7 |
2 files changed, 170 insertions, 55 deletions
diff --git a/src/GF/Infra/ReadFiles.hs b/src/GF/Infra/ReadFiles.hs index 285665747..7b95141c7 100644 --- a/src/GF/Infra/ReadFiles.hs +++ b/src/GF/Infra/ReadFiles.hs @@ -1,6 +1,14 @@ -module ReadFiles where +module ReadFiles +--- where -import Arch (selectLater, modifiedFiles, ModTime) +-- +( +-- +getAllFiles,fixNewlines,ModName,getOptionsFromFile, +-- +gfcFile,gfFile,gfrFile,isGFC,resModName) where + +import Arch (selectLater, modifiedFiles, ModTime, getModTime,laterModTime) import Option import Operations @@ -10,78 +18,121 @@ import Char import Monad import List --- make analysis for GF grammar modules. AR 11/6/2003 +-- make analysis for GF grammar modules. AR 11/6/2003--24/2/2004 -- to find all files that have to be read, put them in dependency order, and -- decide which files need recompilation. Name file.gf is returned for them, -- and file.gfc or file.gfr otherwise. type ModName = String -type FileName = String -type InitPath = String -type FullPath = String +type ModEnv = [(ModName,ModTime)] -getAllFiles :: [InitPath] -> [(FullPath,ModTime)] -> FileName -> - IOE [FullPath] +getAllFiles :: [InitPath] -> ModEnv -> FileName -> IOE [FullPath] getAllFiles ps env file = do - ds <- getImports ps file - -- print ds ---- debug + + -- read module headers from all files recursively + ds0 <- getImports ps file + let ds = [((snd m,map fst ms),p) | ((m,ms),p) <- ds0] + ioeIO $ putStrLn $ "all modules:" +++ show (map (fst . fst) ds) + + -- get a topological sorting of files: returns file names --- deletes paths ds1 <- ioeErr $ either return - (\ms -> Bad $ "circular modules" +++ unwords (map show (head ms))) $ - topoTest $ map fst ds + (\ms -> Bad $ "circular modules" +++ + unwords (map show (head ms))) $ topoTest $ map fst ds + + -- associate each file name with its path --- more optimal: save paths in ds1 let paths = [(f,p) | ((f,_),p) <- ds] let pds1 = [(p,f) | f <- ds1, Just p <- [lookup f paths]] - ds2 <- ioeIO $ mapM selectFormat pds1 - -- print ds2 ---- debug - let ds3 = needCompile ds ds2 - ds4 <- ioeIO $ modifiedFiles env ds3 + + + ds2 <- ioeIO $ mapM (selectFormat env) pds1 + + let ds4 = needCompile (map fst ds0) ds2 return ds4 -getImports :: [InitPath] -> FileName -> IOE [((ModName,[ModName]),InitPath)] -getImports ps = get [] where - get ds file = do - let name = fileBody file - (p,s) <- readFileIfPath ps $ file - let imps = importsOfFile s - case imps of - _ | elem name (map (fst . fst) ds) -> return ds --- file already read - [] -> return $ ((name,[]),p):ds - _ -> do - let files = map gfFile imps - foldM get (((name,imps),p):ds) files +-- to decide whether to read gf or gfc, or if in env; returns full file path --- to decide whether to read gf or gfc; returns full file path +data CompStatus = + CSComp -- compile: read gf + | CSRead -- read gfc + | CSEnv -- gfc is in env + | CSEnvR -- also gfr is in env + | CSDont -- don't read at all + | CSRes -- read gfr + deriving (Eq,Show) -selectFormat :: (InitPath,ModName) -> IO (ModName,(FullPath,Bool)) -selectFormat (p,f) = do +selectFormat :: ModEnv -> (InitPath,ModName) -> IO (ModName,(InitPath,CompStatus)) +selectFormat env (p,f) = do let pf = prefixPathName p f - f0 <- selectLater (gfFile pf) (gfcFile pf) - f1 <- selectLater (gfrFile pf) f0 - return $ (f, (f1, f1 == gfFile pf)) -- True if needs compile - -needCompile :: [((ModName,[ModName]),InitPath)] -> [(ModName,(FullPath,Bool))] -> - [FullPath] -needCompile deps sfiles = filt $ mark $ iter changed where + let mtenv = lookup f env -- Nothing if f is not in env + let rtenv = lookup (resModName f) env + mtgfc <- getModTime $ gfcFile pf + mtgf <- getModTime $ gfFile pf + let stat = case (rtenv,mtenv,mtgfc,mtgf) of + (Just tenv,_,_,Just tgf) | laterModTime tenv tgf -> CSEnvR + (_,Just tenv,_,Just tgf) | laterModTime tenv tgf -> CSEnv + (_,_,Just tgfc,Just tgf) | laterModTime tgfc tgf -> CSRead + _ -> CSComp + return $ (f, (p,stat)) + + +needCompile :: [ModuleHeader] -> [(ModName,(InitPath,CompStatus))] -> [FullPath] +needCompile headers sfiles0 = paths $ res $ mark $ iter changed where + + deps = [(snd m,map fst ms) | (m,ms) <- headers] + typ m = maybe MTyOther id $ lookup m [(m,t) | ((t,m),_) <- headers] + uses m = [(n,u) | ((_,n),ms) <- headers, (k,u) <- ms, k==m] + stat0 m = maybe CSComp snd $ lookup m sfiles0 + + allDeps = [(m,iterFix add ms) | (m,ms) <- deps] where + add os = [m | o <- os, Just n <- [lookup o deps],m <- n] + + -- only treat reused, interface, or instantiation if needed + sfiles = map relevant sfiles0 + relevant fp@(f,(p,st)) = + let us = uses f in + if not (all noComp us) then + fp else + if (typ f == MTyIncomplete || (not (null us) && all isAux us)) then + (f,(p,CSDont)) else + fp + + isAux = flip elem [MUReuse,MUInstance,MUComplete] . snd + noComp = flip elem [CSRead,CSEnv,CSEnvR] . stat0 . fst -- start with the changed files themselves; returns [ModName] - changed = [f | (f,(_,True)) <- sfiles] + changed = [f | (f,(_,CSComp)) <- sfiles] -- add other files that depend on some changed file; returns [ModName] - iter np = let new = [f | ((f,fs),_) <- deps, + iter np = let new = [f | (f,fs) <- deps, not (elem f np), any (flip elem np) fs] in if null new then np else (iter (new ++ np)) - -- for each module in the full list, choose source file if change is needed + -- for each module in the full list, compile if depends on what needs compile -- returns [FullPath] - mark cs = [f' | (f,(file,_)) <- sfiles, - let f' = if (elem f cs) then gfFile (fileBody file) else file] - - -- if the top file is gfc, only gfc files need be read (could be even better)--- - filt ds = if isGFC (last ds) - then [gfcFile name | f <- ds, - let (name,suff) = nameAndSuffix f, elem suff ["gfc","gfr"]] - else ds + mark cs = [(f,(path,st)) | + (f,(path,st0)) <- sfiles, + let st = if (elem f cs) then CSComp else st0] + + -- if a compilable file depends on a resource, read gfr instead of gfc/env + -- but don't read gfr if already in env (by CSEnvR) + res cs = map mkRes cs where + mkRes x@(f,(path,st)) | elem st [CSRead,CSEnv] = case typ f of + MTyResource | not (null [m | (m,(_,CSComp)) <- cs, + Just ms <- [lookup m allDeps], elem f ms]) + -> (f,(path,CSRes)) + _ -> x + mkRes x = x + + -- construct list of paths to read + paths cs = [mkName f p st | (f,(p,st)) <- cs, elem st [CSComp, CSRead,CSRes]] + + mkName f p st = mk $ prefixPathName p f where + mk = case st of + CSComp -> gfFile + CSRead -> gfcFile + CSRes -> gfrFile isGFC = (== "gfc") . fileSuffix @@ -89,11 +140,45 @@ gfcFile = suffixFile "gfc" gfrFile = suffixFile "gfr" gfFile = suffixFile "gf" --- to get imports without parsing the file +resModName = ('#':) -importsOfFile :: String -> [FilePath] +-- to get imports without parsing the whole files + +getImports :: [InitPath] -> FileName -> IOE [(ModuleHeader,InitPath)] +getImports ps = get [] where + get ds file = do + let name = fileBody file + (p,s) <- readFileIfPath ps $ file + let ((typ,mname),imps) = importsOfFile s + ioeErr $ testErr (mname == name) $ + "module name differs from file name in" +++ name + case imps of + _ | elem name (map (snd . fst . fst) ds) -> return ds --- file already read + [] -> return $ (((typ,name),[]),p):ds + _ -> do + let files = map (gfFile . fst) imps --- requires there's always .gf file + foldM get ((((typ,name),imps),p):ds) files + +-- internal module dep information + +data ModUse = + MUReuse + | MUInstance + | MUComplete + | MUOther + deriving (Eq,Show) + +data ModTyp = + MTyResource + | MTyIncomplete + | MTyOther + deriving (Eq,Show) + +type ModuleHeader = ((ModTyp,ModName),[(ModName,ModUse)]) + +importsOfFile :: String -> ModuleHeader importsOfFile = - drop 1 . -- ignore module name itself + getModuleHeader . -- analyse into mod header filter (not . spec) . -- ignore keywords and special symbols unqual . -- take away qualifiers takeWhile (not . term) . -- read until curly or semic @@ -101,14 +186,37 @@ importsOfFile = unComm -- ignore comments before the headed line where term = flip elem ["{",";"] - spec = flip elem ["of", "open","in", ":", "->", "reuse", "=", "(", ")",",","**","with", - "abstract","concrete","resource","transfer","interface","incomplete", - "instance"] + spec = flip elem ["of", "open","in",":", "->","=", "(", ")",",","**"] unqual ws = case ws of "(":q:ws' -> unqual ws' w:ws' -> w:unqual ws' _ -> ws +getModuleHeader :: [String] -> ModuleHeader -- with, reuse +getModuleHeader ws = case ws of + "incomplete":ws2 -> let ((_,name),us) = getModuleHeader ws2 in + ((MTyIncomplete,name),us) + "interface":ws2 -> let ((_,name),us) = getModuleHeader ("resource":ws2) in + ((MTyIncomplete,name),us) + + "resource":name:ws2 -> case ws2 of + "reuse":m:_ -> ((MTyResource,name),[(m,MUReuse)]) + m:"with":ms -> ((MTyResource,name),(m,MUOther):[(n,MUComplete) | n <- ms]) + ms -> ((MTyResource,name),[(n,MUOther) | n <- ms]) + + "instance":name:m:ws2 -> case ws2 of + "reuse":n:_ -> ((MTyResource,name),(m,MUInstance):[(n,MUReuse)]) + n:"with":ms -> + ((MTyResource,name),(m,MUInstance):(n,MUComplete):[(n,MUOther) | n <- ms]) + ms -> ((MTyResource,name),(m,MUInstance):[(n,MUOther) | n <- ms]) + + _:name:ws2 -> case ws2 of + "reuse":m:_ -> ((MTyOther,name),[(m,MUReuse)]) + m:n:"with":ms -> + ((MTyOther,name),(m,MUInstance):(n,MUOther):[(n,MUComplete) | n <- ms]) + m:"with":ms -> ((MTyOther,name),(m,MUOther):[(n,MUComplete) | n <- ms]) + ms -> ((MTyOther,name),[(n,MUOther) | n <- ms]) + unComm s = case s of '-':'-':cs -> unComm $ dropWhile (/='\n') cs '{':'-':cs -> dpComm cs diff --git a/src/GF/Infra/UseIO.hs b/src/GF/Infra/UseIO.hs index bd9d9e22a..347af2adb 100644 --- a/src/GF/Infra/UseIO.hs +++ b/src/GF/Infra/UseIO.hs @@ -57,6 +57,10 @@ readFileIf f = catch (readFile f) (\_ -> reportOn f) where putStrLnFlush ("File " ++ f ++ " does not exist. Returned empty string") return "" +type FileName = String +type InitPath = String +type FullPath = String + getFilePath :: [FilePath] -> String -> IO (Maybe FilePath) getFilePath paths file = get paths where get [] = putStrLnFlush ("file" +++ file +++ "not found") >> return Nothing @@ -104,6 +108,9 @@ justFileName = reverse . takeWhile (/='/') . reverse suffixFile :: String -> FilePath -> FilePath suffixFile suff file = file ++ "." ++ suff +justModuleName :: FilePath -> String +justModuleName = fileBody . justFileName + -- getLineWell :: IO String -> IO String |
