8dbfadb118795c20d51f70c8b90f1296ad5cf2d5
[blender.git] / build_files / cmake / cmake_consistency_check.py
1 #!/usr/bin/env python
2
3 # $Id$
4 # ***** BEGIN GPL LICENSE BLOCK *****
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #
20 # Contributor(s): Campbell Barton
21 #
22 # ***** END GPL LICENSE BLOCK *****
23
24 # <pep8 compliant>
25
26 IGNORE = (
27     "/test/",
28     "/decimate_glut_test/",
29     "/BSP_GhostTest/",
30     "/release/",
31     "/xembed/",
32     "/decimation/intern/future/",
33     "/TerraplayNetwork/",
34     "/ik_glut_test/",
35     )
36
37 import os
38 from os.path import join, dirname, normpath, abspath, splitext
39
40 base = join(os.path.dirname(__file__), "..", "..")
41 base = normpath(base)
42 base = abspath(base)
43
44 print("Scanning:", base)
45
46 global_h = set()
47 global_c = set()
48
49
50 def source_list(path, filename_check=None):
51     for dirpath, dirnames, filenames in os.walk(path):
52
53         # skip '.svn'
54         if dirpath.startswith("."):
55             continue
56
57         for filename in filenames:
58             if filename_check is None or filename_check(filename):
59                 yield os.path.join(dirpath, filename)
60
61
62 # extension checking
63 def is_cmake(filename):
64     ext = splitext(filename)[1]
65     return (ext == ".cmake") or (filename == "CMakeLists.txt")
66
67
68 def is_c_header(filename):
69     ext = splitext(filename)[1]
70     return (ext in (".h", ".hpp", ".hxx"))
71
72
73 def is_c(filename):
74     ext = splitext(filename)[1]
75     return (ext in (".c", ".cpp", ".cxx", ".m", ".mm", ".rc"))
76
77
78 def is_c_any(filename):
79     return is_c(filename) or is_c_header(filename)
80
81
82 def cmake_get_src(f):
83
84     sources_h = []
85     sources_c = []
86
87     filen = open(f, "r", encoding="utf8")
88     it = iter(filen)
89     found = False
90     i = 0
91     # print(f)
92     while it is not None:
93         while it is not None:
94             i += 1
95             try:
96                 l = next(it)
97             except StopIteration:
98                 it = None
99                 break
100             l = l.strip()
101             if not l.startswith("#"):
102                 if 'set(SRC' in l or ('set(' in l and l.endswith("SRC")):
103                     if len(l.split()) > 1:
104                         raise Exception("strict formatting not kept 'set(SRC*' %s:%d" % (f, i))
105                     found = True
106                     break
107
108                 if "list(APPEND SRC" in l or ('list(APPEND ' in l and l.endswith("SRC")):
109                     if l.endswith(")"):
110                         raise Exception("strict formatting not kept 'list(APPEND SRC...)' on 1 line %s:%d" % (f, i))
111                     found = True
112                     break
113
114         if found:
115             cmake_base = dirname(f)
116
117             while it is not None:
118                 i += 1
119                 try:
120                     l = next(it)
121                 except StopIteration:
122                     it = None
123                     break
124
125                 l = l.strip()
126
127                 if not l.startswith("#"):
128
129                     if ")" in l:
130                         if l.strip() != ")":
131                             raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
132                         break
133
134                     # replace dirs
135                     l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
136
137                     if not l:
138                         pass
139                     elif l.startswith("$"):
140                         # assume if it ends with SRC we know about it
141                         if not l.split("}")[0].endswith("SRC"):
142                             print("Can't use var '%s' %s:%d" % (l, f, i))
143                     elif len(l.split()) > 1:
144                         raise Exception("Multi-line define '%s' %s:%d" % (l, f, i))
145                     else:
146                         new_file = normpath(join(cmake_base, l))
147
148                         if is_c_header(new_file):
149                             sources_h.append(new_file)
150                         elif is_c(new_file):
151                             sources_c.append(new_file)
152                         elif l in ("PARENT_SCOPE", ):
153                             # cmake var, ignore
154                             pass
155                         else:
156                             raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))
157
158                         # print(new_file)
159
160             global_h.update(set(sources_h))
161             global_c.update(set(sources_c))
162             '''
163             if not sources_h and not sources_c:
164                 raise Exception("No sources %s" % f)
165
166             sources_h_fs = list(source_list(cmake_base, is_c_header))
167             sources_c_fs = list(source_list(cmake_base, is_c))
168             '''
169             # find missing C files:
170             '''
171             for ff in sources_c_fs:
172                 if ff not in sources_c:
173                     print("  missing: " + ff)
174             '''
175
176     filen.close()
177
178
179 for cmake in source_list(base, is_cmake):
180     cmake_get_src(cmake)
181
182
183 def is_ignore(f):
184     for ig in IGNORE:
185         if ig in f:
186             return True
187     return False
188
189 # First do stupid check, do these files exist?
190 for f in (global_h | global_c):
191     if f.endswith("dna.c"):
192         continue
193
194     if not os.path.exists(f):
195         raise Exception("CMake referenced file missing: " + f)
196
197
198 # now check on files not accounted for.
199 print("\nC/C++ Files CMake doesnt know about...")
200 for cf in sorted(source_list(base, is_c)):
201     if not is_ignore(cf):
202         if cf not in global_c:
203             print("missing_c: ", cf)
204 print("\nC/C++ Headers CMake doesnt know about...")
205 for hf in sorted(source_list(base, is_c_header)):
206     if not is_ignore(hf):
207         if hf not in global_h:
208             print("missing_h: ", hf)
209
210 # test encoding
211 import traceback
212 for files in (global_c, global_h):
213     for f in sorted(files):
214         if os.path.exists(f):
215             # ignore outside of our source tree
216             if "extern" not in f:
217                 i = 1
218                 try:
219                     for l in open(f, "r", encoding="utf8"):
220                         i += 1
221                 except:
222                     print("Non utf8: %s:%d" % (f, i))
223                     if i > 1:
224                         traceback.print_exc()