Go

From NixOS Wiki
Revision as of 00:55, 13 August 2023 by Hypnosis2839 (talk | contribs) (→‎Install using Home Manager: remove outdated instructions, avoid encouraging installing dev packages globally)
Jump to: navigation, search

Go is a statically-typed language with syntax loosely derived from that of C, adding garbage collected memory management, type safety, some dynamic-typing capabilities, additional built-in types such as variable-length arrays and key-value maps, and a large standard library.

Packaging go modules

The nixpkgs library function buildGoModule (implementation) works in most cases of packaging go modules or applications. See nixpkgs manual Language: Go

buildGoModule

the go.mod file must be in the source root for buildGoModule. to change the source root, use

  some-package = buildGoModule {
    src = fetchFromGitHub {
      # ...
    } + "/path/to/module";
    # ...
  };

buildGoPackage

If no go.mod file is available, buildGoPackage (implementation) can be used. Dependencies must be specified manually in a deps.nix file, which is linked with

  some-package = buildGoPackage {
    # ...
    goDeps = ./deps.nix;
  };

Using cgo on NixOS

On NixOS, include files and libraries aren't kept in a system-wide search path. If a Go program uses cgo and attempts to include C header files, or link against libraries, compilation is likely to fail.

In order to expose header files and libraries in environment variable search paths, nix-shell can be used to enter an environment which provides the requested development dependencies.

For example, suppose a Go program includes <sys/capability.h> (provided by libcap), and links against libcap. To obtain an environment in which the program can be compiled, run:

$ nix-shell -p libcap go gcc

You can verify the presence of the necessary environment variables via the following command:

$ export | egrep 'NIX_.*(LDFLAGS|COMPILE|LINK)'

If you intend to compile against glibc statically (such as via go build -ldflags "-s -w -linkmode external -extldflags -static"), add glibc.static to the list of packages passed to nix-shell.

If you encounter this issue and receive an error about _FORTIFY_SOURCE when running delve (for example in VSCode), put hardeningDisable = [ "fortify" ]; inside shell.nix or in the mkShell invocation argument like this:

pkgs.mkShell {
  hardeningDisable = [ "fortify" ];
  buildInputs = [ pkgs.go_1_18 ];
};

Compile go program with static compile flag

If go build -ldflags "-s -w -linkmode external -extldflags -static" fails on NixOS, with the error message cannot find `-lpthread and cannot find -lc - it is because the linker cannot find static glibc to link with. You need to have glibc.static in your environment (and have CFLAGS/LDFLAGS adjusted accordingly). One way to achieve this is to have something like the following as shell.nix and run the compilation in a nix-shell:

with import <nixpkgs> {}; {
  devEnv = stdenv.mkDerivation {
    name = "dev";
    buildInputs = [ stdenv go glibc.static ];
    CFLAGS="-I${pkgs.glibc.dev}/include";
    LDFLAGS="-L${pkgs.glibc}/lib";
  };
}

Compile go program with static compile flag (take 2)

Linking against glibc.static does not really work because glibc does not really like static linking. You get a warning like warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking. To really create a static build, use musl. Example based on buildGoModule example from documentation:

pet = buildGoModule rec {
  pname = "pet";
  version = "0.3.4";

  src = fetchFromGitHub {
    owner = "knqyf263";
    repo = "pet";
    rev = "v${version}";
    sha256 = "0m2fzpqxk7hrbxsgqplkg7h2p7gv6s1miymv3gvw0cz039skag0s";
  };

  vendorSha256 = "1879j77k96684wi554rkjxydrj8g3hpp0kvxz03sd8dmwr3lh83j";

  ldflags = [
    "-s -w -X github.com/knqyf263/pet/cmd.version=${version}"
  ];

  nativeBuildInputs = [musl];

  CGO_ENABLED = 0;

  ldflags = [
    "-linkmode external"
    "-extldflags '-static -L${musl}/lib'"
  ];

  meta = with lib; {
    description = "Simple command-line snippet manager, written in Go";
    homepage = "https://github.com/knqyf263/pet";
    license = licenses.mit;
    maintainers = with maintainers; [ kalbasit ];
  };
}