2

How can pass parameters in shake and then use them in our rules? I would like to make some equivalents of:

make ARCH=x86_64
or
make DEBUG=YES etc...

For now i've been using enviroment variables and getEnv function to simulate these.
How can i make a dependency on a given parameter (environment variable) so that it can be read once in a build, store it somewhere and access it from multiple rules?

e.g. For now i am doing getEnv multiple times in multiple rules but during compilation time the enviroment variable can change. Also for example a debug flag can alter both compiler and linker flags meaning that the info would be needed to be available in both link and compile output rules.

Fr0stBit
  • 1,455
  • 1
  • 13
  • 22
  • "but during compilation time the enviroment variable can change" - can you clarify? Environment variables are captured at the start of a process, so won't change during a run, unless you call something like `setEnv`. – Neil Mitchell Oct 06 '15 at 13:48
  • The other question is do you want changing `DEBUG=YES` to `DEBUG=NO` to change all rules that depended on that setting (rebuilding most of your project), or just ignore the change, or build to a different directory? – Neil Mitchell Oct 06 '15 at 13:49
  • On the first comment: Well thats something i forgot about so scratch that. On the second comment: Yes i want to change all the rules that depend on that setting to change. For example, the compile/link flags will change and the output directories too. – Fr0stBit Oct 06 '15 at 14:33

1 Answers1

1

Your existing approach of using environment variables should work, and since environment variables are already tracked by Shake, one approach is to parse DEBUG=YES and turn it into an environment variable. For example:

main = shakeArgsWith shakeOptions [] $ \_ args -> do
    let (vars,files) = partition ('=' `elem`) args
    forM_ vars $ \v -> let (a,'=':b) = break (== '=') v in setEnv a b
    return $ Just $ if null files then rules else want files >> withoutActions rules

rules :: Rules ()
rules = ...

Since environment variables are local to a process (and their child processes), this will probably work fine.

Of course, storing such information in the environment variables is a bit ugly. You can create your own oracle to store the information instead:

main = shakeArgsWith shakeOptions [] $ \_ args -> do
    let (vars,files) = partition ('=' `elem`) args
    let vars2 = [(a,b) | v <- vars, let (a,'=':b) = break (== '=') v]
    return $ Just $ do
        global <- addOracle $ \(Global x) -> return $ lookup x vars2
        if null files then rules global else want files >> withoutActions (rules global)

newtype Global = Global String deriving (Show,Typeable,Eq,Hashable,Binary,NFData)

rules :: (Global -> Action (Maybe String)) -> Rules ()
rules global = undefined

Now, instead of writing the information into an environment variable with setEnv, we store it in an oracle with addOracle, which will still be tracked and not conflict with any other environment-variable pieces.

Neil Mitchell
  • 9,090
  • 1
  • 27
  • 85
  • Fantastic solutions! The shakeArgsWith entrypoint is everything i wanted. The oracle feature seems pretty neat too, but its kinda hard to understand from the documentation. I'll give it a shot though :P! Thanks for answering! – Fr0stBit Oct 07 '15 at 16:08
  • The best docs are at https://hackage.haskell.org/package/shake/docs/Development-Shake.html#v:addOracle. They are one of the more complex aspects of Shake though. – Neil Mitchell Oct 07 '15 at 22:16