content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE PatternGuards #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE QuasiQuotes #-}
  import System.Directory
  import System.Environment
  import System.FilePath
  import System.Info
  import System.Process
  import System.IO.Error ( isDoesNotExistError )
  import Control.Monad
  import Control.Exception
  import Data.Char ( isSpace, isDigit )
  import Data.List
  import Data.Maybe
  import Distribution.Simple
  import Distribution.Simple.Setup
  import Distribution.InstalledPackageInfo
  import Distribution.PackageDescription
  import Distribution.Simple.LocalBuildInfo
  import Distribution.Simple.Install
  import Distribution.Simple.Register
  import Distribution.Simple.Utils
  import Distribution.Text ( display )
  import Language.Haskell.TH
  
  main = do
      let hooks = autoconfUserHooks { postConf = if os == "mingw32"
                                                 then generateBuildInfo
                                                 else postConf autoconfUserHooks
                                    , instHook = installHookWithExtraGhciLibraries
                                    , regHook  = regHookWithExtraGhciLibraries
                                    }
      defaultMainWithHooks hooks
  
  -- On Windows we can't count on the configure script, so generate the
  -- llvm.buildinfo from a template.
  generateBuildInfo _ conf _ _ = do
      let args = configConfigureArgs conf
      let pref = "--with-llvm-config="
      configbin <-
          case [ p | arg <- args, Just p <- [stripPrefix pref arg] ] of
            [p] -> return p
            _ -> do
              r <- tryJust (guard . isDoesNotExistError)
                           (readProcess "llvm-config" ["--bindir"] "")
              case r of
                Left _ -> error $ "Use '--configure-option " ++ pref ++ "PATH' to give llvm-config path"
                Right p -> return $ takeWhile (/= '\n') p ++ "/llvm-config"
      info <- readFile "llvm-base.buildinfo.windows.in"
      let llvmconfig = (\opts -> fmap (takeWhile (/= '\n')) $ readProcess configbin opts "")
      cppflags <- llvmconfig ["--cppflags"]
      version <- llvmconfig ["--version"]
      let major = read $ takeWhile isDigit version
          minor = read . takeWhile isDigit . tail $ dropWhile (/= '.') version
          hsversion = major * 100 + minor
      cflags <- llvmconfig ["--cflags"]
      libs <- fmap (subst "-l" "") $ llvmconfig ["--libs"]
      libdir <- llvmconfig ["--libdir"]
      ldopts <- llvmconfig ["--ldflags"]
      includedir <- llvmconfig ["--includedir"]
      let substs = [ ("@llvm_cppflags@", cppflags)
                   , ("@llvm_num_version@", show hsversion)
                   , ("@llvm_cflags@", cflags)
                   , ("@llvm_extra_libs@", libs)
                   , ("@llvm_extra_libdirs@", libdir)
                   , ("@llvm_ldoptions@", ldopts)
                   , ("@llvm_includedir@", includedir)
                   ]
      writeFile "llvm-base.buildinfo" $ foldl (\i (key, val) -> subst key val i) info substs
  
  subst from to [] = []
  subst from to xs | Just r <- stripPrefix from xs = to ++ subst from to r
  subst from to (x:xs) = x : subst from to xs
  
  {-
  To compensate for Cabal's current design,
  we need to replicate the default registration hook code here,
  to inject a value for extra-ghci-libraries into the package registration info.
  (Inspired by 'Gtk2HsSetup.hs'.)
  This only works for Cabal 1.10,
  thus we added an according constraint to llvm.cabal.
  
  We define an extension field 'x-extra-ghci-libraries' in the .buildinfo file
  in order to communicate the version information of the LLVM dynamic library
  from the configure script to the registration code.
  -}
  installHookWithExtraGhciLibraries :: PackageDescription -> LocalBuildInfo
                     -> UserHooks -> InstallFlags -> IO ()
  installHookWithExtraGhciLibraries pkg_descr localbuildinfo _ flags = do
    let copyFlags = defaultCopyFlags {
                        copyDistPref   = installDistPref flags,
                        copyDest       = toFlag NoCopyDest,
                        copyVerbosity  = installVerbosity flags
                    }
    install pkg_descr localbuildinfo copyFlags
    let registerFlags = defaultRegisterFlags {
                            regDistPref  = installDistPref flags,
                            regInPlace   = installInPlace flags,
                            regPackageDB = installPackageDB flags,
                            regVerbosity = installVerbosity flags
                        }
    when (hasLibs pkg_descr) $ register' pkg_descr localbuildinfo registerFlags
  
  regHookWithExtraGhciLibraries :: PackageDescription -> LocalBuildInfo
          -> UserHooks -> RegisterFlags -> IO ()
  regHookWithExtraGhciLibraries pkg_descr localbuildinfo _ flags =
      if hasLibs pkg_descr
      then register' pkg_descr localbuildinfo flags
      else setupMessage verbosity
             "Package contains no library to register:" (packageId pkg_descr)
    where verbosity = fromFlag (regVerbosity flags)
  
  
  
  {-
  this is the workaround for conditional compilation if template haskell was more
      permissive, but isn't
  
   --- what i'd like to write, but can't because template haskell rejecting
   the branch that has the wrong api version
   extractCLBI x=
       $(if cabalVersion >= Version [1,17,0] []
           then [|  getComponentLocalBuildInfo 'x CLibName  |]
           else  [|
                      let   LocalBuildInfo  { libraryConfig = Just clbi } = 'x
                          in clbi |]
      )
  
  
  -}
  
  --- horrible hack to support cabal versions both above and below 1.17
  extractCLBI x=
      $(if cabalVersion >= Version [1,17,0] []
          then  appE (appE  ( varE $ mkName "getComponentLocalBuildInfo") ( varE 'x) ) (conE ( mkName "CLibName"))
  
          else  letE
                  [valD  (recP
                              (mkName "LocalBuildInfo" )
                              [fieldPat (mkName "libraryConfig")
                               (conP (mkName "Just")    [varP $ mkName "clbi"] ) ] )
                      (normalB $ varE 'x)   []    ]
                   (varE $ mkName "clbi")  )
  
  register' :: PackageDescription -> LocalBuildInfo
            -> RegisterFlags -- ^Install in the user's database?; verbose
            -> IO ()
  register' pkg@PackageDescription { library       = Just lib  }
            lbi regFlags
    = do
      let clbi = extractCLBI lbi
      installedPkgInfoRaw <- generateRegistrationInfo
                             verbosity pkg lib lbi clbi inplace distPref
  
      let ghciLibraries    = case lookup "x-extra-ghci-libraries" (customFieldsBI (libBuildInfo lib)) of
                               Just s | not (all isSpace s) -> [s]
                               _                            -> []
          installedPkgInfo = installedPkgInfoRaw {
                                  extraGHCiLibraries = ghciLibraries }
  
       -- Three different modes:
      case () of
       _ | modeGenerateRegFile   -> writeRegistrationFile installedPkgInfo
         | modeGenerateRegScript -> die "Generate Reg Script not supported"
         | otherwise             -> registerPackage verbosity
                                      installedPkgInfo pkg lbi inplace
                                      (withPackageDB lbi)
  
    where
      modeGenerateRegFile = isJust (flagToMaybe (regGenPkgConf regFlags))
      modeGenerateRegScript = fromFlag (regGenScript regFlags)
      inplace   = fromFlag (regInPlace regFlags)
      packageDb = nub $ withPackageDB lbi ++
                        maybeToList (flagToMaybe  (regPackageDB regFlags))
      distPref  = fromFlag (regDistPref regFlags)
      verbosity = fromFlag (regVerbosity regFlags)
      regFile             = fromMaybe (display (packageId pkg) <.> "conf")
                                      (fromFlag (regGenPkgConf regFlags))
      writeRegistrationFile installedPkgInfo = do
        notice verbosity ("Creating package registration file: " ++ regFile)
        writeUTF8File regFile (showInstalledPackageInfo installedPkgInfo)
  
  register' _ _ regFlags = notice verbosity "No package to register"
    where
      verbosity = fromFlag (regVerbosity regFlags)
  