synched with trunk at revision 36569
[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 import os
37 from os.path import join, dirname, normpath, abspath, splitext
38
39 base = join(os.path.dirname(__file__), "..", "..")
40 base = normpath(base)
41 base = abspath(base)
42
43 print("Scanning:", base)
44
45 global_h = set()
46 global_c = set()
47
48
49 def source_list(path, filename_check=None):
50     for dirpath, dirnames, filenames in os.walk(path):
51
52         # skip '.svn'
53         if dirpath.startswith("."):
54             continue
55
56         for filename in filenames:
57             if filename_check is None or filename_check(filename):
58                 yield os.path.join(dirpath, filename)
59
60
61 # extension checking
62 def is_cmake(filename):
63     ext = splitext(filename)[1]
64     return (ext == ".cmake") or (filename == "CMakeLists.txt")
65
66
67 def is_c_header(filename):
68     ext = splitext(filename)[1]
69     return (ext in (".h", ".hpp", ".hxx"))
70
71
72 def is_c(filename):
73     ext = splitext(filename)[1]
74     return (ext in (".c", ".cpp", ".cxx", ".m", ".mm", ".rc"))
75
76
77 def is_c_any(filename):
78     return is_c(filename) or is_c_header(filename)
79
80
81 def cmake_get_src(f):
82
83     sources_h = []
84     sources_c = []
85
86     filen = open(f, "r", encoding="utf8")
87     it = iter(filen)
88     found = False
89     i = 0
90     # print(f)
91     while it is not None:
92         while it is not None:
93             i += 1
94             try:
95                 l = next(it)
96             except StopIteration:
97                 it = None
98                 break
99             l = l.strip()
100             if not l.startswith("#"):
101                 if 'set(SRC' in l or ('set(' in l and l.endswith("SRC")):
102                     if len(l.split()) > 1:
103                         raise Exception("strict formatting not kept 'set(SRC*' %s:%d" % (f, i))
104                     found = True
105                     break
106
107                 if "list(APPEND SRC" in l:
108                     if l.endswith(")"):
109                         raise Exception("strict formatting not kept 'list(APPEND SRC...)' on 1 line %s:%d" % (f, i))
110                     found = True
111                     break
112
113         if found:
114             cmake_base = dirname(f)
115
116             while it is not None:
117                 i += 1
118                 try:
119                     l = next(it)
120                 except StopIteration:
121                     it = None
122                     break
123
124                 l = l.strip()
125
126                 if not l.startswith("#"):
127
128                     if ")" in l:
129                         if l.strip() != ")":
130                             raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
131                         break
132
133                     # replace dirs
134                     l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
135
136                     if not l:
137                         pass
138                     elif l.startswith("$"):
139                         print("Cant use var '%s' %s:%d" % (l, f, i))
140                     elif len(l.split()) > 1:
141                         raise Exception("Multi-line define '%s' %s:%d" % (l, f, i))
142                     else:
143                         new_file = normpath(join(cmake_base, l))
144
145                         if is_c_header(new_file):
146                             sources_h.append(new_file)
147                         elif is_c(new_file):
148                             sources_c.append(new_file)
149                         elif l in ("PARENT_SCOPE", ):
150                             # cmake var, ignore
151                             pass
152                         else:
153                             raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))
154
155                         # print(new_file)
156
157             global_h.update(set(sources_h))
158             global_c.update(set(sources_c))
159             '''
160             if not sources_h and not sources_c:
161                 raise Exception("No sources %s" % f)
162
163             sources_h_fs = list(source_list(cmake_base, is_c_header))
164             sources_c_fs = list(source_list(cmake_base, is_c))
165             '''
166             # find missing C files:
167             '''
168             for ff in sources_c_fs:
169                 if ff not in sources_c:
170                     print("  missing: " + ff)
171             '''
172
173     filen.close()
174
175
176 for cmake in source_list(base, is_cmake):
177     cmake_get_src(cmake)
178
179
180 def is_ignore(f):
181     for ig in IGNORE:
182         if ig in f:
183             return True
184     return False
185
186 # First do stupid check, do these files exist?
187 for f in (global_h | global_c):
188     if f.endswith("dna.c"):
189         continue
190
191     if not os.path.exists(f):
192         raise Exception("CMake referenced file missing: " + f)
193
194
195 # now check on files not accounted for.
196 print("\nC/C++ Files CMake doesnt know about...")
197 for cf in sorted(source_list(base, is_c)):
198     if not is_ignore(cf):
199         if cf not in global_c:
200             print("missing_c: ", cf)
201 print("\nC/C++ Headers CMake doesnt know about...")
202 for hf in sorted(source_list(base, is_c_header)):
203     if not is_ignore(hf):
204         if hf not in global_h:
205             print("missing_h: ", hf)
206
207 # test encoding
208 import traceback
209 for files in (global_c, global_h):
210     for f in sorted(files):
211         if os.path.exists(f):
212             # ignore outside of our source tree
213             if "extern" not in f:
214                 i = 1
215                 try:
216                     for l in open(f, "r", encoding="utf8"):
217                         i += 1
218                 except:
219                     print("Non utf8: %s:%d" % (f, i))
220                     if i > 1:
221                         traceback.print_exc()