2

In my Makefiles, I prefer having the output directory defined by a environment variable rather than hard-coded (with some reasonable default value if its unset). For example, a Make rule would look like

$(OUTPUT_DIR)/some_file: deps
     #build commands

I have yet to figure out how to achieve a similar goal in Shake. I like using getEnvWithDefault to grab the value of the environment variable or a reasonable default, but no amount of bashing it with binds or lambdas have allowed me to combine it with (*>).

How might it be possible to interpolate an environment variable in a FilePattern for use with (*>)?

Lane
  • 23
  • 2

1 Answers1

1

The function getEnvWithDefault runs in the Action monad, and the name of the rule has to be supplied in a context where you cannot access the Action monad, so you can't translate this pattern the way you tried. There are a few alternatives:

Option 1: Use lookupEnv before calling shake

To exactly match the behaviour of Make you can write:

main = do
    outputDir <- fromMaybe "output" <$> lookupEnv "OUTPUT_DIR"
    shakeArgs shakeOptions $ do
        (outputDir </> "some_file") *> \out -> do
             need deps
             -- build commands

Here we use the lookupEnv function (from System.Environment) to grab the environment variable before we start running Shake. We can then define a file that precisely matches the environment variable.

Option 2: Don't force the output in the rule

Alternatively, we can define a rule that builds some_file regardless of what directory it is in, and then use the tracked getEnvWithDefault when we say which file we want to build:

main = shakeArgs shakeOptions $ do
    "//some_file" *> \out -> do
        need deps
        -- build commands
    action $ do
        out <- getEnvWithDefault "OUTPUT_DIR"
        need [out </> "some_file"]

Here the rule pattern can build anything, and the caller picks what the output should be. I prefer this variant, but there is a small risk that if the some_file pattern overlaps in some way you might get name clashes. Introducing a unique name, so all outputs are named something like $OUTPUT_DIR/my_outputs/some_file eliminates that risk, but is usually unnecessary.

Neil Mitchell
  • 9,090
  • 1
  • 27
  • 85
  • Spot on! I was too narrowly focused on the functions in Shake to think about just grabbing the environment variable as you would in any other Haskell program. I like the second option, and appreciate the tip with the name clashes. One of my rules copies .md files to the output directory, so adding the unique name prevents recursive rules. Thanks for the thorough answer! – Lane Sep 08 '14 at 16:04