OSDN Git Service

[GN] Set exit code to 1 if changes are needed
[android-x86/external-llvm.git] / utils / gn / build / sync_source_lists_from_cmake.py
1 #!/usr/bin/env python
2
3 """Helps to keep BUILD.gn files in sync with the corresponding CMakeLists.txt.
4
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.
8
9 Also checks that each CMakeLists.txt file below unittests/ folders that define
10 binaries have corresponding BUILD.gn files.
11 """
12
13 from __future__ import print_function
14
15 import os
16 import re
17 import subprocess
18 import sys
19
20
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()
25
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))$',
30                               re.MULTILINE)
31
32     changed = False
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):
38             continue
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):
42             continue
43
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())
48
49         if gn_cpp == cmake_cpp:
50             continue
51
52         changed = True
53         print(gn_file)
54         add = cmake_cpp - gn_cpp
55         if add:
56             print('add:\n' + '\n'.join('    "%s",' % a for a in add))
57         remove = gn_cpp - cmake_cpp
58         if remove:
59             print('remove:\n' + '\n'.join(remove))
60         print()
61     return changed
62
63
64 def sync_unittests():
65     # Matches e.g. |add_llvm_unittest_with_input_files|.
66     unittest_re = re.compile(r'^add_\S+_unittest', re.MULTILINE)
67
68     checked = [ 'clang', 'clang-tools-extra', 'lld', 'llvm' ]
69     changed = False
70     for c in checked:
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):
74                 continue
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):
79                 changed = True
80                 print('missing GN file %s for unittest CMake file %s' %
81                       (gn_file, cmake_file))
82     return changed
83
84
85 def main():
86     src = sync_source_lists()
87     tests = sync_unittests()
88     if src or tests:
89         sys.exit(1)
90
91
92
93 if __name__ == '__main__':
94     main()