1#!/usr/bin/python2.7
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 re
8import sys
9import glob
10import shutil
11import errno
12
13import ThirdPartyPaths
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
35def write_cmake(module_path):
36  names = map(lambda f: '  ' + os.path.basename(f),
37              glob.glob("%s/*.cpp" % module_path))
38  with open(os.path.join(module_path, 'CMakeLists.txt'), 'wb') as f:
39    f.write("""set(LLVM_LINK_COMPONENTS support)
40
41add_definitions( -DCLANG_TIDY )
42add_definitions( -DHAVE_NEW_ASTMATCHER_NAMES )
43
44add_clang_library(clangTidyMozillaModule
45  ThirdPartyPaths.cpp
46%(names)s
47
48  LINK_LIBS
49  clangAST
50  clangASTMatchers
51  clangBasic
52  clangLex
53  clangTidy
54  clangTidyReadabilityModule
55  clangTidyUtils
56  )""" % {'names': "\n".join(names)})
57
58def add_item_to_cmake_section(cmake_path, section, library):
59  with open(cmake_path, 'r') as f:
60    lines = f.readlines()
61  f.close()
62
63  libs = []
64  seen_target_libs = False
65  for line in lines:
66    if line.find(section) > -1:
67      seen_target_libs = True
68    elif seen_target_libs:
69      if line.find(')') > -1:
70        break
71      else:
72        libs.append(line.strip())
73  libs.append(library)
74  libs = sorted(libs, key = lambda s: s.lower())
75
76  with open(cmake_path, 'wb') as f:
77    seen_target_libs = False
78    for line in lines:
79      if line.find(section) > -1:
80        seen_target_libs = True
81        f.write(line)
82        f.writelines(map(lambda p: '  ' + p + '\n', libs))
83        continue
84      elif seen_target_libs:
85        if line.find(')') > -1:
86          seen_target_libs = False
87        else:
88          continue
89      f.write(line)
90
91  f.close()
92
93
94def write_third_party_paths(mozilla_path, module_path):
95  tpp_txt = os.path.join(mozilla_path, '../../tools/rewriting/ThirdPartyPaths.txt')
96  with open(os.path.join(module_path, 'ThirdPartyPaths.cpp'), 'w') as f:
97    ThirdPartyPaths.generate(f, tpp_txt)
98
99
100def do_import(mozilla_path, clang_tidy_path):
101  module = 'mozilla'
102  module_path = os.path.join(clang_tidy_path, module)
103  if not os.path.isdir(module_path):
104      os.mkdir(module_path)
105
106  copy_dir_contents(mozilla_path, module_path)
107  write_third_party_paths(mozilla_path, module_path)
108  write_cmake(module_path)
109  add_item_to_cmake_section(os.path.join(module_path, '..', 'plugin',
110                                         'CMakeLists.txt'),
111                            'LINK_LIBS', 'clangTidyMozillaModule')
112  add_item_to_cmake_section(os.path.join(module_path, '..', 'tool',
113                                         'CMakeLists.txt'),
114                            'target_link_libraries', 'clangTidyMozillaModule')
115  with open(os.path.join(module_path, '..', 'CMakeLists.txt'), 'a') as f:
116    f.write('add_subdirectory(%s)\n' % module)
117  with open(os.path.join(module_path, '..', 'tool', 'ClangTidyMain.cpp'), 'a') as f:
118    f.write('''
119// This anchor is used to force the linker to link the MozillaModule.
120extern volatile int MozillaModuleAnchorSource;
121static int LLVM_ATTRIBUTE_UNUSED MozillaModuleAnchorDestination =
122          MozillaModuleAnchorSource;
123''')
124
125def main():
126  if len(sys.argv) != 3:
127    print """\
128Usage: import_mozilla_checks.py <mozilla-clang-plugin-path> <clang-tidy-path>
129Imports the Mozilla static analysis checks into a clang-tidy source tree.
130"""
131
132    return
133
134  mozilla_path = sys.argv[1]
135  if not os.path.isdir(mozilla_path):
136      print "Invalid path to mozilla clang plugin"
137
138  clang_tidy_path = sys.argv[2]
139  if not os.path.isdir(mozilla_path):
140      print "Invalid path to clang-tidy source directory"
141
142  do_import(mozilla_path, clang_tidy_path)
143
144
145if __name__ == '__main__':
146  main()
147