Search code examples
haskellshake-build-system

Shake does not recognize wether to build a rule/always builds a rule even if it shouldn't


I have a project where I use shake to generate a bunch of reports based on input-files, and run as a restful webservice. One reason why I chose Shake is that is should not rebuild anything if input-files have not changed (e.g. based on Digest, configurable via shakeOptions).

I noticed that the processes would run even if I had just built them and no file change had happened in between runs. So I tried a simple example it should simulate the process of one of my builds

import           Control.Monad.IO.Class
import           Development.Shake

wanted = "foo.3"

main = shake shakeOptions $ do
  want [wanted]                                       -- (1)
  "*.1" %> \out -> do                                 -- (2)
    liftIO $ putStrLn out
    unit $ cmd Shell "touch" "foo.2"

  "*.2" %> \out -> do need [out]                      -- (3)
                      unit $ cmd "touch" wanted

  action $ unit $ cmd Shell "touch" wanted            -- (4)

while the %> rules never run - the action rule is always done.

If I comment out (4) and create a file "foo.1" manually - the rule (1) is failing because it does not figure out that (2) -> (3) can build the result.

If on the other hand I comment out (2) and (3) and leave (4) - the rule runs even if "foo.3" exists and has been "built" by shake before.

I am using shake-0.15.11 in combination with shake - using shake runhaskell Main.hs --package shake or shake exec -- testshake does not make any difference. Compiler is GHC-8.0.2. All of this is done on a Linux (Mint).

I read How does Shake decide whether to rebuild a target? - from which I gathered that shake rebuilds stuff only if either the input-file(s) or the output-file change (where change is determined by the shakeOptions configuration)


Solution

  • First of all, pat %> act mean that you create the file-build-rule, which will be run for any file path witch will be accepted with the pattern pat. It's mean that if some one call need [foo] and pat ?== foo then will be run the action act with argument foo. So, the action act should to try build foo.

    Secondly, about action, as wrote in the documentation:

    The action will be run in every build execution (unless withoutActions is used).

    And wand defined like this: want = action . need.

    For example, I redesign your example with some assumptions about dependencies between your files. Namely, foo.3 need foo.2, foo.2 need foo.1 and foo.1 already exists (it's source).

    wanted = "foo.3"
    main = shake shakeOptions $ do
        want [wanted]
    
        "*.3" %> \out -> do                      
            putNormal out
            need [out -<.> ".2"]
            unit $ cmd Shell "touch" out
    
        "*.2" %> \out -> do
            putNormal out
            need [out -<.> ".1"]
            unit $ cmd Shell "touch" out