summaryrefslogtreecommitdiff
path: root/src/Propellor/Bootstrap.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Propellor/Bootstrap.hs')
-rw-r--r--src/Propellor/Bootstrap.hs203
1 files changed, 151 insertions, 52 deletions
diff --git a/src/Propellor/Bootstrap.hs b/src/Propellor/Bootstrap.hs
index 2c8fa95a..08af6878 100644
--- a/src/Propellor/Bootstrap.hs
+++ b/src/Propellor/Bootstrap.hs
@@ -1,8 +1,16 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
module Propellor.Bootstrap (
+ Bootstrapper(..),
+ Builder(..),
+ defaultBootstrapper,
+ getBootstrapper,
bootstrapPropellorCommand,
checkBinaryCommand,
installGitCommand,
buildPropellor,
+ checkDepsCommand,
+ buildCommand,
) where
import Propellor.Base
@@ -14,74 +22,124 @@ 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 (Show, Typeable)
+
+data Builder = Cabal | Stack
+ deriving (Show, Typeable)
+
+defaultBootstrapper :: Bootstrapper
+defaultBootstrapper = Robustly Cabal
+
+-- | Gets the Bootstrapper for the Host propellor is running on.
+getBootstrapper :: Propellor Bootstrapper
+getBootstrapper = go <$> askInfo
+ where
+ go NoInfoVal = defaultBootstrapper
+ go (InfoVal bs) = bs
+
+getBuilder :: Bootstrapper -> Builder
+getBuilder (Robustly b) = b
+getBuilder OSOnly = 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 :: Maybe System -> ShellCommand
-bootstrapPropellorCommand msys = checkDepsCommand msys ++
+bootstrapPropellorCommand :: Bootstrapper -> Maybe System -> ShellCommand
+bootstrapPropellorCommand bs msys = checkDepsCommand bs msys ++
"&& if ! test -x ./propellor; then "
- ++ buildCommand ++
- "; fi;" ++ checkBinaryCommand
+ ++ buildCommand bs ++
+ "; fi;" ++ checkBinaryCommand bs
-- Use propellor --check to detect if the local propellor binary has
-- stopped working (eg due to library changes), and must be rebuilt.
-checkBinaryCommand :: ShellCommand
-checkBinaryCommand = "if test -x ./propellor && ! ./propellor --check; then " ++ go ++ "; fi"
+checkBinaryCommand :: Bootstrapper -> ShellCommand
+checkBinaryCommand bs = "if test -x ./propellor && ! ./propellor --check; then " ++ go (getBuilder bs) ++ "; fi"
where
- go = intercalate " && "
+ go Cabal = intercalate " && "
[ "cabal clean"
- , buildCommand
+ , buildCommand bs
+ ]
+ go Stack = intercalate " && "
+ [ "stack clean"
+ , buildCommand bs
]
-buildCommand :: ShellCommand
-buildCommand = intercalate " && "
- [ "cabal configure"
- , "cabal build propellor-config"
- , "ln -sf dist/build/propellor-config/propellor-config propellor"
- ]
+buildCommand :: Bootstrapper -> ShellCommand
+buildCommand bs = intercalate " && " (go (getBuilder bs))
+ where
+ go Cabal =
+ [ "cabal configure"
+ , "cabal build propellor-config"
+ , "ln -sf dist/build/propellor-config/propellor-config propellor"
+ ]
+ go Stack =
+ [ "stack build :propellor-config"
+ , "ln -sf $(stack path --dist-dir)/build/propellor-config/propellor-config propellor"
+ ]
--- Run cabal configure to check if all dependencies are installed;
--- if not, run the depsCommand.
-checkDepsCommand :: Maybe System -> ShellCommand
-checkDepsCommand sys = "if ! cabal configure >/dev/null 2>&1; then " ++ depsCommand sys ++ "; fi"
+-- Check if all dependencies are installed; if not, run the depsCommand.
+checkDepsCommand :: Bootstrapper -> Maybe System -> ShellCommand
+checkDepsCommand bs sys = go (getBuilder bs)
+ where
+ go Cabal = "if ! cabal configure >/dev/null 2>&1; then " ++ depsCommand bs sys ++ "; fi"
+ go Stack = "if ! stack build --dry-run >/dev/null 2>&1; then " ++ depsCommand bs sys ++ "; fi"
--- Install build dependencies of propellor.
---
--- First, try to install ghc, cabal, gnupg, and all haskell libraries that
--- propellor uses from OS packages.
+-- 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 Debian
-- (eg, Debian wheezy lacks async), or propellor may need a newer version.
--- So, as a second step, cabal is used to install all dependencies.
+-- So, as a second step, any other dependencies are installed from source
+-- using the builder.
--
-- Note: May succeed and leave some deps not installed.
-depsCommand :: Maybe System -> ShellCommand
-depsCommand msys = "( " ++ intercalate " ; " (concat [osinstall, cabalinstall]) ++ " ) || true"
+depsCommand :: Bootstrapper -> Maybe System -> ShellCommand
+depsCommand bs msys = "( " ++ intercalate " ; " (go bs) ++ ") || true"
where
- osinstall = case msys of
- Just (System (FreeBSD _) _) -> map pkginstall fbsddeps
- Just (System (Debian _ _) _) -> useapt
- Just (System (Buntish _) _) -> useapt
- -- assume a debian derived system when not specified
- Nothing -> useapt
-
- useapt = "apt-get update" : map aptinstall debdeps
-
- cabalinstall =
+ go (Robustly Cabal) = osinstall Cabal ++
[ "cabal update"
, "cabal install --only-dependencies"
+ ]
+ go (Robustly Stack) = osinstall Stack ++
+ [ "stack setup"
+ , "stack build --only-dependencies :propellor-config"
]
+ go OSOnly = osinstall Cabal
+
+ osinstall builder = case msys of
+ Just (System (FreeBSD _) _) -> map pkginstall (fbsddeps builder)
+ Just (System (ArchLinux) _) -> map pacmaninstall (archlinuxdeps builder)
+ Just (System (Debian _ _) _) -> useapt builder
+ Just (System (Buntish _) _) -> useapt builder
+ -- assume a Debian derived system when not specified
+ Nothing -> useapt builder
+
+ useapt builder = "apt-get update" : map aptinstall (debdeps builder)
aptinstall p = "DEBIAN_FRONTEND=noninteractive apt-get -qq --no-upgrade --no-install-recommends -y install " ++ p
pkginstall p = "ASSUME_ALWAYS_YES=yes pkg install " ++ p
+ pacmaninstall p = "pacman -S --noconfirm --needed " ++ p
-- This is the same deps listed in debian/control.
- debdeps =
+ debdeps Cabal =
[ "gnupg"
, "ghc"
, "cabal-install"
, "libghc-async-dev"
- , "libghc-missingh-dev"
+ , "libghc-split-dev"
, "libghc-hslogger-dev"
, "libghc-unix-compat-dev"
, "libghc-ansi-terminal-dev"
@@ -92,14 +150,19 @@ depsCommand msys = "( " ++ intercalate " ; " (concat [osinstall, cabalinstall])
, "libghc-exceptions-dev"
, "libghc-stm-dev"
, "libghc-text-dev"
- , "make"
+ , "libghc-hashable-dev"
+ ]
+ debdeps Stack =
+ [ "gnupg"
+ , "haskell-stack"
]
- fbsddeps =
+
+ fbsddeps Cabal =
[ "gnupg"
, "ghc"
, "hs-cabal-install"
, "hs-async"
- , "hs-MissingH"
+ , "hs-split"
, "hs-hslogger"
, "hs-unix-compat"
, "hs-ansi-terminal"
@@ -110,7 +173,35 @@ depsCommand msys = "( " ++ intercalate " ; " (concat [osinstall, cabalinstall])
, "hs-exceptions"
, "hs-stm"
, "hs-text"
- , "gmake"
+ , "hs-hashable"
+ ]
+ fbsddeps Stack =
+ [ "gnupg"
+ , "stack"
+ ]
+
+ archlinuxdeps Cabal =
+ [ "gnupg"
+ , "ghc"
+ , "cabal-install"
+ , "haskell-async"
+ , "haskell-split"
+ , "haskell-hslogger"
+ , "haskell-unix-compat"
+ , "haskell-ansi-terminal"
+ , "haskell-hackage-security"
+ , "haskell-ifelse"
+ , "haskell-network"
+ , "haskell-mtl"
+ , "haskell-transformers-base"
+ , "haskell-exceptions"
+ , "haskell-stm"
+ , "haskell-text"
+ , "hashell-hashable"
+ ]
+ archlinuxdeps Stack =
+ [ "gnupg"
+ , "stack"
]
installGitCommand :: Maybe System -> ShellCommand
@@ -121,31 +212,39 @@ installGitCommand msys = case msys of
[ "ASSUME_ALWAYS_YES=yes pkg update"
, "ASSUME_ALWAYS_YES=yes pkg install git"
]
+ (Just (System (ArchLinux) _)) -> use
+ [ "pacman -S --noconfirm --needed git"]
-- assume a debian derived system when not specified
Nothing -> use apt
where
- use cmds = "if ! git --version >/dev/null; then " ++ intercalate " && " cmds ++ "; fi"
+ use cmds = "if ! git --version >/dev/null 2>&1; then " ++ intercalate " && " cmds ++ "; fi"
apt =
[ "apt-get update"
, "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 mh = unlessM (actionMessage "Propellor build" (build msys)) $
+buildPropellor mh = unlessM (actionMessage "Propellor build" build) $
errorMessage "Propellor build failed!"
where
msys = case fmap (fromInfo . hostInfo) mh of
Just (InfoVal sys) -> Just sys
_ -> Nothing
--- Build propellor using cabal or stack, and symlink propellor to the
--- built binary.
-build :: Maybe System -> IO Bool
-build msys = catchBoolIO $ do
- bs <- getGitConfigValue "propellor.buildsystem"
- case bs of
- Just "stack" -> stackBuild msys
- _ -> cabalBuild msys
+ build = catchBoolIO $ do
+ case fromInfo (maybe mempty hostInfo mh) of
+ NoInfoVal -> do
+ bs <- getGitConfigValue "propellor.buildsystem"
+ case bs of
+ Just "stack" -> stackBuild msys
+ _ -> cabalBuild msys
+ InfoVal bs -> case getBuilder bs of
+ Cabal -> cabalBuild msys
+ Stack -> stackBuild 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.
@@ -178,7 +277,7 @@ cabalBuild msys = do
, case msys of
Nothing -> return False
Just sys ->
- boolSystem "sh" [Param "-c", Param (depsCommand (Just sys))]
+ boolSystem "sh" [Param "-c", Param (depsCommand (Robustly Cabal) (Just sys))]
<&&> cabal ["configure"]
)
cabal_build = cabal ["build", "propellor-config"]