|
| 1 | +#!/usr/bin/env sh |
| 2 | +set -eu |
| 3 | + |
| 4 | +################################################################################ |
| 5 | +# Pre-flight checks. |
| 6 | +################################################################################ |
| 7 | + |
| 8 | +command -v git >/dev/null 2>&1 || { echo >&2 "git is not installed, aborting."; exit 1; } |
| 9 | +command -v buildah >/dev/null 2>&1 || { echo >&2 "buildah is not installed, aborting."; exit 1; } |
| 10 | + |
| 11 | +# Start the script in the top-level repository directory no matter what. |
| 12 | +cd "$( git rev-parse --show-toplevel )" |
| 13 | + |
| 14 | +################################################################################ |
| 15 | +# Argument parsing and related values. |
| 16 | +# |
| 17 | +# cf. https://sookocheff.com/post/bash/parsing-bash-script-arguments-with-shopts/ |
| 18 | +################################################################################ |
| 19 | + |
| 20 | +alpine_ver="3.14" |
| 21 | +container="ghc-with-tooling" |
| 22 | +image="ghc-with-tooling" |
| 23 | +ghc_version="8.10.7" |
| 24 | + |
| 25 | +# NOTE: The logic associated with this will have to change for GHC 9.x and up to |
| 26 | +# support the changes introduced with the switch to `ghc-bignum`. |
| 27 | +numeric="gmp" |
| 28 | + |
| 29 | +cabal_version="3.6.0.0" |
| 30 | + |
| 31 | +stack_version="2.7.3" |
| 32 | +stack_expected_checksum="c5bce24defa2b2b86f1bbb14bed4f1ee83bec14c6ed9fcc81174d5473fbf3450" |
| 33 | + |
| 34 | +hls_version="1.4.0" |
| 35 | + |
| 36 | +usage="USAGE: $0 |
| 37 | + -h show this help text |
| 38 | + -a ALPINE_VER override the default Alpine version |
| 39 | + default: ${alpine_ver} |
| 40 | + -c CONTAINER override the default container name |
| 41 | + default: ${container} |
| 42 | + -g GHC_VER override the numeric library GHC is built against; either 'gmp' or 'simple' |
| 43 | + default: ${ghc_version} |
| 44 | + -i IMAGE override the default image base name |
| 45 | + default: ${image} |
| 46 | + -n NUMERIC override the numeric library GHC is built against; either 'gmp' or 'simple' |
| 47 | + default: ${numeric} |
| 48 | + -C CABAL_VER override the default version of 'cabal-install' to build |
| 49 | + default: ${cabal_version} |
| 50 | + -S STACK_VER override the default version of 'stack' to download and install |
| 51 | + default: ${stack_version} |
| 52 | + -H HLS_VER override the default version of 'haskell-language-server' to download and install |
| 53 | + default: ${hls_version}" |
| 54 | + |
| 55 | +while getopts "a:c:g:i:n:C:S:H:h" opt; do |
| 56 | + case ${opt} in |
| 57 | + a ) { |
| 58 | + alpine_ver="${OPTARG}" |
| 59 | + };; |
| 60 | + c ) { |
| 61 | + container="${OPTARG}" |
| 62 | + };; |
| 63 | + g ) { |
| 64 | + ghc_version="${OPTARG}" |
| 65 | + };; |
| 66 | + i ) { |
| 67 | + image="${OPTARG}" |
| 68 | + };; |
| 69 | + n ) { |
| 70 | + if [ "${OPTARG}" = "gmp" ] || [ "${OPTARG}" = "simple" ]; then |
| 71 | + numeric="${OPTARG}" |
| 72 | + else |
| 73 | + echo "Invalid NUMERIC argument (i.e. '-n')." >&2 |
| 74 | + echo "Expected either 'gmp' or 'simple', got '${OPTARG}'" >&2 |
| 75 | + exit 1 |
| 76 | + fi; |
| 77 | + };; |
| 78 | + C ) { |
| 79 | + cabal_version="${OPTARG}" |
| 80 | + };; |
| 81 | + S ) { |
| 82 | + stack_version="${OPTARG}" |
| 83 | + };; |
| 84 | + H ) { |
| 85 | + hls_version="${OPTARG}" |
| 86 | + };; |
| 87 | + h ) { |
| 88 | + echo "${usage}" |
| 89 | + exit 0 |
| 90 | + };; |
| 91 | + \? ) { |
| 92 | + echo "${usage}" |
| 93 | + exit 1 |
| 94 | + };; |
| 95 | + esac |
| 96 | +done |
| 97 | +shift $((OPTIND -1)) |
| 98 | + |
| 99 | +if [ "$#" -ne 0 ]; then |
| 100 | + exit 1 |
| 101 | +fi |
| 102 | + |
| 103 | +# Add the GHC version and numeric library to container and image names. |
| 104 | +container="${container}-${numeric}-${ghc_version}" |
| 105 | +image="${image}-${numeric}" |
| 106 | + |
| 107 | +################################################################################ |
| 108 | +# Container. |
| 109 | +################################################################################ |
| 110 | + |
| 111 | +# Create the container that will be used to download and/or compile various bits |
| 112 | +# of Haskell tooling. |
| 113 | +buildah \ |
| 114 | + --signature-policy=./policy.json \ |
| 115 | + --name "${container}" \ |
| 116 | + from --pull "docker.io/library/alpine:${alpine_ver}" |
| 117 | + |
| 118 | +# Install common dependencies. |
| 119 | +buildah run "${container}" \ |
| 120 | + apk add \ |
| 121 | + binutils-gold \ |
| 122 | + curl \ |
| 123 | + gcc \ |
| 124 | + musl-dev \ |
| 125 | + ncurses-libs \ |
| 126 | + xz \ |
| 127 | + zlib |
| 128 | + |
| 129 | +if [ "${numeric}" = "gmp" ]; then |
| 130 | + buildah run "${container}" \ |
| 131 | + apk add gmp-dev |
| 132 | +fi |
| 133 | + |
| 134 | +################################################################################ |
| 135 | +# Copy `ghcup` (and files) from other containers. |
| 136 | +################################################################################ |
| 137 | + |
| 138 | +buildah unshare ./common/copy_ghcup.sh "ghcup" "${container}" |
| 139 | +buildah unshare ./common/copy_ghcup_bin_dir.sh "ghc-${numeric}:${ghc_version}" "${container}" |
| 140 | + |
| 141 | +# Add `ghcup`'s bin directory to the container's `PATH`. |
| 142 | +# |
| 143 | +# NOTE: This little bit of indirection is needed to get the container's 'PATH', |
| 144 | +# since '$PATH' would be sourced from the host. |
| 145 | +cntr_path=$(buildah run "${container}" printenv PATH) |
| 146 | +buildah config \ |
| 147 | + --env PATH="${cntr_path}:/root/.ghcup/bin" \ |
| 148 | + "${container}" |
| 149 | + |
| 150 | +################################################################################ |
| 151 | +# Download and install `cabal-install. |
| 152 | +# |
| 153 | +# TODO: Compile `cabal-install` from source for non-gmp images so that the |
| 154 | +# library won't be necessary at all. |
| 155 | +# |
| 156 | +# This should be easier once `ghcup` re-adds the `ghcup compile cabal` |
| 157 | +# subcommand; cf. https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/254 |
| 158 | +################################################################################ |
| 159 | + |
| 160 | +buildah run "${container}" \ |
| 161 | + ghcup install cabal "${cabal_version}" |
| 162 | + |
| 163 | +################################################################################ |
| 164 | +# Download and install `stack`. |
| 165 | +################################################################################ |
| 166 | + |
| 167 | +# Fetch `stack`. |
| 168 | +buildah run "${container}" \ |
| 169 | + wget \ |
| 170 | + -O "/tmp/stack-${stack_version}.tar.gz" \ |
| 171 | + "https://github.com/commercialhaskell/stack/releases/download/v${stack_version}/stack-${stack_version}-linux-x86_64-static.tar.gz" |
| 172 | + |
| 173 | +# Copy the checksum validation script into the container... |
| 174 | +buildah copy --chmod 111 "${container}" \ |
| 175 | + ./common/validate_checksum.sh \ |
| 176 | + /tmp/validate_checksum.sh |
| 177 | + |
| 178 | +# ...and verify that the expected and actual actual `stack` checksums match. |
| 179 | +buildah run "${container}" \ |
| 180 | + ./tmp/validate_checksum.sh \ |
| 181 | + "/tmp/stack-${stack_version}.tar.gz" \ |
| 182 | + "${stack_expected_checksum}" |
| 183 | + |
| 184 | +# Extract the `stack` binary... |
| 185 | +buildah run "${container}" \ |
| 186 | + tar -xvzf "/tmp/stack-${stack_version}.tar.gz" \ |
| 187 | + --directory "/tmp" |
| 188 | + |
| 189 | +# ...relocate it... |
| 190 | +buildah run "${container}" \ |
| 191 | + mv "/tmp/stack-${stack_version}-linux-x86_64-static/stack" /usr/bin/stack |
| 192 | + |
| 193 | +# ...make it executable... |
| 194 | +buildah run "${container}" \ |
| 195 | + chmod +x /usr/bin/stack |
| 196 | + |
| 197 | +# ...and clean up after ourselves. |
| 198 | +buildah run "${container}" rm "/tmp/stack-${stack_version}.tar.gz" |
| 199 | +buildah run "${container}" rm -rf "/tmp/stack-${stack_version}-linux-x86_64-static" |
| 200 | +buildah run "${container}" rm /tmp/validate_checksum.sh |
| 201 | + |
| 202 | +################################################################################ |
| 203 | +# Compile the Haskell Language Server (HLS) from source. |
| 204 | +################################################################################ |
| 205 | + |
| 206 | +# Install HLS-specific dependencies. |
| 207 | +buildah run "${container}" \ |
| 208 | + apk add \ |
| 209 | + ncurses-dev \ |
| 210 | + zlib-dev |
| 211 | + |
| 212 | +# NOTE: This is just some arbirary time, but it's fixed here so that the package |
| 213 | +# set chosen by `cabal-install` should be consistent. |
| 214 | +buildah run "${container}" \ |
| 215 | + cabal update hackage.haskell.org,2021-10-03T23:05:17Z |
| 216 | + |
| 217 | +# Compile HLS... |
| 218 | +buildah run "${container}" \ |
| 219 | + ghcup compile hls \ |
| 220 | + --version "${hls_version}" \ |
| 221 | + --jobs "$(nproc)" \ |
| 222 | + "${ghc_version}" |
| 223 | + |
| 224 | +# ...and set it as the default version. |
| 225 | +buildah run "${container}" \ |
| 226 | + ghcup set hls "${hls_version}" |
| 227 | + |
| 228 | +# Remove HLS-specific system dependencies... |
| 229 | +buildah run "${container}" \ |
| 230 | + apk del \ |
| 231 | + ncurses-dev \ |
| 232 | + zlib-dev |
| 233 | + |
| 234 | +# ...and leftover `cabal-install` build cruft. |
| 235 | +buildah run "${container}" \ |
| 236 | + rm -rf /root/.cabal |
| 237 | + |
| 238 | +################################################################################ |
| 239 | +# Generate the final image. |
| 240 | +################################################################################ |
| 241 | + |
| 242 | +buildah \ |
| 243 | + --signature-policy=./policy.json \ |
| 244 | + commit --rm "${container}" "${image}:${ghc_version}" |
0 commit comments