I have a Haskell program that I want to compile with GHC, orchestrated by the Shake build system. Which commands should I execute, and under what circumstances should they be rerun?
There are two approaches to doing the compilation, and two approaches to getting the dependencies. You need to pick one from each set (all 4 combinations make sense), to come up with a combined approach.
Compilation
You can either:
ghc -c
on each file in turn, depending on the .hs
file and any .hi
files it transitively imports, generating both a .hi
and .o
file. At the end, call ghc -o
depending on all the .o
files. For actual code see this example.ghc --make
once, depending on all .hs
files. For actual code see this example.The advantage of ghc --make
is that it is faster than multiple calls to ghc -c
since GHC can load each .hi
file only once, instead of once per command. Typically the speedup is 3x. The disadvantage is parallelism is harder (you can use -j
to ghc --make
, but Shake still assumes each action consumes one CPU), and that two ghc --make
compilations can't both run at the same time if they overlap on any dependencies.
Dependencies
You can either:
import
statements (and perhaps #include
statements) following a coding convention, or use a library such as haskell-src-exts
. For actual code with a very approximate import
parser see this example.ghc -M
to detect the dependencies, which can be parsed using the Shake helper function parseMakefile
. For actual code see this example.The advantage of parsing the Haskell files is that it is possible to have generated Haskell files and it can be much quicker. The advantage of using ghc -M
is that it is easier to support all GHC features.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With