    {-# LANGUAGE DeriveDataTypeable #-}

    module Propellor.Bootstrap (
    	Bootstrapper(..),
    	Builder(..),
    	defaultBootstrapper,
    	getBootstrapper,
    	bootstrapPropellorCommand,
    	checkBinaryCommand,
    	installGitCommand,
    	buildPropellor,
    	checkDepsCommand,
    	buildCommand,
    ) where

    import Propellor.Base
    import Propellor.Types.Info
    import Propellor.Git.Config

    import System.Posix.Files
    import Data.List

    type ShellCommand = String

    -- | Different ways that Propellor's dependencies can be installed,
    -- and propellor can be built. The default is `Robustly Cabal`
    --
    -- `Robustly Cabal` and `Robustly Stack` use the OS's native packages
    -- as much as possible to install Cabal, Stack, and propellor's build
    -- dependencies. When necessary, dependencies are built from source
    -- using Cabal or Stack rather than using the OS's native packages.
    --
    -- `OSOnly` uses the OS's native packages of Cabal and all of propellor's
    -- build dependencies. It may not work on all systems.
    data Bootstrapper = Robustly Builder | OSOnly
    	deriving (Int -> Bootstrapper -> ShowS
    [Bootstrapper] -> ShowS
    Bootstrapper -> String
    (Int -> Bootstrapper -> ShowS)
    -> (Bootstrapper -> String)
    -> ([Bootstrapper] -> ShowS)
    -> Show Bootstrapper
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Bootstrapper -> ShowS
    showsPrec :: Int -> Bootstrapper -> ShowS
    $cshow :: Bootstrapper -> String
    show :: Bootstrapper -> String
    $cshowList :: [Bootstrapper] -> ShowS
    showList :: [Bootstrapper] -> ShowS
    Show, Typeable)

    data Builder = Cabal | Stack
    	deriving (Int -> Builder -> ShowS
    [Builder] -> ShowS
    Builder -> String
    (Int -> Builder -> ShowS)
    -> (Builder -> String) -> ([Builder] -> ShowS) -> Show Builder
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Builder -> ShowS
    showsPrec :: Int -> Builder -> ShowS
    $cshow :: Builder -> String
    show :: Builder -> String
    $cshowList :: [Builder] -> ShowS
    showList :: [Builder] -> ShowS
    Show, Typeable)

    defaultBootstrapper :: Bootstrapper
    defaultBootstrapper :: Bootstrapper
    defaultBootstrapper = Builder -> Bootstrapper
    Robustly Builder
    Cabal

    -- | Gets the Bootstrapper for the Host propellor is running on.
    getBootstrapper :: Propellor Bootstrapper
    getBootstrapper :: Propellor Bootstrapper
    getBootstrapper = InfoVal Bootstrapper -> Bootstrapper
    go (InfoVal Bootstrapper -> Bootstrapper)
    -> Propellor (InfoVal Bootstrapper) -> Propellor Bootstrapper
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Propellor (InfoVal Bootstrapper)
    forall v. IsInfo v => Propellor v
    askInfo
      where
    	go :: InfoVal Bootstrapper -> Bootstrapper
    go InfoVal Bootstrapper
    NoInfoVal = Bootstrapper
    defaultBootstrapper
    	go (InfoVal Bootstrapper
    bs) = Bootstrapper
    bs

    getBuilder :: Bootstrapper -> Builder
    getBuilder :: Bootstrapper -> Builder
    getBuilder (Robustly Builder
    b) = Builder
    b
    getBuilder Bootstrapper
    OSOnly = Builder
    Cabal

    -- Shell command line to ensure propellor is bootstrapped and ready to run.
    -- Should be run inside the propellor config dir, and will install
    -- all necessary build dependencies and build propellor.
    bootstrapPropellorCommand :: Bootstrapper -> Maybe System -> ShellCommand
    bootstrapPropellorCommand :: Bootstrapper -> Maybe System -> String
    bootstrapPropellorCommand Bootstrapper
    bs Maybe System
    msys = Bootstrapper -> Maybe System -> String
    checkDepsCommand Bootstrapper
    bs Maybe System
    msys String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
    	String
    "&& if ! test -x ./propellor; then "
    		String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Bootstrapper -> String
    buildCommand Bootstrapper
    bs String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
    	String
    "; fi;" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Bootstrapper -> String
    checkBinaryCommand Bootstrapper
    bs

    -- Use propellor --check to detect if the local propellor binary has
    -- stopped working (eg due to library changes), and must be rebuilt.
    checkBinaryCommand :: Bootstrapper -> ShellCommand
    checkBinaryCommand :: Bootstrapper -> String
    checkBinaryCommand Bootstrapper
    bs = String
    "if test -x ./propellor && ! ./propellor --check; then " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Builder -> String
    go (Bootstrapper -> Builder
    getBuilder Bootstrapper
    bs) String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "; fi"
      where
    	go :: Builder -> String
    go Builder
    Cabal = String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    " && "
    		[ String
    "cabal clean"
    		, Bootstrapper -> String
    buildCommand Bootstrapper
    bs
    		]
    	go Builder
    Stack = String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    " && "
    		[ String
    "stack clean"
    		, Bootstrapper -> String
    buildCommand Bootstrapper
    bs
    		]

    buildCommand :: Bootstrapper -> ShellCommand
    buildCommand :: Bootstrapper -> String
    buildCommand Bootstrapper
    bs = String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    " && " (Builder -> [String]
    go (Bootstrapper -> Builder
    getBuilder Bootstrapper
    bs))
      where
    	go :: Builder -> [String]
    go Builder
    Cabal =
    		[ String
    "cabal configure"
    		, String
    "cabal build -j1 propellor-config"
    		, String
    "ln -sf" String -> ShowS
    `commandCabalBuildTo` String
    "propellor"
    		]
    	go Builder
    Stack =
    		[ String
    "stack build :propellor-config"
    		, String
    "ln -sf $(stack path --dist-dir)/build/propellor-config/propellor-config propellor"
    		]

    commandCabalBuildTo :: ShellCommand -> FilePath -> ShellCommand
    commandCabalBuildTo :: String -> ShowS
    commandCabalBuildTo String
    cmd String
    dest = String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    "; "
    	[ String
    "if [ -d dist-newstyle ]"
    	, String
    "then " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    cmd String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " $(cabal exec -- sh -c 'command -v propellor-config') " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ShowS
    shellEscape String
    dest
    	, String
    "else " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    cmd String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " dist/build/propellor-config/propellor-config " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ShowS
    shellEscape String
    dest
    	, String
    "fi"
    	]

    -- Check if all dependencies are installed; if not, run the depsCommand.
    checkDepsCommand :: Bootstrapper -> Maybe System -> ShellCommand
    checkDepsCommand :: Bootstrapper -> Maybe System -> String
    checkDepsCommand Bootstrapper
    bs Maybe System
    sys = Builder -> String
    go (Bootstrapper -> Builder
    getBuilder Bootstrapper
    bs)
      where
    	go :: Builder -> String
    go Builder
    Cabal = String
    "if ! cabal configure >/dev/null 2>&1; then " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Bootstrapper -> Maybe System -> String
    depsCommand Bootstrapper
    bs Maybe System
    sys String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "; fi"
    	go Builder
    Stack = String
    "if ! stack build --dry-run >/dev/null 2>&1; then " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Bootstrapper -> Maybe System -> String
    depsCommand Bootstrapper
    bs Maybe System
    sys String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "; fi"

    data Dep = Dep String | OptDep String | OldDep String

    -- Install build dependencies of propellor, using the specified
    -- Bootstrapper.
    --
    -- When bootstrapping Robustly, first try to install the builder,
    -- and all haskell libraries that propellor uses from OS packages.
    -- Some packages may not be available in some versions of the OS,
    -- or propellor may need a newer version. So, as a second step,
    -- ny other dependencies are installed from source using the builder.
    --
    -- Note: May succeed and leave some deps not installed.
    depsCommand :: Bootstrapper -> Maybe System -> ShellCommand
    depsCommand :: Bootstrapper -> Maybe System -> String
    depsCommand Bootstrapper
    bs Maybe System
    msys = String
    "( " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    " ; " (Bootstrapper -> [String]
    go Bootstrapper
    bs) String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ") || true"
      where
    	go :: Bootstrapper -> [String]
    go (Robustly Builder
    Cabal) = Builder -> [String]
    osinstall Builder
    Cabal [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++
    		[ String
    "cabal update"
    		, String
    "cabal install --only-dependencies"
    		]
    	go (Robustly Builder
    Stack) = Builder -> [String]
    osinstall Builder
    Stack [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++
    		[ String
    "stack setup"
    		, String
    "stack build --only-dependencies :propellor-config"
    		]
    	go Bootstrapper
    OSOnly = Builder -> [String]
    osinstall Builder
    Cabal

    	osinstall :: Builder -> [String]
    osinstall Builder
    builder = case Maybe System
    msys of
    		Just (System (FreeBSD FreeBSDRelease
    _) Architecture
    _) -> ShowS -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map ShowS
    pkginstall (Builder -> [String]
    fbsddeps Builder
    builder)
    		Just (System (Distribution
    ArchLinux) Architecture
    _) -> ShowS -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map ShowS
    pacmaninstall (Builder -> [String]
    archlinuxdeps Builder
    builder)
    		Just (System (Debian DebianKernel
    _ DebianSuite
    _) Architecture
    _) -> Builder -> [String]
    useapt Builder
    builder
    		Just (System (Buntish String
    _) Architecture
    _) -> Builder -> [String]
    useapt Builder
    builder
    		-- assume a Debian derived system when not specified
    		Maybe System
    Nothing -> Builder -> [String]
    useapt Builder
    builder

    	useapt :: Builder -> [String]
    useapt Builder
    builder = String
    "apt-get update" String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : (Dep -> String) -> [Dep] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Dep -> String
    aptinstall (Builder -> [Dep]
    debdeps Builder
    builder)

    	aptinstall :: Dep -> String
    aptinstall (Dep String
    p) = String
    "DEBIAN_FRONTEND=noninteractive apt-get -qq --no-upgrade --no-install-recommends -y install " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    p
    	aptinstall (OptDep String
    p) = String
    "if LANG=C apt-cache policy " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    p String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "| grep -q Candidate:; then " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Dep -> String
    aptinstall (String -> Dep
    Dep String
    p) String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "; fi"
    	aptinstall (OldDep String
    p) = Dep -> String
    aptinstall (String -> Dep
    OptDep String
    p)
    	pkginstall :: ShowS
    pkginstall String
    p = String
    "ASSUME_ALWAYS_YES=yes pkg install " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    p
    	pacmaninstall :: ShowS
    pacmaninstall String
    p = String
    "pacman -S --noconfirm --needed " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    p

    	debdeps :: Builder -> [Dep]
    debdeps Builder
    Cabal =
    		[ String -> Dep
    Dep String
    "gnupg"
    		-- Below are the same deps listed in debian/control.
    		, String -> Dep
    Dep String
    "ghc"
    		, String -> Dep
    Dep String
    "cabal-install"
    		, String -> Dep
    Dep String
    "libghc-async-dev"
    		, String -> Dep
    Dep String
    "libghc-split-dev"
    		, String -> Dep
    Dep String
    "libghc-hslogger-dev"
    		, String -> Dep
    Dep String
    "libghc-unix-compat-dev"
    		, String -> Dep
    Dep String
    "libghc-ansi-terminal-dev"
    		, String -> Dep
    Dep String
    "libghc-ifelse-dev"
    		, String -> Dep
    Dep String
    "libghc-network-dev"
    		, String -> Dep
    Dep String
    "libghc-mtl-dev"
    		, String -> Dep
    Dep String
    "libghc-transformers-dev"
    		, String -> Dep
    Dep String
    "libghc-exceptions-dev"
    		, String -> Dep
    Dep String
    "libghc-text-dev"
    		, String -> Dep
    Dep String
    "libghc-hashable-dev"
    		-- Deps that can be skipped on old systems.
    		, String -> Dep
    OptDep String
    "libghc-type-errors-dev"
    		-- Deps that are only needed on old systems.
    		, String -> Dep
    OldDep String
    "libghc-stm-dev"
    		]
    	debdeps Builder
    Stack =
    		[ String -> Dep
    Dep String
    "gnupg"
    		, String -> Dep
    Dep String
    "haskell-stack"
    		]

    	fbsddeps :: Builder -> [String]
    fbsddeps Builder
    Cabal =
    		[ String
    "gnupg"
    		, String
    "ghc"
    		, String
    "hs-cabal-install"
    		, String
    "hs-async"
    		, String
    "hs-split"
    		, String
    "hs-hslogger"
    		, String
    "hs-unix-compat"
    		, String
    "hs-ansi-terminal"
    		, String
    "hs-IfElse"
    		, String
    "hs-network"
    		, String
    "hs-mtl"
    		, String
    "hs-transformers-base"
    		, String
    "hs-exceptions"
    		, String
    "hs-stm"
    		, String
    "hs-text"
    		, String
    "hs-hashable"
    		]
    	fbsddeps Builder
    Stack =
    		[ String
    "gnupg"
    		, String
    "stack"
    		]

    	archlinuxdeps :: Builder -> [String]
    archlinuxdeps Builder
    Cabal =
    		[ String
    "gnupg"
    		, String
    "ghc"
    		, String
    "cabal-install"
    		, String
    "haskell-async"
    		, String
    "haskell-split"
    		, String
    "haskell-hslogger"
    		, String
    "haskell-unix-compat"
    		, String
    "haskell-ansi-terminal"
    		, String
    "haskell-hackage-security"
    		, String
    "haskell-ifelse"
    		, String
    "haskell-network"
    		, String
    "haskell-mtl"
    		, String
    "haskell-transformers-base"
    		, String
    "haskell-exceptions"
    		, String
    "haskell-stm"
    		, String
    "haskell-text"
    		, String
    "haskell-hashable"
    		, String
    "haskell-type-errors"
    		]
    	archlinuxdeps Builder
    Stack =
    		[ String
    "gnupg"
    		, String
    "stack"
    		]

    installGitCommand :: Maybe System -> ShellCommand
    installGitCommand :: Maybe System -> String
    installGitCommand Maybe System
    msys = case Maybe System
    msys of
    	(Just (System (Debian DebianKernel
    _ DebianSuite
    _) Architecture
    _)) -> [String] -> String
    use [String]
    apt
    	(Just (System (Buntish String
    _) Architecture
    _)) -> [String] -> String
    use [String]
    apt
    	(Just (System (FreeBSD FreeBSDRelease
    _) Architecture
    _)) -> [String] -> String
    use
    		[ String
    "ASSUME_ALWAYS_YES=yes pkg update"
    		, String
    "ASSUME_ALWAYS_YES=yes pkg install git"
    		]
    	(Just (System (Distribution
    ArchLinux) Architecture
    _)) -> [String] -> String
    use
    		[ String
    "pacman -S --noconfirm --needed git"]
    	-- assume a debian derived system when not specified
    	Maybe System
    Nothing -> [String] -> String
    use [String]
    apt
      where
    	use :: [String] -> String
    use [String]
    cmds = String
    "if ! git --version >/dev/null 2>&1; then " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    " && " [String]
    cmds String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "; fi"
    	apt :: [String]
    apt =
    		[ String
    "apt-get update"
    		, String
    "DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends --no-upgrade -y install git"
    		]

    -- Build propellor, and symlink the built binary to ./propellor.
    --
    -- When the Host has a Buildsystem specified it is used. If none is
    -- specified, look at git config propellor.buildsystem.
    buildPropellor :: Maybe Host -> IO ()
    buildPropellor :: Maybe Host -> IO ()
    buildPropellor Maybe Host
    mh = IO Bool -> IO () -> IO ()
    forall {m :: * -> *}. Monad m => m Bool -> m () -> m ()
    unlessM (String -> IO Bool -> IO Bool
    forall (m :: * -> *) r.
    (MonadIO m, MonadMask m, ActionResult r, ToResult r) =>
    String -> m r -> m r
    actionMessage String
    "Propellor build" IO Bool
    build) (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    	String -> IO ()
    forall (m :: * -> *) a. MonadIO m => String -> m a
    errorMessage String
    "Propellor build failed!"
      where
    	msys :: Maybe System
    msys = case (Host -> InfoVal System) -> Maybe Host -> Maybe (InfoVal System)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Info -> InfoVal System
    forall v. IsInfo v => Info -> v
    fromInfo (Info -> InfoVal System)
    -> (Host -> Info) -> Host -> InfoVal System
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Host -> Info
    hostInfo) Maybe Host
    mh of
    		Just (InfoVal System
    sys) -> System -> Maybe System
    forall a. a -> Maybe a
    Just System
    sys
    		Maybe (InfoVal System)
    _ -> Maybe System
    forall a. Maybe a
    Nothing

    	build :: IO Bool
    build = IO Bool -> IO Bool
    forall (m :: * -> *). MonadCatch m => m Bool -> m Bool
    catchBoolIO (IO Bool -> IO Bool) -> IO Bool -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ do
    		case Info -> InfoVal Bootstrapper
    forall v. IsInfo v => Info -> v
    fromInfo (Info -> (Host -> Info) -> Maybe Host -> Info
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Info
    forall a. Monoid a => a
    mempty Host -> Info
    hostInfo Maybe Host
    mh) of
    			InfoVal Bootstrapper
    NoInfoVal -> do
    				bs <- String -> IO (Maybe String)
    getGitConfigValue String
    "propellor.buildsystem"
    				case bs of
    					Just String
    "stack" -> Maybe System -> IO Bool
    stackBuild Maybe System
    msys
    					Maybe String
    _ -> Maybe System -> IO Bool
    cabalBuild Maybe System
    msys
    			InfoVal Bootstrapper
    bs -> case Bootstrapper -> Builder
    getBuilder Bootstrapper
    bs of
    				Builder
    Cabal -> Maybe System -> IO Bool
    cabalBuild Maybe System
    msys
    				Builder
    Stack -> Maybe System -> IO Bool
    stackBuild Maybe System
    msys

    -- For speed, only runs cabal configure when it's not been run before.
    -- If the build fails cabal may need to have configure re-run.
    --
    -- If the cabal configure fails, and a System is provided, installs
    -- dependencies and retries.
    cabalBuild :: Maybe System -> IO Bool
    cabalBuild :: Maybe System -> IO Bool
    cabalBuild Maybe System
    msys = do
    	String -> [String] -> IO Bool -> IO ()
    make String
    "configured" [String
    "propellor.cabal"] IO Bool
    cabal_configure
    	IO Bool -> IO () -> IO ()
    forall {m :: * -> *}. Monad m => m Bool -> m () -> m ()
    unlessM IO Bool
    cabal_build (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    		IO Bool -> IO () -> IO ()
    forall {m :: * -> *}. Monad m => m Bool -> m () -> m ()
    unlessM (IO Bool
    cabal_configure IO Bool -> IO Bool -> IO Bool
    forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
    <&&> IO Bool
    cabal_build) (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    			String -> IO ()
    forall a. HasCallStack => String -> a
    error String
    "cabal build failed"
    	-- Make a copy of the binary, and move it into place atomically.
    	let safetycopy :: String
    safetycopy = String
    "propellor.built"
    	let cpcmd :: String
    cpcmd = String
    "cp -pfL" String -> ShowS
    `commandCabalBuildTo` String
    safetycopy
    	IO Bool -> IO () -> IO ()
    forall {m :: * -> *}. Monad m => m Bool -> m () -> m ()
    unlessM (String -> [CommandParam] -> IO Bool
    boolSystem String
    "sh" [String -> CommandParam
    Param String
    "-c", String -> CommandParam
    Param String
    cpcmd]) (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    		String -> IO ()
    forall a. HasCallStack => String -> a
    error String
    "cp of binary failed"
    	String -> String -> IO ()
    rename String
    safetycopy String
    "propellor"
    	Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    True
      where
    	cabal_configure :: IO Bool
    cabal_configure = IO Bool -> (IO Bool, IO Bool) -> IO Bool
    forall (m :: * -> *) a. Monad m => m Bool -> (m a, m a) -> m a
    ifM ([String] -> IO Bool
    cabal [String
    "configure"])
    		( do
    			String -> String -> IO ()
    writeFile String
    "configured" String
    ""
    			Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    True
    		, case Maybe System
    msys of
    			Maybe System
    Nothing -> Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False
    			Just System
    sys ->
    				String -> [CommandParam] -> IO Bool
    boolSystem String
    "sh" [String -> CommandParam
    Param String
    "-c", String -> CommandParam
    Param (Bootstrapper -> Maybe System -> String
    depsCommand (Builder -> Bootstrapper
    Robustly Builder
    Cabal) (System -> Maybe System
    forall a. a -> Maybe a
    Just System
    sys))]
    					IO Bool -> IO Bool -> IO Bool
    forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
    <&&> [String] -> IO Bool
    cabal [String
    "configure"]
    					IO Bool -> IO Bool -> IO Bool
    forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
    <&&> (String -> String -> IO ()
    writeFile String
    "configured" String
    "" IO () -> IO Bool -> IO Bool
    forall a b. IO a -> IO b -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    True)
    		)
    	-- The -j1 is to only run one job at a time -- in some situations,
    	-- eg in qemu, ghc does not run reliably in parallel.
    	cabal_build :: IO Bool
    cabal_build = [String] -> IO Bool
    cabal [String
    "build", String
    "-j1", String
    "propellor-config"]

    stackBuild :: Maybe System -> IO Bool
    stackBuild :: Maybe System -> IO Bool
    stackBuild Maybe System
    _msys = do
    	Bool -> String -> IO ()
    createDirectoryIfMissing Bool
    True String
    builddest
    	IO Bool -> (IO Bool, IO Bool) -> IO Bool
    forall (m :: * -> *) a. Monad m => m Bool -> (m a, m a) -> m a
    ifM ([String] -> IO Bool
    stack [String]
    buildparams)
    		( do
    			String -> IO ()
    symlinkPropellorBin (String
    builddest String -> ShowS
    </> String
    "propellor-config")
    			Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    True
    		, Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False
    		)
      where
     	builddest :: String
    builddest = String
    ".built"
    	buildparams :: [String]
    buildparams =
    		[ String
    "--local-bin-path", String
    builddest
    		, String
    "build"
    		, String
    ":propellor-config" -- only build config program
    		, String
    "--copy-bins"
    		]

    -- Atomic symlink creation/update.
    symlinkPropellorBin :: FilePath -> IO ()
    symlinkPropellorBin :: String -> IO ()
    symlinkPropellorBin String
    bin = do
    	String -> String -> IO ()
    createSymbolicLink String
    bin (ShowS
    tmpfor String
    dest)
    	String -> String -> IO ()
    rename (ShowS
    tmpfor String
    dest) String
    dest
      where
    	dest :: String
    dest = String
    "propellor"

    tmpfor :: FilePath -> FilePath
    tmpfor :: ShowS
    tmpfor String
    f = String
    f String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ".propellortmp"

    make :: FilePath -> [FilePath] -> IO Bool -> IO ()
    make :: String -> [String] -> IO Bool -> IO ()
    make String
    dest [String]
    srcs IO Bool
    builder = do
    	dt <- String -> IO (Maybe UTCTime)
    getmtime String
    dest
    	st <- mapM getmtime srcs
    	when (dt == Nothing || any (> dt) st) $
    		unlessM builder $
    			error $ "failed to make " ++ dest
      where
    	getmtime :: String -> IO (Maybe UTCTime)
    getmtime = IO UTCTime -> IO (Maybe UTCTime)
    forall (m :: * -> *) a. MonadCatch m => m a -> m (Maybe a)
    catchMaybeIO (IO UTCTime -> IO (Maybe UTCTime))
    -> (String -> IO UTCTime) -> String -> IO (Maybe UTCTime)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> IO UTCTime
    getModificationTime

    cabal :: [String] -> IO Bool
    cabal :: [String] -> IO Bool
    cabal = String -> [CommandParam] -> IO Bool
    boolSystem String
    "cabal" ([CommandParam] -> IO Bool)
    -> ([String] -> [CommandParam]) -> [String] -> IO Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> CommandParam) -> [String] -> [CommandParam]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> CommandParam
    Param

    stack :: [String] -> IO Bool
    stack :: [String] -> IO Bool
    stack = String -> [CommandParam] -> IO Bool
    boolSystem String
    "stack" ([CommandParam] -> IO Bool)
    -> ([String] -> [CommandParam]) -> [String] -> IO Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> CommandParam) -> [String] -> [CommandParam]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> CommandParam
    Param