Mercurial > hg > orthanc
comparison OrthancFramework/Resources/CheckOrthancFrameworkSymbols.py @ 4303:44b53a2c0a13
improving detection of ABI compatibility
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 06 Nov 2020 15:37:30 +0100 |
parents | b30a8de92ad9 |
children | 50b0c69b653a |
comparison
equal
deleted
inserted
replaced
4302:4c91fbede7d2 | 4303:44b53a2c0a13 |
---|---|
77 f.write('#include "%s"\n' % os.path.join(ROOT, '..', 'Sources', 'OrthancFramework.h')) | 77 f.write('#include "%s"\n' % os.path.join(ROOT, '..', 'Sources', 'OrthancFramework.h')) |
78 for source in SOURCES: | 78 for source in SOURCES: |
79 f.write('#include "%s"\n' % source) | 79 f.write('#include "%s"\n' % source) |
80 | 80 |
81 | 81 |
82 tu = index.parse(AMALGAMATION, | 82 tu = index.parse(AMALGAMATION, [ |
83 [ '-DORTHANC_BUILDING_FRAMEWORK_LIBRARY=1' ]) | 83 '-DORTHANC_BUILDING_FRAMEWORK_LIBRARY=1', |
84 '-DORTHANC_BUILD_UNIT_TESTS=0', | |
85 '-DORTHANC_SANDBOXED=0', | |
86 '-DORTHANC_ENABLE_BASE64=1', | |
87 '-DORTHANC_ENABLE_CIVETWEB=1', | |
88 '-DORTHANC_ENABLE_CURL=1', | |
89 '-DORTHANC_ENABLE_DCMTK=1', | |
90 '-DORTHANC_ENABLE_DCMTK_JPEG=1', | |
91 '-DORTHANC_ENABLE_DCMTK_NETWORKING=1', | |
92 '-DORTHANC_ENABLE_DCMTK_TRANSCODING=1', | |
93 '-DORTHANC_ENABLE_JPEG=1', | |
94 '-DORTHANC_ENABLE_LOCALE=1', | |
95 '-DORTHANC_ENABLE_LUA=1', | |
96 '-DORTHANC_ENABLE_LOGGING=1', | |
97 '-DORTHANC_ENABLE_MD5=1', | |
98 '-DORTHANC_ENABLE_PKCS11=1', | |
99 '-DORTHANC_ENABLE_PNG=1', | |
100 '-DORTHANC_ENABLE_PUGIXML=1', | |
101 '-DORTHANC_ENABLE_SSL=1', | |
102 '-DORTHANC_SQLITE_STANDALONE=0', | |
103 '-DORTHANC_ENABLE_LOGGING_STDIO=0', | |
104 ]) | |
84 | 105 |
85 | 106 |
86 FILES = [] | 107 FILES = [] |
87 COUNT = 0 | 108 COUNT = 0 |
88 | 109 |
110 def ReportProblem(message, fqn, cursor): | |
111 global FILES, COUNT | |
112 FILES.append(os.path.normpath(str(cursor.location.file))) | |
113 COUNT += 1 | |
114 | |
115 print('%s: %s::%s()' % (message, '::'.join(fqn), cursor.spelling)) | |
116 | |
117 | |
89 def ExploreClass(child, fqn): | 118 def ExploreClass(child, fqn): |
119 # Safety check | |
120 if (child.kind != clang.cindex.CursorKind.CLASS_DECL and | |
121 child.kind != clang.cindex.CursorKind.STRUCT_DECL): | |
122 raise Exception() | |
123 | |
124 # Ignore forward declaration of classes | |
125 if not child.is_definition(): | |
126 return | |
127 | |
128 | |
129 ## | |
130 ## Verify that the class is publicly exported (its visibility must | |
131 ## be "default") | |
132 ## | |
90 visible = False | 133 visible = False |
91 | 134 |
92 for i in child.get_children(): | 135 for i in child.get_children(): |
93 if (i.kind == clang.cindex.CursorKind.VISIBILITY_ATTR and | 136 if (i.kind == clang.cindex.CursorKind.VISIBILITY_ATTR and |
94 i.spelling == 'default'): | 137 i.spelling == 'default'): |
95 visible = True | 138 visible = True |
96 | 139 |
97 if visible: | 140 if not visible: |
98 isPublic = (child.kind == clang.cindex.CursorKind.STRUCT_DECL) | 141 return |
142 | |
143 | |
144 ## | |
145 ## Ignore pure abstract interfaces, by checking the following | |
146 ## criteria: | |
147 ## - It must be a C++ class (not a struct) | |
148 ## - The class name must start with "I" | |
149 ## - All its methods must be pure virtual (abstract) and public | |
150 ## - Its destructor must be public, virtual, and must do nothing | |
151 ## | |
152 | |
153 if (child.kind == clang.cindex.CursorKind.CLASS_DECL and | |
154 fqn[-1].startswith('I')): | |
155 abstract = True | |
156 isPublic = False | |
99 | 157 |
100 for i in child.get_children(): | 158 for i in child.get_children(): |
101 if i.kind == clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL: | 159 if i.kind == clang.cindex.CursorKind.VISIBILITY_ATTR: # "default" |
160 pass | |
161 elif i.kind == clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL: | |
102 isPublic = (i.access_specifier == clang.cindex.AccessSpecifier.PUBLIC) | 162 isPublic = (i.access_specifier == clang.cindex.AccessSpecifier.PUBLIC) |
103 | 163 elif i.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER: |
104 elif (i.kind == clang.cindex.CursorKind.CLASS_DECL or | 164 if i.spelling != 'boost::noncopyable': |
105 i.kind == clang.cindex.CursorKind.STRUCT_DECL): | 165 abstract = False |
106 # This is a subclass | 166 elif isPublic: |
167 if i.kind == clang.cindex.CursorKind.CXX_METHOD: | |
168 if i.is_pure_virtual_method(): | |
169 pass # pure virtual is ok | |
170 elif i.is_static_method(): | |
171 # static method without an inline implementation is ok | |
172 for j in i.get_children(): | |
173 if j.kind == clang.cindex.CursorKind.COMPOUND_STMT: | |
174 abstract = False | |
175 else: | |
176 abstract = False | |
177 elif (i.kind == clang.cindex.CursorKind.DESTRUCTOR and | |
178 i.is_virtual_method()): | |
179 # The destructor must be virtual, and must do nothing | |
180 c = list(i.get_children()) | |
181 if (len(c) != 1 or | |
182 c[0].kind != clang.cindex.CursorKind.COMPOUND_STMT or | |
183 len(list(c[0].get_children())) != 0): | |
184 abstract = False | |
185 elif (i.kind == clang.cindex.CursorKind.CLASS_DECL or | |
186 i.kind == clang.cindex.CursorKind.STRUCT_DECL): | |
187 ExploreClass(i, fqn + [ i.spelling ]) | |
188 else: | |
189 abstract = False | |
190 | |
191 if abstract: | |
192 print('Detected a pure interface (this is fine): %s' % ('::'.join(fqn))) | |
193 return | |
194 | |
195 | |
196 ## | |
197 ## We are facing a standard C++ class or struct | |
198 ## | |
199 | |
200 isPublic = (child.kind == clang.cindex.CursorKind.STRUCT_DECL) | |
201 | |
202 for i in child.get_children(): | |
203 if (i.kind == clang.cindex.CursorKind.VISIBILITY_ATTR or # "default" | |
204 i.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER): # base class | |
205 pass | |
206 | |
207 elif i.kind == clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL: | |
208 isPublic = (i.access_specifier == clang.cindex.AccessSpecifier.PUBLIC) | |
209 | |
210 elif (i.kind == clang.cindex.CursorKind.CLASS_DECL or | |
211 i.kind == clang.cindex.CursorKind.STRUCT_DECL): | |
212 # This is a subclass | |
213 if isPublic: | |
107 ExploreClass(i, fqn + [ i.spelling ]) | 214 ExploreClass(i, fqn + [ i.spelling ]) |
108 | 215 |
109 elif (i.kind == clang.cindex.CursorKind.CXX_METHOD or | 216 elif (i.kind == clang.cindex.CursorKind.CXX_METHOD or |
110 i.kind == clang.cindex.CursorKind.CONSTRUCTOR or | 217 i.kind == clang.cindex.CursorKind.CONSTRUCTOR or |
111 i.kind == clang.cindex.CursorKind.DESTRUCTOR): | 218 i.kind == clang.cindex.CursorKind.DESTRUCTOR): |
112 if isPublic: | 219 if isPublic: |
113 hasImplementation = False | 220 hasImplementation = False |
114 for j in i.get_children(): | 221 for j in i.get_children(): |
115 if j.kind == clang.cindex.CursorKind.COMPOUND_STMT: | 222 if j.kind == clang.cindex.CursorKind.COMPOUND_STMT: |
116 hasImplementation = True | 223 hasImplementation = True |
117 | 224 |
118 if hasImplementation: | 225 if hasImplementation: |
119 global FILES, COUNT | 226 ReportProblem('Exported public method with an implementation', fqn, i) |
120 FILES.append(os.path.normpath(str(child.location.file))) | 227 |
121 COUNT += 1 | 228 elif i.kind == clang.cindex.CursorKind.VAR_DECL: |
122 | 229 if isPublic: |
123 print('Exported public method with an implementation: %s::%s()' % | 230 ReportProblem('Exported public member variable', fqn, i) |
124 ('::'.join(fqn), i.spelling)) | 231 |
232 elif i.kind == clang.cindex.CursorKind.FUNCTION_TEMPLATE: | |
233 if isPublic: | |
234 ReportProblem('Exported public template method', fqn, i) | |
235 | |
236 elif i.kind == clang.cindex.CursorKind.FRIEND_DECL: | |
237 if isPublic: | |
238 ReportProblem('Exported public friend method', fqn, i) | |
239 | |
240 elif (i.kind == clang.cindex.CursorKind.TYPEDEF_DECL or # Allow "typedef" | |
241 i.kind == clang.cindex.CursorKind.ENUM_DECL): # Allow enums | |
242 pass | |
243 | |
244 else: | |
245 if isPublic: | |
246 raise Exception('Unsupported: %s, %s' % (i.kind, i.location)) | |
125 | 247 |
126 | 248 |
127 def ExploreNamespace(node, namespace): | 249 def ExploreNamespace(node, namespace): |
128 for child in node.get_children(): | 250 for child in node.get_children(): |
129 fqn = namespace + [ child.spelling ] | 251 fqn = namespace + [ child.spelling ] |
133 | 255 |
134 elif (child.kind == clang.cindex.CursorKind.CLASS_DECL or | 256 elif (child.kind == clang.cindex.CursorKind.CLASS_DECL or |
135 child.kind == clang.cindex.CursorKind.STRUCT_DECL): | 257 child.kind == clang.cindex.CursorKind.STRUCT_DECL): |
136 ExploreClass(child, fqn) | 258 ExploreClass(child, fqn) |
137 | 259 |
260 | |
261 | |
262 print('') | |
263 | |
138 for node in tu.cursor.get_children(): | 264 for node in tu.cursor.get_children(): |
139 if (node.kind == clang.cindex.CursorKind.NAMESPACE and | 265 if (node.kind == clang.cindex.CursorKind.NAMESPACE and |
140 node.spelling == 'Orthanc'): | 266 node.spelling == 'Orthanc'): |
141 ExploreNamespace(node, [ 'Orthanc' ]) | 267 ExploreNamespace(node, [ 'Orthanc' ]) |
142 | 268 |
143 | 269 |
144 print('\nTotal of possibly problematic methods: %d' % COUNT) | 270 print('\nTotal of possibly problematic methods: %d' % COUNT) |
145 | 271 |
146 print('\nFiles:\n') | 272 print('\nProblematic files:\n') |
147 for i in sorted(list(set(FILES))): | 273 for i in sorted(list(set(FILES))): |
148 print(i) | 274 print(i) |
149 | 275 |
276 print('') |