Jump to content

Go: Difference between revisions

From Official NixOS Wiki
imported>Shindakun
m Corrected spelling in Home Manager heading
DHCP (talk | contribs)
m use monospace formatting where applicable, add more links, update link to the Go homepage
 
(8 intermediate revisions by 7 users not shown)
Line 1: Line 1:
[https://golang.org/ 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.
[https://go.dev/ 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 ==
== buildGoModule ==
The nixpkgs library function '''buildGoModule''' ([https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/go/module.nix implementation]) works in most cases of packaging go modules or applications.
nixpkgs includes a library function called '''buildGoModule''' ([https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/go/module.nix implementation]) See [https://nixos.org/manual/nixpkgs/stable/#sec-language-go nixpkgs manual '''Language: Go''']
See [https://nixos.org/manual/nixpkgs/stable/#sec-language-go nixpkgs manual '''Language: Go''']


=== buildGoModule ===
<code>buildGoModule</code> uses the version of Go that's included in <code>nixpkgs</code> to build the software.
 
==== Using a specific version of Go ====
To build for a specific version of Go, you may need to find the appropriate <code>pkgs.buildGoXXXModule</code> function to use.
 
This function may not be present in the version of nixpkgs that you're using, for example, <code>buildGo122Module</code> is not available in <code>github:NixOS/nixpkgs/nixos-23.05</code>, but is available in <code>github:NixOS/nixpkgs/nixos-unstable</code>.
 
==== Subpackages ====
By default, <code>buildGoModule</code> will attempt to build the <code>main</code> package that's in the root of the source code location.
 
However, it's a common pattern in Go applications to have binaries within the <code>./cmd/binary-name</code> directory instead.
 
Setting the <code>subPackages</code> attribute to be a list of the packages to build supports this pattern.
 
==== Example (downloading source code from Github) ====
The following <code>flake.nix</code> demonstrates how to build a Go module, where the source code is located in Github. To use it, copy this file as <code>flake.nix</code> into an empty directory on your computer, and run <code>nix build</code>. Nix will download the source code, including dependencies, and produce a <code>./result</code> folder containing a <code>ziti</code> binary.
 
Running <code>nix shell</code> will create a shell, where you can execute the <code>ziti</code> binary.<syntaxhighlight lang="nix">
{
  description = "OpenZiti";
 
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };
 
  outputs = { self, nixpkgs }:
    let
      allSystems = [
        "x86_64-linux" # 64-bit Intel/AMD Linux
        "aarch64-linux" # 64-bit ARM Linux
        "x86_64-darwin" # 64-bit Intel macOS
        "aarch64-darwin" # 64-bit ARM macOS
      ];
      forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
        pkgs = import nixpkgs { inherit system; };
      });
    in
    {
      packages = forAllSystems ({ pkgs }: {
        default = pkgs.buildGo122Module rec {
          pname = "openziti";
          version = "1.0.0";
          subPackages = [ "ziti" ];
          src = pkgs.fetchFromGitHub {
            owner = "openziti";
            repo = "ziti";
            rev = "v${version}";
            sha256 = "sha256-2li/+XWKk+lybB1DE0unKvQrA0pKE9VIRFoEYMcbLS8=";
          };
          vendorHash = "sha256-uyjQd5kB61UEKSl1Qf1Gu6Fr40l4KixHSnEtTMq58Vc=";
        };
      });
    };
}
 
</syntaxhighlight>
 
==== Example (local source) ====
If you want to build a local project with Nix, replace the <code>src</code> attribute to be the local directory, e.g.:<syntaxhighlight lang="nix">
  some-package = buildGoModule {
    src = ./.
  };
</syntaxhighlight>
 
==== Monorepo support ====
the <tt>go.mod</tt> file must be in the source root for <tt>buildGoModule</tt>.
the <tt>go.mod</tt> file must be in the source root for <tt>buildGoModule</tt>.
to change the source root, use
to change the source root, use
Line 28: Line 91:
</syntaxhighlight>
</syntaxhighlight>


==Install using Home Manager==
== Using cgo on NixOS ==
Enable go in home manager config `home.nix` in `~/.config/nixpkgs`.
On NixOS, include files and libraries aren't kept in a system-wide search path. If a Go program uses [https://go.dev/wiki/cgo cgo] and attempts to include [[C]] header files, or link against libraries, compilation is likely to fail.
<syntaxhighlight lang=nix>
programs.go.enable = true;
</syntaxhighlight>
 
==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, <tt>nix-shell</tt> can be used to enter an environment which provides the requested development dependencies.
In order to expose header files and libraries in environment variable search paths, <tt>nix-shell</tt> can be used to enter an environment which provides the requested development dependencies.


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


<syntaxhighlight lang="console">
<syntaxhighlight lang="console">
Line 52: Line 109:
If you intend to compile against glibc statically (such as via <tt>go build -ldflags "-s -w -linkmode external -extldflags -static"</tt>), add <tt>glibc.static</tt> to the list of packages passed to <tt>nix-shell</tt>.
If you intend to compile against glibc statically (such as via <tt>go build -ldflags "-s -w -linkmode external -extldflags -static"</tt>), add <tt>glibc.static</tt> to the list of packages passed to <tt>nix-shell</tt>.


If you encounter [https://github.com/go-delve/delve/issues/3085 this issue] and receive an error about _FORTIFY_SOURCE when running delve (for example in VSCode), put <tt>hardeningDisable = [ "fortify" ];</tt> inside shell.nix or in the <tt>mkShell</tt> invocation argument like this:
If you encounter [https://github.com/go-delve/delve/issues/3085 this issue] and receive an error about <code>_FORTIFY_SOURCE</code> when running delve (for example in [[Visual Studio Code|VSCode]]), put <tt>hardeningDisable = [ "fortify" ];</tt> inside <code>shell.nix</code> or in the <tt>mkShell</tt> invocation argument like this:
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
pkgs.mkShell {
pkgs.mkShell {
Line 61: Line 118:


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


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
Line 77: Line 134:
== Compile go program with static compile flag (take 2) ==
== 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 <code>warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking</code>. To really create a static build, use musl. Example based on buildGoModule example from documentation:
Linking against <tt>glibc.static</tt> does not really work because glibc does not really like static linking. You get a warning like <code>warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking</code>. To really create a static build, use musl. Example based on <code>buildGoModule</code> example from documentation:


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>
Line 92: Line 149:


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


   nativeBuildInputs = [musl];
   nativeBuildInputs = [musl];
Line 112: Line 173:


[[Category:Languages]]
[[Category:Languages]]
[[Category:Applications]]
[[Category:Go]]

Latest revision as of 19:32, 20 May 2026

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.

buildGoModule

nixpkgs includes a library function called buildGoModule (implementation) See nixpkgs manual Language: Go

buildGoModule uses the version of Go that's included in nixpkgs to build the software.

Using a specific version of Go

To build for a specific version of Go, you may need to find the appropriate pkgs.buildGoXXXModule function to use.

This function may not be present in the version of nixpkgs that you're using, for example, buildGo122Module is not available in github:NixOS/nixpkgs/nixos-23.05, but is available in github:NixOS/nixpkgs/nixos-unstable.

Subpackages

By default, buildGoModule will attempt to build the main package that's in the root of the source code location.

However, it's a common pattern in Go applications to have binaries within the ./cmd/binary-name directory instead.

Setting the subPackages attribute to be a list of the packages to build supports this pattern.

Example (downloading source code from Github)

The following flake.nix demonstrates how to build a Go module, where the source code is located in Github. To use it, copy this file as flake.nix into an empty directory on your computer, and run nix build. Nix will download the source code, including dependencies, and produce a ./result folder containing a ziti binary.

Running nix shell will create a shell, where you can execute the ziti binary.

{
  description = "OpenZiti";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs }:
    let
      allSystems = [
        "x86_64-linux" # 64-bit Intel/AMD Linux
        "aarch64-linux" # 64-bit ARM Linux
        "x86_64-darwin" # 64-bit Intel macOS
        "aarch64-darwin" # 64-bit ARM macOS
      ];
      forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
        pkgs = import nixpkgs { inherit system; };
      });
    in
    {
      packages = forAllSystems ({ pkgs }: {
        default = pkgs.buildGo122Module rec {
          pname = "openziti";
          version = "1.0.0";
          subPackages = [ "ziti" ];
          src = pkgs.fetchFromGitHub {
            owner = "openziti";
            repo = "ziti";
            rev = "v${version}";
            sha256 = "sha256-2li/+XWKk+lybB1DE0unKvQrA0pKE9VIRFoEYMcbLS8=";
          };
          vendorHash = "sha256-uyjQd5kB61UEKSl1Qf1Gu6Fr40l4KixHSnEtTMq58Vc=";
        };
      });
    };
}

Example (local source)

If you want to build a local project with Nix, replace the src attribute to be the local directory, e.g.:

  some-package = buildGoModule {
    src = ./.
  };

Monorepo support

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 ];
  };
}