The basic task of the ghc driver is to run each input file through the right phases (compiling, linking, etc.).
The first phase to run is determined by the input-file suffix, and the last phase is determined by a flag. If no relevant flag is present, then go all the way through linking. This table summarises:
|Phase of the compilation system||Suffix saying “start here”||Flag saying “stop after”||(suffix of) output file|
|C pre-processor (opt.)||-||-||-|
|Haskell compiler||.hs||-C, -S||.hc, .s|
|C compiler (opt.)||.hc or .c||-S||.s|
Thus, a common invocation would be: ghc -c Foo.hs
Note: What the Haskell compiler proper produces depends on whether a native-code generator is used (producing assembly language) or not (producing C).
The option -cpp must be given for the C pre-processor phase to be run, that is, the pre-processor will be run over your Haskell source file before continuing.
The option -E runs just the pre-processing passes of the compiler, outputting the result on stdout before stopping. If used in conjunction with -cpp, the output is the code blocks of the original (literal) source after having put it through the grinder that is the C pre-processor. Sans -cpp, the output is the de-litted version of the original source.
The option -optcpp-E runs just the pre-processing stage of the C-compiling phase, sending the result to stdout. (For debugging or obfuscation contests, usually.)