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,