The encoding package uses HaXml in its build script (in Setup.hs
). It happens to use bits of the interface that changed between HaXml-1.19 and HaXml-1.22. It would be nice if the encoding package were able to build with either version. I tried using the usual Cabal trick, namely, doing something like
{-# LANGUAGE CPP #-}
#if MIN_VERSION_HaXml(1,22,0)
-- HaXml-1.22 code
#else
-- HaXml-1.19 code
#endif
...but the magic defines can't exist before the package is configured, and this file is being built to make the configure step possible. What are my options? Is there a way to change the command that cabal-install calls to compile Setup.hs
? Is there another mechanism for conditionally selecting code that sidesteps cabal?
The Data.Data
interface is capable (just about!) of constructing and deconstructing values of a type that may or may not exist. Unfortunately, HaXml doesn't appear to have Data
instances for its types, and you can't define one since you can't refer to the type that might or might not exist, so we have to resort to Template Haskell:
The following module exports qnameCompat
:
{-# LANGUAGE TemplateHaskell #-}
module HaXmlCompat (qnameCompat) where
import Language.Haskell.TH
qnameCompat :: Q [Dec]
qnameCompat = do
mi <- maybeReify "N"
case mi of
Nothing -> sequence [
tySynD (mkName "QName") [] [t| String |],
valD [p| toQName |] (normalB [| id |]) [],
valD [p| fromQName |] (normalB [| Just |]) []]
Just (DataConI n _ _ _) -> do
s <- newName "s"
sequence [
valD [p| toQName |] (normalB (conE n)) [],
funD (mkName "fromQName") [
clause [conP n [varP s]] (normalB (appE [| Just |] (varE s))) [],
clause [ [p| _ |] ] (normalB [| Nothing |]) []]]
Just i -> fail $
"N exists, but isn't the sort of thing I expected: " ++ show i
maybeReify :: String -> Q (Maybe Info)
maybeReify = recover (return Nothing) . fmap Just . reify . mkName
When spliced at the top level using Template Haskell, qnameCompat
will check if N
exists. If it does, it produces the following code:
toQName = N
fromQName (N s) = Just s
fromQName _ = Nothing
If it doesn't, the following is produced:
type QName = String
toQName = id
fromQName = Just
Now you can create and deconstruct Element
s, e.g. using the ViewPatterns extension:
myElt :: String -> Element i
myElt = Elem (toQName "elemName") [] []
eltName :: Element i -> String
eltName (Elem (fromQName -> Just n) _ _) = n
ViewPatterns is convenient, but not essential, of course: using ordinary pattern matching on the result of fromQName
will work just as well.
(These ideas are what led me to develop the notcpp package, which includes maybeReify
and some other useful utilities)
There don't seem to be very many knobs in cabal-install/Distribution/Client/SetupWrapper.hs
controlling the compilation of Setup.hs
, so your best bet may be to create a stub Setup.hs file which performs the version test, and then hands off to real Setup.hs
once it has figured out what the version is.
Another trick is to make a compatibility shim library which your Setup script uses, which has the appropriate version tricks.
But maybe the real question to ask, is this: why is Setup.hs
using external libraries?
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