Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion cc/private/compile/cc_compilation_helper.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def _compute_public_headers(
if include_prefix and include_prefix.startswith("/"):
include_prefix = include_prefix[1:]

if strip_prefix == None and not include_prefix:
if (strip_prefix == None and not include_prefix) or not public_headers_artifacts:
# If CppOptions.experimentalStarlarkCompiling is enabled, then
# strip_include_prefix and include_prefix are not None.
# If the option is disabled, their default values (from CcCompilationHelper) are None.
Expand All @@ -121,6 +121,41 @@ def _compute_public_headers(
virtual_to_original_headers = [],
)

# When only stripping, we don't need to use _virtual_include path
if strip_prefix and not include_prefix:
module_map_headers = []
virtual_to_original_headers_list = []
include_paths = {}
for original_header in public_headers_artifacts:
repo_relative_path = _repo_relative_path(original_header)
if not repo_relative_path.startswith(strip_prefix):
if not must_use_strip_prefix:
module_map_headers.append(original_header)
continue

fail("header '{}' is not under the specified strip prefix '{}'".format(repo_relative_path, strip_prefix))
include_path = original_header.root.path
workspace_root = original_header.owner.workspace_root
if not include_path: # this is a source/non-generated file
include_path = workspace_root
elif workspace_root.startswith("external/"):
# in non-sibling repo layout, we need to add workspace root as well
include_path = include_path + "/" + workspace_root
include_path = get_relative_path(include_path, strip_prefix)
include_paths[include_path] = True
module_map_headers.append(original_header)

virtual_headers = module_map_headers + non_module_map_headers

# We can only handle 1 include path. In case there are more, fallback to _virtual_imports.
if len(include_paths.keys()) == 1:
return struct(
headers = virtual_headers,
module_map_headers = module_map_headers,
virtual_include_path = include_paths.keys()[0],
virtual_to_original_headers = virtual_to_original_headers_list,
)

module_map_headers = []
virtual_to_original_headers_list = []
source_package_path = package_source_root(label.workspace_name, label.package, is_sibling_repository_layout)
Expand Down
106 changes: 101 additions & 5 deletions tests/cc/common/cc_common_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ load("@rules_testing//lib:analysis_test.bzl", "test_suite")
load("@rules_testing//lib:truth.bzl", "matching")
load("@rules_testing//lib:util.bzl", "TestingAspectInfo", "util")
load("//cc:cc_library.bzl", "cc_library")
load("//cc/common:cc_info.bzl", "CcInfo")
load("//tests/cc/testutil:cc_analysis_test.bzl", "cc_analysis_test")
load("//tests/cc/testutil:cc_info_subject.bzl", "cc_info_subject")

Expand Down Expand Up @@ -108,12 +109,107 @@ def _test_isolated_includes_impl(env, target):
else:
subject.compilation_context().system_include_dirs().contains_at_least(expected_includes)

def _test_strip_include_prefix_no_virtual_includes(name):
"""Tests that strip_include_prefix without include_prefix avoids _virtual_includes."""
util.helper_target(
cc_library,
name = name + "/lib",
hdrs = ["v1/foo.h"],
strip_include_prefix = "v1",
)

cc_analysis_test(
name = name,
impl = _test_strip_include_prefix_no_virtual_includes_impl,
target = name + "/lib",
)

def _test_strip_include_prefix_no_virtual_includes_impl(env, target):
subject = cc_info_subject.from_target(env, target)

# The include dir should be the direct stripped path, not a _virtual_includes path.
include_dirs = subject.compilation_context().include_dirs()
include_dirs.contains_none_of(
[matching.custom(
"contains '_virtual_includes'",
lambda s: "_virtual_includes" in s,
)],
)

# The direct public headers should be the original source files, not symlinks.
direct_public_headers = target[CcInfo].compilation_context.direct_public_headers
for header in direct_public_headers:
if "_virtual_includes" in header.path:
env.expect.meta.add_failure(
"expected no _virtual_includes in header path",
"actual: {}".format(header.path),
)

def _test_strip_include_prefix_with_include_prefix_uses_virtual_includes(name):
"""Tests that strip_include_prefix with include_prefix still uses _virtual_includes."""
util.helper_target(
cc_library,
name = name + "/lib",
hdrs = ["v1/foo.h"],
strip_include_prefix = "v1",
include_prefix = "mylib",
)

cc_analysis_test(
name = name,
impl = _test_strip_include_prefix_with_include_prefix_uses_virtual_includes_impl,
target = name + "/lib",
)

def _test_strip_include_prefix_with_include_prefix_uses_virtual_includes_impl(env, target):
subject = cc_info_subject.from_target(env, target)

# When include_prefix is set, _virtual_includes should be used.
include_dirs = subject.compilation_context().include_dirs()
include_dirs.contains_at_least_predicates(
[matching.custom(
"contains '_virtual_includes'",
lambda s: "_virtual_includes" in s,
)],
)

def _test_strip_include_prefix_error_not_under_prefix(name):
"""Tests that headers not under strip_include_prefix produce an error."""
util.helper_target(
cc_library,
name = name + "/lib",
hdrs = ["other/foo.h"],
strip_include_prefix = "v1",
)

cc_analysis_test(
name = name,
impl = _test_strip_include_prefix_error_not_under_prefix_impl,
target = name + "/lib",
expect_failure = True,
)

def _test_strip_include_prefix_error_not_under_prefix_impl(env, target):
env.expect.that_target(target).failures().contains_predicate(
matching.custom(
"contains 'is not under the specified strip prefix'",
lambda s: "is not under the specified strip prefix" in s,
),
)

def cc_common_tests(name):
tests = [
_test_same_cc_file_twice,
_test_same_header_file_twice,
_test_isolated_includes,
]
if bazel_features.cc.cc_common_is_in_rules_cc:
tests.extend([
_test_strip_include_prefix_no_virtual_includes,
_test_strip_include_prefix_with_include_prefix_uses_virtual_includes,
_test_strip_include_prefix_error_not_under_prefix,
])
test_suite(
name = name,
tests = [
_test_same_cc_file_twice,
_test_same_header_file_twice,
_test_isolated_includes,
],
tests = tests,
)
8 changes: 8 additions & 0 deletions tests/cc/testutil/cc_info_subject.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ def _new_cc_compilation_context_subject(cc_compilation_context, meta):
self.actual.external_includes.to_list(),
self.meta.derive("external_include_dirs"),
),
direct_public_headers = lambda: subjects.collection(
self.actual.direct_public_headers,
self.meta.derive("direct_public_headers"),
),
headers = lambda: subjects.depset_file(
self.actual.headers,
meta = self.meta.derive("headers"),
),
)
return public

Expand Down