clang_bootstrap.bzl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. # Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. # Exceptions. See /LICENSE for license information.
  3. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. """Starlark rules to bootstrap Clang (and LLVM).
  5. These rules are loaded as part of the `WORKSPACE`, and used by
  6. `clang_configuration.bzl`. The llvm-project submodule is used for the build.
  7. """
  8. FORCE_LOCAL_BOOTSTRAP_ENV = "CARBON_FORCE_LOCAL_BOOTSTRAP_BUILD"
  9. def _run(
  10. repository_ctx,
  11. cmd,
  12. timeout = 10,
  13. environment = {},
  14. quiet = True):
  15. """Runs the provided `cmd`, checks for failure, and returns the result."""
  16. exec_result = repository_ctx.execute(
  17. cmd,
  18. timeout = timeout,
  19. environment = environment,
  20. quiet = quiet,
  21. )
  22. if exec_result.return_code != 0:
  23. fail("Unable to run command successfully: %s" % str(cmd))
  24. return exec_result
  25. def _detect_system_clang(repository_ctx):
  26. """Detects whether a system-provided clang can be used.
  27. Returns a tuple of (is_clang, environment).
  28. """
  29. # If the user provides an explicit `CC` environment variable, use that as
  30. # the compiler.
  31. cc = repository_ctx.os.environ.get("CC")
  32. cxx = repository_ctx.os.environ.get("CXX")
  33. if cc or cxx:
  34. version_output = _run(repository_ctx, [cc, "--version"]).stdout
  35. return "clang" in version_output, {}
  36. # If we can build our Clang toolchain using a system-installed Clang, try
  37. # to do so.
  38. system_clang = repository_ctx.which("clang")
  39. if system_clang:
  40. return True, {
  41. "CC": str(system_clang),
  42. "CXX": str(system_clang) + "++",
  43. }
  44. return False, {}
  45. def _get_cmake_defines(repository_ctx, is_clang):
  46. """Returns a long list of cmake defines for the bootstrap."""
  47. modules_setting = "OFF"
  48. if is_clang:
  49. modules_setting = "ON"
  50. static_link_cxx = "ON"
  51. unstable_libcxx_abi = "ON"
  52. if repository_ctx.os.name.lower().startswith("mac os"):
  53. # macOS doesn't support the static C++ standard library linking. Turn
  54. # it off here, and disable the unstable libc++ ABI as we will also be
  55. # unable to use it later on.
  56. static_link_cxx = "OFF"
  57. unstable_libcxx_abi = "OFF"
  58. return [
  59. "-DLLVM_ENABLE_PROJECTS=clang;clang-tools-extra;lld;libcxx;libcxxabi;compiler-rt;libunwind",
  60. "-DCMAKE_BUILD_TYPE=Release",
  61. "-DLLVM_ENABLE_ASSERTIONS=OFF",
  62. "-DLLVM_ENABLE_MODULES=" + modules_setting,
  63. "-DLLVM_STATIC_LINK_CXX_STDLIB=" + static_link_cxx,
  64. "-DLLVM_TARGETS_TO_BUILD=AArch64;X86",
  65. "-DLIBCXX_ABI_UNSTABLE=" + unstable_libcxx_abi,
  66. "-DLIBCXX_ENABLE_ASSERTIONS=OFF",
  67. "-DLIBCXXABI_ENABLE_ASSERTIONS=OFF",
  68. # Disable components of the build that we don't use while building Carbon.
  69. "-DCLANG_ENABLE_ARCMT=OFF",
  70. "-DCLANG_INCLUDE_TESTS=OFF",
  71. "-DCLANG_TOOL_APINOTES_TEST_BUILD=OFF",
  72. "-DCLANG_TOOL_ARCMT_TEST_BUILD=OFF",
  73. "-DCLANG_TOOL_CLANG_CHECK_BUILD=OFF",
  74. "-DCLANG_TOOL_CLANG_DIFF_BUILD=OFF",
  75. "-DCLANG_TOOL_CLANG_EXTDEF_MAPPING_BUILD=OFF",
  76. "-DCLANG_TOOL_CLANG_FUZZER_BUILD=OFF",
  77. "-DCLANG_TOOL_CLANG_IMPORT_TEST_BUILD=OFF",
  78. "-DCLANG_TOOL_CLANG_OFFLOAD_BUNDLER_BUILD=OFF",
  79. "-DCLANG_TOOL_CLANG_OFFLOAD_WRAPPER_BUILD=OFF",
  80. "-DCLANG_TOOL_CLANG_SCAN_DEPS_BUILD=OFF",
  81. "-DCLANG_TOOL_CLANG_SHLIB_BUILD=OFF",
  82. "-DCLANG_TOOL_C_ARCMT_TEST_BUILD=OFF",
  83. "-DCLANG_TOOL_C_INDEX_TEST_BUILD=OFF",
  84. "-DCLANG_TOOL_DIAGTOOL_BUILD=OFF",
  85. "-DCLANG_TOOL_LIBCLANG_BUILD=OFF",
  86. "-DCLANG_TOOL_SCAN_BUILD_BUILD=OFF",
  87. "-DCLANG_TOOL_SCAN_VIEW_BUILD=OFF",
  88. "-DLLVM_BUILD_UTILS=OFF",
  89. "-DLLVM_ENABLE_BINDINGS=OFF",
  90. "-DLLVM_ENABLE_LIBXML2=OFF",
  91. "-DLLVM_ENABLE_OCAMLDOC=OFF",
  92. "-DLLVM_INCLUDE_BENCHMARKS=OFF",
  93. "-DLLVM_INCLUDE_DOCS=OFF",
  94. "-DLLVM_INCLUDE_EXAMPLES=OFF",
  95. "-DLLVM_INCLUDE_GO_TESTS=OFF",
  96. "-DLLVM_INCLUDE_TESTS=OFF",
  97. "-DLLVM_INCLUDE_UTILS=OFF",
  98. "-DLLVM_TOOL_BUGPOINT_BUILD=OFF",
  99. "-DLLVM_TOOL_BUGPOINT_PASSES_BUILD=OFF",
  100. "-DLLVM_TOOL_DSYMUTIL_BUILD=OFF",
  101. "-DLLVM_TOOL_GOLD_BUILD=OFF",
  102. "-DLLVM_TOOL_LLC_BUILD=OFF",
  103. "-DLLVM_TOOL_LLI_BUILD=OFF",
  104. "-DLLVM_TOOL_LLVM_AS_BUILD=OFF",
  105. "-DLLVM_TOOL_LLVM_BCANALYZER_BUILD=OFF",
  106. "-DLLVM_TOOL_LLVM_CAT_BUILD=OFF",
  107. "-DLLVM_TOOL_LLVM_CFI_VERIFY_BUILD=OFF",
  108. "-DLLVM_TOOL_LLVM_CONFIG_BUILD=OFF",
  109. "-DLLVM_TOOL_LLVM_CVTRES_BUILD=OFF",
  110. "-DLLVM_TOOL_LLVM_CXXDUMP_BUILD=OFF",
  111. "-DLLVM_TOOL_LLVM_CXXFILT_BUILD=OFF",
  112. "-DLLVM_TOOL_LLVM_CXXMAP_BUILD=OFF",
  113. "-DLLVM_TOOL_LLVM_C_TEST_BUILD=OFF",
  114. "-DLLVM_TOOL_LLVM_DIFF_BUILD=OFF",
  115. "-DLLVM_TOOL_LLVM_DWARFDUMP_BUILD=OFF",
  116. "-DLLVM_TOOL_LLVM_ELFABI_BUILD=OFF",
  117. "-DLLVM_TOOL_LLVM_EXEGESIS_BUILD=OFF",
  118. "-DLLVM_TOOL_LLVM_EXTRACT_BUILD=OFF",
  119. "-DLLVM_TOOL_LLVM_GO_BUILD=OFF",
  120. "-DLLVM_TOOL_LLVM_GSYMUTIL_BUILD=OFF",
  121. "-DLLVM_TOOL_LLVM_IFS_BUILD=OFF",
  122. "-DLLVM_TOOL_LLVM_ISEL_FUZZER_BUILD=OFF",
  123. "-DLLVM_TOOL_LLVM_ITANIUM_DEMANGLE_FUZZER_BUILD=OFF",
  124. "-DLLVM_TOOL_LLVM_JITLINK_BUILD=OFF",
  125. "-DLLVM_TOOL_LLVM_JITLISTENER_BUILD=OFF",
  126. "-DLLVM_TOOL_LLVM_LIBTOOL_DARWIN_BUILD=OFF",
  127. "-DLLVM_TOOL_LLVM_LINK_BUILD=OFF",
  128. "-DLLVM_TOOL_LLVM_LIPO_BUILD=OFF",
  129. "-DLLVM_TOOL_LLVM_LTO2_BUILD=OFF",
  130. "-DLLVM_TOOL_LLVM_LTO_BUILD=OFF",
  131. "-DLLVM_TOOL_LLVM_MCA_BUILD=OFF",
  132. "-DLLVM_TOOL_LLVM_MC_BUILD=OFF",
  133. "-DLLVM_TOOL_LLVM_ML_BUILD=OFF",
  134. "-DLLVM_TOOL_LLVM_MICROSOFT_DEMANGLE_FUZZER_BUILD=OFF",
  135. "-DLLVM_TOOL_LLVM_MT_BUILD=OFF",
  136. "-DLLVM_TOOL_LLVM_OPT_FUZZER_BUILD=OFF",
  137. "-DLLVM_TOOL_LLVM_PDBUTIL_BUILD=OFF",
  138. "-DLLVM_TOOL_LLVM_PROFDATA_BUILD=OFF",
  139. "-DLLVM_TOOL_LLVM_PROFGEN_BUILD=OFF",
  140. "-DLLVM_TOOL_LLVM_RC_BUILD=OFF",
  141. "-DLLVM_TOOL_LLVM_READOBJ_BUILD=OFF",
  142. "-DLLVM_TOOL_LLVM_REDUCE_BUILD=OFF",
  143. "-DLLVM_TOOL_LLVM_RTDYLD_BUILD=OFF",
  144. "-DLLVM_TOOL_LLVM_SHLIB_BUILD=OFF",
  145. "-DLLVM_TOOL_LLVM_SIZE_BUILD=OFF",
  146. "-DLLVM_TOOL_LLVM_SPECIAL_CASE_LIST_FUZZER_BUILD=OFF",
  147. "-DLLVM_TOOL_LLVM_SPLIT_BUILD=OFF",
  148. "-DLLVM_TOOL_LLVM_STRESS_BUILD=OFF",
  149. "-DLLVM_TOOL_LLVM_STRINGS_BUILD=OFF",
  150. "-DLLVM_TOOL_LLVM_XRAY_BUILD=OFF",
  151. "-DLLVM_TOOL_LLVM_YAML_NUMERIC_PARSER_FUZZER_BUILD=OFF",
  152. "-DLLVM_TOOL_LLVM_YAML_PARSER_FUZZER_BUILD=OFF",
  153. "-DLLVM_TOOL_LTO_BUILD=OFF",
  154. "-DLLVM_TOOL_OBJ2YAML_BUILD=OFF",
  155. "-DLLVM_TOOL_OPT_BUILD=OFF",
  156. "-DLLVM_TOOL_OPT_VIEWER_BUILD=OFF",
  157. "-DLLVM_TOOL_REMARKS_SHLIB_BUILD=OFF",
  158. "-DLLVM_TOOL_SPLIT_FILE_BUILD=OFF",
  159. "-DLLVM_TOOL_VERIFY_USELISTORDER_BUILD=OFF",
  160. "-DLLVM_TOOL_YAML2OBJ_BUILD=OFF",
  161. ]
  162. def _local_cmake_build_clang_toolchain(repository_ctx):
  163. """Locally build the LLVM toolchain with CMake and Ninja.
  164. This is used as a fallback for when we can't download a prebuilt set of
  165. binaries and libraries for a particular platform.
  166. """
  167. repository_ctx.report_progress("Configuring Clang toolchain bootstrap...")
  168. is_clang, environment = _detect_system_clang(repository_ctx)
  169. cmake = repository_ctx.which("cmake")
  170. if not cmake:
  171. fail("`cmake` not found: is it installed?")
  172. ninja = repository_ctx.which("ninja")
  173. if not ninja:
  174. fail("`ninja` not found: is it installed?")
  175. workspace_dir = repository_ctx.path(repository_ctx.attr._workspace).dirname
  176. llvm_dir = repository_ctx.path("%s/third_party/llvm-project/llvm" %
  177. workspace_dir)
  178. repository_ctx.report_progress(
  179. "Running CMake for the Clang toolchain build...",
  180. )
  181. cmake_args = [cmake, "-G", "Ninja", str(llvm_dir)]
  182. cmake_args += _get_cmake_defines(repository_ctx, is_clang)
  183. _run(
  184. repository_ctx,
  185. cmake_args,
  186. timeout = 600,
  187. environment = environment,
  188. # This is very slow, so print output as a form of progress.
  189. quiet = False,
  190. )
  191. # Run ninja for the final build.
  192. repository_ctx.report_progress("Building the Clang toolchain...")
  193. _run(
  194. repository_ctx,
  195. [ninja],
  196. timeout = 10800,
  197. # This is very slow, so print output as a form of progress.
  198. quiet = False,
  199. )
  200. def _download_prebuilt_toolchain(repository_ctx):
  201. """Downloads and extracts an LLVM build for the current platform.
  202. Returns `True` when a toolchain can be successfully downloaded.
  203. """
  204. repository_ctx.report_progress("Checking for a downloadable toolchain...")
  205. os = repository_ctx.os.name
  206. if os == "linux":
  207. url = "https://github.com/mmdriley/llvm-builds/releases/download/r32/llvm-linux.tar.xz"
  208. sha256 = "db9f2698aa84935efca3402bdebada127de16f6746adbe54d4cdb7e3b8fec5f3"
  209. elif os == "mac os x":
  210. url = "https://github.com/mmdriley/llvm-builds/releases/download/r32/llvm-macos.tar.xz"
  211. sha256 = "937b81c235977ed2b265baf656f30b7a03c33b6299090d91beb72c2b41846673"
  212. elif os.startswith("windows"):
  213. url = "https://github.com/mmdriley/llvm-builds/releases/download/r32/llvm-windows.tar.xz"
  214. sha256 = "b6b015f9f2fcfb79381004e6a3ae925df4fb827cf7e07f3d5b0b66210fddd172"
  215. else:
  216. print(("No prebuilt LLVM toolcahin to download for {}, falling back " +
  217. "to a local build. This may be very slow!").format(os))
  218. return False
  219. repository_ctx.report_progress("Downloading and extracting a toolchain...")
  220. repository_ctx.download_and_extract(url, sha256 = sha256)
  221. return True
  222. def _bootstrap_clang_toolchain_impl(repository_ctx):
  223. """Bootstrap a fresh Clang and LLVM toolchain for use.
  224. This will first try to download a pre-built archive of the LLVM toolchain
  225. if one is available.
  226. Otherwise will locally build the toolchain using CMake out of the LLVM
  227. submodule.
  228. """
  229. force_local_build = False
  230. if FORCE_LOCAL_BOOTSTRAP_ENV in repository_ctx.os.environ:
  231. print("Forcing a local bootstrap build. This may be very slow!")
  232. force_local_build = True
  233. if force_local_build or not _download_prebuilt_toolchain(repository_ctx):
  234. # Fallback to a local build.
  235. _local_cmake_build_clang_toolchain(repository_ctx)
  236. # Create an empty BUILD file to mark the package. The files are used without
  237. # Bazel labels directly pointing at them.
  238. repository_ctx.file("BUILD")
  239. bootstrap_clang_toolchain = repository_rule(
  240. implementation = _bootstrap_clang_toolchain_impl,
  241. configure = True,
  242. attrs = {
  243. # We use a label pointing at the workspace file to compute the
  244. # workspace directory.
  245. "_workspace": attr.label(
  246. default = Label("//:WORKSPACE"),
  247. allow_single_file = True,
  248. ),
  249. },
  250. environ = ["CC", "CXX", FORCE_LOCAL_BOOTSTRAP_ENV],
  251. )