{-# LANGUAGE RecordWildCards #-} import Prelude hiding ((<$>)) import Control.Applicative ((<$>)) import Control.Exception (bracket) import Control.Monad (unless, when) import Data.Maybe (fromMaybe, isJust) import System.Directory hiding (makeAbsolute, withCurrentDirectory) import System.FilePath import Distribution.PackageDescription import Distribution.Simple import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program.Builtin (arProgram, ghcProgram) import qualified Distribution.Simple.Program.Db as Db import Distribution.Simple.Program.Types (ConfiguredProgram(..), Program(..), ProgramLocation(..)) import Distribution.Simple.Setup import Distribution.Simple.Utils import Distribution.Verbosity main :: IO () main = defaultMainWithHooks customHooks where customHooks = simpleUserHooks { confHook = customConfHook (confHook simpleUserHooks) , buildHook = customBuildHook (buildHook simpleUserHooks) , preSDist = customPreSDist (preSDist simpleUserHooks) } customConfHook :: (a -> b -> IO LocalBuildInfo) -> a -> b -> IO LocalBuildInfo customConfHook innerHook a b = do localBuildInfo <- innerHook a b root <- makeAbsolute =<< getCurrentDirectory let cabalBuildDir = buildDir localBuildInfo sh = "sh" Just ar = Db.lookupProgram arProgram (withPrograms localBuildInfo) arLocation = resolveProgramLocation ar arWrapperPath = root "build-tools" "ar-wrapper" arWrapper = ar { programDefaultArgs = arWrapperPath : arLocation : programDefaultArgs ar , programLocation = UserSpecified sh , programOverrideEnv = ("CABAL_BUILD_DIR", Just cabalBuildDir) : programOverrideEnv ar } Just ghc = Db.lookupProgram ghcProgram (withPrograms localBuildInfo) ghcLocation = resolveProgramLocation ghc ghcWrapperPath = root "build-tools" "ghc-wrapper" ghcWrapper = ghc { programDefaultArgs = ghcWrapperPath : ghcLocation : programDefaultArgs ghc , programLocation = UserSpecified sh , programOverrideEnv = ("CABAL_BUILD_DIR", Just cabalBuildDir) : programOverrideEnv ghc } withPrograms' = Db.updateProgram arWrapper $ Db.updateProgram ghcWrapper $ withPrograms localBuildInfo return localBuildInfo { withPrograms = withPrograms' } where resolveProgramLocation prog = case programLocation prog of UserSpecified path -> path FoundOnSystem path -> path customBuildHook :: (PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()) -> PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO () customBuildHook innerHook packageDescription@PackageDescription{..} localBuildInfo@LocalBuildInfo{..} userHooks buildFlags = do let wantSIMD = fromMaybe True $ lookup (FlagName "simd") $ configConfigurationsFlags configFlags verbosity = fromFlag $ buildVerbosity buildFlags version = pkgVersion package root <- makeAbsolute =<< getCurrentDirectory absBuildDir <- makeAbsolute buildDir let cbitsBuildDir = absBuildDir "cbits" when wantSIMD $ do let configure = root "cbits" "configure" configureExists <- doesFileExist configure unless configureExists $ die $ concat [ "'configure' script not found. " , "If this is not a release version, you probably need " , "to run 'autoreconf -i' in the 'cbits' directory to generate it." ] createDirectoryIfMissing True cbitsBuildDir hasMakefile <- doesFileExist (cbitsBuildDir "Makefile") unless hasMakefile $ withCurrentDirectory cbitsBuildDir $ do let libOptions = case versionBranch version of [999] -> [] _ -> ["--disable-maintainer-mode"] fromWindows = map (\c -> if c == '\\' then '/' else c) rawSystemExit verbosity "sh" $ [ fromWindows configure ] ++ libOptions rawSystemExit verbosity "make" ["-C", cbitsBuildDir, "--no-print-directory"] let addIncludeDir info = info { includeDirs = cbitsBuildDir : includeDirs info } packageDescription' = if wantSIMD then mapBuildInfo addIncludeDir packageDescription else packageDescription innerHook packageDescription' localBuildInfo userHooks buildFlags mapBuildInfo :: (BuildInfo -> BuildInfo) -> PackageDescription -> PackageDescription mapBuildInfo f desc = desc { library = fmap (\l -> l { libBuildInfo = f (libBuildInfo l) }) (library desc) , executables = map (\e -> e { buildInfo = f (buildInfo e) }) (executables desc) , testSuites = map (\t -> t { testBuildInfo = f (testBuildInfo t) }) (testSuites desc) , benchmarks = map (\b -> b { benchmarkBuildInfo = f (benchmarkBuildInfo b) }) (benchmarks desc) } makeAbsolute :: FilePath -> IO FilePath makeAbsolute = (normalise <$>) . absolutize where absolutize path -- avoid the call to `getCurrentDirectory` if we can | isRelative path = ( path) . addTrailingPathSeparator <$> getCurrentDirectory | otherwise = return path withCurrentDirectory :: FilePath -- ^ Directory to execute in -> IO a -- ^ Action to be executed -> IO a withCurrentDirectory dir action = bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do setCurrentDirectory dir action customPreSDist :: (Args -> SDistFlags -> IO HookedBuildInfo) -> Args -> SDistFlags -> IO HookedBuildInfo customPreSDist innerHook args flags = do hookedBuildInfo <- innerHook args flags root <- makeAbsolute =<< getCurrentDirectory let cbits = root "cbits" verbosity = fromFlag (sDistVerbosity flags) withCurrentDirectory cbits $ rawSystemExit verbosity "autoreconf" ["-f", "-i"] return hookedBuildInfo