Skip to content

Commit

Permalink
[config] Support yaml CodeChecker configuration files
Browse files Browse the repository at this point in the history
  • Loading branch information
csordasmarton committed Feb 24, 2022
1 parent fc3d1fe commit 92aedce
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 262 deletions.
23 changes: 1 addition & 22 deletions analyzer/codechecker_analyzer/cmd/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,28 +363,7 @@ def add_arguments_to_parser(parser):
"report directory. When this flag is "
"used, 'failed' directory remains empty.")

analyzer_opts.add_argument('--config',
dest='config_file',
required=False,
help="R|Allow the configuration from an "
"explicit JSON based configuration file. "
"The value of the 'analyzer' key in the "
"config file will be emplaced as command "
"line arguments. The format of "
"configuration file is:\n"
"{\n"
" \"analyzer\": [\n"
" \"--enable=core.DivideZero\",\n"
" \"--enable=core.CallAndMessage\",\n"
" \"--report-hash=context-free-v2\",\n"
" \"--verbose=debug\",\n"
" \"--skip=$HOME/project/skip.txt\",\n"
" \"--clean\"\n"
" ]\n"
"}.\n"
"You can use any environment variable "
"inside this file and it will be "
"expaneded.")
cmd_config.add_option(analyzer_opts)

analyzer_opts.add_argument('--saargs',
dest="clangsa_args_cfg_file",
Expand Down
23 changes: 1 addition & 22 deletions analyzer/codechecker_analyzer/cmd/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,28 +317,7 @@ def add_arguments_to_parser(parser):
"report directory. When this flag is "
"used, 'failed' directory remains empty.")

analyzer_opts.add_argument('--config',
dest='config_file',
required=False,
help="R|Allow the configuration from an "
"explicit JSON based configuration file. "
"The value of the 'analyzer' key in the "
"config file will be emplaced as command "
"line arguments. The format of "
"configuration file is:\n"
"{\n"
" \"analyzer\": [\n"
" \"--enable=core.DivideZero\",\n"
" \"--enable=core.CallAndMessage\",\n"
" \"--report-hash=context-free-v2\",\n"
" \"--verbose=debug\",\n"
" \"--skip=$HOME/project/skip.txt\",\n"
" \"--clean\"\n"
" ]\n"
"}.\n"
"You can use any environment variable "
"inside this file and it will be "
"expaneded.")
cmd_config.add_option(analyzer_opts)

# TODO: One day, get rid of these. See Issue #36, #427.
analyzer_opts.add_argument('--saargs',
Expand Down
16 changes: 1 addition & 15 deletions analyzer/codechecker_analyzer/cmd/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,7 @@ def add_arguments_to_parser(parser):
"containing analysis results which should be "
"parsed and printed.")

parser.add_argument('--config',
dest='config_file',
required=False,
help="R|Allow the configuration from an "
"explicit JSON based configuration file. "
"The value of the 'parse' key in the "
"config file will be emplaced as command "
"line arguments. The format of "
"configuration file is:\n"
"{\n"
" \"parse\": [\n"
" \"--trim-path-prefix\",\n"
" \"$HOME/workspace\"\n"
" ]\n"
"}")
cmd_config.add_option(parser)

parser.add_argument('-t', '--type', '--input-format',
dest="input_format",
Expand Down
78 changes: 55 additions & 23 deletions analyzer/tests/functional/config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ def setUp(self):
self._codechecker_cmd = env.codechecker_cmd()

self.reports_dir = os.path.join(self.test_workspace, "reports")
self.config_file = os.path.join(self.test_workspace,
"codechecker.json")
self.config_file_json = os.path.join(
self.test_workspace, "codechecker.json")
self.config_file_yaml = os.path.join(
self.test_workspace, "codechecker.yaml")
self.build_json = os.path.join(self.test_workspace,
"build_simple.json")
self.source_file = os.path.join(self.test_workspace, "simple.cpp")
Expand All @@ -59,14 +61,14 @@ def setUp(self):
encoding="utf-8", errors="ignore") as source:
source.write(simple_file_content)

