forked from qt-creator/qt-creator
		
	Change-Id: I406665390406dca324c2c98dda37540b22a065df Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
		
			
				
	
	
		
			261 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #! /usr/bin/env python2
 | |
| 
 | |
| ############################################################################
 | |
| #
 | |
| # Copyright (C) 2016 The Qt Company Ltd.
 | |
| # Contact: https://www.qt.io/licensing/
 | |
| #
 | |
| # This file is part of Qt Creator.
 | |
| #
 | |
| # Commercial License Usage
 | |
| # Licensees holding valid commercial Qt licenses may use this file in
 | |
| # accordance with the commercial license agreement provided with the
 | |
| # Software or, alternatively, in accordance with the terms contained in
 | |
| # a written agreement between you and The Qt Company. For licensing terms
 | |
| # and conditions see https://www.qt.io/terms-conditions. For further
 | |
| # information use the contact form at https://www.qt.io/contact-us.
 | |
| #
 | |
| # GNU General Public License Usage
 | |
| # Alternatively, this file may be used under the terms of the GNU
 | |
| # General Public License version 3 as published by the Free Software
 | |
| # Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
 | |
| # included in the packaging of this file. Please review the following
 | |
| # information to ensure the GNU General Public License requirements will
 | |
| # be met: https://www.gnu.org/licenses/gpl-3.0.html.
 | |
| #
 | |
| ############################################################################
 | |
| 
 | |
| import glob
 | |
| import logging
 | |
| import os
 | |
| import re
 | |
| import subprocess
 | |
| import sys
 | |
| import platform
 | |
| 
 | |
| class Library:
 | |
|     def __init__(self, path):
 | |
|         self.path = path
 | |
|         self.name = ''
 | |
|         self.exportedSymbols = {}
 | |
| 
 | |
|         self.name = re.sub('^(.*/)?lib', '', path)
 | |
|         self.name = re.sub('\.so.*$', '', self.name)
 | |
| 
 | |
|         self._runNM(self.path)
 | |
| 
 | |
|     def isLibrary(self):
 | |
|         return True
 | |
| 
 | |
|     def isPlugin(self):
 | |
|         return False
 | |
| 
 | |
|     def debugDump(self):
 | |
|         log.debug('Library "%s" exports %d symbols.', self.name, len(self.exportedSymbols))
 | |
| 
 | |
|     def _runNM(self, path):
 | |
|         try:
 | |
|             output = subprocess.check_output(['/usr/bin/nm', '--demangle', path], stderr=subprocess.STDOUT).splitlines()
 | |
|         except:
 | |
|             output = []
 | |
|         for line in output:
 | |
|             self._parseNMline(line)
 | |
| 
 | |
|     def _parseNMline(self, line):
 | |
|         m = re.search('^[0-9a-fA-F]{8,16} [TD] (.*)$', line)
 | |
|         if m:
 | |
|             self.exportedSymbols[m.group(1)] = 1
 | |
| 
 | |
| 
 | |
| 
 | |
| class Plugin(Library):
 | |
|     def __init__(self, spec):
 | |
|         self.pluginSpec = spec
 | |
|         self.specDependencies = {}
 | |
|         self.symbolDependencies = {}
 | |
|         self.name = ''
 | |
|         self.importedSymbols = []
 | |
|         self.path = self._parsePluginSpec(spec)
 | |
|         Library.__init__(self, self.path)
 | |
| 
 | |
|         self.importedSymbols.sort()
 | |
| 
 | |
|     def isLibrary(self):
 | |
|         return False
 | |
| 
 | |
|     def isPlugin(self):
 | |
|         return True
 | |
| 
 | |
|     def debugDump(self):
 | |
|         log.debug('Plugin "%s" imports %d symbols and exports %d symbols.', self.name, len(self.importedSymbols),
 | |
|             len(self.exportedSymbols))
 | |
|         for i in self.specDependencies:
 | |
|             log.debug('    Spec declares dependency on "%s"', i)
 | |
|         for i in self.symbolDependencies:
 | |
|             tmp = 'plugin'
 | |
|             if i.isLibrary():
 | |
|                 tmp = 'lib'
 | |
|             log.debug('    Symbol dependency on %s "%s" (%d)', tmp, i.name, self.symbolDependencies[i])
 | |
| 
 | |
|     def _parsePluginSpec(self, spec):
 | |
|         dirname = os.path.dirname(spec)
 | |
|         with open(spec) as f:
 | |
|             content = f.readlines()
 | |
|         for line in content:
 | |
|             m = re.search('(plugin|dependency)\s+name="([^"]+)"(?:.*\stype="([^"]+)")?', line)
 | |
|             if not(m):
 | |
|                 continue
 | |
|             if m.group(1) == 'plugin':
 | |
|                 if self.name != '':
 | |
|                     log.critical('Plugin name already set to "%s"!', self.name)
 | |
|                 else:
 | |
|                     self.name = m.group(2)
 | |
|             else:
 | |
|                 kind = m.group(3)
 | |
|                 if not(kind):
 | |
|                     kind = 'strong'
 | |
|                 self.specDependencies[m.group(2)] = kind
 | |
| 
 | |
|         if self.name == '':
 | |
|             log.critical('Plugin name not set for spec "%s".', spec)
 | |
| 
 | |
|         return os.path.join(dirname, "lib%s.so" % self.name)
 | |
| 
 | |
|     def _parseNMline(self, line):
 | |
|         m = re.search('^\s+ U (.*)$', line)
 | |
|         if m:
 | |
|             self.importedSymbols.append(m.group(1))
 | |
|         else:
 | |
|             Library._parseNMline(self, line)
 | |
| 
 | |
