summaryrefslogtreecommitdiff
path: root/src/Propellor/Bootstrap.hs
blob: 1cf921cf7dbc027ef629ae821b417dbffd06f156 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
module Propellor.Bootstrap (
	bootstrapPropellorCommand,
	checkBinaryCommand,
	installGitCommand,
	buildPropellor,
) where

import Propellor

import System.Posix.Files
import Data.List

type ShellCommand = String

-- 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 :: ShellCommand
bootstrapPropellorCommand = checkDepsCommand ++ 
	"&& if ! test -x ./propellor; then " 
		++ buildCommand ++ 
	"; fi;" ++ checkBinaryCommand

-- 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 2>/dev/null; then " ++ go ++ "; fi"
  where
	go = intercalate " && "
		[ "cabal clean"
		, buildCommand
		]

buildCommand :: ShellCommand
buildCommand = intercalate " && "
	[ "cabal configure"
	, "cabal build"
	, "ln -sf dist/build/propellor-config/propellor-config propellor"
	]

-- Run cabal configure to check if all dependencies are installed;
-- if not, run the depsCommand.
checkDepsCommand :: ShellCommand
checkDepsCommand = "if ! cabal configure >/dev/null 2>&1; then " ++ depsCommand ++ "; fi"

-- Install build dependencies of propellor.
--
-- First, try to install ghc, cabal, gnupg, 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.
--
-- Note: May succeed and leave some deps not installed.
depsCommand :: ShellCommand
depsCommand = "( " ++ intercalate " ; " (concat [osinstall, cabalinstall]) ++ " ) || true"
  where
	osinstall = "apt-get update" : map aptinstall debdeps

	cabalinstall = 
		[ "cabal update"
		, "cabal install --only-dependencies"
		]

	aptinstall p = "apt-get --no-upgrade --no-install-recommends -y install " ++ p

	-- This is the same build deps listed in debian/control.
	debdeps =
		[ "gnupg"
		, "ghc"
		, "cabal-install"
		, "libghc-async-dev"
		, "libghc-missingh-dev"
		, "libghc-hslogger-dev"
		, "libghc-unix-compat-dev"
		, "libghc-ansi-terminal-dev"
		, "libghc-ifelse-dev"
		, "libghc-network-dev"
		, "libghc-quickcheck2-dev"
		, "libghc-mtl-dev"
		, "libghc-transformers-dev"
		, "libghc-exceptions-dev"
		]

installGitCommand :: ShellCommand
installGitCommand = "if ! git --version >/dev/null; then apt-get update && apt-get --no-install-recommends --no-upgrade -y install git; fi"

buildPropellor :: IO ()
buildPropellor = unlessM (actionMessage "Propellor build" build) $
	errorMessage "Propellor build failed!"

-- Build propellor using cabal, and symlink propellor to where cabal
-- leaves the built binary.
--
-- 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.
build :: IO Bool
build = catchBoolIO $ do
	make "dist/setup-config" ["propellor.cabal"] $
		cabal ["configure"]
	unlessM (cabal ["build"]) $ do
		void $ cabal ["configure"]
		unlessM (cabal ["build"]) $
			error "cabal build failed"
	nukeFile "propellor"
	createSymbolicLink "dist/build/propellor-config/propellor-config" "propellor"
	return True

make :: FilePath -> [FilePath] -> IO Bool -> IO ()
make dest srcs builder = do
	dt <- getmtime dest
	st <- mapM getmtime srcs
	when (dt == Nothing || any (> dt) st) $
		unlessM builder $
			error $ "failed to make " ++ dest
  where
	getmtime = catchMaybeIO . getModificationTime

cabal :: [String] -> IO Bool
cabal = boolSystem "cabal" . map Param