Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static executables on other platforms: Cross compilation via Nix #3281

Open
wolfgangwalther opened this issue Feb 26, 2024 · 0 comments
Open
Labels
docker nix related to Nix tooling

Comments

@wolfgangwalther
Copy link
Member

We have been building a static executable for x64-linux for quite a while now. From this, the minimal docker image is created.

It would be really cool, if we could build the static executable on other platforms, too - and then create a multi-layer docker image, which provides minimal image size for different platforms.

One way to do this would be to run a nix environment on linux x64 and build the static executable for other platforms via cross-compiling.

This issue collects the current state of affairs.


I have been building GHC 9.4.8 as a cross-compiler for aarch64 successfully. However, when compiling PostgREST and its dependencies a major challenge comes up quickly: Template Haskell.

Template Haskell is a challenge, because it needs to be run during compilation. Because the cross-compiler compiles for the target platform, the compiled TH code can't be executed on the build platform. GHC will then fail like this:

Couldn't find a target code interpreter. Try with -fexternal-interpreter

There are a few different possible approaches how to deal with that:

  1. Remove all Template Haskell from PostgREST and its dependencies. This is not easy, because a few transitive dependencies use TH and for example the whole hasql ecosystem depends on them. We also use it ourselves for the Paths_ module, although that could probably be worked around.
  2. Compile and execute TH natively and then pass the resulting splices to the cross-compiler. The idea is, that most Template Haskell applications are merely "code generation", e.g. helpers to create a lot of similar shaped haskell code, which is then compiled to the actual program. Many of those uses are actually independent of the target system - so it could be possible to compile and execute those splices on the build system and then pass the generated code to the cross-compiler. I experimented with that and I was able to cross-compile almost all our dependencies that way. I was doing it manually and this quickly got very annoying, that's why I didn't finish this, yet. This could possibly be automated, but it would never be the "clean" solution. Once any of our dependencies would use "unsafe" Template Haskell, this might result in hard to debug errors.
  3. GHC comes with a ghc-iserv executable. This is basically a wrapper around the GHCi library - so it allows to execute compiled code. For a GHC cross-compiler, this executable is built for the target platform. This can therefore be run with qemu and passed to -fexternal-interpreter. This should make Template Haskell just work. I tried this with GHC 9.4.8 and was able to make it work - but at some point hit a compiler bug in GHC 9.4.8 when cross-compiling to aarch64. My understanding is, that this bug was fixed in GHC 9.6 and 9.8 - but not in the 9.4 series. Therefore, I tried upgrading to GHC 9.6 / 9.8, but couldn't make that work, yet. GHC 9.6 doesn't build for pkgsStatic in nixpkgs, yet - and GHC 9.8 does build for pkgsStatic, but fails with the same TH-related error message as mentioned above, but already for pkgsStatic (no cross-platform-compiling involved, yet). This is tracked in Static linking with GHC 9.8.1 is broken NixOS/nixpkgs#275304.

The last approach is the most promising in principle and is also tracked in NixOS/nixpkgs#248979 upstream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docker nix related to Nix tooling
Development

No branches or pull requests

1 participant