1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}
-- | A module for parsing and using config files in a Shake build system. Config files
-- consist of variable bindings, for example:
--
-- > # This is my Config file
-- > HEADERS_DIR = /path/to/dir
-- > CFLAGS = -g -I${HEADERS_DIR}
-- > CFLAGS = $CFLAGS -O2
-- > include extra/file.cfg
--
-- This defines the variable @HEADERS_DIR@ (equal to @\/path\/to\/dir@), and
-- @CFLAGS@ (equal to @-g -I\/path\/to\/dir -O2@), and also includes the configuration
-- statements in the file @extra/file.cfg@. The full lexical syntax for configuration
-- files is defined here: <http://martine.github.io/ninja/manual.html#_lexical_syntax>.
--
-- To use the configuration file either use 'readConfigFile' to parse the configuration file
-- and use the values directly, or 'usingConfigFile' and 'getConfig' to track the configuration
-- values, so they become build dependencies.
module Development.Shake.Config(
readConfigFile,
usingConfigFile, usingConfig,
getConfig
) where
import Development.Shake
import Development.Shake.Classes
import qualified Development.Ninja.Parse as Ninja
import qualified Development.Ninja.Env as Ninja
import qualified Data.HashMap.Strict as Map
import qualified Data.ByteString.UTF8 as UTF8
import Control.Applicative
import Control.Arrow
-- | Read a config file, returning a list of the variables and their bindings.
-- Config files use the Ninja lexical syntax:
-- <http://martine.github.io/ninja/manual.html#_lexical_syntax>
readConfigFile :: FilePath -> IO (Map.HashMap String String)
readConfigFile file = do
env <- Ninja.newEnv
Ninja.parse file env
mp <- Ninja.fromEnv env
return $ Map.fromList $ map (UTF8.toString *** UTF8.toString) $ Map.toList mp
newtype Config = Config String deriving (Show,Typeable,Eq,Hashable,Binary,NFData)
-- | Specify the file to use with 'getConfig'.
usingConfigFile :: FilePath -> Rules ()
usingConfigFile file = do
mp <- newCache $ \() -> liftIO $ readConfigFile file
addOracle $ \(Config x) -> Map.lookup x <$> mp ()
return ()
-- | Specify the values to use with 'getConfig', generally prefer
-- 'usingConfigFile' unless you also need access to the values
-- of variables outside 'Action'.
usingConfig :: Map.HashMap String String -> Rules ()
usingConfig mp = do
addOracle $ \(Config x) -> return $ Map.lookup x mp
return ()
-- | Obtain the value of a configuration variable, returns 'Nothing' to indicate the variable
-- has no binding. Any build system using 'getConfig' /must/ call either 'usingConfigFile'
-- or 'usingConfig'. The 'getConfig' function will introduce a dependency on the configuration
-- variable (but not the whole configuration file), and if the configuration variable changes, the rule will be rerun.
-- As an example:
--
-- @
-- 'usingConfigFile' \"myconfiguration.cfg\"
-- \"*.o\" '*>' \\out -> do
-- cflags <- 'getConfig' \"CFLAGS\"
-- 'cmd' \"gcc\" [out '-<.>' \"c\"] (fromMaybe \"\" cflags)
-- @
getConfig :: String -> Action (Maybe String)
getConfig = askOracle . Config
|