Skip to content

Commit

Permalink
feat(aspect): support toolchain feature external_include_paths
Browse files Browse the repository at this point in the history
When this feature is active, some include paths in `CompilationContext` are
automatically moved to `external_includes`. This allows Bazel to pass them
to the compiler via `-isystem` to silence compiler warnings for
external headers.

Resolves: #299
  • Loading branch information
martis42 committed Dec 17, 2024
1 parent 89e1220 commit a95b336
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/analyze_includes/system_under_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def replace_dot(paths: list[str]) -> list[str]:
return (
replace_dot(target_info["includes"])
+ replace_dot(target_info["quote_includes"])
+ replace_dot(target_info["external_includes"])
+ replace_dot(target_info["system_includes"])
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
],
"includes": ["."],
"quote_includes": ["some/dir"],
"external_includes": ["external/dir"],
"system_includes": ["another/dir"],
"header_files": [
"self/header_1.h",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"defines": [],
"includes": [],
"quote_includes": [],
"external_includes": [],
"system_includes": [],
"header_files": [],
"target": "//:foo"
Expand Down
2 changes: 1 addition & 1 deletion src/analyze_includes/test/system_under_inspection_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def test_load_full_file(self) -> None:
self.assertEqual(sui.impl_deps[1].header_files, ["private/dep/bar_1.h", "private/dep/bar_2.h"])
self.assertEqual(sui.impl_deps[1].usage.usage, UsageStatus.NONE)

self.assertEqual(sui.include_paths, ["", "some/dir", "another/dir"])
self.assertEqual(sui.include_paths, ["", "some/dir", "external/dir", "another/dir"])
self.assertEqual(sui.defines, ["SOME_DEFINE", "ANOTHER_DEFINE 42"])

def test_load_empty_file(self) -> None:
Expand Down
2 changes: 2 additions & 0 deletions src/aspect/dwyu.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ def _process_target(ctx, target, defines, output_path, is_target_under_inspectio
args.add("--output", processing_output)
args.add_all("--header_files", header_files, expand_directories = True, omit_if_empty = False)
if is_target_under_inspection:
external_includes = cc_context.external_includes if hasattr(cc_context, "external_includes") else []
args.add_all("--includes", cc_context.includes, omit_if_empty = False)
args.add_all("--quote_includes", cc_context.quote_includes, omit_if_empty = False)
args.add_all("--external_includes", external_includes, omit_if_empty = False)
args.add_all("--system_includes", cc_context.system_includes, omit_if_empty = False)
args.add_all("--defines", defines)
if verbose:
Expand Down
9 changes: 9 additions & 0 deletions src/aspect/process_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def cli() -> Namespace:
help="Include paths available to the compiler for quoted include statements."
" Only relevant when analyzing the target under inspection itself. This is irrelevant for dependencies.",
)
parser.add_argument(
"--external_includes",
nargs="*",
help="Include paths available to the compiler for include statements pointing to headers from external targets."
" Only relevant when analyzing the target under inspection itself. This is irrelevant for dependencies.",
)
parser.add_argument(
"--system_includes",
nargs="*",
Expand Down Expand Up @@ -55,6 +61,7 @@ def main(args: Namespace) -> int:
logging.debug(f"Header files '{args.header_files}'")
logging.debug(f"Includes '{args.includes}'")
logging.debug(f"Quote includes '{args.quote_includes}'")
logging.debug(f"External includes '{args.external_includes}'")
logging.debug(f"System includes '{args.system_includes}'")
logging.debug(f"Defines '{args.defines}'")

Expand All @@ -63,6 +70,8 @@ def main(args: Namespace) -> int:
output["includes"] = args.includes
if args.quote_includes is not None:
output["quote_includes"] = args.quote_includes
if args.external_includes is not None:
output["external_includes"] = args.external_includes
if args.system_includes is not None:
output["system_includes"] = args.system_includes
if args.defines is not None:
Expand Down
15 changes: 11 additions & 4 deletions src/utils/utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,29 @@ def print_compilation_context(cc_info, headline = None):
"""
Print CcInfo's compilation_context in a structured way.
print debugging is eased by those flags which prevent print statements being omitted on subsequent execution
Debugging is eased by those flags which prevent print statements being omitted on subsequent execution:
--nokeep_state_after_build
--notrack_incremental_state
Args:
cc_info: A CompilationContext object
headline: Optional context information displayed before the CompilationContext information
"""
cc = cc_info.compilation_context
headline_str = "\n" + headline if headline else ""
external_includes = cc.external_includes if hasattr(cc, "external_includes") else "NA"
print("""{headline}
defines : {d}
local_defines : {ld}
headers : {h}
direct_headers : {dh}
direct_private_headers : {d_priv_h}
direct_public_headers : {d_pub_h}
direct_textual_headers : {dth}
framework_includes : {fi}
headers : {h}
includes : {i}
local_defines : {ld}
framework_includes : {fi}
quote_includes : {qi}
external_includes : {ei}
system_includes : {si}
""".rstrip().format(
headline = headline_str,
Expand All @@ -38,5 +44,6 @@ def print_compilation_context(cc_info, headline = None):
i = cc.includes,
ld = cc.local_defines,
qi = cc.quote_includes,
ei = external_includes,
si = cc.system_includes,
))
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from expected_result import ExpectedResult
from test_case import CompatibleVersions, TestCaseBase

from test.support.result import Result


class TestCase(TestCaseBase):
@property
def compatible_bazel_versions(self) -> CompatibleVersions:
"""
The 'external_include_paths' feature exists for Bazel < 7.0.0. But the corresponding information was not added
to CompilationContext, which hides it from an aspect. This information was added in Bazel 7.0.0.
"""
return CompatibleVersions(minimum="7.0.0")

def execute_test_logic(self) -> Result:
"""
The 'external_include_paths' toolchain feature automatically moves some include paths in the Bazel
CompilationContext to the 'external_includes' list so they can be provided to the compiler via '-isystem'.
This allows silencing compiler warnings originating from those external headers.
"""
expected = ExpectedResult(success=True)
actual = self._run_dwyu(
target=["//complex_includes:all", "@complex_includes_test_repo//..."],
extra_args=["--features=external_include_paths"],
aspect=self.default_aspect,
)

return self._check_result(actual=actual, expected=expected)

0 comments on commit a95b336

Please sign in to comment.