From d2d3a8304b82f8a2a561d810932579c72f967a71 Mon Sep 17 00:00:00 2001 From: PikachuHy Date: Mon, 29 Jun 2026 23:00:54 +0800 Subject: [PATCH] Support Using Clang Module Map to Avoid mixing #include and import problems --- cc/common/cc_helper.bzl | 27 +++- cc/private/cc_common.bzl | 5 + cc/private/compile/compile.bzl | 132 ++++++++++++++---- cc/private/rules_impl/attrs.bzl | 15 ++ cc/private/rules_impl/cc_binary_impl.bzl | 1 + cc/private/rules_impl/cc_library_impl.bzl | 1 + .../toolchain/unix_cc_toolchain_config.bzl | 19 +++ 7 files changed, 174 insertions(+), 26 deletions(-) diff --git a/cc/common/cc_helper.bzl b/cc/common/cc_helper.bzl index 92483ac6d..6d52c01cf 100644 --- a/cc/common/cc_helper.bzl +++ b/cc/common/cc_helper.bzl @@ -901,7 +901,9 @@ def _report_invalid_options(cc_toolchain, cpp_config): fail("The selected toolchain does not support setting --grte_top (it doesn't specify builtin_sysroot).") def _check_cpp_modules(ctx, feature_configuration): - if len(ctx.files.module_interfaces) == 0: + has_module_interfaces = len(ctx.files.module_interfaces) > 0 + has_module_header_maps = hasattr(ctx.attr, "module_header_maps") and len(ctx.attr.module_header_maps) > 0 + if not has_module_interfaces and not has_module_header_maps: return if not ctx.fragments.cpp.experimental_cpp_modules(): fail("requires --experimental_cpp_modules", attr = "module_interfaces") @@ -1016,6 +1018,28 @@ def _get_cpp_module_interfaces(ctx): artifact_label_map = _calculate_artifact_label_map(ctx.attr.module_interfaces, "module_interfaces") return _map_to_list(artifact_label_map) +def _get_module_header_map_entries(ctx): + """Returns (module_name, map_file) tuples from module_header_maps.""" + if not hasattr(ctx.attr, "module_header_maps"): + return [] + entries = [] + for name, map_label in ctx.attr.module_header_maps.items(): + map_files = map_label.files.to_list() + if len(map_files) != 1: + fail( + "module_header_maps[%r]: label %s must provide exactly one .modulemap file, got %d" % + (name, map_label, len(map_files)), + attr = "module_header_maps", + ) + map_file = map_files[0] + if not map_file.basename.endswith(".modulemap"): + fail( + "module_header_maps[%r]: expected a .modulemap file, got %s" % (name, map_file.basename), + attr = "module_header_maps", + ) + entries.append((name, map_file)) + return entries + # Returns a list of (Artifact, Label) tuples. Each tuple represents an input source # file and the label of the rule that generates it (or the label of the source file itself if it # is an input file). @@ -1155,6 +1179,7 @@ cc_helper = struct( local_defines = _local_defines, get_srcs = _get_srcs, get_cpp_module_interfaces = _get_cpp_module_interfaces, + get_module_header_map_entries = _get_module_header_map_entries, get_private_hdrs = _get_private_hdrs, get_public_hdrs = _get_public_hdrs, is_code_coverage_enabled = _is_code_coverage_enabled, diff --git a/cc/private/cc_common.bzl b/cc/private/cc_common.bzl index 8db674306..1d0cd9eda 100644 --- a/cc/private/cc_common.bzl +++ b/cc/private/cc_common.bzl @@ -489,6 +489,7 @@ def _compile( purpose = _UNBOUND, separate_module_headers = _UNBOUND, module_interfaces = _UNBOUND, + module_header_map_entries = _UNBOUND, non_compilation_additional_inputs = _UNBOUND): if module_map != _UNBOUND or \ additional_module_maps != _UNBOUND or \ @@ -501,6 +502,7 @@ def _compile( implementation_compilation_contexts != _UNBOUND or \ separate_module_headers != _UNBOUND or \ module_interfaces != _UNBOUND or \ + module_header_map_entries != _UNBOUND or \ non_compilation_additional_inputs != _UNBOUND: _cc_internal.check_private_api(allowlist = _PRIVATE_STARLARKIFICATION_ALLOWLIST) @@ -524,6 +526,8 @@ def _compile( separate_module_headers = [] if module_interfaces == _UNBOUND: module_interfaces = [] + if module_header_map_entries == _UNBOUND: + module_header_map_entries = [] if non_compilation_additional_inputs == _UNBOUND: non_compilation_additional_inputs = [] @@ -538,6 +542,7 @@ def _compile( name = name, srcs = srcs, module_interfaces = module_interfaces, + module_header_map_entries = module_header_map_entries, public_hdrs = public_hdrs, private_hdrs = private_hdrs, textual_hdrs = textual_hdrs, diff --git a/cc/private/compile/compile.bzl b/cc/private/compile/compile.bzl index d7272e1d2..8b0b21fdd 100644 --- a/cc/private/compile/compile.bzl +++ b/cc/private/compile/compile.bzl @@ -135,6 +135,7 @@ def compile( purpose = None, separate_module_headers = [], module_interfaces = [], + module_header_map_entries = [], non_compilation_additional_inputs = [], progress_message_prefix = None): """Should be used for C++ compilation. @@ -385,6 +386,7 @@ def compile( auxiliary_fdo_inputs = auxiliary_fdo_inputs, fdo_build_variables = fdo_build_variables, progress_message_prefix = progress_message_prefix, + module_header_map_entries = module_header_map_entries, ) compilation_outputs_dict["lto_compilation_context"] = create_lto_compilation_context( @@ -637,6 +639,77 @@ def _create_gen_modmap_action( toolchain = None, ) +def _create_header_map_modmap_actions( + *, + actions, + action_construction_context, + cc_toolchain, + configuration, + label, + use_pic, + module_header_map_entries, + modules_info_file): + """Creates a shared modmap for all module_header_maps on this target.""" + if use_pic: + output_name_base_for_modmap = _cc_internal.get_artifact_name_for_category( + cc_toolchain = cc_toolchain, + category = artifact_category.PIC_FILE, + output_name = label.name, + ) + else: + output_name_base_for_modmap = label.name + modmap_output_name = output_name_base_for_modmap + "_header_maps" + modmap_file = _get_compile_output_file( + action_construction_context, + label, + configuration = configuration, + output_name = _cc_internal.get_artifact_name_for_category( + cc_toolchain = cc_toolchain, + category = artifact_category.CPP_MODULES_MODMAP, + output_name = modmap_output_name, + ), + ) + modmap_input_file = _get_compile_output_file( + action_construction_context, + label, + configuration = configuration, + output_name = _cc_internal.get_artifact_name_for_category( + cc_toolchain = cc_toolchain, + category = artifact_category.CPP_MODULES_MODMAP_INPUT, + output_name = modmap_output_name, + ), + ) + ddi_file = _get_compile_output_file( + action_construction_context, + label, + configuration = configuration, + output_name = _cc_internal.get_artifact_name_for_category( + cc_toolchain = cc_toolchain, + category = artifact_category.CPP_MODULES_DDI, + output_name = modmap_output_name, + ), + ) + actions.write( + ddi_file, + json.encode({ + "rules": [{ + "requires": [ + {"logical-name": name} + for name, _ in module_header_map_entries + ], + }], + }), + ) + _create_gen_modmap_action( + actions = actions, + cc_toolchain = cc_toolchain, + ddi_file = ddi_file, + modules_info_file = modules_info_file, + modmap_file = modmap_file, + modmap_input_file = modmap_input_file, + ) + return modmap_file, modmap_input_file + # buildifier: disable=unused-variable def _create_cc_compile_actions_with_cpp20_module_helper( *, @@ -668,7 +741,8 @@ def _create_cc_compile_actions_with_cpp20_module_helper( use_pic, enable_dotd_files, output_name_map, - progress_message_prefix): + progress_message_prefix, + module_header_map_entries = []): direct_module_files = [] source_to_module_file_map = {} source_to_ddi_file_map = {} @@ -752,6 +826,10 @@ def _create_cc_compile_actions_with_cpp20_module_helper( ) source_to_ddi_file_map[source_artifact] = ddi_file + shared_header_map_modmap_file = None + shared_header_map_modmap_input_file = None + header_map_input_files = [] + _create_aggregate_ddi_action( actions = actions, cc_toolchain = cc_toolchain, @@ -760,6 +838,18 @@ def _create_cc_compile_actions_with_cpp20_module_helper( transitive_modules_info_files = cc_compilation_context._pic_modules_info_files if use_pic else cc_compilation_context._modules_info_files, modules_info_file = modules_info_file, ) + if module_header_map_entries: + header_map_input_files = [map_file for _, map_file in module_header_map_entries] + shared_header_map_modmap_file, shared_header_map_modmap_input_file = _create_header_map_modmap_actions( + actions = actions, + action_construction_context = action_construction_context, + cc_toolchain = cc_toolchain, + configuration = configuration, + label = label, + use_pic = use_pic, + module_header_map_entries = module_header_map_entries, + modules_info_file = modules_info_file, + ) compiled_basenames = set() transitive_module_files = cc_compilation_context._pic_module_files if use_pic else cc_compilation_context._module_files for cpp_source in module_interfaces_sources.values(): @@ -876,9 +966,17 @@ def _create_cc_compile_actions_with_cpp20_module_helper( additional_build_variables = {} modmap_file = None modmap_input_file = None + source_additional_compilation_inputs = additional_compilation_inputs + is_cc_source = "." + source_artifact.extension in extensions.CC_SOURCE # Only C++ compilation unit will be compiled with C++20 Modules. - if "." + source_artifact.extension in extensions.CC_SOURCE: + if is_cc_source and shared_header_map_modmap_file != None: + modmap_file = shared_header_map_modmap_file + modmap_input_file = shared_header_map_modmap_input_file + additional_build_variables["cpp_module_modmap_file"] = modmap_file + additional_build_variables["cpp_module_header_map_files"] = header_map_input_files + source_additional_compilation_inputs = additional_compilation_inputs + header_map_input_files + elif is_cc_source: modmap_file = _get_compile_output_file( action_construction_context, label, @@ -933,26 +1031,6 @@ def _create_cc_compile_actions_with_cpp20_module_helper( ddi_output_name = ddi_output_name, progress_message_prefix = progress_message_prefix, ) - modmap_file = _get_compile_output_file( - action_construction_context, - label, - configuration = configuration, - output_name = _cc_internal.get_artifact_name_for_category( - cc_toolchain = cc_toolchain, - category = artifact_category.CPP_MODULES_MODMAP, - output_name = output_name_base, - ), - ) - modmap_input_file = _get_compile_output_file( - action_construction_context, - label, - configuration = configuration, - output_name = _cc_internal.get_artifact_name_for_category( - cc_toolchain = cc_toolchain, - category = artifact_category.CPP_MODULES_MODMAP_INPUT, - output_name = output_name_base, - ), - ) additional_build_variables["cpp_module_modmap_file"] = modmap_file _create_gen_modmap_action( actions = actions, @@ -989,7 +1067,7 @@ def _create_cc_compile_actions_with_cpp20_module_helper( bitcode_output = bitcode_output, fdo_context = fdo_context, auxiliary_fdo_inputs = auxiliary_fdo_inputs, - additional_compilation_inputs = additional_compilation_inputs, + additional_compilation_inputs = source_additional_compilation_inputs, additional_include_scanning_roots = additional_include_scanning_roots, use_pic = use_pic, enable_dotd_files = enable_dotd_files, @@ -1030,7 +1108,8 @@ def _create_cc_compile_actions_with_cpp20_module( auxiliary_fdo_inputs, fdo_build_variables, enable_dotd_files, - progress_message_prefix): + progress_message_prefix, + module_header_map_entries = []): """Constructs the C++ compiler actions with C++20 modules support. """ output_name_prefix_dir = _cc_internal.compute_output_name_prefix_dir(configuration = configuration, purpose = purpose) @@ -1071,6 +1150,7 @@ def _create_cc_compile_actions_with_cpp20_module( enable_dotd_files = enable_dotd_files, output_name_map = output_name_map, progress_message_prefix = progress_message_prefix, + module_header_map_entries = module_header_map_entries, ) def _create_cc_compile_actions( @@ -1103,7 +1183,8 @@ def _create_cc_compile_actions( common_compile_build_variables, auxiliary_fdo_inputs, fdo_build_variables, - progress_message_prefix): + progress_message_prefix, + module_header_map_entries = []): """Constructs the C++ compiler actions. It generally creates one action for every specified source @@ -1148,6 +1229,7 @@ def _create_cc_compile_actions( fdo_build_variables = fdo_build_variables, enable_dotd_files = enable_dotd_files, progress_message_prefix = progress_message_prefix, + module_header_map_entries = module_header_map_entries, ) return diff --git a/cc/private/rules_impl/attrs.bzl b/cc/private/rules_impl/attrs.bzl index 9ca8fae9e..3621355e1 100644 --- a/cc/private/rules_impl/attrs.bzl +++ b/cc/private/rules_impl/attrs.bzl @@ -102,6 +102,21 @@ C++ Standard has no restriction about module interface file extension --experimental_cpp_modules.

