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
28 changes: 21 additions & 7 deletions src/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
load(":codechecker_toolchain.bzl", "codechecker_toolchain")

# Tool filter compile_commands.json file
# In bazel 6 we use our own python toolchain,
# For targets using WORKSPACE files we use our own python toolchain,
# therefore we cannot load from rules_python.
# buildifier: disable=native-py
py_binary(
Expand All @@ -23,12 +23,26 @@ py_binary(
visibility = ["//visibility:public"],
)

# Build & Test script template
exports_files(
[
"codechecker_script.py",
"per_file_script.py",
],
# For targets using WORKSPACE files we use our own python toolchain,
# therefore we cannot load from rules_python.
# buildifier: disable=native-py
py_binary(
name = "per_file_script",
srcs = ["per_file_script.py"],
main = "per_file_script.py",
# Bazel 6 doesn't see this otherwise
visibility = ["//visibility:public"],
)

# For targets using WORKSPACE files we use our own python toolchain,
# therefore we cannot load from rules_python.
# buildifier: disable=native-py
py_binary(
name = "codechecker_script",
srcs = ["codechecker_script.py"],
main = "codechecker_script.py",
# Bazel 6 doesn't see this otherwise
visibility = ["//visibility:public"],
)

# The following are flags and default values for clang_tidy_aspect
Expand Down
110 changes: 61 additions & 49 deletions src/codechecker.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,26 @@ def _codechecker_impl(ctx):
info = ctx.toolchains["//src:toolchain_type"].codecheckerinfo

codechecker_files = ctx.actions.declare_directory(ctx.label.name + "/codechecker-files")
ctx.actions.expand_template(
template = ctx.file._codechecker_script_template,
output = ctx.outputs.codechecker_script,
is_executable = True,
substitutions = {
"{Mode}": "Run",
"{Verbosity}": "DEBUG",
"{clang_bin}": info.clangsa.path,
"{clang_tidy_bin}": info.clang_tidy.path,
"{codechecker_analyze}": " ".join(ctx.attr.analyze),
"{codechecker_bin}": info.codechecker.path,
"{codechecker_config}": config_file.path,
"{codechecker_env}": codechecker_env,
"{codechecker_files}": codechecker_files.path,
"{codechecker_log}": ctx.outputs.codechecker_log.path,
"{codechecker_skipfile}": ctx.outputs.codechecker_skipfile.path,
"{compile_commands}": ctx.outputs.codechecker_commands.path,
},

bazel_constants_json = ctx.actions.declare_file(ctx.label.name + "_bazel_build_constants.json")
codechecker_data = {
"Mode": "Run",
"Verbosity": "DEBUG",
"clang_bin": info.clangsa.path,
"clang_tidy_bin": info.clang_tidy.path,
"codechecker_analyze": ctx.attr.analyze, # this is a list
"codechecker_bin": info.codechecker.path,
"codechecker_config": config_file.path,
"codechecker_env": codechecker_env,
"codechecker_files": codechecker_files.path,
"codechecker_log": ctx.outputs.codechecker_log.path,
"codechecker_skipfile": ctx.outputs.codechecker_skipfile.path,
"compile_commands": ctx.outputs.codechecker_commands.path,
}

ctx.actions.write(
output = bazel_constants_json,
content = json.encode_indent(codechecker_data),
)

ctx.actions.run(
Expand All @@ -122,20 +124,18 @@ def _codechecker_impl(ctx):
info.codechecker,
info.clangsa,
info.clang_tidy,
ctx.outputs.codechecker_script,
ctx.outputs.codechecker_commands,
ctx.outputs.codechecker_skipfile,
config_file,
bazel_constants_json,
] + source_files,
),
outputs = [
codechecker_files,
ctx.outputs.codechecker_log,
],
executable = ctx.outputs.codechecker_script,
arguments = [],
# executable = python_path(ctx),
# arguments = [ctx.outputs.codechecker_script.path],
executable = ctx.executable._codechecker_script,
arguments = [bazel_constants_json.path],
mnemonic = "CodeChecker",
progress_message = "CodeChecker %s" % str(ctx.label),
# use_default_shell_env = True,
Expand All @@ -148,7 +148,6 @@ def _codechecker_impl(ctx):
ctx.outputs.codechecker_skipfile,
config_file,
codechecker_files,
ctx.outputs.codechecker_script,
ctx.outputs.codechecker_log,
] + source_files

Expand Down Expand Up @@ -190,9 +189,10 @@ codechecker = rule(
],
doc = "List of compilable targets which should be checked.",
),
"_codechecker_script_template": attr.label(
default = ":codechecker_script.py",
allow_single_file = True,
"_codechecker_script": attr.label(
default = ":codechecker_script",
executable = True,
cfg = "exec",
),
"_compile_commands_filter": attr.label(
allow_files = True,
Expand All @@ -204,7 +204,6 @@ codechecker = rule(
outputs = {
"codechecker_commands": "%{name}/codechecker_commands.json",
"codechecker_log": "%{name}/codechecker.log",
"codechecker_script": "%{name}/codechecker_script.py",
"codechecker_skipfile": "%{name}/codechecker_skipfile.cfg",
"compile_commands": "%{name}/compile_commands.json",
},
Expand Down Expand Up @@ -233,34 +232,48 @@ def _codechecker_test_impl(ctx):

info = ctx.toolchains["//src:toolchain_type"].codecheckerinfo

# Create test script from template
ctx.actions.expand_template(
template = ctx.file._codechecker_script_template,
output = ctx.outputs.codechecker_test_script,
bazel_constants_json = ctx.actions.declare_file(ctx.label.name + "_bazel_test_constants.json")
ctx.actions.write(
output = bazel_constants_json,
content = json.encode_indent({
"Mode": "Test",
"Severities": " ".join(ctx.attr.severities),
"Verbosity": "INFO",
"clang_bin": info.clangsa.short_path,
"clang_tidy_bin": info.clang_tidy.short_path,
"codechecker_bin": info.codechecker.short_path,
"codechecker_files": codechecker_files.short_path,
}, indent = " ") + "\n",
is_executable = False,
)

launcher = ctx.actions.declare_file(ctx.label.name + "_launcher.sh")
ctx.actions.write(
output = launcher,
content = """#!/bin/bash
exec {tool} {constants}
""".format(
tool = ctx.executable._codechecker_script.short_path,
constants = bazel_constants_json.short_path,
),
is_executable = True,
substitutions = {
"{Mode}": "Test",
"{Severities}": " ".join(ctx.attr.severities),
"{Verbosity}": "INFO",
"{clang_bin}": info.clangsa.short_path,
"{clang_tidy_bin}": info.clang_tidy.short_path,
"{codechecker_bin}": info.codechecker.short_path,
"{codechecker_files}": codechecker_files.short_path,
},
)

# Return test script and all required files
run_files = default_runfiles + [
ctx.outputs.codechecker_test_script,
ctx.executable._codechecker_script,
info.codechecker,
info.clang_tidy,
info.clangsa,
bazel_constants_json,
]
run_files = ctx.runfiles(files = run_files)
run_files = run_files.merge(ctx.attr._codechecker_script[DefaultInfo].default_runfiles)
return [
DefaultInfo(
files = depset(all_files),
runfiles = ctx.runfiles(files = run_files),
executable = ctx.outputs.codechecker_test_script,
runfiles = run_files,
executable = launcher,
),
]

Expand Down Expand Up @@ -296,9 +309,10 @@ _codechecker_test = rule(
cfg = platforms_transition,
doc = "List of compilable targets which should be checked.",
),
"_codechecker_script_template": attr.label(
default = ":codechecker_script.py",
allow_single_file = True,
"_codechecker_script": attr.label(
default = ":codechecker_script",
executable = True,
cfg = "exec",
),
"_compile_commands_filter": attr.label(
allow_files = True,
Expand All @@ -310,9 +324,7 @@ _codechecker_test = rule(
outputs = {
"codechecker_commands": "%{name}/codechecker_commands.json",
"codechecker_log": "%{name}/codechecker.log",
"codechecker_script": "%{name}/codechecker_script.py",
"codechecker_skipfile": "%{name}/codechecker_skipfile.cfg",
"codechecker_test_script": "%{name}/codechecker_test_script.py",
"compile_commands": "%{name}/compile_commands.json",
},
toolchains = [
Expand Down
35 changes: 19 additions & 16 deletions src/codechecker_script.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python3

# Copyright 2023 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -26,20 +24,25 @@
import shlex
import subprocess
import sys
import json
from pathlib import Path

EXECUTION_MODE = "{Mode}"
VERBOSITY = "{Verbosity}"
CODECHECKER_PATH = os.path.realpath("{codechecker_bin}")
CLANG_PATH = os.path.realpath("{clang_bin}")
CLANG_TIDY_PATH = os.path.realpath("{clang_tidy_bin}")
CODECHECKER_SKIPFILE = "{codechecker_skipfile}"
CODECHECKER_CONFIG = "{codechecker_config}"
CODECHECKER_ANALYZE = "{codechecker_analyze}"
CODECHECKER_FILES = "{codechecker_files}"
CODECHECKER_LOG = "{codechecker_log}"
CODECHECKER_SEVERITIES = "{Severities}"
CODECHECKER_ENV = "{codechecker_env}"
COMPILE_COMMANDS = "{compile_commands}"
if __name__ == "__main__":
# Arguments should only be parsed, if this script is the entry point
CONSTANTS: dict = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
EXECUTION_MODE = CONSTANTS.get("Mode", "Run")
VERBOSITY = CONSTANTS.get("Verbosity", "INFO")
CODECHECKER_PATH = os.path.realpath(CONSTANTS["codechecker_bin"])
CLANG_PATH = os.path.realpath(CONSTANTS["clang_bin"])
CLANG_TIDY_PATH = os.path.realpath(CONSTANTS["clang_tidy_bin"])
CODECHECKER_SKIPFILE = CONSTANTS.get("codechecker_skipfile")
CODECHECKER_CONFIG = CONSTANTS.get("codechecker_config")
CODECHECKER_ANALYZE: list[str] = CONSTANTS.get("codechecker_analyze", [])
CODECHECKER_FILES = CONSTANTS["codechecker_files"]
CODECHECKER_LOG = CONSTANTS.get("codechecker_log")
CODECHECKER_SEVERITIES = CONSTANTS.get("Severities", [])
CODECHECKER_ENV = CONSTANTS.get("codechecker_env")
COMPILE_COMMANDS = CONSTANTS.get("compile_commands")

START_PATH = r"\/(?:(?!\.\s+)\S)+"
BAZEL_PATHS = {
Expand Down Expand Up @@ -204,7 +207,7 @@ def analyze():
command = (
f"{CODECHECKER_PATH} analyze --skip={CODECHECKER_SKIPFILE} "
f"{COMPILE_COMMANDS} --output={CODECHECKER_FILES}/data "
f"--config {CODECHECKER_CONFIG} {CODECHECKER_ANALYZE}"
f"--config {CODECHECKER_CONFIG} {' '.join(CODECHECKER_ANALYZE)}"
)
# FIXME: Workaround "CodeChecker simply remove compiler-rt include path".
# This can be removed once codechecker 6.16.0 is used.
Expand Down
Loading
Loading