lsupg Static Builds With GHC 9 (Part 5)
Static building of lsupg
is now working, using a project-specific Dockerfile
to
create ephemeral Alpine Linux
containers with GHCup
builds of GHC.
The project currently uses a single Dockerfile
that works with all of the supported GHC versions, using the
GHC_VERSION
argument to specify the version to install when
the image is built. Images are tagged as
lsupg-build:${GHC_VERSION}
. Since the goal is to use the
latest versions of dependencies, the images are meant to be ephemeral.
New images should be built to make new releases. To support this goal,
the alpine:latest
image is used, and version numbers are
not specified when installing packages. In cases where the
build environment needs to be kept, you can export the image or
container to save it.
The Makefile
provides a command for making static
builds. When using Stack,
run make static CONFIG=stack-9.8.2.yaml
, using the
CONFIG
argument to specify the compiler/configuration. If
no CONFIG
is specified, stack.yaml
is used,
which is just a link to the current LTS configuration. When using Cabal, run
make static MODE=cabal GHC_VERSION=9.8.2
, using the
GHC_VERSION
argument to specify the compiler version. If no
GHC_VERSION
is specified, the version of ghc
in your PATH
is used.
The lsupg-build
image is automatically created if it
does not already exist. It is up to the developer to manage the
alpine:latest
and lsupg-build
images, however.
Note that lsupg
can be used to investigate if an image has pending upgrades, which is
the whole purpose of the program. For example, run
lsupg --docker lsupg-build:8.6.5
to check if there are
upgrades available for the lsupg-build:8.6.5
image.
This was easy to get working thanks to GHCup! There was only one
tricky part: passing Stack
flags. The project is configured to enable static building using a
static
flag, which is passed via the command-line. When one
or more flags are passed via the command-line, however, Stack ignores any flags that
are configured in the stack.yaml
file. It is therefore
necessary to parse the stack.yaml
configuration and pass
any flags configured there via the command-line, along with the
static
flag. I am currently doing this using a stack-yaml-flags
AWK script.
I worked through my backlog of tasks for this project and added support for GHC versions 8.6, 9.6, and 9.8. With all of the target versions now working, I checked the sizes of the (stripped) static executables.
GHC | Stack | Cabal |
---|---|---|
8.6.5 | 13,691,816 | 16,253,512 |
8.8.4 | 13,452,744 | 16,505,288 |
8.10.7 | 15,046,312 | 16,347,080 |
9.0.2 | 28,681,320 | 29,043,720 |
9.2.8 | 28,918,712 | 29,239,192 |
9.4.8 | 18,265,800 | 18,307,752 |
9.6.5 | 33,206,056 | 33,076,616 |
9.8.2 | 33,357,672 | 33,204,808 |
I am not sure why there is a difference in size between Stack and Cabal builds. Perhaps they are
being compiled using different options. Much more interesting, however,
is the small size of the GHC 9.4.8 builds. That is the version using the
dynamically linked official build of GHC! I confirmed that the lsupg
build is indeed static. This might be worth investigating.
I made one change to the lsupg
code, adding build information to the --version
output
(only) when the program is built on Alpine.
$ ./build/lsupg --version
lsupg-haskell 0.3.0.2 built on Alpine 3.19.1 using ghc 9.4.8
I still need to update the README
, and there may be some
other things that I should do first, but I plan on making a new release
soon.