{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} module Stackage.InstallBuild ( InstallFlags (..) , BuildPlanSource (..) , installBuild ) where import qualified Codec.Archive.Tar as Tar import qualified Codec.Compression.GZip as GZip import qualified Data.Yaml as Yaml import Network.HTTP.Client import Network.HTTP.Client.TLS (tlsManagerSettings) import Stackage.BuildPlan import Stackage.CheckBuildPlan import Stackage.PerformBuild import Stackage.Prelude import System.IO (BufferMode (LineBuffering), hSetBuffering) -- | Flags passed in from the command line. data InstallFlags = InstallFlags { ifPlanSource :: !BuildPlanSource , ifInstallDest :: !FilePath , ifLogDir :: !(Maybe FilePath) , ifJobs :: !Int , ifGlobalInstall :: !Bool , ifEnableTests :: !Bool , ifEnableBenches :: !Bool , ifEnableHaddock :: !Bool , ifEnableLibProfiling :: !Bool , ifEnableExecDyn :: !Bool , ifVerbose :: !Bool , ifSkipCheck :: !Bool , ifBuildHoogle :: !Bool , ifNoRebuildCabal :: !Bool } deriving (Show) -- | Source for build plan. data BuildPlanSource = BPSBundleWeb String | BPSFile FilePath deriving (Show) getPerformBuild :: BuildPlan -> InstallFlags -> PerformBuild getPerformBuild plan InstallFlags{..} = PerformBuild { pbPlan = plan , pbInstallDest = ifInstallDest , pbLogDir = fromMaybe (ifInstallDest "logs") ifLogDir , pbLog = hPut stdout , pbJobs = ifJobs , pbGlobalInstall = ifGlobalInstall , pbEnableTests = ifEnableTests , pbEnableBenches = ifEnableBenches , pbEnableHaddock = ifEnableHaddock , pbEnableLibProfiling = ifEnableLibProfiling , pbEnableExecDyn = ifEnableExecDyn , pbVerbose = ifVerbose , pbAllowNewer = ifSkipCheck , pbBuildHoogle = ifBuildHoogle , pbNoRebuildCabal = ifNoRebuildCabal , pbCabalFromHead = False } -- | Install stackage from an existing build plan. installBuild :: InstallFlags -> IO () installBuild installFlags@InstallFlags{..} = do hSetBuffering stdout LineBuffering putStrLn $ "Loading build plan" plan <- case ifPlanSource of BPSBundleWeb url -> do man <- newManager tlsManagerSettings req <- parseUrlThrow url res <- httpLbs req man planBSL <- getPlanEntry $ Tar.read $ GZip.decompress (responseBody res) decodeBuildPlan planBSL BPSFile path -> Yaml.decodeFileEither path >>= either throwM return if ifSkipCheck then putStrLn "Skipping build plan check" else do putStrLn "Checking build plan" checkBuildPlan True plan putStrLn "Performing build" performBuild (getPerformBuild plan installFlags) >>= mapM_ putStrLn where getPlanEntry Tar.Done = throwIO NoBuildPlanException getPlanEntry (Tar.Fail e) = throwIO e getPlanEntry (Tar.Next entry entries) | Tar.entryPath entry == "build-plan.yaml" = case Tar.entryContent entry of Tar.NormalFile bs _ -> return bs _ -> throwIO NoBuildPlanException | otherwise = getPlanEntry entries decodeBuildPlan = either throwIO return . Yaml.decodeEither' . toStrict data InstallBuildException = NoBuildPlanException deriving (Typeable) instance Exception InstallBuildException instance Show InstallBuildException where show NoBuildPlanException = "Bundle has missing or invalid build-plan.yaml"