Browse Source

[CI] Added feature to filter out files to file_check.py that do not require cppcheck (#7499)

Co-authored-by: supperthomas <78900636@qq.com>
dejavudwh 1 year ago
parent
commit
b1584e9fa9
3 changed files with 150 additions and 9 deletions
  1. 3 9
      .github/workflows/static_code_analysis.yml
  2. 63 0
      tools/ci/cpp_check.py
  3. 84 0
      tools/ci/format_ignore.py

+ 3 - 9
.github/workflows/static_code_analysis.yml

@@ -1,6 +1,6 @@
 name: Static code analysis
 
-on: 
+on:
   pull_request:
     branches:
       - master
@@ -21,16 +21,10 @@ jobs:
         run: |
           sudo apt-get update
           sudo apt-get -qq install cppcheck
+          pip install click PyYaml
           git remote -v
           git fetch origin
           cppcheck --version
           ls
           git branch -a
-          changed_files=$(git diff --name-only HEAD origin/master | grep -E '\.(c|cpp|cc|cxx)$' || true)
-          if [ -n "$changed_files" ];then
-              cppcheck --enable=warning,performance,portability --inline-suppr --error-exitcode=1 --force $changed_files
-              err=$?
-              if [ $err -ne 0 ]; then
-                  echo "CPPCHECK REPORT, PLEASE CHECK THE WARNING !!!!!!!!!"
-              fi
-          fi
+          python tools/ci/cpp_check.py check

+ 63 - 0
tools/ci/cpp_check.py

@@ -0,0 +1,63 @@
+#
+# Copyright (c) 2006-2023, RT-Thread Development Team
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Change Logs:
+# Date           Author       Notes
+# 2023-05-16     dejavudwh    the first version
+#
+
+import click
+import logging
+import subprocess
+import sys
+import format_ignore
+
+class CPPCheck:
+    def __init__(self, file_list):
+        self.file_list = file_list
+        
+    def check(self):
+        file_list_filtered = [file for file in self.file_list if file.endswith(('.c', '.cpp', '.cc', '.cxx'))]
+        logging.info("Start to static code analysis.")
+        check_result = True
+        for file in file_list_filtered:
+            result = subprocess.run(['cppcheck', '--enable=warning', 'performance', 'portability', '--inline-suppr', '--error-exitcode=1', '--force', file], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+            logging.info(result.stdout.decode())
+            logging.info(result.stderr.decode())
+            if result.stderr:
+                check_result = False
+        return check_result
+
+@click.group()
+@click.pass_context
+def cli(ctx):
+    pass
+
+@cli.command()
+def check():
+    """
+    static code analysis(cppcheck).
+    """
+    format_ignore.init_logger()
+    # get modified files list
+    checkout = format_ignore.CheckOut()
+    file_list = checkout.get_new_file()
+    if file_list is None:
+        logging.error("checkout files fail")
+        sys.exit(1)
+
+    # use cppcheck
+    cpp_check = CPPCheck(file_list)
+    cpp_check_result = cpp_check.check()
+
+    if not cpp_check_result:
+        logging.error("static code analysis(cppcheck) fail.")
+        sys.exit(1)
+    logging.info("check success.")
+    sys.exit(0)
+
+
+if __name__ == '__main__':
+    cli()

+ 84 - 0
tools/ci/format_ignore.py

@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2006-2023, RT-Thread Development Team
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Change Logs:
+# Date           Author       Notes
+# 2023-05-16     dejavudwh    the first version
+#
+
+import yaml
+import logging
+import os
+import subprocess
+
+def init_logger():
+    log_format = "[%(filename)s %(lineno)d %(levelname)s] %(message)s "
+    date_format = '%Y-%m-%d  %H:%M:%S %a '
+    logging.basicConfig(level=logging.INFO,
+                        format=log_format,
+                        datefmt=date_format,
+                        )
+
+class CheckOut:
+    def __init__(self):
+        pass
+    
+    def __exclude_file(self, file_path):
+        dir_number = file_path.split('/')
+        ignore_path = file_path
+
+        # gets the file path depth.
+        for i in dir_number:
+            # current directory.
+            dir_name = os.path.dirname(ignore_path)
+            ignore_path = dir_name
+            # judge the ignore file exists in the current directory.
+            ignore_file_path = os.path.join(dir_name, ".ignore_format.yml")
+            if not os.path.exists(ignore_file_path):
+                continue
+            try:
+                with open(ignore_file_path) as f:
+                    ignore_config = yaml.safe_load(f.read())
+                file_ignore = ignore_config.get("file_path", [])
+                dir_ignore = ignore_config.get("dir_path", [])
+            except Exception as e:
+                logging.error(e)
+                continue
+            logging.debug("ignore file path: {}".format(ignore_file_path))
+            logging.debug("file_ignore: {}".format(file_ignore))
+            logging.debug("dir_ignore: {}".format(dir_ignore))
+            try:
+                # judge file_path in the ignore file.
+                for file in file_ignore:
+                    if file is not None:
+                        file_real_path = os.path.join(dir_name, file)
+                        if file_real_path == file_path:
+                            logging.info("ignore file path: {}".format(file_real_path))
+                            return 0
+
+                file_dir_path = os.path.dirname(file_path)
+                for _dir in dir_ignore:
+                    if _dir is not None:
+                        dir_real_path = os.path.join(dir_name, _dir)
+                        if file_dir_path.startswith(dir_real_path):
+                            logging.info("ignore dir path: {}".format(dir_real_path))
+                            return 0
+            except Exception as e:
+                logging.error(e)
+                continue
+
+        return 1
+    
+    def get_new_file(self):
+        result = subprocess.run(['git', 'diff', '--name-only', 'HEAD', 'origin/master', '--diff-filter=ACMR', '--no-renames', '--full-index'], stdout = subprocess.PIPE)
+        file_list = result.stdout.decode().strip().split('\n')
+        new_files = []
+        for line in file_list:
+            logging.info("modified file -> {}".format(line))
+            result = self.__exclude_file(line)
+            if result != 0:
+                new_files.append(line)
+        
+        return new_files