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?
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.