1{ lowPrio, newScope, pkgs, lib, stdenv, cmake, ninja
2, gccForLibs, preLibcCrossHeaders
3, libxml2, python3, fetchFromGitHub, overrideCC, wrapCCWith, wrapBintoolsWith
4, buildLlvmTools # tools, but from the previous stage, for cross
5, targetLlvmLibraries # libraries, but from the next stage, for cross
6, targetLlvm
7# This is the default binutils, but with *this* version of LLD rather
8# than the default LLVM version's, if LLD is the choice. We use these for
9# the `useLLVM` bootstrapping below.
10, bootBintoolsNoLibc ?
11 if stdenv.targetPlatform.linker == "lld"
12 then null
13 else pkgs.bintoolsNoLibc
14, bootBintools ?
15 if stdenv.targetPlatform.linker == "lld"
16 then null
17 else pkgs.bintools
18, darwin
19# LLVM release information; specify one of these but not both:
20, gitRelease ? null
21 # i.e.:
22 # {
23 # version = /* i.e. "15.0.0" */;
24 # rev = /* commit SHA */;
25 # rev-version = /* human readable version; i.e. "unstable-2022-26-07" */;
26 # sha256 = /* checksum for this release, can omit if specifying your own `monorepoSrc` */;
27 # }
28, officialRelease ? { version = "15.0.7"; sha256 = "sha256-wjuZQyXQ/jsmvy6y1aksCcEDXGBjuhpgngF3XQJ/T4s="; }
29 # i.e.:
30 # {
31 # version = /* i.e. "15.0.0" */;
32 # candidate = /* optional; if specified, should be: "rcN" */
33 # sha256 = /* checksum for this release, can omit if specifying your own `monorepoSrc` */;
34 # }
35# By default, we'll try to fetch a release from `github:llvm/llvm-project`
36# corresponding to the `gitRelease` or `officialRelease` specified.
37#
38# You can provide your own LLVM source by specifying this arg but then it's up
39# to you to make sure that the LLVM repo given matches the release configuration
40# specified.
41, monorepoSrc ? null
42}:
43assert let
44 int = a: if a then 1 else 0;
45 xor = a: b: ((builtins.bitXor (int a) (int b)) == 1);
46in
47 lib.assertMsg
48 (xor
49 (gitRelease != null)
50 (officialRelease != null))
51 ("must specify `gitRelease` or `officialRelease`" +
52 (lib.optionalString (gitRelease != null) " — not both"));
53let
54 monorepoSrc' = monorepoSrc;
55in let
56 releaseInfo = if gitRelease != null then rec {
57 original = gitRelease;
58 release_version = original.version;
59 version = gitRelease.rev-version;
60 } else rec {
61 original = officialRelease;
62 release_version = original.version;
63 version = if original ? candidate then
64 "${release_version}-${original.candidate}"
65 else
66 release_version;
67 };
68
69 monorepoSrc = if monorepoSrc' != null then
70 monorepoSrc'
71 else let
72 sha256 = releaseInfo.original.sha256;
73 rev = if gitRelease != null then
74 gitRelease.rev
75 else
76 "llvmorg-${releaseInfo.version}";
77 in fetchFromGitHub {
78 owner = "llvm";
79 repo = "llvm-project";
80 inherit rev sha256;
81 };
82
83 inherit (releaseInfo) release_version version;
84
85 llvm_meta = {
86 license = lib.licenses.ncsa;
87 maintainers = lib.teams.llvm.members;
88
89 # See llvm/cmake/config-ix.cmake.
90 platforms =
91 lib.platforms.aarch64 ++
92 lib.platforms.arm ++
93 lib.platforms.m68k ++
94 lib.platforms.mips ++
95 lib.platforms.power ++
96 lib.platforms.riscv ++
97 lib.platforms.s390x ++
98 lib.platforms.wasi ++
99 lib.platforms.x86;
100 };
101
102 tools = lib.makeExtensible (tools: let
103 callPackage = newScope (tools // { inherit stdenv cmake ninja libxml2 python3 release_version version monorepoSrc buildLlvmTools; });
104 mkExtraBuildCommands0 = cc: ''
105 rsrc="$out/resource-root"
106 mkdir "$rsrc"
107 ln -s "${cc.lib}/lib/clang/${release_version}/include" "$rsrc"
108 echo "-resource-dir=$rsrc" >> $out/nix-support/cc-cflags
109 '';
110 mkExtraBuildCommands = cc: mkExtraBuildCommands0 cc + ''
111 ln -s "${targetLlvmLibraries.compiler-rt.out}/lib" "$rsrc/lib"
112 ln -s "${targetLlvmLibraries.compiler-rt.out}/share" "$rsrc/share"
113 '';
114
115 bintoolsNoLibc' =
116 if bootBintoolsNoLibc == null
117 then tools.bintoolsNoLibc
118 else bootBintoolsNoLibc;
119 bintools' =
120 if bootBintools == null
121 then tools.bintools
122 else bootBintools;
123
124 in {
125
126 libllvm = callPackage ./llvm {
127 inherit llvm_meta;
128 };
129
130 # `llvm` historically had the binaries. When choosing an output explicitly,
131 # we need to reintroduce `outputSpecified` to get the expected behavior e.g. of lib.get*
132 llvm = tools.libllvm;
133
134 libclang = callPackage ./clang {
135 inherit llvm_meta;
136 };
137
138 clang-unwrapped = tools.libclang;
139
140 llvm-manpages = lowPrio (tools.libllvm.override {
141 enableManpages = true;
142 python3 = pkgs.python3; # don't use python-boot
143 });
144
145 clang-manpages = lowPrio (tools.libclang.override {
146 enableManpages = true;
147 python3 = pkgs.python3; # don't use python-boot
148 });
149
150 # TODO: lldb/docs/index.rst:155:toctree contains reference to nonexisting document 'design/structureddataplugins'
151 # lldb-manpages = lowPrio (tools.lldb.override {
152 # enableManpages = true;
153 # python3 = pkgs.python3; # don't use python-boot
154 # });
155
156 # pick clang appropriate for package set we are targeting
157 clang =
158 /**/ if stdenv.targetPlatform.libc == null then tools.clangNoLibc
159 else if stdenv.targetPlatform.useLLVM or false then tools.clangUseLLVM
160 else if (pkgs.targetPackages.stdenv or stdenv).cc.isGNU then tools.libstdcxxClang
161 else tools.libcxxClang;
162
163 libstdcxxClang = wrapCCWith rec {
164 cc = tools.clang-unwrapped;
165 # libstdcxx is taken from gcc in an ad-hoc way in cc-wrapper.
166 libcxx = null;
167 extraPackages = [
168 targetLlvmLibraries.compiler-rt
169 ];
170 extraBuildCommands = mkExtraBuildCommands cc;
171 };
172
173 libcxxClang = wrapCCWith rec {
174 cc = tools.clang-unwrapped;
175 libcxx = targetLlvmLibraries.libcxx;
176 extraPackages = [
177 libcxx.cxxabi
178 targetLlvmLibraries.compiler-rt
179 ];
180 extraBuildCommands = mkExtraBuildCommands cc;
181 };
182
183 lld = callPackage ./lld {
184 inherit llvm_meta;
185 };
186
187 lldb = callPackage ../common/lldb.nix {
188 src = callPackage ({ runCommand }: runCommand "lldb-src-${version}" {} ''
189 mkdir -p "$out"
190 cp -r ${monorepoSrc}/cmake "$out"
191 cp -r ${monorepoSrc}/lldb "$out"
192 '') { };
193 patches =
194 let
195 resourceDirPatch = callPackage
196 ({ substituteAll, libclang }: substituteAll
197 {
198 src = ./lldb/resource-dir.patch;
199 clangLibDir = "${libclang.lib}/lib";
200 })
201 { };
202 in
203 [
204 ./lldb/procfs.patch # FIXME: do we need this?
205 resourceDirPatch
206 ./lldb/gnu-install-dirs.patch
207 ];
208 inherit llvm_meta;
209 };
210
211 # Below, is the LLVM bootstrapping logic. It handles building a
212 # fully LLVM toolchain from scratch. No GCC toolchain should be
213 # pulled in. As a consequence, it is very quick to build different
214 # targets provided by LLVM and we can also build for what GCC
215 # doesn’t support like LLVM. Probably we should move to some other
216 # file.
217
218 bintools-unwrapped = callPackage ../common/bintools.nix { };
219
220 bintoolsNoLibc = wrapBintoolsWith {
221 bintools = tools.bintools-unwrapped;
222 libc = preLibcCrossHeaders;
223 };
224
225 bintools = wrapBintoolsWith {
226 bintools = tools.bintools-unwrapped;
227 };
228
229 clangUseLLVM = wrapCCWith rec {
230 cc = tools.clang-unwrapped;
231 libcxx = targetLlvmLibraries.libcxx;
232 bintools = bintools';
233 extraPackages = [
234 libcxx.cxxabi
235 targetLlvmLibraries.compiler-rt
236 ] ++ lib.optionals (!stdenv.targetPlatform.isWasm) [
237 targetLlvmLibraries.libunwind
238 ];
239 extraBuildCommands = mkExtraBuildCommands cc;
240 nixSupport.cc-cflags =
241 [ "-rtlib=compiler-rt"
242 "-Wno-unused-command-line-argument"
243 "-B${targetLlvmLibraries.compiler-rt}/lib"
244 ]
245 ++ lib.optional (!stdenv.targetPlatform.isWasm) "--unwindlib=libunwind"
246 ++ lib.optional
247 (!stdenv.targetPlatform.isWasm && stdenv.targetPlatform.useLLVM or false)
248 "-lunwind"
249 ++ lib.optional stdenv.targetPlatform.isWasm "-fno-exceptions";
250 };
251
252 clangNoLibcxx = wrapCCWith rec {
253 cc = tools.clang-unwrapped;
254 libcxx = null;
255 bintools = bintools';
256 extraPackages = [
257 targetLlvmLibraries.compiler-rt
258 ];
259 extraBuildCommands = mkExtraBuildCommands cc;
260 nixSupport.cc-cflags = [
261 "-rtlib=compiler-rt"
262 "-B${targetLlvmLibraries.compiler-rt}/lib"
263 "-nostdlib++"
264 ];
265 };
266
267 clangNoLibc = wrapCCWith rec {
268 cc = tools.clang-unwrapped;
269 libcxx = null;
270 bintools = bintoolsNoLibc';
271 extraPackages = [
272 targetLlvmLibraries.compiler-rt
273 ];
274 extraBuildCommands = mkExtraBuildCommands cc;
275 nixSupport.cc-cflags = [
276 "-rtlib=compiler-rt"
277 "-B${targetLlvmLibraries.compiler-rt}/lib"
278 ];
279 };
280
281 clangNoCompilerRt = wrapCCWith rec {
282 cc = tools.clang-unwrapped;
283 libcxx = null;
284 bintools = bintoolsNoLibc';
285 extraPackages = [ ];
286 extraBuildCommands = mkExtraBuildCommands0 cc;
287 nixSupport.cc-cflags = [ "-nostartfiles" ];
288 };
289
290 clangNoCompilerRtWithLibc = wrapCCWith rec {
291 cc = tools.clang-unwrapped;
292 libcxx = null;
293 bintools = bintools';
294 extraPackages = [ ];
295 extraBuildCommands = mkExtraBuildCommands0 cc;
296 };
297
298 });
299
300 libraries = lib.makeExtensible (libraries: let
301 callPackage = newScope (libraries // buildLlvmTools // { inherit stdenv cmake ninja libxml2 python3 release_version version monorepoSrc; });
302 in {
303
304 compiler-rt-libc = callPackage ./compiler-rt {
305 inherit llvm_meta;
306 stdenv = if stdenv.hostPlatform.useLLVM or false
307 then overrideCC stdenv buildLlvmTools.clangNoCompilerRtWithLibc
308 else stdenv;
309 };
310
311 compiler-rt-no-libc = callPackage ./compiler-rt {
312 inherit llvm_meta;
313 stdenv = if stdenv.hostPlatform.useLLVM or false
314 then overrideCC stdenv buildLlvmTools.clangNoCompilerRt
315 else stdenv;
316 };
317
318 # N.B. condition is safe because without useLLVM both are the same.
319 compiler-rt = if stdenv.hostPlatform.isAndroid
320 then libraries.compiler-rt-libc
321 else libraries.compiler-rt-no-libc;
322
323 stdenv = overrideCC stdenv buildLlvmTools.clang;
324
325 libcxxStdenv = overrideCC stdenv buildLlvmTools.libcxxClang;
326
327 libcxxabi = let
328 # CMake will "require" a compiler capable of compiling C++ programs
329 # cxx-header's build does not actually use one so it doesn't really matter
330 # what stdenv we use here, as long as CMake is happy.
331 cxx-headers = callPackage ./libcxx {
332 inherit llvm_meta;
333 # Note that if we use the regular stdenv here we'll get cycle errors
334 # when attempting to use this compiler in the stdenv.
335 #
336 # The final stdenv pulls `cxx-headers` from the package set where
337 # hostPlatform *is* the target platform which means that `stdenv` at
338 # that point attempts to use this toolchain.
339 #
340 # So, we use `stdenv_` (the stdenv containing `clang` from this package
341 # set, defined below) to sidestep this issue.
342 #
343 # Because we only use `cxx-headers` in `libcxxabi` (which depends on the
344 # clang stdenv _anyways_), this is okay.
345 stdenv = stdenv_;
346 headersOnly = true;
347 };
348
349 # `libcxxabi` *doesn't* need a compiler with a working C++ stdlib but it
350 # *does* need a relatively modern C++ compiler (see:
351 # https://releases.llvm.org/15.0.0/projects/libcxx/docs/index.html#platform-and-compiler-support).
352 #
353 # So, we use the clang from this LLVM package set, like libc++
354 # "boostrapping builds" do:
355 # https://releases.llvm.org/15.0.0/projects/libcxx/docs/BuildingLibcxx.html#bootstrapping-build
356 #
357 # We cannot use `clangNoLibcxx` because that contains `compiler-rt` which,
358 # on macOS, depends on `libcxxabi`, thus forming a cycle.
359 stdenv_ = overrideCC stdenv buildLlvmTools.clangNoCompilerRtWithLibc;
360 in callPackage ./libcxxabi {
361 stdenv = stdenv_;
362 inherit llvm_meta cxx-headers;
363 };
364
365 # Like `libcxxabi` above, `libcxx` requires a fairly modern C++ compiler,
366 # so: we use the clang from this LLVM package set instead of the regular
367 # stdenv's compiler.
368 libcxx = callPackage ./libcxx {
369 inherit llvm_meta;
370 stdenv = overrideCC stdenv buildLlvmTools.clangNoLibcxx;
371 };
372
373 libunwind = callPackage ./libunwind {
374 inherit llvm_meta;
375 stdenv = overrideCC stdenv buildLlvmTools.clangNoLibcxx;
376 };
377
378 openmp = callPackage ./openmp {
379 inherit llvm_meta targetLlvm;
380 };
381 });
382 noExtend = extensible: lib.attrsets.removeAttrs extensible [ "extend" ];
383
384in { inherit tools libraries release_version; } // (noExtend libraries) // (noExtend tools)