""", ), + "module_header_maps": attr.string_keyed_label_dict( + allow_files = [".modulemap"], + doc = """ +Maps C++20 named module names to Clang module map files that redirect legacy +#include to import for the same module. + +

Keys are logical module names (for example a for +export module a;). Values are .modulemap files.

+

All C++ sources on this target compiled with cpp_modules enabled +receive every module listed here via a generated modmap, without per-source +dependency scanning.

+

Requires --experimental_cpp_modules and the +cpp_modules toolchain feature.

+ """, + ), "data": attr.label_list( allow_files = True, flags = ["SKIP_CONSTRAINTS_OVERRIDE"], diff --git a/cc/private/rules_impl/cc_binary_impl.bzl b/cc/private/rules_impl/cc_binary_impl.bzl index 665938498..a951aa726 100644 --- a/cc/private/rules_impl/cc_binary_impl.bzl +++ b/cc/private/rules_impl/cc_binary_impl.bzl @@ -536,6 +536,7 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): public_hdrs = cc_helper.get_public_hdrs(ctx), srcs = cc_helper.get_srcs(ctx), module_interfaces = cc_helper.get_cpp_module_interfaces(ctx), + module_header_map_entries = cc_helper.get_module_header_map_entries(ctx), compilation_contexts = compilation_context_deps, code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx = ctx), additional_inputs = ctx.files.additional_compiler_inputs, diff --git a/cc/private/rules_impl/cc_library_impl.bzl b/cc/private/rules_impl/cc_library_impl.bzl index 83b21c45c..7b6af7e42 100755 --- a/cc/private/rules_impl/cc_library_impl.bzl +++ b/cc/private/rules_impl/cc_library_impl.bzl @@ -68,6 +68,7 @@ def _cc_library_impl(ctx): purpose = "cc_library-compile", srcs = cc_helper.get_srcs(ctx), module_interfaces = cc_helper.get_cpp_module_interfaces(ctx), + module_header_map_entries = cc_helper.get_module_header_map_entries(ctx), private_hdrs = cc_helper.get_private_hdrs(ctx), public_hdrs = cc_helper.get_public_hdrs(ctx), code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx), diff --git a/cc/private/toolchain/unix_cc_toolchain_config.bzl b/cc/private/toolchain/unix_cc_toolchain_config.bzl index 1a4fcbe54..2d001b8c3 100644 --- a/cc/private/toolchain/unix_cc_toolchain_config.bzl +++ b/cc/private/toolchain/unix_cc_toolchain_config.bzl @@ -1865,6 +1865,24 @@ def _impl(ctx): enabled = True, ) + cpp_module_header_map_files_feature = feature( + name = "cpp_module_header_map_files", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_compile, + ], + flag_groups = [ + flag_group( + iterate_over = "cpp_module_header_map_files", + flags = ["-fmodule-map-file=%{cpp_module_header_map_files}"], + ), + ], + ), + ], + enabled = True, + ) + no_dotd_file_feature = feature(name = "no_dotd_file") skip_virtual_includes_feature = feature(name = "skip_virtual_includes") @@ -1883,6 +1901,7 @@ def _impl(ctx): cpp_modules_feature, cpp_module_modmap_file_feature, cpp20_module_compile_flags_feature, + cpp_module_header_map_files_feature, dependency_file_feature, serialized_diagnostics_file_feature, random_seed_feature,