From 4ba7fc44dfd5ef68eb1bdd09aef2d77519bc895b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Wed, 4 Feb 2026 15:28:54 +0100 Subject: [PATCH 1/7] Add rules_python --- MODULE.bazel | 21 ++++++++------------- src/codechecker.bzl | 39 +++++++++++++++++++++++++++++---------- src/codechecker_script.py | 2 -- src/common.bzl | 18 ++++++++++++++++++ src/per_file.bzl | 32 +++++++++++++++++++++++--------- src/per_file_script.py | 2 -- 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index e6b4b5dc..804ed7f7 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,18 +16,13 @@ limitations under the License. module(name = "rules_codechecker") -python_extension = use_extension( - "//src:tools.bzl", - "module_register_default_python_toolchain", -) - +bazel_dep(name = "rules_python", version = "0.38.0") +bazel_dep(name = "rules_cc", version = "0.2.16") +#python = use_extension("@rules_python//python/extensions:python.bzl", "python") +#python.toolchain( +# python_version = "3.10", +# is_default = True, +#) codechecker_extension = use_extension( - "//src:tools.bzl", - "module_register_default_codechecker", -) - -use_repo(python_extension, "default_python_tools") - + "//src:tools.bzl", "module_register_default_codechecker") use_repo(codechecker_extension, "default_codechecker_tools") - -register_toolchains("@default_python_tools//:python_toolchain") diff --git a/src/codechecker.bzl b/src/codechecker.bzl index 6030b201..b52cf792 100644 --- a/src/codechecker.bzl +++ b/src/codechecker.bzl @@ -30,6 +30,7 @@ load( "python_path", "python_toolchain_type", "version_specific_attributes", + "python_interpreter_tool", ) load( "compile_commands.bzl", @@ -100,11 +101,10 @@ def _codechecker_impl(ctx): ctx.actions.expand_template( template = ctx.file._codechecker_script_template, output = ctx.outputs.codechecker_script, - is_executable = True, + is_executable = False, substitutions = { "{Mode}": "Run", "{Verbosity}": "DEBUG", - "{PythonPath}": python_path(ctx), # "/usr/bin/env python3", "{codechecker_bin}": CODECHECKER_BIN_PATH, "{compile_commands}": ctx.outputs.codechecker_commands.path, "{codechecker_skipfile}": ctx.outputs.codechecker_skipfile.path, @@ -129,13 +129,12 @@ def _codechecker_impl(ctx): codechecker_files, ctx.outputs.codechecker_log, ], - executable = ctx.outputs.codechecker_script, - arguments = [], - # executable = python_path(ctx), - # arguments = [ctx.outputs.codechecker_script.path], + executable = python_path(ctx), + tools = python_interpreter_tool(ctx), + arguments = [ctx.outputs.codechecker_script.path], mnemonic = "CodeChecker", progress_message = "CodeChecker %s" % str(ctx.label), - # use_default_shell_env = True, + use_default_shell_env = True, ) # List all files required at build and run (test) time @@ -233,20 +232,39 @@ def _codechecker_test_impl(ctx): substitutions = { "{Mode}": "Test", "{Verbosity}": "INFO", - "{PythonPath}": python_path(ctx), # "/usr/bin/env python3", "{codechecker_bin}": CODECHECKER_BIN_PATH, "{codechecker_files}": codechecker_files.short_path, "{Severities}": " ".join(ctx.attr.severities), }, ) + # For use in script the short path must be used (or absolute path) + # For use in executable the full path + python_interpreter_path = python_path(ctx) + if python_interpreter_tool(ctx) != []: + python_interpreter_path = python_interpreter_tool(ctx)[0].short_path + + ctx.actions.write( + output = ctx.outputs.test_script_wrapper, + is_executable = True, + content = """ + {} {} + """.format( + python_interpreter_path, + ctx.outputs.codechecker_test_script.short_path + ) + ) # Return test script and all required files - run_files = default_runfiles + [ctx.outputs.codechecker_test_script] + run_files = default_runfiles + [ + ctx.outputs.codechecker_test_script, + ctx.outputs.test_script_wrapper, + ] + python_interpreter_tool(ctx) + return [ DefaultInfo( files = depset(all_files), runfiles = ctx.runfiles(files = run_files), - executable = ctx.outputs.codechecker_test_script, + executable = ctx.outputs.test_script_wrapper, ), ] @@ -300,6 +318,7 @@ _codechecker_test = rule( "codechecker_script": "%{name}/codechecker_script.py", "codechecker_log": "%{name}/codechecker.log", "codechecker_test_script": "%{name}/codechecker_test_script.py", + "test_script_wrapper": "%{name}/test_script_wrapper.sh", }, toolchains = [python_toolchain_type()], test = True, diff --git a/src/codechecker_script.py b/src/codechecker_script.py index dd87a74a..d7769bea 100644 --- a/src/codechecker_script.py +++ b/src/codechecker_script.py @@ -1,5 +1,3 @@ -#!{PythonPath} - # Copyright 2023 Ericsson AB # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/common.bzl b/src/common.bzl index 198b800e..038e8188 100644 --- a/src/common.bzl +++ b/src/common.bzl @@ -61,12 +61,30 @@ def python_path(ctx): elif hasattr(py_toolchain, "py3_runtime"): py_runtime = py_toolchain.py3_runtime python_path = py_runtime.interpreter_path + # @rules_python does not provide interpreter_path, but interpreter.path + if python_path == None: + python_path = py_runtime.interpreter.path else: fail("The resolved Python toolchain does not provide a Python3 runtime.") if not python_path: fail("The resolved Python toolchain does not provide a Python3 interpreter.") return python_path +def python_interpreter_tool(ctx): + """ + Returns version specific Python interpreter object as only element in a list + that is appropriate for the Bazel version that is running + This list should be added to tools in ctx.action.run using it + """ + py_toolchain = ctx.toolchains[python_toolchain_type()] + python_interpreter = None + if hasattr(py_toolchain, "py3_runtime"): + py_runtime = py_toolchain.py3_runtime + python_interpreter = py_runtime.interpreter + if python_interpreter == None: + return [] + return [python_interpreter] + def warning(ctx, msg): """ Prints message if the debug tag is enabled. diff --git a/src/per_file.bzl b/src/per_file.bzl index 8a9be0fa..6f3c5bcb 100644 --- a/src/per_file.bzl +++ b/src/per_file.bzl @@ -20,7 +20,6 @@ for each translation unit. load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") load("codechecker_config.bzl", "get_config_file") -load("common.bzl", "SOURCE_ATTR") load( "compile_commands.bzl", "SourceFilesInfo", @@ -28,6 +27,12 @@ load( "compile_commands_impl", "platforms_transition", ) +load("common.bzl", + "python_path", + "python_toolchain_type", + "python_interpreter_tool", +) +load("@default_codechecker_tools//:defs.bzl", "BAZEL_VERSION") def _run_code_checker( ctx, @@ -54,11 +59,21 @@ def _run_code_checker( codechecker_log = ctx.actions.declare_file(codechecker_log_file_name) if "--ctu" in options: - inputs = [compile_commands_json, config_file] + sources_and_headers + inputs = [ + ctx.outputs.per_file_script, + compile_commands_json, + config_file, + ] + sources_and_headers else: # NOTE: we collect only headers, so CTU may not work! headers = depset(transitive = target[SourceFilesInfo].headers.to_list()) - inputs = depset([compile_commands_json, config_file, src], transitive = [headers]) + inputs = depset( + [ + ctx.outputs.per_file_script, + compile_commands_json, + config_file, + src, + ], transitive = [headers]) outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] @@ -69,8 +84,10 @@ def _run_code_checker( ctx.actions.run( inputs = inputs, outputs = outputs, - executable = ctx.outputs.per_file_script, + executable = python_path(ctx), + tools = python_interpreter_tool(ctx), arguments = [ + ctx.outputs.per_file_script.path, data_dir, src.path, codechecker_log.path, @@ -124,9 +141,8 @@ def _create_wrapper_script(ctx, options, compile_commands_json, config_file): ctx.actions.expand_template( template = ctx.file._per_file_script_template, output = ctx.outputs.per_file_script, - is_executable = True, + is_executable = False, substitutions = { - "{PythonPath}": ctx.attr._python_runtime[PyRuntimeInfo].interpreter_path, "{compile_commands_json}": compile_commands_json.path, "{codechecker_args}": options_str, "{config_file}": config_file.path, @@ -225,14 +241,12 @@ per_file_test = rule( default = ":per_file_script.py", allow_single_file = True, ), - "_python_runtime": attr.label( - default = "@default_python_tools//:py3_runtime", - ), }, outputs = { "compile_commands": "%{name}/compile_commands.json", "test_script": "%{name}/test_script.sh", "per_file_script": "%{name}/per_file_script.py", }, + toolchains = [python_toolchain_type()], test = True, ) diff --git a/src/per_file_script.py b/src/per_file_script.py index 1cfad4b9..378d0925 100644 --- a/src/per_file_script.py +++ b/src/per_file_script.py @@ -1,5 +1,3 @@ -#!{PythonPath} - # Copyright 2023 Ericsson AB # # Licensed under the Apache License, Version 2.0 (the "License"); From a5b167dca741a9e86968e9cdfcfe7f54db0ede83 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Wed, 4 Feb 2026 15:43:12 +0100 Subject: [PATCH 2/7] Run buildifier on new changes --- MODULE.bazel | 5 ++++- src/codechecker.bzl | 9 +++++---- src/common.bzl | 1 + src/per_file.bzl | 19 +++++++++++-------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 804ed7f7..cef69f18 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -18,11 +18,14 @@ module(name = "rules_codechecker") bazel_dep(name = "rules_python", version = "0.38.0") bazel_dep(name = "rules_cc", version = "0.2.16") + #python = use_extension("@rules_python//python/extensions:python.bzl", "python") #python.toolchain( # python_version = "3.10", # is_default = True, #) codechecker_extension = use_extension( - "//src:tools.bzl", "module_register_default_codechecker") + "//src:tools.bzl", + "module_register_default_codechecker", +) use_repo(codechecker_extension, "default_codechecker_tools") diff --git a/src/codechecker.bzl b/src/codechecker.bzl index b52cf792..5736b3e1 100644 --- a/src/codechecker.bzl +++ b/src/codechecker.bzl @@ -27,10 +27,10 @@ load( ) load( "common.bzl", + "python_interpreter_tool", "python_path", "python_toolchain_type", "version_specific_attributes", - "python_interpreter_tool", ) load( "compile_commands.bzl", @@ -237,6 +237,7 @@ def _codechecker_test_impl(ctx): "{Severities}": " ".join(ctx.attr.severities), }, ) + # For use in script the short path must be used (or absolute path) # For use in executable the full path python_interpreter_path = python_path(ctx) @@ -250,15 +251,15 @@ def _codechecker_test_impl(ctx): {} {} """.format( python_interpreter_path, - ctx.outputs.codechecker_test_script.short_path - ) + ctx.outputs.codechecker_test_script.short_path, + ), ) # Return test script and all required files run_files = default_runfiles + [ ctx.outputs.codechecker_test_script, ctx.outputs.test_script_wrapper, - ] + python_interpreter_tool(ctx) + ] + python_interpreter_tool(ctx) return [ DefaultInfo( diff --git a/src/common.bzl b/src/common.bzl index 038e8188..387bad4d 100644 --- a/src/common.bzl +++ b/src/common.bzl @@ -61,6 +61,7 @@ def python_path(ctx): elif hasattr(py_toolchain, "py3_runtime"): py_runtime = py_toolchain.py3_runtime python_path = py_runtime.interpreter_path + # @rules_python does not provide interpreter_path, but interpreter.path if python_path == None: python_path = py_runtime.interpreter.path diff --git a/src/per_file.bzl b/src/per_file.bzl index 6f3c5bcb..6c1fe809 100644 --- a/src/per_file.bzl +++ b/src/per_file.bzl @@ -19,7 +19,14 @@ for each translation unit. load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load("@default_codechecker_tools//:defs.bzl", "BAZEL_VERSION") load("codechecker_config.bzl", "get_config_file") +load( + "common.bzl", + "python_interpreter_tool", + "python_path", + "python_toolchain_type", +) load( "compile_commands.bzl", "SourceFilesInfo", @@ -27,12 +34,6 @@ load( "compile_commands_impl", "platforms_transition", ) -load("common.bzl", - "python_path", - "python_toolchain_type", - "python_interpreter_tool", -) -load("@default_codechecker_tools//:defs.bzl", "BAZEL_VERSION") def _run_code_checker( ctx, @@ -63,7 +64,7 @@ def _run_code_checker( ctx.outputs.per_file_script, compile_commands_json, config_file, - ] + sources_and_headers + ] + sources_and_headers else: # NOTE: we collect only headers, so CTU may not work! headers = depset(transitive = target[SourceFilesInfo].headers.to_list()) @@ -73,7 +74,9 @@ def _run_code_checker( compile_commands_json, config_file, src, - ], transitive = [headers]) + ], + transitive = [headers], + ) outputs = [clang_tidy_plist, clangsa_plist, codechecker_log] From f45d8d6e48816a08a6e98d18c48a65a1a86ee76b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Mon, 23 Feb 2026 12:26:12 +0100 Subject: [PATCH 3/7] Finetune dependency versions for very old bazel versions --- MODULE.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index cef69f18..a02540df 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,8 +16,8 @@ limitations under the License. module(name = "rules_codechecker") -bazel_dep(name = "rules_python", version = "0.38.0") -bazel_dep(name = "rules_cc", version = "0.2.16") +bazel_dep(name = "rules_python", version = "0.32.0") +bazel_dep(name = "rules_cc", version = "0.2.3") #python = use_extension("@rules_python//python/extensions:python.bzl", "python") #python.toolchain( From 000b5549b15f8dd93d9134d1ef45614a94960b6b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Tue, 24 Feb 2026 09:30:43 +0100 Subject: [PATCH 4/7] Comment code --- src/codechecker.bzl | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/codechecker.bzl b/src/codechecker.bzl index 5736b3e1..c0a8dde4 100644 --- a/src/codechecker.bzl +++ b/src/codechecker.bzl @@ -98,6 +98,10 @@ def _codechecker_impl(ctx): config_file, codechecker_env = get_config_file(ctx) codechecker_files = ctx.actions.declare_directory(ctx.label.name + "/codechecker-files") + # For hermeticism the executable should be a python interpreter + # provided by Bazel. This interpreter cannot always be added to the script + # as a shebang. Because of this this script is explicitly marked as + # non executable. ctx.actions.expand_template( template = ctx.file._codechecker_script_template, output = ctx.outputs.codechecker_script, @@ -225,10 +229,14 @@ def _codechecker_test_impl(ctx): fail("Execution results required for codechecker test are not available") # Create test script from template + # For hermeticism the executable should be a python interpreter + # provided by Bazel. This interpreter cannot always be added to the script + # as a shebang. Because of this this script is explicitly marked as + # non executable. ctx.actions.expand_template( template = ctx.file._codechecker_script_template, output = ctx.outputs.codechecker_test_script, - is_executable = True, + is_executable = False, substitutions = { "{Mode}": "Test", "{Verbosity}": "INFO", @@ -238,12 +246,18 @@ def _codechecker_test_impl(ctx): }, ) - # For use in script the short path must be used (or absolute path) - # For use in executable the full path + # If we use our toolchain, the path will be an absolute path, + # executable by bazel python_interpreter_path = python_path(ctx) + + # If we can find an interpreter tool provided by bazel, + # use that as an executable if python_interpreter_tool(ctx) != []: python_interpreter_path = python_interpreter_tool(ctx)[0].short_path + # Since we cannot give parameters to the runfiles + # we wrap the executable (a python binary) in a script. + # In the script we give the CodeChecker script as an argument. ctx.actions.write( output = ctx.outputs.test_script_wrapper, is_executable = True, From ae8da57dc6d46d0abec69cca85286abb256e88d1 Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Tue, 24 Feb 2026 10:13:45 +0100 Subject: [PATCH 5/7] =?UTF-8?q?Make=20path=20selection=20more=20robust?= =?UTF-8?q?=C3=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common.bzl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/common.bzl b/src/common.bzl index 387bad4d..cbca0d67 100644 --- a/src/common.bzl +++ b/src/common.bzl @@ -53,6 +53,11 @@ def python_toolchain_type(): def python_path(ctx): """ Returns version specific Python path + + Args: + ctx: The context variable. + Returns: + A string containing the path to a python binary """ py_toolchain = ctx.toolchains[python_toolchain_type()] if hasattr(py_toolchain, "py3_runtime_info"): @@ -60,11 +65,15 @@ def python_path(ctx): python_path = py_runtime_info.interpreter elif hasattr(py_toolchain, "py3_runtime"): py_runtime = py_toolchain.py3_runtime - python_path = py_runtime.interpreter_path - - # @rules_python does not provide interpreter_path, but interpreter.path - if python_path == None: + if ( + hasattr(py_runtime, "interpreter") and + hasattr(py_runtime.interpreter, "path") + ): python_path = py_runtime.interpreter.path + elif hasattr(py_runtime, "interpreter_path"): + python_path = py_runtime.interpreter_path + else: + fail("Python interpreter path could not be resolved.") else: fail("The resolved Python toolchain does not provide a Python3 runtime.") if not python_path: From 960746c5800d40786e53db947169652c61714f5b Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Tue, 24 Feb 2026 11:59:16 +0100 Subject: [PATCH 6/7] Update src/codechecker.bzl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kristóf Umann --- src/codechecker.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codechecker.bzl b/src/codechecker.bzl index c0a8dde4..b26de33c 100644 --- a/src/codechecker.bzl +++ b/src/codechecker.bzl @@ -246,7 +246,7 @@ def _codechecker_test_impl(ctx): }, ) - # If we use our toolchain, the path will be an absolute path, + # If we use our custom toolchain, the path will be an absolute path, # executable by bazel python_interpreter_path = python_path(ctx) From cdd908672b04e234094657d90cd22b59cbbaaebe Mon Sep 17 00:00:00 2001 From: "F.Tibor" Date: Wed, 25 Feb 2026 13:44:16 +0100 Subject: [PATCH 7/7] make format easier to read --- src/codechecker.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/codechecker.bzl b/src/codechecker.bzl index b26de33c..c4d859ea 100644 --- a/src/codechecker.bzl +++ b/src/codechecker.bzl @@ -262,10 +262,10 @@ def _codechecker_test_impl(ctx): output = ctx.outputs.test_script_wrapper, is_executable = True, content = """ - {} {} + {python_bin} {test_script} """.format( - python_interpreter_path, - ctx.outputs.codechecker_test_script.short_path, + python_bin = python_interpreter_path, + test_script = ctx.outputs.codechecker_test_script.short_path, ), )