def __run_analyze(self, extra_options=None):
def __run_analyze(self, config_file_path: str, extra_options=None):
"""
Run the CodeChecker analyze command with a configuration file.
"""
# Create analyze command.
analyze_cmd = [self._codechecker_cmd, "analyze", self.build_json,
"-o", self.reports_dir,
"--config", self.config_file]
"--config", config_file_path]

if extra_options:
analyze_cmd.extend(extra_options)
Expand All @@ -78,16 +80,17 @@ def __run_analyze(self, extra_options=None):
stderr=subprocess.PIPE,
encoding="utf-8",
errors="ignore")
out, _ = process.communicate()
out, err = process.communicate()
print(err)
return out, process.returncode

def __run_parse(self):
def __run_parse(self, config_file_path: str):
"""
Run the CodeChecker analyze command with a configuration file.
"""
# Create analyze command.
analyze_cmd = [self._codechecker_cmd, "parse", self.reports_dir,
"--config", self.config_file]
"--config", config_file_path]

# Run analyze.
process = subprocess.Popen(
Expand All @@ -104,12 +107,12 @@ def test_only_clangsa_config(self):
Run analyze command with a config file which enables the clangsa
analyzer only.
"""
with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'analyze': ['--analyzers', 'clangsa']}, config_f)

out, returncode = self.__run_analyze()
out, returncode = self.__run_analyze(self.config_file_json)

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
Expand All @@ -121,14 +124,14 @@ def test_only_clangsa_config_backward_compatible_mixed(self):
The config name should be 'analyze' to be in sync with the
subcommand names.
"""
with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'analyze': ['--analyzers', 'clangsa'],
'analyzer': ['--analyzers', 'clang-tidy']},
config_f)

out, returncode = self.__run_analyze()
out, returncode = self.__run_analyze(self.config_file_json)

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
Expand All @@ -140,12 +143,12 @@ def test_only_clangsa_config_backward_compatibility(self):
The config name should be 'analyze' to be in sync with the
subcommand names.
"""
with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'analyzer': ['--analyzers', 'clangsa']}, config_f)

out, returncode = self.__run_analyze()
out, returncode = self.__run_analyze(self.config_file_json)

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
Expand All @@ -157,12 +160,13 @@ def test_override_config_file(self):
analyzer only and override this option from the command line and enable
only clangsa analyze.
"""
with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'analyzer': ['--analyzers', 'clang-tidy']}, config_f)

out, returncode = self.__run_analyze(['--analyzers', 'clangsa'])
out, returncode = self.__run_analyze(
self.config_file_json, ['--analyzers', 'clangsa'])

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
Expand All @@ -172,36 +176,64 @@ def test_empty_config(self):
"""
Run analyze with an empty config file.
"""
with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
config_f.write("")

out, returncode = self.__run_analyze()
out, returncode = self.__run_analyze(self.config_file_json)

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
self.assertIn("clang-tidy analyzed simple.cpp", out)

def test_parse_config(self):
"""
Run analyze command with a config file which enables the clangsa
Run analyze command with a JSON config file which enables the clangsa
analyzer only and parse the results with a parse command
config.
"""
with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'analyzer': ['--analyzers', 'clangsa'],
'parse': ['--trim-path-prefix', '/workspace']},
config_f)

out, returncode = self.__run_analyze()
out, returncode = self.__run_analyze(self.config_file_json)

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
self.assertNotIn("clang-tidy analyzed simple.cpp", out)

out, returncode = self.__run_parse(self.config_file_json)
print(out)
self.assertEqual(returncode, 2)

