4

I want to enhance flycheck-haskell's support for auto-configuring flycheck from your .cabal file.

To do this autoconfiguration, flycheck uses a helper file whose original strategy was to read the .cabal file and use flattenPackageDescription. This was simple, but does not respect conditional expressions, which can cause problems with, e.g. not needing bytestring-builder when using newer version of the bytestring package.

The appropriate interface to use would seem to be finalizePackageDescription. This does work...but its type signature changed between 1.20 and 1.22---now, instead of taking a CompilerId, it now takes a CompilerInfo. Yet I would like to provide consistent support across this API change.

While the solution for this is generally to use CPP macros, those macros are provided by Cabal itself, and flycheck is simply invoking the helper file with runhaskell, so we don't have access to them.

The only option I can think of would be to create another helper to first get the Cabal version information, and then construct appropriate CPP settings for our runhaskell invocation so we can do things that way. That should work, but seems like a hack.

So I'm here looking for other options that would allow me to support both versions of the interface, without having to resort to CPP.

The code in question is a call to Distribution.PackageDescription.Configuration.finalizePackageDescription, like so:

case finalizePackageDescription [] (const True) buildPlatform buildCompilerId [] genericDesc' of
  Left e -> putStrLn $ "Issue with package configuration\n" ++ show e
  Right (pkgDesc, _) -> print (dumpPackageDescription pkgDesc cabalFile)

The problem is that the fourth parameter, buildCompilerId changed type from CompilerId to CompilerInfo.

What I have implemented---though I would be happy to consider a more self-contained option---is a helper that spits out -DuseCompilerInfo (as an s-expr, since we're dealing with Emacs) if it's a sufficiently recent version of Cabal:

import Data.Version (Version (Version))
import Distribution.Simple.Utils (cabalVersion)

main :: IO ()
main =
  putStrLn $ if cabalVersion >= Version [1,22] []
             then "(\"-DuseCompilerInfo\")"
             else "()"

The original helper is then run with the flag, and conditionally imports the new structures, as well as having the following conditional code just before the case statement above:

#ifdef useCompilerInfo
      buildCompilerId = unknownCompilerInfo (CompilerId buildCompilerFlavor compilerVersion) NoAbiTag
#else
      buildCompilerId = CompilerId buildCompilerFlavor compilerVersion
#endif

It's not beautiful, but it works.

mdorman
  • 356
  • 1
  • 6
  • 2
    I've been told that some of these tricks work http://www.yesodweb.com/blog/2014/06/evil-conditional-compile – Michael Snoyman Apr 19 '15 at 13:30
  • Thanks for the pointer.Unfortunately, I have come to believe the fact that they introduced a new data type (CompilerInfo) makes this infeasible. – mdorman Apr 19 '15 at 17:25
  • It would be really ugly, but you should always be able to use the Template Haskell approach by constructing the Names that you need with `mkName`. Whether the other approaches mentioned in that blog post would work probably depends on exactly what the two versions of the code you need to write are; maybe include them in your question? – Reid Barton Apr 19 '15 at 20:38

0 Answers0