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