import_mozilla_checks.py (5650B)
1 #!/usr/bin/python3 2 # This Source Code Form is subject to the terms of the Mozilla Public 3 # License, v. 2.0. If a copy of the MPL was not distributed with this 4 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6 import errno 7 import glob 8 import os 9 import shutil 10 11 import ThirdPartyPaths 12 import ThreadAllows 13 14 15 def copy_dir_contents(src, dest): 16 for f in glob.glob("%s/*" % src): 17 try: 18 destname = "%s/%s" % (dest, os.path.basename(f)) 19 if os.path.isdir(f): 20 shutil.copytree(f, destname) 21 else: 22 shutil.copy2(f, destname) 23 except OSError as e: 24 if e.errno == errno.ENOTDIR: 25 shutil.copy2(f, destname) 26 elif e.errno == errno.EEXIST: 27 if os.path.isdir(f): 28 copy_dir_contents(f, destname) 29 else: 30 os.remove(destname) 31 shutil.copy2(f, destname) 32 else: 33 raise Exception("Directory not copied. Error: %s" % e) 34 35 36 def write_cmake(module_path, import_options): 37 names = [" " + os.path.basename(f) for f in glob.glob("%s/*.cpp" % module_path)] 38 39 if import_options["external"]: 40 names += [ 41 " " + os.path.join("external", os.path.basename(f)) 42 for f in glob.glob("%s/external/*.cpp" % (module_path)) 43 ] 44 45 if import_options["alpha"]: 46 names += [ 47 " " + os.path.join("alpha", os.path.basename(f)) 48 for f in glob.glob("%s/alpha/*.cpp" % (module_path)) 49 ] 50 51 with open(os.path.join(module_path, "CMakeLists.txt"), "w") as f: 52 f.write( 53 """set(LLVM_LINK_COMPONENTS support) 54 55 add_definitions( -DCLANG_TIDY ) 56 57 add_clang_library(clangTidyMozillaModule 58 ThirdPartyPaths.cpp 59 %(names)s 60 61 LINK_LIBS 62 clangTidy 63 clangTidyReadabilityModule 64 clangTidyUtils 65 clangTidyMPIModule 66 ) 67 68 clang_target_link_libraries(clangTidyMozillaModule 69 PRIVATE 70 clangAST 71 clangASTMatchers 72 clangBasic 73 clangLex 74 )""" 75 % {"names": "\n".join(names)} 76 ) 77 78 79 def add_moz_module(cmake_path): 80 with open(cmake_path) as f: 81 lines = f.readlines() 82 f.close() 83 84 try: 85 idx = lines.index("set(ALL_CLANG_TIDY_CHECKS\n") 86 lines.insert(idx + 1, " clangTidyMozillaModule\n") 87 88 with open(cmake_path, "w") as f: 89 for line in lines: 90 f.write(line) 91 except ValueError: 92 raise Exception(f"Unable to find ALL_CLANG_TIDY_CHECKS in {cmake_path}") 93 94 95 def write_third_party_paths(mozilla_path, module_path): 96 tpp_txt = os.path.join(mozilla_path, "../../tools/rewriting/ThirdPartyPaths.txt") 97 generated_txt = os.path.join(mozilla_path, "../../tools/rewriting/Generated.txt") 98 with open(os.path.join(module_path, "ThirdPartyPaths.cpp"), "w") as f: 99 ThirdPartyPaths.generate(f, tpp_txt, generated_txt) 100 101 102 def generate_thread_allows(mozilla_path, module_path): 103 names = os.path.join(mozilla_path, "../../build/clang-plugin/ThreadAllows.txt") 104 files = os.path.join(mozilla_path, "../../build/clang-plugin/ThreadFileAllows.txt") 105 with open(os.path.join(module_path, "ThreadAllows.h"), "w") as f: 106 f.write( 107 ThreadAllows.generate_allows(allowed_names=[names], allowed_files=[files]) 108 ) 109 110 111 def do_import(mozilla_path, clang_tidy_path, import_options): 112 module = "mozilla" 113 module_path = os.path.join(clang_tidy_path, module) 114 try: 115 os.makedirs(module_path) 116 except OSError as e: 117 if e.errno != errno.EEXIST: 118 raise 119 120 copy_dir_contents(mozilla_path, module_path) 121 write_third_party_paths(mozilla_path, module_path) 122 generate_thread_allows(mozilla_path, module_path) 123 write_cmake(module_path, import_options) 124 add_moz_module(os.path.join(module_path, "..", "CMakeLists.txt")) 125 with open(os.path.join(module_path, "..", "CMakeLists.txt"), "a") as f: 126 f.write("add_subdirectory(%s)\n" % module) 127 # A better place for this would be in `ClangTidyForceLinker.h` but `ClangTidyMain.cpp` 128 # is also OK. 129 with open(os.path.join(module_path, "..", "tool", "ClangTidyMain.cpp"), "a") as f: 130 f.write( 131 """ 132 // This anchor is used to force the linker to link the MozillaModule. 133 extern volatile int MozillaModuleAnchorSource; 134 static int LLVM_ATTRIBUTE_UNUSED MozillaModuleAnchorDestination = 135 MozillaModuleAnchorSource; 136 """ 137 ) 138 139 140 def main(): 141 import argparse 142 143 parser = argparse.ArgumentParser( 144 usage="import_mozilla_checks.py <mozilla-clang-plugin-path> <clang-tidy-path> [option]", 145 description="Imports the Mozilla static analysis checks into a clang-tidy source tree.", 146 ) 147 parser.add_argument( 148 "mozilla_path", help="Full path to mozilla-central/build/clang-plugin" 149 ) 150 parser.add_argument( 151 "clang_tidy_path", help="Full path to llvm-project/clang-tools-extra/clang-tidy" 152 ) 153 parser.add_argument( 154 "--import-alpha", 155 help="Enable import of in-tree alpha checks", 156 action="store_true", 157 ) 158 parser.add_argument( 159 "--import-external", 160 help="Enable import of in-tree external checks", 161 action="store_true", 162 ) 163 args = parser.parse_args() 164 165 if not os.path.isdir(args.mozilla_path): 166 print("Invalid path to mozilla clang plugin") 167 168 if not os.path.isdir(args.clang_tidy_path): 169 print("Invalid path to clang-tidy source directory") 170 171 import_options = {"alpha": args.import_alpha, "external": args.import_external} 172 173 do_import(args.mozilla_path, args.clang_tidy_path, import_options) 174 175 176 if __name__ == "__main__": 177 main()