| load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "feature", "flag_group", "flag_set", "tool_path", "with_feature_set") |
| load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") |
| |
| load("@soong_injection//cc_toolchain:constants.bzl", "constants") |
| |
| # Clang-specific configuration. |
| _ClangVersionInfo = provider(fields = ["directory", "includes"]) |
| |
| def _clang_version_impl(ctx): |
| directory = ctx.file.directory |
| provider = _ClangVersionInfo( |
| directory = directory, |
| includes = [directory.short_path + "/" + d for d in ctx.attr.includes], |
| ) |
| return [provider] |
| |
| clang_version = rule( |
| implementation = _clang_version_impl, |
| attrs = { |
| "directory": attr.label(allow_single_file = True, mandatory = True), |
| "includes": attr.string_list(default = []), |
| }, |
| ) |
| |
| # These defines should only apply to targets which are not under |
| # @external/. This can be controlled by adding "-non_external_compiler_flags" |
| # to the features list for external/ packages. |
| # This corresponds to special-casing in Soong (see "external/" in build/soong/cc/compiler.go). |
| NON_EXTERNAL_DEFINES = [ |
| "-DANDROID_STRICT", |
| ] |
| |
| COMPILER_FLAGS = [ |
| "-fPIC", |
| ] |
| ASM_COMPILER_FLAGS = [ |
| "-D__ASSEMBLY__", |
| ] |
| # CStdVersion in cc/config/global.go |
| C_COMPILER_FLAGS = [ |
| "-std=gnu99", |
| ] |
| # CppStdVersion in cc/config/global.go |
| CC_COMPILER_STANDARD_STD_FLAGS = [ |
| "-std=gnu++17", |
| ] |
| |
| # Should be toggled instead of CC_COMPILER_STANDARD_STD_FLAGS if |
| # the soong module has "cpp_std: 'experimental'". In bazel, tied |
| # to the feature "cpp_std_experimental". |
| CC_COMPILER_EXPERIMENTAL_STD_FLAGS = [ |
| "-std=gnu++2a", |
| ] |
| |
| # These are the linker flags for OSes that use Bionic: LinuxBionic, Android |
| BIONIC_LINKER_FLAGS = [ |
| "-nostdlib", |
| "-Wl,--no-undefined", |
| "-Wl,--hash-style=gnu", |
| "-Wl,--gc-sections", |
| ] |
| STATIC_LINKER_FLAGS = [ |
| "-static", |
| ] |
| DYNAMIC_LINKER_FLAGS = [ |
| "-shared", |
| ] |
| |
| def _tool_paths(clang_version_info): |
| return [ |
| tool_path( |
| name = "gcc", |
| path = clang_version_info.directory.basename + "/bin/clang", |
| ), |
| tool_path( |
| name = "ld", |
| path = clang_version_info.directory.basename + "/bin/ld.lld", |
| ), |
| tool_path( |
| name = "ar", |
| path = clang_version_info.directory.basename + "/bin/llvm-ar", |
| ), |
| tool_path( |
| name = "cpp", |
| path = "/bin/false", |
| ), |
| tool_path( |
| name = "gcov", |
| path = "/bin/false", |
| ), |
| tool_path( |
| name = "nm", |
| path = clang_version_info.directory.basename + "/bin/llvm-nm", |
| ), |
| tool_path( |
| name = "objdump", |
| path = clang_version_info.directory.basename + "/bin/llvm-objdump", |
| ), |
| # Soong has a wrapper around strip. |
| # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/strip.go;l=62;drc=master |
| # https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/builder.go;l=991-1025;drc=master |
| tool_path( |
| name = "strip", |
| path = clang_version_info.directory.basename + "/bin/llvm-strip", |
| ), |
| ] |
| |
| def _compiler_flag_features(flags = [], os_is_device = False): |
| |
| # Combine the toolchain's provided flags with the default ones. |
| flags = flags + COMPILER_FLAGS + constants.CommonClangGlobalCflags |
| |
| if os_is_device: |
| flags += constants.DeviceClangGlobalCflags |
| else: |
| flags += constants.HostClangGlobalCflags |
| |
| # Default assembler flags. |
| asm_only_flags = ASM_COMPILER_FLAGS |
| |
| # Default C++ compile action only flags (No C) |
| cpp_only_flags = [] |
| cpp_only_flags += constants.CommonClangGlobalCppflags |
| if os_is_device: |
| cpp_only_flags += constants.DeviceGlobalCppflags |
| else: |
| cpp_only_flags += constants.HostGlobalCppflags |
| |
| # Default C compile action only flags (No C++) |
| c_only_flags = C_COMPILER_FLAGS + constants.CommonGlobalConlyflags |
| |
| # Flags that only apply in the external/ directory. |
| non_external_flags = NON_EXTERNAL_DEFINES |
| |
| features = [] |
| |
| features.append(feature( |
| name = "non_external_compiler_flags", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = non_external_flags, |
| ), |
| ], |
| ), |
| ], |
| )) |
| features.append(feature( |
| name = "common_compiler_flags", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = flags, |
| ), |
| ], |
| ), |
| ], |
| )) |
| features.append(feature( |
| name = "asm_compiler_flags", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = asm_only_flags, |
| ), |
| ], |
| ), |
| ], |
| )) |
| features.append(feature( |
| name = "cpp_compiler_flags", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = cpp_only_flags, |
| ), |
| ], |
| ), |
| ], |
| )) |
| features.append(feature( |
| name = "c_compiler_flags", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = c_only_flags, |
| ), |
| ], |
| ), |
| ], |
| )) |
| features.append(feature( |
| name = "cpp_std_experimental", |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_compile, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = CC_COMPILER_EXPERIMENTAL_STD_FLAGS, |
| ), |
| ], |
| ), |
| ], |
| )) |
| features.append(feature( |
| name = "cpp_std_standard", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_compile, |
| ], |
| with_features = [ |
| with_feature_set(not_features = ["cpp_std_experimental"]), |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = CC_COMPILER_STANDARD_STD_FLAGS, |
| ), |
| ], |
| ), |
| ], |
| )) |
| |
| # The user_compile_flags feature is used by Bazel to add --copt, --conlyopt, |
| # and --cxxopt values. Any features added above this call will thus appear |
| # earlier in the commandline than the user opts (so users could override |
| # flags set by earlier features). Anything after the user options are |
| # effectively non-overridable by users. |
| features.append(feature( |
| name = "user_compile_flags", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| expand_if_available = "user_compile_flags", |
| flags = ["%{user_compile_flags}"], |
| iterate_over = "user_compile_flags", |
| ), |
| ], |
| ), |
| ], |
| )) |
| |
| # These cannot be overriden by the user. |
| features.append(feature( |
| name = "no_override_clang_global_copts", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = constants.NoOverrideClangGlobalCflags, |
| ), |
| ], |
| ), |
| ], |
| )) |
| |
| |
| return features |
| |
| def _rpath_features(): |
| runtime_library_search_directories_feature = feature( |
| name = "runtime_library_search_directories", |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_link_executable, |
| ACTION_NAMES.cpp_link_dynamic_library, |
| ACTION_NAMES.cpp_link_nodeps_dynamic_library, |
| ACTION_NAMES.lto_index_for_executable, |
| ACTION_NAMES.lto_index_for_dynamic_library, |
| ACTION_NAMES.lto_index_for_nodeps_dynamic_library, |
| ], |
| flag_groups = [ |
| flag_group( |
| iterate_over = "runtime_library_search_directories", |
| flag_groups = [ |
| flag_group( |
| flags = [ |
| "-Wl,-rpath,$EXEC_ORIGIN/%{runtime_library_search_directories}", |
| ], |
| expand_if_true = "is_cc_test", |
| ), |
| flag_group( |
| flags = [ |
| "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", |
| ], |
| expand_if_false = "is_cc_test", |
| ), |
| ], |
| expand_if_available = |
| "runtime_library_search_directories", |
| ), |
| ], |
| with_features = [ |
| with_feature_set(features = ["static_link_cpp_runtimes"]), |
| ], |
| ), |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_link_executable, |
| ACTION_NAMES.cpp_link_dynamic_library, |
| ACTION_NAMES.cpp_link_nodeps_dynamic_library, |
| ACTION_NAMES.lto_index_for_executable, |
| ACTION_NAMES.lto_index_for_dynamic_library, |
| ACTION_NAMES.lto_index_for_nodeps_dynamic_library, |
| ], |
| flag_groups = [ |
| flag_group( |
| iterate_over = "runtime_library_search_directories", |
| flag_groups = [ |
| flag_group( |
| flags = [ |
| "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", |
| ], |
| ), |
| ], |
| expand_if_available = |
| "runtime_library_search_directories", |
| ), |
| ], |
| with_features = [ |
| with_feature_set( |
| not_features = ["static_link_cpp_runtimes", "disable_rpath"], |
| ), |
| ], |
| ), |
| ], |
| ) |
| disable_rpath_feature = feature( |
| name = "disable_rpath", |
| enabled = False, |
| ) |
| return [runtime_library_search_directories_feature, disable_rpath_feature] |
| |
| def _linker_flag_feature(name, flags = [], additional_static_flags = [], additional_dynamic_flags = []): |
| if not flags: |
| return None |
| return feature( |
| name = name, |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_link_executable, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = flags + additional_static_flags, |
| ), |
| ], |
| ), |
| flag_set( |
| actions = [ |
| ACTION_NAMES.cpp_link_dynamic_library, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = flags + additional_dynamic_flags, |
| ), |
| ], |
| ), |
| ], |
| ) |
| |
| def _toolchain_include_feature(system_includes = []): |
| flags = [] |
| for include in system_includes: |
| flags.append("-isystem") |
| flags.append(include) |
| if not flags: |
| return None |
| return feature( |
| name = "toolchain_include_directories", |
| enabled = True, |
| flag_sets = [ |
| flag_set( |
| actions = [ |
| ACTION_NAMES.assemble, |
| ACTION_NAMES.preprocess_assemble, |
| ACTION_NAMES.linkstamp_compile, |
| ACTION_NAMES.c_compile, |
| ACTION_NAMES.cpp_compile, |
| ACTION_NAMES.cpp_header_parsing, |
| ACTION_NAMES.cpp_module_compile, |
| ACTION_NAMES.cpp_module_codegen, |
| ACTION_NAMES.lto_backend, |
| ACTION_NAMES.clif_match, |
| ], |
| flag_groups = [ |
| flag_group( |
| flags = flags, |
| ), |
| ], |
| ), |
| ], |
| ) |
| |
| def is_target_os_device(ctx): |
| if "_host" in ctx.attr.toolchain_identifier: |
| return False |
| else: |
| return True |
| |
| def _cc_toolchain_config_impl(ctx): |
| clang_version_info = ctx.attr.clang_version[_ClangVersionInfo] |
| os_is_device = is_target_os_device(ctx) |
| |
| builtin_include_dirs = [] |
| |
| # This is so that Bazel doesn't validate .d files against the set of headers |
| # declared in BUILD files (Blueprint files don't contain that data) |
| builtin_include_dirs.extend(["/"]) |
| |
| builtin_include_dirs.extend(clang_version_info.includes) |
| |
| # Compiler action features |
| compiler_flag_features = _compiler_flag_features(ctx.attr.target_flags, os_is_device) |
| |
| # Linker action features |
| linker_target_flag_feature = _linker_flag_feature( |
| "linker_target_flags", |
| flags = ctx.attr.target_flags, |
| ) |
| |
| linker_flags = [] |
| linker_flags += ctx.attr.linker_flags |
| if os_is_device: |
| linker_flags += constants.DeviceGlobalLldflags |
| linker_flags += BIONIC_LINKER_FLAGS |
| else: |
| linker_flags += constants.HostGlobalLldflags |
| linker_flag_feature = _linker_flag_feature( |
| "linker_flags", |
| flags = linker_flags, |
| additional_static_flags = STATIC_LINKER_FLAGS, |
| additional_dynamic_flags = DYNAMIC_LINKER_FLAGS, |
| ) |
| |
| # System include directories features |
| toolchain_include_directories_feature = _toolchain_include_feature( |
| system_includes = builtin_include_dirs, |
| ) |
| |
| # Aggregate all features |
| features = compiler_flag_features + \ |
| _rpath_features() + \ |
| [ |
| linker_target_flag_feature, |
| linker_flag_feature, |
| toolchain_include_directories_feature |
| ] |
| features = [feature for feature in features if feature != None] |
| |
| return cc_common.create_cc_toolchain_config_info( |
| ctx = ctx, |
| toolchain_identifier = ctx.attr.toolchain_identifier, |
| host_system_name = "i686-unknown-linux-gnu", |
| # TODO: replace the following placeholders with the real target values, |
| # preferably declared at the toolchain. |
| target_system_name = "x86_64-unknown-unknown", |
| target_cpu = "x86_64", |
| target_libc = "unknown", |
| compiler = "clang", |
| abi_version = "unknown", |
| abi_libc_version = "unknown", |
| tool_paths = _tool_paths(clang_version_info), |
| features = features, |
| cxx_builtin_include_directories = builtin_include_dirs, |
| ) |
| |
| _cc_toolchain_config = rule( |
| implementation = _cc_toolchain_config_impl, |
| attrs = { |
| "toolchain_identifier": attr.string(mandatory = True), |
| "clang_version": attr.label(mandatory = True, providers = [_ClangVersionInfo]), |
| "target_flags": attr.string_list(default = []), |
| "linker_flags": attr.string_list(default = []), |
| '_android_os_constraint': attr.label(default = "//build/bazel/platforms/os:android"), |
| '_linux_os_constraint': attr.label(default = "//build/bazel/platforms/os:linux"), |
| }, |
| provides = [CcToolchainConfigInfo], |
| ) |
| |
| # Macro to set up both the toolchain and the config. |
| def android_cc_toolchain( |
| name, |
| clang_version = None, |
| # This should come from the clang_version provider. |
| # Instead, it's hard-coded because this is a macro, not a rule. |
| clang_version_directory = None, |
| target_flags = [], |
| linker_flags = [], |
| toolchain_identifier = None): |
| # Write the toolchain config. |
| _cc_toolchain_config( |
| name = "%s_config" % name, |
| clang_version = clang_version, |
| target_flags = target_flags, |
| linker_flags = linker_flags, |
| toolchain_identifier = toolchain_identifier, |
| ) |
| |
| # Create the filegroups needed for sandboxing toolchain inputs to C++ actions. |
| native.filegroup( |
| name = "%s_compiler_clang_includes" % name, |
| srcs = |
| native.glob([clang_version_directory + "/lib64/clang/*/include/**"]), |
| ) |
| |
| native.filegroup( |
| name = "%s_compiler_binaries" % name, |
| srcs = native.glob([ |
| clang_version_directory + "/bin/clang*", |
| ]), |
| ) |
| |
| native.filegroup( |
| name = "%s_linker_binaries" % name, |
| srcs = native.glob([ |
| # Linking shared libraries uses clang. |
| clang_version_directory + "/bin/clang*", |
| ]) + [ |
| clang_version_directory + "/bin/lld", |
| clang_version_directory + "/bin/ld.lld", |
| ], |
| ) |
| |
| native.filegroup( |
| name = "%s_ar_files" % name, |
| srcs = [clang_version_directory + "/bin/llvm-ar"], |
| ) |
| |
| native.filegroup( |
| name = "%s_compiler_files" % name, |
| srcs = [ |
| "%s_compiler_binaries" % name, |
| "%s_compiler_clang_includes" % name, |
| ], |
| ) |
| native.filegroup( |
| name = "%s_linker_files" % name, |
| srcs = [ |
| "%s_linker_binaries" % name, |
| ], |
| ) |
| native.filegroup( |
| name = "%s_all_files" % name, |
| srcs = [ |
| "%s_compiler_files" % name, |
| "%s_linker_files" % name, |
| "%s_ar_files" % name, |
| ], |
| ) |
| |
| # Create the actual cc_toolchain. |
| # The dependency on //:empty is intentional; it's necessary so that Bazel |
| # can parse .d files correctly (see the comment in $TOP/BUILD) |
| native.cc_toolchain( |
| name = name, |
| all_files = "%s_all_files" % name, |
| as_files = "//:empty", # Note the "//" prefix, see comment above |
| ar_files = "%s_ar_files" % name, |
| compiler_files = "%s_compiler_files" % name, |
| dwp_files = ":empty", |
| linker_files = "%s_linker_files" % name, |
| objcopy_files = ":empty", |
| strip_files = ":empty", |
| supports_param_files = 0, |
| toolchain_config = ":%s_config" % name, |
| toolchain_identifier = toolchain_identifier, |
| ) |