Mercurial > hg > orthanc
diff 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 |
line wrap: on
line diff
--- a/OrthancFramework/Resources/CheckOrthancFrameworkSymbols.py Fri Nov 06 10:00:05 2020 +0100 +++ b/OrthancFramework/Resources/CheckOrthancFrameworkSymbols.py Fri Nov 06 15:37:30 2020 +0100 @@ -79,14 +79,57 @@ f.write('#include "%s"\n' % source) -tu = index.parse(AMALGAMATION, - [ '-DORTHANC_BUILDING_FRAMEWORK_LIBRARY=1' ]) +tu = index.parse(AMALGAMATION, [ + '-DORTHANC_BUILDING_FRAMEWORK_LIBRARY=1', + '-DORTHANC_BUILD_UNIT_TESTS=0', + '-DORTHANC_SANDBOXED=0', + '-DORTHANC_ENABLE_BASE64=1', + '-DORTHANC_ENABLE_CIVETWEB=1', + '-DORTHANC_ENABLE_CURL=1', + '-DORTHANC_ENABLE_DCMTK=1', + '-DORTHANC_ENABLE_DCMTK_JPEG=1', + '-DORTHANC_ENABLE_DCMTK_NETWORKING=1', + '-DORTHANC_ENABLE_DCMTK_TRANSCODING=1', + '-DORTHANC_ENABLE_JPEG=1', + '-DORTHANC_ENABLE_LOCALE=1', + '-DORTHANC_ENABLE_LUA=1', + '-DORTHANC_ENABLE_LOGGING=1', + '-DORTHANC_ENABLE_MD5=1', + '-DORTHANC_ENABLE_PKCS11=1', + '-DORTHANC_ENABLE_PNG=1', + '-DORTHANC_ENABLE_PUGIXML=1', + '-DORTHANC_ENABLE_SSL=1', + '-DORTHANC_SQLITE_STANDALONE=0', + '-DORTHANC_ENABLE_LOGGING_STDIO=0', +]) FILES = [] COUNT = 0 +def ReportProblem(message, fqn, cursor): + global FILES, COUNT + FILES.append(os.path.normpath(str(cursor.location.file))) + COUNT += 1 + + print('%s: %s::%s()' % (message, '::'.join(fqn), cursor.spelling)) + + def ExploreClass(child, fqn): + # Safety check + if (child.kind != clang.cindex.CursorKind.CLASS_DECL and + child.kind != clang.cindex.CursorKind.STRUCT_DECL): + raise Exception() + + # Ignore forward declaration of classes + if not child.is_definition(): + return + + + ## + ## Verify that the class is publicly exported (its visibility must + ## be "default") + ## visible = False for i in child.get_children(): @@ -94,34 +137,113 @@ i.spelling == 'default'): visible = True - if visible: - isPublic = (child.kind == clang.cindex.CursorKind.STRUCT_DECL) + if not visible: + return + + + ## + ## Ignore pure abstract interfaces, by checking the following + ## criteria: + ## - It must be a C++ class (not a struct) + ## - The class name must start with "I" + ## - All its methods must be pure virtual (abstract) and public + ## - Its destructor must be public, virtual, and must do nothing + ## + + if (child.kind == clang.cindex.CursorKind.CLASS_DECL and + fqn[-1].startswith('I')): + abstract = True + isPublic = False for i in child.get_children(): - if i.kind == clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL: + if i.kind == clang.cindex.CursorKind.VISIBILITY_ATTR: # "default" + pass + elif i.kind == clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL: isPublic = (i.access_specifier == clang.cindex.AccessSpecifier.PUBLIC) + elif i.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER: + if i.spelling != 'boost::noncopyable': + abstract = False + elif isPublic: + if i.kind == clang.cindex.CursorKind.CXX_METHOD: + if i.is_pure_virtual_method(): + pass # pure virtual is ok + elif i.is_static_method(): + # static method without an inline implementation is ok + for j in i.get_children(): + if j.kind == clang.cindex.CursorKind.COMPOUND_STMT: + abstract = False + else: + abstract = False + elif (i.kind == clang.cindex.CursorKind.DESTRUCTOR and + i.is_virtual_method()): + # The destructor must be virtual, and must do nothing + c = list(i.get_children()) + if (len(c) != 1 or + c[0].kind != clang.cindex.CursorKind.COMPOUND_STMT or + len(list(c[0].get_children())) != 0): + abstract = False + elif (i.kind == clang.cindex.CursorKind.CLASS_DECL or + i.kind == clang.cindex.CursorKind.STRUCT_DECL): + ExploreClass(i, fqn + [ i.spelling ]) + else: + abstract = False - elif (i.kind == clang.cindex.CursorKind.CLASS_DECL or - i.kind == clang.cindex.CursorKind.STRUCT_DECL): - # This is a subclass + if abstract: + print('Detected a pure interface (this is fine): %s' % ('::'.join(fqn))) + return + + + ## + ## We are facing a standard C++ class or struct + ## + + isPublic = (child.kind == clang.cindex.CursorKind.STRUCT_DECL) + + for i in child.get_children(): + if (i.kind == clang.cindex.CursorKind.VISIBILITY_ATTR or # "default" + i.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER): # base class + pass + + elif i.kind == clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL: + isPublic = (i.access_specifier == clang.cindex.AccessSpecifier.PUBLIC) + + elif (i.kind == clang.cindex.CursorKind.CLASS_DECL or + i.kind == clang.cindex.CursorKind.STRUCT_DECL): + # This is a subclass + if isPublic: ExploreClass(i, fqn + [ i.spelling ]) - - elif (i.kind == clang.cindex.CursorKind.CXX_METHOD or - i.kind == clang.cindex.CursorKind.CONSTRUCTOR or - i.kind == clang.cindex.CursorKind.DESTRUCTOR): - if isPublic: - hasImplementation = False - for j in i.get_children(): - if j.kind == clang.cindex.CursorKind.COMPOUND_STMT: - hasImplementation = True + + elif (i.kind == clang.cindex.CursorKind.CXX_METHOD or + i.kind == clang.cindex.CursorKind.CONSTRUCTOR or + i.kind == clang.cindex.CursorKind.DESTRUCTOR): + if isPublic: + hasImplementation = False + for j in i.get_children(): + if j.kind == clang.cindex.CursorKind.COMPOUND_STMT: + hasImplementation = True + + if hasImplementation: + ReportProblem('Exported public method with an implementation', fqn, i) - if hasImplementation: - global FILES, COUNT - FILES.append(os.path.normpath(str(child.location.file))) - COUNT += 1 + elif i.kind == clang.cindex.CursorKind.VAR_DECL: + if isPublic: + ReportProblem('Exported public member variable', fqn, i) + + elif i.kind == clang.cindex.CursorKind.FUNCTION_TEMPLATE: + if isPublic: + ReportProblem('Exported public template method', fqn, i) - print('Exported public method with an implementation: %s::%s()' % - ('::'.join(fqn), i.spelling)) + elif i.kind == clang.cindex.CursorKind.FRIEND_DECL: + if isPublic: + ReportProblem('Exported public friend method', fqn, i) + + elif (i.kind == clang.cindex.CursorKind.TYPEDEF_DECL or # Allow "typedef" + i.kind == clang.cindex.CursorKind.ENUM_DECL): # Allow enums + pass + + else: + if isPublic: + raise Exception('Unsupported: %s, %s' % (i.kind, i.location)) def ExploreNamespace(node, namespace): @@ -135,6 +257,10 @@ child.kind == clang.cindex.CursorKind.STRUCT_DECL): ExploreClass(child, fqn) + + +print('') + for node in tu.cursor.get_children(): if (node.kind == clang.cindex.CursorKind.NAMESPACE and node.spelling == 'Orthanc'): @@ -143,7 +269,8 @@ print('\nTotal of possibly problematic methods: %d' % COUNT) -print('\nFiles:\n') +print('\nProblematic files:\n') for i in sorted(list(set(FILES))): print(i) - + +print('')