Brian Silverman | 7d89e28 | 2021-11-17 17:36:54 -0800 | [diff] [blame] | 1 | # Copyright 2018 The Bazel Authors. |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
| 15 | load( |
| 16 | "//toolchain/internal:common.bzl", |
| 17 | _arch = "arch", |
| 18 | _canonical_dir_path = "canonical_dir_path", |
| 19 | _check_os_arch_keys = "check_os_arch_keys", |
| 20 | _host_tool_features = "host_tool_features", |
| 21 | _host_tools = "host_tools", |
| 22 | _os = "os", |
| 23 | _os_arch_pair = "os_arch_pair", |
| 24 | _os_bzl = "os_bzl", |
| 25 | _pkg_path_from_label = "pkg_path_from_label", |
| 26 | _supported_targets = "SUPPORTED_TARGETS", |
| 27 | ) |
| 28 | load( |
| 29 | "//toolchain/internal:sysroot.bzl", |
| 30 | _default_sysroot_path = "default_sysroot_path", |
| 31 | _sysroot_path = "sysroot_path", |
| 32 | ) |
| 33 | |
| 34 | def _include_dirs_str(rctx, key): |
| 35 | dirs = rctx.attr.cxx_builtin_include_directories.get(key) |
| 36 | if not dirs: |
| 37 | return "" |
| 38 | return ("\n" + 12 * " ").join(["\"%s\"," % d for d in dirs]) |
| 39 | |
| 40 | def llvm_config_impl(rctx): |
| 41 | _check_os_arch_keys(rctx.attr.toolchain_roots) |
| 42 | _check_os_arch_keys(rctx.attr.sysroot) |
| 43 | _check_os_arch_keys(rctx.attr.cxx_builtin_include_directories) |
| 44 | |
| 45 | os = _os(rctx) |
| 46 | if os == "windows": |
| 47 | rctx.file("BUILD.bazel") |
| 48 | rctx.file("toolchains.bzl", """\ |
| 49 | def llvm_register_toolchains(): |
| 50 | pass |
| 51 | """) |
| 52 | return |
| 53 | arch = _arch(rctx) |
| 54 | |
| 55 | key = _os_arch_pair(os, arch) |
| 56 | toolchain_root = rctx.attr.toolchain_roots.get(key) |
| 57 | if not toolchain_root: |
| 58 | toolchain_root = rctx.attr.toolchain_roots.get("") |
| 59 | if not toolchain_root: |
| 60 | fail("LLVM toolchain root missing for ({}, {})", os, arch) |
| 61 | |
| 62 | # Check if the toolchain root is an absolute path. |
| 63 | use_absolute_paths = rctx.attr.absolute_paths |
| 64 | if toolchain_root[0] == "/" and (len(toolchain_root) == 1 or toolchain_root[1] != "/"): |
| 65 | use_absolute_paths = True |
| 66 | |
| 67 | if use_absolute_paths: |
| 68 | llvm_repo_label = Label(toolchain_root + ":BUILD.bazel") # Exact target does not matter. |
| 69 | llvm_repo_path = _canonical_dir_path(str(rctx.path(llvm_repo_label).dirname)) |
| 70 | config_repo_path = _canonical_dir_path(str(rctx.path(""))) |
| 71 | toolchain_path_prefix = llvm_repo_path |
| 72 | tools_path_prefix = llvm_repo_path |
| 73 | wrapper_bin_prefix = config_repo_path |
| 74 | else: |
| 75 | llvm_repo_path = _pkg_path_from_label(Label(toolchain_root + ":BUILD.bazel")) |
| 76 | config_repo_path = "external/%s/" % rctx.name |
| 77 | |
| 78 | # tools can only be defined in a subdirectory of config_repo_path, |
| 79 | # because their paths are relative to the package defining |
| 80 | # cc_toolchain, and cannot contain '..'. |
| 81 | # https://github.com/bazelbuild/bazel/issues/7746. To work around |
| 82 | # this, we symlink the llvm repo under the package so all tools (except |
| 83 | # clang) can be called with normalized relative paths. For clang |
| 84 | # however, using a path with symlinks interferes with the header file |
| 85 | # inclusion validation checks, because clang frontend will infer the |
| 86 | # InstalledDir to be the symlinked path, and will look for header files |
| 87 | # in the symlinked path, but that seems to fail the inclusion |
| 88 | # validation check. So we always use a cc_wrapper (which is called |
| 89 | # through a normalized relative path), and then call clang with the not |
| 90 | # symlinked path from the wrapper. |
| 91 | rctx.symlink("../../" + llvm_repo_path, "llvm") |
| 92 | toolchain_path_prefix = llvm_repo_path |
| 93 | tools_path_prefix = "llvm/" |
| 94 | wrapper_bin_prefix = "" |
| 95 | |
| 96 | default_sysroot_path = _default_sysroot_path(rctx, os) |
| 97 | |
| 98 | workspace_name = rctx.name |
| 99 | toolchain_info = struct( |
| 100 | os = os, |
| 101 | arch = arch, |
| 102 | toolchain_root = toolchain_root, |
| 103 | toolchain_path_prefix = toolchain_path_prefix, |
| 104 | tools_path_prefix = tools_path_prefix, |
| 105 | wrapper_bin_prefix = wrapper_bin_prefix, |
| 106 | additional_include_dirs_dict = rctx.attr.cxx_builtin_include_directories, |
| 107 | sysroot_dict = rctx.attr.sysroot, |
| 108 | default_sysroot_path = default_sysroot_path, |
| 109 | llvm_version = rctx.attr.llvm_version, |
| 110 | ) |
| 111 | host_tools_info = dict([ |
| 112 | pair |
| 113 | for (key, tool_path, features) in [ |
| 114 | # This is used for macOS hosts: |
| 115 | ("libtool", "/usr/bin/libtool", [_host_tool_features.SUPPORTS_ARG_FILE]), |
| 116 | # This is used with old (pre 7) LLVM versions: |
| 117 | ("strip", "/usr/bin/strip", []), |
| 118 | # This is used when lld doesn't support the target platform (i.e. |
| 119 | # Mach-O for macOS): |
| 120 | ("ld", "/usr/bin/ld", []), |
| 121 | ] |
| 122 | for pair in _host_tools.get_tool_info(rctx, tool_path, features, key).items() |
| 123 | ]) |
| 124 | cc_toolchains_str, toolchain_labels_str = _cc_toolchains_str( |
| 125 | workspace_name, |
| 126 | toolchain_info, |
| 127 | use_absolute_paths, |
| 128 | host_tools_info, |
| 129 | ) |
| 130 | |
| 131 | # Convenience macro to register all generated toolchains. |
| 132 | rctx.template( |
| 133 | "toolchains.bzl", |
| 134 | Label("//toolchain:toolchains.bzl.tpl"), |
| 135 | { |
| 136 | "%{toolchain_labels}": toolchain_labels_str, |
| 137 | }, |
| 138 | ) |
| 139 | |
| 140 | # BUILD file with all the generated toolchain definitions. |
| 141 | rctx.template( |
| 142 | "BUILD.bazel", |
| 143 | Label("//toolchain:BUILD.toolchain.tpl"), |
| 144 | { |
| 145 | "%{cc_toolchains}": cc_toolchains_str, |
| 146 | "%{cc_toolchain_config_bzl}": str(rctx.attr._cc_toolchain_config_bzl), |
| 147 | }, |
| 148 | ) |
| 149 | |
| 150 | # CC wrapper script; see comments near the definition of `wrapper_bin_prefix`. |
| 151 | if os == "darwin": |
| 152 | cc_wrapper_tpl = "//toolchain:osx_cc_wrapper.sh.tpl" |
| 153 | else: |
| 154 | cc_wrapper_tpl = "//toolchain:cc_wrapper.sh.tpl" |
| 155 | rctx.template( |
| 156 | "bin/cc_wrapper.sh", |
| 157 | Label(cc_wrapper_tpl), |
| 158 | { |
| 159 | "%{toolchain_path_prefix}": toolchain_path_prefix, |
| 160 | }, |
| 161 | ) |
| 162 | |
| 163 | # libtool wrapper; used if the host libtool doesn't support arg files: |
| 164 | rctx.template( |
| 165 | "bin/host_libtool_wrapper.sh", |
| 166 | Label("//toolchain:host_libtool_wrapper.sh.tpl"), |
| 167 | { |
| 168 | "%{libtool_path}": "/usr/bin/libtool", |
| 169 | }, |
| 170 | ) |
| 171 | |
| 172 | def _cc_toolchains_str( |
| 173 | workspace_name, |
| 174 | toolchain_info, |
| 175 | use_absolute_paths, |
| 176 | host_tools_info): |
| 177 | # Since all the toolchains rely on downloading the right LLVM toolchain for |
| 178 | # the host architecture, we don't need to explicitly specify |
| 179 | # `exec_compatible_with` attribute. If the host and execution platform are |
| 180 | # not the same, then host auto-detection based LLVM download does not work |
| 181 | # and the user has to explicitly specify the distribution of LLVM they |
| 182 | # want. |
| 183 | |
| 184 | # Note that for cross-compiling, the toolchain configuration will need |
| 185 | # appropriate sysroots. A recommended approach is to configure two |
| 186 | # `llvm_toolchain` repos, one without sysroots (for easy single platform |
| 187 | # builds) and register this one, and one with sysroots and provide |
| 188 | # `--extra_toolchains` flag when cross-compiling. |
| 189 | |
| 190 | cc_toolchains_str = "" |
| 191 | toolchain_names = [] |
| 192 | for (target_os, target_arch) in _supported_targets: |
| 193 | suffix = "{}-{}".format(target_arch, target_os) |
| 194 | cc_toolchain_str = _cc_toolchain_str( |
| 195 | suffix, |
| 196 | target_os, |
| 197 | target_arch, |
| 198 | toolchain_info, |
| 199 | use_absolute_paths, |
| 200 | host_tools_info, |
| 201 | ) |
| 202 | if cc_toolchain_str: |
| 203 | cc_toolchains_str = cc_toolchains_str + cc_toolchain_str |
| 204 | toolchain_name = "@{}//:cc-toolchain-{}".format(workspace_name, suffix) |
| 205 | toolchain_names.append(toolchain_name) |
| 206 | |
| 207 | sep = ",\n" + " " * 8 # 2 tabs with tabstop=4. |
| 208 | toolchain_labels_str = sep.join(["\"{}\"".format(d) for d in toolchain_names]) |
| 209 | return cc_toolchains_str, toolchain_labels_str |
| 210 | |
| 211 | def _cc_toolchain_str( |
| 212 | suffix, |
| 213 | target_os, |
| 214 | target_arch, |
| 215 | toolchain_info, |
| 216 | use_absolute_paths, |
| 217 | host_tools_info): |
| 218 | host_os = toolchain_info.os |
| 219 | host_arch = toolchain_info.arch |
| 220 | |
| 221 | host_os_bzl = _os_bzl(host_os) |
| 222 | target_os_bzl = _os_bzl(target_os) |
| 223 | |
| 224 | sysroot_path, sysroot = _sysroot_path( |
| 225 | toolchain_info.sysroot_dict, |
| 226 | target_os, |
| 227 | target_arch, |
| 228 | ) |
| 229 | if not sysroot_path: |
| 230 | if host_os == target_os and host_arch == target_arch: |
| 231 | # For darwin -> darwin, we can use the macOS SDK path. |
| 232 | sysroot_path = toolchain_info.default_sysroot_path |
| 233 | else: |
| 234 | # We are trying to cross-compile without a sysroot, let's bail. |
| 235 | # TODO: Are there situations where we can continue? |
| 236 | return "" |
| 237 | |
| 238 | extra_files_str = ", \":llvm\", \":wrapper-files\"" |
| 239 | |
| 240 | additional_include_dirs = toolchain_info.additional_include_dirs_dict.get(_os_arch_pair(target_os, target_arch)) |
| 241 | additional_include_dirs_str = "[]" |
| 242 | if additional_include_dirs: |
| 243 | additional_include_dirs_str = "[{}]".format( |
| 244 | ", ".join(["\"{}\"".format(d) for d in additional_include_dirs]), |
| 245 | ) |
| 246 | |
| 247 | sysroot_label_str = "\"%s\"" % str(sysroot) if sysroot else "" |
| 248 | |
| 249 | # `struct` isn't allowed in `BUILD` files so we JSON encode + decode to turn |
| 250 | # them into `dict`s. |
| 251 | host_tools_info = json.decode(json.encode(host_tools_info)) |
| 252 | |
| 253 | template = """ |
| 254 | # CC toolchain for cc-clang-{suffix}. |
| 255 | |
| 256 | cc_toolchain_config( |
| 257 | name = "local-{suffix}", |
| 258 | host_arch = "{host_arch}", |
| 259 | host_os = "{host_os}", |
| 260 | target_arch = "{target_arch}", |
| 261 | target_os = "{target_os}", |
| 262 | toolchain_path_prefix = "{toolchain_path_prefix}", |
| 263 | tools_path_prefix = "{tools_path_prefix}", |
| 264 | wrapper_bin_prefix = "{wrapper_bin_prefix}", |
| 265 | sysroot_path = "{sysroot_path}", |
| 266 | additional_include_dirs = {additional_include_dirs_str}, |
| 267 | llvm_version = "{llvm_version}", |
| 268 | host_tools_info = {host_tools_info}, |
| 269 | ) |
| 270 | |
| 271 | toolchain( |
| 272 | name = "cc-toolchain-{suffix}", |
| 273 | exec_compatible_with = [ |
| 274 | "@platforms//cpu:{host_arch}", |
| 275 | "@platforms//os:{host_os_bzl}", |
| 276 | ], |
| 277 | target_compatible_with = [ |
| 278 | "@platforms//cpu:{target_arch}", |
| 279 | "@platforms//os:{target_os_bzl}", |
| 280 | ], |
| 281 | toolchain = ":cc-clang-{suffix}", |
| 282 | toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", |
| 283 | ) |
| 284 | """ |
| 285 | |
| 286 | if use_absolute_paths: |
| 287 | template = template + """ |
| 288 | cc_toolchain( |
| 289 | name = "cc-clang-{suffix}", |
| 290 | all_files = ":empty", |
| 291 | compiler_files = ":empty", |
| 292 | dwp_files = ":empty", |
| 293 | linker_files = ":empty", |
| 294 | objcopy_files = ":empty", |
| 295 | strip_files = ":empty", |
| 296 | toolchain_config = "local-{suffix}", |
| 297 | ) |
| 298 | """ |
| 299 | else: |
| 300 | template = template + """ |
| 301 | filegroup( |
| 302 | name = "sysroot-components-{suffix}", |
| 303 | srcs = [{sysroot_label_str}], |
| 304 | ) |
| 305 | |
| 306 | filegroup( |
| 307 | name = "compiler-components-{suffix}", |
| 308 | srcs = [ |
| 309 | "{toolchain_root}:clang", |
| 310 | "{toolchain_root}:include", |
| 311 | ":sysroot-components-{suffix}", |
| 312 | ], |
| 313 | ) |
| 314 | |
| 315 | filegroup( |
| 316 | name = "linker-components-{suffix}", |
| 317 | srcs = [ |
| 318 | "{toolchain_root}:clang", |
| 319 | "{toolchain_root}:ld", |
| 320 | "{toolchain_root}:ar", |
| 321 | "{toolchain_root}:lib", |
| 322 | ":sysroot-components-{suffix}", |
| 323 | ], |
| 324 | ) |
| 325 | |
| 326 | filegroup( |
| 327 | name = "all-components-{suffix}", |
| 328 | srcs = [ |
| 329 | "{toolchain_root}:bin", |
| 330 | ":compiler-components-{suffix}", |
| 331 | ":linker-components-{suffix}", |
| 332 | ], |
| 333 | ) |
| 334 | |
| 335 | filegroup(name = "all-files-{suffix}", srcs = [":all-components-{suffix}"{extra_files_str}]) |
| 336 | filegroup(name = "archiver-files-{suffix}", srcs = ["{toolchain_root}:ar"{extra_files_str}]) |
| 337 | filegroup(name = "assembler-files-{suffix}", srcs = ["{toolchain_root}:as"{extra_files_str}]) |
| 338 | filegroup(name = "compiler-files-{suffix}", srcs = [":compiler-components-{suffix}"{extra_files_str}]) |
| 339 | filegroup(name = "dwp-files-{suffix}", srcs = ["{toolchain_root}:dwp"{extra_files_str}]) |
| 340 | filegroup(name = "linker-files-{suffix}", srcs = [":linker-components-{suffix}"{extra_files_str}]) |
| 341 | filegroup(name = "objcopy-files-{suffix}", srcs = ["{toolchain_root}:objcopy"{extra_files_str}]) |
| 342 | filegroup(name = "strip-files-{suffix}", srcs = ["{toolchain_root}:strip"{extra_files_str}]) |
| 343 | |
| 344 | cc_toolchain( |
| 345 | name = "cc-clang-{suffix}", |
| 346 | all_files = "all-files-{suffix}", |
| 347 | ar_files = "archiver-files-{suffix}", |
| 348 | as_files = "assembler-files-{suffix}", |
| 349 | compiler_files = "compiler-files-{suffix}", |
| 350 | dwp_files = "dwp-files-{suffix}", |
| 351 | linker_files = "linker-files-{suffix}", |
| 352 | objcopy_files = "objcopy-files-{suffix}", |
| 353 | strip_files = "strip-files-{suffix}", |
| 354 | toolchain_config = "local-{suffix}", |
| 355 | ) |
| 356 | """ |
| 357 | |
| 358 | return template.format( |
| 359 | suffix = suffix, |
| 360 | target_os = target_os, |
| 361 | target_arch = target_arch, |
| 362 | host_os = host_os, |
| 363 | host_arch = host_arch, |
| 364 | target_os_bzl = target_os_bzl, |
| 365 | host_os_bzl = host_os_bzl, |
| 366 | toolchain_root = toolchain_info.toolchain_root, |
| 367 | toolchain_path_prefix = toolchain_info.toolchain_path_prefix, |
| 368 | tools_path_prefix = toolchain_info.tools_path_prefix, |
| 369 | wrapper_bin_prefix = toolchain_info.wrapper_bin_prefix, |
| 370 | additional_include_dirs_str = additional_include_dirs_str, |
| 371 | sysroot_label_str = sysroot_label_str, |
| 372 | sysroot_path = sysroot_path, |
| 373 | llvm_version = toolchain_info.llvm_version, |
| 374 | extra_files_str = extra_files_str, |
| 375 | host_tools_info = host_tools_info, |
| 376 | ) |