| # Copyright 2018 The Bazel Authors. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| load( |
| "//toolchain/internal:common.bzl", |
| _arch = "arch", |
| _canonical_dir_path = "canonical_dir_path", |
| _check_os_arch_keys = "check_os_arch_keys", |
| _host_tool_features = "host_tool_features", |
| _host_tools = "host_tools", |
| _os = "os", |
| _os_arch_pair = "os_arch_pair", |
| _os_bzl = "os_bzl", |
| _pkg_path_from_label = "pkg_path_from_label", |
| _supported_targets = "SUPPORTED_TARGETS", |
| ) |
| load( |
| "//toolchain/internal:sysroot.bzl", |
| _default_sysroot_path = "default_sysroot_path", |
| _sysroot_path = "sysroot_path", |
| ) |
| |
| def _include_dirs_str(rctx, key): |
| dirs = rctx.attr.cxx_builtin_include_directories.get(key) |
| if not dirs: |
| return "" |
| return ("\n" + 12 * " ").join(["\"%s\"," % d for d in dirs]) |
| |
| def _is_absolute(path): |
| return path[0] == "/" and (len(path) == 1 or path[1] != "/") |
| |
| def llvm_config_impl(rctx): |
| _check_os_arch_keys(rctx.attr.toolchain_roots) |
| _check_os_arch_keys(rctx.attr.target_toolchain_roots) |
| _check_os_arch_keys(rctx.attr.sysroot) |
| _check_os_arch_keys(rctx.attr.cxx_builtin_include_directories) |
| _check_os_arch_keys(rctx.attr.standard_libraries) |
| |
| os = _os(rctx) |
| if os == "windows": |
| rctx.file("BUILD.bazel") |
| rctx.file("toolchains.bzl", """\ |
| def llvm_register_toolchains(): |
| pass |
| """) |
| return |
| arch = _arch(rctx) |
| |
| key = _os_arch_pair(os, arch) |
| toolchain_root = rctx.attr.toolchain_roots.get(key) |
| if not toolchain_root: |
| toolchain_root = rctx.attr.toolchain_roots.get("") |
| if not toolchain_root: |
| fail("LLVM toolchain root missing for ({}, {})", os, arch) |
| |
| # Check if the toolchain root is an absolute path. |
| use_absolute_paths = rctx.attr.absolute_paths |
| for target_toolchain_root in rctx.attr.target_toolchain_roots.values(): |
| if _is_absolute(toolchain_root) != _is_absolute(target_toolchain_root): |
| fail("Host and target toolchain roots must both be absolute or not") |
| if _is_absolute(toolchain_root): |
| use_absolute_paths = True |
| |
| target_llvm_repo_paths = {} |
| if use_absolute_paths: |
| llvm_repo_label = Label(toolchain_root + ":BUILD.bazel") # Exact target does not matter. |
| llvm_repo_path = _canonical_dir_path(str(rctx.path(llvm_repo_label).dirname)) |
| for a_key in rctx.attr.target_toolchain_roots: |
| target_llvm_repo_label = Label(rctx.attr.target_toolchain_roots[a_key] + ":BUILD.bazel") |
| target_llvm_repo_paths[a_key] = _canonical_dir_path(str(rctx.path(target_llvm_repo_label).dirname)) |
| config_repo_path = _canonical_dir_path(str(rctx.path(""))) |
| toolchain_path_prefix = llvm_repo_path |
| tools_path_prefix = llvm_repo_path |
| wrapper_bin_prefix = config_repo_path |
| else: |
| llvm_repo_path = _pkg_path_from_label(Label(toolchain_root + ":BUILD.bazel")) |
| for a_key in rctx.attr.target_toolchain_roots: |
| target_llvm_repo_paths[a_key] = _pkg_path_from_label(Label(rctx.attr.target_toolchain_roots[a_key] + ":BUILD.bazel")) |
| config_repo_path = "external/%s/" % rctx.name |
| |
| # tools can only be defined in a subdirectory of config_repo_path, |
| # because their paths are relative to the package defining |
| # cc_toolchain, and cannot contain '..'. |
| # https://github.com/bazelbuild/bazel/issues/7746. To work around |
| # this, we symlink the llvm repo under the package so all tools (except |
| # clang) can be called with normalized relative paths. For clang |
| # however, using a path with symlinks interferes with the header file |
| # inclusion validation checks, because clang frontend will infer the |
| # InstalledDir to be the symlinked path, and will look for header files |
| # in the symlinked path, but that seems to fail the inclusion |
| # validation check. So we always use a cc_wrapper (which is called |
| # through a normalized relative path), and then call clang with the not |
| # symlinked path from the wrapper. |
| rctx.symlink("../../" + llvm_repo_path, "llvm") |
| toolchain_path_prefix = llvm_repo_path |
| tools_path_prefix = "llvm/" |
| wrapper_bin_prefix = "" |
| |
| default_sysroot_path = _default_sysroot_path(rctx, os) |
| |
| workspace_name = rctx.name |
| toolchain_info = struct( |
| os = os, |
| arch = arch, |
| toolchain_root = toolchain_root, |
| additional_target_compatible_with_dict = rctx.attr.additional_target_compatible_with, |
| target_toolchain_roots_dict = rctx.attr.target_toolchain_roots, |
| toolchain_path_prefix = toolchain_path_prefix, |
| target_toolchain_path_prefixes_dict = target_llvm_repo_paths, |
| tools_path_prefix = tools_path_prefix, |
| wrapper_bin_prefix = wrapper_bin_prefix, |
| additional_include_dirs_dict = rctx.attr.cxx_builtin_include_directories, |
| sysroot_dict = rctx.attr.sysroot, |
| default_sysroot_path = default_sysroot_path, |
| llvm_version = rctx.attr.llvm_version, |
| standard_libraries_dict = rctx.attr.standard_libraries, |
| static_libstdcxx = rctx.attr.static_libstdcxx, |
| conlyopts_dict = rctx.attr.conlyopts, |
| cxxopts_dict = rctx.attr.cxxopts, |
| copts_dict = rctx.attr.copts, |
| opt_copts_dict = rctx.attr.opt_copts, |
| dbg_copts_dict = rctx.attr.dbg_copts, |
| fastbuild_copts_dict = rctx.attr.fastbuild_copts, |
| linkopts_dict = rctx.attr.linkopts, |
| ) |
| host_tools_info = dict([ |
| pair |
| for (key, tool_path, features) in [ |
| # This is used for macOS hosts: |
| ("libtool", "/usr/bin/libtool", [_host_tool_features.SUPPORTS_ARG_FILE]), |
| # This is used with old (pre 7) LLVM versions: |
| ("strip", "/usr/bin/strip", []), |
| # This is used when lld doesn't support the target platform (i.e. |
| # Mach-O for macOS): |
| ("ld", "/usr/bin/ld", []), |
| ] |
| for pair in _host_tools.get_tool_info(rctx, tool_path, features, key).items() |
| ]) |
| cc_toolchains_str, toolchain_labels_str = _cc_toolchains_str( |
| workspace_name, |
| toolchain_info, |
| use_absolute_paths, |
| host_tools_info, |
| ) |
| |
| # Convenience macro to register all generated toolchains. |
| rctx.template( |
| "toolchains.bzl", |
| Label("//toolchain:toolchains.bzl.tpl"), |
| { |
| "%{toolchain_labels}": toolchain_labels_str, |
| }, |
| ) |
| |
| # BUILD file with all the generated toolchain definitions. |
| rctx.template( |
| "BUILD.bazel", |
| Label("//toolchain:BUILD.toolchain.tpl"), |
| { |
| "%{cc_toolchains}": cc_toolchains_str, |
| "%{cc_toolchain_config_bzl}": str(rctx.attr._cc_toolchain_config_bzl), |
| }, |
| ) |
| |
| # CC wrapper script; see comments near the definition of `wrapper_bin_prefix`. |
| if os == "darwin": |
| cc_wrapper_tpl = "//toolchain:osx_cc_wrapper.sh.tpl" |
| else: |
| cc_wrapper_tpl = "//toolchain:cc_wrapper.sh.tpl" |
| rctx.template( |
| "bin/cc_wrapper.sh", |
| Label(cc_wrapper_tpl), |
| { |
| "%{toolchain_path_prefix}": toolchain_path_prefix, |
| }, |
| ) |
| |
| # libtool wrapper; used if the host libtool doesn't support arg files: |
| rctx.template( |
| "bin/host_libtool_wrapper.sh", |
| Label("//toolchain:host_libtool_wrapper.sh.tpl"), |
| { |
| "%{libtool_path}": "/usr/bin/libtool", |
| }, |
| ) |
| |
| def _cc_toolchains_str( |
| workspace_name, |
| toolchain_info, |
| use_absolute_paths, |
| host_tools_info): |
| # Since all the toolchains rely on downloading the right LLVM toolchain for |
| # the host architecture, we don't need to explicitly specify |
| # `exec_compatible_with` attribute. If the host and execution platform are |
| # not the same, then host auto-detection based LLVM download does not work |
| # and the user has to explicitly specify the distribution of LLVM they |
| # want. |
| |
| # Note that for cross-compiling, the toolchain configuration will need |
| # appropriate sysroots. A recommended approach is to configure two |
| # `llvm_toolchain` repos, one without sysroots (for easy single platform |
| # builds) and register this one, and one with sysroots and provide |
| # `--extra_toolchains` flag when cross-compiling. |
| |
| cc_toolchains_str = "" |
| toolchain_names = [] |
| for (target_os, target_arch) in _supported_targets: |
| suffix = "{}-{}".format(target_arch, target_os) |
| cc_toolchain_str = _cc_toolchain_str( |
| suffix, |
| target_os, |
| target_arch, |
| toolchain_info, |
| use_absolute_paths, |
| host_tools_info, |
| ) |
| if cc_toolchain_str: |
| cc_toolchains_str = cc_toolchains_str + cc_toolchain_str |
| toolchain_name = "@{}//:cc-toolchain-{}".format(workspace_name, suffix) |
| toolchain_names.append(toolchain_name) |
| |
| sep = ",\n" + " " * 8 # 2 tabs with tabstop=4. |
| toolchain_labels_str = sep.join(["\"{}\"".format(d) for d in toolchain_names]) |
| return cc_toolchains_str, toolchain_labels_str |
| |
| def _cc_toolchain_str( |
| suffix, |
| target_os, |
| target_arch, |
| toolchain_info, |
| use_absolute_paths, |
| host_tools_info): |
| host_os = toolchain_info.os |
| host_arch = toolchain_info.arch |
| |
| host_os_bzl = _os_bzl(host_os) |
| target_os_bzl = _os_bzl(target_os) |
| |
| sysroot_path, sysroot = _sysroot_path( |
| toolchain_info.sysroot_dict, |
| target_os, |
| target_arch, |
| ) |
| if not sysroot_path: |
| if host_os == target_os and host_arch == target_arch: |
| # For darwin -> darwin, we can use the macOS SDK path. |
| sysroot_path = toolchain_info.default_sysroot_path |
| else: |
| # We are trying to cross-compile without a sysroot, let's bail. |
| # TODO: Are there situations where we can continue? |
| return "" |
| |
| extra_files_str = ", \":llvm\", \":wrapper-files\"" |
| |
| key = _os_arch_pair(target_os, target_arch) |
| additional_include_dirs = toolchain_info.additional_include_dirs_dict.get(key) |
| additional_include_dirs_str = "[]" |
| if additional_include_dirs: |
| additional_include_dirs_str = "[{}]".format( |
| ", ".join(["\"{}\"".format(d) for d in additional_include_dirs]), |
| ) |
| |
| sysroot_label_str = "\"%s\"" % str(sysroot) if sysroot else "" |
| |
| # `struct` isn't allowed in `BUILD` files so we JSON encode + decode to turn |
| # them into `dict`s. |
| host_tools_info = json.decode(json.encode(host_tools_info)) |
| |
| standard_library = toolchain_info.standard_libraries_dict.get(key, "") |
| conlyopts = toolchain_info.conlyopts_dict.get(key, []) |
| cxxopts = toolchain_info.cxxopts_dict.get(key, []) |
| copts = toolchain_info.copts_dict.get(key, []) |
| opt_copts = toolchain_info.opt_copts_dict.get(key, []) |
| dbg_copts = toolchain_info.dbg_copts_dict.get(key, []) |
| fastbuild_copts = toolchain_info.fastbuild_copts_dict.get(key, []) |
| linkopts = toolchain_info.linkopts_dict.get(key, []) |
| target_toolchain_root = toolchain_info.toolchain_root |
| if key in toolchain_info.target_toolchain_roots_dict: |
| target_toolchain_root = toolchain_info.target_toolchain_roots_dict[key] |
| elif "" in toolchain_info.target_toolchain_roots_dict: |
| target_toolchain_root = toolchain_info.target_toolchain_roots_dict[""] |
| target_toolchain_path_prefix = toolchain_info.toolchain_path_prefix |
| if key in toolchain_info.target_toolchain_path_prefixes_dict: |
| target_toolchain_path_prefix = toolchain_info.target_toolchain_path_prefixes_dict[key] |
| elif "" in toolchain_info.target_toolchain_roots_dict: |
| target_toolchain_path_prefix = toolchain_info.target_toolchain_path_prefixes_dict[""] |
| additional_target_compatible_with = toolchain_info.additional_target_compatible_with_dict.get(key, []) |
| |
| template = """ |
| # CC toolchain for cc-clang-{suffix}. |
| |
| cc_toolchain_config( |
| name = "local-{suffix}", |
| host_arch = "{host_arch}", |
| host_os = "{host_os}", |
| target_arch = "{target_arch}", |
| target_os = "{target_os}", |
| toolchain_path_prefix = "{toolchain_path_prefix}", |
| target_toolchain_path_prefix = "{target_toolchain_path_prefix}", |
| tools_path_prefix = "{tools_path_prefix}", |
| wrapper_bin_prefix = "{wrapper_bin_prefix}", |
| sysroot_path = "{sysroot_path}", |
| additional_include_dirs = {additional_include_dirs_str}, |
| llvm_version = "{llvm_version}", |
| host_tools_info = {host_tools_info}, |
| standard_library = "{standard_library}", |
| static_libstdcxx = {static_libstdcxx}, |
| conlyopts = {conlyopts}, |
| cxxopts = {cxxopts}, |
| copts = {copts}, |
| opt_copts = {opt_copts}, |
| dbg_copts = {dbg_copts}, |
| fastbuild_copts = {fastbuild_copts}, |
| linkopts = {linkopts}, |
| ) |
| |
| toolchain( |
| name = "cc-toolchain-{suffix}", |
| exec_compatible_with = [ |
| "@platforms//cpu:{host_arch}", |
| "@platforms//os:{host_os_bzl}", |
| ], |
| target_compatible_with = [ |
| "@platforms//cpu:{target_arch}", |
| "@platforms//os:{target_os_bzl}", |
| ] + {additional_target_compatible_with}, |
| toolchain = ":cc-clang-{suffix}", |
| toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", |
| ) |
| """ |
| |
| if use_absolute_paths: |
| template = template + """ |
| cc_toolchain( |
| name = "cc-clang-{suffix}", |
| all_files = ":empty", |
| compiler_files = ":empty", |
| dwp_files = ":empty", |
| linker_files = ":empty", |
| objcopy_files = ":empty", |
| strip_files = ":empty", |
| toolchain_config = "local-{suffix}", |
| ) |
| """ |
| else: |
| template = template + """ |
| filegroup( |
| name = "sysroot-components-{suffix}", |
| srcs = [{sysroot_label_str}], |
| ) |
| |
| filegroup( |
| name = "compiler-components-{suffix}", |
| srcs = [ |
| "{toolchain_root}:clang", |
| "{target_toolchain_root}:include", |
| ":sysroot-components-{suffix}", |
| ], |
| ) |
| |
| filegroup( |
| name = "linker-components-{suffix}", |
| srcs = [ |
| "{toolchain_root}:clang", |
| "{toolchain_root}:ld", |
| "{toolchain_root}:ar", |
| "{target_toolchain_root}:lib", |
| ":sysroot-components-{suffix}", |
| ], |
| ) |
| |
| filegroup( |
| name = "all-components-{suffix}", |
| srcs = [ |
| "{toolchain_root}:bin", |
| ":compiler-components-{suffix}", |
| ":linker-components-{suffix}", |
| ], |
| ) |
| |
| filegroup(name = "all-files-{suffix}", srcs = [":all-components-{suffix}"{extra_files_str}]) |
| filegroup(name = "archiver-files-{suffix}", srcs = ["{toolchain_root}:ar"{extra_files_str}]) |
| filegroup(name = "assembler-files-{suffix}", srcs = ["{toolchain_root}:as"{extra_files_str}]) |
| filegroup(name = "compiler-files-{suffix}", srcs = [":compiler-components-{suffix}"{extra_files_str}]) |
| filegroup(name = "dwp-files-{suffix}", srcs = ["{toolchain_root}:dwp"{extra_files_str}]) |
| filegroup(name = "linker-files-{suffix}", srcs = [":linker-components-{suffix}"{extra_files_str}]) |
| filegroup(name = "objcopy-files-{suffix}", srcs = ["{toolchain_root}:objcopy"{extra_files_str}]) |
| filegroup(name = "strip-files-{suffix}", srcs = ["{toolchain_root}:strip"{extra_files_str}]) |
| |
| cc_toolchain( |
| name = "cc-clang-{suffix}", |
| all_files = "all-files-{suffix}", |
| ar_files = "archiver-files-{suffix}", |
| as_files = "assembler-files-{suffix}", |
| compiler_files = "compiler-files-{suffix}", |
| dwp_files = "dwp-files-{suffix}", |
| linker_files = "linker-files-{suffix}", |
| objcopy_files = "objcopy-files-{suffix}", |
| strip_files = "strip-files-{suffix}", |
| toolchain_config = "local-{suffix}", |
| ) |
| """ |
| |
| return template.format( |
| suffix = suffix, |
| target_os = target_os, |
| target_arch = target_arch, |
| host_os = host_os, |
| host_arch = host_arch, |
| target_os_bzl = target_os_bzl, |
| host_os_bzl = host_os_bzl, |
| additional_target_compatible_with = additional_target_compatible_with, |
| toolchain_root = toolchain_info.toolchain_root, |
| toolchain_path_prefix = toolchain_info.toolchain_path_prefix, |
| target_toolchain_root = target_toolchain_root, |
| target_toolchain_path_prefix = target_toolchain_path_prefix, |
| tools_path_prefix = toolchain_info.tools_path_prefix, |
| wrapper_bin_prefix = toolchain_info.wrapper_bin_prefix, |
| additional_include_dirs_str = additional_include_dirs_str, |
| sysroot_label_str = sysroot_label_str, |
| sysroot_path = sysroot_path, |
| llvm_version = toolchain_info.llvm_version, |
| extra_files_str = extra_files_str, |
| host_tools_info = host_tools_info, |
| standard_library = standard_library, |
| static_libstdcxx = toolchain_info.static_libstdcxx, |
| conlyopts = conlyopts, |
| cxxopts = cxxopts, |
| copts = copts, |
| opt_copts = opt_copts, |
| dbg_copts = dbg_copts, |
| fastbuild_copts = fastbuild_copts, |
| linkopts = linkopts, |
| ) |