• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -- coding: utf-8 --
3# Copyright (c) 2022-2024 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# cmake-checker.py checks that panda wrappers are used instead of standard
17# cmake functions.
18# cmake-checker.py <DIRECTORY> [TEST]
19# - <DIRECTORY>
20#       Root directory for checking
21# - [TEST]
22#       Test that cmake-checker work properly
23
24import os
25import re
26import sys
27import tempfile
28import shutil
29import subprocess
30
31# List of functions to search for
32function_list = [("add_library", "panda_add_library"),
33                 ("add_executable", "panda_add_executable"),
34                 ("target_link_libraries", "panda_target_link_libraries"),
35                 ("target_include_directories", "panda_target_include_directories"),
36                 ("target_compile_options", "panda_target_compile_options"),
37                 ("target_compile_definitions", "panda_target_compile_definitions"),
38                 ("target_sources", "panda_target_sources")]
39
40ERROR_MESSAGE = "Found restricted native CMake function usage:"
41
42
43def run_cmake_checker(directory):
44    # don't search in following files and folders
45    ignore_files = ["cmake/PandaCmakeFunctions.cmake", "cmake/Sanitizers.cmake"]
46    ignore_files = [os.path.join(directory, s) for s in ignore_files]
47    ignore_paths = ["third_party"]
48
49    # search in following third_party folders
50    white_list_paths = ["cmake/third_party"]
51
52    cmake_files = []
53
54    # Fetch all cmake files in directory
55    for root, dirs, files in os.walk(directory, followlinks=True):
56        ignore = False
57        for white_path in white_list_paths:
58            # Check if root directory matches any white list path
59            if root.startswith(os.path.join(directory, white_path)):
60                continue
61            else:
62                # Check if root directory matches any ignore path
63                for ignore_path in ignore_paths:
64                    if root.startswith(os.path.join(directory, ignore_path)) or "third_party" in root:
65                        ignore = True
66                        dirs.clear()
67                        break
68
69        if ignore:
70            continue
71
72        for file in files:
73            if file == "CMakeLists.txt" or file.endswith(".cmake") and os.path.join(root, file) not in ignore_files:
74                cmake_files.append(os.path.join(root, file))
75
76    error = False
77    # Grep for function in each cmake file
78    for file in cmake_files:
79        with open(file, 'r') as f:
80            lines = f.readlines()
81            line_number = 1
82            for line in lines:
83                for function_name in function_list:
84                    if re.search(r'\b{}\b'.format(function_name[0]), line.split('#')[0]):
85                        if not error:
86                            print(ERROR_MESSAGE)
87                        error = True
88                        print("  {} instead of {} at {}:{}".format(function_name[0], function_name[1],
89                            os.path.relpath(file, directory), line_number), file=sys.stderr)
90                line_number += 1
91
92    if error:
93        exit(1)
94
95    print("cmake-checker passed successfully!")
96
97
98# create file that uses standard cmake functions and check that cmake_checker will find them
99def test_cmake_checker(directory):
100    source_file = os.path.join(directory, "CMakeLists.txt")
101
102    temp_dir = tempfile.mkdtemp(dir=directory)
103    temp_file = os.path.join(temp_dir, "CMakeLists.txt")
104
105    try:
106        shutil.copy(source_file, temp_file)
107
108        with open(temp_file, 'r') as file:
109            content = file.read()
110
111        # replace wrappers with standard cmake functions and add standard functions in the end
112        # in case we don't have them in main CMakeLists.txt
113        for function in function_list:
114            content = content.replace(function[1], function[0])
115            content = f"{content}{function[0]}\n"
116
117        with os.fdopen(os.open(temp_file, os.O_RDWR | os.O_CREAT, 0o755), 'w') as file:
118            file.write(content)
119
120        args = [sys.argv[0], directory]
121        process = subprocess.run(args, capture_output=True)
122
123        if process.returncode == 1 and ERROR_MESSAGE in process.stdout.decode():
124            print("test-cmake-checker passed successfully!")
125        else:
126            sys.exit("Failed: cmake-checker doesn't work properly.")
127
128    finally:
129        shutil.rmtree(temp_dir)
130
131if __name__ == "__main__":
132    arg_directory = sys.argv[1]
133    if len(sys.argv) == 3:
134        test_cmake_checker(arg_directory)
135    else:
136        run_cmake_checker(arg_directory)
137