haskellcabalcabal-installcabal-new

Freezing all dependencies when building executables with `cabal v2-install`


I am building a Docker image in which I want to bundle multiple executables. Each executable is defined in a different package, in my case pandoc, pandoc-citeproc, and pandoc-crossref. The build should be as reproducible as reasonably possible on a Debian/Ubuntu based system.

What I'd like to do is use (something like) a cabal.project.freeze file to ensure that all subsequent builds will use the same packages.

I'm aware that I can fix the version of the executables:

cabal v2-install pandoc-2.7.3 pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1

But this will not fix the versions of transitive dependencies, so rebuilding at different times may lead to subtly different build results. Can I somehow create and use a freeze file in this setup? Using v2-freeze seems to be of no use here:

$ cabal new-freeze pandoc-2.7.3 pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1
cabal: 'freeze' doesn't take any extra arguments: pandoc-2.7.3
pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1

Solution

  • Okay, there might be a better built-in way to do this kind of thing, but here's a hacky workaround that might be suitable for you until a real cabal expert comes along.

    The basic plan will be this: temporarily create a project with the three packages you care about -- just long enough to get a freeze file -- then use some simple text-editor macros to turn the freeze file into a v2-install command. So:

    % cabal unpack pandoc-2.7.3 pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1
    % echo >cabal.project packages: pandoc-2.7.3 pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1
    % cabal v2-freeze
    % sed "s/^constraints: /cabal v2-install pandoc-2.7.3 pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1 --constraint '/;s/^ \+/--constraint '/;s/,\$/' \\\\/;\$s/\$/'/" cabal.project.freeze >cabal-v2-install.sh
    

    Woof, that last one is a mouthful. It says:

    # Replace the starting "constraints" stanza with the v2-install command we want to
    # run. The first line of the stanza includes a constraint, so prefix it with
    # --constraint and start a quote.
    s/^constraints: /cabal v2-install pandoc-2.7.3 pandoc-citeproc-0.16.2 pandoc-crossref-0.3.4.1 --constraint '/
    # The line we just produced doesn't start with spaces, so this only fires on the
    # remaining lines. On those lines, it prefixes --constraint and starts a quote.
    s/^ \+/--constraint '/
    # Close the quote begun on each line, and replace cabal's line-continuation
    # character (,) with a shell's line-continuation character (\). The $ and \ are
    # escaped because we are inside the current shell's ""-quoted string.
    s/,\$/' \\\\/
    # The last line doesn't have a line-continuation character, but still needs its
    # quote closed. The two occurrences of $ are escaped because we are inside the
    # current shell's ""-quoted string.
    \$s/\$/'/
    

    You could also do these manually in an editor if you wanted. At the end of this process, which you can run in a temporary directory to ease cleanup afterwards, you should have a file named cabal-v2-install.sh with a command that will select the exact same versions and flags for all packages involved, including dependencies.