prebuilt_runtimes.bzl 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. """Rules for pre-building a runtimes tree."""
  5. def _prebuilt_clang_runtimes_impl(ctx):
  6. runtimes_builder = ctx.executable.internal_exec_runtimes_builder
  7. if runtimes_builder == None:
  8. runtimes_builder = ctx.executable.internal_target_runtimes_builder
  9. # Declare the output directories created when building the runtimes. We
  10. # can't declare just the top-level directory as we need to overlay an
  11. # `include` tree into the `clang_resource_dir`, but we try to use
  12. # directories to minimize writing out the complete list of files in each
  13. # runtime.
  14. output_dirs = [
  15. ctx.actions.declare_directory(ctx.attr.runtimes_path + "/clang_resource_dir/lib"),
  16. ctx.actions.declare_directory(ctx.attr.runtimes_path + "/libunwind/lib"),
  17. ctx.actions.declare_directory(ctx.attr.runtimes_path + "/libcxx/lib"),
  18. ]
  19. # Build up the arguments to use with the runtimes build command.
  20. args = ctx.actions.args()
  21. args.add("--force")
  22. # Provide an explicit target if requested.
  23. if ctx.attr.target:
  24. args.add(ctx.attr.target, format = "--target=%s")
  25. # Compute the path to the root of the runtimes tree we're trying to build.
  26. # We work backwards from the declared `clang_resource_dir/lib` entry.
  27. root_path_arg = output_dirs[0].path
  28. if not root_path_arg.endswith("/clang_resource_dir/lib"):
  29. fail("Unexpected path structure: " + root_path_arg)
  30. root_path_arg = root_path_arg[:-len("/clang_resource_dir/lib")]
  31. args.add(root_path_arg)
  32. # Run the runtimes building tool with the arguments.
  33. ctx.actions.run(
  34. outputs = output_dirs,
  35. executable = runtimes_builder,
  36. arguments = [args],
  37. mnemonic = "BuildRuntimes",
  38. progress_message = "Building runtimes target %{label}",
  39. # Building runtimes will use all the available CPUs. We can't directly
  40. # model this in Bazel, so we use a somewhat arbitrary but large number
  41. # of cores here to indicate that this is a _very_ expensive action. This
  42. # should minimize the risk of other actions running in parallel in
  43. # constrained environments and timing out.
  44. execution_requirements = {"cpu:64": ""},
  45. )
  46. # Now overlay Clang's builtin headers to the `clang_resource_dir` as we
  47. # can't have separate library search and header search paths for that
  48. # specific runtime, and we want Bazel to be aware of the origin of these
  49. # headers rather than creating copies when building the runtimes.
  50. target_include_root = ctx.attr.runtimes_path + "/clang_resource_dir/include"
  51. # We need to compute the relative path of each header file within the
  52. # resource directory's `include` directory. We do this by stripping off
  53. # their prefix.
  54. #
  55. # TODO: It would be nice to find a cleaner way to do this that avoids
  56. # hard-coding both the repository name's spelling and the rule layout.
  57. headers_prefix = "/external/+llvm_project+llvm-project/clang/staging/include/"
  58. # Walk all the headers and symlink them into the runtimes tree below the
  59. # target root. Collect the results for use in establishing dependencies.
  60. input_headers = ctx.attr._builtin_headers.files.to_list()
  61. output_headers = []
  62. for f in input_headers:
  63. # Ensure the file actually lives under the expected path
  64. path = f.path[len(f.root.path):]
  65. if not path.startswith(headers_prefix):
  66. fail("Header file '{}' is not under the expected prefix '{}'".format(
  67. path,
  68. headers_prefix,
  69. ))
  70. path = path[len(headers_prefix):]
  71. # Declare the output file preserving the relative structure. Bazel
  72. # automatically creates any intermediate directories (e.g. `sanitizer/`)
  73. out_file = ctx.actions.declare_file("{}/{}".format(target_include_root, path))
  74. ctx.actions.symlink(output = out_file, target_file = f)
  75. output_headers.append(out_file)
  76. return [
  77. DefaultInfo(
  78. # Build the actual runtimes tree when the target is built.
  79. files = depset(output_dirs + output_headers),
  80. # Make the resulting tree available in the Bazel runfiles tree.
  81. runfiles = ctx.runfiles(files = output_dirs + output_headers),
  82. ),
  83. ]
  84. # The rule implementing the Clang runtimes build. This is kept in its own rule
  85. # and uses separate tools so that it can isolate its dependencies and thus how
  86. # often it has to be re-built as much as possible.
  87. _prebuilt_clang_runtimes_internal = rule(
  88. implementation = _prebuilt_clang_runtimes_impl,
  89. attrs = {
  90. # These are technically builtin attributes, but we provide them via a
  91. # macro in order to use select to configure them based on a flag.
  92. "internal_exec_runtimes_builder": attr.label(
  93. allow_single_file = True,
  94. executable = True,
  95. cfg = "exec",
  96. ),
  97. "internal_target_runtimes_builder": attr.label(
  98. allow_single_file = True,
  99. executable = True,
  100. cfg = "target",
  101. ),
  102. "runtimes_path": attr.string(
  103. doc = "The path for the root of the runtimes",
  104. mandatory = True,
  105. ),
  106. "target": attr.string(
  107. doc = "Optional target for the built runtimes",
  108. ),
  109. "_builtin_headers": attr.label(
  110. default = Label("@llvm-project//clang:builtin_headers_gen"),
  111. ),
  112. },
  113. )
  114. def prebuilt_runtimes(name, target = None, tags = []):
  115. """Build a a runtimes tree.
  116. The runtimes will be built into the directory `name + "_tree"`, and
  117. collected into a filegroup with the provided name for use in rules accessing
  118. these runtimes.
  119. Args:
  120. name: The name of the runtimes build target.
  121. target: Optional `--target` flag value to use when building the runtimes.
  122. tags: Tags to apply to the rule.
  123. """
  124. runtimes_path = name + "_tree"
  125. _prebuilt_clang_runtimes_internal(
  126. name = name + "_clang",
  127. runtimes_path = runtimes_path,
  128. target = target,
  129. tags = tags,
  130. # Synthesize mirrored `select`-filled attributes here so that they can
  131. # have different internal properties (that can't be `select`-ed) and we
  132. # can select between the attributes instead.
  133. internal_exec_runtimes_builder = select({
  134. "//toolchain/driver:use_target_config_runtimes_builder_config": None,
  135. "//conditions:default": "//toolchain/driver:bazel_build_clang_runtimes",
  136. }),
  137. internal_target_runtimes_builder = select({
  138. "//toolchain/driver:use_target_config_runtimes_builder_config": "//toolchain/driver:bazel_build_clang_runtimes",
  139. "//conditions:default": None,
  140. }),
  141. )
  142. # TODO: Add building of the Carbon runtimes here using a parallel rule to
  143. # the above, but adjusted to use the Carbon driver itself as there will be
  144. # no dependency reduction to be gained with a dedicated tool. It should
  145. # reuse `runtimes_path` so that we get a unified runtimes tree for
  146. # downstream use.
  147. # Assemble the various runtimes into a single filegroup for easy
  148. # dependencies.
  149. native.filegroup(
  150. name = name,
  151. srcs = [
  152. ":" + name + "_clang",
  153. ],
  154. tags = tags,
  155. )