I'm working on a Haskell library that contains parts that target WebAssembly (WASM) using Asterius. These parts can't be compiled with the normal ghc
and for that reason we have flags that exclude/include the WASM parts.
Trying to build the documentation with Asterius' ahc-cabal new-haddock
fails. It seems to revert to the normal ghc
for the Haddock command.
My question is: Can I build the Haddock documentation without compiling the source it describes?
Excerpts of my file that might be relevante:
Section of my cabal file:
flag wasm
description: Eanbles builds targeting WASM.
default: False
manual: True
library
exposed-modules: Boardgame
build-depends: base >= 4.12 && < 5.0
if flag(wasm)
exposed-modules: Boardgame.Web
build-depends:
aeson >= 1.4 && < 1.6
, asterius-prelude == 0.0.1
, scientific >= 0.3 && < 0.4
CPP-options: "-DWASM"
Section of Boardgame.hs
:
#ifdef WASM
import Data.Aeson (ToJSON(toJSON), Value(Number, Null))
import Data.Scientific (fromFloatDigits)
#endif
-- | Represents one of the two players.
data Player = Player1 | Player2
deriving (Show, Eq)
#ifdef WASM
instance ToJSON Player where
toJSON = Number . fromFloatDigits . fromIntegral . playerToInt
#endif
Section of Boardgame/Web.hs
:
module Boardgame.Web (
addWebGame
) where
foreign import javascript "wrapper" jsMakeCallback :: IO () -> IO JSVal
foreign import javascript "boardgame.games[$1] = $2" jsSetGame :: JSVal -> JSVal -> IO ()
-- | Adds a named game to the list of games accessible from JavaScript.
addWebGame :: (ToJSON a, ToJSON c, FromJSON c, PositionalGame a c) => String -> a -> IO ()
addWebGame name startState = do
callback <- jsMakeCallback $ playWeb startState
jsSetGame (jsonToJSVal name) callback
My main file (Boardgame.hs
) contains code "hidden" behind the WASM flag but no of code has any documentation. Boardgame/Web.hs
is only included when the WASM flag is specified and has some functions with documentation.
I want to build the documentation for all documented functions in both Boardgame.hs
and Boardgame/Web.hs
.
Instead of excluding your module Boardgame/Web.hs
, which contains the imports that GHC can't process, entirely when the WASM flag is set, you could use CPP in Boardgame/Web.hs
to conditionally set all non-GHC-compatible symbols to undefined
.
The way I would do it is to move all type signatures to the top of the module, and make two sets of definitions, like so:
module Boardgame.Web (
addWebGame
) where
addWebGame :: (ToJSON a, ToJSON c, FromJSON c, PositionalGame a c) => String -> a -> IO ()
-- other signatures...
#ifdef WASM
foreign import javascript "wrapper" jsMakeCallback :: IO () -> IO JSVal
foreign import javascript "boardgame.games[$1] = $2" jsSetGame :: JSVal -> JSVal -> IO ()
addWebGame name startState = do
callback <- jsMakeCallback $ playWeb startState
jsSetGame (jsonToJSVal name) callback
-- other definitions...
#else
addWebGame = undefined
#endif
Boardgame.Web
should then be imported unconditionally.
This should build with GHC. It will obviously error without doing anything useful if run, but it's enough to let cabal/haddock extract the documentation.
The downside is changing the way you structure your code.
I'm sure this can be automated with something like an awk script, but it's beyond me.
Alternatively, if it's just the FFI declarations that are the problem, you could do this much simpler (and more automably):
module Boardgame.Web (
addWebGame
) where
#ifdef WASM
foreign import javascript "wrapper" jsMakeCallback :: IO () -> IO JSVal
foreign import javascript "boardgame.games[$1] = $2" jsSetGame :: JSVal -> JSVal -> IO ()
#else
jsMakeCallback = undefined
jsSetGame = undefined
#endif
-- addWebGame and others unmodified.
To get you started with automating, a command like cat Boardgame/Web.hs | sed "s/foreign import javascript \"[^\"]*\" \([^ ]*\).*/\1 = undefined/"
will extract a list of javascript imports and convert them into undefined assignments.
Happy Haskelling!