[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2 2/5] xen/scripts: add cppcheck tool to the xen-analysis.py script



On Mon, 5 Dec 2022, Luca Fancellu wrote:
> Add Cppcheck analysis to the xen-analysis.py script using the
> arguments --run-cppcheck.
> 
> Now cppcheck analysis will build Xen while the analysis is performed
> on the source files, it will produce a text report and an additional
> html output when the script is called with --cppcheck-html.
> 
> With this patch cppcheck will benefit of platform configuration files
> that will help it to understand the target of the compilation and
> improve the analysis. These are XML files placed in
> xen/tools/cppcheck-plat/, describing to cppcheck the length of basic
> types to help it in the analysis.
> 
> To do so:
>  - Update xen-analysis.py with the code to integrate cppcheck.
>  - add platform configuration files for cppcheck..
>  - add cppcheck-cc.sh script that is a wrapper for cppcheck and it's
>    used as Xen compiler, it will intercept all flags given from the
>    make build system and it will execute cppcheck on the compiled
>    file together with the file compilation.
>  - guarded hypercall-defs.c with CPPCHECK define because cppcheck
>    gets confused as the file does not contain c code.
>  - add false-positive-cppcheck.json file
>  - update documentation.
>  - update .gitignore
> 
> Signed-off-by: Luca Fancellu <luca.fancellu@xxxxxxx>
> ---
> Changes in v2:
>  - changes to commit message (Jan)
>  - changes to the cppcheck-cc.sh script and on
>    platform XML files, fixed version handling
>    in cppcheck_analysis.py (Stefano)
>  - Move revert of Makefile and xen/tools/merge_cppcheck_reports.py
>    to the following patch, modified .gitignore to handle the
>    presence of both Makefile invoked cppcheck and this new method
>    (Stefano)
> ---
> ---
>  .gitignore                                    |   6 +
>  docs/misra/cppcheck.txt                       |  27 +-
>  docs/misra/documenting-violations.rst         |   7 +-
>  docs/misra/false-positive-cppcheck.json       |  12 +
>  docs/misra/xen-static-analysis.rst            |  42 ++-
>  xen/include/hypercall-defs.c                  |   9 +
>  xen/scripts/xen-analysis.py                   |  18 +-
>  xen/scripts/xen_analysis/cppcheck_analysis.py | 273 ++++++++++++++++++
>  .../xen_analysis/cppcheck_report_utils.py     | 130 +++++++++
>  xen/scripts/xen_analysis/generic_analysis.py  |  21 +-
>  xen/scripts/xen_analysis/settings.py          |  77 ++++-
>  xen/scripts/xen_analysis/utils.py             |  21 +-
>  xen/tools/cppcheck-cc.sh                      | 223 ++++++++++++++
>  xen/tools/cppcheck-plat/arm32-wchar_t4.xml    |  17 ++
>  xen/tools/cppcheck-plat/arm64-wchar_t2.xml    |  17 ++
>  xen/tools/cppcheck-plat/x86_64-wchar_t2.xml   |  17 ++
>  16 files changed, 860 insertions(+), 57 deletions(-)
>  create mode 100644 docs/misra/false-positive-cppcheck.json
>  create mode 100644 xen/scripts/xen_analysis/cppcheck_analysis.py
>  create mode 100644 xen/scripts/xen_analysis/cppcheck_report_utils.py
>  create mode 100755 xen/tools/cppcheck-cc.sh
>  create mode 100644 xen/tools/cppcheck-plat/arm32-wchar_t4.xml
>  create mode 100644 xen/tools/cppcheck-plat/arm64-wchar_t2.xml
>  create mode 100644 xen/tools/cppcheck-plat/x86_64-wchar_t2.xml
> 
> diff --git a/.gitignore b/.gitignore
> index f5a66f6194dd..39efe2933a33 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -8,8 +8,11 @@
>  *.d
>  *.d2
>  *.c.cppcheck
> +*.cppcheck.txt
> +*.cppcheck.xml
>  *.opic
>  *.a
> +*.c.json
>  *.safparse
>  *.so
>  *.so.[0-9]*
> @@ -282,9 +285,11 @@ xen/arch/*/efi/efi.h
>  xen/arch/*/efi/pe.c
>  xen/arch/*/efi/runtime.c
>  xen/arch/*/include/asm/asm-offsets.h
> +xen/build-dir-cppcheck/
>  xen/common/config_data.S
>  xen/common/config.gz
>  xen/cppcheck-htmlreport/
> +xen/cppcheck-report/
>  xen/cppcheck-misra.*
>  xen/include/headers*.chk
>  xen/include/compat/*
> @@ -315,6 +320,7 @@ xen/xsm/flask/xenpolicy-*
>  tools/flask/policy/policy.conf
>  tools/flask/policy/xenpolicy-*
>  xen/xen
> +xen/suppression-list.txt
>  xen/xen-cppcheck.xml
>  xen/xen-syms
>  xen/xen-syms.map
> diff --git a/docs/misra/cppcheck.txt b/docs/misra/cppcheck.txt
> index 25d8c3050b72..f7b9f678b4d5 100644
> --- a/docs/misra/cppcheck.txt
> +++ b/docs/misra/cppcheck.txt
> @@ -3,8 +3,7 @@ Cppcheck for Xen static and MISRA analysis
>  
>  Xen can be analysed for both static analysis problems and MISRA violation 
> using
>  cppcheck, the open source tool allows the creation of a report with all the
> -findings. Xen has introduced the support in the Makefile so it's very easy to
> -use and in this document we can see how.
> +findings.
>  
>  The minimum version required for cppcheck is 2.7. Note that at the time of
>  writing (June 2022), the version 2.8 is known to be broken [1].
> @@ -38,27 +37,7 @@ Dependencies are listed in the readme.md of the project 
> repository.
>  Use cppcheck to analyse Xen
>  ===========================
>  
> -Using cppcheck integration is very simple, it requires few steps:
> -
> - 1) Compile Xen
> - 2) call the cppcheck make target to generate a report in xml format:
> -    make CPPCHECK_MISRA=y cppcheck
> - 3) call the cppcheck-html make target to generate a report in xml and html
> -    format:
> -    make CPPCHECK_MISRA=y cppcheck-html
> -
> -    In case the cppcheck binaries are not in the PATH, CPPCHECK and
> -    CPPCHECK_HTMLREPORT variables can be overridden with the full path to the
> -    binaries:
> -
> -    make -C xen \
> -        CPPCHECK=/path/to/cppcheck \
> -        CPPCHECK_HTMLREPORT=/path/to/cppcheck-htmlreport \
> -        CPPCHECK_MISRA=y \
> -        cppcheck-html
> -
> -The output is by default in a folder named cppcheck-htmlreport, but the name
> -can be changed by passing it in the CPPCHECK_HTMLREPORT_OUTDIR variable.
> -
> +To analyse Xen using cppcheck, please refer to the document
> +xen-static-analysis.rst, section "Analyse Xen with Cppcheck".
>  
>  [1] 
> https://sourceforge.net/p/cppcheck/discussion/general/thread/bfc3ab6c41/?limit=25
> diff --git a/docs/misra/documenting-violations.rst 
> b/docs/misra/documenting-violations.rst
> index 1d23447556d2..31dafd5d4ece 100644
> --- a/docs/misra/documenting-violations.rst
> +++ b/docs/misra/documenting-violations.rst
> @@ -51,6 +51,7 @@ Here is an example to add a new justification in safe.json::
>  |        {
>  |            "id": "SAF-0-safe",
>  |            "analyser": {
> +|                "cppcheck": "misra-c2012-20.7",
>  |                "coverity": "misra_c_2012_rule_20_7_violation",
>  |                "eclair": "MC3R1.R20.7"
>  |            },
> @@ -77,9 +78,9 @@ Here is an explanation of the fields inside an object of 
> the "content" array:
>     It tells the tool to substitute a Xen in-code comment having this 
> structure:
>     /* SAF-0-safe [...] \*/
>   - analyser: it is an object containing pair of key-value strings, the key is
> -   the analyser, so it can be coverity or eclair, the value is the 
> proprietary
> -   id corresponding on the finding, for example when coverity is used as
> -   analyser, the tool will translate the Xen in-code coment in this way:
> +   the analyser, so it can be cppcheck, coverity or eclair, the value is the
> +   proprietary id corresponding on the finding, for example when coverity is
> +   used as analyser, the tool will translate the Xen in-code coment in this 
> way:
>     /* SAF-0-safe [...] \*/ -> /* coverity[misra_c_2012_rule_20_7_violation] 
> \*/
>     if the object doesn't have a key-value, then the corresponding in-code
>     comment won't be translated.
> diff --git a/docs/misra/false-positive-cppcheck.json 
> b/docs/misra/false-positive-cppcheck.json
> new file mode 100644
> index 000000000000..5d4da2ce6170
> --- /dev/null
> +++ b/docs/misra/false-positive-cppcheck.json
> @@ -0,0 +1,12 @@
> +{
> +    "version": "1.0",
> +    "content": [
> +        {
> +            "id": "SAF-0-false-positive-cppcheck",
> +            "violation-id": "",
> +            "tool-version": "",
> +            "name": "Sentinel",
> +            "text": "Next ID to be used"
> +        }
> +    ]
> +}

