carbon_cc_toolchain_config.bzl 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
  6. load(
  7. "@rules_cc//cc:cc_toolchain_config_lib.bzl",
  8. "action_config",
  9. "flag_group",
  10. "flag_set",
  11. "tool",
  12. )
  13. load(
  14. "@rules_cc//cc:defs.bzl",
  15. "CcToolchainConfigInfo",
  16. "cc_toolchain",
  17. )
  18. load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
  19. load(
  20. "carbon_clang_variables.bzl",
  21. "clang_include_dirs",
  22. "clang_resource_dir",
  23. "clang_sysroot",
  24. )
  25. load(
  26. "cc_toolchain_actions.bzl",
  27. "all_c_compile_actions",
  28. "all_cpp_compile_actions",
  29. "all_link_actions",
  30. )
  31. load("cc_toolchain_carbon_project_features.bzl", "carbon_project_features")
  32. load("cc_toolchain_features.bzl", "clang_cc_toolchain_features")
  33. def _make_action_configs(tools, runtimes_path = None):
  34. runtimes_flag = "--no-build-runtimes"
  35. if runtimes_path:
  36. runtimes_flag = "--prebuilt-runtimes={0}".format(runtimes_path)
  37. return [
  38. action_config(
  39. action_name = name,
  40. enabled = True,
  41. tools = [tools.clang],
  42. )
  43. for name in all_c_compile_actions
  44. ] + [
  45. action_config(
  46. action_name = name,
  47. enabled = True,
  48. tools = [tools.clangpp],
  49. )
  50. for name in all_cpp_compile_actions
  51. ] + [
  52. action_config(
  53. action_name = name,
  54. enabled = True,
  55. tools = [tools.carbon_busybox],
  56. flag_sets = [flag_set(flag_groups = [flag_group(flags = [
  57. runtimes_flag,
  58. "link",
  59. # We want to allow Bazel to intermingle linked object files and
  60. # Clang-spelled link flags. The first `--` starts the list of
  61. # initial object files by ending flags to the `link` subcommand,
  62. # and the second `--` switches to Clang-spelled flags.
  63. "--",
  64. "--",
  65. ])])],
  66. )
  67. for name in all_link_actions
  68. ] + [
  69. action_config(
  70. action_name = name,
  71. enabled = True,
  72. tools = [tools.llvm_ar],
  73. )
  74. for name in [ACTION_NAMES.cpp_link_static_library]
  75. ] + [
  76. action_config(
  77. action_name = name,
  78. enabled = True,
  79. tools = [tools.llvm_strip],
  80. )
  81. for name in [ACTION_NAMES.strip]
  82. ]
  83. def _compute_clang_system_include_dirs():
  84. system_include_dirs_start_index = None
  85. for index, dir in enumerate(clang_include_dirs):
  86. # Skip over the include search directories until we find the resource
  87. # directory. The system include directories are everything after that.
  88. if dir.startswith(clang_resource_dir):
  89. system_include_dirs_start_index = index + 1
  90. break
  91. if not system_include_dirs_start_index:
  92. fail("Could not find the resource directory in the clang include " +
  93. "directories: {}".format(clang_include_dirs))
  94. return clang_include_dirs[system_include_dirs_start_index:]
  95. def _carbon_cc_toolchain_config_impl(ctx):
  96. tools = struct(
  97. carbon_busybox = tool(path = "carbon-busybox"),
  98. clang = tool(path = "llvm/bin/clang"),
  99. clangpp = tool(path = "llvm/bin/clang++"),
  100. llvm_ar = tool(path = "llvm/bin/llvm-ar"),
  101. llvm_strip = tool(path = "llvm/bin/llvm-strip"),
  102. )
  103. if ctx.attr.bins:
  104. carbon_busybox = None
  105. clang = None
  106. clangpp = None
  107. llvm_ar = None
  108. llvm_strip = None
  109. for f in ctx.files.bins:
  110. if f.basename == "carbon-busybox":
  111. carbon_busybox = f
  112. elif f.basename == "clang":
  113. clang = f
  114. elif f.basename == "clang++":
  115. clangpp = f
  116. elif f.basename == "llvm-ar":
  117. llvm_ar = f
  118. elif f.basename == "llvm-strip":
  119. llvm_strip = f
  120. tools = struct(
  121. carbon_busybox = tool(tool = carbon_busybox),
  122. clang = tool(tool = clang),
  123. clangpp = tool(tool = clangpp),
  124. llvm_ar = tool(tool = llvm_ar),
  125. llvm_strip = tool(tool = llvm_strip),
  126. )
  127. # Only use a sysroot if a non-trivial one is set in Carbon's config.
  128. builtin_sysroot = None
  129. sysroot_include_search = []
  130. if clang_sysroot != "None" and clang_sysroot != "/":
  131. builtin_sysroot = clang_sysroot
  132. sysroot_include_search = ["%sysroot%/usr/include"]
  133. runtimes_path = None
  134. if ctx.attr.runtimes:
  135. for f in ctx.files.runtimes:
  136. if f.basename == "runtimes_root":
  137. runtimes_path = f.dirname
  138. break
  139. if not runtimes_path:
  140. fail("Unable to compute the runtimes path for: {0}".format(
  141. ctx.attr.runtimes,
  142. ))
  143. identifier = "{0}_toolchain_{1}_{2}".format(
  144. ctx.attr.identifier_prefix,
  145. ctx.attr.target_cpu,
  146. ctx.attr.target_os,
  147. )
  148. return cc_common.create_cc_toolchain_config_info(
  149. ctx = ctx,
  150. features = clang_cc_toolchain_features(
  151. target_os = ctx.attr.target_os,
  152. target_cpu = ctx.attr.target_cpu,
  153. # TODO: This should be configured externally rather than here so
  154. # that the install Carbon toolchain doesn't automatically include
  155. # Carbon-project-specific flags. However, that is especially awkward
  156. # to do until we fully migrate to a rules-based toolchain, and the
  157. # project-specific flags are largely harmless at the moment. We also
  158. # omit a meaningful cache key as when using the Carbon toolchain we
  159. # don't need it as it is a hermetic part of Bazel.
  160. project_features = carbon_project_features(cache_key = ""),
  161. ),
  162. action_configs = _make_action_configs(tools, runtimes_path),
  163. cxx_builtin_include_directories = [
  164. "runtimes/libunwind/include",
  165. "runtimes/libcxx/include",
  166. "runtimes/libcxxabi/include",
  167. clang_resource_dir + "/include",
  168. "runtimes/clang_resource_dir/include",
  169. ] + _compute_clang_system_include_dirs() + sysroot_include_search,
  170. builtin_sysroot = builtin_sysroot,
  171. # This configuration only supports local non-cross builds so derive
  172. # everything from the target CPU selected.
  173. toolchain_identifier = identifier,
  174. # This is used to expose a "flag" that `config_setting` rules can use to
  175. # determine if the compiler is Clang.
  176. compiler = "clang",
  177. )
  178. carbon_cc_toolchain_config = rule(
  179. implementation = _carbon_cc_toolchain_config_impl,
  180. attrs = {
  181. "bins": attr.label(mandatory = False),
  182. "identifier_prefix": attr.string(mandatory = True),
  183. "runtimes": attr.label(mandatory = False),
  184. "target_cpu": attr.string(mandatory = True),
  185. "target_os": attr.string(mandatory = True),
  186. },
  187. provides = [CcToolchainConfigInfo],
  188. )
  189. def _set_platform_transition_impl(settings, attr):
  190. original_platforms = settings["//:original_platforms"]
  191. # If the requested platform is the special value of the setting where we
  192. # store the original platforms on an initial transition, set the platform to
  193. # the saved list and clear it. Otherwise, we will set the platform to the
  194. # requested one.
  195. if not attr.platform:
  196. return {
  197. "//:original_platforms": [],
  198. "//command_line_option:platforms": original_platforms,
  199. }
  200. if original_platforms:
  201. # If there is already a saved original platforms list, preserve it.
  202. original_platforms = [str(label) for label in original_platforms]
  203. else:
  204. # If there is no saved original platforms list, save the current one.
  205. current_platforms = settings["//command_line_option:platforms"]
  206. original_platforms = [str(label) for label in current_platforms]
  207. return {
  208. "//:original_platforms": original_platforms,
  209. "//command_line_option:platforms": [str(attr.platform)],
  210. }
  211. set_platform_transition = transition(
  212. inputs = [
  213. "//command_line_option:platforms",
  214. "//:original_platforms",
  215. ],
  216. outputs = [
  217. "//command_line_option:platforms",
  218. "//:original_platforms",
  219. ],
  220. implementation = _set_platform_transition_impl,
  221. )
  222. def _set_platform_filegroup_impl(ctx):
  223. return [DefaultInfo(files = depset(ctx.files.srcs))]
  224. set_platform_filegroup = rule(
  225. implementation = _set_platform_filegroup_impl,
  226. attrs = {
  227. # The platform to use when building the runtimes.
  228. "platform": attr.label(mandatory = False),
  229. # Mark that our dependencies are built through a transition.
  230. "srcs": attr.label_list(mandatory = True, cfg = set_platform_transition),
  231. # Enable transitions in this rule.
  232. "_allowlist_function_transition": attr.label(
  233. default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
  234. ),
  235. },
  236. )
  237. def carbon_cc_toolchain_suite(
  238. name,
  239. all_hdrs,
  240. base_files,
  241. clang_hdrs,
  242. platforms,
  243. runtimes,
  244. build_stage = None,
  245. base_stage = None,
  246. tags = []):
  247. """Create a toolchain suite that uses the local Clang/LLVM install.
  248. Args:
  249. name:
  250. The name of the toolchain suite to produce, used as the base of the
  251. names of each component of the toolchain suite.
  252. all_hdrs: A list of header files to include in the toolchain.
  253. base_files: A list of files to include in the toolchain.
  254. build_stage: The stage to use for the build files.
  255. base_stage: The stage to use for the base files.
  256. clang_hdrs: A list of header files to include in the toolchain.
  257. platforms: An array of (os, cpu) pairs to support in the toolchain.
  258. runtimes: A list of runtimes to include in the toolchain.
  259. tags: Tags to apply to the toolchain.
  260. """
  261. def _platform_name(os, cpu, name_suffix = ""):
  262. return "{}{}_{}_{}_platform".format(name, name_suffix, os, cpu)
  263. # Define platforms for each supported OS/CPU pair.
  264. for os, cpus in platforms.items():
  265. for cpu in cpus:
  266. constraint_values = [
  267. "@platforms//os:" + os,
  268. "@platforms//cpu:" + cpu,
  269. ]
  270. if base_stage:
  271. native.platform(
  272. name = _platform_name(os, cpu, "_base"),
  273. constraint_values = constraint_values + [base_stage],
  274. )
  275. if build_stage:
  276. constraint_values.append(build_stage)
  277. native.platform(
  278. name = _platform_name(os, cpu),
  279. constraint_values = constraint_values,
  280. )
  281. native.platform(
  282. name = _platform_name(os, cpu, "_runtimes"),
  283. constraint_values = constraint_values + [":is_runtimes_build"],
  284. )
  285. base_platform_select = None
  286. if base_stage:
  287. base_platform_select = select({
  288. ":is_{}_{}".format(os, cpu): ":" + _platform_name(os, cpu, "_base")
  289. for os, cpus in platforms.items()
  290. for cpu in cpus
  291. })
  292. runtimes_platform_select = select({
  293. ":is_{}_{}".format(os, cpu): ":" + _platform_name(os, cpu, "_runtimes")
  294. for os, cpus in platforms.items()
  295. for cpu in cpus
  296. })
  297. set_platform_filegroup(
  298. name = name + "_base_files",
  299. srcs = base_files,
  300. platform = base_platform_select,
  301. tags = tags,
  302. )
  303. set_platform_filegroup(
  304. name = name + "_runtimes_compile_files",
  305. srcs = [":" + name + "_base_files"] + clang_hdrs,
  306. platform = base_platform_select,
  307. tags = tags,
  308. )
  309. set_platform_filegroup(
  310. name = name + "_compile_files",
  311. srcs = [":" + name + "_base_files"] + all_hdrs,
  312. platform = base_platform_select,
  313. tags = tags,
  314. )
  315. carbon_cc_toolchain_config(
  316. name = name + "_runtimes_toolchain_config",
  317. identifier_prefix = name + "_runtimes",
  318. target_cpu = select({
  319. # Note that we need to select on both OS and CPU so that we end up
  320. # spelling the CPU in the correct OS-specific ways.
  321. ":is_{}_{}".format(os, cpu): cpu
  322. for os, cpus in platforms.items()
  323. for cpu in cpus
  324. }),
  325. target_os = select({
  326. "@platforms//os:" + os: os
  327. for os in platforms.keys()
  328. }),
  329. bins = ":" + name + "_base_files",
  330. tags = tags,
  331. )
  332. cc_toolchain(
  333. name = name + "_runtimes_cc_toolchain",
  334. all_files = ":" + name + "_runtimes_compile_files",
  335. ar_files = ":" + name + "_base_files",
  336. as_files = ":" + name + "_runtimes_compile_files",
  337. compiler_files = ":" + name + "_runtimes_compile_files",
  338. dwp_files = ":" + name + "_base_files",
  339. linker_files = ":" + name + "_base_files",
  340. objcopy_files = ":" + name + "_base_files",
  341. strip_files = ":" + name + "_base_files",
  342. toolchain_config = ":" + name + "_runtimes_toolchain_config",
  343. toolchain_identifier = select({
  344. ":is_{}_{}".format(os, cpu): _platform_name(os, cpu, "_runtimes")
  345. for os, cpus in platforms.items()
  346. for cpu in cpus
  347. }),
  348. tags = tags,
  349. )
  350. native.toolchain(
  351. name = name + "_runtimes_toolchain",
  352. target_compatible_with = [":is_runtimes_build"],
  353. toolchain = ":" + name + "_runtimes_cc_toolchain",
  354. toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  355. tags = tags,
  356. )
  357. set_platform_filegroup(
  358. name = name + "_runtimes",
  359. srcs = [runtimes],
  360. platform = runtimes_platform_select,
  361. tags = tags,
  362. )
  363. carbon_cc_toolchain_config(
  364. name = name + "_toolchain_config",
  365. identifier_prefix = name,
  366. target_cpu = select({
  367. # Note that we need to select on both OS and CPU so that we end up
  368. # spelling the CPU in the correct OS-specific ways.
  369. ":is_{}_{}".format(os, cpu): cpu
  370. for os, cpus in platforms.items()
  371. for cpu in cpus
  372. }),
  373. target_os = select({
  374. "@platforms//os:" + os: os
  375. for os in platforms.keys()
  376. }),
  377. runtimes = ":" + name + "_runtimes",
  378. bins = ":" + name + "_base_files",
  379. tags = tags,
  380. )
  381. native.filegroup(
  382. name = name + "_linker_files",
  383. srcs = [
  384. ":" + name + "_base_files",
  385. ":" + name + "_runtimes",
  386. ],
  387. tags = tags,
  388. )
  389. native.filegroup(
  390. name = name + "_all_files",
  391. srcs = [
  392. ":" + name + "_compile_files",
  393. ":" + name + "_linker_files",
  394. ],
  395. tags = tags,
  396. )
  397. cc_toolchain(
  398. name = name + "_cc_toolchain",
  399. all_files = ":" + name + "_all_files",
  400. ar_files = ":" + name + "_base_files",
  401. as_files = ":" + name + "_compile_files",
  402. compiler_files = ":" + name + "_compile_files",
  403. dwp_files = ":" + name + "_linker_files",
  404. linker_files = ":" + name + "_linker_files",
  405. objcopy_files = ":" + name + "_base_files",
  406. strip_files = ":" + name + "_base_files",
  407. toolchain_config = ":" + name + "_toolchain_config",
  408. toolchain_identifier = select({
  409. ":is_{}_{}".format(os, cpu): _platform_name(os, cpu)
  410. for os, cpus in platforms.items()
  411. for cpu in cpus
  412. }),
  413. tags = tags,
  414. )
  415. native.toolchain(
  416. name = name + "_toolchain",
  417. target_compatible_with = [build_stage] if build_stage else [],
  418. toolchain = ":" + name + "_cc_toolchain",
  419. toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
  420. tags = tags,
  421. )