blob: 7a276972adee7a480b05433666dd2181a5560a2d [file] [log] [blame]
Tim Windelschmidt5d357d82025-07-10 18:47:15 +02001{ stdenv
2, callPackage
3, # nix tooling and utilities
4 darwin
5, lib
6, fetchzip
7, fetchpatch
8, makeWrapper
9, writeTextFile
10, replaceVars
11, # native build inputs
12 runtimeShell
13, zip
14, unzip
15, bash
16, coreutils
17, which
18, gawk
19, gnused
20, gnutar
21, gnugrep
22, gzip
23, findutils
24, diffutils
25, gnupatch
26, file
27, installShellFiles
28, python3
29, # Apple dependencies
30 cctools
31, # Allow to independently override the jdks used to build and run respectively
32 jdk_headless
33, # Toggle for hacks for running bazel under buildBazelPackage:
34 # Always assume all markers valid (this is needed because we remove markers; they are non-deterministic).
35 # Also, don't clean up environment variables (so that NIX_ environment variables are passed to compilers).
36 version ? "8.3.1"
37,
38}:
39
40let
41 inherit (callPackage ./build-support/patching.nix { }) addFilePatch;
42 inherit (stdenv.hostPlatform) isDarwin isAarch64;
43
44 bazelSystem = if isDarwin then "darwin" else "linux";
45
46 # on aarch64 Darwin, `uname -m` returns "arm64"
47 bazelArch = if isDarwin && isAarch64 then "arm64" else stdenv.hostPlatform.parsed.cpu.name;
48
49 src = fetchzip {
50 url = "https://github.com/bazelbuild/bazel/releases/download/${version}/bazel-${version}-dist.zip";
51 hash = "sha256-Hiny31S+YF7JdKjxzCyKdw3J/3OdDwsKeOkppfvWrNI=";
52 stripRoot = false;
53 };
54
55 defaultShellUtils =
56 # Keep this list conservative. For more exotic tools, prefer to use
57 # @rules_nixpkgs to pull in tools from the nix repository. Example:
58 #
59 # WORKSPACE:
60 #
61 # nixpkgs_git_repository(
62 # name = "nixpkgs",
63 # revision = "def5124ec8367efdba95a99523dd06d918cb0ae8",
64 # )
65 #
66 # # This defines an external Bazel workspace.
67 # nixpkgs_package(
68 # name = "bison",
69 # repositories = { "nixpkgs": "@nixpkgs//:default.nix" },
70 # )
71 #
72 # some/BUILD.bazel:
73 #
74 # genrule(
75 # ...
76 # cmd = "$(location @bison//:bin/bison) -other -args",
77 # tools = [
78 # ...
79 # "@bison//:bin/bison",
80 # ],
81 # )
82 [
83 coreutils
84 diffutils
85 file
86 findutils
87 gawk
88 gnugrep
89 gnupatch
90 gnused
91 gnutar
92 gzip
93 unzip
94 which
95 zip
96 bash
97 ];
98 defaultShell = callPackage ./defaultShell.nix { } { inherit defaultShellUtils; };
99
100 commandArgs =
101 [
102 "--nobuild_python_zip"
103 "--features=-module_maps"
104 "--host_features=-module_maps"
105 "--announce_rc"
106 "--verbose_failures"
107 "--curses=no"
108 ]
109 ++ lib.optionals (isDarwin) [
110 "--macos_sdk_version=${stdenv.hostPlatform.darwinMinVersion}"
111 "--cxxopt=-isystem"
112 "--cxxopt=${lib.getDev stdenv.cc.libcxx}/include/c++/v1"
113 "--host_cxxopt=-isystem"
114 "--host_cxxopt=${lib.getDev stdenv.cc.libcxx}/include/c++/v1"
115 "--copt=-isystem"
116 "--copt=${lib.getDev darwin.libresolv}/include"
117 "--host_copt=-isystem"
118 "--host_copt=${lib.getDev darwin.libresolv}/include"
119 ];
120
121in
122stdenv.mkDerivation rec {
123 pname = "bazel";
124 inherit version src;
125
126 darwinPatches = [
127 # Bazel integrates with apple IOKit to inhibit and track system sleep.
128 # Inside the darwin sandbox, these API calls are blocked, and bazel
129 # crashes. It seems possible to allow these APIs inside the sandbox, but it
130 # feels simpler to patch bazel not to use it at all. So our bazel is
131 # incapable of preventing system sleep, which is a small price to pay to
132 # guarantee that it will always run in any nix context.
133 #
134 # See also ./bazel_darwin_sandbox.patch in bazel_5. That patch uses
135 # NIX_BUILD_TOP env var to conditionnally disable sleep features inside the
136 # sandbox.
137 #
138 # If you want to investigate the sandbox profile path,
139 # IORegisterForSystemPower can be allowed with
140 #
141 # propagatedSandboxProfile = ''
142 # (allow iokit-open (iokit-user-client-class "RootDomainUserClient"))
143 # '';
144 #
145 # I do not know yet how to allow IOPMAssertion{CreateWithName,Release}
146 ./patches/darwin_sleep.patch
147
148 # Fix DARWIN_XCODE_LOCATOR_COMPILE_COMMAND by removing multi-arch support.
149 # Nixpkgs toolcahins do not support that (yet?) and get confused.
150 # Also add an explicit /usr/bin prefix that will be patched below.
151 (replaceVars ./patches/xcode.patch {
152 usrBinEnv = "${coreutils}/bin/env";
153 clangDarwin = "${stdenv.cc}/bin/clang";
154 codesign = "${darwin.sigtool}/bin/codesign";
155 })
156
157 # Revert preference for apple_support over rules_cc toolchain for now
158 # will need to figure out how to build with apple_support toolchain later
159 ./patches/apple_cc_toolchain.patch
160
161 # On Darwin, the last argument to gcc is coming up as an empty string. i.e: ''
162 # This is breaking the build of any C target. This patch removes the last
163 # argument if it's found to be an empty string.
164 ./patches/trim-last-argument-to-gcc-if-empty.patch
165
166 # fdopen() compilation fix
167 (fetchpatch {
168 url = "https://github.com/madler/zlib/commit/4bd9a71f3539b5ce47f0c67ab5e01f3196dc8ef9.patch";
169 hash = "sha256-wlZY0/XqND5Fk+SJkUCUj7XhGVwUJw/VqVGAlDdqOhs=";
170 stripLen = 1;
171 extraPrefix = "third_party/zlib/";
172 })
173 ];
174
175 patches = lib.optionals isDarwin darwinPatches ++ [
176 # patch that propagates rules_* patches below
177 # patches need to be within source root and can't be absolute paths in Nix store
178 # so rules_* patches are injected via addFilePatch
179 ./patches/deps_patches.patch
180 (addFilePatch {
181 path = "b/third_party/rules_python.patch";
182 file = replaceVars ./patches/rules_python.patch {
183 usrBinEnv = "${coreutils}/bin/env";
184 };
185 })
186 (addFilePatch {
187 path = "b/third_party/rules_java.patch";
188 file = replaceVars ./patches/rules_java.patch {
189 defaultBash = "${defaultShell.bashWithDefaultShellUtils}/bin/bash";
190 };
191 })
192 # Suggested for upstream in https://github.com/bazelbuild/bazel/pull/25936
193 ./patches/build_execlog_parser.patch
194 # Part of suggestion for upstream in https://github.com/bazelbuild/bazel/pull/25934
195 ./patches/env_bash.patch
196 # Suggested for upstream in https://github.com/bazelbuild/bazel/pull/25935
197 ./patches/gen_completion.patch
198
199 # --experimental_strict_action_env (which may one day become the default
200 # see bazelbuild/bazel#2574) hardcodes the default
201 # action environment to a non hermetic value (e.g. "/usr/local/bin").
202 # This is non hermetic on non-nixos systems. On NixOS, bazel cannot find the required binaries.
203 # So we are replacing this bazel paths by defaultShellPath,
204 # improving hermeticity and making it work in nixos.
205 (replaceVars ./patches/strict_action_env.patch {
206 strictActionEnvPatch = defaultShell.defaultShellPath;
207 })
208
209 (replaceVars ./patches/default_bash.patch {
210 defaultBash = "${defaultShell.bashWithDefaultShellUtils}/bin/bash";
211 })
212
213 (replaceVars ./patches/md5sum.patch {
214 md5sum = "${coreutils}/bin/md5sum";
215 })
216
217 # Nix build sandbox can configure custom PATH but doesn't have
218 # /usr/bin/env which is unfortunate https://github.com/NixOS/nixpkgs/issues/6227
219 # and we need to do a silly patch
220 (replaceVars ./patches/usr_bin_env.patch {
221 usrBinEnv = "${coreutils}/bin/env";
222 })
223
224 # Provide default JRE for Bazel process by setting --server_javabase=
225 # in a new default system bazelrc file
226 (replaceVars ./patches/bazel_rc.patch {
227 bazelSystemBazelRCPath = replaceVars ./system.bazelrc {
228 serverJavabase = jdk_headless;
229 };
230 })
231 ];
232
233 meta = with lib; {
234 homepage = "https://github.com/bazelbuild/bazel/";
235 description = "Build tool that builds code quickly and reliably";
236 sourceProvenance = with sourceTypes; [
237 fromSource
238 binaryBytecode # source bundles dependencies as jars
239 ];
240 license = licenses.asl20;
241 teams = [ lib.teams.bazel ];
242 mainProgram = "bazel";
243 platforms = lib.platforms.linux ++ lib.platforms.darwin;
244 };
245
246 nativeBuildInputs =
247 [
248 makeWrapper
249 jdk_headless
250 python3
251 unzip
252 which
253
254 # Shell completion
255 installShellFiles
256 python3.pkgs.absl-py # Needed to build fish completion
257 ]
258 # Needed for execlog
259 ++ lib.optional (!stdenv.hostPlatform.isDarwin) stdenv.cc
260 ++ lib.optional (stdenv.hostPlatform.isDarwin) cctools;
261
262 buildPhase = ''
263 runHook preBuild
264 export HOME=$(mktemp -d)
265
266 # If EMBED_LABEL isn't set, it'd be auto-detected from CHANGELOG.md
267 # and `git rev-parse --short HEAD` which would result in
268 # "3.7.0- (@non-git)" due to non-git build and incomplete changelog.
269 # Actual bazel releases use scripts/release/common.sh which is based
270 # on branch/tag information which we don't have with tarball releases.
271 # Note that .bazelversion is always correct and is based on bazel-*
272 # executable name, version checks should work fine
273 export EMBED_LABEL="${version}"
274
275 echo "Stage 1 - Running bazel bootstrap script"
276 export EXTRA_BAZEL_ARGS="${lib.strings.concatStringsSep " " commandArgs}"
277
278 ${bash}/bin/bash ./compile.sh
279
280 # XXX: get rid of this, or move it to another stage.
281 # It is plain annoying when builds fail.
282 echo "Stage 2 - Generate bazel completions"
283 ${bash}/bin/bash ./scripts/generate_bash_completion.sh \
284 --bazel=./output/bazel \
285 --output=./output/bazel-complete.bash \
286 --prepend=./scripts/bazel-complete-header.bash \
287 --prepend=./scripts/bazel-complete-template.bash
288 ${python3}/bin/python3 ./scripts/generate_fish_completion.py \
289 --bazel=./output/bazel \
290 --output=./output/bazel-complete.fish
291
292 runHook postBuild
293 '';
294
295 installPhase = ''
296 runHook preInstall
297
298 # Bazel binary contains zip archive, which contains text files and a jar
299 # both of which can have store references that might be obscured to Nix
300 # builder in packaged form, so we unpack and extract those references
301
302 # Note: grep isn't necessarily 100% accurate, other approaches could be
303 # to disassemble Jar (slow) or hardcode known references
304 mkdir -p $out/nix-support
305 INSTALL_BASE=$(./output/bazel --batch info install_base)
306 find "$INSTALL_BASE" -type f -exec \
307 ${gnugrep}/bin/grep --text --only-matching --no-filename "$NIX_STORE/[^/]*" '{}' \; \
308 | sort -u >> $out/nix-support/depends
309
310 mkdir -p $out/bin
311
312 # official wrapper scripts that searches for $WORKSPACE_ROOT/tools/bazel if
313 # it can’t find something in tools, it calls
314 # $out/bin/bazel-{version}-{os_arch} The binary _must_ exist with this
315 # naming if your project contains a .bazelversion file.
316 cp ./scripts/packages/bazel.sh $out/bin/bazel
317 versioned_bazel="$out/bin/bazel-${version}-${bazelSystem}-${bazelArch}"
318 mv ./output/bazel "$versioned_bazel"
319 wrapProgram "$versioned_bazel" --suffix PATH : ${defaultShell.defaultShellPath}
320
321 mkdir $out/share
322 cp ./output/parser_deploy.jar $out/share/parser_deploy.jar
323 substitute ${./bazel-execlog.sh} $out/bin/bazel-execlog \
324 --subst-var out \
325 --subst-var-by runtimeShell ${runtimeShell} \
326 --subst-var-by javaBin ${jdk_headless}/bin/java
327 chmod +x $out/bin/bazel-execlog
328
329 # shell completion files
330 installShellCompletion --bash \
331 --name bazel.bash \
332 ./output/bazel-complete.bash
333 installShellCompletion --zsh \
334 --name _bazel \
335 ./scripts/zsh_completion/_bazel
336 installShellCompletion --fish \
337 --name bazel.fish \
338 ./output/bazel-complete.fish
339 '';
340
341 postFixup =
342 # verify that bazel binary still works post-fixup
343 ''
344 USE_BAZEL_VERSION=${version} $out/bin/bazel --batch info release
345 '';
346
347 # Bazel binary includes zip archive at the end that `strip` would end up discarding
348 stripExclude = [ "bin/.bazel-${version}-*-wrapped" ];
349
350 passthru = {
351 tests = {
352 inherit (callPackage ./examples.nix { }) cpp java rust;
353 };
354 };
355}