packages feed

clang-pure-0.1.0.3: Setup.hs

{-|
Module      : Main
Description : Setup script for clang-pure build
Copyright   : (C) Richard Cook, Patrick Chilton 2016
Licence     : Apache 2.0
Maintainer  : rcook@rcook.org
Stability   : experimental
Portability : portable

This setup script adds a configuration hook to the build process to discover
the LLVM library and include paths from the standard system-wide installation
location or from an alternative location if overridden with both the
@CLANG_PURE_LLVM_LIB_DIR@ and @CLANG_PURE_LLVM_INCLUDE_DIR@ environment variables.
-}

{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -Wall #-}

module Main (main) where

import Control.Exception
import Control.Monad
import Distribution.PackageDescription
import Distribution.Simple
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Setup
import System.Environment
import System.IO.Error
import System.Process

data SetupException = SetupException String deriving Show

instance Exception SetupException

data LLVMPathInfo = LLVMPathInfo
  { llvmLibraryDir :: FilePath
  , llvmIncludeDir :: FilePath
  } deriving (Eq, Ord, Show)

llvmLibDirEnvVarName :: String
llvmLibDirEnvVarName = "CLANG_PURE_LLVM_LIB_DIR"

llvmIncludeDirEnvVarName :: String
llvmIncludeDirEnvVarName = "CLANG_PURE_LLVM_INCLUDE_DIR"

findLLVMConfigPaths :: IO LLVMPathInfo
findLLVMConfigPaths = do
  let llvmConfigCandidates =
        "llvm-config" :
          [ "llvm-config-" ++ show major ++ "." ++ show minor
          | major <- [9,8..3 :: Int]
          , minor <- [9,8..0 :: Int]
          ]
  let tryCandidates [] = throwIO $ SetupException "Could not find llvm-config."
      tryCandidates (llvmConfig : candidates) = do
        llvmConfigResult <- tryJust (guard . isDoesNotExistError) (readProcess llvmConfig ["--libdir", "--includedir"] "")
        case llvmConfigResult of
          Left _ -> tryCandidates candidates
          Right llvmConfigOutput -> case lines llvmConfigOutput of
            [ libraryDir, includeDir ] -> return $ LLVMPathInfo libraryDir includeDir
            _ -> throwIO $ SetupException "Unexpected llvm-config output."
  tryCandidates llvmConfigCandidates

getLLVMPathInfo :: IO LLVMPathInfo
getLLVMPathInfo = do
  m'llvmLibDirEnvVar <- lookupEnv llvmLibDirEnvVarName
  m'llvmIncludeDirEnvVar <- lookupEnv llvmIncludeDirEnvVarName
  case (m'llvmLibDirEnvVar, m'llvmIncludeDirEnvVar) of
    ( Just libraryDir, Just includeDir ) -> return $ LLVMPathInfo libraryDir includeDir
    _ -> findLLVMConfigPaths

clangPureConfHook :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags -> IO LocalBuildInfo
clangPureConfHook (d, bi) flags = do
  localBuildInfo <- confHook simpleUserHooks (d, bi) flags
  let pd = localPkgDescr localBuildInfo
  let Just lib = library pd
  let lbi = libBuildInfo lib

  LLVMPathInfo{..} <- getLLVMPathInfo

  return localBuildInfo {
    localPkgDescr = pd {
      library = Just $ lib {
        libBuildInfo = lbi
        { includeDirs = llvmIncludeDir : includeDirs lbi
        , extraLibDirs = llvmLibraryDir : extraLibDirs lbi
        , cSources = -- define the generated c-sources here so that they don't get picked up by sdist
#ifdef mingw32_HOST_OS
            ["srcLanguageCClangInternalFFI.c"] -- work around a bug in inline-c (?)
#else
            ["src/Language/C/Clang/Internal/FFI.c"]
#endif
        }
      }
    }
  }

main :: IO ()
main = defaultMainWithHooks simpleUserHooks { confHook = clangPureConfHook }