1#!/usr/bin/env python3
2#
3# Arm SCP/MCP Software
4# Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
5#
6# SPDX-License-Identifier: BSD-3-Clause
7#
8
9"""
10    A wrapper around cppcheck which will be called from CMake's context.
11
12    CMake doesn't give any mechanism to execute any programs or
13    commands prior or after cppcheck's invocation.
14
15    Usually CMAKE_C_CPPCHECK will contain the path to cppcheck binary
16    on the build system, instead CMAKE_C_CPPCHECK will now contain
17    path to this script which in turn calls cppcheck but this enables
18    to inject any commands prior to and after actual invocation of
19    cppcheck.
20
21    One use case would be to mark cppcheck's invocation as below
22
23    [CPPCHECK] ------>
24    ....
25    ....
26    [CPPCHECK] <------
27
28    This script also prints a warning if the version of the cppcheck
29    does not match the required version.
30
31"""
32
33import sys
34import subprocess
35import re
36
37# Holds required version info.
38required_tool_version = "1.90"
39
40tool_name = "CPPCHECK"
41
42
43# Helper function
44def print_msg(message, output):
45    if output:
46        print("[{}]  {}".format(tool_name, message))
47
48
49def main():
50    # CMake create a list of argument which include path of the
51    # tool(e.g. cppcheck) binary and its arguments.
52    tool_path = sys.argv[1]
53    tool_args = ' '.join(sys.argv[2:])
54    cmd = "{} {}".format(tool_path, tool_args)
55
56    # Check if output of the tool (e.g. cppcheck) is to be displayed
57    if re.search("verbose", tool_args):
58        verbose = True
59    else:
60        verbose = False
61
62    # If yes, then remove verbose string from argument list.
63    if verbose:
64        tool_args.replace("verbose", "")
65
66    # Check version
67    r = subprocess.Popen(tool_path + " --version",
68                         shell=True,
69                         stdout=subprocess.PIPE,
70                         stderr=subprocess.PIPE)
71
72    out, err = r.communicate()
73
74    tool_version = out.decode("utf-8", "ignore")
75
76    if out:
77        print_msg("------>", output=verbose)
78        print_msg("cppcheck {}"
79                  " version {} ".format(tool_path, tool_version),
80                  output=verbose)
81
82        if not re.search(required_tool_version, tool_version):
83            print_msg("Warning: recommended"
84                      " {} version is {}"
85                      " but found {}".format(tool_name,
86                                             required_tool_version,
87                                             tool_version),
88                      output=True)
89    if err:
90        print_msg("Error: failed to find"
91                  " binary at {}".format(tool_path),
92                  output=True)
93        return 1
94
95    print_msg(cmd, output=verbose)
96
97    r = subprocess.Popen(cmd,
98                         shell=True,
99                         stdout=subprocess.PIPE,
100                         stderr=subprocess.PIPE)
101
102    out, err = r.communicate()
103
104    if out:
105        print_msg("" + out.decode("utf-8", "ignore"), output=verbose)
106    if err:
107        print_msg("" + err.decode("utf-8", "ignore"), output=True)
108
109    print_msg("<------", output=verbose)
110    return r.returncode
111
112
113if __name__ == '__main__':
114    sys.exit(main())
115