KiCS-prophecy-0.1.0: Curry/Module/Make.curry
-------------------------------------------------------
-- This module provides basic make functionality for
-- curry programs. The provided actions traverse the
-- import tree and execute a given action only if
-- necessary.
-------------------------------------------------------
module Make (
ModuleName,
Path,
FileName,
make, makeWithFrontendParams, obsolete, unless,
parseArgs,Parameter,
quiet,force,modulename,output) where
import FlatCurry
import FlatCurryGoodies (progImports)
import Distribution as D
import FiniteMap
import IOExts
import Sort (leqString)
import FileGoodies
import Time
import Directory
import System (getArgs)
type ModuleName = String
type Path = String
type FileName = String
type TestAct a = Path -> ModuleName -> IO (Maybe a)
type ProgAct a = Path -> [a] -> Prog -> IO a
type Done a = IORef (FM String a)
data Parameter = Parameter Bool Bool (Maybe String) (Maybe String) String
defaults :: Parameter
defaults = Parameter False False Nothing Nothing ""
type Getter a = Parameter -> a
type Setter a = a -> Parameter -> Parameter
quiet :: Getter Bool
quiet (Parameter x _ _ _ _) = x
setQuiet :: Setter Bool
setQuiet x (Parameter _ y z e m) = Parameter x y z e m
force :: Getter Bool
force (Parameter _ x _ _ _) = x
setForce :: Setter Bool
setForce x (Parameter y _ z e m) = Parameter y x z e m
output :: Getter (Maybe String)
output (Parameter _ _ x _ _) = x
setOutput :: Setter (Maybe String)
setOutput x (Parameter y z _ e m) = Parameter y z x e m
main :: Getter (Maybe String)
main (Parameter _ _ _ x _) = x
setMain :: Setter (Maybe String)
setMain x (Parameter y z m _ e) = Parameter y z m x e
modulename :: Getter String
modulename (Parameter _ _ _ _ x) = x
setModulename :: Setter String
setModulename x (Parameter y z m e _) = Parameter y z m e x
parseArgs :: IO Parameter
parseArgs = getArgs >>= return . parse defaults
where
parse _ [] = usage
parse p (x:xs)
| x=="-q" = parse (setQuiet True p) xs
| x=="--quiet" = parse (setQuiet True p) xs
| x=="-f" = parse (setForce True p) xs
| x=="--force" = parse (setForce True p) xs
| x=="-o" = case xs of
o:xs' -> parse (setOutput (Just o) p) xs'
| x=="-m" = case xs of
o:xs' -> parse (setMain (Just o) p) xs'
| null xs = setModulename x p
usage = error "usage: <-f/--force> <-q/--quiet> <-o outputdir> modulename"
--- calls act on each imported module transitively
--- if test returns Nothing.
make :: Bool -> ModuleName -> TestAct a -> ProgAct a -> IO ()
make = makeWithFrontendParams defaultParams
makeWithFrontendParams :: FrontendParams -> Bool -> ModuleName -> TestAct a -> ProgAct a -> IO ()
makeWithFrontendParams givenParams qu file test act = do
let (dir,modu) = splitDirectoryBaseName file
path <- maybe getLoadPath return (fullPath givenParams)
let loadPath = if dir=="." then path else dir:path
params = setFullPath loadPath givenParams
mCurryFile <- lookupFileInPath modu [".curry",".lcurry"] loadPath
unless (mCurryFile==Nothing)
(do
unless qu $ putStrLn "ensuring existence of fcy/fint files..."
callFrontendWithParams FCY (D.setQuiet True params) modu
unless qu $ putStrLn "...ensured")
done <- newIORef (emptyFM (\ x y -> not (leqString x y)))
workUpDependence loadPath done test act modu
return ()
workUpDependence :: [String] -> Done a -> TestAct a -> ProgAct a -> ModuleName -> IO a
workUpDependence path done test act modu = do
fm <- readIORef done
maybe (process path done test act modu) return (lookupFM fm modu)
process :: [String] -> Done a -> TestAct a -> ProgAct a -> ModuleName -> IO a
process path done test act modu = do
fn <- getFileInPath (modu++".fcy") [""] path
imps <- fastReadImports fn >>= mapIO (workUpDependence path done test act)
let dir = dirName fn++"/"
result <- test dir modu >>=
maybe (readFlatCurryFile fn >>= act dir imps) return
updateIORef done (\fm -> addToFM fm modu result)
return result
--- a standard test if a given file is newer than a list of other files
--- if other files do not exist, the given file is assumed to be up-to-date
--- on up-to-date files a given action is performed
obsolete :: Bool -> (String -> String -> String) -> [String -> String]
-> ([String] -> IO a) -> TestAct a
obsolete qu f fs action dir modu = do
let fn = f dir modu
fns = map ((dir++).($modu)) fs
ex <- doesFileExist fn
if ex then do
t <- getModificationTime fn
ns <- mapIO (isNewerThan t) fns
if or ns
then do
unless qu $ putStrLn $ "obsolete : " ++ fn
return Nothing
else do
unless qu $ putStrLn $ "up-to-date: " ++ fn
action fns >>= return . Just
else do unless qu $ putStrLn ("missing : "++ fn)
return Nothing
where
isNewerThan t file = do
ex <- doesFileExist file
if not ex then return False else do
t' <- getModificationTime file
return (compareClockTime t t'/=GT)
fastReadImports :: FileName -> IO [String]
fastReadImports fn = do
cont <- readFile fn
return (strings (takeWhile (/=']') (dropWhile (/='[') cont)))
strings :: String -> [String]
strings [] = []
strings (c:cs) | c=='"' = case break (=='"') cs of
(s,_:rest) -> s : strings rest
| otherwise = strings cs
updateIORef :: IORef a -> (a -> a) -> IO ()
updateIORef ref f = do
x <- readIORef ref
writeIORef ref (f x)
unless :: Bool -> IO () -> IO ()
unless True _ = return ()
unless False act = act