Skip to content
Closed
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
18 changes: 8 additions & 10 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@ 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.32.0")
bazel_dep(name = "rules_cc", version = "0.2.3")

#python = use_extension("@rules_python//python/extensions:python.bzl", "python")
#python.toolchain(
# python_version = "3.10",
# is_default = True,
#)
Comment on lines +22 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the story with these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried everything to leave the toolchain registration for the old toolchain in the MODULE file, and have rules_python have a higher priority, but I failed.
These options I have come across during that. (I left them in thinking they might be interesting)

codechecker_extension = use_extension(
"//src:tools.bzl",
"module_register_default_codechecker",
)

use_repo(python_extension, "default_python_tools")

use_repo(codechecker_extension, "default_codechecker_tools")

register_toolchains("@default_python_tools//:python_toolchain")
56 changes: 45 additions & 11 deletions src/codechecker.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ load(
)
load(
"common.bzl",
"python_interpreter_tool",
"python_path",
"python_toolchain_type",
"version_specific_attributes",
Expand Down Expand Up @@ -97,14 +98,17 @@ 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,
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,
Expand All @@ -129,13 +133,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
Expand Down Expand Up @@ -226,27 +229,57 @@ 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",
"{PythonPath}": python_path(ctx), # "/usr/bin/env python3",
"{codechecker_bin}": CODECHECKER_BIN_PATH,
"{codechecker_files}": codechecker_files.short_path,
"{Severities}": " ".join(ctx.attr.severities),
},
)

# If we use our custom 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,
content = """
{python_bin} {test_script}
""".format(
python_bin = python_interpreter_path,
test_script = ctx.outputs.codechecker_test_script.short_path,
),
)

Comment on lines +249 to +271
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is exactly why I dont like this change.
Do we have any good reasons for this?

# 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)
Comment on lines +261 to +276
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nettle I understand you find this solution inelegant, and I won't contest it. However, would it be agreeable for you if we pushed this PR as-is, and found a nicer solution after the fact? This code is not the architectural backbone of the patch, so I think it is fine to improve it later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the problem with current implementation?
Well, it is also not nice :) but I dont think we should change "not nice" with another "not nice" :)

Copy link
Contributor Author

@furtib furtib Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot use the Python binary provided by rules_python with the old implementation (shebangs).
This is because a shebang must be an absolute path.
I'm not aware of any way to get the absolute path for the rules_python provided binary (remote executors further complicate this).
Also, doing this is not completely unheard of:

I guess a nicer solution would be if the script were a py_binary target in the BUILD file.

We could also just hardcode #!/usr/bin/env python3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I have tried out the py_binary solution in #199.
If we agree with that, then we should merge that one and return here afterwards.


return [
DefaultInfo(
files = depset(all_files),
runfiles = ctx.runfiles(files = run_files),
executable = ctx.outputs.codechecker_test_script,
executable = ctx.outputs.test_script_wrapper,
),
]

Expand Down Expand Up @@ -300,6 +333,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,
Expand Down
2 changes: 0 additions & 2 deletions src/codechecker_script.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!{PythonPath}

# Copyright 2023 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
30 changes: 29 additions & 1 deletion src/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,48 @@ 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"):
py_runtime_info = py_toolchain.py3_runtime_info
python_path = py_runtime_info.interpreter
elif hasattr(py_toolchain, "py3_runtime"):
py_runtime = py_toolchain.py3_runtime
python_path = py_runtime.interpreter_path
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:
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.
Expand Down
35 changes: 26 additions & 9 deletions src/per_file.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +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", "SOURCE_ATTR")
load(
"common.bzl",
"python_interpreter_tool",
"python_path",
"python_toolchain_type",
)
load(
"compile_commands.bzl",
"SourceFilesInfo",
Expand Down Expand Up @@ -54,11 +60,23 @@ 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]

Expand All @@ -69,8 +87,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,
Expand Down Expand Up @@ -124,9 +144,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,
Expand Down Expand Up @@ -225,14 +244,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,
)
2 changes: 0 additions & 2 deletions src/per_file_script.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!{PythonPath}

# Copyright 2023 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down