8 name ? "${args.pname}-${args.version}"
11 , bazelBuildFlags ? []
15 , bazelFetchFlags ? []
17 , bazelTestTargets ? []
18 , bazelRunTarget ? null
22 # Newer versions of Bazel are moving away from built-in rules_cc and instead
23 # allow fetching it as an external dependency in a WORKSPACE file[1]. If
24 # removed in the fixed-output fetch phase, building will fail to download it.
25 # This can be seen e.g. in #73097
27 # This option allows configuring the removal of rules_cc in cases where a
28 # project depends on it via an external dependency.
30 # [1]: https://github.com/bazelbuild/rules_cc
31 , removeRulesCC ? true
32 , removeLocalConfigCc ? true
35 # Use build --nobuild instead of fetch. This allows fetching the dependencies
36 # required for the build as configured, rather than fetching all the dependencies
37 # which may not work in some situations (e.g. Java code which ends up relying on
38 # Debian-specific /usr/share/java paths, but doesn't in the configured build).
39 , fetchConfigured ? true
41 # Don’t add Bazel --copt and --linkopt from NIX_CFLAGS_COMPILE /
42 # NIX_LDFLAGS. This is necessary when using a custom toolchain which
43 # Bazel wants all headers / libraries to come from, like when using
44 # CROSSTOOL. Weirdly, we can still get the flags through the wrapped
46 , dontAddBazelOpts ? false
51 fArgs = removeAttrs args [ "buildAttrs" "fetchAttrs" "removeRulesCC" ] // {
66 fBuildAttrs = fArgs // buildAttrs;
67 fFetchAttrs = fArgs // removeAttrs fetchAttrs [ "hash" "sha256" ];
68 bazelCmd = { cmd, additionalFlags, targets, targetRunFlags ? [ ] }:
69 lib.optionalString (targets != [ ]) ''
70 # See footnote called [USER and BAZEL_USE_CPP_ONLY_TOOLCHAIN variables]
71 BAZEL_USE_CPP_ONLY_TOOLCHAIN=1 \
72 USER=homeless-shelter \
75 --output_base="$bazelOut" \
76 --output_user_root="$bazelUserRoot" \
80 "''${host_copts[@]}" \
82 "''${host_linkopts[@]}" \
84 ${lib.strings.concatStringsSep " " additionalFlags} \
85 ${lib.strings.concatStringsSep " " targets} \
86 ${lib.optionalString (targetRunFlags != []) " -- " + lib.strings.concatStringsSep " " targetRunFlags}
88 # we need this to chmod dangling symlinks on darwin, gnu coreutils refuses to do so:
89 # chmod: cannot operate on dangling symlink '$symlink'
90 chmodder = writeCBin "chmodder" ''
93 #include <sys/types.h>
98 int main(int argc, char** argv) {
99 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
101 fprintf(stderr, "usage: chmodder file");
104 if (lchmod(argv[1], mode) != 0) {
105 fprintf(stderr, "failed to lchmod '%s': %s", argv[0], strerror(errno));
111 stdenv.mkDerivation (fBuildAttrs // {
113 deps = stdenv.mkDerivation (fFetchAttrs // {
114 name = "${name}-deps.tar.gz";
116 impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ fFetchAttrs.impureEnvVars or [];
118 nativeBuildInputs = fFetchAttrs.nativeBuildInputs or [] ++ [ bazel ];
120 preHook = fFetchAttrs.preHook or "" + ''
121 export bazelOut="$(echo ''${NIX_BUILD_TOP}/output | sed -e 's,//,/,g')"
122 export bazelUserRoot="$(echo ''${NIX_BUILD_TOP}/tmp | sed -e 's,//,/,g')"
123 export HOME="$NIX_BUILD_TOP"
125 # This is needed for git_repository with https remotes
126 export GIT_SSL_CAINFO="${cacert}/etc/ssl/certs/ca-bundle.crt"
127 # This is needed for Bazel fetchers that are themselves programs (e.g.
128 # rules_go using the go toolchain)
129 export SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-bundle.crt"
132 buildPhase = fFetchAttrs.buildPhase or ''
137 cmd = if fetchConfigured then "build --nobuild" else "fetch";
139 # We disable multithreading for the fetching phase since it can lead to timeouts with many dependencies/threads:
140 # https://github.com/bazelbuild/bazel/issues/6502
141 "--loading_phase_threads=1"
143 ] ++ (if fetchConfigured then ["--jobs" "$NIX_BUILD_CORES"] else []);
144 targets = fFetchAttrs.bazelTargets ++ fFetchAttrs.bazelTestTargets;
151 installPhase = fFetchAttrs.installPhase or (''
154 # Remove all built in external workspaces, Bazel will recreate them when building
155 rm -rf $bazelOut/external/{bazel_tools,\@bazel_tools.marker}
156 ${lib.optionalString removeRulesCC "rm -rf $bazelOut/external/{rules_cc,\\@rules_cc.marker}"}
157 rm -rf $bazelOut/external/{embedded_jdk,\@embedded_jdk.marker}
158 ${lib.optionalString removeLocalConfigCc "rm -rf $bazelOut/external/{local_config_cc,\\@local_config_cc.marker}"}
159 ${lib.optionalString removeLocal "rm -rf $bazelOut/external/{local_*,\\@local_*.marker}"}
162 find $bazelOut/external -name '@*\.marker' -exec sh -c 'echo > {}' \;
164 # Remove all vcs files
165 rm -rf $(find $bazelOut/external -type d -name .git)
166 rm -rf $(find $bazelOut/external -type d -name .svn)
167 rm -rf $(find $bazelOut/external -type d -name .hg)
169 # Removing top-level symlinks along with their markers.
170 # This is needed because they sometimes point to temporary paths (?).
171 # For example, in Tensorflow-gpu build:
172 # platforms -> NIX_BUILD_TOP/tmp/install/35282f5123611afa742331368e9ae529/_embedded_binaries/platforms
173 find $bazelOut/external -maxdepth 1 -type l | while read symlink; do
174 name="$(basename "$symlink")"
176 test -f "$bazelOut/external/@$name.marker" && rm "$bazelOut/external/@$name.marker" || true
179 # Patching symlinks to remove build directory reference
180 find $bazelOut/external -type l | while read symlink; do
181 new_target="$(readlink "$symlink" | sed "s,$NIX_BUILD_TOP,NIX_BUILD_TOP,")"
183 ln -sf "$new_target" "$symlink"
184 '' + lib.optionalString stdenv.hostPlatform.isDarwin ''
185 # on linux symlink permissions cannot be modified, so we modify those on darwin to match the linux ones
186 ${chmodder}/bin/chmodder "$symlink"
190 echo '${bazel.name}' > $bazelOut/external/.nix-bazel-version
192 (cd $bazelOut/ && tar czf $out --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner external/)
198 allowedRequisites = [];
200 inherit (lib.fetchers.normalizeHash { hashTypes = [ "sha256" ]; } fetchAttrs)
206 nativeBuildInputs = fBuildAttrs.nativeBuildInputs or [] ++ [ (bazel.override { enableNixHacks = true; }) ];
208 preHook = fBuildAttrs.preHook or "" + ''
209 export bazelOut="$NIX_BUILD_TOP/output"
210 export bazelUserRoot="$NIX_BUILD_TOP/tmp"
211 export HOME="$NIX_BUILD_TOP"
217 (cd $bazelOut && tar xfz $deps)
219 test "${bazel.name}" = "$(<$bazelOut/external/.nix-bazel-version)" || {
220 echo "fixed output derivation was built for a different bazel version" >&2
221 echo " got: $(<$bazelOut/external/.nix-bazel-version)" >&2
222 echo "expected: ${bazel.name}" >&2
226 chmod -R +w $bazelOut
227 find $bazelOut -type l | while read symlink; do
228 if [[ $(readlink "$symlink") == *NIX_BUILD_TOP* ]]; then
229 ln -sf $(readlink "$symlink" | sed "s,NIX_BUILD_TOP,$NIX_BUILD_TOP,") "$symlink"
232 '' + fBuildAttrs.preConfigure or "";
234 buildPhase = fBuildAttrs.buildPhase or ''
237 # Bazel sandboxes the execution of the tools it invokes, so even though we are
238 # calling the correct nix wrappers, the values of the environment variables
239 # the wrappers are expecting will not be set. So instead of relying on the
240 # wrappers picking them up, pass them in explicitly via `--copt`, `--linkopt`
247 if [ -z "''${dontAddBazelOpts:-}" ]; then
248 for flag in $NIX_CFLAGS_COMPILE; do
249 copts+=( "--copt=$flag" )
250 host_copts+=( "--host_copt=$flag" )
252 for flag in $NIX_CXXSTDLIB_COMPILE; do
253 copts+=( "--copt=$flag" )
254 host_copts+=( "--host_copt=$flag" )
256 for flag in $NIX_LDFLAGS; do
257 linkopts+=( "--linkopt=-Wl,$flag" )
258 host_linkopts+=( "--host_linkopt=-Wl,$flag" )
266 ["--test_output=errors"] ++ fBuildAttrs.bazelTestFlags ++ ["--jobs" "$NIX_BUILD_CORES"];
267 targets = fBuildAttrs.bazelTestTargets;
273 additionalFlags = fBuildAttrs.bazelBuildFlags ++ ["--jobs" "$NIX_BUILD_CORES"];
274 targets = fBuildAttrs.bazelTargets;
280 additionalFlags = fBuildAttrs.bazelRunFlags ++ [ "--jobs" "$NIX_BUILD_CORES" ];
281 # Bazel run only accepts a single target, but `bazelCmd` expects `targets` to be a list.
282 targets = lib.optionals (fBuildAttrs.bazelRunTarget != null) [ fBuildAttrs.bazelRunTarget ];
283 targetRunFlags = fBuildAttrs.runTargetFlags;
290 # [USER and BAZEL_USE_CPP_ONLY_TOOLCHAIN variables]:
291 # Bazel computes the default value of output_user_root before parsing the
292 # flag. The computation of the default value involves getting the $USER
293 # from the environment. Code here :
294 # https://github.com/bazelbuild/bazel/blob/9323c57607d37f9c949b60e293b573584906da46/src/main/cpp/startup_options.cc#L123-L124
296 # 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.