update cmake checker to ignore file list and add some headers to the source list.
[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     # specific source files
37     "extern/Eigen2/Eigen/src/Cholesky/CholeskyInstantiations.cpp",
38     "extern/Eigen2/Eigen/src/Core/CoreInstantiations.cpp",
39     "extern/Eigen2/Eigen/src/QR/QrInstantiations.cpp",
40     "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp",
41     "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp",
42     "extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp",
43     "extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.cpp",
44     "extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.cpp",
45     "extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.cpp",
46     "extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp",
47     "extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp",
48     "extern/eltopo/common/meshes/ObjLoader.cpp",
49     "extern/eltopo/common/meshes/meshloader.cpp",
50     "extern/eltopo/common/openglutils.cpp",
51     "extern/eltopo/eltopo3d/broadphase_blenderbvh.cpp",
52     "source/blender/imbuf/intern/imbuf_cocoa.m",
53
54     "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h",
55     "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h",
56     "extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h",
57     "extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.h",
58     "extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.h",
59     "extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.h",
60     "extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h",
61     "extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h",
62     "extern/eltopo/common/meshes/Edge.hpp",
63     "extern/eltopo/common/meshes/ObjLoader.hpp",
64     "extern/eltopo/common/meshes/TriangleIndex.hpp",
65     "extern/eltopo/common/meshes/meshloader.h",
66     "extern/eltopo/eltopo3d/broadphase_blenderbvh.h"
67     )
68     
69     
70
71     
72
73 import os
74 from os.path import join, dirname, normpath, abspath, splitext
75
76 base = join(os.path.dirname(__file__), "..", "..")
77 base = normpath(base)
78 base = abspath(base)
79
80 print("Scanning:", base)
81
82 global_h = set()
83 global_c = set()
84
85
86 def source_list(path, filename_check=None):
87     for dirpath, dirnames, filenames in os.walk(path):
88
89         # skip '.svn'
90         if dirpath.startswith("."):
91             continue
92
93         for filename in filenames:
94             if filename_check is None or filename_check(filename):
95                 yield os.path.join(dirpath, filename)
96
97
98 # extension checking
99 def is_cmake(filename):
100     ext = splitext(filename)[1]
101     return (ext == ".cmake") or (filename == "CMakeLists.txt")
102
103
104 def is_c_header(filename):
105     ext = splitext(filename)[1]
106     return (ext in (".h", ".hpp", ".hxx"))
107
108
109 def is_c(filename):
110     ext = splitext(filename)[1]
111     return (ext in (".c", ".cpp", ".cxx", ".m", ".mm", ".rc"))
112
113
114 def is_c_any(filename):
115     return is_c(filename) or is_c_header(filename)
116
117
118 def cmake_get_src(f):
119
120     sources_h = []
121     sources_c = []
122
123     filen = open(f, "r", encoding="utf8")
124     it = iter(filen)
125     found = False
126     i = 0
127     # print(f)
128     while it is not None:
129         while it is not None:
130             i += 1
131             try:
132                 l = next(it)
133             except StopIteration:
134                 it = None
135                 break
136             l = l.strip()
137             if not l.startswith("#"):
138                 if 'set(SRC' in l or ('set(' in l and l.endswith("SRC")):
139                     if len(l.split()) > 1:
140                         raise Exception("strict formatting not kept 'set(SRC*' %s:%d" % (f, i))
141                     found = True
142                     break
143
144                 if "list(APPEND SRC" in l or ('list(APPEND ' in l and l.endswith("SRC")):
145                     if l.endswith(")"):
146                         raise Exception("strict formatting not kept 'list(APPEND SRC...)' on 1 line %s:%d" % (f, i))
147                     found = True
148                     break
149
150         if found:
151             cmake_base = dirname(f)
152
153             while it is not None:
154                 i += 1
155                 try:
156                     l = next(it)
157                 except StopIteration:
158                     it = None
159                     break
160
161                 l = l.strip()
162
163                 if not l.startswith("#"):
164
165                     if ")" in l:
166                         if l.strip() != ")":
167                             raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
168                         break
169
170                     # replace dirs
171                     l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
172
173                     if not l:
174                         pass
175                     elif l.startswith("$"):
176                         # assume if it ends with SRC we know about it
177                         if not l.split("}")[0].endswith("SRC"):
178                             print("Can't use var '%s' %s:%d" % (l, f, i))
179                     elif len(l.split()) > 1:
180                         raise Exception("Multi-line define '%s' %s:%d" % (l, f, i))
181                     else:
182                         new_file = normpath(join(cmake_base, l))
183
184                         if is_c_header(new_file):
185                             sources_h.append(new_file)
186                         elif is_c(new_file):
187                             sources_c.append(new_file)
188                         elif l in ("PARENT_SCOPE", ):
189                             # cmake var, ignore
190                             pass
191                         else:
192                             raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))
193
194                         # print(new_file)
195
196             global_h.update(set(sources_h))
197             global_c.update(set(sources_c))
198             '''
199             if not sources_h and not sources_c:
200                 raise Exception("No sources %s" % f)
201
202             sources_h_fs = list(source_list(cmake_base, is_c_header))
203             sources_c_fs = list(source_list(cmake_base, is_c))
204             '''
205             # find missing C files:
206             '''
207             for ff in sources_c_fs:
208                 if ff not in sources_c:
209                     print("  missing: " + ff)
210             '''
211
212     filen.close()
213
214
215 for cmake in source_list(base, is_cmake):
216     cmake_get_src(cmake)
217
218
219 def is_ignore(f):
220     for ig in IGNORE:
221         if ig in f:
222             return True
223     return False
224
225 # First do stupid check, do these files exist?
226 for f in (global_h | global_c):
227     if f.endswith("dna.c"):
228         continue
229
230     if not os.path.exists(f):
231         raise Exception("CMake referenced file missing: " + f)
232
233
234 # now check on files not accounted for.
235 print("\nC/C++ Files CMake doesnt know about...")
236 for cf in sorted(source_list(base, is_c)):
237     if not is_ignore(cf):
238         if cf not in global_c:
239             print("missing_c: ", cf)
240 print("\nC/C++ Headers CMake doesnt know about...")
241 for hf in sorted(source_list(base, is_c_header)):
242     if not is_ignore(hf):
243         if hf not in global_h:
244             print("missing_h: ", hf)
245
246 # test encoding
247 import traceback
248 for files in (global_c, global_h):
249     for f in sorted(files):
250         if os.path.exists(f):
251             # ignore outside of our source tree
252             if "extern" not in f:
253                 i = 1
254                 try:
255                     for l in open(f, "r", encoding="utf8"):
256                         i += 1
257                 except:
258                     print("Non utf8: %s:%d" % (f, i))
259                     if i > 1:
260                         traceback.print_exc()