carbon_runtimes.bzl 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 for building a Carbon runtimes tree.
  5. TODO: Currently, this produces a complete, static Carbon runtimes tree that
  6. mirrors the exact style of runtimes tree the Carbon toolchain would build on its
  7. own. However, it would be preferable to preserve the builtins, libc++, and
  8. libunwind `cc_library` rules as "normal" library rules (if behind a transition)
  9. and automatically depend on them. This would allow things like LTO and such to
  10. include these. However, this requires support in `@rules_cc` for this kind of
  11. dependency to be added.
  12. """
  13. load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
  14. load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
  15. def _build_crt_file(ctx, cc_toolchain, feature_configuration, crt_file):
  16. _, compilation_outputs = cc_common.compile(
  17. name = ctx.label.name + ".compile_" + crt_file.basename,
  18. actions = ctx.actions,
  19. feature_configuration = feature_configuration,
  20. cc_toolchain = cc_toolchain,
  21. srcs = [crt_file],
  22. user_compile_flags = ctx.attr.crt_copts,
  23. )
  24. # Extract the PIC object file and make sure we built one.
  25. obj = compilation_outputs.pic_objects[0]
  26. if not obj:
  27. fail("The toolchain failed to produce a PIC object file. Ensure your " +
  28. "toolchain supports PIC.")
  29. return obj
  30. def _removeprefix_or_fail(s, prefix):
  31. new_s = s.removeprefix(prefix)
  32. if new_s == s:
  33. fail("Unable to remove prefix '{0}' from '{1}'".format(prefix, s))
  34. return new_s
  35. def _carbon_runtimes_impl(ctx):
  36. outputs = []
  37. prefix = ctx.attr.name
  38. # Create a marker file in the runtimes root first. We'll use this to locate
  39. # the runtimes for the toolchain.
  40. root_out = ctx.actions.declare_file("{0}/runtimes_root".format(prefix))
  41. ctx.actions.write(output = root_out, content = "")
  42. outputs.append(root_out)
  43. # Setup the C++ toolchain and configuration. We also force the `pic` feature
  44. # to be enabled for these actions as we always want PIC generated code --
  45. # this avoids the need to build two versions of the runtimes and doesn't
  46. # create problems with modern code generation when linking statically. This
  47. # also simplifies extracting the outputs as we only need to look at
  48. # `pic_objects`.
  49. cc_toolchain = find_cpp_toolchain(ctx)
  50. feature_configuration = cc_common.configure_features(
  51. ctx = ctx,
  52. cc_toolchain = cc_toolchain,
  53. requested_features = ctx.features + ["pic"],
  54. unsupported_features = ctx.disabled_features,
  55. )
  56. builtins_lib_path = "clang_resource_dir/lib"
  57. builtins_archive_name = "libclang_rt.builtins.a"
  58. if ctx.attr.target_triple != "":
  59. builtins_lib_path = "clang_resource_dir/lib/{0}".format(ctx.attr.target_triple)
  60. elif ctx.attr.darwin_os_suffix:
  61. builtins_lib_path = "clang_resource_dir/lib/darwin"
  62. builtins_archive_name = "libclang_rt.{0}.a".format(ctx.attr.darwin_os_suffix)
  63. for filename, src in [
  64. ("crtbegin", ctx.files.crtbegin_src),
  65. ("crtend", ctx.files.crtend_src),
  66. ]:
  67. if not src:
  68. continue
  69. src = src[0]
  70. crt_obj = _build_crt_file(ctx, cc_toolchain, feature_configuration, src)
  71. crt_out = ctx.actions.declare_file("{0}/{1}/clang_rt.{2}.o".format(
  72. prefix,
  73. builtins_lib_path,
  74. filename,
  75. ))
  76. ctx.actions.symlink(output = crt_out, target_file = crt_obj)
  77. outputs.append(crt_out)
  78. for runtime_dir, archive_name, archive in [
  79. (builtins_lib_path, builtins_archive_name, ctx.files.builtins_archive[0]),
  80. ("libcxx/lib", "libc++.a", ctx.files.libcxx_archive[0]),
  81. ("libunwind/lib", "libunwind.a", ctx.files.libunwind_archive[0]),
  82. ]:
  83. runtime_out = ctx.actions.declare_file("{0}/{1}/{2}".format(
  84. prefix,
  85. runtime_dir,
  86. archive_name,
  87. ))
  88. ctx.actions.symlink(output = runtime_out, target_file = archive)
  89. outputs.append(runtime_out)
  90. for hdr in ctx.files.clang_hdrs:
  91. # Incrementally remove prefixes of the paths to find the `include`
  92. # directory we want to symlink into the output tree.
  93. rel_path = hdr.path
  94. if hdr.root.path != "":
  95. rel_path = _removeprefix_or_fail(rel_path, hdr.root.path + "/")
  96. if hdr.owner.workspace_root != "":
  97. rel_path = _removeprefix_or_fail(rel_path, hdr.owner.workspace_root + "/")
  98. if hdr.owner.package != "":
  99. rel_path = _removeprefix_or_fail(rel_path, hdr.owner.package + "/")
  100. rel_path = _removeprefix_or_fail(rel_path, ctx.attr.clang_hdrs_prefix)
  101. out_hdr = ctx.actions.declare_file(
  102. "{0}/clang_resource_dir/include/{1}".format(prefix, rel_path),
  103. )
  104. ctx.actions.symlink(output = out_hdr, target_file = hdr)
  105. outputs.append(out_hdr)
  106. return [DefaultInfo(files = depset(outputs))]
  107. carbon_runtimes = rule(
  108. implementation = _carbon_runtimes_impl,
  109. attrs = {
  110. "builtins_archive": attr.label(mandatory = True, allow_files = [".a"]),
  111. "clang_hdrs": attr.label_list(mandatory = True, allow_files = True),
  112. "clang_hdrs_prefix": attr.string(default = "include/"),
  113. "crt_copts": attr.string_list(default = []),
  114. "crtbegin_src": attr.label(allow_files = [".c"]),
  115. "crtend_src": attr.label(allow_files = [".c"]),
  116. "darwin_os_suffix": attr.string(mandatory = False),
  117. "libcxx_archive": attr.label(mandatory = True, allow_files = [".a"]),
  118. "libunwind_archive": attr.label(mandatory = True, allow_files = [".a"]),
  119. "target_triple": attr.string(mandatory = False),
  120. "_cc_toolchain": attr.label(
  121. default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
  122. ),
  123. },
  124. toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
  125. fragments = ["cpp"],
  126. doc = """Builds a Carbon runtimes tree under the `name` directory.
  127. This rule works to replicate the behavior of the Carbon toolchain building a
  128. runtimes tree directly in Bazel to allow it to fully benefit from Bazel's
  129. orchestration, caching, and even remote execution.
  130. It produces a runtimes tree customized for the specific target platform,
  131. similar to what the Carbon toolchain does on its own when invoked outside of
  132. Bazel.
  133. The runtimes tree includes a complete Clang resource-dir, including CRT
  134. begin/end objects, and the builtins library. It also includes a built libc++
  135. and libunwind library. These are arranged in the standard Carbon runtimes
  136. layout so that they are correctly found by the toolchain.
  137. """,
  138. )