Merge pull request #225051 from ShamrockLee/go-module-overlay-stdenv

buildGoModule: Fix overriding with overlay-style stdenv
This commit is contained in:
Doron Behar 2024-08-16 15:44:15 +00:00 committed by GitHub
commit 0d920a91a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 219 additions and 32 deletions

View File

@ -62,6 +62,65 @@ The following is an example expression using `buildGoModule`:
}
```
### Obtaining and overriding `vendorHash` for `buildGoModule` {#buildGoModule-vendorHash}
We can use `nix-prefetch` to obtain the actual hash. The following command gets the value of `vendorHash` for package `pet`:
```sh
cd path/to/nixpkgs
nix-prefetch -E "{ sha256 }: ((import ./. { }).my-package.overrideAttrs { vendorHash = sha256; }).goModules"
```
To obtain the hash without external tools, set `vendorHash = lib.fakeHash;` and run the build. ([more details here](#sec-source-hashes)).
`vendorHash` can be overridden with `overrideAttrs`. Override the above example like this:
```nix
{
pet_0_4_0 = pet.overrideAttrs (
finalAttrs: previousAttrs: {
version = "0.4.0";
src = fetchFromGitHub {
inherit (previousAttrs.src) owner repo;
rev = "v${finalAttrs.version}";
hash = "sha256-gVTpzmXekQxGMucDKskGi+e+34nJwwsXwvQTjRO6Gdg=";
};
vendorHash = "sha256-dUvp7FEW09V0xMuhewPGw3TuAic/sD7xyXEYviZ2Ivs=";
}
);
}
```
### Overriding `goModules` (#buildGoModule-goModules-override)
Overriding `<pkg>.goModules` by calling `goModules.overrideAttrs` is unsupported. Still, it is possible to override the `vendorHash` (`goModules`'s `outputHash`) and the `pre`/`post` hooks for both the build and patch phases of the primary and `goModules` derivation. Alternatively, the primary derivation provides an overridable `passthru.overrideModAttrs` function to store the attribute overlay implicitly taken by `goModules.overrideAttrs`. Here's an example usage of `overrideModAttrs`:
```nix
{
pet-overridden = pet.overrideAttrs (
finalAttrs: previousAttrs: {
passthru = previousAttrs.passthru // {
# If the original package has an `overrideModAttrs` attribute set, you'd
# want to extend it, and not replace it. Hence we use
# `lib.composeExtensions`. If you are sure the `overrideModAttrs` of the
# original package trivially does nothing, you can safely replace it
# with your own by not using `lib.composeExtensions`.
overrideModAttrs = lib.composeExtensions previousAttrs.passthru.overrideModAttrs (
finalModAttrs: previousModAttrs: {
# goModules-specific overriding goes here
postBuild = ''
# Here you have access to the `vendor` directory.
substituteInPlace vendor/github.com/example/repo/file.go \
--replace-fail "panic(err)" ""
'';
}
);
};
}
);
}
```
## `buildGoPackage` (legacy) {#ssec-go-legacy}
The function `buildGoPackage` builds legacy Go programs, not supporting Go modules.

View File

@ -328,6 +328,10 @@
[buildRustPackage: Compiling Rust applications with Cargo](https://nixos.org/manual/nixpkgs/unstable/#compiling-rust-applications-with-cargo)
for more information.
- The `vendorHash` of Go packages built with `buildGoModule` can now be overridden with `overrideAttrs`.
`goModules`, `modRoot`, `vendorHash`, `deleteVendor`, and `proxyVendor` are now passed as derivation attributes.
`goModules` and `vendorHash` are no longer placed t under `passthru`.
- `hareHook` has been added as the language framework for Hare. From now on, it,
not the `hare` package, should be added to `nativeBuildInputs` when building
Hare programs.

View File

@ -7,7 +7,7 @@
, patches ? [ ]
# A function to override the goModules derivation
, overrideModAttrs ? (_oldAttrs: { })
, overrideModAttrs ? (finalAttrs: previousAttrs: { })
# path to go.mod and go.sum directory
, modRoot ? "./"
@ -58,18 +58,38 @@
assert goPackagePath != "" -> throw "`goPackagePath` is not needed with `buildGoModule`";
let
args = removeAttrs args' [ "overrideModAttrs" "vendorSha256" "vendorHash" ];
args = removeAttrs args' [ "overrideModAttrs" "vendorSha256" ];
GO111MODULE = "on";
GOTOOLCHAIN = "local";
goModules = if (vendorHash == null) then "" else
toExtension =
overlay0:
if lib.isFunction overlay0 then
final: prev:
if lib.isFunction (overlay0 prev) then
# `overlay0` is `final: prev: { ... }`
overlay0 final prev
else
# `overlay0` is `prev: { ... }`
overlay0 prev
else
# `overlay0` is `{ ... }`
final: prev: overlay0;
in
(stdenv.mkDerivation (finalAttrs:
args
// {
inherit modRoot vendorHash deleteVendor proxyVendor;
goModules = if (finalAttrs.vendorHash == null) then "" else
(stdenv.mkDerivation {
name = "${name}-go-modules";
name = "${finalAttrs.name or "${finalAttrs.pname}-${finalAttrs.version}"}-go-modules";
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [ go git cacert ];
nativeBuildInputs = (finalAttrs.nativeBuildInputs or [ ]) ++ [ go git cacert ];
inherit (args) src;
inherit (finalAttrs) src modRoot;
inherit (go) GOOS GOARCH;
inherit GO111MODULE GOTOOLCHAIN;
@ -77,15 +97,15 @@ let
# argue it's not ideal. Changing it may break vendor hashes in Nixpkgs and
# out in the wild. In anycase, it's documented in:
# doc/languages-frameworks/go.section.md
prePatch = args.prePatch or "";
patches = args.patches or [ ];
patchFlags = args.patchFlags or [ ];
postPatch = args.postPatch or "";
preBuild = args.preBuild or "";
postBuild = args.modPostBuild or "";
sourceRoot = args.sourceRoot or "";
setSourceRoot = args.setSourceRoot or "";
env = args.env or { };
prePatch = finalAttrs.prePatch or "";
patches = finalAttrs.patches or [ ];
patchFlags = finalAttrs.patchFlags or [ ];
postPatch = finalAttrs.postPatch or "";
preBuild = finalAttrs.preBuild or "";
postBuild = finalAttrs.modPostBuild or "";
sourceRoot = finalAttrs.sourceRoot or "";
setSourceRoot = finalAttrs.setSourceRoot or "";
env = finalAttrs.env or { };
impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
"GIT_PROXY_COMMAND"
@ -97,13 +117,13 @@ let
runHook preConfigure
export GOCACHE=$TMPDIR/go-cache
export GOPATH="$TMPDIR/go"
cd "${modRoot}"
cd "$modRoot"
runHook postConfigure
'';
buildPhase = args.modBuildPhase or (''
runHook preBuild
'' + lib.optionalString deleteVendor ''
'' + lib.optionalString finalAttrs.deleteVendor ''
if [ ! -d vendor ]; then
echo "vendor folder does not exist, 'deleteVendor' is not needed"
exit 10
@ -116,7 +136,7 @@ let
exit 10
fi
${if proxyVendor then ''
${if finalAttrs.proxyVendor then ''
mkdir -p "''${GOPATH}/pkg/mod/cache/download"
go mod download
'' else ''
@ -134,7 +154,7 @@ let
installPhase = args.modInstallPhase or ''
runHook preInstall
${if proxyVendor then ''
${if finalAttrs.proxyVendor then ''
rm -rf "''${GOPATH}/pkg/mod/cache/download/sumdb"
cp -r --reflink=auto "''${GOPATH}/pkg/mod/cache/download" $out
'' else ''
@ -152,20 +172,19 @@ let
dontFixup = true;
outputHashMode = "recursive";
outputHash = vendorHash;
outputHash = finalAttrs.vendorHash;
# Handle empty vendorHash; avoid
# error: empty hash requires explicit hash algorithm
outputHashAlgo = if vendorHash == "" then "sha256" else null;
}).overrideAttrs overrideModAttrs;
outputHashAlgo = if finalAttrs.vendorHash == "" then "sha256" else null;
}).overrideAttrs finalAttrs.passthru.overrideModAttrs;
package = stdenv.mkDerivation (args // {
nativeBuildInputs = [ go ] ++ nativeBuildInputs;
inherit (go) GOOS GOARCH;
GOFLAGS = GOFLAGS
++ lib.warnIf (lib.any (lib.hasPrefix "-mod=") GOFLAGS) "use `proxyVendor` to control Go module/vendor behavior instead of setting `-mod=` in GOFLAGS"
(lib.optional (!proxyVendor) "-mod=vendor")
(lib.optional (!finalAttrs.proxyVendor) "-mod=vendor")
++ lib.warnIf (builtins.elem "-trimpath" GOFLAGS) "`-trimpath` is added by default to GOFLAGS by buildGoModule when allowGoReference isn't set to true"
(lib.optional (!allowGoReference) "-trimpath");
inherit CGO_ENABLED enableParallelBuilding GO111MODULE GOTOOLCHAIN;
@ -181,12 +200,12 @@ let
export GOPROXY=off
export GOSUMDB=off
cd "$modRoot"
'' + lib.optionalString (vendorHash != null) ''
${if proxyVendor then ''
export GOPROXY=file://${goModules}
'' + lib.optionalString (finalAttrs.vendorHash != null) ''
${if finalAttrs.proxyVendor then ''
export GOPROXY="file://$goModules"
'' else ''
rm -rf vendor
cp -r --reflink=auto ${goModules} vendor
cp -r --reflink=auto "$goModules" vendor
''}
'' + ''
@ -307,12 +326,17 @@ let
disallowedReferences = lib.optional (!allowGoReference) go;
passthru = passthru // { inherit go goModules vendorHash; };
passthru = {
inherit go;
# Canonicallize `overrideModAttrs` as an attribute overlay.
# `passthru.overrideModAttrs` will be overridden
# when users want to override `goModules`.
overrideModAttrs = toExtension overrideModAttrs;
} // passthru;
meta = {
# Add default meta information
platforms = go.meta.platforms or lib.platforms.all;
} // meta;
});
in
package
}
))

View File

@ -27,6 +27,38 @@ let
expr = ((stdenvNoCC.mkDerivation { pname = "hello-no-final-attrs"; }).overrideAttrs { pname = "hello-no-final-attrs-overridden"; }).pname == "hello-no-final-attrs-overridden";
expected = true;
};
buildGoModule-overrideAttrs = {
expr = lib.all (
attrPath:
let
attrPathPretty = lib.concatStringsSep "." attrPath;
valueNative = lib.getAttrFromPath attrPath pet_0_4_0;
valueOverridden = lib.getAttrFromPath attrPath pet_0_4_0-overridden;
in
lib.warnIfNot
(valueNative == valueOverridden)
"pet_0_4_0.${attrPathPretty} (${valueNative}) does not equal pet_0_4_0-overridden.${attrPathPretty} (${valueOverridden})"
true
) [
[ "drvPath" ]
[ "name" ]
[ "pname" ]
[ "version" ]
[ "vendorHash" ]
[ "goModules" "drvPath" ]
[ "goModules" "name" ]
[ "goModules" "outputHash" ]
];
expected = true;
};
buildGoModule-goModules-overrideAttrs = {
expr = pet-foo.goModules.FOO == "foo";
expected = true;
};
buildGoModule-goModules-overrideAttrs-vendored = {
expr = lib.isString pet-vendored.drvPath;
expected = true;
};
};
addEntangled = origOverrideAttrs: f:
@ -51,6 +83,74 @@ let
overrides1 = example.overrideAttrs (_: super: { pname = "a-better-${super.pname}"; });
repeatedOverrides = overrides1.overrideAttrs (_: super: { pname = "${super.pname}-with-blackjack"; });
pet_0_3_4 = pkgs.buildGoModule rec {
pname = "pet";
version = "0.3.4";
src = pkgs.fetchFromGitHub {
owner = "knqyf263";
repo = "pet";
rev = "v${version}";
hash = "sha256-Gjw1dRrgM8D3G7v6WIM2+50r4HmTXvx0Xxme2fH9TlQ=";
};
vendorHash = "sha256-ciBIR+a1oaYH+H1PcC8cD8ncfJczk1IiJ8iYNM+R6aA=";
meta = {
description = "Simple command-line snippet manager, written in Go";
homepage = "https://github.com/knqyf263/pet";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ kalbasit ];
};
};
pet_0_4_0 = pkgs.buildGoModule rec {
pname = "pet";
version = "0.4.0";
src = pkgs.fetchFromGitHub {
owner = "knqyf263";
repo = "pet";
rev = "v${version}";
hash = "sha256-gVTpzmXekQxGMucDKskGi+e+34nJwwsXwvQTjRO6Gdg=";
};
vendorHash = "sha256-dUvp7FEW09V0xMuhewPGw3TuAic/sD7xyXEYviZ2Ivs=";
meta = {
description = "Simple command-line snippet manager, written in Go";
homepage = "https://github.com/knqyf263/pet";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ kalbasit ];
};
};
pet_0_4_0-overridden = pet_0_3_4.overrideAttrs (finalAttrs: previousAttrs: {
version = "0.4.0";
src = pkgs.fetchFromGitHub {
inherit (previousAttrs.src) owner repo;
rev = "v${finalAttrs.version}";
hash = "sha256-gVTpzmXekQxGMucDKskGi+e+34nJwwsXwvQTjRO6Gdg=";
};
vendorHash = "sha256-dUvp7FEW09V0xMuhewPGw3TuAic/sD7xyXEYviZ2Ivs=";
});
pet-foo = pet_0_3_4.overrideAttrs (
finalAttrs: previousAttrs: {
passthru = previousAttrs.passthru // {
overrideModAttrs = lib.composeExtensions previousAttrs.passthru.overrideModAttrs (
finalModAttrs: previousModAttrs: {
FOO = "foo";
}
);
};
}
);
pet-vendored = pet-foo.overrideAttrs { vendorHash = null; };
in
stdenvNoCC.mkDerivation {