core/pkgs/build-support/go/module.nix
2024-06-30 09:16:52 +01:00

392 lines
11 KiB
Nix

{
go,
cacert,
git,
lib,
stdenv,
}:
{
name ? "${args'.pname}-${args'.version}",
src,
nativeBuildInputs ? [ ],
passthru ? { },
patches ? [ ],
# A function to override the goModules derivation
overrideModAttrs ? (_oldAttrs: { }),
# path to go.mod and go.sum directory
modRoot ? "./",
# vendorHash is the SRI hash of the vendored dependencies
#
# if vendorHash is null, then we won't fetch any dependencies and
# rely on the vendor folder within the source.
vendorHash ? throw (
if args' ? vendorSha256 then
"buildGoModule: Expect vendorHash instead of vendorSha256"
else
"buildGoModule: vendorHash is missing"
),
# Whether to delete the vendor folder supplied with the source.
deleteVendor ? false,
# Whether to fetch (go mod download) and proxy the vendor directory.
# This is useful if your code depends on c code and go mod tidy does not
# include the needed sources to build or if any dependency has case-insensitive
# conflicts which will produce platform dependant `vendorHash` checksums.
proxyVendor ? false,
# We want parallel builds by default
enableParallelBuilding ? true,
# Do not enable this without good reason
# IE: programs coupled with the compiler
allowGoReference ? false,
CGO_ENABLED ? go.CGO_ENABLED,
meta ? { },
# Not needed with buildGoModule
goPackagePath ? "",
ldflags ? [ ],
GOFLAGS ? [ ],
# needed for buildFlags{,Array} warning
buildFlags ? "",
buildFlagsArray ? "",
...
}@args':
assert goPackagePath != "" -> throw "`goPackagePath` is not needed with `buildGoModule`";
let
args = removeAttrs args' [
"overrideModAttrs"
"vendorSha256"
"vendorHash"
];
GO111MODULE = "on";
GOTOOLCHAIN = "local";
goModules =
if (vendorHash == null) then
""
else
(stdenv.mkDerivation {
name = "${name}-go-modules";
nativeBuildInputs = (args.nativeBuildInputs or [ ]) ++ [
go
git
cacert
];
inherit (args) src;
inherit (go) GOOS GOARCH;
inherit GO111MODULE GOTOOLCHAIN;
# The following inheritence behavior is not trivial to expect, and some may
# 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 "";
env = args.env or { };
impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
"GIT_PROXY_COMMAND"
"SOCKS_SERVER"
"GOPROXY"
];
configurePhase =
args.modConfigurePhase or ''
runHook preConfigure
export GOCACHE=$TMPDIR/go-cache
export GOPATH="$TMPDIR/go"
cd "${modRoot}"
runHook postConfigure
'';
buildPhase =
args.modBuildPhase or (
''
runHook preBuild
''
+ lib.optionalString deleteVendor ''
if [ ! -d vendor ]; then
echo "vendor folder does not exist, 'deleteVendor' is not needed"
exit 10
else
rm -rf vendor
fi
''
+ ''
if [ -d vendor ]; then
echo "vendor folder exists, please set 'vendorHash = null;' in your expression"
exit 10
fi
${
if proxyVendor then
''
mkdir -p "''${GOPATH}/pkg/mod/cache/download"
go mod download
''
else
''
if (( "''${NIX_DEBUG:-0}" >= 1 )); then
goModVendorFlags+=(-v)
fi
go mod vendor "''${goModVendorFlags[@]}"
''
}
mkdir -p vendor
runHook postBuild
''
);
installPhase =
args.modInstallPhase or ''
runHook preInstall
${
if proxyVendor then
''
rm -rf "''${GOPATH}/pkg/mod/cache/download/sumdb"
cp -r --reflink=auto "''${GOPATH}/pkg/mod/cache/download" $out
''
else
''
cp -r --reflink=auto vendor $out
''
}
if ! [ "$(ls -A $out)" ]; then
echo "vendor folder is empty, please set 'vendorHash = null;' in your expression"
exit 10
fi
runHook postInstall
'';
dontFixup = true;
outputHashMode = "recursive";
outputHash = vendorHash;
# Handle empty vendorHash; avoid
# error: empty hash requires explicit hash algorithm
outputHashAlgo = if vendorHash == "" then "sha256" else null;
}).overrideAttrs
overrideModAttrs;
package = stdenv.mkDerivation (
args
// {
nativeBuildInputs = [ go ] ++ nativeBuildInputs;
inherit (go) GOOS GOARCH;
GOFLAGS =
GOFLAGS
++ lib.optional (!proxyVendor) "-mod=vendor"
++ lib.optional (!allowGoReference) "-trimpath";
inherit
CGO_ENABLED
enableParallelBuilding
GO111MODULE
GOTOOLCHAIN
;
# If not set to an explicit value, set the buildid empty for reproducibility.
ldflags = ldflags ++ lib.optional (!lib.any (lib.hasPrefix "-buildid=") ldflags) "-buildid=";
configurePhase =
args.configurePhase or (
''
runHook preConfigure
export GOCACHE=$TMPDIR/go-cache
export GOPATH="$TMPDIR/go"
export GOPROXY=off
export GOSUMDB=off
cd "$modRoot"
''
+ lib.optionalString (vendorHash != null) ''
${
if proxyVendor then
''
export GOPROXY=file://${goModules}
''
else
''
rm -rf vendor
cp -r --reflink=auto ${goModules} vendor
''
}
''
+ ''
# currently pie is only enabled by default in pkgsMusl
# this will respect the `hardening{Disable,Enable}` flags if set
if [[ $NIX_HARDENING_ENABLE =~ "pie" ]]; then
export GOFLAGS="-buildmode=pie $GOFLAGS"
fi
runHook postConfigure
''
);
buildPhase =
args.buildPhase or (
''
runHook preBuild
exclude='\(/_\|examples\|Godeps\|testdata'
if [[ -n "$excludedPackages" ]]; then
IFS=' ' read -r -a excludedArr <<<$excludedPackages
printf -v excludedAlternates '%s\\|' "''${excludedArr[@]}"
excludedAlternates=''${excludedAlternates%\\|} # drop final \| added by printf
exclude+='\|'"$excludedAlternates"
fi
exclude+='\)'
buildGoDir() {
local cmd="$1" dir="$2"
. $TMPDIR/buildFlagsArray
declare -a flags
flags+=($buildFlags "''${buildFlagsArray[@]}")
flags+=(''${tags:+-tags=''${tags// /,}})
flags+=(''${ldflags:+-ldflags="$ldflags"})
flags+=("-p" "$NIX_BUILD_CORES")
if [ "$cmd" = "test" ]; then
flags+=(-vet=off)
flags+=($checkFlags)
fi
local OUT
if ! OUT="$(go $cmd "''${flags[@]}" $dir 2>&1)"; then
if ! echo "$OUT" | grep -qE '(no( buildable| non-test)?|build constraints exclude all) Go (source )?files'; then
echo "$OUT" >&2
return 1
fi
fi
if [ -n "$OUT" ]; then
echo "$OUT" >&2
fi
return 0
}
getGoDirs() {
local type;
type="$1"
if [ -n "$subPackages" ]; then
echo "$subPackages" | sed "s,\(^\| \),\1./,g"
else
find . -type f -name \*$type.go -exec dirname {} \; | grep -v "/vendor/" | sort --unique | grep -v "$exclude"
fi
}
if (( "''${NIX_DEBUG:-0}" >= 1 )); then
buildFlagsArray+=(-x)
fi
if [ ''${#buildFlagsArray[@]} -ne 0 ]; then
declare -p buildFlagsArray > $TMPDIR/buildFlagsArray
else
touch $TMPDIR/buildFlagsArray
fi
if [ -z "$enableParallelBuilding" ]; then
export NIX_BUILD_CORES=1
fi
for pkg in $(getGoDirs ""); do
echo "Building subPackage $pkg"
buildGoDir install "$pkg"
done
''
+ lib.optionalString (stdenv.hostPlatform != stdenv.buildPlatform) ''
# normalize cross-compiled builds w.r.t. native builds
(
dir=$GOPATH/bin/${go.GOOS}_${go.GOARCH}
if [[ -n "$(shopt -s nullglob; echo $dir/*)" ]]; then
mv $dir/* $dir/..
fi
if [[ -d $dir ]]; then
rmdir $dir
fi
)
''
+ ''
runHook postBuild
''
);
doCheck = args.doCheck or true;
checkPhase =
args.checkPhase or ''
runHook preCheck
# We do not set trimpath for tests, in case they reference test assets
export GOFLAGS=''${GOFLAGS//-trimpath/}
for pkg in $(getGoDirs test); do
buildGoDir test "$pkg"
done
runHook postCheck
'';
installPhase =
args.installPhase or ''
runHook preInstall
mkdir -p $out
dir="$GOPATH/bin"
[ -e "$dir" ] && cp -r $dir $out
runHook postInstall
'';
strictDeps = true;
disallowedReferences = lib.optional (!allowGoReference) go;
passthru = passthru // {
inherit go goModules vendorHash;
};
meta = {
# Add default meta information
platforms = go.meta.platforms or lib.platforms.all;
} // meta;
}
);
in
lib.warnIf (buildFlags != "" || buildFlagsArray != "")
"Use the `ldflags` and/or `tags` attributes instead of `buildFlags`/`buildFlagsArray`"
lib.warnIf
(builtins.elem "-buildid=" ldflags)
"`-buildid=` is set by default as ldflag by buildGoModule"
lib.warnIf
(builtins.elem "-trimpath" GOFLAGS)
"`-trimpath` is added by default to GOFLAGS by buildGoModule when allowGoReference isn't set to true"
lib.warnIf
(lib.any (lib.hasPrefix "-mod=") GOFLAGS)
"use `proxyVendor` to control Go module/vendor behavior instead of setting `-mod=` in GOFLAGS"
package