{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE FlexibleContexts #-} module Development.Cake3.Rules.UrWeb( urweb , Config(..) , defaultConfig , urdeps ) where import Data.Monoid import Data.List import Data.Set (member) import Control.Applicative import Control.Monad.Trans import Control.Monad.State import System.Directory import System.IO as IO import System.FilePath.Wrapper import Development.Cake3 import Development.Cake3.Types import Development.Cake3.Monad as C3 splitWhen c l = (h,if null t then [] else tail t) where (h,t) = span (/=c) l -- | URP file parser. Takes file name and 2 callbacks: one for header lines and -- one for source lines urpparse :: (Monad m) => String -> ((String,String) -> m ()) -> (FilePath -> m ()) -> m () urpparse inp hact sact = do parseline False (lines inp) where parseline _ [] = return () parseline False (l:ls) | (unwords (words l)) == [] = parseline True ls | otherwise = hact (splitWhen ' ' l) >> parseline False ls parseline True (l:ls) = sact l >> parseline True ls data Config = Config { urObjRule :: File -> Rule , urInclude :: Variable , urEmbed :: [(String, [File])] } defaultConfig = Config { urObjRule = \f -> rule f (fail "urobj: not set") , urInclude = makevar "UR_INCLUDE_DIR" "/usr/local/include/urweb" , urEmbed = [] } -- | Helper function, parses dependencies of an *urp urdeps :: Config -> File -> A () urdeps cfg f = do ps <- prerequisites let check = msum [ lookup (unpack (takeBaseName f)) (urEmbed cfg) , lookup (unpack (takeFileName f)) (urEmbed cfg) ] case check of Just embeddable -> do depend (urembed cfg f embeddable) Nothing -> do depend f inp <- C3.readFile f urpparse inp lib src where relative x = takeDirectory f (fromFilePath x) lib (h,x) | (h=="library") = do let nested = relative x isdir <- liftIO $ doesDirectoryExist (unpack nested) case isdir of True -> urdeps cfg (nested (fromFilePath "lib.urp")) False -> urdeps cfg (nested .= "urp") | (h=="ffi") = do depend ((relative x) .= "urs") | (h=="include") = do depend (relative x) | (h=="link") = do depend (urObjRule cfg (relative x)) | otherwise = return () src d@(c:_) | (c/='$') = do depend ((relative d) .= "ur" :: File) let urs = (relative d) .= "urs" e <- liftIO $ doesFileExist (toFilePath urs) when e $ do depend ((relative d) .= "urs" :: File) | otherwise = return () src _ = return () -- | Search for @sect@ in the urp file's header. urpline :: String -> File -> String -> IO File urpline sect f c = flip execStateT mempty $ urpparse c lib (const $ return ()) where relative x = takeDirectory f (fromFilePath x) lib (n,x) | n == sect = put (relative x) | otherwise = return () -- | Search for section @sect@ in the urp file's database line -- FIXME: actually supports only 'databse dbname=XXX' format urpdb :: String -> File -> String -> IO File urpdb dbsect f c = flip execStateT mempty $ urpparse c lib (const $ return ()) where relative x = takeDirectory f (fromFilePath x) lib (n,x) | n == "database" = put (relative $ snd (splitWhen '=' x)) | otherwise = return () -- | Get executable name of an URP project urpexe :: File -> File urpexe f = f .= "exe" -- | Take the URP file and the build action. Provide three aliases: one for -- executable, one for SQL-file and one for database file -- -- FIXME: Rewrite in urembed style: fill urweb_cmd and pass it back to the user urweb :: Config -> File -> A() -> IO (Alias, Alias, Alias) urweb cfg f act = do c <- liftIO (IO.readFile $ unpack f) exe <- return (urpexe f) sql <- urpline "sql" f c db <- urpdb "name" f c ruleM (exe,sql,db) (act >> urdeps cfg f) -- | Generate Ur/Web project file @urp@ providing embedded files @files@ -- FIXME: Generate unique variable name instead of URGCC -- FIXME: implement variable dependency urembed :: Config -> File -> [File] -> Alias urembed cfg urp files = let dir = takeDirectory urp makefile = (dir takeBaseName urp) .= "mk" dir_ = string dir urp_ = string urp mf_ = string (takeFileName makefile) in rule urp $ do let urcc = makevar "URCC" "$(shell urweb -print-ccompiler)" let gcc = makevar "CC" "$(shell $(URCC) -print-prog-name=gcc)" let ld = makevar "LD" "$(shell $(URCC) -print-prog-name=ld)" let mf = rule makefile $ do -- FIXME: strange recursion occures if uncomment -- depend urp shell [cmd|mkdir -pv $(dir_)|] shell [cmd|urembed -o $(urp_) $files|] depend urcc depend mf shell [cmd|$(extvar "MAKE") -C $(dir_) -f $(mf_) CC=$gcc LD=$ld UR_INCLUDE_DIR=$(urInclude cfg) urp|]