2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 # Only necessary for flake8 to be happy...
7 from __future__
import print_function
22 from contextlib
import contextmanager
23 from distutils
.dir_util
import copy_tree
25 from shutil
import which
30 def symlink(source
, link_name
):
31 os_symlink
= getattr(os
, "symlink", None)
32 if callable(os_symlink
):
33 os_symlink(source
, link_name
)
35 if os
.path
.isdir(source
):
36 # Fall back to copying the directory :(
37 copy_tree(source
, link_name
)
41 print(" ".join(args
), file=sys
.stderr
, flush
=True)
42 if args
[0] == "cmake":
43 # CMake `message(STATUS)` messages, as appearing in failed source code
44 # compiles, appear on stdout, so we only capture that.
45 p
= subprocess
.Popen(args
, stdout
=subprocess
.PIPE
)
49 sys
.stdout
.write(line
.decode())
53 cmake_output_re
= re
.compile(b
'See also "(.*/CMakeOutput.log)"')
54 cmake_error_re
= re
.compile(b
'See also "(.*/CMakeError.log)"')
56 def find_first_match(re
):
62 output_match
= find_first_match(cmake_output_re
)
63 error_match
= find_first_match(cmake_error_re
)
66 with
open(log
, "r", errors
="replace") as f
:
67 print("\nContents of", log
, "follow\n", file=sys
.stderr
)
69 print(line
, file=sys
.stderr
)
72 dump_file(output_match
.group(1))
74 dump_file(error_match
.group(1))
76 r
= subprocess
.call(args
)
80 def run_in(path
, args
):
88 print('cd "%s"' % path
, file=sys
.stderr
)
93 print('cd "%s"' % d
, file=sys
.stderr
)
97 def patch(patch
, srcdir
):
98 patch
= os
.path
.realpath(patch
)
99 check_run(["patch", "-d", srcdir
, "-p1", "-i", patch
, "--fuzz=0", "-s"])
102 def import_clang_tidy(source_dir
, build_clang_tidy_alpha
, build_clang_tidy_external
):
103 clang_plugin_path
= os
.path
.join(os
.path
.dirname(sys
.argv
[0]), "..", "clang-plugin")
104 clang_tidy_path
= os
.path
.join(source_dir
, "clang-tools-extra/clang-tidy")
105 sys
.path
.append(clang_plugin_path
)
106 from import_mozilla_checks
import do_import
109 "alpha": build_clang_tidy_alpha
,
110 "external": build_clang_tidy_external
,
112 do_import(clang_plugin_path
, clang_tidy_path
, import_options
)
115 def build_package(package_build_dir
, cmake_args
):
116 if not os
.path
.exists(package_build_dir
):
117 os
.mkdir(package_build_dir
)
118 # If CMake has already been run, it may have been run with different
119 # arguments, so we need to re-run it. Make sure the cached copy of the
120 # previous CMake run is cleared before running it again.
121 if os
.path
.exists(package_build_dir
+ "/CMakeCache.txt"):
122 os
.remove(package_build_dir
+ "/CMakeCache.txt")
123 if os
.path
.exists(package_build_dir
+ "/CMakeFiles"):
124 shutil
.rmtree(package_build_dir
+ "/CMakeFiles")
126 run_in(package_build_dir
, ["cmake"] + cmake_args
)
127 run_in(package_build_dir
, ["ninja", "install", "-v"])
131 def updated_env(env
):
132 old_env
= os
.environ
.copy()
133 os
.environ
.update(env
)
136 os
.environ
.update(old_env
)
139 def build_tar_package(name
, base
, directory
):
140 name
= os
.path
.realpath(name
)
141 print("tarring {} from {}/{}".format(name
, base
, directory
), file=sys
.stderr
)
142 assert name
.endswith(".tar.zst")
144 cctx
= zstandard
.ZstdCompressor()
145 with
open(name
, "wb") as f
, cctx
.stream_writer(f
) as z
:
146 with tarfile
.open(mode
="w|", fileobj
=z
) as tf
:
155 if e
.errno
!= errno
.EEXIST
or not os
.path
.isdir(path
):
160 if os
.path
.isdir(path
):
169 def install_import_library(build_dir
, clang_dir
):
171 os
.path
.join(build_dir
, "lib", "clang.lib"), os
.path
.join(clang_dir
, "lib")
175 def install_asan_symbols(build_dir
, clang_dir
):
176 lib_path_pattern
= os
.path
.join("lib", "clang", "*.*.*", "lib", "windows")
177 src_path
= glob
.glob(
178 os
.path
.join(build_dir
, lib_path_pattern
, "clang_rt.asan_dynamic-*.pdb")
180 dst_path
= glob
.glob(os
.path
.join(clang_dir
, lib_path_pattern
))
182 if len(src_path
) != 1:
183 raise Exception("Source path pattern did not resolve uniquely")
185 if len(src_path
) != 1:
186 raise Exception("Destination path pattern did not resolve uniquely")
188 shutil
.copy2(src_path
[0], dst_path
[0])
192 return platform
.system() == "Darwin"
196 return platform
.system() == "Linux"
200 return platform
.system() == "Windows"
220 compiler_rt_source_dir
=None,
221 runtimes_source_link
=None,
222 compiler_rt_source_link
=None,
223 is_final_stage
=False,
224 android_targets
=None,
228 if is_final_stage
and (android_targets
or extra_targets
):
229 # Linking compiler-rt under "runtimes" activates LLVM_RUNTIME_TARGETS
230 # and related arguments.
231 symlink(compiler_rt_source_dir
, runtimes_source_link
)
233 os
.unlink(compiler_rt_source_link
)
237 if not os
.path
.exists(stage_dir
):
240 build_dir
= stage_dir
+ "/build"
241 inst_dir
= stage_dir
+ "/" + package_name
243 # cmake doesn't deal well with backslashes in paths.
244 def slashify_path(path
):
245 return path
.replace("\\", "/")
247 def cmake_base_args(cc
, cxx
, asm
, ld
, ar
, ranlib
, libtool
, inst_dir
):
248 machine_targets
= "X86;ARM;AArch64" if is_final_stage
else "X86"
251 "-DCMAKE_C_COMPILER=%s" % slashify_path(cc
[0]),
252 "-DCMAKE_CXX_COMPILER=%s" % slashify_path(cxx
[0]),
253 "-DCMAKE_ASM_COMPILER=%s" % slashify_path(asm
[0]),
254 "-DCMAKE_LINKER=%s" % slashify_path(ld
[0]),
255 "-DCMAKE_AR=%s" % slashify_path(ar
),
256 "-DCMAKE_C_FLAGS=%s" % " ".join(cc
[1:]),
257 "-DCMAKE_CXX_FLAGS=%s" % " ".join(cxx
[1:]),
258 "-DCMAKE_ASM_FLAGS=%s" % " ".join(asm
[1:]),
259 "-DCMAKE_EXE_LINKER_FLAGS=%s" % " ".join(ld
[1:]),
260 "-DCMAKE_SHARED_LINKER_FLAGS=%s" % " ".join(ld
[1:]),
261 "-DCMAKE_BUILD_TYPE=%s" % build_type
,
262 "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir
,
263 "-DLLVM_TARGETS_TO_BUILD=%s" % machine_targets
,
264 "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions
else "OFF"),
265 "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx
else "OFF"),
266 "-DLLVM_ENABLE_BINDINGS=OFF",
268 if "TASK_ID" in os
.environ
:
270 "-DCLANG_REPOSITORY_STRING=taskcluster-%s" % os
.environ
["TASK_ID"],
272 if not is_final_stage
:
274 "-DLLVM_ENABLE_PROJECTS=clang;compiler-rt",
275 "-DLLVM_INCLUDE_TESTS=OFF",
276 "-DLLVM_TOOL_LLI_BUILD=OFF",
277 "-DCOMPILER_RT_BUILD_SANITIZERS=OFF",
278 "-DCOMPILER_RT_BUILD_XRAY=OFF",
279 "-DCOMPILER_RT_BUILD_MEMPROF=OFF",
280 "-DCOMPILER_RT_BUILD_LIBFUZZER=OFF",
283 cmake_args
+= ["-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly"]
284 if is_linux() and not osx_cross_compile
and is_final_stage
:
285 cmake_args
+= ["-DLLVM_BINUTILS_INCDIR=/usr/include"]
286 cmake_args
+= ["-DLLVM_ENABLE_LIBXML2=FORCE_ON"]
287 sysroot
= os
.path
.join(os
.environ
.get("MOZ_FETCHES_DIR", ""), "sysroot")
288 if os
.path
.exists(sysroot
):
289 cmake_args
+= ["-DCMAKE_SYSROOT=%s" % sysroot
]
290 # Work around the LLVM build system not building the i386 compiler-rt
291 # because it doesn't allow to use a sysroot for that during the cmake
293 cmake_args
+= ["-DCAN_TARGET_i386=1"]
295 cmake_args
.insert(-1, "-DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON")
296 cmake_args
.insert(-1, "-DLLVM_USE_CRT_RELEASE=MT")
298 # libllvm as a shared library is not supported on Windows
299 cmake_args
+= ["-DLLVM_LINK_LLVM_DYLIB=ON"]
300 if ranlib
is not None:
301 cmake_args
+= ["-DCMAKE_RANLIB=%s" % slashify_path(ranlib
)]
302 if libtool
is not None:
303 cmake_args
+= ["-DCMAKE_LIBTOOL=%s" % slashify_path(libtool
)]
304 if osx_cross_compile
:
305 arch
= "arm64" if os
.environ
.get("OSX_ARCH") == "arm64" else "x86_64"
307 "aarch64" if os
.environ
.get("OSX_ARCH") == "arm64" else "x86_64"
310 "-DCMAKE_SYSTEM_NAME=Darwin",
311 "-DCMAKE_SYSTEM_VERSION=%s" % os
.environ
["MACOSX_DEPLOYMENT_TARGET"],
312 "-DLIBCXXABI_LIBCXX_INCLUDES=%s" % libcxx_include_dir
,
313 "-DCMAKE_OSX_SYSROOT=%s" % slashify_path(os
.getenv("CROSS_SYSROOT")),
314 "-DCMAKE_FIND_ROOT_PATH=%s" % slashify_path(os
.getenv("CROSS_SYSROOT")),
315 "-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER",
316 "-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY",
317 "-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY",
318 "-DCMAKE_MACOSX_RPATH=ON",
319 "-DCMAKE_OSX_ARCHITECTURES=%s" % arch
,
320 "-DDARWIN_osx_ARCHS=%s" % arch
,
321 "-DDARWIN_osx_SYSROOT=%s" % slashify_path(os
.getenv("CROSS_SYSROOT")),
322 "-DLLVM_DEFAULT_TARGET_TRIPLE=%s-apple-darwin" % target_cpu
,
324 if os
.environ
.get("OSX_ARCH") == "arm64":
326 "-DDARWIN_osx_BUILTIN_ARCHS=arm64",
328 # Starting in LLVM 11 (which requires SDK 10.12) the build tries to
329 # detect the SDK version by calling xcrun. Cross-compiles don't have
330 # an xcrun, so we have to set the version explicitly.
332 "-DDARWIN_macosx_OVERRIDE_SDK_VERSION=%s"
333 % os
.environ
["MACOSX_DEPLOYMENT_TARGET"],
335 if pgo_phase
== "gen":
336 # Per https://releases.llvm.org/10.0.0/docs/HowToBuildWithPGO.html
338 "-DLLVM_BUILD_INSTRUMENTED=IR",
339 "-DLLVM_BUILD_RUNTIME=No",
341 if pgo_phase
== "use":
343 "-DLLVM_PROFDATA_FILE=%s/merged.profdata" % stage_dir
,
352 runtime_targets
= list(sorted(android_targets
.keys()))
354 runtime_targets
.extend(sorted(extra_targets
))
358 "-DLLVM_BUILTIN_TARGETS=%s" % ";".join(runtime_targets
),
359 "-DLLVM_RUNTIME_TARGETS=%s" % ";".join(runtime_targets
),
362 for target
in runtime_targets
:
364 "-DRUNTIMES_%s_COMPILER_RT_BUILD_PROFILE=ON" % target
,
365 "-DRUNTIMES_%s_COMPILER_RT_BUILD_SANITIZERS=ON" % target
,
366 "-DRUNTIMES_%s_COMPILER_RT_BUILD_XRAY=OFF" % target
,
367 "-DRUNTIMES_%s_SANITIZER_ALLOW_CXXABI=OFF" % target
,
368 "-DRUNTIMES_%s_COMPILER_RT_BUILD_LIBFUZZER=OFF" % target
,
369 "-DRUNTIMES_%s_COMPILER_RT_INCLUDE_TESTS=OFF" % target
,
370 "-DRUNTIMES_%s_LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF" % target
,
371 "-DRUNTIMES_%s_LLVM_INCLUDE_TESTS=OFF" % target
,
374 # The above code flipped switches to build various runtime libraries on
375 # Android; we now have to provide all the necessary compiler switches to
377 if is_final_stage
and android_targets
:
378 android_link_flags
= "-fuse-ld=lld"
380 for target
, cfg
in android_targets
.items():
381 sysroot_dir
= cfg
["ndk_sysroot"].format(**os
.environ
)
382 android_gcc_dir
= cfg
["ndk_toolchain"].format(**os
.environ
)
383 android_include_dirs
= cfg
["ndk_includes"]
384 api_level
= cfg
["api_level"]
387 "-isystem %s" % d
.format(**os
.environ
) for d
in android_include_dirs
389 android_flags
+= ["--gcc-toolchain=%s" % android_gcc_dir
]
390 android_flags
+= ["-D__ANDROID_API__=%s" % api_level
]
392 # Our flags go last to override any --gcc-toolchain that may have
394 rt_c_flags
= " ".join(cc
[1:] + android_flags
)
395 rt_cxx_flags
= " ".join(cxx
[1:] + android_flags
)
396 rt_asm_flags
= " ".join(asm
[1:] + android_flags
)
398 for kind
in ("BUILTINS", "RUNTIMES"):
401 ("CMAKE_ASM_FLAGS", rt_asm_flags
),
402 ("CMAKE_CXX_FLAGS", rt_cxx_flags
),
403 ("CMAKE_C_FLAGS", rt_c_flags
),
404 ("CMAKE_EXE_LINKER_FLAGS", android_link_flags
),
405 ("CMAKE_SHARED_LINKER_FLAGS", android_link_flags
),
406 ("CMAKE_SYSROOT", sysroot_dir
),
407 ("ANDROID_NATIVE_API_LEVEL", api_level
),
409 cmake_args
+= ["-D%s_%s_%s=%s" % (kind
, target
, var
, arg
)]
411 cmake_args
+= cmake_base_args(cc
, cxx
, asm
, ld
, ar
, ranlib
, libtool
, inst_dir
)
412 cmake_args
+= [src_dir
]
413 build_package(build_dir
, cmake_args
)
415 # For some reasons the import library clang.lib of clang.exe is not
416 # installed, so we copy it by ourselves.
418 # The compiler-rt cmake scripts don't allow to build it for multiple
419 # targets at once on Windows, so manually build the 32-bits compiler-rt
420 # during the final stage.
423 # Only build the 32-bits compiler-rt when we originally built for
424 # 64-bits, which we detect through the contents of the LIB
425 # environment variable, which we also adjust for a 32-bits build
427 old_lib
= os
.environ
["LIB"]
429 for l
in old_lib
.split(os
.pathsep
):
430 if l
.endswith("x64"):
433 elif l
.endswith("amd64"):
438 os
.environ
["LIB"] = os
.pathsep
.join(new_lib
)
439 compiler_rt_build_dir
= stage_dir
+ "/compiler-rt"
440 compiler_rt_inst_dir
= inst_dir
+ "/lib/clang/"
441 subdirs
= os
.listdir(compiler_rt_inst_dir
)
442 assert len(subdirs
) == 1
443 compiler_rt_inst_dir
+= subdirs
[0]
444 cmake_args
= cmake_base_args(
445 [os
.path
.join(inst_dir
, "bin", "clang-cl.exe"), "-m32"] + cc
[1:],
446 [os
.path
.join(inst_dir
, "bin", "clang-cl.exe"), "-m32"] + cxx
[1:],
447 [os
.path
.join(inst_dir
, "bin", "clang-cl.exe"), "-m32"] + asm
[1:],
452 compiler_rt_inst_dir
,
455 "-DLLVM_CONFIG_PATH=%s"
456 % slashify_path(os
.path
.join(inst_dir
, "bin", "llvm-config")),
457 os
.path
.join(src_dir
, "projects", "compiler-rt"),
459 build_package(compiler_rt_build_dir
, cmake_args
)
460 os
.environ
["LIB"] = old_lib
462 install_import_library(build_dir
, inst_dir
)
463 install_asan_symbols(build_dir
, inst_dir
)
466 # Return the absolute path of a build tool. We first look to see if the
467 # variable is defined in the config file, and if so we make sure it's an
468 # absolute path to an existing tool, otherwise we look for a program in
471 # This expects the name of the key in the config file to match the name of
472 # the tool in the default toolchain on the system (for example, "ld" on Unix
473 # and "link" on Windows).
474 def get_tool(config
, key
):
477 f
= config
[key
].format(**os
.environ
)
479 if not os
.path
.exists(f
):
480 raise ValueError("%s must point to an existing path" % key
)
483 # Assume that we have the name of some program that should be on PATH.
484 tool
= which(f
) if f
else which(key
)
486 raise ValueError("%s not found on PATH" % (f
or key
))
490 # This function is intended to be called on the final build directory when
491 # building clang-tidy. Also clang-format binaries are included that can be used
492 # in conjunction with clang-tidy.
493 # As a separate binary we also ship clangd for the language server protocol that
494 # can be used as a plugin in `vscode`.
495 # Its job is to remove all of the files which won't be used for clang-tidy or
496 # clang-format to reduce the download size. Currently when this function
497 # finishes its job, it will leave final_dir with a layout like this:
501 # clang-apply-replacements
506 # * (nothing will be deleted here)
511 # * (nothing will be deleted here)
514 # clang-format-diff.py
517 def prune_final_dir_for_clang_tidy(final_dir
, osx_cross_compile
):
518 # Make sure we only have what we expect.
530 dirs
.append("x86_64-unknown-linux-gnu")
531 for f
in glob
.glob("%s/*" % final_dir
):
532 if os
.path
.basename(f
) not in dirs
:
533 raise Exception("Found unknown file %s in the final directory" % f
)
534 if not os
.path
.isdir(f
):
535 raise Exception("Expected %s to be a directory" % f
)
538 "clang-apply-replacements",
544 re_clang_tidy
= re
.compile(r
"^(" + "|".join(kept_binaries
) + r
")(\.exe)?$", re
.I
)
545 for f
in glob
.glob("%s/bin/*" % final_dir
):
546 if re_clang_tidy
.search(os
.path
.basename(f
)) is None:
549 # Keep include/ intact.
551 # Remove the target-specific files.
553 if os
.path
.exists(os
.path
.join(final_dir
, "x86_64-unknown-linux-gnu")):
554 shutil
.rmtree(os
.path
.join(final_dir
, "x86_64-unknown-linux-gnu"))
556 # In lib/, only keep lib/clang/N.M.O/include and the LLVM shared library.
557 re_ver_num
= re
.compile(r
"^\d+\.\d+\.\d+$", re
.I
)
558 for f
in glob
.glob("%s/lib/*" % final_dir
):
559 name
= os
.path
.basename(f
)
562 if osx_cross_compile
and name
in ["libLLVM.dylib", "libclang-cpp.dylib"]:
565 fnmatch
.fnmatch(name
, "libLLVM*.so")
566 or fnmatch
.fnmatch(name
, "libclang-cpp.so*")
570 for f
in glob
.glob("%s/lib/clang/*" % final_dir
):
571 if re_ver_num
.search(os
.path
.basename(f
)) is None:
573 for f
in glob
.glob("%s/lib/clang/*/*" % final_dir
):
574 if os
.path
.basename(f
) != "include":
577 # Completely remove libexec/, msbuild-bin and tools, if it exists.
578 shutil
.rmtree(os
.path
.join(final_dir
, "libexec"))
579 for d
in ("msbuild-bin", "tools"):
580 d
= os
.path
.join(final_dir
, d
)
581 if os
.path
.exists(d
):
584 # In share/, only keep share/clang/*tidy*
585 re_clang_tidy
= re
.compile(r
"format|tidy", re
.I
)
586 for f
in glob
.glob("%s/share/*" % final_dir
):
587 if os
.path
.basename(f
) != "clang":
589 for f
in glob
.glob("%s/share/clang/*" % final_dir
):
590 if re_clang_tidy
.search(os
.path
.basename(f
)) is None:
594 if __name__
== "__main__":
595 parser
= argparse
.ArgumentParser()
600 type=argparse
.FileType("r"),
601 help="Clang configuration file",
604 "--clean", required
=False, action
="store_true", help="Clean the build directory"
610 help="Skip tar packaging stage",
616 help="Do not patch source",
619 args
= parser
.parse_args()
621 if not os
.path
.exists("llvm/README.txt"):
623 "The script must be run from the root directory of the llvm-project tree"
625 source_dir
= os
.getcwd()
626 build_dir
= source_dir
+ "/build"
629 shutil
.rmtree(build_dir
)
632 llvm_source_dir
= source_dir
+ "/llvm"
633 extra_source_dir
= source_dir
+ "/clang-tools-extra"
634 clang_source_dir
= source_dir
+ "/clang"
635 lld_source_dir
= source_dir
+ "/lld"
636 compiler_rt_source_dir
= source_dir
+ "/compiler-rt"
637 libcxx_source_dir
= source_dir
+ "/libcxx"
638 libcxxabi_source_dir
= source_dir
+ "/libcxxabi"
648 cxx_name
= "clang-cl"
650 config_dir
= os
.path
.dirname(args
.config
.name
)
651 config
= json
.load(args
.config
)
654 if "stages" in config
:
655 stages
= int(config
["stages"])
656 if stages
not in (1, 2, 3, 4):
657 raise ValueError("We only know how to build 1, 2, 3, or 4 stages.")
661 if pgo
not in (True, False):
662 raise ValueError("Only boolean values are accepted for pgo.")
663 if pgo
and stages
!= 4:
664 raise ValueError("PGO is only supported in 4-stage builds.")
665 build_type
= "Release"
666 if "build_type" in config
:
667 build_type
= config
["build_type"]
668 if build_type
not in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"):
670 "We only know how to do Release, Debug, RelWithDebInfo or "
674 if "build_libcxx" in config
:
675 build_libcxx
= config
["build_libcxx"]
676 if build_libcxx
not in (True, False):
677 raise ValueError("Only boolean values are accepted for build_libcxx.")
679 if "build_wasm" in config
:
680 build_wasm
= config
["build_wasm"]
681 if build_wasm
not in (True, False):
682 raise ValueError("Only boolean values are accepted for build_wasm.")
683 build_clang_tidy
= False
684 if "build_clang_tidy" in config
:
685 build_clang_tidy
= config
["build_clang_tidy"]
686 if build_clang_tidy
not in (True, False):
687 raise ValueError("Only boolean values are accepted for build_clang_tidy.")
688 build_clang_tidy_alpha
= False
689 # check for build_clang_tidy_alpha only if build_clang_tidy is true
690 if build_clang_tidy
and "build_clang_tidy_alpha" in config
:
691 build_clang_tidy_alpha
= config
["build_clang_tidy_alpha"]
692 if build_clang_tidy_alpha
not in (True, False):
694 "Only boolean values are accepted for build_clang_tidy_alpha."
696 build_clang_tidy_external
= False
697 # check for build_clang_tidy_external only if build_clang_tidy is true
698 if build_clang_tidy
and "build_clang_tidy_external" in config
:
699 build_clang_tidy_external
= config
["build_clang_tidy_external"]
700 if build_clang_tidy_external
not in (True, False):
702 "Only boolean values are accepted for build_clang_tidy_external."
704 osx_cross_compile
= False
705 if "osx_cross_compile" in config
:
706 osx_cross_compile
= config
["osx_cross_compile"]
707 if osx_cross_compile
not in (True, False):
708 raise ValueError("Only boolean values are accepted for osx_cross_compile.")
709 if osx_cross_compile
and not is_linux():
710 raise ValueError("osx_cross_compile can only be used on Linux.")
712 if "assertions" in config
:
713 assertions
= config
["assertions"]
714 if assertions
not in (True, False):
715 raise ValueError("Only boolean values are accepted for assertions.")
717 android_targets
= None
718 if "android_targets" in config
:
719 android_targets
= config
["android_targets"]
720 for attr
in ("ndk_toolchain", "ndk_sysroot", "ndk_includes", "api_level"):
721 for target
, cfg
in android_targets
.items():
724 "must specify '%s' as a key for android target: %s"
728 if "extra_targets" in config
:
729 extra_targets
= config
["extra_targets"]
730 if not isinstance(extra_targets
, list):
731 raise ValueError("extra_targets must be a list")
732 if not all(isinstance(t
, str) for t
in extra_targets
):
733 raise ValueError("members of extra_targets should be strings")
735 if is_darwin() or osx_cross_compile
:
736 os
.environ
["MACOSX_DEPLOYMENT_TARGET"] = (
737 "11.0" if os
.environ
.get("OSX_ARCH") == "arm64" else "10.12"
740 cc
= get_tool(config
, "cc")
741 cxx
= get_tool(config
, "cxx")
742 asm
= get_tool(config
, "ml" if is_windows() else "as")
743 ld
= get_tool(config
, "link" if is_windows() else "ld")
744 ar
= get_tool(config
, "lib" if is_windows() else "ar")
745 ranlib
= None if is_windows() else get_tool(config
, "ranlib")
747 if "libtool" in config
:
748 libtool
= get_tool(config
, "libtool")
750 if not os
.path
.exists(source_dir
):
751 os
.makedirs(source_dir
)
753 if not args
.skip_patch
:
754 for p
in config
.get("patches", []):
755 patch(os
.path
.join(config_dir
, p
), source_dir
)
757 compiler_rt_source_link
= llvm_source_dir
+ "/projects/compiler-rt"
760 (clang_source_dir
, llvm_source_dir
+ "/tools/clang"),
761 (extra_source_dir
, llvm_source_dir
+ "/tools/clang/tools/extra"),
762 (lld_source_dir
, llvm_source_dir
+ "/tools/lld"),
763 (compiler_rt_source_dir
, compiler_rt_source_link
),
764 (libcxx_source_dir
, llvm_source_dir
+ "/projects/libcxx"),
765 (libcxxabi_source_dir
, llvm_source_dir
+ "/projects/libcxxabi"),
768 # On Windows, we have to re-copy the whole directory every time.
769 if not is_windows() and os
.path
.islink(l
[1]):
772 if os
.path
.exists(l
[0]):
775 package_name
= "clang"
777 package_name
= "clang-tidy"
778 if not args
.skip_patch
:
780 source_dir
, build_clang_tidy_alpha
, build_clang_tidy_external
783 if not os
.path
.exists(build_dir
):
784 os
.makedirs(build_dir
)
786 libcxx_include_dir
= os
.path
.join(llvm_source_dir
, "projects", "libcxx", "include")
788 stage1_dir
= build_dir
+ "/stage1"
789 stage1_inst_dir
= stage1_dir
+ "/" + package_name
791 final_stage_dir
= stage1_dir
792 final_inst_dir
= stage1_inst_dir
796 extra_cxxflags
= ["-stdlib=libc++"]
798 extra_cxxflags2
= ["-stdlib=libc++"]
804 extra_cflags2
= ["-fPIC"]
805 # Silence clang's warnings about arguments not being used in compilation.
808 "-Qunused-arguments",
811 # Avoid libLLVM internal function calls going through the PLT.
812 extra_ldflags
= ["-Wl,-Bsymbolic-functions"]
813 # For whatever reason, LLVM's build system will set things up to turn
814 # on -ffunction-sections and -fdata-sections, but won't turn on the
815 # corresponding option to strip unused sections. We do it explicitly
816 # here. LLVM's build system is also picky about turning on ICF, so
817 # we do that explicitly here, too.
818 extra_ldflags
+= ["-fuse-ld=gold", "-Wl,--gc-sections", "-Wl,--icf=safe"]
822 # clang-cl would like to figure out what it's supposed to be emulating
823 # by looking at an MSVC install, but we don't really have that here.
827 "-fms-compatibility-version=19.15.26726",
834 if osx_cross_compile
:
835 # undo the damage done in the is_linux() block above, and also simulate
836 # the is_darwin() block above.
838 extra_cxxflags
= ["-stdlib=libc++"]
839 extra_cxxflags2
= ["-stdlib=libc++"]
843 "x86_64-apple-darwin",
844 "-mlinker-version=137",
846 "%s/bin" % os
.getenv("CROSS_CCTOOLS_PATH"),
848 os
.getenv("CROSS_SYSROOT"),
849 # technically the sysroot flag there should be enough to deduce this,
850 # but clang needs some help to figure this out.
851 "-I%s/usr/include" % os
.getenv("CROSS_SYSROOT"),
853 "%s/System/Library/Frameworks" % os
.getenv("CROSS_SYSROOT"),
855 extra_cflags
+= extra_flags
856 extra_cxxflags
+= extra_flags
857 extra_cflags2
+= extra_flags
858 extra_cxxflags2
+= extra_flags
859 extra_asmflags
+= extra_flags
861 "-Wl,-syslibroot,%s" % os
.getenv("CROSS_SYSROOT"),
865 upload_dir
= os
.getenv("UPLOAD_DIR")
866 if assertions
and upload_dir
:
867 extra_cflags2
+= ["-fcrash-diagnostics-dir=%s" % upload_dir
]
868 extra_cxxflags2
+= ["-fcrash-diagnostics-dir=%s" % upload_dir
]
872 [cxx
] + extra_cxxflags
,
873 [asm
] + extra_asmflags
,
874 [ld
] + extra_ldflags
,
887 is_final_stage
=(stages
== 1),
890 runtimes_source_link
= llvm_source_dir
+ "/runtimes/compiler-rt"
893 stage2_dir
= build_dir
+ "/stage2"
894 stage2_inst_dir
= stage2_dir
+ "/" + package_name
895 final_stage_dir
= stage2_dir
896 final_inst_dir
= stage2_inst_dir
897 pgo_phase
= "gen" if pgo
else None
899 [stage1_inst_dir
+ "/bin/%s%s" % (cc_name
, exe_ext
)] + extra_cflags2
,
900 [stage1_inst_dir
+ "/bin/%s%s" % (cxx_name
, exe_ext
)] + extra_cxxflags2
,
901 [stage1_inst_dir
+ "/bin/%s%s" % (cc_name
, exe_ext
)] + extra_asmflags
,
902 [ld
] + extra_ldflags
,
915 compiler_rt_source_dir
,
916 runtimes_source_link
,
917 compiler_rt_source_link
,
918 is_final_stage
=(stages
== 2),
919 android_targets
=android_targets
,
920 extra_targets
=extra_targets
,
925 stage3_dir
= build_dir
+ "/stage3"
926 stage3_inst_dir
= stage3_dir
+ "/" + package_name
927 final_stage_dir
= stage3_dir
928 final_inst_dir
= stage3_inst_dir
930 [stage2_inst_dir
+ "/bin/%s%s" % (cc_name
, exe_ext
)] + extra_cflags2
,
931 [stage2_inst_dir
+ "/bin/%s%s" % (cxx_name
, exe_ext
)] + extra_cxxflags2
,
932 [stage2_inst_dir
+ "/bin/%s%s" % (cc_name
, exe_ext
)] + extra_asmflags
,
933 [ld
] + extra_ldflags
,
946 compiler_rt_source_dir
,
947 runtimes_source_link
,
948 compiler_rt_source_link
,
950 extra_targets
=extra_targets
,
954 stage4_dir
= build_dir
+ "/stage4"
955 stage4_inst_dir
= stage4_dir
+ "/" + package_name
956 final_stage_dir
= stage4_dir
957 final_inst_dir
= stage4_inst_dir
961 llvm_profdata
= stage3_inst_dir
+ "/bin/llvm-profdata%s" % exe_ext
962 merge_cmd
= [llvm_profdata
, "merge", "-o", "merged.profdata"]
963 profraw_files
= glob
.glob(
964 os
.path
.join(stage2_dir
, "build", "profiles", "*.profraw")
966 if not os
.path
.exists(stage4_dir
):
968 run_in(stage4_dir
, merge_cmd
+ profraw_files
)
970 [stage3_inst_dir
+ "/bin/%s%s" % (cc_name
, exe_ext
)] + extra_cflags2
,
971 [stage3_inst_dir
+ "/bin/%s%s" % (cxx_name
, exe_ext
)] + extra_cxxflags2
,
972 [stage3_inst_dir
+ "/bin/%s%s" % (cc_name
, exe_ext
)] + extra_asmflags
,
973 [ld
] + extra_ldflags
,
986 compiler_rt_source_dir
,
987 runtimes_source_link
,
988 compiler_rt_source_link
,
990 extra_targets
=extra_targets
,
995 prune_final_dir_for_clang_tidy(
996 os
.path
.join(final_stage_dir
, package_name
), osx_cross_compile
999 # Copy the wasm32 builtins to the final_inst_dir if the archive is present.
1000 if "wasi-compiler-rt" in config
:
1001 compiler_rt
= config
["wasi-compiler-rt"].format(**os
.environ
)
1002 if os
.path
.isdir(compiler_rt
):
1003 for libdir
in glob
.glob(
1004 os
.path
.join(final_inst_dir
, "lib", "clang", "*", "lib")
1006 srcdir
= os
.path
.join(compiler_rt
, "lib", "wasi")
1007 print("Copying from wasi-compiler-rt srcdir %s" % srcdir
)
1008 # Copy the contents of the "lib/wasi" subdirectory to the
1009 # appropriate location in final_inst_dir.
1010 destdir
= os
.path
.join(libdir
, "wasi")
1012 copy_tree(srcdir
, destdir
)
1014 if not args
.skip_tar
:
1015 build_tar_package("%s.tar.zst" % package_name
, final_stage_dir
, package_name
)