core/pkgs/build-support/build-bazel-package/default.nix

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

344 lines
12 KiB
Nix
Raw Normal View History

2024-05-02 00:46:19 +00:00
{
stdenv,
cacert,
lib,
writeCBin,
}:
args@{
name ? "${args.pname}-${args.version}",
bazel,
bazelFlags ? [ ],
bazelBuildFlags ? [ ],
bazelTestFlags ? [ ],
bazelRunFlags ? [ ],
runTargetFlags ? [ ],
bazelFetchFlags ? [ ],
bazelTargets ? [ ],
bazelTestTargets ? [ ],
bazelRunTarget ? null,
buildAttrs,
fetchAttrs,
# Newer versions of Bazel are moving away from built-in rules_cc and instead
# allow fetching it as an external dependency in a WORKSPACE file[1]. If
# removed in the fixed-output fetch phase, building will fail to download it.
# This can be seen e.g. in #73097
#
# This option allows configuring the removal of rules_cc in cases where a
# project depends on it via an external dependency.
#
# [1]: https://github.com/bazelbuild/rules_cc
removeRulesCC ? true,
removeLocalConfigCc ? true,
removeLocal ? true,
# Use build --nobuild instead of fetch. This allows fetching the dependencies
# required for the build as configured, rather than fetching all the dependencies
# which may not work in some situations (e.g. Java code which ends up relying on
# Debian-specific /usr/share/java paths, but doesn't in the configured build).
fetchConfigured ? true,
# Dont add Bazel --copt and --linkopt from NIX_CFLAGS_COMPILE /
# NIX_LDFLAGS. This is necessary when using a custom toolchain which
# Bazel wants all headers / libraries to come from, like when using
# CROSSTOOL. Weirdly, we can still get the flags through the wrapped
# compiler.
dontAddBazelOpts ? false,
...
}:
let
fArgs =
removeAttrs args [
"buildAttrs"
"fetchAttrs"
"removeRulesCC"
]
// {
inherit
name
bazelFlags
bazelBuildFlags
bazelTestFlags
bazelRunFlags
runTargetFlags
bazelFetchFlags
bazelTargets
bazelTestTargets
bazelRunTarget
dontAddBazelOpts
;
};
fBuildAttrs = fArgs // buildAttrs;
fFetchAttrs = fArgs // removeAttrs fetchAttrs [ "sha256" ];
bazelCmd =
{
cmd,
additionalFlags,
targets,
targetRunFlags ? [ ],
}:
lib.optionalString (targets != [ ]) ''
# See footnote called [USER and BAZEL_USE_CPP_ONLY_TOOLCHAIN variables]
BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \
USER=homeless-shelter \
bazel \
--batch \
--output_base="$bazelOut" \
--output_user_root="$bazelUserRoot" \
${cmd} \
--curses=no \
"''${copts[@]}" \
"''${host_copts[@]}" \
"''${linkopts[@]}" \
"''${host_linkopts[@]}" \
$bazelFlags \
${lib.strings.concatStringsSep " " additionalFlags} \
${lib.strings.concatStringsSep " " targets} \
${
lib.optionalString (targetRunFlags != [ ]) " -- " + lib.strings.concatStringsSep " " targetRunFlags
}
'';
# we need this to chmod dangling symlinks on darwin, gnu coreutils refuses to do so:
# chmod: cannot operate on dangling symlink '$symlink'
chmodder = writeCBin "chmodder" ''
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
int main(int argc, char** argv) {
mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
if (argc != 2) {
fprintf(stderr, "usage: chmodder file");
exit(EXIT_FAILURE);
}
if (lchmod(argv[1], mode) != 0) {
fprintf(stderr, "failed to lchmod '%s': %s", argv[0], strerror(errno));
exit(EXIT_FAILURE);
}
}
'';
in
stdenv.mkDerivation (
fBuildAttrs
// {
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
deps = stdenv.mkDerivation (
fFetchAttrs
// {
name = "${name}-deps.tar.gz";
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ fFetchAttrs.impureEnvVars or [ ];
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
nativeBuildInputs = fFetchAttrs.nativeBuildInputs or [ ] ++ [ bazel ];
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
preHook =
fFetchAttrs.preHook or ""
+ ''
export bazelOut="$(echo ''${NIX_BUILD_TOP}/output | sed -e 's,//,/,g')"
export bazelUserRoot="$(echo ''${NIX_BUILD_TOP}/tmp | sed -e 's,//,/,g')"
export HOME="$NIX_BUILD_TOP"
export USER="nix"
# This is needed for git_repository with https remotes
export GIT_SSL_CAINFO="${cacert}/etc/ssl/certs/ca-bundle.crt"
# This is needed for Bazel fetchers that are themselves programs (e.g.
# rules_go using the go toolchain)
export SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-bundle.crt"
2024-06-30 08:12:46 +00:00
'';
2024-05-02 00:46:19 +00:00
buildPhase =
fFetchAttrs.buildPhase or ''
runHook preBuild
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
${bazelCmd {
cmd = if fetchConfigured then "build --nobuild" else "fetch";
additionalFlags =
2024-06-30 08:12:46 +00:00
[
2024-05-02 00:46:19 +00:00
# We disable multithreading for the fetching phase since it can lead to timeouts with many dependencies/threads:
# https://github.com/bazelbuild/bazel/issues/6502
"--loading_phase_threads=1"
"$bazelFetchFlags"
2024-06-30 08:12:46 +00:00
]
++ (
2024-05-02 00:46:19 +00:00
if fetchConfigured then
2024-06-30 08:12:46 +00:00
[
"--jobs"
2024-05-02 00:46:19 +00:00
"$NIX_BUILD_CORES"
2024-06-30 08:12:46 +00:00
]
else
[ ]
);
2024-05-02 00:46:19 +00:00
targets = fFetchAttrs.bazelTargets ++ fFetchAttrs.bazelTestTargets;
2024-06-30 08:12:46 +00:00
}}
2024-05-02 00:46:19 +00:00
runHook postBuild
'';
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
installPhase =
fFetchAttrs.installPhase or (
''
runHook preInstall
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
# Remove all built in external workspaces, Bazel will recreate them when building
rm -rf $bazelOut/external/{bazel_tools,\@bazel_tools.marker}
${lib.optionalString removeRulesCC "rm -rf $bazelOut/external/{rules_cc,\\@rules_cc.marker}"}
rm -rf $bazelOut/external/{embedded_jdk,\@embedded_jdk.marker}
${lib.optionalString removeLocalConfigCc "rm -rf $bazelOut/external/{local_config_cc,\\@local_config_cc.marker}"}
${lib.optionalString removeLocal "rm -rf $bazelOut/external/{local_*,\\@local_*.marker}"}
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
# Clear markers
find $bazelOut/external -name '@*\.marker' -exec sh -c 'echo > {}' \;
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
# Remove all vcs files
rm -rf $(find $bazelOut/external -type d -name .git)
rm -rf $(find $bazelOut/external -type d -name .svn)
rm -rf $(find $bazelOut/external -type d -name .hg)
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
# Removing top-level symlinks along with their markers.
# This is needed because they sometimes point to temporary paths (?).
# For example, in Tensorflow-gpu build:
# platforms -> NIX_BUILD_TOP/tmp/install/35282f5123611afa742331368e9ae529/_embedded_binaries/platforms
find $bazelOut/external -maxdepth 1 -type l | while read symlink; do
name="$(basename "$symlink")"
rm "$symlink"
test -f "$bazelOut/external/@$name.marker" && rm "$bazelOut/external/@$name.marker" || true
2024-06-30 08:12:46 +00:00
done
2024-05-02 00:46:19 +00:00
# Patching symlinks to remove build directory reference
find $bazelOut/external -type l | while read symlink; do
new_target="$(readlink "$symlink" | sed "s,$NIX_BUILD_TOP,NIX_BUILD_TOP,")"
rm "$symlink"
ln -sf "$new_target" "$symlink"
2024-06-30 08:12:46 +00:00
''
2024-05-02 00:46:19 +00:00
+ lib.optionalString stdenv.isDarwin ''
# on linux symlink permissions cannot be modified, so we modify those on darwin to match the linux ones
${chmodder}/bin/chmodder "$symlink"
2024-06-30 08:12:46 +00:00
''
2024-05-02 00:46:19 +00:00
+ ''
done
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
echo '${bazel.name}' > $bazelOut/external/.nix-bazel-version
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
(cd $bazelOut/ && tar czf $out --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner external/)
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
runHook postInstall
2024-06-30 08:12:46 +00:00
''
);
2024-05-02 00:46:19 +00:00
dontFixup = true;
allowedRequisites = [ ];
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
outputHashAlgo = "sha256";
outputHash = fetchAttrs.sha256;
}
2024-06-30 08:12:46 +00:00
);
2024-05-02 00:46:19 +00:00
nativeBuildInputs = fBuildAttrs.nativeBuildInputs or [ ] ++ [
(bazel.override { enableNixHacks = true; })
2024-06-30 08:12:46 +00:00
];
2024-05-02 00:46:19 +00:00
preHook =
fBuildAttrs.preHook or ""
2024-06-30 08:12:46 +00:00
+ ''
2024-05-02 00:46:19 +00:00
export bazelOut="$NIX_BUILD_TOP/output"
export bazelUserRoot="$NIX_BUILD_TOP/tmp"
export HOME="$NIX_BUILD_TOP"
2024-06-30 08:12:46 +00:00
'';
2024-05-02 00:46:19 +00:00
preConfigure =
2024-06-30 08:12:46 +00:00
''
2024-05-02 00:46:19 +00:00
mkdir -p "$bazelOut"
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
(cd $bazelOut && tar xfz $deps)
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
test "${bazel.name}" = "$(<$bazelOut/external/.nix-bazel-version)" || {
echo "fixed output derivation was built for a different bazel version" >&2
echo " got: $(<$bazelOut/external/.nix-bazel-version)" >&2
echo "expected: ${bazel.name}" >&2
2024-06-30 08:12:46 +00:00
exit 1
}
2024-05-02 00:46:19 +00:00
chmod -R +w $bazelOut
find $bazelOut -type l | while read symlink; do
if [[ $(readlink "$symlink") == *NIX_BUILD_TOP* ]]; then
ln -sf $(readlink "$symlink" | sed "s,NIX_BUILD_TOP,$NIX_BUILD_TOP,") "$symlink"
fi
done
2024-06-30 08:12:46 +00:00
''
2024-05-02 00:46:19 +00:00
+ fBuildAttrs.preConfigure or "";
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
buildPhase =
fBuildAttrs.buildPhase or ''
runHook preBuild
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
# Bazel sandboxes the execution of the tools it invokes, so even though we are
# calling the correct nix wrappers, the values of the environment variables
# the wrappers are expecting will not be set. So instead of relying on the
# wrappers picking them up, pass them in explicitly via `--copt`, `--linkopt`
# and related flags.
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
copts=()
host_copts=()
linkopts=()
host_linkopts=()
if [ -z "''${dontAddBazelOpts:-}" ]; then
for flag in $NIX_CFLAGS_COMPILE; do
copts+=( "--copt=$flag" )
host_copts+=( "--host_copt=$flag" )
2024-06-30 08:12:46 +00:00
done
2024-05-02 00:46:19 +00:00
for flag in $NIX_CXXSTDLIB_COMPILE; do
copts+=( "--copt=$flag" )
host_copts+=( "--host_copt=$flag" )
done
for flag in $NIX_LDFLAGS; do
linkopts+=( "--linkopt=-Wl,$flag" )
host_linkopts+=( "--host_linkopt=-Wl,$flag" )
done
fi
2024-06-30 08:12:46 +00:00
2024-05-02 00:46:19 +00:00
${bazelCmd {
cmd = "test";
additionalFlags =
[ "--test_output=errors" ]
++ fBuildAttrs.bazelTestFlags
++ [
"--jobs"
"$NIX_BUILD_CORES"
];
targets = fBuildAttrs.bazelTestTargets;
2024-06-30 08:12:46 +00:00
}}
2024-05-02 00:46:19 +00:00
${bazelCmd {
cmd = "build";
additionalFlags = fBuildAttrs.bazelBuildFlags ++ [
2024-06-30 08:12:46 +00:00
"--jobs"
2024-05-02 00:46:19 +00:00
"$NIX_BUILD_CORES"
];
targets = fBuildAttrs.bazelTargets;
}}
${bazelCmd {
cmd = "run";
additionalFlags = fBuildAttrs.bazelRunFlags ++ [
"--jobs"
"$NIX_BUILD_CORES"
];
# Bazel run only accepts a single target, but `bazelCmd` expects `targets` to be a list.
targets = lib.optionals (fBuildAttrs.bazelRunTarget != null) [ fBuildAttrs.bazelRunTarget ];
targetRunFlags = fBuildAttrs.runTargetFlags;
}}
runHook postBuild
'';
}
)
# [USER and BAZEL_USE_CPP_ONLY_TOOLCHAIN variables]:
# Bazel computes the default value of output_user_root before parsing the
# flag. The computation of the default value involves getting the $USER
# from the environment. Code here :
# https://github.com/bazelbuild/bazel/blob/9323c57607d37f9c949b60e293b573584906da46/src/main/cpp/startup_options.cc#L123-L124
#
# On macOS Bazel will use the system installed Xcode or CLT toolchain instead of the one in the PATH unless we pass BAZEL_USE_CPP_ONLY_TOOLCHAIN.