carbon_cc_toolchain_config.bzl 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  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 cc_toolchain configuration rules for using the Carbon toolchain"""
  5. load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
  6. load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
  7. load(
  8. "@rules_cc//cc:cc_toolchain_config_lib.bzl",
  9. "action_config",
  10. "flag_group",
  11. "flag_set",
  12. "tool",
  13. )
  14. load(
  15. "@rules_cc//cc:defs.bzl",
  16. "CcToolchainConfigInfo",
  17. "cc_toolchain",
  18. )
  19. load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
  20. load("//toolchain/runtimes:carbon_runtimes.bzl", "carbon_runtimes_build")
  21. load(
  22. "carbon_clang_variables.bzl",
  23. "clang_include_dirs",
  24. "clang_resource_dir",
  25. "clang_sysroot",
  26. )
  27. load(
  28. "cc_toolchain_actions.bzl",
  29. "all_c_compile_actions",
  30. "all_cpp_compile_actions",
  31. "all_link_actions",
  32. )
  33. load("cc_toolchain_carbon_project_features.bzl", "carbon_project_features")
  34. load("cc_toolchain_features.bzl", "clang_cc_toolchain_features")
  35. load(
  36. ":cc_toolchain_tools.bzl",
  37. "llvm_tool_paths",
  38. )
  39. def _make_action_configs(tools, runtimes_path = None):
  40. runtimes_flag = "--no-build-runtimes"
  41. if runtimes_path:
  42. runtimes_flag = "--prebuilt-runtimes={0}".format(runtimes_path)
  43. return [
  44. action_config(
  45. action_name = name,
  46. enabled = True,
  47. tools = [tools.clang],
  48. )
  49. for name in all_c_compile_actions
  50. ] + [
  51. action_config(
  52. action_name = name,
  53. enabled = True,
  54. tools = [tools.clangpp],
  55. )
  56. for name in all_cpp_compile_actions
  57. ] + [
  58. action_config(
  59. action_name = name,
  60. enabled = True,
  61. tools = [tools.carbon_busybox],
  62. flag_sets = [flag_set(flag_groups = [flag_group(flags = [
  63. runtimes_flag,
  64. "link",
  65. # We want to allow Bazel to intermingle linked object files and
  66. # Clang-spelled link flags. The first `--` starts the list of
  67. # initial object files by ending flags to the `link` subcommand,
  68. # and the second `--` switches to Clang-spelled flags.
  69. "--",
  70. "--",
  71. ])])],
  72. )
  73. for name in all_link_actions
  74. ] + [
  75. action_config(
  76. action_name = name,
  77. enabled = True,
  78. tools = [tools.llvm_ar],
  79. )
  80. for name in [ACTION_NAMES.cpp_link_static_library]
  81. ] + [
  82. action_config(
  83. action_name = name,
  84. enabled = True,
  85. tools = [tools.llvm_strip],
  86. )
  87. for name in [ACTION_NAMES.strip]
  88. ]
  89. def _compute_clang_system_include_dirs():
  90. system_include_dirs_start_index = None
  91. for index, dir in enumerate(clang_include_dirs):
  92. # Skip over the include search directories until we find the resource
  93. # directory. The system include directories are everything after that.
  94. if dir.startswith(clang_resource_dir):
  95. system_include_dirs_start_index = index + 1
  96. break
  97. if not system_include_dirs_start_index:
  98. fail("Could not find the resource directory in the clang include " +
  99. "directories: {}".format(clang_include_dirs))
  100. return clang_include_dirs[system_include_dirs_start_index:]
  101. def _carbon_cc_toolchain_config_impl(ctx):
  102. llvm_bindir = "llvm/bin"
  103. clang_bindir = llvm_bindir
  104. tools = struct(
  105. carbon_busybox = tool(path = "carbon-busybox"),
  106. clang = tool(path = clang_bindir + "/clang"),
  107. clangpp = tool(path = clang_bindir + "/clang++"),
  108. llvm_ar = tool(path = llvm_bindir + "/llvm-ar"),
  109. llvm_strip = tool(path = llvm_bindir + "/llvm-strip"),
  110. )
  111. if ctx.attr.bins:
  112. carbon_busybox = None
  113. clang = None
  114. clangpp = None
  115. llvm_ar = None
  116. llvm_strip = None
  117. for f in ctx.files.bins:
  118. if f.basename == "carbon-busybox":
  119. carbon_busybox = f
  120. elif f.basename == "clang":
  121. clang = f
  122. elif f.basename == "clang++":
  123. clangpp = f
  124. elif f.basename == "llvm-ar":
  125. llvm_ar = f
  126. elif f.basename == "llvm-strip":
  127. llvm_strip = f
  128. if not all([carbon_busybox, clang, clangpp, llvm_ar, llvm_strip]):
  129. fail("Missing required tool in bins: {0}".format(ctx.attr.bins))
  130. llvm_bindir = llvm_ar.dirname
  131. clang_bindir = clang.dirname
  132. tools = struct(
  133. carbon_busybox = tool(tool = carbon_busybox),
  134. clang = tool(tool = clang),
  135. clangpp = tool(tool = clangpp),
  136. llvm_ar = tool(tool = llvm_ar),
  137. llvm_strip = tool(tool = llvm_strip),
  138. )
  139. # Only use a sysroot if a non-trivial one is set in Carbon's config.
  140. builtin_sysroot = None
  141. sysroot_include_search = []
  142. if clang_sysroot != "None" and clang_sysroot != "/":
  143. builtin_sysroot = clang_sysroot
  144. sysroot_include_search = ["%sysroot%/usr/include"]
  145. runtimes_path = None
  146. if ctx.attr.runtimes:
  147. for f in ctx.files.runtimes:
  148. if f.basename == "runtimes_root":
  149. runtimes_path = f.dirname
  150. break
  151. if not runtimes_path:
  152. fail("Unable to compute the runtimes path for: {0}".format(
  153. ctx.attr.runtimes,
  154. ))
  155. identifier = "{0}_toolchain_{1}_{2}".format(
  156. ctx.attr.identifier_prefix,
  157. ctx.attr.target_cpu,
  158. ctx.attr.target_os,
  159. )
  160. return cc_common.create_cc_toolchain_config_info(
  161. ctx = ctx,
  162. features = clang_cc_toolchain_features(
  163. target_os = ctx.attr.target_os,
  164. target_cpu = ctx.attr.target_cpu,
  165. # TODO: This should be configured externally rather than here so
  166. # that the install Carbon toolchain doesn't automatically include
  167. # Carbon-project-specific flags. However, that is especially awkward
  168. # to do until we fully migrate to a rules-based toolchain, and the
  169. # project-specific flags are largely harmless at the moment. We also
  170. # omit a meaningful cache key as when using the Carbon toolchain we
  171. # don't need it as it is a hermetic part of Bazel.
  172. project_features = carbon_project_features(cache_key = ""),
  173. ),
  174. action_configs = _make_action_configs(tools, runtimes_path),
  175. cxx_builtin_include_directories = [
  176. "runtimes/libunwind/include",
  177. "runtimes/libcxx/include",
  178. "runtimes/libcxxabi/include",
  179. "{}/include".format(clang_resource_dir),
  180. "runtimes/clang_resource_dir/include",
  181. ] + _compute_clang_system_include_dirs() + sysroot_include_search,
  182. builtin_sysroot = builtin_sysroot,
  183. # This configuration only supports local non-cross builds so derive
  184. # everything from the target CPU selected.
  185. toolchain_identifier = identifier,
  186. # This is used to expose a "flag" that `config_setting` rules can use to
  187. # determine if the compiler is Clang.
  188. compiler = "clang",
  189. # Pass in our tool paths to expose Make variables like $(NM) and
  190. # $(OBJCOPY).
  191. tool_paths = llvm_tool_paths(llvm_bindir, clang_bindir),
  192. )
  193. carbon_cc_toolchain_config = rule(
  194. implementation = _carbon_cc_toolchain_config_impl,
  195. attrs = {
  196. "bins": attr.label(mandatory = False),
  197. "identifier_prefix": attr.string(mandatory = True),
  198. "runtimes": attr.label(mandatory = False),
  199. "target_cpu": attr.string(mandatory = True),
  200. "target_os": attr.string(mandatory = True),
  201. },
  202. provides = [CcToolchainConfigInfo],
  203. )
  204. def _transition_with_stage_impl(_, attr):
  205. return {
  206. "//:bootstrap_stage": attr.stage,
  207. "//:runtimes_build": attr.enable_runtimes_build,
  208. }
  209. _transition_with_stage = transition(
  210. inputs = [],
  211. outputs = [
  212. "//:bootstrap_stage",
  213. "//:runtimes_build",
  214. ],
  215. implementation = _transition_with_stage_impl,
  216. )
  217. def _filegroup_with_stage_impl(ctx):
  218. return [DefaultInfo(files = depset(ctx.files.srcs))]
  219. filegroup_with_stage = rule(
  220. implementation = _filegroup_with_stage_impl,
  221. attrs = {
  222. # Whether to enable runtimes building for the sources of this filegroup.
  223. "enable_runtimes_build": attr.bool(default = False),
  224. # Mark that our dependencies are built through a transition.
  225. "srcs": attr.label_list(mandatory = True, cfg = _transition_with_stage),
  226. # The bootstrap stage that the sources of this filegroup should be built
  227. # with.
  228. "stage": attr.int(mandatory = True),
  229. # Enable transitions in this rule.
  230. "_allowlist_function_transition": attr.label(
  231. default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
  232. ),
  233. },
  234. doc = """
  235. A filegroup whose sources are built using a specific toolchain stage, and
  236. which provides an interface to build those sources with or without enabling
  237. runtimes building.
  238. """,
  239. )
  240. def _exec_filegroup_impl(ctx):
  241. return [DefaultInfo(files = depset(ctx.files.srcs))]
  242. _exec_filegroup = rule(
  243. implementation = _exec_filegroup_impl,
  244. attrs = {
  245. "srcs": attr.label_list(cfg = "exec"),
  246. },
  247. )
  248. def filegroup_with_stage_and_exec(name, srcs, stage, tags = []):
  249. """Wraps `filegroup_with_stage` with a conditional `exec` config transition.
  250. When `//:bootstrap_exec_config` is disabled, this works exactly like
  251. `filegroup_with_stage`. But when it is _enabled_, it also adds an `exec`
  252. config transition. This allows bootstrapping for a target that is not exec
  253. compatible with the host, and in general makes bootstrapping more robust at
  254. the expense of a likely duplicate build of the entire toolchain.
  255. """
  256. filegroup_with_stage(
  257. name = name + "_stage_only",
  258. srcs = srcs,
  259. stage = stage,
  260. tags = tags,
  261. )
  262. _exec_filegroup(
  263. name = name + "_with_exec",
  264. srcs = [":" + name + "_stage_only"],
  265. tags = tags,
  266. )
  267. native.alias(
  268. name = name,
  269. actual = select({
  270. "//:bootstrap_with_exec_config": ":" + name + "_with_exec",
  271. "//conditions:default": ":" + name + "_stage_only",
  272. }),
  273. tags = tags,
  274. )
  275. def _gen_cc_toolchain_paths_impl(ctx):
  276. cc_toolchain = find_cpp_toolchain(ctx)
  277. expanded_vars = [
  278. ctx.expand_make_variables("vars", v, {})
  279. for v in ctx.attr.vars
  280. ]
  281. out = ctx.actions.declare_file(ctx.attr.name + ".txt")
  282. ctx.actions.write(out, "\n".join(expanded_vars) + "\n")
  283. # Include all toolchain files in runfiles.
  284. runfiles = ctx.runfiles(files = [out]).merge(
  285. ctx.runfiles(transitive_files = cc_toolchain.all_files),
  286. )
  287. return [DefaultInfo(files = depset([out]), runfiles = runfiles)]
  288. gen_cc_toolchain_paths_with_stage = rule(
  289. implementation = _gen_cc_toolchain_paths_impl,
  290. attrs = {
  291. "enable_runtimes_build": attr.bool(default = False),
  292. "stage": attr.int(mandatory = True),
  293. "vars": attr.string_list(
  294. default = ["$(CC)", "$(AR)", "$(NM)", "$(OBJCOPY)", "$(STRIP)"],
  295. ),
  296. "_allowlist_function_transition": attr.label(
  297. default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
  298. ),
  299. "_cc_toolchain": attr.label(
  300. default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
  301. ),
  302. },
  303. toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
  304. cfg = _transition_with_stage,
  305. )
  306. def carbon_cc_toolchain_suite(
  307. name,
  308. all_hdrs,
  309. base_files,
  310. clang_hdrs,
  311. platforms,
  312. runtimes_cfg,
  313. build_stage = 1,
  314. base_stage = 0,
  315. tags = []):
  316. """Create a Carbon `cc_toolchain` for the current target platform.
  317. This provides the final toolchain for Carbon, but also all of the
  318. infrastructure for supporting on-demand built runtimes in this toolchain.
  319. There is also support for bootstrapping, where one `build_stage` toolchain
  320. builds on top of another `base_stage`.
  321. Args:
  322. name:
  323. The name of the toolchain suite to produce, used as the base of the
  324. names of each component of the toolchain suite.
  325. all_hdrs: A list of header files to include in the toolchain.
  326. base_files: A list of files to include in the toolchain.
  327. build_stage: The stage to use for the build files.
  328. base_stage: The stage to use for the base files.
  329. clang_hdrs: A list of header files to include in the toolchain.
  330. platforms: An array of (os, cpu) pairs to support in the toolchain.
  331. runtimes_cfg: The runtimes configuration to use in the toolchain.
  332. tags: Tags to apply to the toolchain.
  333. """
  334. # First, declare file groups that are explicitly built using the base stage,
  335. # and not in the runtimes build. These allow us to form the inputs to both
  336. # the runtimes toolchain and the main toolchain of this stage that are built
  337. # entirely by the base stage toolchain.
  338. filegroup_with_stage_and_exec(
  339. name = "{}_clang_hdrs".format(name),
  340. srcs = clang_hdrs,
  341. stage = base_stage,
  342. tags = tags,
  343. )
  344. filegroup_with_stage_and_exec(
  345. name = "{}_base_files".format(name),
  346. srcs = base_files,
  347. stage = base_stage,
  348. tags = tags,
  349. )
  350. filegroup_with_stage_and_exec(
  351. name = "{}_runtimes_compile_files".format(name),
  352. srcs = [
  353. ":{}_base_files".format(name),
  354. ":{}_clang_hdrs".format(name),
  355. ],
  356. stage = base_stage,
  357. tags = tags,
  358. )
  359. filegroup_with_stage_and_exec(
  360. name = "{}_compile_files".format(name),
  361. srcs = [":{}_base_files".format(name)] + all_hdrs,
  362. stage = base_stage,
  363. tags = tags,
  364. )
  365. # Now build a configuration and toolchain that is configured to work
  366. # _without_ runtimes, and be used to _build_ the runtimes on-demand.
  367. carbon_cc_toolchain_config(
  368. name = "{}_runtimes_toolchain_config".format(name),
  369. identifier_prefix = "{}_runtimes".format(name),
  370. target_cpu = select({
  371. # Note that we need to select on both OS and CPU so that we end up
  372. # spelling the CPU in the correct OS-specific ways.
  373. ":is_{}_{}".format(os, cpu): cpu
  374. for os, cpus in platforms.items()
  375. for cpu in cpus
  376. }),
  377. target_os = select({
  378. "@platforms//os:{}".format(os): os
  379. for os in platforms.keys()
  380. }),
  381. bins = ":{}_base_files".format(name),
  382. tags = tags,
  383. )
  384. cc_toolchain(
  385. name = "{}_runtimes_cc_toolchain".format(name),
  386. all_files = ":{}_runtimes_compile_files".format(name),
  387. ar_files = ":{}_base_files".format(name),
  388. as_files = ":{}_runtimes_compile_files".format(name),
  389. compiler_files = ":{}_runtimes_compile_files".format(name),
  390. dwp_files = ":{}_base_files".format(name),
  391. linker_files = ":{}_base_files".format(name),
  392. objcopy_files = ":{}_base_files".format(name),
  393. strip_files = ":{}_base_files".format(name),
  394. toolchain_config = ":{}_runtimes_toolchain_config".format(name),
  395. toolchain_identifier = select({
  396. ":is_{}_{}".format(os, cpu): "{}_{}_{}_runtimes_toolchain".format(name, os, cpu)
  397. for os, cpus in platforms.items()
  398. for cpu in cpus
  399. }),
  400. tags = tags,
  401. )
  402. native.toolchain(
  403. name = "{}_runtimes_toolchain".format(name),
  404. target_settings = [
  405. ":is_bootstrap_stage_{}".format(build_stage),
  406. ":is_runtimes_build",
  407. ],
  408. use_target_platform_constraints = True,
  409. toolchain = ":{}_runtimes_cc_toolchain".format(name),
  410. toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  411. tags = tags,
  412. )
  413. # Now that we have a toolchain for building runtimes, actually do the build
  414. # here using the runtimes config provided to us. This is important to do
  415. # here because we need each runtimes build for a particular bootstrapping
  416. # stage of the toolchain to be distinct.
  417. carbon_runtimes_build(
  418. name = "{}_runtimes_build".format(name),
  419. config = runtimes_cfg,
  420. clang_hdrs = [":{}_clang_hdrs".format(name)],
  421. tags = tags,
  422. )
  423. # Wrap the built runtimes for this stage in a filegroup that ensures they
  424. # are built at this stage, but with the runtimes build enabled. This will
  425. # select the runtimes build toolchain above that doesn't yet provide any
  426. # runtimes, avoiding a cycle when the main toolchain below depends on these
  427. # runtimes.
  428. filegroup_with_stage(
  429. name = "{}_runtimes".format(name),
  430. enable_runtimes_build = True,
  431. srcs = ["{}_runtimes_build".format(name)],
  432. stage = build_stage,
  433. tags = tags,
  434. )
  435. # Now we can build the main toolchain configuration, filegroups including
  436. # the on-demand built runtimes, and the final tolochain itself.
  437. carbon_cc_toolchain_config(
  438. name = "{}_toolchain_config".format(name),
  439. identifier_prefix = name,
  440. target_cpu = select({
  441. # Note that we need to select on both OS and CPU so that we end up
  442. # spelling the CPU in the correct OS-specific ways.
  443. ":is_{}_{}".format(os, cpu): cpu
  444. for os, cpus in platforms.items()
  445. for cpu in cpus
  446. }),
  447. target_os = select({
  448. "@platforms//os:{}".format(os): os
  449. for os in platforms.keys()
  450. }),
  451. runtimes = ":{}_runtimes".format(name),
  452. bins = ":{}_base_files".format(name),
  453. tags = tags,
  454. )
  455. native.filegroup(
  456. name = "{}_linker_files".format(name),
  457. srcs = [
  458. ":{}_base_files".format(name),
  459. ":{}_runtimes".format(name),
  460. ],
  461. tags = tags,
  462. )
  463. native.filegroup(
  464. name = "{}_all_files".format(name),
  465. srcs = [
  466. ":{}_compile_files".format(name),
  467. ":{}_linker_files".format(name),
  468. ],
  469. tags = tags,
  470. )
  471. cc_toolchain(
  472. name = "{}_cc_toolchain".format(name),
  473. all_files = ":{}_all_files".format(name),
  474. ar_files = ":" + name + "_base_files",
  475. as_files = ":" + name + "_compile_files",
  476. compiler_files = ":" + name + "_compile_files",
  477. dwp_files = ":" + name + "_linker_files",
  478. linker_files = ":" + name + "_linker_files",
  479. objcopy_files = ":" + name + "_base_files",
  480. strip_files = ":" + name + "_base_files",
  481. toolchain_config = ":" + name + "_toolchain_config",
  482. toolchain_identifier = select({
  483. ":is_{}_{}".format(os, cpu): "{}_{}_{}_toolchain".format(name, os, cpu)
  484. for os, cpus in platforms.items()
  485. for cpu in cpus
  486. }),
  487. tags = tags,
  488. )
  489. native.toolchain(
  490. name = name + "_toolchain",
  491. target_settings = [":is_bootstrap_stage_{}".format(build_stage), ":not_runtimes_build"],
  492. use_target_platform_constraints = True,
  493. toolchain = ":" + name + "_cc_toolchain",
  494. toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  495. tags = tags,
  496. )