I think we need to add to the cppcheck document how to figure out the
cppcheck id for a given violation in the html report


> diff --git a/docs/misra/xen-static-analysis.rst 
> b/docs/misra/xen-static-analysis.rst
> index 5b886474d4a0..2712255db1b0 100644
> --- a/docs/misra/xen-static-analysis.rst
> +++ b/docs/misra/xen-static-analysis.rst
> @@ -7,9 +7,8 @@ The Xen codebase integrates some scripts and tools that helps 
> the developer to
>  perform static analysis of the code, currently Xen supports three analysis 
> tool
>  that are eclair, coverity and cppcheck.
>  The Xen tree has a script (xen-analysis.py) available to ease the analysis
> -process and it integrates a way to suppress findings on these tools (only 
> Eclair
> -and Coverity are currently supported by the script), please check the
> -documenting-violation.rst document to know more about it.
> +process and it integrates a way to suppress findings on these tools, please
> +check the documenting-violation.rst document to know more about it.
>  
>  Analyse Xen with Coverity or Eclair
>  -----------------------------------
> @@ -52,3 +51,40 @@ When invoking the script, the procedure below will be 
> followed:
>      this step, call the script adding the --no-clean argument, but before
>      running again the script, call it with the --clean-only argument, that 
> will
>      execute only this cleaning step.
> +
> +
> +Analyse Xen with Cppcheck
> +-------------------------
> +
> +Cppcheck tool is integrated in xen-analysis.py script, when using the script,
> +the tool will be called on every source file compiled by the make build 
> system.
> +Here how to start the analysis with Cppcheck:
> +
> + - xen-analysis.py --run-cppcheck [--cppcheck-misra] [--cppcheck-html] --
> +   [optional make arguments]
> +
> +The command above tells the script to prepare the codebase and use Cppcheck 
> tool
> +for the analysis.
> +The optional argument --cppcheck-misra activates the analysis also for MISRA
> +compliance.
> +The optional argument --cppcheck-html instruct cppcheck to produce an 
> additional
> +HTML report.
> +
> +When invoking the script for Cppcheck analysis, the followed procedure is
> +similar to the one above for Coverity or Eclair, but it has some additional
> +steps:
> +
> + 1. This step is the same as step 1 for Coverity/Eclair.
> + 2. The cppcheck dependency are created, build directory for cppcheck 
> analysis
> +    and an header file containing internal compiler macro
> +    (include/generated/compiler-def.h) are generated
> + 3. Xen compilation starts using every <additional make parameters> supplied
> +    at the script invocation, but because cppcheck is not able to intercept 
> the
> +    compiled files and flags on compiler invocation, a script 
> (cppcheck-cc.sh)
> +    is passed as CC to the make system, it is a wrapper for the compiler that
> +    will also execute cppcheck on every compiled file.
> + 4. After the compilation and analysis, the cppcheck report will be created
> +    putting together all the cppcheck report fragments for every analysed 
> file.
> +    Cppcheck will produce a text fragment and an additional XML report 
> fragment
> +    if the script is configured to produce the HTML output.
> + 5. This step is the same as step 3 for Coverity/Eclair.
> diff --git a/xen/include/hypercall-defs.c b/xen/include/hypercall-defs.c
> index 189612107402..41e1af01f6b2 100644
> --- a/xen/include/hypercall-defs.c
> +++ b/xen/include/hypercall-defs.c
> @@ -60,6 +60,13 @@
>   * are possible.
>   */
>  
> +/*
> + * Cppcheck thinks this file needs to be analysed because it is preprocessed 
> by
> + * the compiler, but it gets confused because this file does not contains C
> + * code. Hence protect the code when CPPCHECK is used.
> + */
> +#ifndef CPPCHECK
> +
>  #ifdef CONFIG_HVM
>  #define PREFIX_hvm hvm
>  #else
> @@ -288,3 +295,5 @@ mca                                do       do       -    
>     -        -
>  #ifndef CONFIG_PV_SHIM_EXCLUSIVE
>  paging_domctl_cont                 do       do       do       do       -
>  #endif
> +
> +#endif /* !CPPCHECK */
> diff --git a/xen/scripts/xen-analysis.py b/xen/scripts/xen-analysis.py
> index b5d9ef1862c9..8e50c27cd898 100755
> --- a/xen/scripts/xen-analysis.py
> +++ b/xen/scripts/xen-analysis.py
> @@ -1,28 +1,42 @@
>  #!/usr/bin/env python3
>  
>  import sys
> -from xen_analysis import settings, generic_analysis
> +from xen_analysis import settings, generic_analysis, cppcheck_analysis
>  from xen_analysis.generic_analysis import *
> +from xen_analysis.cppcheck_analysis import *
> +
> +PhaseExceptions = (GetMakeVarsPhaseError, ParseTagPhaseError,
> +                   CppcheckDepsPhaseError, BuildPhaseError,
> +                   CppcheckReportPhaseError)
>  
>  
>  def main(argv):
>      ret_code = 0
>      settings.parse_commandline(argv)
>      try:
> +        if settings.step_get_make_vars:
> +            cppcheck_analysis.get_make_vars()
>          if settings.step_parse_tags:
>              generic_analysis.parse_xen_tags()
> +        if settings.step_cppcheck_deps:
> +            cppcheck_analysis.generate_cppcheck_deps()
>          if settings.step_build_xen:
>              generic_analysis.build_xen()
> -    except (ParseTagPhaseError, BuildPhaseError) as e:
> +        if settings.step_cppcheck_report:
> +            cppcheck_analysis.generate_cppcheck_report()
> +    except PhaseExceptions as e:
>          print("ERROR: {}".format(e))
>          if hasattr(e, "errorcode"):
>              ret_code = e.errorcode
>      finally:
>          if settings.step_clean_analysis:
> +            cppcheck_analysis.clean_analysis_artifacts()
>              e = generic_analysis.clean_analysis_artifacts()
>              if e:
>                  print("ERROR: {}".format(e))
>                  ret_code = 1
> +        if settings.step_distclean_analysis:
> +            cppcheck_analysis.clean_reports()
>  
>      sys.exit(ret_code)
>  
> diff --git a/xen/scripts/xen_analysis/cppcheck_analysis.py 
> b/xen/scripts/xen_analysis/cppcheck_analysis.py
> new file mode 100644
> index 000000000000..0e952a169641
> --- /dev/null
> +++ b/xen/scripts/xen_analysis/cppcheck_analysis.py
> @@ -0,0 +1,273 @@
> +#!/usr/bin/env python3
> +
> +import os, re, shutil
> +from . import settings, utils, cppcheck_report_utils
> +
> +class GetMakeVarsPhaseError(Exception):
> +    pass
> +
> +class CppcheckDepsPhaseError(Exception):
> +    pass
> +
> +class CppcheckReportPhaseError(Exception):
> +    pass
> +
> +CPPCHECK_BUILD_DIR = "build-dir-cppcheck"
> +CPPCHECK_HTMLREPORT_OUTDIR = "cppcheck-htmlreport"
> +CPPCHECK_REPORT_OUTDIR = "cppcheck-report"
> +cppcheck_extra_make_args = ""
> +xen_cc = ""
> +
> +def get_make_vars():
> +    global xen_cc
> +    invoke_make = utils.invoke_command(
> +            "make -C {} {} export-variable-CC"
> +                .format(settings.xen_dir, settings.make_forward_args),
> +            True, GetMakeVarsPhaseError,
> +            "Error occured retrieving make vars:\n{}"
> +        )
> +
> +    cc_var_regex = re.search('^CC=(.*)$', invoke_make, flags=re.M)
> +    if cc_var_regex:
> +        xen_cc = cc_var_regex.group(1)
> +
> +    if xen_cc == "":
> +        raise GetMakeVarsPhaseError("CC variable not found in Xen make 
> output")
> +
> +
> +def __generate_suppression_list(out_file):
> +    # The following lambda function will return a file if it contains lines 
> with
> +    # a comment containing "cppcheck-suppress[*]" on a single line.
> +    grep_action = lambda x: utils.grep(x,
> +                    r'^[ \t]*/\* cppcheck-suppress\[(.*)\] \*/$')
> +    # Look for a list of .h files that matches the condition above
> +    headers = utils.recursive_find_file(settings.xen_dir, r'.*\.h$',
> +                                        grep_action)
> +
> +    try:
> +        with open(out_file, "wt") as supplist_file:
> +            # Add this rule to skip every finding in the autogenerated
> +            # header for cppcheck
> +            supplist_file.write("*:*generated/compiler-def.h\n")
> +
> +            for entry in headers:
> +                filename = entry["file"]
> +                try:
> +                    with open(filename, "rt") as infile:
> +                        header_content = infile.readlines()
> +                except OSError as e:
> +                    raise CppcheckDepsPhaseError(
> +                            "Issue with reading file {}: {}"
> +                                .format(filename, e)
> +                          )
> +                header_lines_len = len(header_content)
> +                # line_num in entry will be header_content[line_num-1], here 
> we
> +                # are going to search the first line after line_num that have
> +                # anything different from comments or empty line, because the
> +                # in-code comment suppression is related to that line then.
> +                for line_num in entry["matches"]:
> +                    cppcheck_violation_id = ""
> +                    tmp_line = line_num
> +                    # look up to which line is referring the comment at
> +                    # line_num (which would be header_content[tmp_line-1])
> +                    comment_section = False
> +                    while tmp_line < header_lines_len:
> +                        line = header_content[tmp_line]
> +                        # Matches a line with just optional spaces/tabs and 
> the
> +                        # start of a comment '/*'
> +                        comment_line_starts = re.match('^[ \t]*/\*.*$', line)
> +                        # Matches a line with text and the end of a comment 
> '*/'
> +                        comment_line_stops = re.match('^.*\*/$', line)
> +                        if (not comment_section) and comment_line_starts:
> +                            comment_section = True
> +                        if (len(line.strip()) != 0) and (not 
> comment_section):
> +                            cppcheck_violation_id = 
> entry["matches"][line_num][0]
> +                            break
> +                        if comment_section and comment_line_stops:
> +                            comment_section = False
> +                        tmp_line = tmp_line + 1
> +
> +                    if cppcheck_violation_id == "":
> +                        raise CppcheckDepsPhaseError(
> +                            "Error matching cppcheck comment in {} at line 
> {}."
> +                                .format(filename, line_num)
> +                          )
> +                    # Write [error id]:[filename]:[line]
> +                    # tmp_line refers to the array index, so translated to 
> the
> +                    # file line (that begins with 1) it is tmp_line+1
> +                    supplist_file.write(
> +                            "{}:{}:{}\n".format(cppcheck_violation_id, 
> filename,
> +                                                (tmp_line + 1))
> +                        )
> +    except OSError as e:
> +        raise CppcheckDepsPhaseError("Issue with writing file {}: {}"
> +                                     .format(out_file, e))
> +
> +
> +def generate_cppcheck_deps():
> +    global cppcheck_extra_make_args
> +
> +    # Compile flags to pass to cppcheck:
> +    # - include config.h as this is passed directly to the compiler.
> +    # - define CPPCHECK as we use it to disable or enable some specific part 
> of
> +    #   the code to solve some cppcheck issues.
> +    # - explicitely enable some cppcheck checks as we do not want to use 
> "all"
> +    #   which includes unusedFunction which gives wrong positives as we check
> +    #   file per file.
> +    # - Explicitly suppress warnings on compiler-def.h because cppcheck 
> throws
> +    #   an unmatchedSuppression due to the rule we put in 
> suppression-list.txt
> +    #   to skip every finding in the file.
> +    #
> +    # Compiler defines are in compiler-def.h which is included in config.h
> +    #
> +    cppcheck_flags="""
> +--cppcheck-build-dir={}/{}
> + --max-ctu-depth=10
> + --enable=style,information,missingInclude
> + --template=\'{{file}}({{line}},{{column}}):{{id}}:{{severity}}:{{message}}\'
> + --relative-paths={}
> + --inline-suppr
> + --suppressions-list={}/suppression-list.txt
> + --suppress='unmatchedSuppression:*generated/compiler-def.h'
> + --include={}/include/xen/config.h
> + -DCPPCHECK
> +""".format(settings.outdir, CPPCHECK_BUILD_DIR, settings.xen_dir,
> +           settings.outdir, settings.xen_dir)
> +
> +    invoke_cppcheck = utils.invoke_command(
> +            "{} --version".format(settings.cppcheck_binpath),
> +            True, CppcheckDepsPhaseError,
> +            "Error occured retrieving cppcheck version:\n{}\n\n{}"
> +        )
> +
> +    version_regex = re.search('^Cppcheck (.*)$', invoke_cppcheck, flags=re.M)
> +    # Currently, only cppcheck version >= 2.7 is supported, but version 2.8 
> is
> +    # known to be broken, please refer to docs/misra/cppcheck.txt
> +    if (not version_regex) or (not version_regex.group(1).startswith("2.7")):
> +        raise CppcheckDepsPhaseError(
> +                "Can't find cppcheck version or version is not 2.7"
> +              )
> +
> +    # If misra option is selected, append misra addon and generate cppcheck
> +    # files for misra analysis
> +    if settings.cppcheck_misra:
> +        cppcheck_flags = cppcheck_flags + " --addon=cppcheck-misra.json"
> +
> +        utils.invoke_command(
> +            "{}/convert_misra_doc.py -i {}/docs/misra/rules.rst"
> +            " -o {}/cppcheck-misra.txt -j {}/cppcheck-misra.json"
> +                .format(settings.tools_dir, settings.repo_dir,
> +                        settings.outdir, settings.outdir),
> +            False, CppcheckDepsPhaseError,
> +            "An error occured when running:\n{}"
> +        )
> +
> +    # Generate compiler macros
> +    os.makedirs("{}/include/generated".format(settings.outdir), 
> exist_ok=True)
> +    utils.invoke_command(
> +            "{} -dM -E -o \"{}/include/generated/compiler-def.h\" - < 
> /dev/null"
> +                .format(xen_cc, settings.outdir),
> +            False, CppcheckDepsPhaseError,
> +            "An error occured when running:\n{}"
> +        )
> +
> +    # Generate cppcheck suppression list
> +    __generate_suppression_list(
> +        "{}/suppression-list.txt".format(settings.outdir))
> +
> +    # Generate cppcheck build folder
> +    os.makedirs("{}/{}".format(settings.outdir, CPPCHECK_BUILD_DIR),
> +                exist_ok=True)
> +
> +    cppcheck_cc_flags = """--compiler={} --cppcheck-cmd={} {}
> + --cppcheck-plat={}/cppcheck-plat --ignore-path=tools/
> + --ignore-path=arch/x86/efi/check.c
> +""".format(xen_cc, settings.cppcheck_binpath, cppcheck_flags,
> +           settings.tools_dir)
> +
> +    if settings.cppcheck_html:
> +        cppcheck_cc_flags = cppcheck_cc_flags + " --cppcheck-html"
> +
> +    # Generate the extra make argument to pass the cppcheck-cc.sh wrapper as 
> CC
> +    cppcheck_extra_make_args = "CC=\"{}/cppcheck-cc.sh {} --\"".format(
> +                                        settings.tools_dir,
> +                                        cppcheck_cc_flags
> +                                    ).replace("\n", "")
> +
> +
> +def generate_cppcheck_report():
> +    # Prepare text report
> +    # Look for a list of .cppcheck.txt files, those are the txt report
> +    # fragments
> +    fragments = utils.recursive_find_file(settings.outdir, 
> r'.*\.cppcheck.txt$')
> +    text_report_dir = "{}/{}".format(settings.outdir,
> +                                        CPPCHECK_REPORT_OUTDIR)
> +    report_filename = "{}/xen-cppcheck.txt".format(text_report_dir)
> +    os.makedirs(text_report_dir, exist_ok=True)
> +    try:
> +        cppcheck_report_utils.cppcheck_merge_txt_fragments(fragments,
> +                                                           report_filename,
> +                                                           
> [settings.xen_dir])
> +    except cppcheck_report_utils.CppcheckTXTReportError as e:
> +        raise CppcheckReportPhaseError(e)
> +
> +    # If HTML output is requested
> +    if settings.cppcheck_html:
> +        # Look for a list of .cppcheck.xml files, those are the XML report
> +        # fragments
> +        fragments = utils.recursive_find_file(settings.outdir,
> +                                              r'.*\.cppcheck.xml$')
> +        html_report_dir = "{}/{}".format(settings.outdir,
> +                                         CPPCHECK_HTMLREPORT_OUTDIR)
> +        xml_filename = "{}/xen-cppcheck.xml".format(html_report_dir)
> +        os.makedirs(html_report_dir, exist_ok=True)
> +        try:
> +            cppcheck_report_utils.cppcheck_merge_xml_fragments(fragments,
> +                                                               xml_filename,
> +                                                               
> settings.xen_dir,
> +                                                               
> settings.outdir)
> +        except cppcheck_report_utils.CppcheckHTMLReportError as e:
> +            raise CppcheckReportPhaseError(e)
> +        # Call cppcheck-htmlreport utility to generate the HTML output
> +        utils.invoke_command(
> +            "{} --file={} --source-dir={} --report-dir={}/html --title=Xen"
> +                .format(settings.cppcheck_htmlreport_binpath, xml_filename,
> +                        settings.xen_dir, html_report_dir),
> +            False, CppcheckReportPhaseError,
> +            "Error occured generating Cppcheck HTML report:\n{}"
> +        )
> +        # Strip src and obj path from *.html files
> +        html_files = utils.recursive_find_file(html_report_dir, r'.*\.html$')
> +        try:
> +            cppcheck_report_utils.cppcheck_strip_path_html(html_files,
> +                                                           (settings.xen_dir,
> +                                                            settings.outdir))
> +        except cppcheck_report_utils.CppcheckHTMLReportError as e:
> +            raise CppcheckReportPhaseError(e)
> +
> +
> +def clean_analysis_artifacts():
> +    clean_files = ("suppression-list.txt", "cppcheck-misra.txt",
> +                   "cppcheck-misra.json")
> +    cppcheck_build_dir = "{}/{}".format(settings.outdir, CPPCHECK_BUILD_DIR)
> +    if os.path.isdir(cppcheck_build_dir):
> +        shutil.rmtree(cppcheck_build_dir)
> +    artifact_files = utils.recursive_find_file(settings.outdir,
> +                                
> r'.*\.(?:c\.json|cppcheck\.txt|cppcheck\.xml)$')
> +    for file in clean_files:
> +        file = "{}/{}".format(settings.outdir, file)
> +        if os.path.isfile(file):
> +            artifact_files.append(file)
> +    for delfile in artifact_files:
> +        os.remove(delfile)
> +
> +
> +def clean_reports():
> +    text_report_dir = "{}/{}".format(settings.outdir,
> +                                     CPPCHECK_REPORT_OUTDIR)
> +    html_report_dir = "{}/{}".format(settings.outdir,
> +                                     CPPCHECK_HTMLREPORT_OUTDIR)
> +    if os.path.isdir(text_report_dir):
> +        shutil.rmtree(text_report_dir)
> +    if os.path.isdir(html_report_dir):
> +        shutil.rmtree(html_report_dir)
> diff --git a/xen/scripts/xen_analysis/cppcheck_report_utils.py 
> b/xen/scripts/xen_analysis/cppcheck_report_utils.py
> new file mode 100644
> index 000000000000..02440aefdfec
> --- /dev/null
> +++ b/xen/scripts/xen_analysis/cppcheck_report_utils.py
> @@ -0,0 +1,130 @@
> +#!/usr/bin/env python3
> +
> +import os
> +from xml.etree import ElementTree
> +
> +class CppcheckHTMLReportError(Exception):
> +    pass
> +
> +class CppcheckTXTReportError(Exception):
> +    pass
> +
> +
> +def __elements_equal(el1, el2):
> +    if type(el1) != type(el2): return False
> +
> +    if el1.find('location') is None: return False
> +    if el2.find('location') is None: return False
> +
> +    el1_location = str(el1.find('location').attrib)
> +    el2_location = str(el2.find('location').attrib)
> +
> +    if el1_location != el2_location: return False
> +
> +    return True
> +
> +
> +def __contain_element(new, lst):
> +    for elem in lst:
> +        if __elements_equal(new, elem):
> +            return True
> +    return False
> +
> +
> +def __get_xml_root_file(filename):
> +    try:
> +        result_xml_root = ElementTree.parse(filename).getroot()
> +    except ElementTree.ParseError as e:
> +        raise CppcheckHTMLReportError(
> +                    "XML parsing error in {}: {}".format(filename, e)
> +                )
> +    return result_xml_root
> +
> +
> +def __sanitize_cppcheck_xml_path(xml_tree, src_path, obj_path):
> +    # Some path are relative to the source tree but some others are generated
> +    # in the obj tree, for cppcheck when using cppcheck-htmlreport we can 
> pass
> +    # only one source tree where the files will be fetched if relative path 
> are
> +    # found. So for every path that does not exists in src tree, we guess it
> +    # comes from obj tree and we put explicit absolute path to it
> +    error_item_root = xml_tree.findall("errors")[0]
> +    for error_item in error_item_root:
> +        for location_item in error_item.findall("location"):
> +            path = location_item.attrib["file"]
> +            new_obj_path = obj_path + "/" + path
> +            new_src_path = src_path + "/" + path
> +            if (path[0] != "/") and (not os.path.isfile(new_src_path)) \
> +               and os.path.isfile(new_obj_path):
> +                location_item.attrib["file"] = new_obj_path
> +
> +
> +def cppcheck_merge_xml_fragments(fragments_list, out_xml_file, src_path,
> +                                 obj_path):
> +
> +    result_xml = __get_xml_root_file(fragments_list[0])
> +    insert_point = result_xml.findall("errors")[0]
> +    for xml_file in fragments_list[1:]:
> +        xml_root = __get_xml_root_file(xml_file)
> +        curr_elem_list = list(insert_point)
> +        new_elem_list = list(xml_root.findall("errors")[0])
> +        for xml_error_elem in new_elem_list:
> +            if not __contain_element(xml_error_elem, curr_elem_list):
> +                insert_point.insert(1, xml_error_elem)
> +
> +    if result_xml is None:
> +        return False
> +
> +    __sanitize_cppcheck_xml_path(result_xml, src_path, obj_path)
> +
> +    ElementTree.ElementTree(result_xml).write(out_xml_file)
> +
> +    return True
> +
> +
> +def cppcheck_merge_txt_fragments(fragments_list, out_txt_file, strip_paths):
> +    try:
> +        with open(out_txt_file, "wt") as outfile:
> +            # Using a set will remove automatically the duplicate lines
> +            text_report_content = set()
> +            for file in fragments_list:
> +                try:
> +                    with open(file, "rt") as infile:
> +                        frag_lines = infile.readlines()
> +                except OSError as e:
> +                    raise CppcheckTXTReportError(
> +                            "Issue with reading file {}: {}"
> +                                .format(file, e)
> +                            )
> +                text_report_content.update(frag_lines)
> +
> +            # Back to modifiable list
> +            text_report_content = list(text_report_content)
> +            # Strip path from report lines
> +            for i in list(range(0, len(text_report_content))):
> +                for path in strip_paths:
> +                    text_report_content[i] = text_report_content[i].replace(
> +                                                                path + "/", 
> "")
> +            # Write the final text report
> +            outfile.writelines(text_report_content)
> +    except OSError as e:
> +        raise CppcheckTXTReportError("Issue with writing file {}: {}"
> +                                            .format(out_txt_file, e))
> +
> +
> +def cppcheck_strip_path_html(html_files, strip_paths):
> +    for file in html_files:
> +        try:
> +            with open(file, "rt") as infile:
> +                html_lines = infile.readlines()
> +        except OSError as e:
> +            raise CppcheckHTMLReportError("Issue with reading file {}: {}"
> +                                                            .format(file, e))
> +        for i in list(range(0, len(html_lines))):
> +            for path in strip_paths:
> +                html_lines[i] = html_lines[i].replace(path + "/", "")
> +        try:
> +            with open(file, "wt") as outfile:
> +                outfile.writelines(html_lines)
> +        except OSError as e:
> +            raise CppcheckHTMLReportError("Issue with writing file {}: {}"
> +                                                            .format(file, e))
> diff --git a/xen/scripts/xen_analysis/generic_analysis.py 
> b/xen/scripts/xen_analysis/generic_analysis.py
> index 0b470c4ecf7d..94122aebace0 100644
> --- a/xen/scripts/xen_analysis/generic_analysis.py
> +++ b/xen/scripts/xen_analysis/generic_analysis.py
> @@ -1,7 +1,7 @@
>  #!/usr/bin/env python3
>  
> -import os, subprocess
> -from . import settings, utils, tag_database
> +import os
> +from . import settings, utils, tag_database, cppcheck_analysis
>  
>  class ParseTagPhaseError(Exception):
>      pass
> @@ -60,18 +60,13 @@ def parse_xen_tags():
>  
>  
>  def build_xen():
> -    try:
> -        subprocess.run(
> -            "make -C {} {} build"
> -                .format(settings.xen_dir, settings.make_forward_args),
> -            shell=True, check=True
> +    utils.invoke_command(
> +            "make -C {} {} {} build"
> +                .format(settings.xen_dir, settings.make_forward_args,
> +                        cppcheck_analysis.cppcheck_extra_make_args),
> +            False, BuildPhaseError,
> +            "Build error occured when running:\n{}"
>          )
> -    except (subprocess.CalledProcessError, subprocess.SubprocessError)  as e:
> -        excp = BuildPhaseError(
> -                "Build error occured when running:\n{}".format(e.cmd)
> -            )
> -        excp.errorcode = e.returncode if hasattr(e, 'returncode') else 1
> -        raise excp

Any reason why we can't have utils.invoke_command directly in patch #1?

  
>  def clean_analysis_artifacts():
> diff --git a/xen/scripts/xen_analysis/settings.py 
> b/xen/scripts/xen_analysis/settings.py
> index 947dfa2d50af..bd1faafe79a3 100644
> --- a/xen/scripts/xen_analysis/settings.py
> +++ b/xen/scripts/xen_analysis/settings.py
> @@ -7,14 +7,23 @@ xen_dir = os.path.realpath(module_dir + "/../..")
>  repo_dir = os.path.realpath(xen_dir + "/..")
>  tools_dir = os.path.realpath(xen_dir + "/tools")
>  
> +step_get_make_vars = False
>  step_parse_tags = True
> +step_cppcheck_deps = False
>  step_build_xen = True
> +step_cppcheck_report = False
>  step_clean_analysis = True
> +step_distclean_analysis = False
>  
>  target_build = False
>  target_clean = False
> +target_distclean = False
>  
>  analysis_tool = ""
> +cppcheck_binpath = "cppcheck"
> +cppcheck_html = False
> +cppcheck_htmlreport_binpath = "cppcheck-htmlreport"
> +cppcheck_misra = False
>  make_forward_args = ""
>  outdir = xen_dir
>  
> @@ -26,29 +35,47 @@ Usage: {} [OPTION] ... [-- [make arguments]]
>  This script runs the analysis on the Xen codebase.
>  
>  Options:
> -  --build-only    Run only the commands to build Xen with the optional make
> -                  arguments passed to the script
> -  --clean-only    Run only the commands to clean the analysis artifacts
> -  -h, --help      Print this help
> -  --no-build      Skip the build Xen phase
> -  --no-clean      Don\'t clean the analysis artifacts on exit
> -  --run-coverity  Run the analysis for the Coverity tool
> -  --run-eclair    Run the analysis for the Eclair tool
> +  --build-only          Run only the commands to build Xen with the optional
> +                        make arguments passed to the script
> +  --clean-only          Run only the commands to clean the analysis artifacts
> +  --cppcheck-bin=       Path to the cppcheck binary (Default: {})
> +  --cppcheck-html       Produce an additional HTML output report for Cppcheck
> +  --cppcheck-html-bin=  Path to the cppcheck-html binary (Default: {})
> +  --cppcheck-misra      Activate the Cppcheck MISRA analysis
> +  --distclean           Clean analysis artifacts and reports
> +  -h, --help            Print this help
> +  --no-build            Skip the build Xen phase
> +  --no-clean            Don\'t clean the analysis artifacts on exit
> +  --run-coverity        Run the analysis for the Coverity tool
> +  --run-cppcheck        Run the Cppcheck analysis tool on Xen
> +  --run-eclair          Run the analysis for the Eclair tool
>  """
> -    print(msg.format(sys.argv[0]))
> +    print(msg.format(sys.argv[0], cppcheck_binpath,
> +                     cppcheck_htmlreport_binpath))
>  
>  
>  def parse_commandline(argv):
>      global analysis_tool
> +    global cppcheck_binpath
> +    global cppcheck_html
> +    global cppcheck_htmlreport_binpath
> +    global cppcheck_misra
>      global make_forward_args
>      global outdir
> +    global step_get_make_vars
>      global step_parse_tags
> +    global step_cppcheck_deps
>      global step_build_xen
> +    global step_cppcheck_report
>      global step_clean_analysis
> +    global step_distclean_analysis
>      global target_build
>      global target_clean
> +    global target_distclean
>      forward_to_make = False
>      for option in argv:
> +        args_with_content_regex = re.match(r'^(--[a-z]+[a-z-]*)=(.*)$', 
> option)
> +
>          if forward_to_make:
>              # Intercept outdir
>              outdir_regex = re.match("^O=(.*)$", option)
> @@ -60,6 +87,18 @@ def parse_commandline(argv):
>              target_build = True
>          elif option == "--clean-only":
>              target_clean = True
> +        elif args_with_content_regex and \
> +             args_with_content_regex.group(1) == "--cppcheck-bin":
> +            cppcheck_binpath = args_with_content_regex.group(2)
> +        elif option == "--cppcheck-html":
> +            cppcheck_html = True
> +        elif args_with_content_regex and \
> +             args_with_content_regex.group(1) == "--cppcheck-html-bin":
> +            cppcheck_htmlreport_binpath = args_with_content_regex.group(2)
> +        elif option == "--cppcheck-misra":
> +            cppcheck_misra = True
> +        elif option == "--distclean":
> +            target_distclean = True
>          elif (option == "--help") or (option == "-h"):
>              help()
>              sys.exit(0)
> @@ -69,6 +108,11 @@ def parse_commandline(argv):
>              step_clean_analysis = False
>          elif (option == "--run-coverity") or (option == "--run-eclair"):
>              analysis_tool = option[6:]
> +        elif (option == "--run-cppcheck"):
> +            analysis_tool = "cppcheck"
> +            step_get_make_vars = True
> +            step_cppcheck_deps = True
> +            step_cppcheck_report = True
>          elif option == "--":
>              forward_to_make = True
>          else:
> @@ -76,13 +120,23 @@ def parse_commandline(argv):
>              help()
>              sys.exit(1)
>  
> -    if target_build and target_clean:
> -        print("--build-only is not compatible with --clean-only argument.")
> +    if target_build and (target_clean or target_distclean):
> +        print("--build-only is not compatible with --clean-only/--distclean "
> +              "argument.")
>          sys.exit(1)
>  
> +    if target_distclean:
> +        # Implicit activation of clean target
> +        target_clean = True
> +
> +        step_distclean_analysis = True
> +
>      if target_clean:
> +        step_get_make_vars = False
>          step_parse_tags = False
> +        step_cppcheck_deps = False
>          step_build_xen = False
> +        step_cppcheck_report = False
>          step_clean_analysis = True
>          return
>  
> @@ -95,3 +149,4 @@ def parse_commandline(argv):
>          step_parse_tags = False
>          step_build_xen = True
>          step_clean_analysis = False
> +        step_cppcheck_report = False

I think that target_build should not say anything about
step_cppcheck_report.

- if one wants to just do a regular build, they can do "make xen"
- if one is calling xen-analysis.py --cppcheck-html --run-cppcheck
  --build-only, it means that they want the build done, not the cleaning
  done, not the tags substitution. If they also add --cppcheck-html and
  --run-cppcheck, then it means that they also want the cppcheck report
  produced. --build-only still makes sense because they don't want the
  cleaning done and don't want the tag substitution.

Does it make sense to you as well?


If it does, I think we also need to add a note in the help message from
xen_analysis because it is not clear. So basically:

<nothing>: tags, build, clean [, cppcheck]
--no-clean: tags, build [, cppcheck]
--build-only: build [, cppcheck]
--no-build: tags
--clean-only: clean

Did I get it right?



> diff --git a/xen/scripts/xen_analysis/utils.py 
> b/xen/scripts/xen_analysis/utils.py
> index a912d812c3df..1193e3f4631e 100644
> --- a/xen/scripts/xen_analysis/utils.py
> +++ b/xen/scripts/xen_analysis/utils.py
> @@ -1,6 +1,6 @@
>  #!/usr/bin/env python3
>  
> -import os, re
> +import os, re, subprocess
>  
>  
>  def grep(filepath, regex):
> @@ -35,3 +35,22 @@ def recursive_find_file(path, filename_regex, action = 
> None):
>                          res.append(out)
>  
>      return res
> +
> +
> +def invoke_command(command, needs_output, exeption_type = Exception,
> +                   exeption_msg = ""):
> +    try:
> +        pipe_stdout = subprocess.PIPE if (needs_output == True) else None
> +        output = subprocess.run(command, shell=True, check=True,
> +                                stdout=pipe_stdout, stderr=subprocess.STDOUT,
> +                                encoding='utf8')
> +    except (subprocess.CalledProcessError, subprocess.SubprocessError) as e:
> +        if needs_output == True:
> +            exeption_msg = exeption_msg.format(e.cmd, output.stdout)
> +        else:
> +            exeption_msg = exeption_msg.format(e.cmd)
> +        excp = exeption_type(exeption_msg)
> +        excp.errorcode = e.returncode if hasattr(e, 'returncode') else 1
> +        raise excp
> +
> +    return output.stdout
> diff --git a/xen/tools/cppcheck-cc.sh b/xen/tools/cppcheck-cc.sh
> new file mode 100755
> index 000000000000..deb3a19918e9
> --- /dev/null
> +++ b/xen/tools/cppcheck-cc.sh
> @@ -0,0 +1,223 @@
> +#!/usr/bin/env bash
> +
> +set -e
> +
> +function help() {
> +    cat <<EOF
> +Usage: ${0} [OPTION] ... -- <compiler arguments>
> +
> +This script is a wrapper for cppcheck that enables it to analyse the files 
> that
> +are the target for the build, it is used in place of a selected compiler and 
> the
> +make process will run it on every file that needs to be built.
> +All the arguments passed to the original compiler are forwarded to it without
> +modification, furthermore, they are used to improve the cppcheck analysis.
> +
> +Options:
> +  --compiler=       Use this compiler for the build
> +  --cppcheck-cmd=   Command line for the cppcheck analysis.
> +  --cppcheck-html   Prepare for cppcheck HTML output
> +  --cppcheck-plat=  Path to the cppcheck platform folder
> +  --ignore-path=    This script won't run cppcheck on the files having this
> +                    path, the compiler will run anyway on them. This argument
> +                    can be specified multiple times.
> +  -h, --help        Print this help
> +EOF
> +}
> +
> +CC_FILE=""
> +COMPILER=""
> +CPPCHECK_HTML="n"
> +CPPCHECK_PLAT_PATH=""
> +CPPCHECK_TOOL=""
> +CPPCHECK_TOOL_ARGS=""
> +FORWARD_FLAGS=""
> +IGNORE_PATH="n"
> +IGNORE_PATH_LIST=""
> +JDB_FILE=""
> +OBJTREE_PATH=""
> +
> +# Variable used for arg parsing
> +forward_to_cc="n"
> +sm_tool_args="n"
> +obj_arg_content="n"
> +
> +for OPTION in "$@"
> +do
> +    if [ "${forward_to_cc}" = "y" ]; then
> +        if [[ ${OPTION} == *.c ]]
> +        then
> +            CC_FILE="${OPTION}"
> +        elif [ "${OPTION}" = "-o" ]
> +        then
> +            # After -o there is the path to the obj file, flag it
> +            obj_arg_content="y"
> +        elif [ "${obj_arg_content}" = "y" ]
> +        then
> +            # This must be the path to the obj file, turn off flag and save 
> path
> +            OBJTREE_PATH="$(dirname "${OPTION}")"
> +            obj_arg_content="n"
> +        fi
> +        # Forward any argument to the compiler
> +        FORWARD_FLAGS="${FORWARD_FLAGS} ${OPTION}"
> +        continue
> +    fi
> +    case ${OPTION} in
> +        -h|--help)
> +            help
> +            exit 0
> +            ;;
> +        --compiler=*)
> +            COMPILER="${OPTION#*=}"
> +            sm_tool_args="n"
> +            ;;
> +        --cppcheck-cmd=*)
> +            CPPCHECK_TOOL="${OPTION#*=}"
> +            sm_tool_args="y"
> +            ;;
> +        --cppcheck-html)
> +            CPPCHECK_HTML="y"
> +            sm_tool_args="n"
> +            ;;
> +        --cppcheck-plat=*)
> +            CPPCHECK_PLAT_PATH="${OPTION#*=}"
> +            sm_tool_args="n"
> +            ;;
> +        --ignore-path=*)
> +            IGNORE_PATH_LIST="${IGNORE_PATH_LIST} ${OPTION#*=}"
> +            sm_tool_args="n"
> +            ;;
> +        --)
> +            forward_to_cc="y"
> +            sm_tool_args="n"
> +            ;;
> +        *)
> +            if [ "${sm_tool_args}" = "y" ]; then
> +                CPPCHECK_TOOL_ARGS="${CPPCHECK_TOOL_ARGS} ${OPTION}"
> +            else
> +                echo "Invalid option ${OPTION}"
> +                exit 1
> +            fi
> +            ;;
> +    esac
> +done
> +
> +if [ "${COMPILER}" = "" ]
> +then
> +    echo "--compiler arg is mandatory."
> +    exit 1
> +fi
> +
> +function create_jcd() {
> +    local line="${1}"
> +    local arg_num=0
> +    local same_line=0
> +
> +    {
> +        echo -e -n "[\n"

Everywhere in this bash function: there is no point in passing -n and
then adding \n at the end. You might as well do this:

echo -e "["

Also, you'll find that in most cases, you don't need -e either, which
simplifies it to:

echo "["

That's better right? :-)  Of course feel free to use -e when you have
escape and -n when you don't want \n in the output


> +        echo -e -n "    {\n"
> +        echo -e -n "        \"arguments\": [\n"
> +
> +        for arg in ${line}; do
> +            # This code prevents to put comma in the last element of the 
> list or
> +            # on sequential lines that are going to be merged
> +            if [ "${arg_num}" -ne 0 ] && [ "${same_line}" -eq 0 ]
> +            then
> +                echo -e -n ",\n"
> +            fi
> +            if [ "${same_line}" -ne 0 ]
> +            then
> +                echo -e -n "${arg}\""
> +                same_line=0
> +            elif [ "${arg}" = "-iquote" ] || [ "${arg}" = "-I" ]
> +            then
> +                # cppcheck doesn't understand -iquote, substitute with -I
> +                echo -e -n "            \"-I"
> +                same_line=1
> +            else
> +                echo -e -n "            \"${arg}\""
> +            fi
> +            arg_num=$(( arg_num + 1 ))
> +        done
> +        echo -e -n "\n"
> +        echo -e -n "        ],\n"
> +        echo -e -n "        \"directory\": \"$(pwd -P)\",\n"
> +        echo -e -n "        \"file\": \"${CC_FILE}\"\n"
> +        echo -e -n "    }\n"
> +        echo -e -n "]\n"
> +    } > "${JDB_FILE}"
> +}
> +
> +
> +# Execute compiler with forwarded flags
> +# Shellcheck complains about missing quotes on FORWARD_FLAGS, but they can't 
> be
> +# used here
> +# shellcheck disable=SC2086
> +${COMPILER} ${FORWARD_FLAGS}
> +
> +if [ -n "${CC_FILE}" ];
> +then
> +    for path in ${IGNORE_PATH_LIST}
> +    do
> +        if [[ ${CC_FILE} == *${path}* ]]
> +        then
> +            IGNORE_PATH="y"
> +            echo "${0}: ${CC_FILE} ignored by --ignore-path matching 
> *${path}*"
> +        fi
> +    done
> +    if [ "${IGNORE_PATH}" = "n" ]
> +    then
> +        JDB_FILE="${OBJTREE_PATH}/$(basename "${CC_FILE}".json)"
> +
> +        # Prepare the Json Compilation Database for the file
> +        create_jcd "${COMPILER} ${FORWARD_FLAGS}"
> +
> +        out_file="${OBJTREE_PATH}/$(basename "${CC_FILE%.c}".cppcheck.txt)"
> +
> +        # Select the right target platform, ARCH is generated from Xen 
> Makefile
> +        case ${ARCH} in
> +            arm64)
> +                # arm64 has efi code compiled with -fshort-wchar
> +                platform="${CPPCHECK_PLAT_PATH}/arm64-wchar_t2.xml"
> +                ;;
> +            arm32)
> +                # arm32 has no efi code
> +                platform="${CPPCHECK_PLAT_PATH}/arm32-wchar_t4.xml"
> +                ;;
> +            x86_64)
> +                # x86_64 has efi code compiled with -fshort-wchar
> +                platform="${CPPCHECK_PLAT_PATH}/x86_64-wchar_t2.xml"
> +                ;;
> +            *)
> +                echo "ARCH: ${ARCH} not expected!"
> +                exit 1
> +                ;;
> +        esac
> +
> +        if [ ! -f "${platform}" ]
> +        then
> +            echo "${platform} not found!"
> +            exit 1
> +        fi
> +
> +        # Shellcheck complains about missing quotes on CPPCHECK_TOOL_ARGS, 
> but
> +        # they can't be used here
> +        # shellcheck disable=SC2086
> +        ${CPPCHECK_TOOL} ${CPPCHECK_TOOL_ARGS} \
> +            --project="${JDB_FILE}" \
> +            --output-file="${out_file}" \
> +            --platform="${platform}"
> +
> +        if [ "${CPPCHECK_HTML}" = "y" ]
> +        then
> +            # Shellcheck complains about missing quotes on 
> CPPCHECK_TOOL_ARGS,
> +            # but they can't be used here
> +            # shellcheck disable=SC2086
> +            ${CPPCHECK_TOOL} ${CPPCHECK_TOOL_ARGS} \
> +                --project="${JDB_FILE}" \
> +                --output-file="${out_file%.txt}.xml" \
> +                --platform="${platform}" \
> +                -q \
> +                --xml
> +        fi
> +    fi
> +fi

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.