3 """Helps to keep BUILD.gn files in sync with the corresponding CMakeLists.txt.
5 For each BUILD.gn file in the tree, checks if the list of cpp files in
6 it is identical to the list of cpp files in the corresponding CMakeLists.txt
7 file, and prints the difference if not.
9 Also checks that each CMakeLists.txt file below unittests/ folders that define
10 binaries have corresponding BUILD.gn files.
13 from __future__ import print_function
21 def sync_source_lists():
22 # Use shell=True on Windows in case git is a bat file.
23 gn_files = subprocess.check_output(['git', 'ls-files', '*BUILD.gn'],
24 shell=os.name == 'nt').splitlines()
26 # Matches e.g. | "foo.cpp",|, captures |foo| in group 1.
27 gn_cpp_re = re.compile(r'^\s*"([^"]+\.(?:cpp|c|h|S))",$', re.MULTILINE)
28 # Matches e.g. | foo.cpp|, captures |foo| in group 1.
29 cmake_cpp_re = re.compile(r'^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$',
33 for gn_file in gn_files:
34 # The CMakeLists.txt for llvm/utils/gn/secondary/foo/BUILD.gn is
35 # directly at foo/CMakeLists.txt.
36 strip_prefix = 'llvm/utils/gn/secondary/'
37 if not gn_file.startswith(strip_prefix):
39 cmake_file = os.path.join(
40 os.path.dirname(gn_file[len(strip_prefix):]), 'CMakeLists.txt')
41 if not os.path.exists(cmake_file):
44 def get_sources(source_re, text):
45 return set([m.group(1) for m in source_re.finditer(text)])
46 gn_cpp = get_sources(gn_cpp_re, open(gn_file).read())
47 cmake_cpp = get_sources(cmake_cpp_re, open(cmake_file).read())
49 if gn_cpp == cmake_cpp:
54 add = cmake_cpp - gn_cpp
56 print('add:\n' + '\n'.join(' "%s",' % a for a in add))
57 remove = gn_cpp - cmake_cpp
59 print('remove:\n' + '\n'.join(remove))
65 # Matches e.g. |add_llvm_unittest_with_input_files|.
66 unittest_re = re.compile(r'^add_\S+_unittest', re.MULTILINE)
68 checked = [ 'clang', 'clang-tools-extra', 'lld', 'llvm' ]
71 for root, _, _ in os.walk(os.path.join(c, 'unittests')):
72 cmake_file = os.path.join(root, 'CMakeLists.txt')
73 if not os.path.exists(cmake_file):
75 if not unittest_re.search(open(cmake_file).read()):
76 continue # Skip CMake files that just add subdirectories.
77 gn_file = os.path.join('llvm/utils/gn/secondary', root, 'BUILD.gn')
78 if not os.path.exists(gn_file):
80 print('missing GN file %s for unittest CMake file %s' %
81 (gn_file, cmake_file))
86 src = sync_source_lists()
87 tests = sync_unittests()
93 if __name__ == '__main__':