How to cross compile using buildGoModule?

As mentioned numerous times the go compiler and the corresponding buildGoModule function as packaged in Nixpkgs has pre-built binaries for the target architecture and IIUC has CGO enabled by default. It very much prioritizes being able to work with everything else in Nixpkgs over making pure-Go (IIUC) packages built in a way that is not ‘silly and unnecessary’

If you really need something like buildGoModulePure I guess you can just create your own. As a test, I convinced goss (no idea what that is) that go (indirect dependency through buildGoModule) is for GOOS=linux GOARCH=arm64:

let pkgs = /* wherever you get pkgs from */;
in pkgs.goss.override {
  buildGoModule = pkgs.buildGoModule.override {
    go = pkgs.go // {
      GOOS = "linux";
      GOARCH = "arm64";

It does seem to build, and produces result/bin/linux_arm64/goss:

$ file result/bin/linux_arm64/goss
result/bin/linux_arm64/goss: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, Go BuildID=QPyYpUVC4wpsJRvhFf6K/6BbfIuYR72laBuJswAKp/4BI-fNwNf_TFn5_N1EIe/qVPFoQ6rqRVt0Jxlc5Sw, stripped

If I knew more about Go than just plugging source code repos into buildGoModule, I might be able to properly make a fully working buildGoModulePure that works with cross compilation without pulling in the target compiler and libraries. However this is all I can figure out for now.

1 Like

Yes, that’s perfectly sensible. And quite easy in case of Go projects.

I am considering that, but I’d prefer to first find a way that doesn’t involve changing such core functions. That’s a dangerous game to play. If doing what I want without changing buildGoModule turns to be too complicated or too ugly I might consider it, but I don’t want to break any established patterns or fundamental reasoning about what it’s supposed to be for.

That’s understandable, but even if I have to do that I’d still consider Nix to be a better option than Docker. Much more elegant and efficient at not rebuilding things that already were built, hence much better suited for CI.

Yes, that’s one way to do it, and might be the most elegant there is, if I want to avoid having to change GOOS or GOARCH with dirty tricks like export in some build phase. Which isn’t the prettiest, and could easily break.

I appreciate the input. I understand this is not the intended use, but I’m always assuming I’m missing something obvious when doing things in Nix, since that often was the case, but not this time.

1 Like

The answer is to use overrideAttrs

      packages.aarch64-darwin.auth = (pkgs.aarch64-darwin.buildGoModule {
        pname = "auth";
        version = "0.0.1";
        src = ./.;
        vendorHash = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=";
        CGO_ENABLED = 0;
      }).overrideAttrs (old: old // { GOOS = "linux"; GOARCH = "amd64"; });

This works fine and produces a statically linked x86_64-linux binary on MacOS

Hosted by Flying Circus.