From 00dfc34b28e4e187e9053f69e85db859237ebd9e Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 24 Jul 2025 18:49:52 +0200 Subject: [PATCH 1/7] Fix per-file to incremental analysis Why: Incremental analysis doesn't work with the per-file rule What: Make inputs dependenies as transitive Addresses: #21 --- src/code_checker.bzl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/code_checker.bzl b/src/code_checker.bzl index 2d0c81b4..b8cfdca4 100644 --- a/src/code_checker.bzl +++ b/src/code_checker.bzl @@ -62,7 +62,7 @@ def _run_code_checker( clangsa_plist = ctx.actions.declare_file(clangsa_plist_file_name) codechecker_log = ctx.actions.declare_file(codechecker_log_file_name) - inputs = [compile_commands_json] + sources_and_headers + inputs = depset([compile_commands_json, src], transitive = [sources_and_headers]) outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] # Create CodeChecker wrapper script @@ -274,20 +274,19 @@ def _compile_commands_impl(ctx): return compile_commands_json def _collect_all_sources_and_headers(ctx): - all_files = [] - headers = depset() + sources_and_headers = depset() for target in ctx.attr.targets: if not CcInfo in target: continue if CompileInfo in target: if hasattr(target[CompileInfo], "arguments"): srcs = target[CompileInfo].arguments.keys() - all_files += srcs compilation_context = target[CcInfo].compilation_context - headers = depset( - transitive = [headers, compilation_context.headers], + srcs_hdrs = depset( + srcs, + transitive = [compilation_context.headers], ) - sources_and_headers = all_files + headers.to_list() + sources_and_headers = depset(transitive = [srcs_hdrs]) return sources_and_headers def _code_checker_impl(ctx): From 73a1ee8c27a47a8a9811c6fc505e35cab50a2f9f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 24 Jul 2025 18:49:52 +0200 Subject: [PATCH 2/7] Fix per-file incremental analysis Why: Incremental analysis doesn't work with the per-file rule What: Make inputs dependenies as transitive per target Addresses: #21 --- src/code_checker.bzl | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/code_checker.bzl b/src/code_checker.bzl index b8cfdca4..90ffb9bb 100644 --- a/src/code_checker.bzl +++ b/src/code_checker.bzl @@ -62,7 +62,7 @@ def _run_code_checker( clangsa_plist = ctx.actions.declare_file(clangsa_plist_file_name) codechecker_log = ctx.actions.declare_file(codechecker_log_file_name) - inputs = depset([compile_commands_json, src], transitive = [sources_and_headers]) + inputs = depset([compile_commands_json, src], transitive = sources_and_headers) outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] # Create CodeChecker wrapper script @@ -273,25 +273,23 @@ def _compile_commands_impl(ctx): ) return compile_commands_json -def _collect_all_sources_and_headers(ctx): - sources_and_headers = depset() - for target in ctx.attr.targets: - if not CcInfo in target: - continue - if CompileInfo in target: - if hasattr(target[CompileInfo], "arguments"): - srcs = target[CompileInfo].arguments.keys() - compilation_context = target[CcInfo].compilation_context - srcs_hdrs = depset( - srcs, - transitive = [compilation_context.headers], - ) - sources_and_headers = depset(transitive = [srcs_hdrs]) - return sources_and_headers +def _collect_sources_and_headers(target): + if not CcInfo in target: + return [] + if not CompileInfo in target: + return [] + if not hasattr(target[CompileInfo], "arguments"): + return [] + srcs = target[CompileInfo].arguments.keys() + compilation_context = target[CcInfo].compilation_context + sources_and_headers = depset( + srcs, + transitive = [compilation_context.headers], + ) + return [sources_and_headers] def _code_checker_impl(ctx): compile_commands_json = _compile_commands_impl(ctx) - sources_and_headers = _collect_all_sources_and_headers(ctx) options = ctx.attr.default_options + ctx.attr.options all_files = [compile_commands_json] for target in ctx.attr.targets: @@ -302,6 +300,7 @@ def _code_checker_impl(ctx): srcs = target[CompileInfo].arguments.keys() all_files += srcs compilation_context = target[CcInfo].compilation_context + sources_and_headers = _collect_sources_and_headers(target) for src in srcs: args = target[CompileInfo].arguments[src] outputs = _run_code_checker( From 133b96e688436bfb02b1ea3dcac24d1b52a3c0e2 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 24 Jul 2025 20:44:55 +0200 Subject: [PATCH 3/7] Add dependency only on headers --- src/code_checker.bzl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/code_checker.bzl b/src/code_checker.bzl index 90ffb9bb..50eb644c 100644 --- a/src/code_checker.bzl +++ b/src/code_checker.bzl @@ -62,7 +62,9 @@ def _run_code_checker( clangsa_plist = ctx.actions.declare_file(clangsa_plist_file_name) codechecker_log = ctx.actions.declare_file(codechecker_log_file_name) - inputs = depset([compile_commands_json, src], transitive = sources_and_headers) + # NOTE: we collect only headers, so CTU may not work! + headers = depset([src], transitive = [compilation_context.headers]) + inputs = depset([compile_commands_json, src], transitive = [headers]) outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] # Create CodeChecker wrapper script From d9e3c39dd9bca492e37d27c1da51537365480c28 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 29 Jul 2025 20:40:02 +0200 Subject: [PATCH 4/7] Minimize changes --- src/code_checker.bzl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/code_checker.bzl b/src/code_checker.bzl index 50eb644c..186b8b88 100644 --- a/src/code_checker.bzl +++ b/src/code_checker.bzl @@ -48,8 +48,7 @@ def _run_code_checker( label, options, compile_commands_json, - compilation_context, - sources_and_headers): + compilation_context): # Define Plist and log file names data_dir = ctx.attr.name + "/data" file_name_params = (data_dir, src.path.replace("/", "-")) @@ -275,20 +274,23 @@ def _compile_commands_impl(ctx): ) return compile_commands_json -def _collect_sources_and_headers(target): - if not CcInfo in target: - return [] - if not CompileInfo in target: - return [] - if not hasattr(target[CompileInfo], "arguments"): - return [] - srcs = target[CompileInfo].arguments.keys() - compilation_context = target[CcInfo].compilation_context - sources_and_headers = depset( - srcs, - transitive = [compilation_context.headers], - ) - return [sources_and_headers] +def _collect_all_sources_and_headers(ctx): + # NOTE: we are not using this function + all_files = [] + headers = depset() + for target in ctx.attr.targets: + if not CcInfo in target: + continue + if CompileInfo in target: + if hasattr(target[CompileInfo], "arguments"): + srcs = target[CompileInfo].arguments.keys() + all_files += srcs + compilation_context = target[CcInfo].compilation_context + headers = depset( + transitive = [headers, compilation_context.headers], + ) + sources_and_headers = all_files + headers.to_list() + return sources_and_headers def _code_checker_impl(ctx): compile_commands_json = _compile_commands_impl(ctx) @@ -302,7 +304,6 @@ def _code_checker_impl(ctx): srcs = target[CompileInfo].arguments.keys() all_files += srcs compilation_context = target[CcInfo].compilation_context - sources_and_headers = _collect_sources_and_headers(target) for src in srcs: args = target[CompileInfo].arguments[src] outputs = _run_code_checker( @@ -313,7 +314,6 @@ def _code_checker_impl(ctx): options, compile_commands_json, compilation_context, - sources_and_headers, ) all_files += outputs ctx.actions.write( From c7de1bbddf74345aed86703217d685e5fb9c4594 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 25 Aug 2025 13:17:15 +0200 Subject: [PATCH 5/7] Disable optimization, when ctu is enabled --- src/code_checker.bzl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/code_checker.bzl b/src/code_checker.bzl index 186b8b88..efff088d 100644 --- a/src/code_checker.bzl +++ b/src/code_checker.bzl @@ -48,7 +48,8 @@ def _run_code_checker( label, options, compile_commands_json, - compilation_context): + compilation_context, + sources_and_headers): # Define Plist and log file names data_dir = ctx.attr.name + "/data" file_name_params = (data_dir, src.path.replace("/", "-")) @@ -61,9 +62,13 @@ def _run_code_checker( clangsa_plist = ctx.actions.declare_file(clangsa_plist_file_name) codechecker_log = ctx.actions.declare_file(codechecker_log_file_name) - # NOTE: we collect only headers, so CTU may not work! - headers = depset([src], transitive = [compilation_context.headers]) - inputs = depset([compile_commands_json, src], transitive = [headers]) + if "--ctu" in options: + inputs = [compile_commands_json] + sources_and_headers + else: + # NOTE: we collect only headers, so CTU may not work! + headers = depset([src], transitive = [compilation_context.headers]) + inputs = depset([compile_commands_json, src], transitive = [headers]) + outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] # Create CodeChecker wrapper script @@ -275,7 +280,7 @@ def _compile_commands_impl(ctx): return compile_commands_json def _collect_all_sources_and_headers(ctx): - # NOTE: we are not using this function + # NOTE: we are only using this function for CTU all_files = [] headers = depset() for target in ctx.attr.targets: @@ -294,6 +299,7 @@ def _collect_all_sources_and_headers(ctx): def _code_checker_impl(ctx): compile_commands_json = _compile_commands_impl(ctx) + sources_and_headers = _collect_all_sources_and_headers(ctx) options = ctx.attr.default_options + ctx.attr.options all_files = [compile_commands_json] for target in ctx.attr.targets: @@ -314,6 +320,7 @@ def _code_checker_impl(ctx): options, compile_commands_json, compilation_context, + sources_and_headers ) all_files += outputs ctx.actions.write( From 91b6e68562637427126f2f76ade350f936a8fa1d Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Fri, 29 Aug 2025 13:41:14 +0200 Subject: [PATCH 6/7] Activate test --- test/unit/caching/test_caching.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/unit/caching/test_caching.py b/test/unit/caching/test_caching.py index 4690f06b..de6e4b30 100644 --- a/test/unit/caching/test_caching.py +++ b/test/unit/caching/test_caching.py @@ -73,10 +73,8 @@ def test_bazel_test_code_checker_caching(self): self.fail(f"File not found!") ret, _, stderr = self.run_command(f"bazel build {target} --subcommands") self.assertEqual(ret, 0) - # FIXME: This should be 1; 2 means that both .cpp files were reanalyzed - # despite only one of them being changed. self.assertEqual( - stderr.count(f"SUBCOMMAND: # {target} [action 'CodeChecker"), 2 + stderr.count(f"SUBCOMMAND: # {target} [action 'CodeChecker"), 1 ) def test_bazel_test_code_checker_ctu_caching(self): From 289eeb0a782e644a9a2985e19005622f09238478 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Wed, 3 Sep 2025 18:59:14 +0200 Subject: [PATCH 7/7] Update code_checker.bzlAdd back missing comma --- src/code_checker.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code_checker.bzl b/src/code_checker.bzl index efff088d..1f0d0424 100644 --- a/src/code_checker.bzl +++ b/src/code_checker.bzl @@ -320,7 +320,7 @@ def _code_checker_impl(ctx): options, compile_commands_json, compilation_context, - sources_and_headers + sources_and_headers, ) all_files += outputs ctx.actions.write(