def test_yaml_analyze_and_parse(self):
"""
Run analyze command with a yaml config file which enables the clangsa
analyzer only and parse the results with a parse command
config.
"""
with open(self.config_file_yaml, 'w+',
encoding="utf-8", errors="ignore") as f:
f.write("""
analyzer:
# Enable only clangsa analyzer.
- --analyzers=clangsa
parse:
# Removes leading path from file paths.
- --trim-path-prefix=/workspace
""")

out, returncode = self.__run_analyze(self.config_file_yaml)

self.assertEqual(returncode, 0)
self.assertIn("clangsa analyzed simple.cpp", out)
self.assertNotIn("clang-tidy analyzed simple.cpp", out)

out, returncode = self.__run_parse()
out, returncode = self.__run_parse(self.config_file_yaml)
print(out)
self.assertEqual(returncode, 2)

Expand All @@ -218,7 +250,7 @@ def test_check_config(self):
path_prefix = os.path.join(os.sep, *split_path[:3])
trimmed_file_path = os.path.join(*split_path[3:])

with open(self.config_file, 'w+',
with open(self.config_file_json, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'analyzer': ['--analyzers', 'clangsa'],
Expand All @@ -228,7 +260,7 @@ def test_check_config(self):
check_cmd = [self._codechecker_cmd, "check",
"-l", self.build_json,
"-o", self.reports_dir,
"--config", self.config_file]
"--config", self.config_file_json]

# Run analyze.
process = subprocess.Popen(
Expand Down
27 changes: 25 additions & 2 deletions codechecker_common/cmd_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# -------------------------------------------------------------------------

import os
import yaml

from typing import List

Expand All @@ -16,6 +18,22 @@
LOG = logger.get_logger('system')


def add_option(parser):
""" Add config file option to the given parser. """
parser.add_argument('--config',
dest='config_file',
required=False,
help="R|Allow the configuration from an explicit "
"configuration file. The values configured in "
"the config file will overwrite the values set "
"in the command line.\n"
"You can use any environment variable inside "
"this file and it will be expaneded.\n"
"For more information see the docs: "
"https://github.com/Ericsson/codechecker/tree/"
"master/docs/config_file.md")


def get_analyze_options(cfg) -> List[str]:
""" Get analyze related options. """
# The config value can be 'analyze' or 'analyzer'
Expand All @@ -39,8 +57,13 @@ def process_config_file(args, subcommand_name):
if 'config_file' not in args:
return {}

if args.config_file and os.path.exists(args.config_file):
cfg = load_json_or_empty(args.config_file, default={})
config_file = args.config_file
if config_file and os.path.exists(config_file):
if config_file.endswith(('.yaml', '.yml')):
with open(config_file, encoding='utf-8', errors='ignore') as f:
cfg = yaml.load(f, Loader=yaml.BaseLoader)
else:
cfg = load_json_or_empty(config_file, default={})

# The subcommand name is analyze but the
# configuration section name is analyzer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
],
"parse": [
"--trim-path-prefix",
"/$HOME/workspace"
"$HOME/workspace"
],
"server": [
"--workspace=$HOME/workspace",
"--port=9090"
],
"store": [
"--url", "localhost:9090/Default"
"--name=run_name",
"--tag=my_tag",
"--url=http://codechecker.my:9090/MyProduct"
]
}
23 changes: 23 additions & 0 deletions config/config_files/codechecker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
analyzer:
# Enable/disable checkers.
- --enable=core.DivideZero
- --enable=core.CallAndMessage
# Analyzer configurations.
- --analyzer-config=clangsa:unroll-loops=true
- --checker-config=clang-tidy:google-readability-function-size.StatementThreshold=100
# Optional options.
- --report-hash=context-free-v2
- --verbose=debug
- --clean

parse:
- --trim-path-prefix=$HOME/workspace

server:
- --workspace=$HOME/workspace"
- --port=9090"

store:
- --name=run_name
- --tag=my_tag
- --url=http://codechecker.my:9090/MyProduct
Loading

0 comments on commit 92aedce

Please sign in to comment.