Skip to content

Instantly share code, notes, and snippets.

@mzero
Created July 20, 2014 17:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mzero/d4ba11c567977111749a to your computer and use it in GitHub Desktop.
Save mzero/d4ba11c567977111749a to your computer and use it in GitHub Desktop.
Root cause of haddock / clang failure

Root cause of haddock / clang failure

Consider this simple .hs file:

module MinHaddockFail where

yo :: String
yo = "yo"

And the .cabal file:

Name:            min-haddock-fail
Version:         0.1.0.0
Build-type:      Simple
Cabal-version:   >=1.8

Library
  Exposed-modules: MinHaddockFail
  Build-depends: base == 4.*
  Extensions: CPP

The Extensions directive in the .cabal file will cause all .hs files to be pre-processed. The pre-processed output looks pretty much the same under gcc (extra blank lines ellided):

{-# LINE 1 "MinHaddockFail.hs" #-}
# 1 "MinHaddockFail.hs"
# 1 "<built-in>"
# 1 "<command-line>"
# 13 "<command-line>"
# 1 "./dist/build/autogen/cabal_macros.h" 1

# 13 "<command-line>" 2
# 1 "MinHaddockFail.hs"
module MinHaddockFail where

yo :: String
yo = "yo"

or clang:

{-# LINE 1 "MinHaddockFail.hs" #-}
# 1 "MinHaddockFail.hs"
# 1 "<built-in>" 1
# 19 "<built-in>"
# 1 "dist/build/autogen/cabal_macros.h" 1

# 20 "<built-in>" 2
# 1 "MinHaddockFail.hs" 2
module MinHaddockFail where

yo :: String
yo = "yo"

When generating haddock cabal does a bad thing: It pre-processes the .hs file with ghc before handing it to haddock, but then it adds --optghc=-XCPP to the haddock command line as well. This causes the pre-processed output to be pre-processed again. This is almost certainly wrong: pre-processing is not defined to be idempotent.

The output, under either compiler is indeed a bit of a mess. gcc's output is:

# 1 "dist/build/tmp-5926/MinHaddockFail.hs"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "dist/build/tmp-5926/MinHaddockFail.hs"
{-# LINE 1 "MinHaddockFail.hs" #-}
# 1 "MinHaddockFail.hs"
# 1 "<built-in>"
# 1 "<command-line>"
# 13 "<command-line>"

# 1 "./dist/build/autogen/cabal_macros.h" 1
# 13 "<command-line>" 2
# 1 "MinHaddockFail.hs"
module MinHaddockFail where

yo :: String
yo = "yo"

The line markers from the first pass have been passed through blind into the second. This is inoccuous for the purposes of haddock (though may be a source of incorrect line numbers).

clang's output in this situation is slightly more rigorous:

# 1 "dist/build/tmp-88937/MinHaddockFail.hs"
# 1 "<built-in>" 1
# 1 "dist/build/tmp-88937/MinHaddockFail.hs" 2
{-# LINE 1 "MinHaddockFail.hs" #-}
 # 1 "MinHaddockFail.hs"
 # 1 "<built-in>" 1
 # 19 "<built-in>"
 # 1 "dist/build/autogen/cabal_macros.h" 1

# 79 "dist/build/tmp-88937/MinHaddockFail.hs"
 # 20 "<built-in>" 2
 # 1 "MinHaddockFail.hs" 2
module MinHaddockFail where

yo :: String
yo = "yo"

It attempts to shield the exsiting linemarkers from the new ones by adding a space to them. ghc correctly does not parse those lines as line-markers, and then those lines cause parse errors.

Cabal's Extensions directive to blame

If the Extensions directive is removed from the .cabal file, then it all works fine because cabal doesn't attempt to pre-process the file before handing it off to haddock. It even works if the file itself has the pragma:

{-# LANGUAGE CPP #-}

With that (and with the Extensions directive) the file will correctly compile and haddock, being prep-rocessed exactly once.

Work Around

The only work around is to add -optP-P to ghc during the first pre-processing step. This can be done while invoking cabal haddock:

cabal haddock --ghc-option=-optP-P

However, there is no way to configure cabal to add this option all the time when running haddock: The new program-default-options directive lets one set the options for haddock itself, but not the ghc pre-processing step before haddock is called.

One could add -optP-P to the ghc-options directive. But then all compilations would lose accurate line number tracking. This would apply to all compilations even those that don't use CPP because once the Extensions directive includes CPP, all files are pre-processed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment