Search code examples
shake-build-system

Generating Shake Rules from Actions


I would like find all of the 'Makefiles' in my project and then generate Rules based them. However, it looks like (purposely) nothing can escape an Action. So, Instead I lifted my 'Makefile' scanner to the IO operation before the shake function.

main = do
  makefiles <- findMakefiles
  shake skakeOptions $ generate_rules makefiles

Is there a better way?


Solution

  • Firstly, I don't think there's anything wrong with the current approach. The Shake package itself uses a similar approach for the built in Ninja and Makefile interpreters, see details about the Makefile interpreter or the Sake interpreter. As long as processing the Makefile is sufficiently quick not to matter (which it usually is) then you are basically writing an interpreter for your custom build syntax using Shake, which works nicely.

    There are a couple of reasons to avoid that approach, for example, if the processing of the Makefile is expensive, or if the Makefile itself has other dependencies. In that situation you can use a combination of newCache to parse the Makefile once, and addOracle so rules can depend on the subset of the Makefile they require. However, that approach is tricker to get right, since it basically forces a layer of indirection, and you have to make some assumptions about how generate_rules works. The ghc-make package uses this technique (see the "makefile" rule and call to newCache), and the usingConfigFile function from the Shake package uses the same newCache/addOracle pattern.

    Sticking with the original approach, there is somewhere slightly different you can put your findMakefiles step. You can do:

    main = shakeArgsWith shakeOptions [] $ \_ -> xs -> do
        makefiles <- findMakefiles
        let rules = generate_rules makefiles
        if null xs then rules else withoutActions rules >> want xs
    

    This has the advantage that you can easily add command line flags to figure out where to find the Makefile, or options for processing it. Both the Ninja and Makefile systems in Shake support that, using --makefile=FILE to override the default. Even if you don't want to move generate_rules, you might want to replace shake with shakeArgs in your original example, which will provide you with command line handling.