#!/usr/bin/python # encoding: UTF-8 #=============================================================================== # Define global imports #=============================================================================== import os import re import sys import codecs import hashlib import subprocess as sp from . import constants from .GLError import GLError from .GLConfig import GLConfig from .GLFileSystem import GLFileSystem #=============================================================================== # Define module information #=============================================================================== __author__ = constants.__author__ __license__ = constants.__license__ __copyright__ = constants.__copyright__ #=============================================================================== # Define global constants #=============================================================================== PYTHON3 = constants.PYTHON3 NoneType = type(None) APP = constants.APP DIRS = constants.DIRS ENCS = constants.ENCS UTILS = constants.UTILS MODES = constants.MODES TESTS = constants.TESTS compiler = constants.compiler joinpath = constants.joinpath cleaner = constants.cleaner string = constants.string isabs = os.path.isabs isdir = os.path.isdir isfile = os.path.isfile normpath = os.path.normpath relpath = os.path.relpath filter_filelist = constants.filter_filelist #=============================================================================== # Define GLModuleSystem class #=============================================================================== class GLModuleSystem(object): '''GLModuleSystem is used to operate with module system using dynamic searching and patching.''' def __init__(self, config): '''GLModuleSystem.__init__(config) -> GLModuleSystem Create new GLModuleSystem instance. Some functions use GLFileSystem class to look up a file in localdir or gnulib directories, or combine it through 'patch' utility.''' self.args = dict() if type(config) is not GLConfig: raise(TypeError('config must be a GLConfig, not %s' % type(config).__name__)) self.config = config self.filesystem = GLFileSystem(self.config) def __repr__(self): '''x.__repr__ <==> repr(x)''' result = '' % hex(id(self)) return(result) def exists(self, module): '''GLModuleSystem.exists(module) -> bool Check whether the given module exists. GLConfig: localdir.''' if type(module) is bytes or string: if type(module) is bytes: module = module.decode(ENCS['default']) else: # if module has not bytes or string type raise(TypeError( 'module must be a string, not %s' % type(module).__name__)) result = bool() badnames = ['ChangeLog', 'COPYING', 'README', 'TEMPLATE', 'TEMPLATE-EXTENDED', 'TEMPLATE-TESTS'] if isfile(joinpath(DIRS['modules'], module)) or \ all([ # Begin all(iterable) function self.config['localdir'], isdir(joinpath(self.config['localdir'], 'modules')), isfile( joinpath(self.config['localdir'], 'modules', module)) ]): # Close all(iterable) function if module not in badnames: result = True return(result) def find(self, module): '''GLModuleSystem.find(module) -> GLModule Find the given module.''' if type(module) is bytes or string: if type(module) is bytes: module = module.decode(ENCS['default']) else: # if module has not bytes or string type raise(TypeError( 'module must be a string, not %s' % type(module).__name__)) if self.exists(module): path, istemp = self.filesystem.lookup(joinpath('modules', module)) result = GLModule(self.config, path, istemp) return(result) else: # if not self.exists(module) if self.config['errors']: raise(GLError(3, module)) else: # if not self.config['errors'] sys.stderr.write('gnulib-tool: warning: ') sys.stderr.write('file %s does not exist\n' % str(module)) def list(self): '''GLModuleSystem.list() -> list Return the available module names as tuple. We could use a combination of os.walk() function and re module. However, it takes too much time to complete, so this version uses subprocess to run shell commands.''' result = string() listing = list() localdir = self.config['localdir'] find_args = ['find', 'modules', '-type', 'f', '-print'] sed_args = \ [ 'sed', '-e', r's,^modules/,,', '-e', r'/^ChangeLog$/d', '-e', r'/\/ChangeLog$/d', '-e', r'/^COPYING$/d', '-e', r'/\/COPYING$/d', '-e', r'/^README$/d', '-e', r'/\/README$/d', '-e', r'/^TEMPLATE$/d', '-e', r'/^TEMPLATE-EXTENDED$/d', '-e', r'/^TEMPLATE-TESTS$/d', '-e', r'/^\..*/d', '-e', r'/~$/d', '-e', r'/-tests$/d', ] # Read modules from gnulib root directory. os.chdir(constants.DIRS['root']) find = sp.Popen(find_args, stdout=sp.PIPE) result += find.stdout.read().decode("UTF-8") # Read modules from local directory. if localdir and isdir(joinpath(localdir, 'modules')): os.chdir(localdir) find = sp.Popen(find_args, stdout=sp.PIPE) result += find.stdout.read().decode("UTF-8") sed_args += ['-e', r's,\.diff$,,'] # Save the list of the modules to file. os.chdir(DIRS['cwd']) path = joinpath(self.config['tempdir'], 'list') with codecs.open(path, 'wb', 'UTF-8') as file: file.write(result) # Filter the list of the modules. stdin = codecs.open(path, 'rb', 'UTF-8') sed = sp.Popen(sed_args, stdin=stdin, stdout=sp.PIPE) result = sed.stdout.read().decode("UTF-8") stdin.close() os.remove(path) listing = [line for line in result.split('\n') if line.strip()] listing = sorted(set(listing)) return(listing) #=============================================================================== # Define GLModule class #=============================================================================== class GLModule(object): '''GLModule is used to create a module object from the file with the given path. GLModule can get all information about module, get its dependencies, files, etc.''' def __init__(self, config, module, patched=False): '''GLModule.__init__(config, module[, patched]) -> GLModule Create new GLModule instance. Arguments are module and patched, where module is a string representing the path to the module and patched is a bool indicating that module was created after applying patch.''' self.args = dict() self.cache = dict() self.content = string() if type(config) is not GLConfig: raise(TypeError('config must be a GLConfig, not %s' % type(config).__name__)) if type(module) is bytes or type(module) is string: if type(module) is bytes: module = module.decode(ENCS['default']) else: # if module has not bytes or string type raise(TypeError('module must be a string, not %s' % type(module).__name__)) if type(patched) is not bool: raise(TypeError('patched must be a bool, not %s' % type(module).__name__)) self.module = module self.patched = patched self.config = config self.filesystem = GLFileSystem(self.config) self.modulesystem = GLModuleSystem(self.config) with codecs.open(module, 'rb', 'UTF-8') as file: self.content = file.read() self.regex = '(?:Description:|Comment:|Status:|Notice:|Applicability:|\ Files:|Depends-on:|configure\\.ac-early:|configure\\.ac:|Makefile\\.am:|\ Include:|Link:|License:|Maintainer:)' def __eq__(self, module): '''x.__eq__(y) <==> x==y''' result = bool() if type(module) is GLModule: if self.module == module.module: result = True return(result) def __ne__(self, module): '''x.__ne__(y) <==> x!=y''' result = bool() if type(module) is GLModule: if self.module != module.module: result = True return(result) def __ge__(self, module): '''x.__ge__(y) <==> x>=y''' result = bool() if type(module) is GLModule: if self.module >= module.module: result = True return(result) def __gt__(self, module): '''x.__gt__(y) <==> x>y''' result = bool() if type(module) is GLModule: if self.module > module.module: result = True return(result) def __hash__(self): '''x.__hash__() <==> hash(x)''' module = hash(self.module) patched = hash(self.patched) result = module ^ patched return(result) def __le__(self, module): '''x.__le__(y) <==> x<=y''' result = bool() if type(module) is GLModule: if self.module <= module.module: result = True return(result) def __lt__(self, module): '''x.__lt__(y) <==> x str(x)''' result = self.getName() return(result) def __repr__(self): '''x.__repr__ <==> repr(x)''' result = '' % \ (repr(self.getName()), hex(id(self))) return(result) def getName(self): '''GLModule.getName() -> string Return the name of the module.''' pattern = compiler(joinpath('modules', '(.*?)$')) result = pattern.findall(self.module)[0] return(result) def isPatched(self): '''GLModule.isPatched() -> bool Check whether module was created after applying patch.''' return(self.patched) def isTests(self): '''GLModule.isTests() -> bool Check whether module is a -tests version of module.''' result = self.getName().endswith('-tests') return(result) def isNonTests(self): '''GLModule.isTests() -> bool Check whether module is not a -tests version of module.''' result = not(self.isTests()) return(result) def getTestsName(self): '''Return -tests version of the module name.''' result = self.getName() if not result.endswith('-tests'): result += '-tests' return(result) def getTestsModule(self): '''Return -tests version of the module as GLModule.''' result = self.modulesystem.find(self.getTestsName()) return(result) def getShellFunc(self): '''GLModule.getShellFunc() -> string Computes the shell function name that will contain the m4 macros for the module.''' isalnum = True macro_prefix = self.config['macro_prefix'] for char in str(module): if char not in constants.ALPHANUMERIC: isalnum = False break if isalnum: module = str(self) else: # if not isalnum module = '%s\n' % str(self) if type(module) is string: module = module.encode(ENCS['default']) module = hashlib.md5(module).hexdigest() result = 'func_%s_gnulib_m4code_%s' % (macro_prefix, module) if type(result) is bytes: result = result.decode(ENCS['default']) return(result) def getShellVar(self): '''GLModule.getShellVar() -> string Compute the shell variable name the will be set to true once the m4 macros for the module have been executed.''' isalnum = True macro_prefix = self.config['macro_prefix'] for char in str(module): if char not in constants.ALPHANUMERIC: isalnum = False break if isalnum: module = str(self) else: # if not isalnum module = '%s\n' % str(self) if type(module) is string: module = module.encode(ENCS['default']) module = hashlib.md5(module).hexdigest() result = '%s_gnulib_enabled_%s' % (macro_prefix, module) if type(result) is bytes: result = result.decode(ENCS['default']) return(result) def getConditionalName(self): '''GLModule.getConditionalName() -> string Return the automake conditional name. GLConfig: macro_prefix.''' macro_prefix = self.config['macro_prefix'] nonascii = \ [ # Begin to filter non-ascii chars char for char in self.getName() if char not in \ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' ] # Finish to filter non-ascii chars if nonascii: name = self.getName().encode(ENCS['default']) name = hashlib.md5(name).hexdigest() conditional = '%s_GNULIB_ENABLED_%s' % (macro_prefix, name) else: # if not nonascii result = '%s_GNULIB_ENABLED_%s' (macro_prefix, name) if type(result) is bytes: result = result.decode(ENCS['default']) return(result) def getDescription(self): '''GLModule.getDescription() -> string Return description of the module.''' section = 'Description:' if 'description' not in self.cache: if section not in self.content: result = string() else: # if section in self.content pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex) pattern = compiler(pattern, re.S | re.M) result = pattern.findall(self.content) if type(result) is list: if not result: result = string() else: # if result result = result[-1] result = result.strip() self.cache['description'] = result return(self.cache['description']) def getComment(self): '''GLModule.getComment() -> string Return comment to module.''' section = 'Comment:' if 'comment' not in self.cache: if section not in self.content: result = string() else: # if section in self.content pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex) pattern = compiler(pattern, re.S | re.M) result = pattern.findall(self.content) if type(result) is list: if not result: result = string() else: # if result result = result[-1] result = result.strip() self.cache['comment'] = result return(self.cache['comment']) def getStatus(self): '''GLModule.getStatus() -> string Return module status.''' section = 'Status:' if 'status' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = [part.strip() for part in parts if part.strip()] self.cache['status'] = list(result) return(list(self.cache['status'])) def getNotice(self): '''GLModule.getNotice() -> string Return notice to module.''' section = 'Notice:' if 'notice' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = ''.join(parts) self.cache['notice'] = result return(self.cache['notice']) def getApplicability(self): '''GLModule.getApplicability() -> string Return applicability of module.''' section = 'Applicability:' if 'applicability' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] parts = [part.strip() for part in parts] result = ''.join(parts) if not result.strip(): if self.getName().endswith('-tests'): result = 'tests' else: # if not self.getName().endswith('-tests') result = 'main' if type(result) is bytes: result = result.decode(ENCS['default']) result = result.strip() self.cache['applicability'] = result return(self.cache['applicability']) def getFiles(self): '''GLModule.getFiles() -> list Return list of files. GLConfig: ac_version.''' ac_version = self.config['ac_version'] section = 'Files:' result = list() if 'files' not in self.cache: if section not in self.content: result = list() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = [part.strip() for part in parts if part.strip()] result += [joinpath('m4', '00gnulib.m4')] result += [joinpath('m4', 'gnulib-common.m4')] if ac_version == 2.59: result += [joinpath('m4', 'onceonly.m4')] self.cache['files'] = list(result) return(list(self.cache['files'])) def getDependencies(self): '''GLModule.getDependencies() -> list Return list of dependencies. GLConfig: localdir.''' localdir = self.config['localdir'] result = list() section = 'Depends-on:' if 'dependencies' not in self.cache: if section not in self.content: depmodules = list() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] modules = ''.join(parts) modules = [line for line in modules.split( '\n') if line.strip()] modules = [ module for module in modules if not module.startswith('#')] for line in modules: split = [part for part in line.split(' ') if part.strip()] if len(split) == 1: module = line.strip() condition = None else: # if len(split) != 1 module = split[0] condition = split[1] if type(condition) is bytes: condition = condition.decode(ENCS['default']) result += [tuple([self.modulesystem.find(module), condition])] self.cache['dependencies'] = result return(list(self.cache['dependencies'])) def getAutoconfSnippet_Early(self): '''GLModule.getAutoconfSnippet_Early() -> string Return autoconf-early snippet.''' section = 'configure.ac-early:' if 'autoconf-early' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = ''.join(parts) self.cache['autoconf-early'] = result return(self.cache['autoconf-early']) def getAutoconfSnippet(self): '''GLModule.getAutoconfSnippet() -> string Return autoconf snippet.''' section = 'configure.ac:' if 'autoconf' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = ''.join(parts) self.cache['autoconf'] = result return(self.cache['autoconf']) def getAutomakeSnippet(self): '''getAutomakeSnippet() -> string Get automake snippet. GLConfig: auxdir, ac_version.''' result = string() # Define stack variable conditional = self.getAutomakeSnippet_Conditional() if conditional.strip(): result += self.getAutomakeSnippet_Conditional() else: # if not conditional.strip() result += '\n' result += self.getAutomakeSnippet_Unconditional() return(result) def getAutomakeSnippet_Conditional(self): '''GLModule.getAutomakeSnippet_Conditional() -> string Return conditional automake snippet.''' section = 'Makefile.am:' if 'makefile-conditional' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = ''.join(parts) self.cache['makefile-conditional'] = result return(self.cache['makefile-conditional']) def getAutomakeSnippet_Unconditional(self): '''GLModule.getAutomakeSnippet_Unconditional() -> string Return unconditional automake snippet. GLConfig: auxdir, ac_version.''' auxdir = self.config['auxdir'] ac_version = self.config['ac_version'] result = string() if 'makefile-unconditional' not in self.cache: if self.isTests(): files = self.getFiles() extra_files = filter_filelist(constants.NL, files, 'tests/', '', 'tests/', '').split(constants.NL) extra_files = sorted(set(extra_files)) if extra_files: result += string('EXTRA_DIST += %s' % ' '.join(extra_files)) result += constants.NL * 2 else: # if not tests module # TODO: unconditional automake snippet for nontests modules snippet = self.getAutomakeSnippet_Conditional() snippet = constants.combine_lines(snippet) pattern = compiler( '^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M) mentioned_files = pattern.findall(snippet) if mentioned_files != list(): mentioned_files = mentioned_files[-1].split(' ') mentioned_files = [f.strip() for f in mentioned_files] mentioned_files = [f for f in mentioned_files if f != ''] mentioned_files = sorted(set(mentioned_files)) all_files = self.getFiles() lib_files = filter_filelist(constants.NL, all_files, 'lib/', '', 'lib/', '').split(constants.NL) extra_files = [ f for f in lib_files if f not in mentioned_files] extra_files = sorted(set(extra_files)) if extra_files != [''] and extra_files: result += string('EXTRA_DIST += %s' % ' '.join(extra_files)) result += '\n\n' # Synthesize also an EXTRA_lib_SOURCES augmentation if str(self) != 'relocatable-prog-wrapper' and str(self) != 'pt_chown': extra_files = filter_filelist(constants.NL, extra_files, '', '.c', '', '').split(constants.NL) extra_files = sorted(set(extra_files)) if extra_files != ['']: result += string('EXTRA_lib_SOURCES += %s' % ' '.join(extra_files)) result += '\n\n' # Synthesize an EXTRA_DIST augmentation also for the files in build-aux buildaux_files = filter_filelist(constants.NL, all_files, 'build-aux/', '', 'build-aux/', '').split(constants.NL) buildaux_files = sorted(set(buildaux_files)) if buildaux_files != ['']: buildaux_files = ''.join(buildaux_files) buildaux_files = joinpath( '$(top_srcdir)', auxdir, buildaux_files) result += string('EXTRA_DIST += %s' % buildaux_files) result += '\n\n' # Synthesize an EXTRA_DIST augmentation also for the files from top/. top_files = filter_filelist(constants.NL, all_files, 'top/', '', 'top/', '').split(constants.NL) top_files = sorted(set(top_files)) if top_files != ['']: top_files = ''.join(top_files) top_files = joinpath('$(top_srcdir)', top_files) result += string('EXTRA_DIST += %s' % top_files) result += '\n\n' result = constants.nlconvert(result) self.cache['makefile-unconditional'] = result return(self.cache['makefile-unconditional']) def getInclude(self): '''GLModule.getInclude() -> string Return include directive.''' section = 'Include:' if 'include' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = ''.join(parts) result = result.strip() pattern = compiler('^(["<].*?[>"])', re.S | re.M) result = pattern.sub('#include \\1', result) self.cache['include'] = result return(self.cache['include']) def getLink(self): '''GLModule.getLink() -> string Return link directive.''' section = 'Link:' if 'link' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] parts = [part.strip() for part in parts if part.strip()] result = ''.join(parts) self.cache['link'] = result return(self.cache['link']) def getLicense(self): '''GLModule.getLicense(self) -> string Get license and warn user if module lacks a license.''' license = self.getLicense_Raw() if not self.isTests(): if not license: if self.config['errors']: raise(GLError(18, string(self))) else: # if not self.config['errors'] sys.stderr.write('gnulib-tool: warning: ') sys.stderr.write('module %s lacks a license\n' % str(self)) if not license: license = 'GPL' return(license) def getLicense_Raw(self): '''GLModule.getLicense_Raw() -> string Return module license.''' section = 'License:' if 'license' not in self.cache: if section not in self.content: result = string() else: # if section in self.content pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex) pattern = compiler(pattern, re.S | re.M) result = pattern.findall(self.content) if type(result) is list: if not result: result = string() else: # if result result = result[-1] result = result.strip() self.cache['license'] = result return(self.cache['license']) def getMaintainer(self): '''GLModule.getMaintainer() -> string Return maintainer directive.''' section = 'Maintainer:' if 'maintainer' not in self.cache: if section not in self.content: result = string() else: # if section in self.content snippet = self.content.split(section)[-1] snippet = snippet.replace('\r\n', '\n') lines = ['%s\n' % line for line in snippet.split('\n')] parts = list() for line in lines: regex = '^(Description|Comment|Status|Notice|Applicability|' regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|' regex += 'Makefile\\.am|Include|Link|License|Maintainer):$' pattern = compiler(regex) findflag = pattern.findall(line) if findflag: break parts += [line] result = ''.join(parts) result = result.strip() self.cache['maintainer'] = result return(self.cache['maintainer']) #=============================================================================== # Define GLModuleTable class #=============================================================================== class GLModuleTable(object): '''GLModuleTable is used to work with the list of the modules.''' def __init__(self, config, avoids=list()): '''GLModuleTable.__init__(config, avoids) -> GLModuleTable Create new GLModuleTable instance. If modules are specified, then add every module from iterable as unconditional module. If avoids is specified, then in transitive_closure every dependency which is in avoids won't be included in the final modules list. If testflags iterable is enabled, then don't add module which status is in the testflags. If conddeps are enabled, then store condition for each dependency if it has a condition. The only necessary argument is localdir, which is needed just to create modulesystem instance to look for dependencies.''' self.avoids = list() # Avoids self.dependers = dict() # Dependencies self.conditionals = dict() # Conditional modules self.unconditionals = dict() # Unconditional modules self.base_modules = list() # Base modules self.main_modules = list() # Main modules self.tests_modules = list() # Tests modules self.final_modules = list() # Final modules if type(config) is not GLConfig: raise(TypeError('config must be a GLConfig, not %s' % type(config).__name__)) for avoid in avoids: if type(avoid) is not GLModule: raise(TypeError('each avoid must be a GLModule instance')) self.avoids += [avoids] self.config = config self.filesystem = GLFileSystem(self.config) self.modulesystem = GLModuleSystem(self.config) def __repr__(self): '''x.__repr__() <==> repr(x)''' result = '' % hex(id(self)) return(result) def __getitem__(self, y): '''x.__getitem__(y) <==> x[y]''' if y in ['base', 'final', 'main', 'tests', 'avoids']: if y == 'base': return(self.getBaseModules()) elif y == 'final': return(self.getFinalModules()) elif y == 'main': return(self.getMainModules()) elif y == 'tests': return(self.getTestsModules()) else: # if y == 'avoids' return(self.getAvoids()) else: # if y is not in list raise(KeyError('GLModuleTable does not contain key: %s' % repr(y))) def addConditional(self, parent, module, condition): '''GLModuleTable.addConditional(module, condition) Add new conditional dependency from parent to module with condition.''' if type(parent) is not GLModule: raise(TypeError('parent must be a GLModule, not %s' % type(parent).__name__)) if type(module) is not GLModule: raise(TypeError('module must be a GLModule, not %s' % type(module).__name__)) if type(condition) is bytes or type(condition) is string \ or condition == True: if type(condition) is bytes: condition = condition.decode(ENCS['default']) else: # if condition has not bytes or string type or is not True raise(TypeError('condition must be a string or True, not %s' % type(condition).__name__)) if not str(module) in self.unconditionals: if str(module) not in self.dependers: self.dependers[module] = list() self.dependers[module] += [module] key = '%s---%s' % (str(parent), str(module)) self.conditionals[key] = condition def addUnconditional(self, module): '''GLModuleTable.addUnconditional(module) Add module as unconditional dependency.''' if type(module) is not GLModule: raise(TypeError('module must be a GLModule, not %s' % type(module).__name__)) if str(module) in self.dependers: self.dependers.pop(str(module)) self.unconditionals[str(module)] = True def isConditional(self, module): '''GLModuleTable.isConditional(module) -> bool Check whether module is unconditional.''' if type(module) is not GLModule: raise(TypeError('module must be a GLModule, not %s' % type(module).__name__)) result = str(module) in self.dependers return(result) def getCondition(self, parent, module): '''GLModuleTable.getCondition(module) -> string or True Return condition from parent to module. Condition can be string or True. If module is not in the list of conddeps, method returns None.''' if type(parent) is not GLModule: raise(TypeError('parent must be a GLModule, not %s' % type(parent).__name__)) if type(module) is not GLModule: raise(TypeError('module must be a GLModule, not %s' % type(module).__name__)) key = '%s---%s' % (str(parent), str(module)) result = None if key in self.conditionals: result = self.conditionals[key] return(result) def transitive_closure(self, modules): '''GLModuleTable.transitive_closure(modules) -> list Use transitive closure to add module and its dependencies. Add every module and its dependencies from modules list, but do not add dependencies which contain in avoids list. If any testflag is enabled, then do not add dependencies which have the status as this flag. If conddeps are enabled, then store condition for each dependency if it has a condition. This method is used to update final list of modules. Method returns list of modules. GLConfig: testflags.''' for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) handledmodules = list() inmodules = modules outmodules = list() if self.config['conddeps']: for module in modules: self.addUnconditional(module) while inmodules: inmodules_this_round = inmodules inmodules = list() for module in inmodules_this_round: outmodules += [module] if self.config['conddeps']: automake_snippet = \ module.getAutomakeSnippet_Conditional() pattern = compiler('^if') if not pattern.findall(automake_snippet): self.addUnconditional(module) conditional = self.isConditional(module) dependencies = module.getDependencies() depmodules = [pair[0] for pair in dependencies] conditions = [pair[1] for pair in dependencies] if TESTS['tests'] in self.config['testflags']: testsname = module.getTestsName() if self.modulesystem.exists(testsname): testsmodule = self.modulesystem.find(testsname) depmodules += [testsmodule] conditions += [None] for depmodule in depmodules: include = True includes = list() status = depmodule.getStatus() for word in status: if word == 'obsolete': if TESTS['obsolete'] in self.config['testflags'] or \ TESTS['all-test'] in self.config['testflags']: includes += [False] elif word == 'c++-test': if TESTS['c++-test'] in self.config['testflags'] or \ TESTS['all-test'] in self.config['testflags']: includes += [False] elif word == 'longrunning-test': if TESTS['longrunning-test'] in self.config['testflags'] or \ TESTS['all-test'] in self.config['testflags']: includes += [False] elif word == 'privileged-test': if TESTS['privileged-test'] in self.config['testflags'] or \ TESTS['all-test'] in self.config['testflags']: includes += [False] elif word == 'all-test': if TESTS['all-test'] in self.config['testflags'] or \ TESTS['all-test'] in self.config['testflags']: includes += [False] else: # if any other word if word.endswith('-tests'): if TESTS['all-test'] in self.config['testflags']: includes += [False] include = any(includes) if include and depmodule not in self.avoids: inmodules += [depmodule] if self.config['conddeps']: index = depmodules.index(depmodule) condition = conditions[index] if condition: self.addConditional( module, depmodule, condition) else: # if condition if conditional: self.addConditional( module, depmodule, True) else: # if not conditional self.addUnconditional(module) listing = list() # Create empty list inmodules = sorted(set(inmodules)) handledmodules = sorted(set(handledmodules + inmodules_this_round)) inmodules = \ [ # Begin to filter inmodules module for module in inmodules if module not in handledmodules ] # Finish to filter inmodules inmodules = sorted(set(inmodules)) modules = sorted(set(outmodules)) self.modules = modules return(list(modules)) def transitive_closure_separately(self, basemodules, finalmodules): '''GLModuleTable.transitive_closure_separately(*args, **kwargs) -> tuple Determine main module list and tests-related module list separately. The main module list is the transitive closure of the specified modules, ignoring tests modules. Its lib/* sources go into $sourcebase/. If lgpl is specified, it will consist only of LGPLed source. The tests-related module list is the transitive closure of the specified modules, including tests modules, minus the main module list excluding modules of applicability 'all'. Its lib/* sources (brought in through dependencies of *-tests modules) go into $testsbase/. It may contain GPLed source, even if lgpl is specified. Arguments are basemodules and finalmodules, where basemodules argument represents modules specified by user and finalmodules represents modules list after previous transitive_closure. Method returns tuple which contains two lists: the list of main modules and the list of tests-related modules. Both lists contain dependencies. GLConfig: testflags.''' inctests = False main_modules = list() tests_modules = list() if TESTS['tests'] in self.config['testflags']: self.config['testflags'].pop(TESTS['tests']) inctests = True for module in basemodules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) for module in finalmodules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) main_modules = self.transitive_closure(basemodules) tests_modules = \ [m for m in finalmodules if m not in main_modules] + \ [m for m in main_modules if m.getApplicability() != 'main'] tests_modules = sorted(set(tests_modules)) if inctests: testflags = sorted( set(self.config['testflags'] + [TESTS['tests']])) self.config.setTestFlags(testflags) result = tuple([main_modules, tests_modules]) return(result) def add_dummy(self, modules): '''GLModuleTable.add_dummy(modules) -> list Add dummy package to list of modules if dummy package is needed. If not, return original list of modules. GLConfig: auxdir, ac_version.''' auxdir = self.config['auxdir'] ac_version = self.config['ac_version'] have_lib_sources = False for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) snippet = module.getAutomakeSnippet() snippet = constants.remove_backslash_newline(snippet) pattern = compiler( '^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M) files = pattern.findall(snippet) if files: # if source files were found files = files[-1].split(' ') for file in files: if not file.endswith('.h'): have_lib_sources = True break if not have_lib_sources: dummy = self.modulesystem.find('dummy') modules = sorted(set(modules + [dummy])) return(list(modules)) def filelist(self, modules): '''GLModuleTable.filelist(modules) -> list Determine the final file list for the given list of modules. The list of modules must already include dependencies. GLConfig: ac_version.''' ac_version = self.config['ac_version'] filelist = list() for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) listings = [module.getFiles() for module in modules] for listing in listings: for file in listing: if file not in filelist: filelist += [file] return(filelist) def filelist_separately(self, main_modules, tests_modules): '''GLModuleTable.filelist_separately(**kwargs) -> list Determine the final file lists. They must be computed separately, because files in lib/* go into $sourcebase/ if they are in the main file list but into $testsbase/ if they are in the tests-related file list. Furthermore lib/dummy.c can be in both.''' ac_version = self.config['ac_version'] main_filelist = self.filelist(main_modules) tests_filelist = self.filelist(tests_modules) tests_filelist = \ [ # Begin to sort filelist file.replace('lib/', 'tests=lib/', 1) \ if file.startswith('lib/') else file for file in tests_filelist ] # Finish to sort filelist result = tuple([main_filelist, tests_filelist]) return(result) def getAvoids(self): '''GLModuleTable.getAvoids() -> list Return list of avoids.''' return(list(self.avoids)) def setAvoids(self, modules): '''GLModuleTable.setAvoids(modules) Specify list of avoids.''' for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) self.avoids = sorted(set(modules)) def getBaseModules(self): '''GLModuleTable.getBaseModules() -> list Return list of base modules.''' return(list(self.base_modules)) def setBaseModules(self, modules): '''GLModuleTable.setBaseModules(modules) Specify list of base modules.''' for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) self.base_modules = sorted(set(modules)) def getFinalModules(self): '''GLModuleTable.getFinalModules() -> list Return list of final modules.''' return(list(self.final_modules)) def setFinalModules(self, modules): '''GLModuleTable.setFinalModules(modules) Specify list of final modules.''' for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) self.final_modules = sorted(set(modules)) def getMainModules(self): '''GLModuleTable.getMainModules() -> list Return list of main modules.''' return(list(self.main_modules)) def setMainModules(self, modules): '''GLModuleTable.setMainModules(modules) Specify list of main modules.''' for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) self.main_modules = sorted(set(modules)) def getTestsModules(self): '''GLModuleTable.getTestsModules() -> list Return list of tests modules.''' return(list(self.tests_modules)) def setTestsModules(self, modules): '''GLModuleTable.setTestsModules(modules) Specify list of tests modules.''' for module in modules: if type(module) is not GLModule: raise(TypeError('each module must be a GLModule instance')) self.tests_modules = sorted(set(modules))