clang_cc_toolchain_config.bzl 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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. """A Starlark cc_toolchain configuration rule"""
  5. load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
  6. load(
  7. "@rules_cc//cc:cc_toolchain_config_lib.bzl",
  8. "feature",
  9. "feature_set",
  10. "flag_group",
  11. "flag_set",
  12. "with_feature_set",
  13. )
  14. load("@rules_cc//cc:defs.bzl", "cc_toolchain")
  15. load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
  16. load(
  17. ":cc_toolchain_actions.bzl",
  18. "all_compile_actions",
  19. "all_cpp_compile_actions",
  20. "all_link_actions",
  21. "codegen_compile_actions",
  22. "preprocessor_compile_actions",
  23. )
  24. load(
  25. ":cc_toolchain_base_features.bzl",
  26. "base_features",
  27. "output_flags_feature",
  28. "user_flags_feature",
  29. )
  30. load(":cc_toolchain_config_features.bzl", "target_os_features")
  31. load(":cc_toolchain_debugging.bzl", "debugging_features")
  32. load(
  33. ":cc_toolchain_linking.bzl",
  34. "default_link_libraries_feature",
  35. "linking_features",
  36. "macos_link_libraries_feature",
  37. )
  38. load(":cc_toolchain_modules.bzl", "modules_features")
  39. load(
  40. ":cc_toolchain_optimization.bzl",
  41. "aarch64_cpu_flags",
  42. "optimization_features",
  43. "x86_64_cpu_flags",
  44. )
  45. load(
  46. ":cc_toolchain_sanitizer_features.bzl",
  47. "macos_asan_workarounds",
  48. "sanitizer_features",
  49. "sanitizer_static_lib_flags",
  50. )
  51. load(
  52. ":cc_toolchain_tools.bzl",
  53. "llvm_action_configs",
  54. "llvm_tool_paths",
  55. )
  56. load(
  57. ":clang_detected_variables.bzl",
  58. "clang_bindir",
  59. "clang_include_dirs_list",
  60. "clang_resource_dir",
  61. "clang_version_for_cache",
  62. "llvm_bindir",
  63. "sysroot_dir",
  64. )
  65. def _build_features(ctx):
  66. # TODO: Refactor this into a reusable form in its own file.
  67. default_flags_feature = feature(
  68. name = "default_flags",
  69. enabled = True,
  70. flag_sets = [
  71. flag_set(
  72. actions = all_compile_actions + all_link_actions,
  73. flag_groups = [
  74. flag_group(flags = [
  75. "-no-canonical-prefixes",
  76. "-fcolor-diagnostics",
  77. ]),
  78. flag_group(
  79. expand_if_available = "sysroot",
  80. flags = ["--sysroot=%{sysroot}"],
  81. ),
  82. ],
  83. ),
  84. flag_set(
  85. actions = all_compile_actions,
  86. flag_groups = [
  87. # First, flags for all compiles, regardless of output.
  88. flag_group(flags = [
  89. "-Werror",
  90. "-Wall",
  91. "-Wextra",
  92. "-Wthread-safety",
  93. "-Wself-assign",
  94. "-Wimplicit-fallthrough",
  95. "-Wctad-maybe-unsupported",
  96. "-Wextra-semi",
  97. "-Wmissing-prototypes",
  98. "-Wzero-as-null-pointer-constant",
  99. "-Wdelete-non-virtual-dtor",
  100. # TODO: Regression that warns on anonymous unions;
  101. # remove depending on fix.
  102. "-Wno-missing-designated-field-initializers",
  103. # Don't warn on external code as we can't
  104. # necessarily patch it easily. Note that these have
  105. # to be initial directories in the `#include` line.
  106. "--system-header-prefix=absl/",
  107. "--system-header-prefix=benchmark/",
  108. "--system-header-prefix=boost/",
  109. "--system-header-prefix=clang-tools-extra/",
  110. "--system-header-prefix=clang/",
  111. "--system-header-prefix=gmock/",
  112. "--system-header-prefix=gtest/",
  113. "--system-header-prefix=libfuzzer/",
  114. "--system-header-prefix=llvm/",
  115. "--system-header-prefix=re2/",
  116. "--system-header-prefix=tools/cpp/",
  117. "--system-header-prefix=tree_sitter/",
  118. # Compile actions shouldn't link anything.
  119. "-c",
  120. ]),
  121. # Flags controlling the production of specific outputs from
  122. # compile actions.
  123. flag_group(
  124. expand_if_available = "output_assembly_file",
  125. flags = ["-S"],
  126. ),
  127. flag_group(
  128. expand_if_available = "output_preprocess_file",
  129. flags = ["-E"],
  130. ),
  131. flag_group(
  132. expand_if_available = "dependency_file",
  133. flags = ["-MD", "-MF", "%{dependency_file}"],
  134. ),
  135. flag_group(
  136. expand_if_available = "output_file",
  137. flags = ["-frandom-seed=%{output_file}"],
  138. ),
  139. ],
  140. ),
  141. flag_set(
  142. # Flags specific to compiling C++ source.
  143. actions = all_cpp_compile_actions,
  144. flag_groups = [flag_group(flags = [
  145. "-std=c++20",
  146. ])],
  147. ),
  148. flag_set(
  149. actions = codegen_compile_actions,
  150. flag_groups = [flag_group(flags = [
  151. "-ffunction-sections",
  152. "-fdata-sections",
  153. ])],
  154. ),
  155. flag_set(
  156. actions = codegen_compile_actions,
  157. flag_groups = [flag_group(
  158. expand_if_available = "pic",
  159. flags = ["-fPIC"],
  160. )],
  161. ),
  162. flag_set(
  163. actions = preprocessor_compile_actions,
  164. flag_groups = [
  165. flag_group(flags = [
  166. # Disable a warning and override builtin macros to
  167. # ensure a hermetic build.
  168. "-Wno-builtin-macro-redefined",
  169. "-D__DATE__=\"redacted\"",
  170. "-D__TIMESTAMP__=\"redacted\"",
  171. "-D__TIME__=\"redacted\"",
  172. # Pass the clang version as a define so that bazel
  173. # caching is more likely to notice version changes.
  174. "-DCLANG_VERSION_FOR_CACHE=\"%s\"" % clang_version_for_cache,
  175. ]),
  176. flag_group(
  177. flags = ["-D%{preprocessor_defines}"],
  178. iterate_over = "preprocessor_defines",
  179. ),
  180. flag_group(
  181. expand_if_available = "includes",
  182. flags = ["-include", "%{includes}"],
  183. iterate_over = "includes",
  184. ),
  185. flag_group(
  186. flags = ["-iquote", "%{quote_include_paths}"],
  187. iterate_over = "quote_include_paths",
  188. ),
  189. flag_group(
  190. flags = ["-I%{include_paths}"],
  191. iterate_over = "include_paths",
  192. ),
  193. flag_group(
  194. flags = ["-isystem", "%{system_include_paths}"],
  195. iterate_over = "system_include_paths",
  196. ),
  197. ],
  198. ),
  199. flag_set(
  200. actions = [
  201. ACTION_NAMES.cpp_link_dynamic_library,
  202. ACTION_NAMES.cpp_link_nodeps_dynamic_library,
  203. ],
  204. flag_groups = [flag_group(flags = ["-shared"])],
  205. ),
  206. flag_set(
  207. actions = all_link_actions,
  208. flag_groups = [
  209. flag_group(
  210. expand_if_available = "strip_debug_symbols",
  211. flags = ["-Wl,-S"],
  212. ),
  213. flag_group(
  214. expand_if_available = "library_search_directories",
  215. flags = ["-L%{library_search_directories}"],
  216. iterate_over = "library_search_directories",
  217. ),
  218. flag_group(
  219. expand_if_available =
  220. "runtime_library_search_directories",
  221. iterate_over = "runtime_library_search_directories",
  222. flags = [
  223. "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}",
  224. ],
  225. ),
  226. ],
  227. ),
  228. ],
  229. )
  230. libcxx_feature = feature(
  231. name = "libcxx",
  232. enabled = True,
  233. flag_sets = [
  234. flag_set(
  235. actions = all_cpp_compile_actions + all_link_actions,
  236. flag_groups = [flag_group(flags = [
  237. "-stdlib=libc++",
  238. ])],
  239. with_features = [
  240. # libc++ is only used on non-Windows platforms.
  241. with_feature_set(not_features = ["windows_target"]),
  242. ],
  243. ),
  244. flag_set(
  245. actions = all_link_actions,
  246. flag_groups = [flag_group(flags = [
  247. "-unwindlib=libunwind",
  248. ])],
  249. with_features = [
  250. # libc++ is only used on non-Windows platforms.
  251. with_feature_set(not_features = ["windows_target"]),
  252. ],
  253. ),
  254. ],
  255. )
  256. # An enabled feature that requires the `fastbuild` compilation. This is used
  257. # to toggle general features on by default, while allowing them to be
  258. # directly enabled and disabled more generally as desired.
  259. enable_in_fastbuild = feature(
  260. name = "enable_in_fastbuild",
  261. enabled = True,
  262. requires = [feature_set(["fastbuild"])],
  263. implies = [
  264. "asan",
  265. "asan_min_size",
  266. "minimal_optimization_flags",
  267. "minimal_debug_info_flags",
  268. ],
  269. )
  270. # Clang HARDENING_MODE has 4 possible values:
  271. # https://libcxx.llvm.org/Hardening.html#notes-for-users
  272. #
  273. # Do not enable DEBUG hardening mode, even for -c dbg, because its
  274. # performance impact on llvm-symbolizer is too severe -- this flag
  275. # results in symbolization becoming quadratic in the number of debug
  276. # symbols, in practice meaning it never completes.
  277. libcpp_debug_flags = [
  278. "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE",
  279. ]
  280. libcpp_release_flags = [
  281. "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST",
  282. ]
  283. # TODO: Refactor this into a reusable form in its own file.
  284. linux_flags_feature = feature(
  285. name = "linux_flags",
  286. enabled = True,
  287. flag_sets = [
  288. flag_set(
  289. actions = all_link_actions,
  290. flag_groups = [flag_group(
  291. flags = [
  292. "-fuse-ld=lld",
  293. # Force the C++ standard library and runtime libraries
  294. # to be statically linked. This works even with libc++
  295. # and libunwind despite the names, provided libc++ is
  296. # built with the CMake option:
  297. # - `-DCMAKE_POSITION_INDEPENDENT_CODE=ON`
  298. "-static-libstdc++",
  299. "-static-libgcc",
  300. # Link with Clang's runtime library. This is always
  301. # linked statically.
  302. "-rtlib=compiler-rt",
  303. # Explicitly add LLVM libs to the search path to preempt
  304. # the detected GCC installation's library paths. Those
  305. # might have a system installed libc++ and we want to
  306. # find the one next to our Clang.
  307. "-L" + llvm_bindir + "/../lib",
  308. # Link with pthread.
  309. "-lpthread",
  310. # Force linking the static libc++abi archive here. This
  311. # *should* be linked automatically, but not every
  312. # release of LLVM correctly sets the CMake flags to do
  313. # so.
  314. "-l:libc++abi.a",
  315. ],
  316. )],
  317. ),
  318. flag_set(
  319. actions = all_compile_actions,
  320. flag_groups = [flag_group(flags = libcpp_debug_flags)],
  321. with_features = [with_feature_set(not_features = ["opt"])],
  322. ),
  323. flag_set(
  324. actions = all_compile_actions,
  325. flag_groups = [flag_group(flags = libcpp_release_flags)],
  326. with_features = [with_feature_set(features = ["opt"])],
  327. ),
  328. flag_set(
  329. actions = [ACTION_NAMES.cpp_link_executable],
  330. flag_groups = [flag_group(
  331. expand_if_available = "force_pic",
  332. flags = ["-pie"],
  333. )],
  334. ),
  335. ],
  336. )
  337. # TODO: Refactor this into a reusable form in its own file.
  338. macos_flags_feature = feature(
  339. name = "macos_flags",
  340. enabled = True,
  341. flag_sets = [
  342. flag_set(
  343. actions = [ACTION_NAMES.cpp_link_executable],
  344. flag_groups = [flag_group(
  345. expand_if_available = "force_pic",
  346. flags = ["-fpie"],
  347. )],
  348. ),
  349. ],
  350. )
  351. # TODO: Refactor this into a reusable form in its own file.
  352. freebsd_flags_feature = feature(
  353. name = "freebsd_flags",
  354. enabled = True,
  355. flag_sets = [
  356. flag_set(
  357. actions = [
  358. ACTION_NAMES.c_compile,
  359. ACTION_NAMES.cpp_compile,
  360. ACTION_NAMES.cpp_header_parsing,
  361. ACTION_NAMES.cpp_module_compile,
  362. ],
  363. flag_groups = [flag_group(flags = ["-DHAVE_MALLCTL"])],
  364. ),
  365. flag_set(
  366. actions = [ACTION_NAMES.cpp_link_executable],
  367. flag_groups = [flag_group(
  368. expand_if_available = "force_pic",
  369. flags = ["-pie"],
  370. )],
  371. ),
  372. ],
  373. )
  374. # The order of the features determines the relative order of flags used.
  375. features = []
  376. features += base_features
  377. features += target_os_features(ctx.attr.target_os)
  378. features += [
  379. default_flags_feature,
  380. libcxx_feature,
  381. ]
  382. features += sanitizer_features
  383. features += optimization_features
  384. # TODO: Refactor target-specific feature management to be part of
  385. # `optimization_features`.
  386. if ctx.attr.target_cpu in ["aarch64", "arm64"]:
  387. features.append(aarch64_cpu_flags)
  388. else:
  389. features.append(x86_64_cpu_flags)
  390. features += modules_features
  391. features += debugging_features
  392. # Next, add the features based on the target platform. Here too the
  393. # features are order sensitive.
  394. if ctx.attr.target_os == "linux":
  395. features.append(sanitizer_static_lib_flags)
  396. features.append(linux_flags_feature)
  397. sysroot = None
  398. elif ctx.attr.target_os == "windows":
  399. # TODO: Need to figure out if we need to add windows specific features
  400. # I think the .pdb debug files will need to be handled differently,
  401. # so that might be an example where a feature must be added.
  402. sysroot = None
  403. elif ctx.attr.target_os == "macos":
  404. features.append(macos_asan_workarounds)
  405. features.append(macos_flags_feature)
  406. sysroot = sysroot_dir
  407. elif ctx.attr.target_os == "freebsd":
  408. features.append(sanitizer_static_lib_flags)
  409. features.append(freebsd_flags_feature)
  410. sysroot = sysroot_dir
  411. else:
  412. fail("Unsupported target OS!")
  413. # Next, append the libraries to link.
  414. features += linking_features
  415. # TODO: Refactor the target-specific feature management here to be part of
  416. # building `linking_features`.
  417. features += [
  418. feature(name = "supports_dynamic_linker", enabled = ctx.attr.target_os == "linux"),
  419. feature(name = "supports_start_end_lib", enabled = ctx.attr.target_os == "linux"),
  420. ]
  421. if ctx.attr.target_os == "macos":
  422. features.append(macos_link_libraries_feature)
  423. else:
  424. features.append(default_link_libraries_feature)
  425. # Lastly, we add a feature that enables others in the default `fastbuild`
  426. # mode. This is also a good place to add any project-specific features.
  427. features.append(enable_in_fastbuild)
  428. # Add user flags and the output flags at the end.
  429. features.append(user_flags_feature)
  430. features.append(output_flags_feature)
  431. return features
  432. def _impl(ctx):
  433. # TODO: See if this can be refactored into platform features.
  434. if ctx.attr.target_os == "linux":
  435. sysroot = None
  436. elif ctx.attr.target_os == "windows":
  437. sysroot = None
  438. elif ctx.attr.target_os == "macos":
  439. sysroot = sysroot_dir
  440. elif ctx.attr.target_os == "freebsd":
  441. sysroot = sysroot_dir
  442. else:
  443. fail("Unsupported target OS!")
  444. identifier = "local-{0}-{1}".format(ctx.attr.target_cpu, ctx.attr.target_os)
  445. return cc_common.create_cc_toolchain_config_info(
  446. ctx = ctx,
  447. features = _build_features(ctx),
  448. action_configs = llvm_action_configs(llvm_bindir, clang_bindir),
  449. cxx_builtin_include_directories = clang_include_dirs_list + [
  450. # Add Clang's resource directory to the end of the builtin include
  451. # directories to cover the use of sanitizer resource files by the
  452. # driver.
  453. clang_resource_dir + "/share",
  454. ],
  455. builtin_sysroot = sysroot,
  456. # This configuration only supports local non-cross builds so derive
  457. # everything from the target CPU selected.
  458. toolchain_identifier = identifier,
  459. host_system_name = identifier,
  460. target_system_name = identifier,
  461. target_cpu = ctx.attr.target_cpu,
  462. # This is used to expose a "flag" that `config_setting` rules can use to
  463. # determine if the compiler is Clang.
  464. compiler = "clang",
  465. # These attributes aren't meaningful at all so just use placeholder
  466. # values.
  467. target_libc = "local",
  468. abi_version = "local",
  469. abi_libc_version = "local",
  470. # We do have to pass in our tool paths.
  471. tool_paths = llvm_tool_paths(llvm_bindir, clang_bindir),
  472. )
  473. cc_toolchain_config = rule(
  474. implementation = _impl,
  475. attrs = {
  476. "target_cpu": attr.string(mandatory = True),
  477. "target_os": attr.string(mandatory = True),
  478. },
  479. provides = [CcToolchainConfigInfo],
  480. )
  481. def cc_local_toolchain_suite(name, configs):
  482. """Create a toolchain suite that uses the local Clang/LLVM install.
  483. Args:
  484. name: The name of the toolchain suite to produce.
  485. configs: An array of (os, cpu) pairs to support in the toolchain.
  486. """
  487. # An empty filegroup to use when stubbing out the toolchains.
  488. native.filegroup(
  489. name = name + "_empty",
  490. srcs = [],
  491. )
  492. # Create the individual local toolchains for each CPU.
  493. for (os, cpu) in configs:
  494. config_name = "{0}_{1}_{2}".format(name, os, cpu)
  495. cc_toolchain_config(
  496. name = config_name + "_config",
  497. target_os = os,
  498. target_cpu = cpu,
  499. )
  500. cc_toolchain(
  501. name = config_name + "_tools",
  502. all_files = ":" + name + "_empty",
  503. ar_files = ":" + name + "_empty",
  504. as_files = ":" + name + "_empty",
  505. compiler_files = ":" + name + "_empty",
  506. dwp_files = ":" + name + "_empty",
  507. linker_files = ":" + name + "_empty",
  508. objcopy_files = ":" + name + "_empty",
  509. strip_files = ":" + name + "_empty",
  510. supports_param_files = 1,
  511. toolchain_config = ":" + config_name + "_config",
  512. toolchain_identifier = config_name,
  513. )
  514. compatible_with = ["@platforms//cpu:" + cpu, "@platforms//os:" + os]
  515. native.toolchain(
  516. name = config_name,
  517. exec_compatible_with = compatible_with,
  518. target_compatible_with = compatible_with,
  519. toolchain = config_name + "_tools",
  520. toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  521. )