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