diff --git a/src/main/starlark/builtins_bzl/common/cc/compile/cc_compilation_helper.bzl b/src/main/starlark/builtins_bzl/common/cc/compile/cc_compilation_helper.bzl index 25a27b9f3df9ce..c0936861a32bd0 100644 --- a/src/main/starlark/builtins_bzl/common/cc/compile/cc_compilation_helper.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/compile/cc_compilation_helper.bzl @@ -20,7 +20,7 @@ load( "repository_exec_path", ) load(":common/cc/cc_info.bzl", "create_compilation_context", "create_module_map") -load(":common/cc/semantics.bzl", "USE_EXEC_ROOT_FOR_VIRTUAL_INCLUDES_SYMLINKS") +load(":common/cc/semantics.bzl", "STRIP_INCLUDE_PREFIX_APPLIES_TO_TEXTUAL_HEADERS", "USE_EXEC_ROOT_FOR_VIRTUAL_INCLUDES_SYMLINKS") load(":common/paths.bzl", "paths") _cc_common_internal = _builtins.internal.cc_common @@ -61,7 +61,9 @@ def _compute_public_headers( binfiles_dir, non_module_map_headers, is_sibling_repository_layout, - shorten_virtual_includes): + shorten_virtual_includes, + *, + must_use_strip_prefix = True): if include_prefix: if not paths.is_normalized(include_prefix, False): fail("include prefix should not contain uplevel references: " + include_prefix) @@ -118,7 +120,7 @@ def _compute_public_headers( headers = public_headers_artifacts + non_module_map_headers, module_map_headers = public_headers_artifacts, virtual_include_path = None, - virtual_to_original_headers = depset(), + virtual_to_original_headers = [], ) module_map_headers = [] @@ -129,8 +131,13 @@ def _compute_public_headers( else: virtual_include_dir = paths.join(source_package_path, _VIRTUAL_INCLUDES_DIR, label.name) for original_header in public_headers_artifacts: + module_map_headers.append(original_header) + repo_relative_path = _repo_relative_path(original_header) if not repo_relative_path.startswith(strip_prefix): + if not must_use_strip_prefix: + continue + fail("header '{}' is not under the specified strip prefix '{}'".format(repo_relative_path, strip_prefix)) include_path = paths.relativize(repo_relative_path, strip_prefix) if include_prefix != None: @@ -147,14 +154,12 @@ def _compute_public_headers( if config.coverage_enabled: virtual_to_original_headers_list.append((virtual_header.path, original_header.path)) - module_map_headers.append(original_header) - virtual_headers = module_map_headers + non_module_map_headers return struct( headers = virtual_headers, module_map_headers = module_map_headers, virtual_include_path = paths.join(binfiles_dir, virtual_include_dir), - virtual_to_original_headers = depset(virtual_to_original_headers_list), + virtual_to_original_headers = virtual_to_original_headers_list, ) def _generates_header_module(feature_configuration, public_headers, private_headers, generate_action): @@ -196,6 +201,7 @@ _ModuleMapInfo = provider( fields = [ "module_map", "public_headers", + "textual_headers", "private_headers", "dependency_module_maps", "additional_exported_headers", @@ -250,6 +256,12 @@ def _module_map_struct_to_module_map_content(parameters, tree_expander): add_header(path = header.path, visibility = "", can_compile = True) added_paths.add(header.path) + for header in expanded(parameters.textual_headers): + if header.path in added_paths: + continue + add_header(path = header.path, visibility = "", can_compile = False) + added_paths.add(header.path) + for header in expanded(parameters.private_headers): if header.path in added_paths: continue @@ -304,6 +316,7 @@ def _create_module_map_action( module_map, private_headers, public_headers, + textual_headers, dependency_module_maps, additional_exported_headers, separate_module_headers, @@ -323,6 +336,7 @@ def _create_module_map_action( data_struct = _ModuleMapInfo( module_map = module_map, public_headers = public_headers, + textual_headers = textual_headers, private_headers = private_headers, dependency_module_maps = dependency_module_maps, additional_exported_headers = additional_exported_headers, @@ -340,6 +354,7 @@ def _create_module_map_action( # simple null function. tree_artifacts = [h for h in private_headers if h.is_directory] tree_artifacts += [h for h in public_headers if h.is_directory] + tree_artifacts += [h for h in textual_headers if h.is_directory] content.add_all(tree_artifacts, map_each = lambda x: None, allow_closure = True) actions.write(module_map.file, content = content, is_executable = True, mnemonic = "CppModuleMap") @@ -434,15 +449,34 @@ def _init_cc_compilation_context( else: include_dirs_for_context.append(public_headers.virtual_include_path) + textual_headers = _compute_public_headers( + actions, + config, + public_textual_headers, + include_prefix, + strip_include_prefix if STRIP_INCLUDE_PREFIX_APPLIES_TO_TEXTUAL_HEADERS else None, + label, + binfiles_dir, + non_module_map_headers, + sibling_repo_layout, + shorten_virtual_includes, + must_use_strip_prefix = False, + ) + if textual_headers.virtual_include_path: + if external or feature_configuration.is_requested("system_include_paths"): + external_include_dirs.append(textual_headers.virtual_include_path) + else: + include_dirs_for_context.append(textual_headers.virtual_include_path) + if config.coverage_enabled: # Populate the map only when code coverage collection is enabled, to report the actual # source file name in the coverage output file. - virtual_to_original_headers = public_headers.virtual_to_original_headers + virtual_to_original_headers = public_headers.virtual_to_original_headers + textual_headers.virtual_to_original_headers else: - virtual_to_original_headers = depset() + virtual_to_original_headers = [] declared_include_srcs.extend(public_headers.headers) - declared_include_srcs.extend(public_textual_headers) + declared_include_srcs.extend(textual_headers.headers) declared_include_srcs.extend(private_headers_artifacts) declared_include_srcs.extend(additional_inputs) @@ -489,8 +523,10 @@ def _init_cc_compilation_context( if _enabled(feature_configuration, "only_doth_headers_in_module_maps"): public_headers_for_module_map_action = [header for header in public_headers.module_map_headers if (header.is_directory or header.extension == "h")] + textual_headers_for_module_map_action = [header for header in textual_headers.module_map_headers if (header.is_directory or header.extension == "h")] else: public_headers_for_module_map_action = public_headers.module_map_headers + textual_headers_for_module_map_action = textual_headers.module_map_headers private_headers_for_module_map_action = private_headers_artifacts if _enabled(feature_configuration, "exclude_private_headers_in_module_maps"): @@ -500,6 +536,7 @@ def _init_cc_compilation_context( actions = actions, module_map = module_map, public_headers = public_headers_for_module_map_action, + textual_headers = textual_headers_for_module_map_action, separate_module_headers = separate_public_headers.module_map_headers, dependency_module_maps = dependency_module_maps, private_headers = private_headers_for_module_map_action, @@ -560,7 +597,7 @@ def _init_cc_compilation_context( external_includes = depset(external_include_dirs), system_includes = depset(system_include_dirs_for_context), includes = depset(include_dirs_for_context), - virtual_to_original_headers = virtual_to_original_headers, + virtual_to_original_headers = depset(virtual_to_original_headers), dependent_cc_compilation_contexts = dependent_cc_compilation_contexts, non_code_inputs = additional_inputs, defines = depset(defines), @@ -568,7 +605,7 @@ def _init_cc_compilation_context( headers = depset(declared_include_srcs), direct_public_headers = public_headers.headers, direct_private_headers = private_headers_artifacts, - direct_textual_headers = public_textual_headers, + direct_textual_headers = textual_headers.headers, module_map = module_map, pic_header_module = pic_header_module, header_module = header_module, @@ -586,7 +623,7 @@ def _init_cc_compilation_context( external_includes = depset(external_include_dirs), system_includes = depset(system_include_dirs_for_context), includes = depset(include_dirs_for_context), - virtual_to_original_headers = virtual_to_original_headers, + virtual_to_original_headers = depset(virtual_to_original_headers), dependent_cc_compilation_contexts = dependent_cc_compilation_contexts + implementation_deps, non_code_inputs = additional_inputs, defines = depset(defines), @@ -594,7 +631,7 @@ def _init_cc_compilation_context( headers = depset(declared_include_srcs), direct_public_headers = public_headers.headers, direct_private_headers = private_headers_artifacts, - direct_textual_headers = public_textual_headers, + direct_textual_headers = textual_headers.headers, module_map = module_map, pic_header_module = pic_header_module, header_module = header_module, diff --git a/src/main/starlark/builtins_bzl/common/cc/semantics.bzl b/src/main/starlark/builtins_bzl/common/cc/semantics.bzl index 501d5ef1ee831d..1f03459c67ceba 100644 --- a/src/main/starlark/builtins_bzl/common/cc/semantics.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/semantics.bzl @@ -21,6 +21,8 @@ _config = _builtins.toplevel.config # TODO: b/320980684 - Add a test that fails if this is flipped to True. USE_EXEC_ROOT_FOR_VIRTUAL_INCLUDES_SYMLINKS = False +STRIP_INCLUDE_PREFIX_APPLIES_TO_TEXTUAL_HEADERS = True + def _get_proto_aspects(): return [] diff --git a/src/main/starlark/tests/builtins_bzl/cc/basics/test/BUILD.builtin_test b/src/main/starlark/tests/builtins_bzl/cc/basics/test/BUILD.builtin_test new file mode 100644 index 00000000000000..f11c62693e58fe --- /dev/null +++ b/src/main/starlark/tests/builtins_bzl/cc/basics/test/BUILD.builtin_test @@ -0,0 +1,20 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + +package(features = ["layering_check"]) + +cc_library( + name = "lib", + srcs = ["nested/lib.cc"], + strip_include_prefix = "nested", + textual_hdrs = [ + "nested/lib.h", + "not_nested.h", + ], +) + +cc_test( + name = "test_include_textual", + srcs = ["test_include_textual.cc"], + deps = [":lib"], +) diff --git a/src/main/starlark/tests/builtins_bzl/cc/basics/test/nested/lib.cc b/src/main/starlark/tests/builtins_bzl/cc/basics/test/nested/lib.cc new file mode 100644 index 00000000000000..da31c2cea26fab --- /dev/null +++ b/src/main/starlark/tests/builtins_bzl/cc/basics/test/nested/lib.cc @@ -0,0 +1,21 @@ +// Copyright 2025 The Bazel Authors. All rights reserved. +// +// 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. + +// NOLINTBEGIN(build/include) +// That the following two includes don't have a directory is the point of this +// test. +#include "lib.h" +// NOLINTEND(build/include) + +int foo() { return 42; } diff --git a/src/main/starlark/tests/builtins_bzl/cc/basics/test/nested/lib.h b/src/main/starlark/tests/builtins_bzl/cc/basics/test/nested/lib.h new file mode 100644 index 00000000000000..0cc6d1d6063a63 --- /dev/null +++ b/src/main/starlark/tests/builtins_bzl/cc/basics/test/nested/lib.h @@ -0,0 +1,19 @@ +// Copyright 2025 The Bazel Authors. All rights reserved. +// +// 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. +#ifndef EXAMPLES_TEST_CC__LIB_H +#define EXAMPLES_TEST_CC__LIB_H + +int foo(); + +#endif // EXAMPLES_TEST_CC__LIB_H diff --git a/src/main/starlark/tests/builtins_bzl/cc/basics/test/not_nested.h b/src/main/starlark/tests/builtins_bzl/cc/basics/test/not_nested.h new file mode 100644 index 00000000000000..8e525f6624e713 --- /dev/null +++ b/src/main/starlark/tests/builtins_bzl/cc/basics/test/not_nested.h @@ -0,0 +1,20 @@ +// Copyright 2025 The Bazel Authors. All rights reserved. +// +// 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. + +#ifndef EXAMPLES_TEST_CC__NOT_NESTED_H +#define EXAMPLES_TEST_CC__NOT_NESTED_H + +#define NOT_NESTED 42 + +#endif // EXAMPLES_TEST_CC__NOT_NESTED_H diff --git a/src/main/starlark/tests/builtins_bzl/cc/basics/test/test_include_textual.cc b/src/main/starlark/tests/builtins_bzl/cc/basics/test/test_include_textual.cc new file mode 100644 index 00000000000000..9a0b5c5751b53a --- /dev/null +++ b/src/main/starlark/tests/builtins_bzl/cc/basics/test/test_include_textual.cc @@ -0,0 +1,30 @@ +// Copyright 2025 The Bazel Authors. All rights reserved. +// +// 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. + +// NOLINTBEGIN(build/include) +// That the following two includes don't have a directory is the point of this +// test. +#include "lib.h" +#include "not_nested.h" +// NOLINTEND(build/include) + +int main() { + if (foo() != 42) { + return 1; + } + if (NOT_NESTED != 42) { + return 2; + } + return 0; +}