|     def addSymbolDependency(self, dep, symbol):
 | |
|         if dep in self.symbolDependencies:
 | |
|             self.symbolDependencies[dep]['total'] += 1
 | |
|         else:
 | |
|             self.symbolDependencies[dep] = {}
 | |
|             self.symbolDependencies[dep]['total'] = 1
 | |
| 
 | |
|         self.symbolDependencies[dep][symbol] = 1
 | |
| 
 | |
| 
 | |
| 
 | |
| class SymbolResolver:
 | |
|     def __init__(self, plugins, libraries):
 | |
|         self.libraries = libraries
 | |
|         self.libraries.extend(plugins)
 | |
| 
 | |
|         for i in plugins:
 | |
|             self._resolve(i)
 | |
| 
 | |
|     def _resolve(self, plugin):
 | |
|         print 'Resolving symbols for {}...'.format(plugin.name)
 | |
|         for symbol in plugin.importedSymbols:
 | |
|             lib = self._resolveSymbol(symbol)
 | |
|             if lib:
 | |
|                 plugin.addSymbolDependency(lib, symbol)
 | |
| 
 | |
|     def _resolveSymbol(self, symbol):
 | |
|         for i in self.libraries:
 | |
|             if symbol in i.exportedSymbols:
 | |
|                 return i
 | |
|         return None
 | |
| 
 | |
| 
 | |
| 
 | |
| class Reporter:
 | |
|     def __init__(self, plugins):
 | |
|         for i in plugins:
 | |
|             self._reportPluginSpecIssues(i)
 | |
| 
 | |
|     def _reportPluginSpecIssues(self, plugin):
 | |
|         print 'Plugin "{}" imports {} symbols and exports {} symbols.'.format(
 | |
|             plugin.name, len(plugin.importedSymbols), len(plugin.exportedSymbols))
 | |
| 
 | |
|         spec = plugin.specDependencies
 | |
|         symb = {}
 | |
|         lib = {}
 | |
|         for p in plugin.symbolDependencies:
 | |
|             if p.isPlugin():
 | |
|                 symb[p.name] = plugin.symbolDependencies[p]
 | |
|             else:
 | |
|                 lib[p.name] = plugin.symbolDependencies[p]
 | |
| 
 | |
|         for i in spec:
 | |
|             if i in symb:
 | |
|                 total = symb[i]['total']
 | |
|                 print '    {}: OK ({} usages)'.format(i, total)
 | |
| 
 | |
|                 self._printSome(symb[i])
 | |
|                 del symb[i]
 | |
|             else:
 | |
|                 if spec[i] == 'optional':
 | |
|                     print '    {}: OK (optional)'.format(i)
 | |
|                 else:
 | |
|                     print '    {}: WARNING: unused'.format(i)
 | |
|         for i in symb:
 | |
|             total = symb[i]['total']
 | |
|             print '    {}: ERROR: undeclared ({} usages)'.format(i, total)
 | |
|             self._printSome(symb[i])
 | |
|         for i in lib:
 | |
|             total = lib[i]['total']
 | |
|             print '    LIBRARY {} used ({} usages)'.format(i, total)
 | |
| 
 | |
|     def _printSome(self, data):
 | |
|         keys = data.keys()
 | |
|         if len(keys) <= 11:
 | |
|             for i in keys:
 | |
|                 if i != 'total':
 | |
|                     print '        {}'.format(i)
 | |
| 
 | |
| 
 | |
| 
 | |
| class BinaryDirExaminer:
 | |
|     def __init__(self, path):
 | |
|         self.libraries = []
 | |
|         self.plugins = []
 | |
|         self.binaryDir = path
 | |
| 
 | |
|         log.debug('Examining directory "%s".', path)
 | |
| 
 | |
|         self._findLibraries(path)
 | |
|         self._findPlugins(path)
 | |
| 
 | |
|     def _findLibraries(self, path):
 | |
|         libdir = glob.glob(os.path.join(path, "lib", "qtcreator", "lib*"))
 | |
|         for l in libdir:
 | |
|             if os.path.islink(l):
 | |
|                 continue
 | |
|             log.debug('   Looking at library "%s".', l)
 | |
|             self.libraries.append(Library(l))
 | |
| 
 | |
|     def _findPlugins(self, path):
 | |
|         pluginspecs = glob.glob(os.path.join(path, "lib", "qtcreator", "plugins", "*.pluginspec"))
 | |
|         for spec in pluginspecs:
 | |
|             log.debug('   Looking at plugin "%s".', spec)
 | |
|             self.plugins.append(Plugin(spec))
 | |
| 
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     # Setup logging:
 | |
|     log = logging.getLogger('log')
 | |
|     log.setLevel(logging.DEBUG)
 | |
|     ch = logging.StreamHandler()
 | |
|     ch.setLevel(logging.DEBUG)
 | |
|     log.addHandler(ch)
 | |
| 
 | |
|     # Make sure we are on linux:
 | |
|     if platform.system() != 'Linux':
 | |
|         log.critical("This check can only run on Linux")
 | |
|         sys.exit(1)
 | |
| 
 | |
|     # Sanity check:
 | |
|     if not(os.path.exists(os.path.join(os.getcwd(), "bin", "qtcreator"))):
 | |
|         log.critical('Not a top level Qt Creator build directory.')
 | |
|         sys.exit(1)
 | |
| 
 | |
|     binExaminer = BinaryDirExaminer(os.path.abspath(os.getcwd()))
 | |
|     # Find symbol dependencies:
 | |
|     resolver = SymbolResolver(binExaminer.plugins, binExaminer.libraries)
 | |
|     reporter = Reporter(binExaminer.plugins)
 |