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 6import os 7import glob 8import shutil 9import errno 10 11import ThirdPartyPaths 12import ThreadAllows 13 14 15def 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 36def 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 55add_definitions( -DCLANG_TIDY ) 56 57add_clang_library(clangTidyMozillaModule 58 ThirdPartyPaths.cpp 59%(names)s 60 61 LINK_LIBS 62 clangAST 63 clangASTMatchers 64 clangBasic 65 clangLex 66 clangTidy 67 clangTidyReadabilityModule 68 clangTidyUtils 69 clangTidyMPIModule 70 )""" 71 % {"names": "\n".join(names)} 72 ) 73 74 75def add_moz_module(cmake_path): 76 with open(cmake_path, "r") as f: 77 lines = f.readlines() 78 f.close() 79 80 try: 81 idx = lines.index("set(ALL_CLANG_TIDY_CHECKS\n") 82 lines.insert(idx + 1, " clangTidyMozillaModule\n") 83 84 with open(cmake_path, "w") as f: 85 for line in lines: 86 f.write(line) 87 except ValueError: 88 raise Exception("Unable to find ALL_CLANG_TIDY_CHECKS in {}".format(cmake_path)) 89 90 91def write_third_party_paths(mozilla_path, module_path): 92 tpp_txt = os.path.join(mozilla_path, "../../tools/rewriting/ThirdPartyPaths.txt") 93 generated_txt = os.path.join(mozilla_path, "../../tools/rewriting/Generated.txt") 94 with open(os.path.join(module_path, "ThirdPartyPaths.cpp"), "w") as f: 95 ThirdPartyPaths.generate(f, tpp_txt, generated_txt) 96 97 98def generate_thread_allows(mozilla_path, module_path): 99 names = os.path.join(mozilla_path, "../../build/clang-plugin/ThreadAllows.txt") 100 files = os.path.join(mozilla_path, "../../build/clang-plugin/ThreadFileAllows.txt") 101 with open(os.path.join(module_path, "ThreadAllows.h"), "w") as f: 102 f.write(ThreadAllows.generate_allows({files, names})) 103 104 105def do_import(mozilla_path, clang_tidy_path, import_options): 106 module = "mozilla" 107 module_path = os.path.join(clang_tidy_path, module) 108 try: 109 os.makedirs(module_path) 110 except OSError as e: 111 if e.errno != errno.EEXIST: 112 raise 113 114 copy_dir_contents(mozilla_path, module_path) 115 write_third_party_paths(mozilla_path, module_path) 116 generate_thread_allows(mozilla_path, module_path) 117 write_cmake(module_path, import_options) 118 add_moz_module(os.path.join(module_path, "..", "CMakeLists.txt")) 119 with open(os.path.join(module_path, "..", "CMakeLists.txt"), "a") as f: 120 f.write("add_subdirectory(%s)\n" % module) 121 # A better place for this would be in `ClangTidyForceLinker.h` but `ClangTidyMain.cpp` 122 # is also OK. 123 with open(os.path.join(module_path, "..", "tool", "ClangTidyMain.cpp"), "a") as f: 124 f.write( 125 """ 126// This anchor is used to force the linker to link the MozillaModule. 127extern volatile int MozillaModuleAnchorSource; 128static int LLVM_ATTRIBUTE_UNUSED MozillaModuleAnchorDestination = 129 MozillaModuleAnchorSource; 130""" 131 ) 132 133 134def main(): 135 import argparse 136 137 parser = argparse.ArgumentParser( 138 usage="import_mozilla_checks.py <mozilla-clang-plugin-path> <clang-tidy-path> [option]", 139 description="Imports the Mozilla static analysis checks into a clang-tidy source tree.", 140 ) 141 parser.add_argument( 142 "mozilla_path", help="Full path to mozilla-central/build/clang-plugin" 143 ) 144 parser.add_argument( 145 "clang_tidy_path", help="Full path to llvm-project/clang-tools-extra/clang-tidy" 146 ) 147 parser.add_argument( 148 "--import-alpha", 149 help="Enable import of in-tree alpha checks", 150 action="store_true", 151 ) 152 parser.add_argument( 153 "--import-external", 154 help="Enable import of in-tree external checks", 155 action="store_true", 156 ) 157 args = parser.parse_args() 158 159 if not os.path.isdir(args.mozilla_path): 160 print("Invalid path to mozilla clang plugin") 161 162 if not os.path.isdir(args.clang_tidy_path): 163 print("Invalid path to clang-tidy source directory") 164 165 import_options = {"alpha": args.import_alpha, "external": args.import_external} 166 167 do_import(args.mozilla_path, args.clang_tidy_path, import_options) 168 169 170if __name__ == "__main__": 171 main() 172