diff --git a/gnulib-tool.py b/gnulib-tool.py new file mode 100644 index 000000000..f61654039 --- /dev/null +++ b/gnulib-tool.py @@ -0,0 +1,987 @@ +#!/usr/bin/python +# +# Copyright (C) 2002-2012 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# This program is meant for authors or maintainers which want to import +# modules from gnulib into their packages. + + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import codecs +import random +import shutil +import argparse +import subprocess as sp +from pprint import pprint +from pygnulib import constants +from pygnulib import classes + + +#=============================================================================== +# Define global constants +#=============================================================================== +PYTHON3 = constants.PYTHON3 +APP = constants.APP +DIRS = constants.DIRS +ENCS = constants.ENCS +UTILS = constants.UTILS +FILES = constants.FILES +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 + + +#=============================================================================== +# Define main part +#=============================================================================== +def main(): + # Reset arguments + mode = None + destdir = None + localdir = None + modcache = None + verbose = None + auxdir = None + modules = None + avoids = None + sourcebase = None + m4base = None + pobase = None + docbase = None + testsbase = None + tests = None + libname = None + lgpl = None + makefile = None + libtool = None + conddeps = None + macro_prefix = None + podomain = None + witness_c_macro = None + vc_files = None + dryrun = None + errors = None + + info = classes.GLInfo() + parser = argparse.ArgumentParser( + prog=constants.APP['name'], + usage='gnulib-tool.py --help', + add_help=False) + # help + parser.add_argument('-h', '--help', '--hel', '--he', '--h', + dest='help', + default=None, + action='store_true') + # version + parser.add_argument('--version', '--versio', '--versi', '--vers', + dest='version', + default=None, + action='store_true') + # list + parser.add_argument('-l', '--list', '--lis', + dest='mode_list', + default=None, + action='store_true') + # find + parser.add_argument('-f', '--find', '--fin', '--fi', '--f', + dest='mode_find', + default=None, + nargs='*') + # import + parser.add_argument('-i', '--import', + dest='mode_import', + default=None, + nargs='+') + # add-import + parser.add_argument('-a', '--add-import', + dest='mode_add_import', + default=None, + nargs='+') + # remove-import + parser.add_argument('-r', '--remove-import', + dest='mode_remove_import', + default=None, + nargs='+') + # update + parser.add_argument('-u', '--update', + dest='mode_update', + default=None, + action='store_true') + # create-testdir + parser.add_argument('-td', '--create-testdir', + dest='mode_create_testdir', + default=None, + nargs='*') + # create-megatestdir + parser.add_argument('-mtd', '--create-megatestdir', + dest='mode_create_megatestdir', + default=None, + nargs='*') + # test + parser.add_argument('-t', '--test', + dest='mode_test', + default=None, + nargs='*') + # megatest + parser.add_argument('-mt', '--megatest', '--megates', '--megate', '--megat', + '--mega', '--meg', '--me', '--m', + dest='mode_megatest', + default=None, + nargs='*') + # copy-file + parser.add_argument('-c', '--copy-file', + dest='mode_copy_file', + default=None, + nargs='+') + # extract-* + parser.add_argument('-xD', '--extract-description', + dest='mode_xdescription', + default=None, + nargs='*') + parser.add_argument('-xc', '--extract-comment', + dest='mode_xcomment', + default=None, + nargs='*') + parser.add_argument('-xs', '--extract-status', + dest='mode_xstatus', + default=None, + nargs='*') + parser.add_argument('-xn', '--extract-notice', + dest='mode_xnotice', + default=None, + nargs='*') + parser.add_argument('-xa', '--extract-applicability', + dest='mode_xapplicability', + default=None, + nargs='*') + parser.add_argument('-xf', '--extract-filelist', + dest='mode_xfilelist', + default=None, + nargs='*') + parser.add_argument('-xd', '--extract-dependencies', + dest='mode_xdependencies', + default=None, + nargs='*') + parser.add_argument('-xac', '--extract-autoconf-snippet', + dest='mode_xautoconf', + default=None, + nargs='*') + parser.add_argument('-xam', '--extract-automake-snippet', + dest='mode_xautomake', + default=None, + nargs='*') + parser.add_argument('-xi', '--extract-include-directive', + dest='mode_xinclude', + default=None, + nargs='*') + parser.add_argument('-xl', '--extract-link-directive', + dest='mode_xlink', + default=None, + nargs='*') + parser.add_argument('-xL', '--extract-license', + dest='mode_xlicense', + default=None, + nargs='*') + parser.add_argument('-xm', '--extract-maintainer', + dest='mode_xmaintainer', + default=None, + nargs='*') + # destdir + parser.add_argument('-d', '--destdir', + dest='destdir', + default=None, + nargs=1) + # localdir + parser.add_argument('-ld', '--local-dir', + dest='localdir', + default=None, + nargs=1) + # verbose + parser.add_argument('-v', '--verbose', + default=0, + action='count') + # quiet + parser.add_argument('-q', '--quiet', + default=0, + action='count') + # dryrun + parser.add_argument('--dry-run', + dest='dryrun', + default=None, + action='store_true') + # inctests + parser.add_argument('--with-tests', + dest='inctests', + default=None, + action='store_true') + # obsolete + parser.add_argument('--with-obsolete', + dest='obsolete', + default=None, + action='store_true') + # c++-tests + parser.add_argument('--with-c++-tests', + dest='cxx', + default=None, + action='store_true') + # longrunning-tests + parser.add_argument('--with-longrunning-tests', + dest='longrunning', + default=None, + action='store_true') + # privileged-tests + parser.add_argument('--with-privileged-tests', + dest='privileged', + default=None, + action='store_true') + # unportable-tests + parser.add_argument('--with-unportable-tests', + dest='unportable', + default=None, + action='store_true') + # all-tests + parser.add_argument('--with-all-tests', + dest='alltests', + default=None, + action='store_true') + # auxdir + parser.add_argument('--aux-dir', + dest='auxdir', + default=None, + nargs=1) + # libname + parser.add_argument('--lib', + dest='libname', + default=None, + nargs=1) + # sourcebase + parser.add_argument('-sb', '--source-base', + dest='sourcebase', + default=None, + nargs=1) + # m4base + parser.add_argument('-mb', '--m4-base', + dest='m4base', + default=None, + nargs=1) + # pobase + parser.add_argument('-pb', '--po-base', + dest='pobase', + default=None, + nargs=1) + # docbase + parser.add_argument('-db', '--doc-base', + dest='docbase', + default=None, + nargs=1) + # testsbase + parser.add_argument('-tb', '--tests-base', + dest='testsbase', + default=None, + nargs=1) + # lgpl + parser.add_argument('--lgpl', + dest='lgpl', + default=False, + type=int, + nargs='?') + # avoids + parser.add_argument('--avoid', + dest='avoids', + default=None, + nargs='+') + + # Parse the given arguments. + cmdargs = parser.parse_args() + + # Determine when user tries to combine modes. + args = [ + cmdargs.mode_list, + cmdargs.mode_import, + cmdargs.mode_add_import, + cmdargs.mode_remove_import, + cmdargs.mode_update, + cmdargs.mode_create_testdir, + cmdargs.mode_create_megatestdir, + cmdargs.mode_test, + cmdargs.mode_megatest, + cmdargs.mode_copy_file, + cmdargs.mode_xdescription, + cmdargs.mode_xcomment, + cmdargs.mode_xstatus, + cmdargs.mode_xnotice, + cmdargs.mode_xapplicability, + cmdargs.mode_xfilelist, + cmdargs.mode_xdependencies, + cmdargs.mode_xautoconf, + cmdargs.mode_xautomake, + cmdargs.mode_xinclude, + cmdargs.mode_xlink, + cmdargs.mode_xlicense, + cmdargs.mode_xmaintainer, + ] + overflow = [arg for arg in args if arg] + if len(overflow) > 1: + message = 'gnulib-tool: Unable to combine different modes of work.\n' + message += 'Try \'gnulib-tool --help\' for more information.\n' + sys.stderr.write(message) + sys.exit(1) + + # Determine selected mode. + if cmdargs.help != None: + print(info.usage()) + sys.exit(0) + if cmdargs.version != None: + message = '''gnulib-tool (%s %s)%s\n%s\n%s\n\nWritten by %s.''' % \ + (info.package(), info.date(), info.version(), info.copyright(), + info.license(), info.authors()) + print(message) + sys.exit(0) + if cmdargs.mode_list != None: + mode = 'list' + if cmdargs.mode_import != None: + mode = 'import' + modules = list(cmdargs.mode_import) + if cmdargs.mode_add_import != None: + mode = 'add-import' + modules = list(cmdargs.mode_add_import) + if cmdargs.mode_remove_import != None: + mode = 'remove-import' + modules = list(cmdargs.mode_remove_import) + if cmdargs.mode_update != None: + mode = 'update' + if cmdargs.mode_create_testdir != None: + mode = 'create-testdir' + modules = list(cmdargs.mode_create_testdir) + if cmdargs.mode_create_megatestdir != None: + mode = 'create-megatestdir' + modules = list(cmdargs.mode_create_megatestdir) + if cmdargs.mode_test != None: + mode = 'test' + modules = list(cmdargs.mode_test) + if cmdargs.mode_megatest != None: + mode = 'megatest' + modules = list(cmdargs.mode_megatest) + if cmdargs.mode_xdescription != None: + mode = 'extract-description' + modules = list(cmdargs.mode_xdescription) + if cmdargs.mode_xcomment != None: + mode = 'extract-comment' + modules = list(cmdargs.mode_xcomment) + if cmdargs.mode_xstatus != None: + mode = 'extract-status' + modules = list(cmdargs.mode_xstatus) + if cmdargs.mode_xnotice != None: + mode = 'extract-notice' + modules = list(cmdargs.mode_xnotice) + if cmdargs.mode_xapplicability != None: + mode = 'extract-applicability' + modules = list(cmdargs.mode_xapplicability) + if cmdargs.mode_xfilelist != None: + mode = 'extract-filelist' + modules = list(cmdargs.mode_xfilelist) + if cmdargs.mode_xautoconf != None: + mode = 'extract-autoconf-snippet' + modules = list(cmdargs.mode_xautoconf) + if cmdargs.mode_xautomake != None: + mode = 'extract-automake-snippet' + modules = list(cmdargs.mode_xautomake) + if cmdargs.mode_xdependencies != None: + mode = 'extract-dependencies' + modules = list(cmdargs.mode_xdependencies) + if cmdargs.mode_xinclude != None: + mode = 'extract-include-directive' + modules = list(cmdargs.mode_xinclude) + if cmdargs.mode_xlink != None: + mode = 'extract-link-directive' + modules = list(cmdargs.mode_xlink) + if cmdargs.mode_xlicense != None: + mode = 'extract-license' + modules = list(cmdargs.mode_xlicense) + if cmdargs.mode_xmaintainer != None: + mode = 'extract-maintainer' + modules = list(cmdargs.mode_xmaintainer) + if cmdargs.mode_copy_file != None: + mode = 'copy-file' + if len(cmdargs.mode_copy_file) > 2: + message = '%s: *** ' % constants.APP['name'] + message += 'invalid number of arguments for --%s' % mode + message += '\n%s: *** Exit.\n' % constants.APP['name'] + sys.stderr.write(message) + sys.exit(1) + files = list(cmdargs.mode_copy_file) + + # Determine specific settings. + destdir = cmdargs.destdir + if destdir != None: + destdir = cmdargs.destdir[0] + localdir = cmdargs.localdir + if localdir != None: + localdir = cmdargs.localdir[0] + libname = cmdargs.libname + if libname != None: + libname = cmdargs.libname[0] + auxdir = cmdargs.auxdir + if auxdir != None: + auxdir = cmdargs.auxdir[0] + sourcebase = cmdargs.sourcebase + if sourcebase != None: + sourcebase = cmdargs.sourcebase[0] + m4base = cmdargs.m4base + if m4base != None: + m4base = cmdargs.m4base[0] + pobase = cmdargs.pobase + if pobase != None: + pobase = cmdargs.pobase[0] + testsbase = cmdargs.testsbase + if testsbase != None: + testsbase = cmdargs.testsbase[0] + dryrun = cmdargs.dryrun + verbose = -cmdargs.quiet +cmdargs.verbose + inctests = cmdargs.inctests + flags = [cmdargs.inctests, cmdargs.obsolete, cmdargs.cxx, + cmdargs.longrunning, cmdargs.privileged, cmdargs.unportable, + cmdargs.alltests] + testflags = list() + for flag in flags: + index = flags.index(flag) + if flag != None: + if flag: + if index == 0: + testflags += [constants.TESTS['tests']] + elif index == 1: + testflags += [constants.TESTS['obsolete']] + elif index == 2: + testflags += [constants.TESTS['cxx-tests']] + elif index == 3: + testflags += [constants.TESTS['longrunning-tests']] + elif index == 4: + testflags += [constants.TESTS['privileged-tests']] + elif index == 5: + testflags += [constants.TESTS['unportable-tests']] + elif index == 6: + testflags += [constants.TESTS['all-tests']] + lgpl = cmdargs.lgpl + if lgpl == None: + lgpl = True + avoids = cmdargs.avoids + + # Create pygnulib configuration. + config = classes.GLConfig\ + ( + destdir=destdir, + localdir=localdir, + m4base=m4base, + auxdir=auxdir, + modules=modules, + avoids=avoids, + sourcebase=sourcebase, + pobase=pobase, + docbase=docbase, + testsbase=testsbase, + testflags=testflags, + libname=libname, + lgpl=lgpl, + makefile=makefile, + libtool=libtool, + conddeps=conddeps, + macro_prefix=macro_prefix, + podomain=podomain, + witness_c_macro=witness_c_macro, + vc_files=vc_files, + modcache=modcache, + verbose=verbose, + dryrun=dryrun, + ) + + # Canonicalize the inctests variable. + if inctests == None: + if mode in ['import', 'add-import', 'remove-import', 'update']: + config.disableTestFlag(TESTS['tests']) + elif mode in ['create-testdir', 'create-megatestdir', 'test', 'megatest']: + config.enableTestFlag(TESTS['tests']) + + # Work in the given mode. + if mode in ['list']: + modulesystem = classes.GLModuleSystem(config) + listing = modulesystem.list() + result = '\n'.join(listing) + os.rmdir(config['tempdir']) + print(result) + + elif mode in ['import', 'add-import', 'remove-import', 'update']: + mode = MODES[mode] + if not destdir: + destdir = '.' + config.setDestDir(destdir) + + if mode == MODES['import']: + # Set variables to default values. + if not sourcebase: + sourcebase = 'lib' + if not m4base: + m4base = 'm4' + if not docbase: + docbase = 'doc' + if not testsbase: + testsbase = 'tests' + if not macro_prefix: + macro_prefix = 'gl' + config.setSourceBase(sourcebase) + config.setM4Base(m4base) + config.setDocBase(docbase) + config.setTestsBase(testsbase) + config.setMacroPrefix(macro_prefix) + + # Perform GLImport actions. + importer = classes.GLImport(config, mode) + filetable, transformers = importer.prepare() + importer.execute(filetable, transformers) + + else: # if mode != MODE['--import'] + if m4base: + if not isfile(joinpath(destdir, m4base, 'gnulib-cache.m4')): + if not sourcebase: + sourcebase = 'lib' + if not docbase: + docbase = 'doc' + if not testsbase: + testsbase = 'tests' + if not macro_prefix: + macro_prefix = 'gl' + config.setSourceBase(sourcebase) + config.setM4Base(m4base) + config.setDocBase(docbase) + config.setTestsBase(testsbase) + config.setMacroPrefix(macro_prefix) + # Perform GLImport actions. + importer = classes.GLImport(config, mode) + filetable, transformers = importer.prepare() + importer.execute(filetable, transformers) + else: # if not m4base + m4dirs = list() + dirisnext = bool() + filepath = joinpath(destdir, 'Makefile.am') + if isfile(filepath): + with codecs.open(filepath, 'rb', 'UTF-8') as file: + data = file.read() + data = data.split('ACLOCAL_AMFLAGS')[1] + data = data[data.find('=')+1:data.find('\n')] + aclocal_amflags = data.split() + for aclocal_amflag in aclocal_amflags: + if dirisnext: + if not isabs(aclocal_amflag): + m4dirs += [aclocal_amflag] + else: # if not dirisnext + if aclocal_amflag == '-I': + dirisnext = True + else: # if aclocal_amflag != '-I' + dirisnext = False + else: # if not isfile(filepath) + filepath = joinpath(destdir, 'aclocal.m4') + if isfile(filepath): + pattern = constants.compiler(r'm4_include\(\[(.*?)\]\)') + with codecs.open(filepath, 'rb', 'UTF-8') as file: + m4dirs = pattern.findall(file.read()) + m4dirs = [os.path.dirname(m4dir) for m4dir in m4dirs] + m4dirs = \ + [ # Begin filtering + m4dir for m4dir in m4dirs \ + if isfile(joinpath(destdir, m4dir, 'gnulib-cache.m4')) + ] # Finish filtering + m4dirs = sorted(set(m4dirs)) + if len(m4dirs) == 0: + # First use of gnulib in a package. + # Any number of additional modules can be given. + if not sourcebase: + sourcebase = 'lib' + m4base = 'm4' + if not docbase: + docbase = 'doc' + if not testsbase: + testsbase = 'tests' + if not macro_prefix: + macro_prefix = 'gl' + config.setSourceBase(sourcebase) + config.setM4Base(m4base) + config.setDocBase(docbase) + config.setTestsBase(testsbase) + config.setMacroPrefix(macro_prefix) + # Perform GLImport actions. + importer = classes.GLImport(config, mode) + filetable, transformers = importer.prepare() + importer.execute(filetable, transformers) + elif len(m4dirs) == 1: + m4base = m4dirs[-1] + config.setM4Base(m4base) + # Perform GLImport actions. + importer = classes.GLImport(config, mode) + filetable, transformers = importer.prepare() + importer.execute(filetable, transformers) + else: # if len(m4dirs) > 1 + for m4base in m4dirs: + config.setM4Base(m4base) + # Perform GLImport actions. + importer = classes.GLImport(config, mode) + filetable, transformers = importer.prepare() + importer.execute(filetable, transformers) + + elif mode == 'create-testdir': + if not destdir: + message = '%s: *** ' % constants.APP['name'] + message += 'please specify --dir option' + message += '\n%s: *** Exit.\n' % constants.APP['name'] + sys.stderr.write(message) + sys.exit(1) + if not auxdir: + auxdir = 'build-aux' + config.setAuxDir(auxdir) + testdir = classes.GLTestDir(config, destdir) + testdir.execute() + + elif mode == 'create-megatestdir': + if not destdir: + message = '%s: *** ' % constants.APP['name'] + message += 'please specify --dir option' + message += '\n%s: *** Exit.\n' % constants.APP['name'] + sys.stderr.write(message) + sys.exit(1) + if not auxdir: + auxdir = 'build-aux' + config.setAuxDir(auxdir) + testdir = classes.GLMegaTestDir(config, destdir) + testdir.execute() + + elif mode == 'test': + if not destdir: + destdir = 'testdir %04d' % random.randrange(0, 9999) + if not auxdir: + auxdir = 'build-aux' + config.setAuxDir(auxdir) + testdir = classes.GLTestDir(config, destdir) + testdir.execute() + os.chdir(destdir) + os.mkdir('build') + os.chdir('build') + try: # Try to execute commands + sp.call(['../configure']) + sp.call([UTILS['make']]) + sp.call([UTILS['make'], 'check']) + sp.call([UTILS['make'], 'distclean']) + except Exception as error: + sys.exit(1) + args = ['find', '.', '-type', 'f', '-print'] + remaining = sp.check_output(args).decode(ENCS['shell']) + lines = [line.strip() for line in remaining.split('\n') if line.strip()] + remaining = ' '.join(lines) + if remaining: + message = 'Remaining files: %s\n' % remaining + message += 'gnulib-tool: *** Stop.\n' + sys.stderr.write(message) + sys.exit(1) + os.chdir('../..') + sp.call(['rm', '-rf', destdir], shell=False) + + elif mode == 'megatest': + if not destdir: + destdir = 'testdir %04d' % random.randrange(0, 9999) + if not auxdir: + auxdir = 'build-aux' + config.setAuxDir(auxdir) + testdir = classes.GLMegaTestDir(config, destdir) + testdir.execute() + os.chdir(destdir) + os.mkdir('build') + os.chdir('build') + sp.call(['../configure']) + sp.call([UTILS['make']]) + sp.call([UTILS['make'], 'check']) + sp.call([UTILS['make'], 'distclean']) + args = ['find', '.', '-type', 'f', '-print'] + remaining = sp.check_output(args).decode(ENCS['shell']) + lines = [line.strip() for line in remaining.split('\n') if line.strip()] + remaining = ' '.join(lines) + if remaining: + message = 'Remaining files: %s\n' % remaining + message += 'gnulib-tool: *** Stop.\n' + sys.stderr.write(message) + sys.exit(1) + os.chdir('../..') + sp.call(['rm', '-rf', destdir], shell=False) + + elif mode == 'extract-description': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getDescription()) + + elif mode == 'extract-comment': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getComment()) + + elif mode == 'extract-status': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + status = module.getStatus() + print('\n'.join(status)) + + elif mode == 'extract-notice': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getNotice()) + + elif mode == 'extract-applicability': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getApplicability()) + + elif mode == 'extract-filelist': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + files = module.getFiles() + print('\n'.join(files)) + + elif mode == 'extract-dependencies': + result = string() + if avoids: + message = '%s: *** ' % constants.APP['name'] + message += 'cannot combine --avoid and --extract-dependencies\n' + message += '%s: *** Exit.\n' % constants.APP['name'] + sys.stderr.write(message) + sys.exit(1) + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + dependencies = module.getDependencies() + if dependencies: + for depmodule, condition in dependencies: + if condition == None: + result += '%s\n' % str(depmodule) + else: # if condition != None + result += '%s\t%s' % (str(depmodule), condition) + print(result) + + elif mode == 'extract-autoconf-snippet': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getAutoconfSnippet()) + + elif mode == 'extract-automake-snippet': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getAutomakeSnippet()) + + elif mode == 'extract-include-directive': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getInclude()) + + elif mode == 'extract-link-directive': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getLink()) + + elif mode == 'extract-license': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getLicense()) + + elif mode == 'extract-maintainer': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + print(module.getMaintainer()) + + elif mode == 'extract-tests-module': + modulesystem = classes.GLModuleSystem(config) + modules = [modulesystem.find(module) for module in modules] + for module in modules: + if module.getTestsModule(): + print(module.getTestsName()) + + elif mode == 'copy-file': + srcpath = files[0] + if len(files) == 2: + dest = files[1] + else: # if len(files) != 2 + dest = '.' + if type(srcpath) is bytes: + srcpath = srcpath.decode(ENCS['default']) + if type(dest) is bytes: + dest = dest.decode(ENCS['default']) + if not auxdir: + auxdir = 'build-aux' + if not sourcebase: + sourcebase = 'lib' + if not m4base: + m4base = 'm4' + if not docbase: + docbase = 'doc' + if not testsbase: + testsbase = 'tests' + config.setAuxDir(auxdir) + config.setSourceBase(sourcebase) + config.setM4Base(m4base) + config.setDocBase(docbase) + config.setTestsBase(testsbase) + filesystem = classes.GLFileSystem(config) + lookedup, flag = filesystem.lookup(srcpath) + if isdir(dest): + destdir = string(dest) + if srcpath.startswith('build-aux/'): + destpath = constants.substart('build-aux/', '%s/' % auxdir, srcpath) + elif srcpath.startswith('doc/'): + destpath = constants.substart('doc/', '%s/' % docbase, srcpath) + elif srcpath.startswith('lib/'): + destpath = constants.substart('lib/', '%s/' % sourcebase, srcpath) + elif srcpath.startswith('m4/'): + destpath = constants.substart('m4/', '%s/' % m4base, srcpath) + elif srcpath.startswith('tests/'): + destpath = constants.substart('tests/', '%s/' % testsbase, srcpath) + elif srcpath.startswith('tests=lib/'): + destpath = constants.substart('tests=lib/', '%s/' % testsbase, srcpath) + elif srcpath.startswith('top/'): + destpath = constants.substart('top/', '', srcpath) + else: # either case + destpath = srcpath + else: # if not isdir(destpath) + destdir = os.path.dirname(destpath) + destpath = os.path.basename(destpath) + # Create the directory for destfile. + dirname = os.path.dirname(joinpath(destdir, destpath)) + if not config['dryrun']: + if dirname and not isdir(dirname): + try: # Try to create directories + os.makedirs(dirname) + except Exception as error: + pass + # Copy the file. + assistant = classes.GLFileAssistant(config) + tmpfile = assistant.tmpfilename(destpath) + shutil.copy(lookedup, tmpfile) + already_present = True + assistant.setOriginal(srcpath) + assistant.setRewritten(destpath) + if isfile(joinpath(destdir, destpath)): + # The file already exists. + assistant.update(lookedup, flag, tmpfile, already_present) + else: # if not isfile(joinpath(destdir, destpath)) + # Install the file. + # Don't protest if the file should be there but isn't: it happens + # frequently that developers don't put autogenerated files under version + # control. + assistant.add(lookedup, flag, tmpfile) + if isfile(tmpfile): + os.remove(tmpfile) + + else: + message = '%s: *** ' % constants.APP['name'] + message += 'no mode specified' + message += '\n%s: *** Exit.\n' % constants.APP['name'] + sys.stderr.write(message) + sys.exit(1) + +if __name__ == '__main__': + try: # Try to execute + main() + except classes.GLError as error: + errmode = 0 # gnulib-style errors + errno = error.errno + errinfo = error.errinfo + if errmode == 0: + message = '%s: *** ' % constants.APP['name'] + if errinfo == None: + errinfo = string() + if errno == 1: + message += 'file %s not found' % errinfo + elif errno == 2: + message += 'patch file %s didn\'t apply cleanly' % errinfo + elif errno == 3: + message += 'cannot find %s - make sure ' % errinfo + message += 'you run gnulib-tool from within your package\'s directory' + elif errno == 4: + message += 'minimum supported autoconf version is 2.59. Try adding' + message += 'AC_PREREQ([%s])' % constants.DEFAULT_AUTOCONF_MINVERSION + message += ' to your configure.ac.' + elif errno == 5: + "%s is expected to contain gl_M4_BASE([%s])" % \ + (repr(os.path.join(errinfo, 'gnulib-comp.m4')), repr(errinfo)) + elif errno == 6: + message += 'missing --source-base option' + elif errno == 7: + message += 'missing --doc-base option. --doc-base has been introduced ' + message += 'on 2006-07-11; if your last invocation of \'gnulib-tool ' + message += '--import\' is before that date, you need to run' + message += '\'gnulib-tool --import\' once, with a --doc-base option.' + elif errno == 8: + message += 'missing --tests-base option' + elif errno == 9: + message += 'missing --lib option' + elif errno == 10: + message = 'gnulib-tool: option --conditional-dependencies is not ' + message += 'supported with --with-tests\n' + elif errno == 11: + incompatibilities = string() + message += 'incompatible license on modules:%s' % constants.NL + for pair in errinfo: + incompatibilities += pair[0] + incompatibilities += ' %s' % pair[1] + incompatibilities += constants.NL + tempname = tempfile.mktemp() + with codecs.open(tempname, 'wb', 'UTF-8') as file: + file.write(incompatibilities) + sed_table = 's,^\\([^ ]*\\) ,\\1' +' ' *51 +',\n' + sed_table += 's,^\\(' +'.'*49 +'[^ ]*\) *,' +' '*17 +'\\1 ,' + args = ['sed', '-e', sed_table, tempname] + incompatibilities = sp.check_output(args).decode(ENCS['default']) + message += incompatibilities + os.remove(tempname) + elif errno == 12: + message += 'refusing to do nothing' + elif errno in [13, 14, 15, 16, 17]: + message += 'failed' + elif errno == 19: + message += 'could not create destination directory: %s' % errinfo + if errno != 10: + message += '\n%s: *** Exit.\n' % constants.APP['name'] + sys.stderr.write(message) + sys.exit(1) + diff --git a/pygnulib/GLConfig.py b/pygnulib/GLConfig.py new file mode 100644 index 000000000..6470ff53f --- /dev/null +++ b/pygnulib/GLConfig.py @@ -0,0 +1,1180 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import copy +import codecs +import tempfile +import subprocess as sp +from . import constants +from .GLError import GLError + + +#=============================================================================== +# 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 +FILES = constants.FILES +MODES = constants.MODES +TESTS = constants.TESTS +compiler = constants.compiler +joinpath = constants.joinpath +cleaner = constants.cleaner +relpath = constants.relativize +string = constants.string +isabs = os.path.isabs +isdir = os.path.isdir +isfile = os.path.isfile +normpath = os.path.normpath + + +#=============================================================================== +# Define GLConfig class +#=============================================================================== +class GLConfig(object): + '''This class is used to store intermediate settings for all pygnulib + classes. It contains all necessary attributes to setup any other class. + By default all attributes are set to empty string, empty list or zero. + The most common value, however, is a None value.''' + + def __init__(self, destdir=None, localdir=None, auxdir=None, + sourcebase=None, m4base=None, pobase=None, docbase=None, testsbase=None, + modules=None, avoids=None, files=None, testflags=None, libname=None, + lgpl=None, makefile=None, libtool=None, conddeps=None, macro_prefix=None, + podomain=None, witness_c_macro=None, vc_files=None, symbolic=None, + lsymbolic=None, modcache=None, configure_ac=None, ac_version=None, + libtests=None, single_configure=None, verbose=None, dryrun=None, + errors=None): + '''GLConfig.__init__(arguments) -> GLConfig + + Create new GLConfig instance.''' + self.table = dict() + self.table['tempdir'] = tempfile.mkdtemp() + # destdir + self.resetDestDir() + if destdir != None: + self.setDestDir(destdir) + # localdir + self.resetLocalDir() + if localdir != None: + self.setLocalDir(localdir) + # auxdir + self.resetAuxDir() + if auxdir != None: + self.setAuxDir(auxdir) + # sourcebase + self.resetSourceBase() + if sourcebase != None: + self.setSourceBase(sourcebase) + # m4base + self.resetM4Base() + if m4base != None: + self.setM4Base(m4base) + # pobase + self.resetPoBase() + if pobase != None: + self.setPoBase(pobase) + # docbase + self.resetDocBase() + if docbase != None: + self.setDocBase(docbase) + # testsbase + self.resetTestsBase() + if testsbase != None: + self.setTestsBase(testsbase) + # modules + self.resetModules() + if modules != None: + self.setModules(modules) + # avoids + self.resetAvoids() + if avoids != None: + self.setAvoids(avoids) + # files + self.resetFiles() + if files != None: + self.setFiles(files) + # testflags + self.resetTestFlags() + if testflags != None: + self.setTestFlags(testflags) + # libname + self.resetLibName() + if libname != None: + self.setLibName(libname) + # lgpl + self.resetLGPL() + if lgpl != None: + self.setLGPL(lgpl) + # makefile + self.resetMakefile() + if makefile != None: + self.setMakefile(makefile) + # libtool + self.resetLibtool() + if libtool != None: + if type(libtool) is bool: + if not libtool: + self.disableLibtool() + else: # if libtool + self.enableLibtool() + else: # if type(libtool) is not bool + raise(TypeError('libtool must be a bool, not %s' % \ + type(libtool).__name__)) + # conddeps + self.resetCondDeps() + if conddeps != None: + if type(conddeps) is bool: + if not conddeps: + self.disableCondDeps() + else: # if conddeps + self.enableCondDeps() + else: # if type(conddeps) is not bool + raise(TypeError('conddeps must be a bool, not %s' % \ + type(conddeps).__name__)) + # macro_prefix + self.resetMacroPrefix() + if macro_prefix != None: + self.setMacroPrefix(macro_prefix) + # podomain + self.resetPoDomain() + if podomain != None: + self.setPoDomain(podomain) + # witness_c_macro + self.resetWitnessCMacro() + if witness_c_macro != None: + if type(witness_c_macro) is bool: + if not witness_c_macro: + self.setWitnessCMacro() + else: # if witness_c_macro + self.resetWitnessCMacro() + else: # if type(witness_c_macro) is not bool + raise(TypeError('witness_c_macro must be a bool, not %s' % \ + type(witness_c_macro).__name__)) + # vc_files + self.resetVCFiles() + if vc_files != None: + if type(vc_files) is bool: + if not vc_files: + self.disableVCFiles() + else: # if vc_files + self.enableVCFiles() + else: # if type(vc_files) is not bool + raise(TypeError('vc_files must be a bool, not %s' % \ + type(vc_files).__name__)) + # symbolic + self.resetSymbolic() + if symbolic != None: + if type(symbolic) is bool: + if not symbolic: + self.disableSymbolic() + else: # if symbolic + self.enableSymbolic() + else: # if type(symbolic) is not bool + raise(TypeError('symbolic must be a bool, not %s' % \ + type(symbolic).__name__)) + # lsymbolic + self.resetLSymbolic() + if lsymbolic != None: + if type(lsymbolic) is bool: + if not lsymbolic: + self.disableLSymbolic() + else: # if lsymbolic + self.enableLSymbolic() + else: # if type(lsymbolic) is not bool + raise(TypeError('lsymbolic must be a bool, not %s' % \ + type(lsymbolic).__name__)) + # modcache + self.resetModuleCaching() + if modcache != None: + if type(modcache) is bool: + if not modcache: + self.disableModuleCaching() + else: # if modcache + self.enableModuleCaching() + else: # if type(modcache) is not bool + raise(TypeError('modcache must be a bool, not %s' % \ + type(modcache).__name__)) + # configure_ac + self.resetAutoconfFile() + if configure_ac != None: + self.setAutoconfFile(configure_ac) + # ac_version + self.resetAutoconfVersion() + if ac_version != None: + self.setAutoconfVersion(ac_version) + # libtests + self.resetLibtests() + if libtests != None: + if type(libtests) is bool: + if not libtests: + self.disableLibtests() + else: # if libtests + self.enableLibtests() + else: # if type(libtests) is not bool + raise(TypeError('libtests must be a bool, not %s' % \ + type(libtests).__name__)) + # single_configure + self.resetSingleConfigure() + if single_configure != None: + if type(single_configure) is bool: + if not single_configure: + self.disableSingleConfigure() + else: # if single_configure + self.enableSingleConfigure() + else: # if type(single_configure) is not bool + raise(TypeError('single_configure must be a bool, not %s' % \ + type(single_configure).__name__)) + # verbose + self.resetVerbosity() + if verbose != None: + self.setVerbosity(verbose) + # dryrun + self.resetDryRun() + if dryrun != None: + if type(dryrun) is bool: + if not dryrun: + self.disableDryRun() + else: # if dryrun + self.enableDryRun() + else: # if type(dryrun) is not bool + raise(TypeError('dryrun must be a bool, not %s' % \ + type(dryrun).__name__)) + # errors + self.resetErrors() + if errors != None: + if type(errors) is bool: + if not errors: + self.disableErrors() + else: # if errors + self.enableErrors() + else: # if type(errors) is not bool + raise(TypeError('errors must be a bool, not %s' % \ + type(errors).__name__)) + + # Define special methods. + def __repr__(self): + '''x.__repr__() <==> repr(x)''' + return('') + + def __getitem__(self, y): + '''x.__getitem__(y) <==> x[y]''' + if y in self.table: + result = self.table[y] + if type(y) is list: + result = list(self.table[y]) + return(self.table[y]) + else: # if y not in self.table + raise(KeyError('GLConfig does not contain key: %s' % repr(y))) + + def dictionary(self): + '''Return the configuration as a dict object.''' + return(dict(self.table)) + + def copy(self): + '''Return the copy of the configuration.''' + table = copy.deepcopy(self) + return(table) + + def update(self, dictionary): + '''Specify the dictionary whose keys will be used to update config.''' + if type(dictionary) is not GLConfig: + raise(TypeError('dictionary must be a GLConfig, not %s' % \ + type(dictionary).__name__)) + dictionary = dict(dictionary.table) + result = dict() + for key in dictionary: + src = self.table[key] + dest = dictionary[key] + result[key] = src + if src != dest: + if self.isdefault(key, src): + result[key] = dest + else: # if not self.isdefault(key, src) + result[key] == src + if not self.isdefault(key, dest): + if key in ['modules', 'avoids', 'tests']: + dest = sorted(set(src +dest)) + result[key] = dest + self.table = dict(result) + + def update_key(self, dictionary, key): + '''Update the given key using value from the given dictionary.''' + if key in self.table: + if type(dictionary) is not GLConfig: + raise(TypeError('dictionary must be a GLConfig, not %s' % \ + type(dictionary).__name__)) + dictionary = dict(dictionary.table) + self.table[key] = dictionary[key] + else: # if key not in self.table + raise(KeyError('GLConfig does not contain key: %s' % repr(key))) + + def default(self, key): + '''Return default value for the given key.''' + if key in self.table: + if key == 'libname': + return(string('libgnu')) + elif key == 'macro_prefix': + return(string('gl')) + elif key == 'include_guard_prefix': + return(string('GL')) + elif key == 'ac_version': + return(2.59) + elif key == 'verbosity': + return(0) + elif key == 'copyrights': + return(True) + elif key in ['modules', 'avoids', 'tests', 'testflags']: + return(list()) + elif key in ['libtool', 'lgpl', 'conddeps', 'modcache', 'symbolic', + 'lsymbolic', 'libtests', 'dryrun']: + return(False) + if key == 'vc_files': + return(None) + elif key == 'errors': + return(True) + else: # otherwise + return(string()) + else: # if key not in self.table + raise(KeyError('GLConfig does not contain key: %s' % repr(key))) + + def isdefault(self, key, value): + '''Check whether the value for the given key is a default value.''' + if key in self.table: + default = self.default(key) + return(value == default) + else: # if key not in self.table + raise(KeyError('GLConfig does not contain key: %s' % repr(key))) + + def keys(self): + '''Return list of keys.''' + return(list(self.table.keys())) + + def values(self): + '''Return list of values.''' + return(list(self.table.values())) + + + # Define destdir methods. + def getDestDir(self): + '''Return the target directory. For --import, this specifies where your + configure.ac can be found. Defaults to current directory.''' + return(self.table['destdir']) + + def setDestDir(self, destdir): + '''Specify the target directory. For --import, this specifies where your + configure.ac can be found. Defaults to current directory.''' + if type(destdir) is bytes or type(destdir) is string: + if type(destdir) is bytes: + destdir = string(destdir, ENCS['system']) + if destdir: + self.table['destdir'] = os.path.normpath(destdir) + else: # if destdir has not bytes/string type + raise(TypeError('destdir must be a string, not %s' % \ + type(destdir).__name__)) + + def resetDestDir(self): + '''Reset the target directory. For --import, this specifies where your + configure.ac can be found. Defaults to current directory.''' + self.table['destdir'] = string() + + + # Define localdir methods. + def getLocalDir(self): + '''Return a local override directory where to look up files before looking + in gnulib's directory.''' + return(self.table['localdir']) + + def setLocalDir(self, localdir): + '''Specify a local override directory where to look up files before looking + in gnulib's directory.''' + if type(localdir) is bytes or type(localdir) is string: + if type(localdir) is bytes: + localdir = string(localdir, ENCS['system']) + if localdir: + self.table['localdir'] = localdir + else: # if localdir has not bytes/string type + raise(TypeError('localdir must be a string, not %s' % \ + type(localdir).__name__)) + + def resetLocalDir(self): + '''Reset a local override directory where to look up files before looking + in gnulib's directory.''' + self.table['localdir'] = string() + + + # Define auxdir methods. + def getAuxDir(self): + '''Return directory relative to --dir where auxiliary build tools are + placed. Default comes from configure.ac or configure.in.''' + return(self.table['auxdir']) + + def setAuxDir(self, auxdir): + '''Specify directory relative to --dir where auxiliary build tools are + placed. Default comes from configure.ac or configure.in.''' + if type(auxdir) is bytes or type(auxdir) is string: + if type(auxdir) is bytes: + auxdir = string(auxdir, ENCS['system']) + if auxdir: + self.table['auxdir'] = auxdir + else: # if type of auxdir is not bytes or string + raise(TypeError('auxdir must be a string, not %s' % \ + type(auxdir).__name__)) + + def resetAuxDir(self): + '''Reset directory relative to --dir where auxiliary build tools are + placed. Default comes from configure.ac or configure.in.''' + self.table['auxdir'] = string() + + + # Define sourcebase methods. + def getSourceBase(self): + '''Return directory relative to destdir where source code is placed.''' + return(self.table['sourcebase']) + + def setSourceBase(self, sourcebase): + '''Specify directory relative to destdir where source code is placed.''' + if type(sourcebase) is bytes or type(sourcebase) is string: + if type(sourcebase) is bytes: + sourcebase = string(sourcebase, ENCS['system']) + if sourcebase: + self.table['sourcebase'] = sourcebase + else: # if type of sourcebase is not bytes or string + raise(TypeError('sourcebase must be a string, not %s' % \ + type(sourcebase).__name__)) + + def resetSourceBase(self): + '''Return directory relative to destdir where source code is placed.''' + self.table['sourcebase'] = string() + + + # Define m4base methods. + def getM4Base(self): + '''Return directory relative to destdir where *.m4 macros are placed.''' + return(self.table['m4base']) + + def setM4Base(self, m4base): + '''Specify directory relative to destdir where *.m4 macros are placed.''' + if type(m4base) is bytes or type(m4base) is string: + if type(m4base) is bytes: + m4base = string(m4base, ENCS['system']) + if m4base: + self.table['m4base'] = m4base + else: # if type of m4base is not bytes or string + raise(TypeError('m4base must be a string, not %s' % \ + type(m4base).__name__)) + + def resetM4Base(self): + '''Reset directory relative to destdir where *.m4 macros are placed.''' + self.table['m4base'] = string() + + + # Define pobase methods. + def getPoBase(self): + '''Return directory relative to destdir where *.po files are placed.''' + return(self.table['pobase']) + + def setPoBase(self, pobase): + '''Specify directory relative to destdir where *.po files are placed.''' + if type(pobase) is bytes or type(pobase) is string: + if type(pobase) is bytes: + pobase = string(pobase, ENCS['system']) + if pobase: + self.table['pobase'] = pobase + else: # if type of pobase is not bytes or string + raise(TypeError('pobase must be a string, not %s' % \ + type(pobase).__name__)) + + def resetPoBase(self): + '''Reset directory relative to destdir where *.po files are placed.''' + self.table['pobase'] = string() + + + # Define docbase methods. + def getDocBase(self): + '''Return directory relative to destdir where doc files are placed. + Default value for this variable is 'doc').''' + return(self.table['docbase']) + + def setDocBase(self, docbase): + '''Specify directory relative to destdir where doc files are placed. + Default value for this variable is 'doc').''' + if type(docbase) is bytes or type(docbase) is string: + if type(docbase) is bytes: + docbase = string(docbase, ENCS['system']) + if docbase: + self.table['docbase'] = docbase + else: # if type of docbase is not bytes or string + raise(TypeError('docbase must be a string, not %s' % \ + type(docbase).__name__)) + + def resetDocBase(self): + '''Reset directory relative to destdir where doc files are placed. + Default value for this variable is 'doc').''' + self.table['docbase'] = string() + + + # Define testsbase methods. + def getTestsBase(self): + '''Return directory relative to destdir where unit tests are placed. + Default value for this variable is 'tests').''' + return(self.table['testsbase']) + + def setTestsBase(self, testsbase): + '''Specify directory relative to destdir where unit tests are placed. + Default value for this variable is 'tests').''' + if type(testsbase) is bytes or type(testsbase) is string: + if type(testsbase) is bytes: + testsbase = string(testsbase, ENCS['system']) + if testsbase: + self.table['testsbase'] = testsbase + else: # if type of testsbase is not bytes or string + raise(TypeError('testsbase must be a string, not %s' % \ + type(testsbase).__name__)) + + def resetTestsBase(self): + '''Reset directory relative to destdir where unit tests are placed. + Default value for this variable is 'tests').''' + self.table['testsbase'] = string() + + + # Define modules methods. + def addModule(self, module): + '''Add the module to the modules list.''' + if type(module) is bytes or type(module) is string: + if type(module) is bytes: + module = module.decode(ENCS['default']) + if module not in self.table['modules']: + self.table['modules'] += [module] + else: # if module has not bytes or string type + raise(TypeError('module must be a string, not %s' % \ + type(module).__name__)) + + def removeModule(self, module): + '''Remove the module from the modules list.''' + if type(module) is bytes or type(module) is string: + if type(module) is bytes: + module = module.decode(ENCS['default']) + if module in self.table['modules']: + self.table['modules'].remove(module) + else: # if module has not bytes or string type + raise(TypeError('module must be a string, not %s' % \ + type(module).__name__)) + + def getModules(self): + '''Return the modules list.''' + return(list(self.table['modules'])) + + def setModules(self, modules): + '''Set the modules list.''' + if type(modules) is list or type(modules) is tuple: + old_modules = self.table['modules'] + self.table['modules'] = list() + for module in modules: + try: # Try to add each module + self.addModule(module) + except TypeError as error: + self.table['modules'] = old_modules + raise(TypeError('each module must be a string')) + except GLError as error: + self.table['modules'] = old_modules + raise(GLError(error.errno, error.errinfo)) + else: # if type of modules is not list or tuple + raise(TypeError('modules must be a list or a tuple, not %s' % \ + type(modules).__name__)) + + def resetModules(self): + '''Reset the list of the modules.''' + self.table['modules'] = list() + + + # Define avoids methods. + def addAvoid(self, module): + '''Avoid including the given module. Useful if you have code that provides + equivalent functionality.''' + if type(module) is bytes or type(module) is string: + if type(module) is bytes: + module = module.decode(ENCS['default']) + if module not in self.table['avoids']: + self.table['avoids'].append(module) + else: # if module has not bytes or string type + raise(TypeError('avoid must be a string, not %s' % \ + type(module).__name__)) + + def removeAvoid(self, module): + '''Remove the given module from the list of avoided modules.''' + if type(module) is bytes or type(module) is string: + if type(module) is bytes: + module = module.decode(ENCS['default']) + if module in self.table['avoids']: + self.table['avoids'].remove(module) + else: # if module has not bytes or string type + raise(TypeError('avoid must be a string, not %s' % \ + type(module).__name__)) + + def getAvoids(self): + '''Return the list of the avoided modules.''' + return(list(self.table['avoids'])) + + def setAvoids(self, modules): + '''Specify the modules which will be avoided.''' + if type(modules) is list or type(modules) is tuple: + old_avoids = self.table['avoids'] + self.table['avoids'] = list() + for module in modules: + try: # Try to add each module + self.addAvoid(module) + except TypeError as error: + self.table['avoids'] = old_avoids + raise(TypeError('each module must be a string')) + except GLError as error: + self.table['avoids'] = old_avoids + raise(GLError(error.errno, error.errinfo)) + else: # if type of modules is not list or tuple + raise(TypeError('modules must be a list or a tuple, not %s' % \ + type(modules).__name__)) + + def resetAvoids(self): + '''Reset the list of the avoided modules.''' + self.table['avoids'] = list() + + + # Define files methods. + def addFile(self, file): + '''Add file to the list of files.''' + if type(file) is bytes or type(file) is string: + if type(file) is bytes: + file = file.decode(ENCS['default']) + if file not in self.table['files']: + self.table['files'].append(file) + else: # if file has not bytes or string type + raise(TypeError('file must be a string, not %s' % \ + type(file).__name__)) + + def removeFile(self, file): + '''Remove the given file from the list of files.''' + if type(file) is bytes or type(file) is string: + if type(file) is bytes: + file = file.decode(ENCS['default']) + if file in self.table['files']: + self.table['files'].remove(file) + else: # if file has not bytes or string type + raise(TypeError('file must be a string, not %s' % \ + type(file).__name__)) + + def getFiles(self): + '''Return the list of the fileed files.''' + return(list(self.table['files'])) + + def setFiles(self, files): + '''Specify the list of files.''' + if type(files) is list or type(files) is tuple: + old_files = self.table['files'] + self.table['files'] = list() + for file in files: + try: # Try to add each file + self.addFile(file) + except TypeError as error: + self.table['files'] = old_files + raise(TypeError('each file must be a string')) + except GLError as error: + self.table['files'] = old_files + raise(GLError(error.errno, error.errinfo)) + else: # if type of files is not list or tuple + raise(TypeError('files must be a list or a tuple, not %s' % \ + type(files).__name__)) + + def resetFiles(self): + '''Reset the list of files.''' + self.table['files'] = list() + + + # Define tests/testflags methods + def checkTestFlag(self, flag): + '''Return the status of the test flag.''' + if flag in TESTS.values(): + return(flag in self.table['testflags']) + else: # if flag is not in TESTS + raise(TypeError('unknown flag: %s' % repr(flag))) + + def enableTestFlag(self, flag): + '''Enable test flag. You can get flags from TESTS variable.''' + if flag in TESTS.values(): + if flag not in self.table['testflags']: + self.table['testflags'].append(flag) + else: # if flag is not in TESTS + raise(TypeError('unknown flag: %s' % repr(flag))) + + def disableTestFlag(self, flag): + '''Disable test flag. You can get flags from TESTS variable.''' + if flag in TESTS.values(): + if flag in self.table['testflags']: + self.table['testflags'].remove(flag) + else: # if flag is not in TESTS + raise(TypeError('unknown flag: %s' % repr(flag))) + + def getTestFlags(self): + '''Return test flags. You can get flags from TESTS variable.''' + return(list(self.table['testflags'])) + + def setTestFlags(self, testflags): + '''Specify test flags. You can get flags from TESTS variable.''' + if type(testflags) is list or type(testflags) is tuple: + old_testflags = self.table['testflags'] + self.table['testflags'] = list() + for flag in testflags: + try: # Try to enable each flag + self.enableTestFlag(flag) + except TypeError as error: + raise(TypeError('each flag must be one of TESTS integers')) + self.table['testflags'] = testflags + else: # if type of testflags is not list or tuple + raise(TypeError('testflags must be a list or a tuple, not %s' % \ + type(testflags).__name__)) + + def resetTestFlags(self): + '''Reset test flags (only default flag will be enabled).''' + self.table['testflags'] = list() + self.table['tests'] = self.table['testflags'] + + + # Define libname methods. + def getLibName(self): + '''Return the library name.''' + return(self.table['libname']) + + def setLibName(self, libname): + '''Specify the library name.''' + if type(libname) is bytes or type(libname) is string: + if type(libname) is bytes: + libname = string(libname, ENCS['system']) + if libname: + self.table['libname'] = libname + else: # if type of libname is not bytes or string + raise(TypeError('libname must be a string, not %s' % \ + type(module).__name__)) + + def resetLibName(self): + '''Reset the library name to 'libgnu'.''' + self.table['libname'] = string('libgnu') + + + # Define libtool methods. + def checkLibtool(self): + '''Check if user enabled libtool rules.''' + return(self.table['libtool']) + + def enableLibtool(self): + '''Enable libtool rules.''' + self.table['libtool'] = True + + def disableLibtool(self): + '''Disable libtool rules.''' + self.table['libtool'] = False + + def resetLibtool(self): + '''Reset libtool rules.''' + self.table['libtool'] = False + + + # Define conddeps methods. + def checkCondDeps(self): + '''Check if user enabled cond. dependencies.''' + return(self.table['conddeps']) + + def enableCondDeps(self): + '''Enable cond. dependencies (may save configure time and object code).''' + self.table['conddeps'] = True + + def disableCondDeps(self): + '''Disable cond. dependencies (may save configure time and object code).''' + self.table['conddeps'] = False + + def resetCondDeps(self): + '''Reset cond. dependencies (may save configure time and object code).''' + self.table['conddeps'] = False + + + # Define lgpl methods. + def getLGPL(self): + '''Check for abort if modules aren't available under the LGPL. + Default value is False, which means that lgpl is disabled.''' + return(self.table['lgpl']) + + def setLGPL(self, lgpl): + '''Abort if modules aren't available under the LGPL. + Default value is False, which means that lgpl is disabled.''' + if (type(lgpl) is int and 2 <= lgpl <= 3) or type(lgpl) is bool: + self.table['lgpl'] = lgpl + else: # if lgpl is not False, 2 or 3 + raise(TypeError('invalid LGPL version: %s' % repr(lgpl))) + + def resetLGPL(self): + '''Disable abort if modules aren't available under the LGPL. + Default value is False, which means that lgpl is disabled.''' + self.table['lgpl'] = False + + + # Define macro_prefix methods. + def getIncludeGuardPrefix(self): + '''Return include_guard_prefix to use inside GLEmiter class.''' + return(self.table['include_guard_prefix']) + + def getMacroPrefix(self): + '''Return the prefix of the macros 'gl_EARLY' and 'gl_INIT'. + Default macro_prefix is 'gl'.''' + return(self.table['macro_prefix']) + + def setMacroPrefix(self, macro_prefix): + '''Specify the prefix of the macros 'gl_EARLY' and 'gl_INIT'. + Default macro_prefix is 'gl'.''' + if type(macro_prefix) is bytes or type(macro_prefix) is string: + if type(macro_prefix) is bytes: + macro_prefix = string(macro_prefix, ENCS['system']) + if macro_prefix: + self.table['macro_prefix'] = macro_prefix + else: # if type of macro_prefix is not bytes or string + raise(TypeError('macro_prefix must be a string, not %s' % \ + type(macro_prefix).__name__)) + if macro_prefix == 'gl': + include_guard_prefix = 'GL' + else: # macro_prefix != 'gl' + include_guard_prefix = 'GL_%s' % macro_prefix.upper() + if type(include_guard_prefix) is bytes: + include_guard_prefix = include_guard_prefix.decode(ENCS['default']) + self.table['include_guard_prefix'] = include_guard_prefix + + def resetMacroPrefix(self): + '''Reset the prefix of the macros 'gl_EARLY' and 'gl_INIT'. + Default macro_prefix is 'gl'.''' + self.table['macro_prefix'] = string('gl') + include_guard_prefix = string('GL') + if type(include_guard_prefix) is bytes: + include_guard_prefix = include_guard_prefix.decode(ENCS['default']) + self.table['include_guard_prefix'] = include_guard_prefix + + + # Define makefile methods. + def getMakefile(self): + '''Return the name of makefile in automake syntax in the source-base and + tests-base directories. Default is 'Makefile.am'.''' + return(self.table['makefile']) + + def setMakefile(self, makefile): + '''Specify the name of makefile in automake syntax in the source-base and + tests-base directories. Default is 'Makefile.am'.''' + if type(makefile) is bytes or type(makefile) is string: + if type(makefile) is bytes: + makefile = string(makefile, ENCS['system']) + if makefile: + self.table['makefile'] = makefile + else: # if type of makefile is not bytes or string + raise(TypeError('makefile must be a string, not %s' % + type(makefile).__name__)) + + def resetMakefile(self): + '''Reset the name of makefile in automake syntax in the source-base and + tests-base directories. Default is 'Makefile.am'.''' + self.table['makefile'] = string() + + + # Define podomain methods. + def getPoDomain(self): + '''Return the prefix of the i18n domain. Usually use the package name. + A suffix '-gnulib' is appended.''' + return(self.table['podomain']) + + def setPoDomain(self, podomain): + '''Specify the prefix of the i18n domain. Usually use the package name. + A suffix '-gnulib' is appended.''' + if type(podomain) is bytes or type(podomain) is string: + if type(podomain) is bytes: + podomain = string(podomain, ENCS['system']) + if podomain: + self.table['podomain'] = podomain + else: # if type of podomain is not bytes or string + raise(TypeError('podomain must be a string, not %s' % \ + type(podomain).__name__)) + + def resetPoDomain(self): + '''Reset the prefix of the i18n domain. Usually use the package name. + A suffix '-gnulib' is appended.''' + self.table['podomain'] = string() + + + # Define witness_c_macro methods. + def getWitnessCMacro(self): + '''Return the C macro that is defined when the sources in this directory + are compiled or used.''' + return(self.table['witness_c_macro']) + + def setWitnessCMacro(self, witness_c_macro): + '''Specify the C macro that is defined when the sources in this directory + are compiled or used.''' + if type(witness_c_macro) is bytes or type(witness_c_macro) is string: + if type(witness_c_macro) is bytes: + witness_c_macro = string(witness_c_macro, ENCS['system']) + if witness_c_macro: + self.table['witness_c_macro'] = witness_c_macro + else: # if type of witness_c_macro is not bytes or string + raise(TypeError('witness_c_macro must be a string, not %s' % + type(witness_c_macro).__name__)) + + def resetWitnessCMacro(self): + '''Return the C macro that is defined when the sources in this directory + are compiled or used.''' + self.table['witness_c_macro'] = string() + + + # Define vc_files methods. + def checkVCFiles(self): + '''Check if update of the version control files is enabled or disabled.''' + return(self.table['vc_files']) + + def enableVCFiles(self): + '''Enable update of the version control files.''' + self.table['vc_files'] = True + + def disableVCFiles(self): + '''Disable update of the version control files.''' + self.table['vc_files'] = False + + def resetVCFiles(self): + '''Reset update of the version control files and set it to None.''' + self.table['vc_files'] = None + + + # Define modcache methods. + def checkModuleCaching(self): + '''Get status of module caching optimization.''' + return(self.table['modcache']) + + def enableModuleCaching(self): + '''Enable module caching optimization.''' + self.table['modcache'] = True + + def disableModuleCaching(self): + '''Disable module caching optimization.''' + self.table['modcache'] = False + + def resetModuleCaching(self): + '''Reset module caching optimization.''' + self.table['modcache'] = False + + + # Define configure_ac methods. + def getAutoconfFile(self): + '''Return path of autoconf file relative to destdir.''' + return(self.table['configure_ac']) + + def setAutoconfFile(self, configure_ac): + '''Specify path of autoconf file relative to destdir.''' + if type(configure_ac) is bytes or type(configure_ac) is string: + if type(configure_ac) is bytes: + configure_ac = string(configure_ac, ENCS['system']) + if configure_ac: + self.table['configure_ac'] = \ + relpath(self.table['destdir'],configure_ac) + else: # if type of configure_ac is not bytes or string + raise(TypeError('configure_ac must be a string, not %s' % \ + type(configure_ac).__name__)) + + def resetAutoconfFile(self): + '''Reset path of autoconf file relative to destdir.''' + configure_ac = string() + if isfile(joinpath(self.table['destdir'], 'configure.ac')): + configure_ac = joinpath(self.table['destdir'], 'configure.ac') + elif isfile(joinpath(self.table['destdir'], 'configure.in')): + configure_ac = joinpath(self.table['destdir'], 'configure.in') + self.table['configure_ac'] = configure_ac + + + # Define ac_version methods. + def getAutoconfVersion(self): + '''Return preferred autoconf version. Default value is 2.59.''' + return(self.table['ac_version']) + + def setAutoconfVersion(self, ac_version): + '''Specify preferred autoconf version. Default value is 2.59.''' + if type(ac_version) is float or type(ac_version) is int: + self.table['ac_version'] = float(ac_version) + else: # if ac_version has not int or float type + raise(TypeError('ac_version must be an int or a float, not %s' % \ + type(ac_version).__name__)) + + def resetAutoconfVersion(self): + '''Specify preferred autoconf version. Default value is 2.59.''' + self.table['ac_version'] = 2.59 + + + # Define symbolic methods. + def checkCopyrights(self): + '''Check if copyright notices in files should be replaced.''' + return(self.table['copyrights']) + + def checkSymbolic(self): + '''Check if pygnulib will make symbolic links instead of copying files.''' + return(self.table['symbolic']) + + def enableSymbolic(self): + '''Enable creation of the symbolic links instead of copying files.''' + self.table['symbolic'] = True + self.table['copyrights'] = False + + def disableSymbolic(self): + '''Enable creation of the symbolic links instead of copying files.''' + self.table['symbolic'] = False + self.table['copyrights'] = True + + def resetSymbolic(self): + '''Reset creation of the symbolic links instead of copying files.''' + self.table['symbolic'] = False + self.table['copyrights'] = True + + + # Define lsymbolic methods. + def checkLSymbolic(self): + '''Check if pygnulib will make symbolic links instead of copying files, only + for files from the local override directory.''' + return(self.table['lsymbolic']) + + def enableLSymbolic(self): + '''Enable creation of symbolic links instead of copying files, only for + files from the local override directory.''' + self.table['lsymbolic'] = True + + def disableLSymbolic(self): + '''Disable creation of symbolic links instead of copying files, only for + files from the local override directory.''' + self.table['lsymbolic'] = False + + def resetLSymbolic(self): + '''Reset creation of symbolic links instead of copying files, only for + files from the local override directory.''' + self.table['lsymbolic'] = False + + + # Define verbosity methods. + def getVerbosity(self): + '''Get verbosity level.''' + return(self.table['verbosity']) + + def decreaseVerbosity(self): + '''Decrease verbosity level.''' + if self.table['verbosity'] > MODES['verbose-min']: + self.table['verbosity'] -= 1 + + def increaseVerbosity(self): + '''Increase verbosity level.''' + if self.table['verbosity'] < MODES['verbose-max']: + self.table['verbosity'] += 1 + + def setVerbosity(self, verbose): + '''Set verbosity level to verbose, where -2 <= verbose <= 2. + If verbosity level is less than -2, verbosity level will be set to -2. + If verbosity level is greater than 2, verbosity level will be set to 2.''' + if type(verbose) is int: + if MODES['verbose-min'] <= verbose <= MODES['verbose-max']: + self.table['verbosity'] = verbose + elif verbose < MODES['verbose-min']: + self.table['verbosity'] = MODES['verbose-min'] + elif verbose > MODES['verbose-max']: + self.table['verbosity'] = MODES['verbose-max'] + else: # if type(verbose) is not int + raise(TypeError('verbosity must be an int, not %s' % \ + type(verbose).__name__)) + + def resetVerbosity(self): + '''Reset verbosity level.''' + self.table['verbosity'] = 0 + + + # Define libtests methods. + def checkLibtests(self): + '''Return True if a testsbase/libtests.a is needed.''' + return(self.table['libtests']) + + def enableLibtests(self): + '''If libtests is enabled, then testsbase/libtests.a is needed.''' + self.table['libtests'] = True + + def disableLibtests(self): + '''If libtests is disabled, then testsbase/libtests.a is not needed.''' + self.table['libtests'] = False + + def resetLibtests(self): + '''Reset status of testsbase/libtests.a.''' + self.table['libtests'] = False + + + # Define single_configure methods. + def checkSingleConfigure(self): + '''Check whether single configure file should be generated.''' + return(self.table['single_configure']) + + def enableSingleConfigure(self): + '''Enable generation of the single configure file.''' + self.table['single_configure'] = True + + def disableSingleConfigure(self): + '''Disable generation of the single configure file.''' + self.table['single_configure'] = False + + def resetSingleConfigure(self): + '''Reset status of the single configure file generation.''' + self.table['single_configure'] = False + + + # Define dryrun methods. + def checkDryRun(self): + '''Check whether dryrun is enabled.''' + return(self.table['dryrun']) + + def enableDryRun(self): + '''Enable dryrun mode.''' + self.table['dryrun'] = True + + def disableDryRun(self): + '''Disable dryrun mode.''' + self.table['dryrun'] = False + + def resetDryRun(self): + '''Reset status of dryrun mode.''' + self.table['dryrun'] = False + + + # Define errors methods. + def checkErrors(self): + '''Check if GLError will be raised in non-critical situations.''' + return(self.table['errors']) + + def enableErrors(self): + '''Raise GLError in non-critical situations.''' + self.table['errors'] = True + + def disableErrors(self): + '''Do not raise GLError in non-critical situations.''' + self.table['errors'] = False + + def resetErrors(self): + '''Reset status of raising GLError in non-critical situations.''' + self.table['errors'] = False + diff --git a/pygnulib/GLConfig.pyc b/pygnulib/GLConfig.pyc new file mode 100644 index 000000000..1704b57a1 Binary files /dev/null and b/pygnulib/GLConfig.pyc differ diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py new file mode 100644 index 000000000..3a6ee0098 --- /dev/null +++ b/pygnulib/GLEmiter.py @@ -0,0 +1,1138 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import codecs +import shutil +import subprocess as sp +from . import constants +from .GLInfo import GLInfo +from .GLError import GLError +from .GLConfig import GLConfig +from .GLModuleSystem import GLModule +from .GLModuleSystem import GLModuleTable +from .GLMakefileTable import GLMakefileTable +from .GLFileSystem import GLFileAssistant +from pprint import pprint + + +#=============================================================================== +# 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 +FILES = constants.FILES +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 + + +#=============================================================================== +# Define GLEmiter class +#=============================================================================== +class GLEmiter(object): + '''This class is used to emit the contents of necessary files.''' + + def __init__(self, config): + '''GLEmiter.__init__(config) -> GLEmiter + + Create GLEmiter instance.''' + self.info = GLInfo() + if type(config) is not GLConfig: + raise(TypeError('config must be a GLConfig, not %s' % \ + type(config).__name__)) + self.config = config + + def __repr__(self): + '''x.__repr__() <==> repr(x)''' + result = '' % hex(id(self)) + return(result) + + def copyright_notice(self): + '''GLEmiter.copyright_notice() -> string + + Emit a header for a generated file.''' + emit = string() + emit += "# %s" % self.info.copyright() + emit += """ +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file. If not, see . +# +# As a special exception to the GNU General Public License, +# this file may be distributed as part of a program that +# contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# Generated by gnulib-tool.\n""" + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def autoconfSnippet(self, module, fileassistant, toplevel, + disable_libtool, disable_gettext, replace_auxdir, indentation): + '''GLEmiter.autoconfSnippet(module, toplevel, + disable_libtool, disable_gettext, replace_auxdir, + indentation) -> string + + Emit the autoconf snippet of a module. + GLConfig: include_guard_prefix. + + module is a GLModule instance, which is processed. + fileassistant is a GLFileAssistant instance, which is used to get temporary + directories and sed transformer. + toplevel is a bool variable, False means a subordinate use of pygnulib. + disable_libtool is a bool variable; it tells whether to disable libtool + handling even if it has been specified through the GLConfig class. + disable_gettext is a bool variable; it tells whether to disable + AM_GNU_GETTEXT invocations. + replace_auxdir is a bool variable; it tells whether to replace + 'build-aux' directory in AC_CONFIG_FILES. + indentation is a string which contain spaces to prepend on each line.''' + emit = string() + if type(module) is not GLModule: + raise(TypeError('module must be a GLModule, not %s' % \ + type(module).__name__)) + if type(fileassistant) is not GLFileAssistant: + raise(TypeError('fileassistant must be a GLFileAssistant, not %s' % \ + type(fileassistant).__name__)) + if type(toplevel) is not bool: + raise(TypeError('toplevel must be a bool, not %s' % \ + type(toplevel).__name__)) + if type(disable_libtool) is not bool: + raise(TypeError('disable_libtool must be a bool, not %s' % \ + type(disable_libtool).__name__)) + if type(disable_gettext) is not bool: + raise(TypeError('disable_gettext must be a bool, not %s' % \ + type(disable_gettext).__name__)) + if type(indentation) is bytes or type(indentation) is string: + if type(indentation) is bytes: + indentation = indentation.decode(ENCS['default']) + else: # if indentation has not bytes or string type + raise(TypeError('indentation must be a string, not %s' % \ + type(indentation).__name__)) + if not indentation.isspace(): + raise(ValueError('indentation must contain only whitespaces')) + auxdir = self.config['auxdir'] + libtool = self.config['libtool'] + include_guard_prefix = self.config['include_guard_prefix'] + if str(module) in ['gnumakefile', 'maintainer-makefile']: + # These modules are meant to be used only in the top-level directory. + flag = toplevel + else: # if not str(module) in ['gnumakefile', 'maintainer-makefile'] + flag = True + if flag: + snippet = module.getAutoconfSnippet() + snippet = snippet.replace('${gl_include_guard_prefix}', + include_guard_prefix) + lines = [line for line in snippet.split('\n') if line.strip()] + snippet = '%s\n' % '\n'.join(lines) + transformer = fileassistant.transformers.get('aux', '') + pattern = compiler('(^.*?$)', re.S | re.M) + snippet = pattern.sub('%s\\1' % indentation, snippet) + if transformer: + args = ['sed', '-e', transformer] + path = fileassistant.tmpfilename('snippet') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(snippet) + stdin = codecs.open(path, 'rb', 'UTF-8') + snippet = sp.check_output(args, stdin=stdin, shell=False) + snippet = snippet.decode(ENCS['shell']) + os.remove(path) + if disable_libtool: + snippet = snippet.replace('$gl_cond_libtool', 'false') + snippet = snippet.replace('gl_libdeps', 'gltests_libdeps') + snippet = snippet.replace('gl_ltlibdeps', 'gltests_ltlibdeps') + if disable_gettext: + snippet = snippet.replace('AM_GNU_GETTEXT([external])', 'dnl you must \ +add AM_GNU_GETTEXT([external]) or similar to configure.ac.') + emit += snippet + if str(module) == 'alloca' and libtool and not disable_libtool: + emit += 'changequote(,)dnl\n' + emit += "LTALLOCA=`echo \"$ALLOCA\" | sed -e 's/\\.[^.]* /.lo \ +/g;s/\\.[^.]*$/.lo/'`\n" + emit += 'changequote([, ])dnl\n' + emit += 'AC_SUBST([LTALLOCA])' + if replace_auxdir: + regex = 'AC_CONFIG_FILES\\(\\[(.*?)\\:build-aux/(.*?)\\]\\)' + repl = 'AC_CONFIG_FILES([\\1:%s/\\2])' % auxdir + pattern = compiler(regex, re.S | re.M) + emit = pattern.sub(repl, emit) + lines = [line for line in emit.split('\n') if line.strip()] + emit = '%s\n' % '\n'.join(lines) + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(emit) + + def autoconfSnippets(self, modules, moduletable, fileassistant, + verifier, toplevel, disable_libtool, disable_gettext, replace_auxdir): + '''GLEmiter.autoconfSnippets(modules, fileassistant, + verifier, toplevel, disable_libtool, disable_gettext, + replace_auxdir) -> string + + Collect and emit the autoconf snippets of a set of modules. + GLConfig: conddeps. + + basemodules argument represents list of modules; every module in this list + must be a GLModule instance; this list of modules is used to sort all + modules after they were processed. + modules argument represents list of modules; every module in this list must + be a GLModule instance. + moduletable is a GLModuleTable instance, which contains necessary + information about dependencies of the modules. + fileassistant is a GLFileAssistant instance, which is used to get temporary + directories and sed transformers. + verifier is an integer, which can be 0, 1 or 2. + if verifier == 0, then process every module; + if verifier == 1, then process only non-tests modules; + if verifier == 2, then process only tests modules. + toplevel is a bool variable, False means a subordinate use of pygnulib. + disable_libtool is a bool variable; it tells whether to disable libtool + handling even if it has been specified through the GLConfig class. + disable_gettext is a bool variable; it tells whether to disable + AM_GNU_GETTEXT invocations. + replace_auxdir is a bool variable; it tells whether to replace + 'build-aux' directory in AC_CONFIG_FILES.''' + emit = string() + for module in modules: + if type(module) is not GLModule: + raise(TypeError('each module must be a GLModule instance')) + if type(moduletable) is not GLModuleTable: + raise(TypeError('moduletable must be a GLFileAssistant, not %s' % \ + type(moduletable).__name__)) + if type(fileassistant) is not GLFileAssistant: + raise(TypeError('fileassistant must be a GLFileAssistant, not %s' % \ + type(fileassistant).__name__)) + if type(verifier) is not int: + raise(TypeError('verifier must be an int, not %s' % \ + type(verifier).__name__)) + if not (0 <= verifier <= 2): + raise(ValueError('verifier must be 0, 1 or 2, not %d' % verifier)) + if type(toplevel) is not bool: + raise(TypeError('toplevel must be a bool, not %s' % \ + type(toplevel).__name__)) + if type(disable_libtool) is not bool: + raise(TypeError('disable_libtool must be a bool, not %s' % \ + type(disable_libtool).__name__)) + if type(disable_gettext) is not bool: + raise(TypeError('disable_gettext must be a bool, not %s' % \ + type(disable_gettext).__name__)) + if type(replace_auxdir) is not bool: + raise(TypeError('replace_auxdir must be a bool, not %s' % \ + type(replace_auxdir).__name__)) + auxdir = self.config['auxdir'] + conddeps = self.config['conddeps'] + macro_prefix = self.config['macro_prefix'] + if not conddeps: + # Ignore the conditions, and enable all modules unconditionally. + for module in modules: + if verifier == 0: + solution = True + elif verifier == 1: + solution = module.isNonTests() + elif verifier == 2: + solution = module.isTests() + if solution: + emit += self.autoconfSnippet(module, fileassistant, toplevel, + disable_libtool, disable_gettext, replace_auxdir, ' ') + else: # if conddeps + # Emit the autoconf code for the unconditional modules. + for module in modules: + if verifier == 0: + solution = True + elif verifier == 1: + solution = module.isNonTests() + elif verifier == 2: + solution = module.isTests() + if solution: + if not moduletable.isConditional(module): + emit += self.autoconfSnippet(module, fileassistant, toplevel, + disable_libtool, disable_gettext, replace_auxdir, ' ') + # Initialize the shell variables indicating that the modules are enabled. + for module in modules: + if verifier == 0: + solution = True + elif verifier == 1: + solution = module.isNonTests() + elif verifier == 2: + solution = module.isTests() + if solution: + if moduletable.isConditional(module): + shellvar = module.getShellVar() + emit += ' %s=false\n' % module.getShellVar() + # Emit the autoconf code for the conditional modules, each in a separate + # function. This makes it possible to support cycles among conditional + # modules. + for module in modules: + if verifier == 0: + solution = True + elif verifier == 1: + solution = module.isNonTests() + elif verifier == 2: + solution = module.isTests() + if solution: + if moduletable.isConditional(module): + shellfunc = module.getShellFunc() + shellvar = module.getShellVar() + emit += ' %s ()\n' % shellfunc + emit += ' {\n' + emit += ' if ! $%s; then\n' % shellvar + emit += self.autoconfSnippet(module, fileassistant, toplevel, + disable_libtool, disable_gettext, replace_auxdir, ' ') + emit += ' %s=true\n' % shellvar + dependencies = module.getDependencies() + depmodules = [pair[0] for pair in dependencies] + # Intersect dependencies with the modules list. + depmodules = [dep for dep in depmodules if dep in modules] + for depmodule in depmodules: + if moduletable.isConditional(depmodule): + shellfunc = depmodule.getShellFunc() + condition = moduletable.getCondition(module, depmodule) + if condition != None: + emit += ' if %s; then\n' % condition + emit += ' %s\n' % shellfunc + emit += ' fi\n' + else: # if condition == None + emit += ' %s\n' % shellfunc + else: # if not moduletable.isConditional(depmodule) + # The autoconf code for $dep has already been emitted above and + # therefore is already executed when this code is run. + pass + # Define the Automake conditionals. + emit += ' m4_pattern_allow([^%s_GNULIB_ENABLED_])\n' % macro_prefix + for module in modules: + if verifier == 0: + solution = True + elif verifier == 1: + solution = module.isNonTests() + elif verifier == 2: + solution = module.isTests() + if solution: + condname = module.getConditionalName() + shellvar = module.getShellVar() + emit += ' AM_CONDITIONAL([%s], [$%s])\n' % (condname, shellvar) + lines = [line for line in emit.split('\n') if line.strip()] + emit = '%s\n' % '\n'.join(lines) + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(emit) + + def po_Makevars(self): + '''GLEmiter.po_Makevars() -> string + + Emit the contents of po/ makefile parameterization. + GLConfig: pobase, podomain.''' + emit = string() + pobase = self.config['pobase'] + podomain = self.config['podomain'] + top_subdir = string() + source = '%s/' % os.path.normpath(pobase) + if os.path.sep in source: + for directory in source.split(os.path.sep): + if directory != '': + top_subdir += '../' + top_subdir = os.path.normpath(top_subdir) + emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n" + emit += "%s\n" % self.copyright_notice() + emit += "# Usually the message domain is the same as the package name.\n" + emit += "# But here it has a '-gnulib' suffix.\n" + emit += "DOMAIN = %s-gnulib\n\n" % podomain + emit += "# These two variables depend on the location of this directory.\n" + emit += "subdir = %s\n" % pobase + emit += "top_subdir = %s\n" % top_subdir + emit += """ +# These options get passed to xgettext. +XGETTEXT_OPTIONS = \\ + --keyword=_ --flag=_:1:pass-c-format \\ + --keyword=N_ --flag=N_:1:pass-c-format \\ + --keyword='proper_name:1,"This is a proper name. See the gettext manual, \ +section Names."' \\ + --keyword='proper_name_utf8:1,"This is a proper name. See the gettext \ +manual, section Names."' \\ + --flag=error:3:c-format --flag=error_at_line:5:c-format + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. gnulib is copyrighted by the FSF. +COPYRIGHT_HOLDER = Free Software Foundation, Inc. + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = address@hidden + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = + +# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' +# context. Possible values are "yes" and "no". Set this to yes if the +# package uses functions taking also a message context, like pgettext(), or +# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. +USE_MSGCTXT = no\n""" + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def po_POTFILES_in(self, files): + '''GLEmiter.po_POTFILES_in(files) -> string + + Emit the file list to be passed to xgettext. + GLConfig: sourcebase.''' + emit = string() + sourcebase = self.config['sourcebase'] + sourcebase = '%s%s' % (self.sourcebase, os.path.sep) + if type(sourcebase) is bytes: + sourcebase = sourcebase.decode(ENCS['default']) + files = [substart('lib/', sourcebase, file) for file in files] + files = [file for file in files if file.startswith(sourcebase)] + emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n" + emit += "%s\n" % self.copyright_notice() + emit += "# List of files which contain translatable strings.\n" + emit += '\n'.join(files) + emit += '\n' + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def initmacro_start(self, macro_prefix_arg): + '''GLEmiter.initmacro_start(macro_prefix_arg) -> string + + Emit the first few statements of the gl_INIT macro.''' + emit = string() + if type(macro_prefix_arg) is bytes or type(macro_prefix_arg) is string: + if type(macro_prefix_arg) is bytes: + macro_prefix_arg = macro_prefix_arg.decode(ENCS['default']) + else: # if macro_prefix_arg has not bytes or string type + raise(TypeError('macro_prefix_arg must be a string, not %s' % \ + type(macro_prefix_arg).__name__)) + # Overriding AC_LIBOBJ and AC_REPLACE_FUNCS has the effect of storing + # platform-dependent object files in ${macro_prefix_arg}_LIBOBJS instead + # of LIBOBJS. The purpose is to allow several gnulib instantiations under + # a single configure.ac file. (AC_CONFIG_LIBOBJ_DIR does not allow this + # flexibility). + # Furthermore it avoids an automake error like this when a Makefile.am + # that uses pieces of gnulib also uses $(LIBOBJ): + # automatically discovered file `error.c' should not be explicitly + # mentioned. + emit += " m4_pushdef([AC_LIBOBJ]," + emit += " m4_defn([%V1%_LIBOBJ]))\n" + emit += " m4_pushdef([AC_REPLACE_FUNCS]," + emit += " m4_defn([%V1%_REPLACE_FUNCS]))\n" + # Overriding AC_LIBSOURCES has the same purpose of avoiding the automake + # error when a Makefile.am that uses pieces of gnulib also uses $(LIBOBJ): + # automatically discovered file `error.c' should not be explicitly + # mentioned + # We let automake know about the files to be distributed through the + # EXTRA_lib_SOURCES variable. + emit += " m4_pushdef([AC_LIBSOURCES]," + emit += " m4_defn([%V1%_LIBSOURCES]))\n" + # Create data variables for checking the presence of files that are + # mentioned as AC_LIBSOURCES arguments. These are m4 variables, not shell + # variables, because we want the check to happen when the configure file is + # created, not when it is run. ${macro_prefix_arg}_LIBSOURCES_LIST is the + # list of files to check for. ${macro_prefix_arg}_LIBSOURCES_DIR is the + # subdirectory in which to expect them. + emit += " m4_pushdef([%V1%_LIBSOURCES_LIST], [])\n" + emit += " m4_pushdef([%V1%_LIBSOURCES_DIR], [])\n" + emit += " gl_COMMON\n" + emit = emit.replace('%V1%', macro_prefix_arg) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def initmacro_end(self, macro_prefix_arg): + '''GLEmiter.initmacro_end(macro_prefix_arg) -> string + + Emit the last few statements of the gl_INIT macro.''' + emit = string() + if type(macro_prefix_arg) is bytes or type(macro_prefix_arg) is string: + if type(macro_prefix_arg) is bytes: + macro_prefix_arg = macro_prefix_arg.decode(ENCS['default']) + else: # if macro_prefix_arg has not bytes or string type + raise(TypeError('macro_prefix_arg must be a string, not %s' % \ + type(macro_prefix_arg).__name__)) + # Check the presence of files that are mentioned as AC_LIBSOURCES + # arguments. The check is performed only when autoconf is run from the + # directory where the configure.ac resides; if it is run from a different + # directory, the check is skipped. + emit += """\ + m4_ifval(%V1%_LIBSOURCES_LIST, [ + m4_syscmd([test ! -d ]m4_defn([%V1%_LIBSOURCES_DIR])[ || + for gl_file in ]%V1%_LIBSOURCES_LIST[ ; do + if test ! -r ]m4_defn([%V1%_LIBSOURCES_DIR])[/$gl_file ; then + echo "missing file ]m4_defn([%V1%_LIBSOURCES_DIR])[/$gl_file" >&2 + exit 1 + fi + done])dnl + m4_if(m4_sysval, [0], [], + [AC_FATAL([expected source file, required through AC_LIBSOURCES, not \ +found])]) + ]) + m4_popdef([%V1%_LIBSOURCES_DIR]) + m4_popdef([%V1%_LIBSOURCES_LIST]) + m4_popdef([AC_LIBSOURCES]) + m4_popdef([AC_REPLACE_FUNCS]) + m4_popdef([AC_LIBOBJ]) + AC_CONFIG_COMMANDS_PRE([ + %V1%_libobjs= + %V1%_ltlibobjs= + if test -n "$%V1%_LIBOBJS"; then + # Remove the extension. + sed_drop_objext='s/\.o$//;s/\.obj$//' + for i in `for i in $%V1%_LIBOBJS; do echo "$i"; done | sed -e \ +"$sed_drop_objext" | sort | uniq`; do + %V1%_libobjs="$%V1%_libobjs $i.$ac_objext" + %V1%_ltlibobjs="$%V1%_ltlibobjs $i.lo" + done + fi + AC_SUBST([%V1%_LIBOBJS], [$%V1%_libobjs]) + AC_SUBST([%V1%_LTLIBOBJS], [$%V1%_ltlibobjs]) + ])\n""" + emit = emit.replace('%V1%', macro_prefix_arg) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def initmacro_done(self, macro_prefix_arg, sourcebase_arg): + '''GLEmiter.initmacro_done(macro_prefix_arg, sourcebase_arg) -> string + + Emit a few statements after the gl_INIT macro. + GLConfig: sourcebase.''' + emit = string() + if type(macro_prefix_arg) is bytes or type(macro_prefix_arg) is string: + if type(macro_prefix_arg) is bytes: + macro_prefix_arg = macro_prefix_arg.decode(ENCS['default']) + else: # if macro_prefix_arg has not bytes or string type + raise(TypeError('macro_prefix_arg must be a string, not %s' % \ + type(macro_prefix_arg).__name__)) + if type(sourcebase_arg) is bytes or type(sourcebase_arg) is string: + if type(sourcebase_arg) is bytes: + sourcebase_arg = sourcebase_arg.decode(ENCS['default']) + else: # if sourcebase_arg has not bytes or string type + raise(TypeError('sourcebase_arg must be a string, not %s' % \ + type(sourcebase_arg).__name__)) + emit += """\ + +# Like AC_LIBOBJ, except that the module name goes +# into %V1%_LIBOBJS instead of into LIBOBJS. +AC_DEFUN([%V1%_LIBOBJ], [ + AS_LITERAL_IF([$1], [%V1%_LIBSOURCES([$1.c])])dnl + %V1%_LIBOBJS="$%V1%_LIBOBJS $1.$ac_objext" +]) + +# Like AC_REPLACE_FUNCS, except that the module name goes +# into %V1%_LIBOBJS instead of into LIBOBJS. +AC_DEFUN([%V1%_REPLACE_FUNCS], [ + m4_foreach_w([gl_NAME], [$1], [AC_LIBSOURCES(gl_NAME[.c])])dnl + AC_CHECK_FUNCS([$1], , [%V1%_LIBOBJ($ac_func)]) +]) + +# Like AC_LIBSOURCES, except the directory where the source file is +# expected is derived from the gnulib-tool parameterization, +# and alloca is special cased (for the alloca-opt module). +# We could also entirely rely on EXTRA_lib..._SOURCES. +AC_DEFUN([%V1%_LIBSOURCES], [ + m4_foreach([_gl_NAME], [$1], [ + m4_if(_gl_NAME, [alloca.c], [], [ + m4_define([%V1%_LIBSOURCES_DIR], [%V2%]) + m4_append([%V1%_LIBSOURCES_LIST], _gl_NAME, [ ]) + ]) + ]) +])\n""" + emit = emit.replace('%V1%', macro_prefix_arg) + emit = emit.replace('%V2%', sourcebase_arg) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def lib_Makefile_am(self, destfile, modules, + moduletable, makefiletable, actioncmd, for_test): + '''GLEmiter.lib_Makefile_am(destfile, modules, moduletable, makefiletable, + actioncmd, for_test) -> tuple of string and bool + + Emit the contents of the library Makefile. Returns string and a bool + variable which shows if subdirectories are used. + GLConfig: localdir, sourcebase, libname, pobase, auxdir, makefile, libtool, + macro_prefix, podomain, conddeps, witness_c_macro. + + destfile is a filename relative to destdir of Makefile being generated. + modules is a list of GLModule instances. + moduletable is a GLModuleTable instance. + makefiletable is a GLMakefileTable instance. + actioncmd is a string variable, which represents the actioncmd; it can be + an empty string e.g. when user wants to generate files for GLTestDir. + for_test is a bool variable; it must be set to True if creating a package + for testing, False otherwise.''' + if type(destfile) is bytes or type(destfile) is string: + if type(destfile) is bytes: + destfile = destfile.decode(ENCS['default']) + else: # if destfile has not bytes or string type + raise(TypeError('destfile must be a string, not %s' % \ + type(destfile).__name__)) + for module in modules: + if type(module) is not GLModule: + raise(TypeError('each module must be a GLModule instance')) + if type(moduletable) is not GLModuleTable: + raise(TypeError('moduletable must be a GLModuleTable, not %s' % \ + type(moduletable).__name__)) + if type(makefiletable) is not GLMakefileTable: + raise(TypeError('makefiletable must be a GLMakefileTable, not %s' % \ + type(makefiletable).__name__)) + if type(actioncmd) is bytes or type(actioncmd) is string: + if type(actioncmd) is bytes: + actioncmd = actioncmd.decode(ENCS['default']) + else: # if actioncmd has not bytes or string type + raise(TypeError('actioncmd must be a string, not %s' % \ + type(actioncmd).__name__)) + if type(for_test) is not bool: + raise(TypeError('for_test must be a bool, not %s' % \ + type(for_test).__name__)) + emit = string() + localdir = self.config['localdir'] + sourcebase = self.config['sourcebase'] + modcache = self.config['modcache'] + libname = self.config['libname'] + pobase = self.config['pobase'] + auxdir = self.config['auxdir'] + makefile = self.config['makefile'] + libtool = self.config['libtool'] + macro_prefix = self.config['macro_prefix'] + podomain = self.config['podomain'] + conddeps = self.config['conddeps'] + witness_c_macro = self.config['witness_c_macro'] + include_guard_prefix = self.config['include_guard_prefix'] + ac_version = self.config['ac_version'] + destfile = os.path.normpath(destfile) + + # When creating an includable Makefile.am snippet, augment variables with + # += instead of assigning them. + if makefile: + assign = '+=' + else: # if not makefile + assign = '=' + if libtool: + libext = 'la' + perhapsLT = 'LT' + LD_flags = False + else: # if not libtool + libext = 'a' + perhapsLT = '' + LD_flags = True + if for_test: + # When creating a package for testing: Attempt to provoke failures, + # especially link errors, already during "make" rather than during + # "make check", because "make check" is not possible in a cross-compiling + # situation. Turn check_PROGRAMS into noinst_PROGRAMS. + check_PROGRAMS = True + else: # if not for_test + check_PROGRAMS = False + emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n" + emit += "## Process this file with automake to produce Makefile.in.\n" + emit += self.copyright_notice() + if actioncmd: + if len(actioncmd) <= 3000: + emit += "# Reproduce by: %s\n" % actioncmd + emit += '\n' + uses_subdirs = False + + # Modify allsnippets variable. + allsnippets = string() + for module in modules: + if not module.isTests(): + # Get conditional snippet, edit it and save to amsnippet1. + amsnippet1 = module.getAutomakeSnippet_Conditional() + amsnippet1 = amsnippet1.replace('lib_LIBRARIES', 'lib%_LIBRARIES') + amsnippet1 = amsnippet1.replace('lib_LTLIBRARIES', 'lib%_LTLIBRARIES') + if LD_flags: + pattern = compiler('lib_LDFLAGS[\t ]*\\+=(.*?)$', re.S | re.M) + amsnippet1 = pattern.sub('', amsnippet1) + pattern = compiler('lib_([A-Z][A-Z](?:.*?))', re.S | re.M) + amsnippet1 = pattern.sub('%s_%s_\\1' % (libname, libext), amsnippet1) + amsnippet1 = amsnippet1.replace('lib%_LIBRARIES', 'lib_LIBRARIES') + amsnippet1 = amsnippet1.replace('lib%_LTLIBRARIES', 'lib_LTLIBRARIES') + amsnippet1 = amsnippet1.replace('${gl_include_guard_prefix}', + include_guard_prefix) + if str(module) == 'alloca': + amsnippet1 += '%s_%s_LIBADD += @address@hidden' % \ + (libname, libext, perhapsLT) + amsnippet1 += '%s_%s_DEPENDENCIES += @address@hidden' % \ + (libname, libext, perhapsLT) + if check_PROGRAMS: + amsnippet1 = amsnippet1.replace('check_PROGRAMS', 'noinst_PROGRAMS') + + # Get unconditional snippet, edit it and save to amsnippet1. + amsnippet2 = module.getAutomakeSnippet_Unconditional() + pattern = compiler('lib_([A-Z][A-Z](?:.*?))', re.S | re.M) + amsnippet2 = pattern.sub('%s_%s_\\1' % (libname, libext), amsnippet2) + if type(amsnippet1) is bytes: + amsnippet1 = amsnippet1.decode(ENCS['default']) + if type(amsnippet2) is bytes: + amsnippet2 = amsnippet1.decode(ENCS['default']) + if not (amsnippet1 +amsnippet2).isspace(): + allsnippets += '## begin gnulib module %s\n' % str(module) + if conddeps: + if moduletable.isConditional(module): + name = module.getConditionalName() + allsnippets += 'if %s\n' % name + allsnippets += amsnippet1 + if conddeps: + allsnippets += 'endif\n' + allsnippets += amsnippet2 + allsnippets += '## end gnulib module %s\n\n' % str(module) + + # Test whether there are some source files in subdirectories. + for file in module.getFiles(): + if file.startswith('lib/') and file.endswith('.c') and \ + file.count('/') > 1: + uses_subdirs = True + break + if not makefile: + subdir_options = string() + # If there are source files in subdirectories, prevent collision of the + # object files (example: hash.c and libxml/hash.c). + if uses_subdirs: + subdir_options = string(' subdir-objects') + emit += 'AUTOMAKE_OPTIONS = 1.5 gnits%s\n' % subdir_options + emit += '\n' + if not makefile: + emit += 'SUBDIRS =\n' + emit += 'noinst_HEADERS =\n' + emit += 'noinst_LIBRARIES =\n' + emit += 'noinst_LTLIBRARIES =\n' + # Automake versions < 1.11.4 create an empty pkgdatadir at + # installation time if you specify pkgdata_DATA to empty. + # See automake bugs #10997 and #11030: + # * http://debbugs.gnu.org/10997 + # * http://debbugs.gnu.org/11030 + # So we need this workaround. + pattern = compiler('^pkgdata_DATA *\\+=', re.S | re.M) + if pattern.findall(allsnippets): + emit += 'pkgdata_DATA =\n' + emit += 'EXTRA_DIST =\n' + emit += 'BUILT_SOURCES =\n' + emit += 'SUFFIXES =\n' + emit += 'MOSTLYCLEANFILES %s core *.stackdump\n' % assign + if not makefile: + emit += 'MOSTLYCLEANDIRS =\n' + emit += 'CLEANFILES =\n' + emit += 'DISTCLEANFILES =\n' + emit += 'MAINTAINERCLEANFILES =\n' + + # Execute edits that apply to the Makefile.am being generated. + current_edit = int() + makefile_am_edits = makefiletable.count() + while current_edit != makefile_am_edits: + dictionary = makefiletable[current_edit] + if dictionary['var']: + paths = list() + paths += [joinpath(dictionary['dir'], 'Makefile.am')] + paths += [os.path.normpath('./%s/Makefile.am' % dictionary['dir'])] + paths = sorted(set(paths)) + if destfile in paths: + emit += '%s += %s\n' % (dictionary['var'], dictionary['val']) + current_edit += 1 + + # Define two parts of cppflags variable. + emit += '\n' + cppflags_part1 = string() + cppflags_part2 = string() + if witness_c_macro: + cppflags_part1 = ' -D%s=1' % witness_c_macro + if for_test: + cppflags_part2 = ' -DGNULIB_STRICT_CHECKING=1' + cppflags = '%s%s' % (cppflags_part1, cppflags_part2) + if not makefile: + emit += 'AM_CPPFLAGS =%s\n' % cppflags + emit += 'AM_CFLAGS =\n' + else: # if makefile + if cppflags: + emit += 'AM_CPPFLAGS +=%s\n' % cppflags + emit += '\n' + + # One of the snippets or the user's Makefile.am already specifies an + # installation location for the library. Don't confuse automake by saying + # it should not be installed. + # First test if allsnippets already specify an installation location. + insnippets = False + inmakefile = False + regex = '^[a-zA-Z0-9_]*_%sLIBRARIES *\\+{0,1}= *%s.%s' % \ + (perhapsLT, libname, libext) + pattern = compiler(regex, re.S | re.M) + insnippets = bool(pattern.findall(allsnippets)) + # Then test if $sourcebase/Makefile.am (if it exists) specifies it. + path = joinpath(sourcebase, 'Makefile.am') + if makefile and isfile(path): + with codecs.open(path, 'rb', 'UTF-8') as file: + data = file.read() + inmakefile = bool(pattern.findall(data)) + if not any([insnippets, inmakefile]): + # By default, the generated library should not be installed. + emit += 'noinst_%sLIBRARIES += %s.%s\n' % (perhapsLT, libname, libext) + + emit += '\n' + emit += '%s_%s_SOURCES =\n' % (libname, libext) + # Here we use $(LIBOBJS), not @address@hidden The value is the same. However, + # automake during its analysis looks for $(LIBOBJS), not for @address@hidden + emit += '%s_%s_LIBADD = $(%s_%sLIBOBJS)\n' % \ + (libname, libext, macro_prefix, perhapsLT) + emit += '%s_%s_DEPENDENCIES = $(%s_%sLIBOBJS)\n' % \ + (libname, libext, macro_prefix, perhapsLT) + emit += 'EXTRA_%s_%s_SOURCES =\n' % (libname, libext) + if libtool: + emit += '%s_%s_LDFLAGS = $(AM_LDFLAGS)\n' % (libname, libext) + emit += '%s_%s_LDFLAGS += -no-undefined\n' % (libname, libext) + # Synthesize an ${libname}_${libext}_LDFLAGS augmentation by combining + # the link dependencies of all modules. + listing = list() + links = [module.getLink() for module in modules if not module.isTests()] + for link in links: + link = constants.nlremove(link) + position = link.find(' when linking with libtool') + if position != -1: + link = link[:position] + listing += [link] + listing = sorted(set([link for link in listing if link != ''])) + for link in listing: + emit += '%s_%s_LDFLAGS += %s\n' % (libname, libext, link) + emit += '\n' + if pobase: + emit += 'AM_CPPFLAGS += -DDEFAULT_TEXT_DOMAIN="%s-gnulib"\n' % podomain + emit += '\n' + allsnippets = allsnippets.replace('$(top_srcdir)/build-aux/', + '$(top_srcdir)/%s/' % auxdir) + emit += allsnippets + emit += '\n' + emit += 'mostlyclean-local: mostlyclean-generic\n' + emit += 'address@hidden dir in \'\' $(MOSTLYCLEANDIRS); do \\\n' + emit += '\t if test -n "$$dir" && test -d $$dir; then \\\n' + emit += '\t echo "rmdir $$dir"; rmdir $$dir; \\\n' + emit += '\t fi; \\\n' + emit += '\tdone; \\\n' + emit += '\t:\n' + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + result = tuple([emit, uses_subdirs]) + return(result) + + def tests_Makefile_am(self, destfile, modules, makefiletable, + witness_macro, for_test): + '''GLEmiter.tests_Makefile_am(destfile, modules, makefiletable, + witness_c_macro, for_test) -> tuple of string and bool + + Emit the contents of the tests Makefile. Returns string and a bool variable + which shows if subdirectories are used. + GLConfig: localdir, modules, libname, auxdir, makefile, libtool, + sourcebase, m4base, testsbase, macro_prefix, witness_c_macro, + single_configure, libtests. + + destfile is a filename relative to destdir of Makefile being generated. + witness_macro is a string which represents witness_c_macro with the suffix. + modules is a list of GLModule instances. + moduletable is a GLModuleTable instance. + makefiletable is a GLMakefileTable instance. + actioncmd is a string variable, which represents the actioncmd; it can be + an empty string e.g. when user wants to generate files for GLTestDir. + for_test is a bool variable; it must be set to True if creating a package + for testing, False otherwise.''' + if type(destfile) is bytes or type(destfile) is string: + if type(destfile) is bytes: + destfile = destfile.decode(ENCS['default']) + else: # if destfile has not bytes or string type + raise(TypeError('destfile must be a string, not %s' % \ + type(destfile).__name__)) + for module in modules: + if type(module) is not GLModule: + raise(TypeError('each module must be a GLModule instance')) + if type(makefiletable) is not GLMakefileTable: + raise(TypeError('makefiletable must be a GLMakefileTable, not %s' % \ + type(makefiletable).__name__)) + if type(witness_macro) is bytes or type(witness_macro) is string: + if type(witness_macro) is bytes: + witness_macro = witness_macro.decode(ENCS['default']) + else: # if witness_macro has not bytes or string type + raise(TypeError('witness_macro must be a string, not %s' % \ + type(witness_macro).__name__)) + if type(for_test) is not bool: + raise(TypeError('for_test must be a bool, not %s' % \ + type(for_test).__name__)) + emit = string() + localdir = self.config['localdir'] + auxdir = self.config['auxdir'] + sourcebase = self.config['sourcebase'] + modcache = self.config['modcache'] + libname = self.config['libname'] + m4base = self.config['m4base'] + pobase = self.config['pobase'] + testsbase = self.config['testsbase'] + makefile = self.config['makefile'] + libtool = self.config['libtool'] + macro_prefix = self.config['macro_prefix'] + podomain = self.config['podomain'] + conddeps = self.config['conddeps'] + witness_c_macro = self.config['witness_c_macro'] + include_guard_prefix = self.config['include_guard_prefix'] + ac_version = self.config['ac_version'] + libtests = self.config['libtests'] + single_configure = self.config['single_configure'] + + if libtool: + libext = 'la' + perhapsLT = 'LT' + LD_flags = False + else: # if not libtool + libext = 'a' + perhapsLT = '' + LD_flags = True + if for_test: + # When creating a package for testing: Attempt to provoke failures, + # especially link errors, already during "make" rather than during + # "make check", because "make check" is not possible in a cross-compiling + # situation. Turn check_PROGRAMS into noinst_PROGRAMS. + check_PROGRAMS = True + else: # if not for_test + check_PROGRAMS = False + + # Calculate testsbase_inverse + counter = int() + testsbase_inverse = string() + while counter < len(testsbase.split('/')): + testsbase_inverse += '../' + counter += 1 + testsbase_inverse = os.path.normpath(testsbase_inverse) + + # Begin the generation. + emit += "## DO NOT EDIT! GENERATED AUTOMATICALLY!\n" + emit += "## Process this file with automake to produce Makefile.in.\n" + emit += '%s\n' % self.copyright_notice() + + uses_subdirs = False + main_snippets = string() + longrun_snippets = string() + for module in modules: + if for_test and not single_configure: + flag = module.isTests() + else: # if for_test and not single_configure + flag = True + if flag: + snippet = module.getAutomakeSnippet() + snippet = snippet.replace('lib_LIBRARIES', 'lib%_LIBRARIES') + snippet = snippet.replace('lib_LTLIBRARIES', 'lib%_LTLIBRARIES') + if LD_flags: + pattern = compiler('lib_LDFLAGS[\t ]*\\+=(.*?)$', re.S | re.M) + snippet = pattern.sub('', snippet) + pattern = compiler('lib_([A-Z][A-Z](?:.*?))', re.S | re.M) + snippet = pattern.sub('libtests_a_\\1', snippet) + snippet = snippet.replace('lib%_LIBRARIES', 'lib_LIBRARIES') + snippet = snippet.replace('lib%_LTLIBRARIES', 'lib_LTLIBRARIES') + snippet = snippet.replace('${gl_include_guard_prefix}', + include_guard_prefix) + if check_PROGRAMS: + snippet = snippet.replace('check_PROGRAMS', 'noinst_PROGRAMS') + # Check if module is 'alloca'. + if libtests and str(module) == 'alloca': + snippet += 'libtests_a_LIBADD += @address@hidden' % perhapsLT + snippet += 'libtests_a_DEPENDENCIES += @address@hidden' % perhapsLT + + # Skip the contents if it's entirely empty. + if snippet.strip(): + # Check status of the module. + status = module.getStatus() + islongrun = False + for word in status: + if word == 'longrunning-test': + islongrun = True + break + if not islongrun: + snippet = snippet.replace('\n\nEXTRA_DIST', '\nEXTRA_DIST') + main_snippets += '## begin gnulib module %s\n' % str(module) + main_snippets += snippet + main_snippets += '## end gnulib module %s\n\n' % str(module) + else: # if islongrunning + snippet = snippet.replace('\n\nEXTRA_DIST', '\nEXTRA_DIST') + longrun_snippets += '## begin gnulib module %s\n' % str(module) + longrun_snippets += snippet + longrun_snippets += '## end gnulib module %s\n' % str(module) + + # Test whether there are some source files in subdirectories. + for file in module.getFiles(): + if file.startswith('lib/') and file.endswith('.c') and \ + file.count('/') > 1: + uses_subdirs = True + break + + # Generate dependencies here, since it eases the debugging of test failures. + # If there are source files in subdirectories, prevent collision of the + # object files (example: hash.c and libxml/hash.c). + subdir_options = string() + if uses_subdirs: + subdir_options = string(' subdir-objects') + emit += 'AUTOMAKE_OPTIONS = 1.5 foreign%s\n\n' % subdir_options + if for_test and not single_configure: + emit += 'ACLOCAL_AMFLAGS = -I %s/%s\n\n' % (testsbase_inverse, m4base) + + # Nothing is being added to SUBDIRS; nevertheless the existence of this + # variable is needed to avoid an error from automake: + # "AM_GNU_GETTEXT used but SUBDIRS not defined" + emit += 'SUBDIRS = .\n' + emit += 'TESTS =\n' + emit += 'XFAIL_TESTS =\n' + emit += 'TESTS_ENVIRONMENT =\n' + emit += 'noinst_PROGRAMS =\n' + if not for_test: + emit += 'check_PROGRAMS =\n' + emit += 'noinst_HEADERS =\n' + emit += 'noinst_LIBRARIES =\n' + if libtests: + if for_test: + emit += 'noinst_LIBRARIES += libtests.a\n' + else: # if not for_test + emit += 'check_LIBRARIES = libtests.a\n' + + # Automake versions < 1.11.4 create an empty pkgdatadir at + # installation time if you specify pkgdata_DATA to empty. + # See automake bugs #10997 and #11030: + # * http://debbugs.gnu.org/10997 + # * http://debbugs.gnu.org/11030 + # So we need this workaround. + pattern = compiler('^pkgdata_DATA *\\+=', re.S | re.M) + if bool(pattern.findall(main_snippets)) or \ + bool(pattern.findall(longrun_snippets)): + emit += 'pkgdata_DATA =\n' + + emit += 'EXTRA_DIST =\n' + emit += 'BUILT_SOURCES =\n' + emit += 'SUFFIXES =\n' + emit += 'MOSTLYCLEANFILES = core *.stackdump\n' + emit += 'MOSTLYCLEANDIRS =\n' + emit += 'CLEANFILES =\n' + emit += 'DISTCLEANFILES =\n' + emit += 'MAINTAINERCLEANFILES =\n' + + # Execute edits that apply to the Makefile.am being generated. + # Execute edits that apply to the Makefile.am being generated. + current_edit = int() + makefile_am_edits = makefiletable.count() + while current_edit != makefile_am_edits: + dictionary = makefiletable[current_edit] + if dictionary['var']: + paths = list() + paths += [joinpath(dictionary['dir'], 'Makefile.am')] + paths += [os.path.normpath('./%s/Makefile.am' % dictionary['dir'])] + paths = sorted(set(paths)) + if destfile in paths: + emit += '%s += %s\n' % (dictionary['var'], dictionary['val']) + current_edit += 1 + + emit += '\nAM_CPPFLAGS = \\\n' + if for_test: + emit += ' -DGNULIB_STRICT_CHECKING=1 \\\n' + if witness_c_macro: + emit += ' -D%s=1 \\\n' % witness_c_macro + if witness_macro: + emit += ' address@hidden@=1 \\\n' % witness_macro + emit += ' -I. -I$(srcdir) \\\n' + emit += ' -I%s -I$(srcdir)/%s \\\n' % \ + (testsbase_inverse, testsbase_inverse) + emit += ' -I%s/%s -I$(srcdir)/%s/%s\n' % \ + (testsbase_inverse, sourcebase, testsbase_inverse, sourcebase) + emit += '\n' + + local_ldadd_before = string() + local_ldadd_after = string() + if libtests: + # All test programs need to be linked with libtests.a. + # It needs to be passed to the linker before ${libname}.${libext}, since + # the tests-related modules depend on the main modules. + # It also needs to be passed to the linker after ${libname}.${libext} + # because the latter might contain incomplete modules (such as the + # 'error' module whose dependency to 'progname' is voluntarily omitted). + # The LIBTESTS_LIBDEPS can be passed to the linker once or twice, it does + # not matter. + local_ldadd_before = ' libtests.a' + local_ldadd_after = ' libtests.a $(LIBTESTS_LIBDEPS)' + emit += 'LDADD =%s %s/%s/%s.%s%s\n\n' % \ + (local_ldadd_before, testsbase_inverse, sourcebase, libname, libext, + local_ldadd_after) + if libtests: + emit += 'libtests_a_SOURCES =\n' + # Here we use $(LIBOBJS), not @address@hidden The value is the same. However, + # automake during its analysis looks for $(LIBOBJS), not for @address@hidden + emit += 'libtests_a_LIBADD = $(%stests_LIBOBJS)\n' % macro_prefix + emit += 'libtests_a_DEPENDENCIES = $(%stests_LIBOBJS)\n' % macro_prefix + emit += 'EXTRA_libtests_a_SOURCES =\n' + # The circular dependency in LDADD requires this. + emit += 'AM_LIBTOOLFLAGS = --preserve-dup-deps\n\n' + # Many test scripts use ${EXEEXT} or ${srcdir}. + # EXEEXT is defined by AC_PROG_CC through autoconf. + # srcdir is defined by autoconf and automake. + emit += "TESTS_ENVIRONMENT += EXEEXT='@EXEEXT@' srcdir='$(srcdir)'\n\n" + main_snippets = main_snippets.replace('$(top_srcdir)/build-aux/', + '$(top_srcdir)/%s/' % auxdir) + longrun_snippets = longrun_snippets.replace('$(top_srcdir)/build-aux/', + '$(top_srcdir)/%s/' % auxdir) + emit += main_snippets +longrun_snippets + emit += '# Clean up after Solaris cc.\n' + emit += 'clean-local:\n' + emit += '\trm -rf SunWS_cache\n\n' + emit += 'mostlyclean-local: mostlyclean-generic\n' + emit += 'address@hidden dir in \'\' $(MOSTLYCLEANDIRS); do \\\n' + emit += '\t if test -n "$$dir" && test -d $$dir; then \\\n' + emit += '\t echo "rmdir $$dir"; rmdir $$dir; \\\n' + emit += '\t fi; \\\n' + emit += '\tdone; \\\n' + emit += '\t:\n' + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + result = tuple([emit, uses_subdirs]) + return(result) + diff --git a/pygnulib/GLEmiter.pyc b/pygnulib/GLEmiter.pyc new file mode 100644 index 000000000..8570a8e4e Binary files /dev/null and b/pygnulib/GLEmiter.pyc differ diff --git a/pygnulib/GLError.py b/pygnulib/GLError.py new file mode 100644 index 000000000..2afe970b2 --- /dev/null +++ b/pygnulib/GLError.py @@ -0,0 +1,115 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import locale +import codecs +from . import constants + + +#=============================================================================== +# 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 +FILES = constants.FILES +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 + + +#=============================================================================== +# Define GLError class +#=============================================================================== +class GLError(Exception): + '''Exception handler for pygnulib classes.''' + + def __init__(self, errno, errinfo=None): + '''Each error has following parameters: + errno: code of error; used to catch error type + 1: file does not exist in GLFileSystem: + 2: cannot patch file inside GLFileSystem: + 3: configure file does not exist: + 4: minimum supported autoconf version is 2.59, not + 5: is expected to contain gl_M4_BASE([]) + 6: missing sourcebase argument + 7: missing docbase argument + 8: missing testsbase argument + 9: missing libname argument + 10: conddeps are not supported with inctests + 11: incompatible licenses on modules: + 12: cannot process empy filelist + 13: cannot create the given directory: + 14: cannot delete the given file: + 15: cannot create the given file: + 16: cannot transform the given file: + 17: cannot update the given file: + 18: module lacks a license: + 19: could not create destination directory: + errinfo: additional information; + style: 0 or 1, wheter old-style''' + self.errno = errno; self.errinfo = errinfo + self.args = (self.errno, self.errinfo) + + def __repr__(self): + errinfo = self.errinfo + errors = \ + [ # Begin list of errors + "file does not exist in GLFileSystem: %s" % repr(errinfo), + "cannot patch file inside GLFileSystem: %s" % repr(errinfo), + "configure file does not exist: %s" % repr(errinfo), + "minimum supported autoconf version is 2.59, not %s" % repr(errinfo), + "%s is expected to contain gl_M4_BASE([%s])" % \ + (repr(os.path.join(errinfo, 'gnulib-comp.m4')), repr(errinfo)), + "missing sourcebase argument; cache file doesn't contain it," + +" so you might have to set this argument", + "missing docbase argument; you might have to create GLImport" \ + +" instance with mode 0 and docbase argument", + "missing testsbase argument; cache file doesn't contain it," + +" so you might have to set this argument" + "missing libname argument; cache file doesn't contain it," + +" so you might have to set this argument", + "conddeps are not supported with inctests", + "incompatible licenses on modules: %s" % repr(errinfo), + "cannot process empy filelist", + "cannot create the given directory: %s" % repr(errinfo), + "cannot remove the given file: %s" % repr(errinfo), + "cannot create the given file: %s" % repr(errinfo), + "cannot transform the given file: %s" % repr(errinfo), + "cannot update/replace the given file: %s" % repr(errinfo), + "module lacks a license: %s" % repr(errinfo), + "error when running subprocess: %s" % repr(errinfo), + ] # Complete list of errors + if not PYTHON3: + self.message = (b'[Errno %d] %s' % \ + (self.errno, errors[self.errno -1].encode(ENCS['default']))) + else: # if PYTHON3 + self.message = ('[Errno %d] %s' % \ + (self.errno, errors[self.errno -1])) + return(self.message) + diff --git a/pygnulib/GLError.pyc b/pygnulib/GLError.pyc new file mode 100644 index 000000000..ff6bca939 Binary files /dev/null and b/pygnulib/GLError.pyc differ diff --git a/pygnulib/GLFileSystem.py b/pygnulib/GLFileSystem.py new file mode 100644 index 000000000..2a21eb1a5 --- /dev/null +++ b/pygnulib/GLFileSystem.py @@ -0,0 +1,401 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import codecs +import shutil +import filecmp +import subprocess as sp +from . import constants +from .GLError import GLError +from .GLConfig import GLConfig + + +#=============================================================================== +# 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 +FILES = constants.FILES +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 + + +#=============================================================================== +# Define GLFileSystem class +#=============================================================================== +class GLFileSystem(object): + '''GLFileSystem class is used to create virtual filesystem, which is based on + the gnulib directory and directory specified by localdir argument. Its main + method lookup(file) is used to find file in these directories or combine it + using Linux 'patch' utility.''' + + def __init__(self, config): + '''Create new GLFileSystem instance. The only argument is localdir, + which can be an empty string too.''' + if type(config) is not GLConfig: + raise(TypeError('config must be a GLConfig, not %s' % \ + type(config).__name__)) + self.config = config + + def __repr__(self): + '''x.__repr__ <==> repr(x)''' + result = '' % hex(id(self)) + return(result) + + def lookup(self, name): + '''GLFileSystem.lookup(name) -> tuple + + Lookup a file in gnulib and localdir directories or combine it using Linux + 'patch' utility. If file was found, method returns string, else it raises + GLError telling that file was not found. Function also returns flag which + indicates whether file is a temporary file. + GLConfig: localdir.''' + if type(name) is bytes or type(name) is string: + if type(name) is bytes: + name = name.decode(ENCS['default']) + else: # if name has not bytes or string type + raise(TypeError( + 'name must be a string, not %s' % type(module).__name__)) + # If name exists in localdir, then we use it + path_gnulib = joinpath(DIRS['root'], name) + path_local = joinpath(self.config['localdir'], name) + path_diff = joinpath(self.config['localdir'], '%s.diff' % name) + path_temp = joinpath(self.config['tempdir'], name) + try: # Try to create directories + os.makedirs(os.path.dirname(path_temp)) + except OSError as error: + pass # Skip errors if directory exists + if isfile(path_temp): + os.remove(path_temp) + if self.config['localdir'] and isfile(path_local): + result = (path_local, False) + else: # if path_local does not exist + if isfile(path_gnulib): + if self.config['localdir'] and isfile(path_diff): + shutil.copy(path_gnulib, path_temp) + command = 'patch -s "%s" < "%s"' % (path_temp, path_diff) + try: # Try to apply patch + sp.check_call(command, shell=True) + except sp.CalledProcessError as error: + raise(GLError(2, name)) + result = (path_temp, True) + else: # if path_diff does not exist + result = (path_gnulib, False) + else: # if path_gnulib does not exist + raise(GLError(1, name)) + return(result) + + +#=============================================================================== +# Define GLFileAssistant class +#=============================================================================== +class GLFileAssistant(object): + '''GLFileAssistant is used to help with file processing.''' + + def __init__(self, config, transformers=dict()): + '''Create GLFileAssistant instance.''' + if type(config) is not GLConfig: + raise(TypeError('config must be a GLConfig, not %s' % \ + type(config).__name__)) + if type(transformers) is not dict: + raise(TypeError('transformers must be a dict, not %s' % \ + type(transformers).__name__)) + for key in ['lib', 'aux', 'main', 'tests']: + if key not in transformers: + transformers[key] = 's,x,x,' + else: # if key in transformers + value = transformers[key] + if type(value) is bytes or type(value) is string: + if type(value) is bytes: + transformers[key] = value.decode(ENCS['default']) + else: # if value has not bytes or string type + raise(TypeError('transformers[%s] must be a string, not %s' % \ + (key, type(value).__name__))) + self.original = None + self.rewritten = None + self.added = list() + self.makefile = list() + self.config = config + self.transformers = transformers + self.filesystem = GLFileSystem(self.config) + + def __repr__(self): + '''x.__repr__() <==> repr(x)''' + result = '' % hex(id(self)) + return(result) + + def tmpfilename(self, path): + '''GLFileAssistant.tmpfilename() -> string + + Return the name of a temporary file (file is relative to destdir).''' + if type(path) is bytes or type(path) is string: + if type(path) is bytes: + path = path.decode(ENCS['default']) + else: # if path has not bytes or string type + raise(TypeError( + 'path must be a string, not %s' % (type(path).__name__))) + if not self.config['dryrun']: + # Put the new contents of $file in a file in the same directory (needed + # to guarantee that an 'mv' to "$destdir/$file" works). + result = joinpath(self.config['destdir'], '%s.tmp' % path) + dirname = os.path.dirname(result) + if dirname and not isdir(dirname): + os.makedirs(dirname) + else: # if self.config['dryrun'] + # Put the new contents of $file in a file in a temporary directory + # (because the directory of "$file" might not exist). + tempdir = self.config['tempdir'] + result = joinpath(tempdir, '%s.tmp' % os.path.basename(path)) + dirname = os.path.dirname(result) + if not isdir(dirname): + os.makedirs(dirname) + if type(result) is bytes: + result = bytes.decode(ENCS['default']) + return(result) + + def setOriginal(self, original): + '''GLFileAssistant.setOriginal(original) + + Set the name of the original file which will be used.''' + if type(original) is bytes or type(original) is string: + if type(original) is bytes: + original = original.decode(ENCS['default']) + else: # if original has not bytes or string type + raise(TypeError( + 'original must be a string, not %s' % (type(original).__name__))) + self.original = original + + def setRewritten(self, rewritten): + '''GLFileAssistant.setRewritten(rewritten) + + Set the name of the rewritten file which will be used.''' + if type(rewritten) is bytes or type(rewritten) is string: + if type(rewritten) is bytes: + rewritten = rewritten.decode(ENCS['default']) + else: # if rewritten has not bytes or string type + raise(TypeError( + 'rewritten must be a string, not %s' % type(rewritten).__name__)) + self.rewritten = rewritten + + def addFile(self, file): + '''GLFileAssistant.addFile(file) + + Add file to the list of added files.''' + if file not in self.added: + self.added += [file] + + def removeFile(self, file): + '''GLFileAssistant.removeFile(file) + + Remove file from the list of added files.''' + if file in self.added: + self.added.pop(file) + + def getFiles(self): + '''Return list of the added files.''' + return(list(self.added)) + + def add(self, lookedup, tmpflag, tmpfile): + '''GLFileAssistant.add(lookedup, tmpflag, tmpfile) + + This method copies a file from gnulib into the destination directory. + The destination is known to exist. If tmpflag is True, then lookedup file + is a temporary one.''' + original = self.original + rewritten = self.rewritten + destdir = self.config['destdir'] + symbolic = self.config['symbolic'] + lsymbolic = self.config['lsymbolic'] + if original == None: + raise(TypeError('original must be set before applying the method')) + elif rewritten == None: + raise(TypeError('rewritten must be set before applying the method')) + if not self.config['dryrun']: + print('Copying file %s' % rewritten) + loriginal = joinpath(self.config['localdir'], original) + if (symbolic or (lsymbolic and lookedup == loriginal)) \ + and not tmpflag and filecmp.cmp(lookedup, tmpfile): + constants.link_if_changed(lookedup, joinpath(destdir, rewritten)) + else: # if any of these conditions is not met + try: # Try to move file + shutil.move(tmpfile, joinpath(destdir, rewritten)) + except Exception as error: + raise(GLError(17, original)) + else: # if self.config['dryrun'] + print('Copy file %s' % rewritten) + + def update(self, lookedup, tmpflag, tmpfile, already_present): + '''GLFileAssistant.update(lookedup, tmpflag, tmpfile, already_present) + + This method copies a file from gnulib into the destination directory. + The destination is known to exist. If tmpflag is True, then lookedup file + is a temporary one.''' + original = self.original + rewritten = self.rewritten + destdir = self.config['destdir'] + symbolic = self.config['symbolic'] + lsymbolic = self.config['lsymbolic'] + if original == None: + raise(TypeError('original must be set before applying the method')) + elif rewritten == None: + raise(TypeError('rewritten must be set before applying the method')) + if type(lookedup) is bytes or type(lookedup) is string: + if type(lookedup) is bytes: + lookedup = lookedup.decode(ENCS['default']) + else: # if lookedup has not bytes or string type + raise(TypeError('lookedup must be a string, not %s' % \ + type(lookedup).__name__)) + if type(already_present) is not bool: + raise(TypeError('already_present must be a bool, not %s' % \ + type(already_present).__name__)) + basename = rewritten + backupname = string('%s~' % basename) + basepath = joinpath(destdir, basename) + backuppath = joinpath(destdir, backupname) + if not filecmp.cmp(basepath, tmpfile): + if not self.config['dryrun']: + if already_present: + print('Updating file %s (backup in %s)' % (basename, backupname)) + else: # if not already_present + message = 'Replacing file ' + message += '%s (non-gnulib code backed up in ' % basename + message += '%s) !!' % backupname + print(message) + if isfile(backuppath): + os.remove(backuppath) + try: # Try to replace the given file + shutil.move(basepath, backuppath) + except Exception as error: + raise(GLError(17, original)) + loriginal = joinpath(self.config['localdir'], original) + if (symbolic or (lsymbolic and lookedup == loriginal)) \ + and not tmpflag and filecmp.cmp(lookedup, tmpfile): + constants.link_if_changed(lookedup, basepath) + else: # if any of these conditions is not met + try: # Try to move file + if exist(basepath): + os.remove(basepath) + shutil.move(tmpfile, rewritten) + except Exception as error: + raise(GLError(17, original)) + else: # if self.config['dryrun'] + if already_present: + print('Update file %s (backup in %s)' % (rewritten, backup)) + else: # if not already_present + print('Replace file %s (backup in %s)' % (rewritten, backup)) + + def add_or_update(self, already_present): + '''GLFileAssistant.add_or_update(already_present) + + This method handles a file that ought to be present afterwards.''' + original = self.original + rewritten = self.rewritten + if original == None: + raise(TypeError('original must be set before applying the method')) + elif rewritten == None: + raise(TypeError('rewritten must be set before applying the method')) + if type(already_present) is not bool: + raise(TypeError('already_present must be a bool, not %s' % \ + type(already_present).__name__)) + xoriginal = original + if original.startswith('tests=lib/'): + xoriginal = constants.substart('tests=lib/', 'lib/', original) + lookedup, tmpflag = self.filesystem.lookup(xoriginal) + tmpfile = self.tmpfilename(rewritten) + sed_transform_lib_file = self.transformers.get('lib', '') + sed_transform_build_aux_file = self.transformers.get('aux', '') + sed_transform_main_lib_file = self.transformers.get('main', '') + sed_transform_testsrelated_lib_file = self.transformers.get('tests', '') + try: # Try to copy lookedup file to tmpfile + shutil.copy(lookedup, tmpfile) + except Exception as error: + raise(GLError(15, lookedup)) + transformer = string() + if original.startswith('lib/'): + if sed_transform_main_lib_file: + transformer = sed_transform_main_lib_file + elif original.startswith('build-aux/'): + if sed_transform_build_aux_file: + transformer = sed_transform_build_aux_file + elif original.startswith('tests=lib/'): + if sed_transform_testsrelated_lib_file: + transformer = sed_transform_testsrelated_lib_file + if transformer: + args = ['sed', '-e', transformer] + stdin = codecs.open(lookedup, 'rb', 'UTF-8') + try: # Try to transform file + data = sp.check_output(args, stdin=stdin, shell=False) + data = data.decode(ENCS['shell']) + except Exception as error: + raise(GLError(16, lookedup)) + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(data) + path = joinpath(self.config['destdir'], rewritten) + if isfile(path): + self.update(lookedup, tmpflag, tmpfile, already_present) + os.remove(tmpfile) + else: # if not isfile(path) + self.add(lookedup, tmpflag, tmpfile) + self.addFile(rewritten) + + def super_update(self, basename, tmpfile): + '''GLFileAssistant.super_update(basename, tmpfile) -> tuple + + Move tmpfile to destdir/basename path, making a backup of it. + Returns tuple, which contains basename, backupname and status. + 0: tmpfile is the same as destfile; + 1: tmpfile was used to update destfile; + 2: destfile was created, because it didn't exist.''' + backupname = '%s~' % basename + basepath = joinpath(self.config['destdir'], basename) + backuppath = joinpath(self.config['destdir'], backupname) + if isfile(basepath): + if filecmp.cmp(basepath, tmpfile): + result_flag = 0 + else: # if not filecmp.cmp(basepath, tmpfile) + result_flag = 1 + if not self.config['dryrun']: + if isfile(backuppath): + os.remove(backuppath) + shutil.move(basepath, backuppath) + shutil.move(tmpfile, basepath) + else: # if self.config['dryrun'] + os.remove(tmpfile) + else: # if not isfile(basepath) + result_flag = 2 + if not self.config['dryrun']: + if isfile(basepath): + os.remove(basepath) + shutil.move(tmpfile, basepath) + else: # if self.config['dryrun'] + os.remove(tmpfile) + result = tuple([basename, backupname, result_flag]) + return(result) + diff --git a/pygnulib/GLFileSystem.pyc b/pygnulib/GLFileSystem.pyc new file mode 100644 index 000000000..dabfed895 Binary files /dev/null and b/pygnulib/GLFileSystem.pyc differ diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py new file mode 100644 index 000000000..c95236962 --- /dev/null +++ b/pygnulib/GLImport.py @@ -0,0 +1,1423 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import locale +import codecs +import shutil +import filecmp +import subprocess as sp +from . import constants +from .GLError import GLError +from .GLConfig import GLConfig +from .GLModuleSystem import GLModule +from .GLModuleSystem import GLModuleTable +from .GLModuleSystem import GLModuleSystem +from .GLFileSystem import GLFileSystem +from .GLFileSystem import GLFileAssistant +from .GLMakefileTable import GLMakefileTable +from .GLEmiter import GLEmiter + + +#=============================================================================== +# 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 +FILES = constants.FILES +MODES = constants.MODES +TESTS = constants.TESTS +compiler = constants.compiler +joinpath = constants.joinpath +cleaner = constants.cleaner +relpath = constants.relativize +string = constants.string +isabs = os.path.isabs +isdir = os.path.isdir +isfile = os.path.isfile +normpath = os.path.normpath + + +#=============================================================================== +# Define GLImport class +#=============================================================================== +class GLImport(object): + '''GLImport class is used to provide methods for --import, --add-import, + --remove-import and --update actions. This is a high-level class, so + developers may have to use lower-level classes to create their own + scripts. However, if user needs just to use power of gnulib-tool, this class + is a very good choice.''' + + def __init__(self, config, mode): + '''Create GLImport instance. + The first variable, mode, must be one of the values of the MODES dict + object, which is accessible from constants module. The second one, config, + must be a GLConfig object.''' + if type(config) is not GLConfig: + raise(TypeError('config must have GLConfig type, not %s' % \ + repr(config))) + if type(mode) is int and \ + MODES['import'] <= mode <= MODES['update']: + self.mode = mode + else: # if mode is not int or is not 0-3 + raise(TypeError('mode must be 0 <= mode <= 3, not %s' % \ + repr(mode))) + + # Initialize some values. + self.cache = GLConfig() + self.config = config.copy() + os.rmdir(self.cache['tempdir']) + + # Get cached auxdir and libtool from configure.ac/in. + self.cache.setAuxDir('.') + path = joinpath(self.config['destdir'], 'configure.ac') + if not isfile(path): + path = joinpath(self.config['destdir'], 'configure.in') + if not isfile(path): + raise(GLError(3, path)) + self.config.setAutoconfFile(path) + with codecs.open(path, 'rb', 'UTF-8') as file: + data = file.read() + pattern = compiler(r'^AC_CONFIG_AUX_DIR\((.*?)\)$', re.S | re.M) + result = cleaner(pattern.findall(data))[0] + self.cache.setAuxDir(joinpath(result, self.config['destdir'])) + pattern = compiler(r'A[CM]_PROG_LIBTOOL', re.S | re.M) + guessed_libtool = bool(pattern.findall(data)) + if self.config['auxdir'] == None: + self.config.setAuxDir(self.cache['auxdir']) + + # Guess autoconf version. + pattern = compiler('.*AC_PREREQ\((.*?)\)', re.S | re.M) + versions = cleaner(pattern.findall(data)) + if versions: + version = sorted(set([float(version) for version in versions]))[-1] + self.config.setAutoconfVersion(version) + if version < 2.59: + raise(GLError(4, version)) + + # Get other cached variables. + path = joinpath(self.config['m4base'], 'gnulib-cache.m4') + if isfile(joinpath(self.config['m4base'], 'gnulib-cache.m4')): + with codecs.open(path, 'rb', 'UTF-8') as file: + data = file.read() + + # Create regex object and keys. + pattern = compiler('^(gl_.*?)\\((.*?)\\)$', re.S | re.M) + keys = \ + [ + 'gl_LOCAL_DIR', 'gl_MODULES', 'gl_AVOID', 'gl_SOURCE_BASE', + 'gl_M4_BASE', 'gl_PO_BASE', 'gl_DOC_BASE', 'gl_TESTS_BASE', + 'gl_MAKEFILE_NAME', 'gl_MACRO_PREFIX', 'gl_PO_DOMAIN', + 'gl_WITNESS_C_MACRO', 'gl_VC_FILES', 'gl_LIB', + ] + + # Find bool values. + if 'gl_LGPL(' in data: + keys.append('gl_LGPL') + self.cache.setLGPL(True) + if 'gl_LIBTOOL' in data: + self.cache.enableLibtool() + data = data.replace('gl_LIBTOOL', '') + if 'gl_CONDITIONAL_DEPENDENCIES' in data: + self.cache.enableCondDeps() + data = data.replace('gl_CONDITIONAL_DEPENDENCIES', '') + if 'gl_VC_FILES' in data: + self.cache.enableVCFiles() + data = data.replace('gl_VC_FILES', '') + if 'gl_WITH_TESTS' in data: + self.cache.enableTestFlag(TESTS['tests']) + data = data.replace('gl_WITH_TESTS', '') + if 'gl_WITH_OBSOLETE' in data: + self.cache.enableTestFlag(TESTS['obsolete']) + data = data.replace('gl_WITH_OBSOLETE', '') + if 'gl_WITH_CXX_TESTS' in data: + self.cache.enableTestFlag(TESTS['c++-test']) + data = data.replace('gl_WITH_CXX_TESTS', '') + if 'gl_WITH_LONGRUNNING_TESTS' in data: + self.cache.enableTestFlag(TESTS['longrunning-test']) + data = data.replace('gl_WITH_LONGRUNNING_TESTS', '') + if 'gl_WITH_PRIVILEGED_TESTS' in data: + self.cache.enableTestFlag(TESTS['privileged-test']) + data = data.replace('gl_WITH_PRIVILEGED_TESTS', '') + if 'gl_WITH_UNPORTABLE_TESTS' in data: + self.cache.enableTestFlag(TESTS['unportable-test']) + data = data.replace('gl_WITH_UNPORTABLE_TESTS', '') + if 'gl_WITH_ALL_TESTS' in data: + self.cache.enableTestFlag(TESTS['all-test']) + data = data.replace('gl_WITH_ALL_TESTS', '') + # Find string values + result = dict(pattern.findall(data)) + values = cleaner([result.get(key, '') for key in keys]) + tempdict = dict(zip(keys, values)) + if 'gl_LGPL' in tempdict: + lgpl = cleaner(tempdict['gl_LGPL']) + if lgpl.isdecimal(): + self.cache.setLGPL(int(self.cache['lgpl'])) + else: # if 'gl_LGPL' not in tempdict + self.cache.setLGPL(False) + if tempdict['gl_LIB']: + self.cache.setLibName(cleaner(tempdict['gl_LIB'])) + if tempdict['gl_LOCAL_DIR']: + self.cache.setLocalDir(cleaner(tempdict['gl_LOCAL_DIR'])) + if tempdict['gl_MODULES']: + self.cache.setModules(cleaner(tempdict['gl_MODULES'].split())) + if tempdict['gl_AVOID']: + self.cache.setAvoids(cleaner(tempdict['gl_AVOID'].split())) + if tempdict['gl_SOURCE_BASE']: + self.cache.setSourceBase(cleaner(tempdict['gl_SOURCE_BASE'])) + if tempdict['gl_M4_BASE']: + self.cache.setM4Base(cleaner(tempdict['gl_M4_BASE'])) + if tempdict['gl_PO_BASE']: + self.cache.setPoBase(cleaner(tempdict['gl_PO_BASE'])) + if tempdict['gl_DOC_BASE']: + self.cache.setDocBase(cleaner(tempdict['gl_DOC_BASE'])) + if tempdict['gl_TESTS_BASE']: + self.cache.setTestsBase(cleaner(tempdict['gl_TESTS_BASE'])) + if tempdict['gl_MAKEFILE_NAME']: + self.cache.setMakefile(cleaner(tempdict['gl_MAKEFILE_NAME'])) + if tempdict['gl_MACRO_PREFIX']: + self.cache.setMacroPrefix(cleaner(tempdict['gl_MACRO_PREFIX'])) + if tempdict['gl_PO_DOMAIN']: + self.cache.setPoDomain(cleaner(tempdict['gl_PO_DOMAIN'])) + if tempdict['gl_WITNESS_C_MACRO']: + self.cache.setWitnessCMacro(cleaner(tempdict['gl_WITNESS_C_MACRO'])) + + # Get cached filelist from gnulib-comp.m4. + destdir, m4base = self.config.getDestDir(), self.config.getM4Base() + path = joinpath(destdir, m4base, 'gnulib-comp.m4') + if isfile(path): + with codecs.open(path, 'rb', 'UTF-8') as file: + data = file.read() + regex = 'AC_DEFUN\\(\\[%s_FILE_LIST\\], \\[(.*?)\\]\\)' % \ + self.cache['macro_prefix'] + pattern = compiler(regex, re.S | re.M) + self.cache.setFiles(pattern.findall(data)[-1].strip().split()) + + # The self.config['localdir'] defaults to the cached one. Recall that the + # cached one is relative to $destdir, whereas the one we use is relative + # to . or absolute. + if not self.config['localdir']: + if self.cache['localdir']: + if isabs(self.config['destdir']): + localdir = joinpath(self.config['destdir'], self.cache['localdir']) + else: # if not isabs(self.config['destdir']) + if isabs(self.cache['localdir']): + localdir = joinpath(self.config['destdir'], self.cache['localdir']) + else: # if not isabs(self.cache['localdir']) + # NOTE: I NEED TO IMPLEMENT RELATIVE_CONCAT + localdir = os.path.relpath(joinpath(self.config['destdir'], + self.cache['localdir'])) + self.config.setLocalDir(localdir) + + if self.mode != MODES['import']: + if self.cache['m4base'] and \ + (self.config['m4base'] != self.cache['m4base']): + raise(GLError(5, m4base)) + + # Perform actions with modules. In --add-import, append each given module + # to the list of cached modules; in --remove-import, remove each given + # module from the list of cached modules; in --update, simply set + # self.config['modules'] to its cached version. + new, old = self.config.getModules(), self.cache.getModules() + if self.mode == MODES['add-import']: + modules = sorted(set(new +old)) + elif self.mode == MODES['remove-import']: + modules = [module for module in old if module in new] + elif self.mode == MODES['update']: + modules = self.cache.getModules() + + # If user tries to apply conddeps and testflag['tests'] together. + if self.config['tests'] and self.config['conddeps']: + raise(GLError(10, None)) + + # Update configuration dictionary. + self.config.update(self.cache) + for key in config.keys(): + value = config[key] + if not config.isdefault(key, value): + self.config.update_key(config, key) + self.config.setModules(modules) + + # Check if conddeps is enabled together with inctests. + inctests = self.config.checkTestFlag(TESTS['tests']) + if self.config['conddeps'] and inctests: + raise(GLError(10, None)) + + # Define GLImport attributes. + self.emiter = GLEmiter(self.config) + self.filesystem = GLFileSystem(self.config) + self.modulesystem = GLModuleSystem(self.config) + self.moduletable = GLModuleTable(self.config, list()) + self.makefiletable = GLMakefileTable(self.config) + + def __repr__(self): + '''x.__repr__ <==> repr(x)''' + result = '' % hex(id(self)) + return(result) + + def rewrite_old_files(self, files): + '''GLImport.rewrite_old_files(files) -> list + + Replace auxdir, docbase, sourcebase, m4base and testsbase from default + to their version from cached config.''' + if type(files) is not list: + raise(TypeError( + 'files argument must has list type, not %s' % type(files).__name__)) + files = \ + [ # Begin to convert bytes to string + file.decode(ENCS['default']) \ + if type(file) is bytes else file \ + for file in files + ] # Finish to convert bytes to string + for file in files: + if type(file) is not string: + raise(TypeError('each file must be a string instance')) + files = sorted(set(files)) + files = ['%s%s' % (file, os.path.sep) for file in files] + auxdir = self.cache['auxdir'] + docbase = self.cache['docbase'] + sourcebase = self.cache['sourcebase'] + m4base = self.cache['m4base'] + testsbase = self.cache['testsbase'] + result = list() + for file in files: + if file.startswith('build-aux/'): + path = constants.substart('build-aux/', '%s/' % auxdir, file) + elif file.startswith('doc/'): + path = constants.substart('doc/', '%s/' % docbase, file) + elif file.startswith('lib/'): + path = constants.substart('lib/', '%s/' % sourcebase, file) + elif file.startswith('m4/'): + path = constants.substart('m4/', '%s/' % m4base, file) + elif file.startswith('tests/'): + path = constants.substart('tests/', '%s/' % testsbase, file) + elif file.startswith('tests=lib/'): + path = constants.substart('tests=lib/', '%s/' % testsbase, file) + elif file.startswith('top/'): + path = constants.substart('top/', '', file) + else: # file is not a special file + path = file + result += [os.path.normpath(path)] + result = sorted(set(result)) + return(list(result)) + + def rewrite_new_files(self, files): + '''GLImport.rewrite_new_files(files) + + Replace auxdir, docbase, sourcebase, m4base and testsbase from default + to their version from config.''' + if type(files) is not list: + raise(TypeError( + 'files argument must has list type, not %s' % type(files).__name__)) + files = \ + [ # Begin to convert bytes to string + file.decode(ENCS['default']) \ + if type(file) is bytes else file \ + for file in files + ] # Finish to convert bytes to string + for file in files: + if type(file) is not string: + raise(TypeError('each file must be a string instance')) + files = sorted(set(files)) + auxdir = self.config['auxdir'] + docbase = self.config['docbase'] + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + testsbase = self.config['testsbase'] + result = list() + for file in files: + if file.startswith('build-aux/'): + path = constants.substart('build-aux/', '%s/' % auxdir, file) + elif file.startswith('doc/'): + path = constants.substart('doc/', '%s/' % docbase, file) + elif file.startswith('lib/'): + path = constants.substart('lib/', '%s/' % sourcebase, file) + elif file.startswith('m4/'): + path = constants.substart('m4/', '%s/' % m4base, file) + elif file.startswith('tests/'): + path = constants.substart('tests/', '%s/' % testsbase, file) + elif file.startswith('tests=lib/'): + path = constants.substart('tests=lib/', '%s/' % testsbase, file) + elif file.startswith('top/'): + path = constants.substart('top/', '', file) + else: # file is not a special file + path = file + result += [os.path.normpath(path)] + result = sorted(set(result)) + return(list(result)) + + def actioncmd(self): + '''Return command-line invocation comment.''' + modules = self.config.getModules() + avoids = self.config.getAvoids() + destdir = self.config.getDestDir() + localdir = self.config.getLocalDir() + auxdir = self.config.getAuxDir() + sourcebase = self.config.getSourceBase() + m4base = self.config.getM4Base() + docbase = self.config.getDocBase() + pobase = self.config.getPoBase() + testsbase = self.config.getTestsBase() + testflags = self.config.getTestFlags() + conddeps = self.config.checkCondDeps() + libname = self.config.getLibName() + lgpl = self.config.getLGPL() + makefile = self.config.getMakefile() + libtool = self.config.checkLibtool() + macro_prefix = self.config.getMacroPrefix() + witness_c_macro = self.config.getWitnessCMacro() + podomain = self.config.getPoDomain() + vc_files = self.config.checkVCFiles() + verbose = self.config.getVerbosity() + + # Create command-line invocation comment. + actioncmd = 'gnulib-tool --import' + actioncmd += ' --dir=%s' % destdir + if localdir: + actioncmd += ' --local-dir=%s' % localdir + actioncmd += ' --lib=%s' % libname + actioncmd += ' --source-base=%s' % sourcebase + actioncmd += ' --m4-base=%s' % m4base + if pobase: + actioncmd += ' --po-base=%s' % pobase + actioncmd += ' --doc-base=%s' % docbase + actioncmd += ' --tests-base=%s' % testsbase + actioncmd += ' --aux-dir=%s' % auxdir + if self.config.checkTestFlag(TESTS['tests']): + actioncmd += ' --with-tests' + if self.config.checkTestFlag(TESTS['obsolete']): + actioncmd += ' --with-obsolete' + if self.config.checkTestFlag(TESTS['c++-test']): + actioncmd += ' --with-c++-tests' + if self.config.checkTestFlag(TESTS['longrunning-test']): + actioncmd += ' --with-longrunning-tests' + if self.config.checkTestFlag(TESTS['privileged-test']): + actioncmd += ' --with-privileged-test' + if self.config.checkTestFlag(TESTS['unportable-test']): + actioncmd += ' --with-unportable-tests' + if self.config.checkTestFlag(TESTS['all-test']): + actioncmd += ' --with-all-tests' + for module in avoids: + actioncmd += ' --avoid=%s' % module + if lgpl: + if lgpl == True: + actioncmd += ' --lgpl' + else: # if lgpl != True + actioncmd += ' --lgpl=%s' % lgpl + if makefile: + actioncmd += ' --makefile-name=%s' % makefile + if conddeps: + actioncmd += ' --conditional-dependencies' + else: # if not conddeps + actioncmd += ' --no-conditional-dependencies' + if libtool: + actioncmd += ' --libtool' + else: # if not libtool + actioncmd += ' --no-libtool' + actioncmd += ' --macro-prefix=%s' % macro_prefix + if podomain: + actioncmd = ' --podomain=%s' % podomain + if witness_c_macro: + actioncmd += ' --witness_c_macro=%s' % witness_c_macro + if vc_files == True: + actioncmd += ' --vc-files' + elif vc_files == False: + actioncmd += ' --no-vc-files' + actioncmd += ' ' # Add a space + actioncmd += ' '.join(modules) + return(actioncmd) + + def gnulib_cache(self): + '''GLImport.gnulib_cache() -> string + + Emit the contents of generated $m4base/gnulib-cache.m4 file. + GLConfig: destdir, localdir, tests, sourcebase, m4base, pobase, docbase, + testsbase, conddeps, libtool, macro_prefix, podomain, vc_files.''' + emit = string() + moduletable = self.moduletable + actioncmd = self.actioncmd() + destdir = self.config['destdir'] + localdir = self.config['localdir'] + testflags = list(self.config['testflags']) + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + pobase = self.config['pobase'] + docbase = self.config['docbase'] + testsbase = self.config['testsbase'] + lgpl = self.config['lgpl'] + libname = self.config['libname'] + makefile = self.config['makefile'] + conddeps = self.config['conddeps'] + libtool = self.config['libtool'] + macro_prefix = self.config['macro_prefix'] + podomain = self.config['podomain'] + witness_c_macro = self.config['witness_c_macro'] + vc_files = self.config['vc_files'] + modules = [str(module) for module in moduletable['base']] + avoids = [str(avoid) for avoid in moduletable['avoids']] + emit += self.emiter.copyright_notice() + emit += '''# +# This file represents the specification of how gnulib-tool is used. +# It acts as a cache: It is written and read by gnulib-tool. +# In projects that use version control, this file is meant to be put under +# version control, like the configure.ac and various Makefile.am files. + + +# Specification in the form of a command-line invocation: +# %s + +# Specification in the form of a few \ +gnulib-tool.m4 macro invocations:\n''' % actioncmd + if not localdir or localdir.startswith('/'): + relative_localdir = localdir + else: # if localdir or not localdir.startswith('/') + relative_localdir = constants.relativize(destdir, localdir) + emit += 'gl_LOCAL_DIR([%s])\n' % relative_localdir + emit += 'gl_MODULES([\n' + emit += ' %s\n' % '\n '.join(modules) + emit += '])\n' + if self.config.checkTestFlag(TESTS['obsolete']): + emit += 'gl_WITH_OBSOLETE\n' + if self.config.checkTestFlag(TESTS['cxx-tests']): + emit += 'gl_WITH_CXX_TESTS\n' + if self.config.checkTestFlag(TESTS['privileged-tests']): + emit += 'gl_WITH_PRIVILEGED_TESTS\n' + if self.config.checkTestFlag(TESTS['unportable-tests']): + emit += 'gl_WITH_UNPORTABLE_TESTS\n' + if self.config.checkTestFlag(TESTS['all-tests']): + emit += 'gl_WITH_ALL_TESTS\n' + emit += 'gl_AVOID([%s])\n' % ' '.join(avoids) + emit += 'gl_SOURCE_BASE([%s])\n' % sourcebase + emit += 'gl_M4_BASE([%s])\n' % m4base + emit += 'gl_PO_BASE([%s])\n' % pobase + emit += 'gl_DOC_BASE([%s])\n' % docbase + emit += 'gl_TESTS_BASE([%s])\n' % testsbase + if self.config.checkTestFlag(TESTS['tests']): + emit += 'gl_WITH_TESTS\n' + emit += 'gl_LIB([%s])\n' % libname + if lgpl != False: + if lgpl == True: + emit += 'gl_LGPL\n' + else: # if lgpl != True + emit += 'gl_LGPL([%d])\n' % lgpl + emit += 'gl_MAKEFILE_NAME([%s])\n' % makefile + if conddeps: + emit += 'gl_CONDITIONAL_DEPENDENCIES\n' + if libtool: + emit += 'gl_LIBTOOL\n' + emit += 'gl_MACRO_PREFIX([%s])\n' % macro_prefix + emit += 'gl_PO_DOMAIN([%s])\n' % podomain + emit += 'gl_WITNESS_C_DOMAIN([%s])\n' % witness_c_macro + if vc_files: + emit += 'gl_VC_FILES([%s])\n' % vc_files + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(constants.nlconvert(emit)) + + def gnulib_comp(self, files): + '''GLImport.gnulib_comp(files) -> string + + Emit the contents of generated $m4base/gnulib-comp.m4 file. + GLConfig: destdir, localdir, tests, sourcebase, m4base, pobase, docbase, + testsbase, conddeps, libtool, macro_prefix, podomain, vc_files.''' + emit = string() + assistant = self.assistant + moduletable = self.moduletable + destdir = self.config['destdir'] + localdir = self.config['localdir'] + auxdir = self.config['auxdir'] + testflags = list(self.config['testflags']) + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + pobase = self.config['pobase'] + docbase = self.config['docbase'] + testsbase = self.config['testsbase'] + lgpl = self.config['lgpl'] + libname = self.config['libname'] + makefile = self.config['makefile'] + conddeps = self.config['conddeps'] + libtool = self.config['libtool'] + macro_prefix = self.config['macro_prefix'] + podomain = self.config['podomain'] + witness_c_macro = self.config['witness_c_macro'] + configure_ac = self.config['configure_ac'] + vc_files = self.config['vc_files'] + libtests = self.config['libtests'] + modules = [str(module) for module in moduletable['base']] + avoids = [str(avoid) for avoid in moduletable['avoids']] + emit += '# DO NOT EDIT! GENERATED AUTOMATICALLY!\n' + emit += self.emiter.copyright_notice() + emit += '''# +# This file represents the compiled summary of the specification in +# gnulib-cache.m4. It lists the computed macro invocations that need +# to be invoked from configure.ac. +# In projects that use version control, this file can be treated like +# other built files. + + +# This macro should be invoked from %s, in the section +# "Checks for programs", right after AC_PROG_CC, and certainly before +# any checks for libraries, header files, types and library functions. +AC_DEFUN([%s_EARLY], +[ + m4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace + m4_pattern_allow([^gl_ES$])dnl a valid locale name + m4_pattern_allow([^gl_LIBOBJS$])dnl a variable + m4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable + AC_REQUIRE([gl_PROG_AR_RANLIB])\n''' % (configure_ac, macro_prefix) + uses_subdirs = False + for module in moduletable['main']: + # Test whether there are some source files in subdirectories. + for file in module.getFiles(): + if file.startswith('lib/') and file.endswith('.c') and \ + file.count('/') > 1: + uses_subdirs = True + break + if uses_subdirs: + emit += ' AC_REQUIRE([AM_PROG_CC_C_O])\n' + for module in moduletable['final']: + emit += ' # Code from module %s:\n' % str(module) + snippet = module.getAutoconfSnippet_Early() + lines = [line for line in snippet.split(constants.NL) if line != ''] + if lines: + emit += ' %s\n' % '\n '.join(lines) + emit += '])\n' + emit += ''' +# This macro should be invoked from %s, in the section +# "Check for header files, types and library functions". +AC_DEFUN([%s_INIT], +[\n''' % (configure_ac, macro_prefix) + if libtool: + emit += ' AM_CONDITIONAL([GL_COND_LIBTOOL], [true])\n' + emit += ' gl_cond_libtool=true\n' + else: # if not libtool + emit += ' AM_CONDITIONAL([GL_COND_LIBTOOL], [false])\n' + emit += ' gl_cond_libtool=false\n' + emit += ' gl_libdeps=\n' + emit += ' gl_ltlibdeps=\n' + replace_auxdir = False + if auxdir != 'build-aux': + replace_auxdir = True + emit += ' gl_m4_base=\'%s\'\n' % m4base + emit += self.emiter.initmacro_start(macro_prefix) + emit += ' gl_source_base=\'%s\'\n' % sourcebase + if witness_c_macro: + emit += ' m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [%s])\n' % \ + witness_c_macro + # Emit main autoconf snippets. + emit += self.emiter.autoconfSnippets(moduletable['main'], + moduletable, assistant, 0, True, False, True, replace_auxdir) + if witness_c_macro: + emit += ' m4_popdef([gl_MODULE_INDICATOR_CONDITION])\n' + emit += ' # End of code from modules\n' + emit += self.emiter.initmacro_end(macro_prefix) + emit += ' gltests_libdeps=\n' + emit += ' gltests_ltlibdeps=\n' + emit += self.emiter.initmacro_start('%stests' % macro_prefix) + emit += ' gl_source_base=\'%s\'\n' % testsbase + # Define a tests witness macro that depends on the package. + # PACKAGE is defined by AM_INIT_AUTOMAKE, PACKAGE_TARNAME is defined by + # AC_INIT. + # See . + emit += 'changequote(,)dnl\n' + emit += ' %stests_WITNESS=' % macro_prefix + emit += 'IN_`echo "${PACKAGE-$PACKAGE_TARNAME}" | LC_ALL=C tr \ +abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | LC_ALL=C sed -e \ +\'s/[^A-Z0-9_]/_/g\'`_GNULIB_TESTS\n' + emit += 'changequote([, ])dnl\n' + emit += ' AC_SUBST([%stests_WITNESS])\n' % macro_prefix + emit += ' gl_module_indicator_condition=$%stests_WITNESS\n' % macro_prefix + emit += ' m4_pushdef([gl_MODULE_INDICATOR_CONDITION], ' + emit += '[$gl_module_indicator_condition])\n' + # Emit tests autoconf snippets. + emit += self.emiter.autoconfSnippets(moduletable['tests'], + moduletable, assistant, 0, True, True, True, replace_auxdir) + emit += ' m4_popdef([gl_MODULE_INDICATOR_CONDITION])\n' + emit += self.emiter.initmacro_end('%stests' % macro_prefix) + # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is + # created using libtool, because libtool already handles the dependencies. + if not libtool: + libname_upper = libname.upper().replace('-', '_') + emit += ' %s_LIBDEPS="$gl_libdeps"\n' % libname_upper + emit += ' AC_SUBST([%s_LIBDEPS])\n' % libname_upper + emit += ' %s_LTLIBDEPS="$gl_ltlibdeps"\n' % libname_upper + emit += ' AC_SUBST([%s_LTLIBDEPS])\n' % libname_upper + if libtests: + emit += ' LIBTESTS_LIBDEPS="$gltests_libdeps"\n' + emit += ' AC_SUBST([LIBTESTS_LIBDEPS])\n' + emit += '])\n' + emit += self.emiter.initmacro_done(macro_prefix, sourcebase) + emit += self.emiter.initmacro_done('%stests' % macro_prefix, testsbase) + emit += ''' +# This macro records the list of files which have been installed by +# gnulib-tool and may be removed by future gnulib-tool invocations. +AC_DEFUN([%s_FILE_LIST], [\n''' % macro_prefix + emit += ' %s\n' % '\n '.join(files) + emit += '])\n' + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + return(emit) + + def _done_dir_(self, directory, dirs_added, dirs_removed): + '''GLImport._done_dir_(directory, dirs_added, dirs_removed) + + This method is used to determine ignore argument for _update_ignorelist_ + method and then call it.''' + destdir = self.config['destdir'] + if isdir(joinpath(destdir, 'CVS')) or \ + isdir(joinpath(destdir, directory, 'CVS')) or \ + isfile(joinpath(destdir, directory, '.cvsignore')): + self._update_ignorelist_(directory, '.cvsignore', + dirs_added, dirs_removed) + if isdir(joinpath(destdir, '.git')) or \ + isfile(joinpath(destdir, directory, '.gitignore')): + self._update_ignorelist_(directory, '.gitignore', + dirs_added, dirs_removed) + + + def _update_ignorelist_(self, directory, ignore, dirs_added, dirs_removed): + '''GLImport._update_ignorelist_(directory, ignore, dirs_added, dirs_removed) + + Update .gitignore or .cvsignore files.''' + result = string() + destdir = self.config['destdir'] + if ignore == '.gitignore': + anchor = '/' + else: + anchor = '' + srcpath = joinpath(destdir, directory, ignore) + backupname = '%s~' % srcpath + if isfile(srcpath): + if dirs_added or dirs_removed: + with codecs.open(srcpath, 'rb', 'UTF-8') as file: + srcdata = file.read() + dirs_ignore = sorted(set(srcdata.split('\n'))) + dirs_ignore = [line for line in dirs_ignore if line.strip()] + srcdata = '\n'.join(sorted(set(dirs_ignore))).strip() + dirs_ignore += [d for d in dirs_added if d not in dirs_ignore] + dirs_ignore = [d for d in dirs_ignore if d in dirs_removed] + dirs_ignore = ['%s%s' % (anchor, d) for d in dirs_ignore] + dirs_ignore = sorted(set(dirs_ignore)) + destdata = '\n'.join(sorted(set(dirs_ignore))).strip() + if srcdata != destdata: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (srcpath, backupname)) + shutil.move(srcpath, backupname) + result = string() + with codecs.open(srcpath, 'wb', 'UTF-8') as file: + file.write(destdata) + else: # if self.config['dryrun'] + print('Updating %s (backup in %s)' % (srcpath, backupname)) + else: # if not isfile(srcpath) + if dirs_added: + if not self.config['dryrun']: + print('Creating %s' % srcpath) + dirs_added = sorted(set(dirs_added)) + dirs_added = ['%s%s' % (anchor, d) for d in dirs_added] + if ignore == '.cvsignore': + dirs_added = ['.deps', '.dirstamp'] +dirs_added + with codecs.open(srcpath, 'wb', 'UTF-8') as file: + file.write('\n'.join(dirs_added)) + file.write('\n') + else: # if self.config['dryrun'] + print('Create %s' % srcpath) + + def prepare(self): + '''Make all preparations before the execution of the code. + Returns filetable and sed transformers, which change the license.''' + destdir = self.config['destdir'] + localdir = self.config['localdir'] + auxdir = self.config['auxdir'] + modules = list(self.config['modules']) + avoids = list(self.config['avoids']) + testflags = list(self.config['testflags']) + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + pobase = self.config['pobase'] + docbase = self.config['docbase'] + testsbase = self.config['testsbase'] + lgpl = self.config['lgpl'] + copyrights = self.config['copyrights'] + libname = self.config['libname'] + makefile = self.config['makefile'] + conddeps = self.config['conddeps'] + libtool = self.config['libtool'] + macro_prefix = self.config['macro_prefix'] + podomain = self.config['podomain'] + witness_c_macro = self.config['witness_c_macro'] + vc_files = self.config['vc_files'] + configure_ac = self.config['configure_ac'] + ac_version = self.config['ac_version'] + verbose = self.config['verbosity'] + base_modules = sorted(set([self.modulesystem.find(m) for m in modules])) + avoids = sorted(set([self.modulesystem.find(a) for a in avoids])) + + # Perform transitive closure. + self.moduletable.setAvoids(avoids) + final_modules = self.moduletable.transitive_closure(base_modules) + + # Show final module list. + if verbose >= 0: + bold_on = '' + bold_off = '' + term = os.getenv('TERM') + if term == 'xterm': + bold_on = '\x1b[1m' + bold_off = '\x1b[0m' + print('Module list with included dependencies (indented):') + for module in final_modules: + if str(module) in self.config.getModules(): + print(' %s%s%s' % (bold_on, module, bold_off)) + else: # if str(module) not in self.config.getModules() + print(' %s' % module) + + # Separate modules into main_modules and tests_modules. + modules = self.moduletable.transitive_closure_separately( + base_modules, final_modules) + main_modules, tests_modules = modules + + # Transmit base_modules, final_modules, main_modules and tests_modules. + self.moduletable.setBaseModules(base_modules) + self.moduletable.setFinalModules(final_modules) + self.moduletable.setMainModules(main_modules) + self.moduletable.setTestsModules(tests_modules) + + # Print main_modules and tests_modules. + if verbose >= 1: + print('Main module list:') + for module in main_modules: + print(' %s' % str(module)) + print('Tests-related module list:') + for module in tests_modules: + print(' %s' % str(module)) + + # Determine whether a $testsbase/libtests.a is needed. + libtests = False + for module in tests_modules: + files = module.getFiles() + for file in files: + if file.startswith('lib/'): + libtests = True + break + if libtests: + self.config.enableLibtests() + + # Add dummy package if it is needed. + main_modules = self.moduletable.add_dummy(main_modules) + if libtests: # if we need to use libtests.a + tests_modules = self.moduletable.add_dummy(tests_modules) + + # Check license incompatibilities. + listing = list() + compatibilities = dict() + incompatibilities = string() + compatibilities['all'] = ['GPLed build tool', 'public domain', 'unlimited', + 'unmodifiable license text'] + compatibilities[3] = ['LGPL', 'LGPLv2+', 'LGPLv3+'] + compatibilities[2] = ['LGPLv2+'] + if lgpl: + for module in main_modules: + license = module.getLicense() + if license not in compatibilities['all']: + if lgpl == 3 or lgpl == True: + if license not in compatibilities[3]: + listing.append(tuple([str(module), license])) + elif lgpl == 2: + if license not in compatibilities[2]: + listing.append(tuple([str(module), license])) + if listing: + raise(GLError(11, listing)) + + # Print notices from modules. + for module in main_modules: + notice = module.getNotice() + notice = notice.strip() + if notice: + print('Notice from module %s:' % str(module)) + pattern = compiler('^(.*?)$', re.S | re.M) + notice = pattern.sub(' \\1', notice) + print(notice) + + # Determine script to apply to imported library files. + lgpl2gpl = ''' + s/GNU Lesser General/GNU General/g + s/Lesser General Public License/General Public License/g + s/GNU Library General/GNU General/g + s/Library General Public License/General Public License/g + s/version 2\\(.1\\)\\{0,1\\}\\([ ,]\\)/version 3\\2/g''' + sed_transform_lib_file = string() + if 'config-h' in [str(module) for module in main_modules]: + sed_transform_lib_file += ''' + s/^#ifdef[\t ]*HAVE_CONFIG_H[\t ]*$/#if 1/ + ''' + sed_transform_main_lib_file = sed_transform_lib_file + if copyrights: + if lgpl: # if lgpl is enabled + if lgpl == 3: + sed_transform_main_lib_file += ''' + s/GNU General/GNU Lesser General/g + s/General Public License/Lesser General Public License/g + s/Lesser Lesser General Public License/Lesser General Public''' \ + +' License/g' + elif lgpl == 2: + sed_transform_main_lib_file += ''' + s/GNU General/GNU Lesser General/g + s/General Public License/Lesser General Public License/g + s/Lesser Lesser General Public License/Lesser General Public''' \ + +'''License/g + s/version [23]\\([ ,]\\)/version 2.1\\1/g''' + else: # if lgpl is disabled + sed_transform_main_lib_file += lgpl2gpl + + # Determine script to apply to auxiliary files that go into $auxdir/. + sed_transform_build_aux_file = string() + if copyrights: + sed_transform_build_aux_file += lgpl2gpl + + # Determine script to apply to library files that go into $testsbase/. + sed_transform_testsrelated_lib_file = sed_transform_lib_file + if copyrights: + sed_transform_testsrelated_lib_file += lgpl2gpl + + # Determine the final file lists. + main_filelist, tests_filelist = \ + self.moduletable.filelist_separately(main_modules, tests_modules) + filelist = sorted(set(main_filelist +tests_filelist), key=string.lower) + if not filelist: + raise(GLError(12, None)) + + # Print list of files. + if verbose >= 0: + print('File list:') + for file in filelist: + if file.startswith('tests=lib/'): + rest = file[10:] + print(' lib/%s -> tests/%s' % (rest, rest)) + else: + print(' %s' % file) + + # Prepare basic filelist and basic old_files/new_files variables. + filelist = sorted(set(filelist)) + new_files = filelist +['m4/gnulib-tool.m4'] + old_files = list(self.cache['files']) + path = joinpath(destdir, m4base, 'gnulib-tool.m4') + if isfile(path): + old_files += [joinpath('m4', 'gnulib-tool.m4')] + + # Construct tables and transformers. + transformers = dict() + transformers['lib'] = string(sed_transform_lib_file) + transformers['aux'] = string(sed_transform_build_aux_file) + transformers['main'] = string(sed_transform_main_lib_file) + transformers['tests'] = string(sed_transform_testsrelated_lib_file) + old_table = list() + new_table = list() + for src in old_files: + dest = self.rewrite_old_files([src])[-1] + old_table += [tuple([dest, src])] + for src in new_files: + dest = self.rewrite_new_files([src])[-1] + new_table += [tuple([dest, src])] + old_table = sorted(set(old_table)) + new_table = sorted(set(new_table)) + + # Prepare the filetable. + filetable = dict() + filetable['all'] = sorted(set(filelist)) + filetable['old'] = \ + sorted(set(old_table), key=lambda t: tuple(t[0].lower())) + filetable['new'] = \ + sorted(set(new_table), key=lambda t: tuple(t[0].lower())) + filetable['added'] = list() + filetable['removed'] = list() + + # Return the result. + result = tuple([filetable, transformers]) + return(result) + + def execute(self, filetable, transformers): + '''Perform operations on the lists of files, which are given in a special + format except filelist argument. Such lists of files can be created using + GLImport.prepare() function.''' + if type(filetable) is not dict: + raise(TypeError('filetable must be a dict, not %s' % \ + type(filetable).__name__)) + for key in ['all', 'old', 'new', 'added', 'removed']: + if key not in filetable: + raise(KeyError('filetable must contain key %s' % repr(key))) + destdir = self.config['destdir'] + localdir = self.config['localdir'] + auxdir = self.config['auxdir'] + modules = list(self.config['modules']) + avoids = list(self.config['avoids']) + testflags = list(self.config['testflags']) + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + pobase = self.config['pobase'] + docbase = self.config['docbase'] + testsbase = self.config['testsbase'] + lgpl = self.config['lgpl'] + copyrights = self.config['copyrights'] + libname = self.config['libname'] + makefile = self.config['makefile'] + conddeps = self.config['conddeps'] + libtool = self.config['libtool'] + macro_prefix = self.config['macro_prefix'] + podomain = self.config['podomain'] + witness_c_macro = self.config['witness_c_macro'] + vc_files = self.config['vc_files'] + configure_ac = self.config['configure_ac'] + ac_version = self.config['ac_version'] + verbose = self.config['verbosity'] + actioncmd = self.actioncmd() + + # Create all necessary directories. + dirs = list() + if pobase: + dirs += [pobase] + if [file for file in filetable['all'] if file.startswith('doc/')]: + dirs += [docbase] + dirs += [sourcebase, m4base, auxdir] + dirs += [os.path.dirname(pair[0]) for pair in filetable['new']] + dirs = sorted(set([joinpath(destdir, d) for d in dirs])) + for directory in dirs: + if not isdir(directory): + print('Creating directory %s' % directory) + if not self.config['dryrun']: + try: # Try to create directory + os.makedirs(directory) + except Exception as error: + raise(GLError(13, directory)) + else: # if self.config['dryrun'] + print('Create directory %s' % directory) + + # Create GLFileAssistant instance to process files. + self.assistant = GLFileAssistant(self.config, transformers) + + # Files which are in filetable['old'] and not in filetable['new']. + # They will be removed and added to filetable['removed'] list. + pairs = [f for f in filetable['old'] if f not in filetable['old']] + pairs = sorted(set(pairs), key=lambda t: tuple(t[0].lower())) + files = sorted(set(pair[0] for pair in pairs)) + for file in files: + path = joinpath(destdir, file) + if isfile(path) or os.path.islink(path): + if not self.config['dryrun']: + backup = string('%s~' % path) + print('Removing file %s (backup in )' % (path, backup)) + try: # Try to move file + if os.path.exists(backup): + os.remove(backup) + shutil.move(path, '%s~' % path) + except Exception as error: + raise(GLError(14, file)) + else: # if self.config['dryrun'] + print('Remove file %s (backup in %s~)' % (path, path)) + filetable['removed'] += [file] + + # Files which are in filetable['new'] and not in filetable['old']. + # They will be added/updated and added to filetable['added'] list. + already_present = False + pairs = [f for f in filetable['new'] if f not in filetable['old']] + pairs = sorted(set(pairs)) + for pair in pairs: + original = pair[1] + rewritten = pair[0] + self.assistant.setOriginal(original) + self.assistant.setRewritten(rewritten) + self.assistant.add_or_update(already_present) + + # Files which are in filetable['new'] and in filetable['old']. + # They will be added/updated and added to filetable['added'] list. + already_present = True + pairs = [f for f in filetable['new'] if f in filetable['old']] + pairs = sorted(set(pairs)) + for pair in pairs: + original = pair[1] + rewritten = pair[0] + self.assistant.setOriginal(original) + self.assistant.setRewritten(rewritten) + self.assistant.add_or_update(already_present) + + # Add files which were added to the list of filetable['added']. + filetable['added'] += self.assistant.getFiles() + filetable['added'] = sorted(set(filetable['added'])) + + # Determine include_guard_prefix. + include_guard_prefix = self.config['include_guard_prefix'] + + # Determine makefile name. + if not makefile: + makefile_am = string('Makefile.am') + else: # if makefile + makefile_am = makefile + + # Create normal Makefile.ams. + for_test = False + + # Setup list of Makefile.am edits that are to be performed afterwards. + # Some of these edits apply to files that we will generate; others are + # under the responsibility of the developer. + makefile_am_edits = dict() + if makefile_am == 'Makefile.am': + sourcebase_dir = os.path.dirname(sourcebase) + sourcebase_base = os.path.basename(sourcebase) + self.makefiletable.editor(sourcebase_dir, 'SUBDIRS', sourcebase_base) + if pobase: + pobase_dir = os.path.dirname(pobase) + pobase_base = os.path.basename(pobase) + self.makefiletable.editor(pobase_dir, 'SUBDIRS', pobase_base) + if self.config.checkTestFlag(TESTS['tests']): + if makefile_am == 'Makefile.am': + testsbase_dir = os.path.dirname(testsbase) + testsbase_base = os.path.basename(testsbase) + self.makefiletable.editor(testsbase_dir, 'SUBDIRS', testsbase_base) + self.makefiletable.editor('', 'ACLOCAL_AMFLAGS', '-I %s' % m4base) + self.makefiletable.parent() + + # Create library makefile. + basename = joinpath(sourcebase, makefile_am) + tmpfile = self.assistant.tmpfilename(basename) + emit, uses_subdirs = self.emiter.lib_Makefile_am(basename, + self.moduletable['main'], self.moduletable, self.makefiletable, + actioncmd, for_test) + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(emit) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + filetable['added'] += [filename] + if isfile(tmpfile): + os.remove(tmpfile) + + # Create po/ directory. + filesystem = GLFileSystem(self.config) + if pobase: + # Create po makefile and auxiliary files. + for file in ['Makefile.in.in', 'remove-potcdate.sin']: + tmpfile = self.assistant.tmpfilename(joinpath(pobase, file)) + path = joinpath('build-aux', 'po', file) + lookedup, flag = filesystem.lookup(path) + shutil.move(lookedup, tmpfile) + basename = joinpath(pobase, file) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + filetable['added'] += [filename] + if isfile(tmpfile): + os.remove(tmpfile) + + # Create po makefile parameterization, part 1. + basename = joinpath(pobase, 'Makevars') + tmpfile = self.assistant.tmpfilename(basename) + emit = self.emiter.po_Makevars() + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(emit) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + filetable['added'] += [filename] + if isfile(tmpfile): + os.remove(tmpfile) + + # Create po makefile parameterization, part 2. + basename = joinpath(pobase, 'POTFILES.in') + tmpfile = self.assistant.tmpfilename(basename) + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(self.emiter.po_POTFILES_in(filetable['all'])) + basename = joinpath(pobase, 'POTFILES.in') + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + filetable['added'] += [filename] + if isfile(tmpfile): + os.remove(tmpfile) + + # Fetch PO files. + TP_URL = 'http://translationproject.org/latest/' + TP_RSYNC_URI = 'translationproject.org::tp/latest/' + if not self.config['dryrun']: + print('Fetching gnulib PO files from %s' % TP_URL) + os.chdir(joinpath(destdir, pobase)) + cmd = 'if type rsync 2>/dev/null | grep / > /dev/null; ' + cmd += 'then echo 1; else echo 0; fi' + result = sp.check_output(cmd, shell=True) + result = bool(int(result)) + if result: # use rsync + args = ['rsync', '-Lrtz', '%sgulib/' % TP_RSYNC_URI, '.'] + else: # use wget + args = ['wget', '--quiet', '-r', '-l1', '-nd', '-np', 'A.po', + '%sgnulib' % TP_URL] + sp.call(args, shell=True) + else: # if self.config['dryrun'] + print('Fetch gnulib PO files from %s' % TP_URL) + + # Create po/LINGUAS. + basename = joinpath(pobase, 'LINGUAS') + if not self.config['dryrun']: + tmpfile = self.assistant.tmpfilename(basename) + data = string('# Set of available languages.\n') + files = [constants.subend('.po', '', file) \ + for file in os.listdir(joinpath(destdir, pobase))] + files = [file.decode(ENCS['default']) if type(file) is bytes \ + else file for file in files] + data += '\n'.join(files) + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(data) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + print('Updating %s (backup in %s)' % (filename, backup)) + elif flag == 2: + print('Creating %s' % filename) + filetable['added'] += [filename] + if isfile(tmpfile): + os.remove(tmpfile) + else: # if not self.config['dryrun'] + backupname = '%s~' % basename + if isfile(destdir, basename): + print('Update %s (backup in %s)' % (basename, backupname)) + else: # if not isfile(destdir, basename) + print('Create %s' % basename) + + # Create m4/gnulib-cache.m4. + basename = joinpath(m4base, 'gnulib-cache.m4') + tmpfile = self.assistant.tmpfilename(basename) + emit = self.gnulib_cache() + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(emit) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + if emit[-2:] == '\r\n': + emit = emit[:-2] + elif emit[-1:] == '\n': + emit = emit[:-1] + print(emit) + if isfile(tmpfile): + os.remove(tmpfile) + + # Create m4/gnulib-comp.m4. + basename = joinpath(m4base, 'gnulib-comp.m4') + tmpfile = self.assistant.tmpfilename(basename) + emit = self.gnulib_comp(filetable['all']) + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(emit) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + if emit[-2:] == '\r\n': + emit = emit[:-2] + elif emit[-1:] == '\n': + emit = emit[:-1] + print(emit) + if isfile(tmpfile): + os.remove(tmpfile) + + # Create tests Makefile. + inctests = self.config.checkTestFlag(TESTS['tests']) + if inctests: + basename = joinpath(testsbase, makefile_am) + tmpfile = self.assistant.tmpfilename(basename) + emit, uses_subdirs = self.emiter.lib_Makefile_am(basename, + self.moduletable['tests'], self.moduletable, self.makefiletable, + actioncmd, for_test) + with codecs.open(tmpfile, 'wb', 'UTF-8') as file: + file.write(emit) + filename, backup, flag = self.assistant.super_update(basename, tmpfile) + if flag == 1: + if not self.config['dryrun']: + print('Updating %s (backup in %s)' % (filename, backup)) + else: # if self.config['dryrun'] + print('Update %s (backup in %s)' % (filename, backup)) + elif flag == 2: + if not self.config['dryrun']: + print('Creating %s' % filename) + else: # if self.config['dryrun']: + print('Create %s' % filename) + filetable['added'] += [filename] + if isfile(tmpfile): + os.remove(tmpfile) + + # Update the .cvsignore and .gitignore files. + ignorelist = list() + filetable['added'] = sorted(set(filetable['added'])) + filetable['removed'] = sorted(set(filetable['added'])) + for file in filetable['added']: + directory, basename = os.path.split(file) + ignorelist += [tuple([directory, '|A|', basename])] + for file in filetable['removed']: + directory, basename = os.path.split(file) + ignorelist += [tuple([directory, '|R|', basename])] + last_dir = string() + last_dirs_added = list() + last_dirs_removed = list() + for row in ignorelist: + next_dir = row[0] + operand = row[1] + filename = row[2] + if next_dir != last_dir: + self._done_dir_(last_dir, last_dirs_added, last_dirs_removed) + last_dir = next_dir + last_dirs_added = list() + last_dirs_removed = list() + if operand == '|A|': + last_dirs_added += [filename] + elif operand == '|R|': + last_dirs_removed += [filename] + self._done_dir_(last_dir, last_dirs_added, last_dirs_removed) + exit() + + # Finish the work. + print('Finished.\n') + print('You may need to add #include directives \ +for the following .h files.') + modules = sorted(set([module for module in self.moduletable['base'] \ + if module in self.moduletable['main']])) + # First the #include <...> directives without #ifs, sorted for convenience, + # then the #include "..." directives without #ifs, sorted for convenience, + # then the #include directives that are surrounded by #ifs. Not sorted. + includes_angles = list() + includes_quotes = list() + includes_if = list() + for module in modules: + include = module.getInclude() + for include in include.split('\n'): + if '%s#if' % constants.NL in '%s%s' % (constants.NL, include): + includes_if += [include] + else: # if '%s#if' % constants.NL in '%s%s' % (constants.NL, include) + if 'include "' in include: + includes_quotes += [include] + else: # if 'include "' not in include + includes_angles += [include] + includes_angles = sorted(set(includes_angles)) + includes_quotes = sorted(set(includes_quotes)) + includes = includes_angles +includes_quotes +includes_if + includes = [include for include in includes if include.split()] + for include in includes: + print(' %s' % include) + + # Get link directives. + links = [module.getLink() for module in self.moduletable['main']] + links = sorted(set([link for link in links if link.strip()])) + if links: + print(''' +You may need to use the following Makefile variables when linking. +Use them in _LDADD when linking a program, or +in _a_LDFLAGS or _la_LDFLAGS when linking a library.''') + for link in links: + print(' %s' % link) + + # Print reminders. + print('') + print('Don\'t forget to') + if makefile_am == 'Makefile.am': + print(' - add "%s/Makefile" to AC_CONFIG_FILES in %s,' % \ + (sourcebase, configure_ac)) + else: # if makefile_am != 'Makefile.am' + print(' - "include %s" from within "%s/Makefile.am",' % \ + (makefile, sourcebase)) + if pobase: + print(' - add "%s/Makefile.in to AC_CONFIG_FILES in %s,' % \ + (pobase, configure_ac)) + if inctests: + if makefile_am == 'Makefile.am': + print(' - add "%s/Makefile" to AC_CONFIG_FILES in %s,' % \ + (testsbase, configure_ac)) + else: # if makefile_am != 'Makefile.am' + print(' - "include %s" from within "%s/Makefile.am",' % \ + (makefile, testsbase)) + # Print makefile edits. + current_edit = int() + makefile_am_edits = self.makefiletable.count() + while current_edit != makefile_am_edits: + dictionary = self.makefiletable[current_edit] + if dictionary['var']: + print(' - mention "%s" in %s in %s,' % \ + (dictionary['val'], dictionary['var'], + joinpath(dictionary['dir'], 'Makefile.am'))) + current_edit += 1 + + # Detect position_early_after. + with codecs.open(configure_ac, 'rb', 'UTF-8') as file: + data = file.read() + match_result1 = \ + bool(compiler('^ *AC_PROG_CC_STDC', re.S | re.M).findall(data)) + match_result2 = \ + bool(compiler('^ *AC_PROG_CC_C99', re.S | re.M).findall(data)) + if match_result1: + position_early_after = 'AC_PROG_CC_STDC' + elif match_result2: + position_early_after = 'AC_PROG_CC_C99' + else: # if not any([match_result1, match_result2]) + position_early_after = 'AC_PROG_CC' + print(' - invoke %s_EARLY in %s, right after %s,' % \ + (macro_prefix, configure_ac, position_early_after)) + print(' - invoke %s_INIT in %s.' % \ + (macro_prefix, configure_ac)) + sp.call(['rm', '-rf', self.config['tempdir']], shell=False) + diff --git a/pygnulib/GLImport.pyc b/pygnulib/GLImport.pyc new file mode 100644 index 000000000..8d74134e7 Binary files /dev/null and b/pygnulib/GLImport.pyc differ diff --git a/pygnulib/GLInfo.py b/pygnulib/GLInfo.py new file mode 100644 index 000000000..dd6b2675c --- /dev/null +++ b/pygnulib/GLInfo.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import locale +import codecs +import subprocess as sp +from . import constants + + +#=============================================================================== +# 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 +FILES = constants.FILES +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 + + +#=============================================================================== +# Define GLInfo class +#=============================================================================== +class GLInfo(object): + '''This class is used to get fromatted information about gnulib-tool. + This information is mainly used in stdout messages, but can be used + anywhere else. The return values are not the same as for the module, + but still depends on them.''' + + def __repr__(self): + '''x.__repr__ <==> repr(x)''' + result = '' % hex(id(self)) + return(result) + + def package(self): + '''Return formatted string which contains name of the package.''' + result = 'GNU gnulib' + return(result) + + def authors(self): + '''Return formatted string which contains authors. + The special __author__ variable is used (type is list).''' + result = string() + for item in __author__: + if item == __author__[-2]: + result += '%s ' % item + elif item == __author__[-1]: + result += 'and %s' % item + else: + result += '%s, ' % item + return(result) + + def license(self): + '''Return formatted string which contains license and its description.''' + result = 'License GPLv3+: GNU GPL version 3 or later' + result += ' \n' + result += 'This is free software: you are free' + result += ' to change and redistribute it.\n' + result += 'There is NO WARRANTY, to the extent permitted by law.' + return(result) + + def copyright(self): + '''Return formatted string which contains copyright. + The special __copyright__ variable is used (type is str).''' + result = 'Copyright (C) %s' % __copyright__ + return(result) + + def date(self): + '''Return formatted string which contains date and time in GMT format.''' + if isdir(DIRS['git']): + counter = int() # Create counter + result = string() # Create string + args = ['git', 'log'] + result = sp.check_output(args).decode(ENCS['shell']) + # Get date as "Fri Mar 21 07:16:51 2008 -0600" from string + pattern = re.compile('Date:[\t ]*(.*?)$', re.S | re.M) + result = pattern.findall(result)[0] + # Turn "Fri Mar 21 07:16:51 2008 -0600" into "Mar 21 2008 07:16:51 -0600" + pattern = re.compile('^[^ ]* ([^ ]*) ([0-9]*) ([0-9:]*) ([0-9]*) ') + result = pattern.sub('\\1 \\2 \\4 \\3 ', result) + # Use GNU date to compute the time in GMT + args = ['date', '-d', result, '-u', '+%Y-%m-%d %H:%M:%S'] + proc = sp.check_output(args) + result = string(proc, ENCS['shell']) + result = result.rstrip(os.linesep) + return(result) + + def usage(self): + '''Show help message.''' + result = '''\ +Usage: gnulib-tool --list + gnulib-tool --find filename + gnulib-tool --import [module1 ... moduleN] + gnulib-tool --add-import [module1 ... moduleN] + gnulib-tool --remove-import [module1 ... moduleN] + gnulib-tool --update + gnulib-tool --create-testdir --dir=directory [module1 ... moduleN] + gnulib-tool --create-megatestdir --dir=directory [module1 ... moduleN] + gnulib-tool --test --dir=directory module1 ... moduleN + gnulib-tool --megatest --dir=directory [module1 ... moduleN] + gnulib-tool --extract-description module + gnulib-tool --extract-comment module + gnulib-tool --extract-status module + gnulib-tool --extract-notice module + gnulib-tool --extract-applicability module + gnulib-tool --extract-filelist module + gnulib-tool --extract-dependencies module + gnulib-tool --extract-autoconf-snippet module + gnulib-tool --extract-automake-snippet module + gnulib-tool --extract-include-directive module + gnulib-tool --extract-link-directive module + gnulib-tool --extract-license module + gnulib-tool --extract-maintainer module + gnulib-tool --extract-tests-module module + gnulib-tool --copy-file file [destination] + +Operation modes: + + --list print the available module names + --find find the modules which contain the specified file + --import import the given modules into the current package + --add-import augment the list of imports from gnulib into the + current package, by adding the given modules; + if no modules are specified, update the current + package from the current gnulib + --remove-import reduce the list of imports from gnulib into the + current package, by removing the given modules + --update update the current package, restore files omitted + from version control + --create-testdir create a scratch package with the given modules + (pass --with-tests to include the unit tests) + --create-megatestdir create a mega scratch package with the given modules + one by one and all together + (pass --with-tests to include the unit tests) + --test test the combination of the given modules + (pass --with-tests to include the unit tests) + (recommended to use CC=\"gcc -Wall\" here) + --megatest test the given modules one by one and all together + (pass --with-tests to include the unit tests) + (recommended to use CC=\"gcc -Wall\" here) + --extract-description extract the description + --extract-comment extract the comment + --extract-status extract the status (obsolete etc.) + --extract-notice extract the notice or banner + --extract-applicability extract the applicability + --extract-filelist extract the list of files + --extract-dependencies extract the dependencies + --extract-autoconf-snippet extract the snippet for configure.ac + --extract-automake-snippet extract the snippet for library makefile + --extract-include-directive extract the #include directive + --extract-link-directive extract the linker directive + --extract-license report the license terms of the source files + under lib/ + --extract-maintainer report the maintainer(s) inside gnulib + --extract-tests-module report the unit test module, if it exists + --copy-file copy a file that is not part of any module + --help Show this help text. + --version Show version and authorship information. + +General options: + + --dir=DIRECTORY Specify the target directory. + For --import, this specifies where your + configure.ac can be found. Defaults to current + directory. + --local-dir=DIRECTORY Specify a local override directory where to look + up files before looking in gnulib's directory. + --cache-modules Enable module caching optimization. + --no-cache-modules Disable module caching optimization. + --verbose Increase verbosity. May be repeated. + --quiet Decrease verbosity. May be repeated. + +Options for --import, --add/remove-import, --update: + + --dry-run Only print what would have been done. + +Options for --import, --add/remove-import, + --create-[mega]testdir, --[mega]test: + + --with-tests Include unit tests for the included modules. + --with-obsolete Include obsolete modules when they occur among the + dependencies. By default, dependencies to obsolete + modules are ignored. + --with-c++-tests Include even unit tests for C++ interoperability. + --with-longrunning-tests + Include even unit tests that are long-runners. + --with-privileged-tests + Include even unit tests that require root + privileges. + --with-unportable-tests + Include even unit tests that fail on some platforms. + --with-all-tests Include all kinds of problematic unit tests. + --avoid=MODULE Avoid including the given MODULE. Useful if you + have code that provides equivalent functionality. + This option can be repeated. + --conditional-dependencies + Support conditional dependencies (may save configure + time and object code). + --no-conditional-dependencies + Don't use conditional dependencies. + --libtool Use libtool rules. + --no-libtool Don't use libtool rules. + +Options for --import, --add/remove-import: + + --lib=LIBRARY Specify the library name. Defaults to 'libgnu'. + --source-base=DIRECTORY + Directory relative to --dir where source code is + placed (default \"lib\"). + --m4-base=DIRECTORY Directory relative to --dir where *.m4 macros are + placed (default \"m4\"). + --po-base=DIRECTORY Directory relative to --dir where *.po files are + placed (default \"po\"). + --doc-base=DIRECTORY Directory relative to --dir where doc files are + placed (default \"doc\"). + --tests-base=DIRECTORY + Directory relative to --dir where unit tests are + placed (default \"tests\"). + --aux-dir=DIRECTORY Directory relative to --dir where auxiliary build + tools are placed (default comes from configure.ac). + --lgpl[=2|=3] Abort if modules aren't available under the LGPL. + Also modify license template from GPL to LGPL. + The version number of the LGPL can be specified; + the default is currently LGPLv3. + --makefile-name=NAME Name of makefile in automake syntax in the + source-base and tests-base directories + (default \"Makefile.am\"). + --macro-prefix=PREFIX Specify the prefix of the macros 'gl_EARLY' and + 'gl_INIT'. Default is 'gl'. + --po-domain=NAME Specify the prefix of the i18n domain. Usually use + the package name. A suffix '-gnulib' is appended. + --witness-c-macro=NAME Specify the C macro that is defined when the + sources in this directory are compiled or used. + --vc-files Update version control related files. + --no-vc-files Don't update version control related files + (.gitignore and/or .cvsignore). + --no-changelog Don't update or create ChangeLog files. + +Options for --create-[mega]testdir, --[mega]test: + + --without-c++-tests Exclude unit tests for C++ interoperability. + --without-longrunning-tests + Exclude unit tests that are long-runners. + --without-privileged-tests + Exclude unit tests that require root privileges. + --without-unportable-tests + Exclude unit tests that fail on some platforms. + --single-configure Generate a single configure file, not a separate + configure file for the tests directory. + +Options for --import, --add/remove-import, --update, + --create-[mega]testdir, --[mega]test: + + -s, --symbolic, --symlink Make symbolic links instead of copying files. + --local-symlink Make symbolic links instead of copying files, only + for files from the local override directory. + +Options for --import, --add/remove-import, --update: + + -S, --more-symlinks Make symbolic links instead of copying files, and + don't replace copyright notices. + +Report bugs to .''' + return(result) + + def version(self): + '''Return formatted string which contains git or CVS version.''' + if isdir(DIRS['git']): + version_gen = joinpath(DIRS['build-aux'], 'git-version-gen') + args = [version_gen, DIRS['root']] + result = sp.check_output(args).decode(ENCS['shell']) + result = result.strip() + if result == 'UNKNOWN': + result = string() + return(result) + diff --git a/pygnulib/GLInfo.pyc b/pygnulib/GLInfo.pyc new file mode 100644 index 000000000..2bab4f0a7 Binary files /dev/null and b/pygnulib/GLInfo.pyc differ diff --git a/pygnulib/GLMakefileTable.py b/pygnulib/GLMakefileTable.py new file mode 100644 index 000000000..3c0e1d4e8 --- /dev/null +++ b/pygnulib/GLMakefileTable.py @@ -0,0 +1,133 @@ +#!/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 +FILES = constants.FILES +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 GLMakefileTable class +#=============================================================================== +class GLMakefileTable(object): + '''This class is used to edit Makefile and store edits as table. + When user creates Makefile, he may need to use this class.''' + + def __init__(self, config): + '''GLMakefileTable.__init__(config) -> GLMakefileTable + + Create GLMakefileTable instance.''' + if type(config) is not GLConfig: + raise(TypeError('config must be a GLConfig, not %s' % \ + type(config).__name__)) + self.config = config + self.table = list() + + def __getitem__(self, y): + '''x.__getitem__(y) = x[y]''' + if type(y) is not int: + raise(TypeError('indices must be integers, not %s' % \ + type(y).__name__)) + result = self.table[y] + return(dict(result)) + + def editor(self, dir, var, val): + '''GLMakefileTable.editor(dir, var, val) + + This method is used to remember that ${dir}Makefile.am needs to be edited + to that ${var} mentions ${val}.''' + if type(dir) is bytes or type(dir) is string: + if type(dir) is bytes: + dir = dir.decode(ENCS['default']) + else: # if dir has not bytes or string type + raise(TypeError( + 'dir must be a string, not %s' % (type(dir).__name__))) + if type(var) is bytes or type(var) is string: + if type(var) is bytes: + var = var.decode(ENCS['default']) + else: # if var has not bytes or string type + raise(TypeError( + 'var must be a string, not %s' % (type(var).__name__))) + if type(val) is bytes or type(val) is string: + if type(val) is bytes: + val = val.decode(ENCS['default']) + else: # if val has not bytes or string type + raise(TypeError( + 'val must be a string, not %s' % (type(val).__name__))) + dictionary = {'dir': dir, 'var': var, 'val': val} + self.table += [dictionary] + + def parent(self): + '''GLMakefileTable.parent() + + Add a special row to Makefile.am table with the first parent directory + which contains or will contain Makefile.am file. + GLConfig: sourcebase, m4base, testsbase, testflags, makefile.''' + m4base = self.config['m4base'] + sourcebase = self.config['sourcebase'] + testsbase = self.config['testsbase'] + makefile = self.config['makefile'] + inctests = self.config.checkTestFlag(TESTS['tests']) + dir1 = string('%s%s' % (m4base, os.path.sep)) + mfd = string('Makefile.am') + if not makefile: + mfx = string('Makefile.am') + else: # if makefile + mfx = makefile + dir2 = string() + while dir1 and \ + (joinpath(self.config['destdir'], dir1, mfd) or \ + joinpath(dir1, mfd) == joinpath(sourcebase, mfx) or \ + (inctests and joinpath(dir1, mfd) == joinpath(testsbase, mfx))): + dir2 = joinpath(os.path.basename(dir1), dir2) + dir1 = os.path.dirname(dir1) + self.editor(dir1, 'EXTRA_DIST', joinpath(dir2, 'gnulib-cache.m4')) + + def count(self): + '''GLMakefileTable.count() -> int + + Count number of edits which were applied.''' + return(len(self.table)) + diff --git a/pygnulib/GLMakefileTable.pyc b/pygnulib/GLMakefileTable.pyc new file mode 100644 index 000000000..eb0ff5297 Binary files /dev/null and b/pygnulib/GLMakefileTable.pyc differ diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py new file mode 100644 index 000000000..53fe0a271 --- /dev/null +++ b/pygnulib/GLModuleSystem.py @@ -0,0 +1,1267 @@ +#!/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 +FILES = constants.FILES +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 = ['CVS', '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'/^CVS\//d', + '-e', r'/\/CVS\//d', + '-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(ENCS['shell']) + + # 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(ENCS['shell']) + 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(ENCS['shell']) + 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 = snippet.replace('\\\n', ' ') + 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 = snippet.replace('\\\n', '') + 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)) + diff --git a/pygnulib/GLModuleSystem.pyc b/pygnulib/GLModuleSystem.pyc new file mode 100644 index 000000000..950b7abbe Binary files /dev/null and b/pygnulib/GLModuleSystem.pyc differ diff --git a/pygnulib/GLTestDir.py b/pygnulib/GLTestDir.py new file mode 100644 index 000000000..8652d1ffc --- /dev/null +++ b/pygnulib/GLTestDir.py @@ -0,0 +1,987 @@ +#!/usr/bin/python +# encoding: UTF-8 + +#=============================================================================== +# Define global imports +#=============================================================================== +import os +import re +import sys +import locale +import codecs +import shutil +import filecmp +import subprocess as sp +from . import constants +from .GLError import GLError +from .GLConfig import GLConfig +from .GLModuleSystem import GLModule +from .GLModuleSystem import GLModuleTable +from .GLModuleSystem import GLModuleSystem +from .GLFileSystem import GLFileSystem +from .GLFileSystem import GLFileAssistant +from .GLMakefileTable import GLMakefileTable +from .GLEmiter import GLEmiter + + +#=============================================================================== +# 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 +FILES = constants.FILES +MODES = constants.MODES +TESTS = constants.TESTS +compiler = constants.compiler +joinpath = constants.joinpath +cleaner = constants.cleaner +relpath = constants.relativize +string = constants.string +isabs = os.path.isabs +isdir = os.path.isdir +isfile = os.path.isfile +normpath = os.path.normpath + + +#=============================================================================== +# Define GLTestDir class +#=============================================================================== +class GLTestDir(object): + '''GLTestDir class is used to create a scratch package with the given + list of the modules.''' + + def __init__(self, config, testdir): + '''GLTestDir.__init__(config, testdir) -> GLTestDir + + Create new GLTestDir instance.''' + if type(config) is not GLConfig: + raise(TypeError('config must be a GLConfig, not %s' % \ + type(config).__name__)) + if type(testdir) is bytes or type(testdir) is string: + if type(testdir) is bytes: + testdir = testdir.decode(ENCS['default']) + self.config = config + self.testdir = os.path.normpath(testdir) + if not os.path.exists(self.testdir): + try: # Try to create directory + os.mkdir(self.testdir) + except Exception as error: + raise(GLError(19, self.testdir)) + self.emiter = GLEmiter(self.config) + self.filesystem = GLFileSystem(self.config) + self.modulesystem = GLModuleSystem(self.config) + self.moduletable = GLModuleTable(self.config) + self.assistant = GLFileAssistant(self.config) + self.makefiletable = GLMakefileTable(self.config) + + # Subdirectory names. + self.config.setSourceBase('gllib') + self.config.setM4Base('glm4') + self.config.setDocBase('gldoc') + self.config.setTestsBase('gltests') + self.config.setMacroPrefix('gl') + self.config.resetPoBase() + self.config.resetPoDomain() + self.config.resetWitnessCMacro() + self.config.resetVCFiles() + + def rewrite_files(self, files): + '''GLTestDir.rewrite_files(files) + + Replace auxdir, docbase, sourcebase, m4base and testsbase from default + to their version from config.''' + if type(files) is not list: + raise(TypeError( + 'files argument must has list type, not %s' % type(files).__name__)) + files = \ + [ # Begin to convert bytes to string + file.decode(ENCS['default']) \ + if type(file) is bytes else file \ + for file in files + ] # Finish to convert bytes to string + for file in files: + if type(file) is not string: + raise(TypeError('each file must be a string instance')) + files = sorted(set(files)) + auxdir = self.config['auxdir'] + docbase = self.config['docbase'] + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + testsbase = self.config['testsbase'] + result = list() + for file in files: + if file.startswith('build-aux/'): + path = constants.substart('build-aux/', '%s/' % auxdir, file) + elif file.startswith('doc/'): + path = constants.substart('doc/', '%s/' % docbase, file) + elif file.startswith('lib/'): + path = constants.substart('lib/', '%s/' % sourcebase, file) + elif file.startswith('m4/'): + path = constants.substart('m4/', '%s/' % m4base, file) + elif file.startswith('tests/'): + path = constants.substart('tests/', '%s/' % testsbase, file) + elif file.startswith('tests=lib/'): + path = constants.substart('tests=lib/', '%s/' % testsbase, file) + elif file.startswith('top/'): + path = constants.substart('top/', '', file) + else: # file is not a special file + path = file + result += [os.path.normpath(path)] + result = sorted(set(result)) + return(list(result)) + + def execute(self): + '''GLTestDir.execute() + + Create a scratch package with the given modules.''' + localdir = self.config['localdir'] + auxdir = self.config['auxdir'] + testflags = list(self.config['testflags']) + sourcebase = self.config['sourcebase'] + m4base = self.config['m4base'] + pobase = self.config['pobase'] + docbase = self.config['docbase'] + testsbase = self.config['testsbase'] + libname = self.config['libname'] + libtool = self.config['libtool'] + witness_c_macro = self.config['witness_c_macro'] + symbolic = self.config['symbolic'] + lsymbolic = self.config['lsymbolic'] + single_configure = self.config['single_configure'] + include_guard_prefix = self.config['include_guard_prefix'] + macro_prefix = self.config['macro_prefix'] + verbose = self.config['verbosity'] + + base_modules = [self.modulesystem.find(m) for m in self.config['modules']] + if not base_modules: + base_modules = self.modulesystem.list() + base_modules = [self.modulesystem.find(m) for m in base_modules] + # All modules together. + # Except config-h, which breaks all modules which use HAVE_CONFIG_H. + # Except ftruncate, mountlist, which abort the configuration on mingw. + # Except lib-ignore, which leads to link errors when Sun C++ is used. + base_modules = sorted(set(base_modules)) + base_modules = [module for module in base_modules if str(module) not in \ + ['config-h', 'ftruncate', 'mountlist', 'lib-ignore']] + + # When computing transitive closures, don't consider $module to depend on + # $module-tests. Need this because tests are implicitly GPL and may depend + # on GPL modules - therefore we don't want a warning in this case. + saved_testflags = list(self.config['testflags']) + self.config.disableTestFlag(TESTS['tests']) + for requested_module in base_modules: + requested_licence = requested_module.getLicense() + # Here we use self.moduletable.transitive_closure([module]), not just + # module.getDependencies, so that we also detect weird situations like + # an LGPL module which depends on a GPLed build tool module which depends + # on a GPL module. + if requested_licence != 'GPL': + modules = self.moduletable.transitive_closure([requested_module]) + for module in modules: + license = module.getLicense() + errormsg = 'module %s depends on a module ' % requested_module + errormsg += 'with an incompatible license: %s\n' % module + if requested_licence == 'GPLv2+': + if license not in ['GPLv2+', 'LGPLv2+']: + sys.stderr.write(errormsg) + elif requested_licence in ['LGPL']: + if license not in ['LGPL', 'LGPLv2+']: + sys.stderr.write(errormsg) + elif requested_licence in ['LGPLv2+']: + if license not in ['LGPLv2+']: + sys.stderr.write(errormsg) + self.config.setTestFlags(saved_testflags) + + # Determine final module list. + modules = self.moduletable.transitive_closure(base_modules) + final_modules = list(modules) + + # Show final module list. + if verbose >= 0: + bold_on = '' + bold_off = '' + term = os.getenv('TERM') + if term == 'xterm': + bold_on = '\x1b[1m' + bold_off = '\x1b[0m' + print('Module list with included dependencies (indented):') + for module in final_modules: + if str(module) in self.config.getModules(): + print(' %s%s%s' % (bold_on, module, bold_off)) + else: # if str(module) not in self.config.getModules() + print(' %s' % module) + + # Generate lists of the modules. + if single_configure: + # Determine main module list and tests-related module list separately. + main_modules, tests_modules = \ + self.moduletable.transitive_closure_separately( + base_modules, final_modules) + # Print main_modules and tests_modules. + if verbose >= 1: + print('Main module list:') + for module in main_modules: + print(' %s' % str(module)) + print('Tests-related module list:') + for module in tests_modules: + print(' %s' % str(module)) + # Determine whether a $testsbase/libtests.a is needed. + libtests = False + for module in tests_modules: + files = module.getFiles() + for file in files: + if file.startswith('lib/'): + libtests = True + break + if libtests: + self.config.enableLibtests() + + if single_configure: + # Add dummy package if it is needed. + main_modules = self.moduletable.add_dummy(main_modules) + if 'dummy' in [str(module) for module in main_modules]: + main_modules = [m for m in main_modules if str(m) != 'dummy'] + dummy = self.modulesystem.find('dummy') + main_modules = sorted(set(main_modules)) +[dummy] + if libtests: # if we need to use libtests.a + tests_modules = self.moduletable.add_dummy(tests_modules) + if 'dummy' in [str(module) for module in tests_modules]: + tests_modules = [m for m in tests_modules if str(m) != 'dummy'] + dummy = self.modulesystem.find('dummy') + tests_modules = sorted(set(tests_modules)) +[dummy] + else: # if not single_configure + modules = self.moduletable.add_dummy(modules) + if 'dummy' in [str(module) for module in modules]: + modules = [m for m in modules if str(m) != 'dummy'] + dummy = self.modulesystem.find('dummy') + modules = sorted(set(modules)) +[dummy] + + # Show banner notice of every module. + if single_configure: + for module in main_modules: + notice = module.getNotice() + if notice: + print('Notice from module %s:' % str(module)) + pattern = compiler('^(.*?)$', re.S | re.M) + notice = pattern.sub(' \\1', notice) + print(notice) + else: # if not single_configure + for module in modules: + notice = module.getNotice() + if notice: + print('Notice from module %s:' % str(module)) + pattern = compiler('^(.*?)$', re.S | re.M) + notice = pattern.sub(' \\1', notice) + print(notice) + + # Determine final file list. + if single_configure: + main_filelist, tests_filelist = \ + self.moduletable.filelist_separately(main_modules, tests_modules) + filelist = sorted(set(main_filelist +tests_filelist)) + else: # if not single_configure + filelist = self.moduletable.filelist(modules) + + filelist = sorted(set(filelist)) + + # Print list of files. + if verbose >= 0: + print('File list:') + for file in filelist: + if file.startswith('tests=lib/'): + rest = file[10:] + print(' lib/%s -> tests/%s' % (rest, rest)) + else: + print(' %s' % file) + + # Add files for which the copy in gnulib is newer than the one that + # "automake --add-missing --copy" would provide. + filelist += ['build-aux/config.guess', 'build-aux/config.sub'] + filelist = sorted(set(filelist)) + + # Create directories. + directories = [os.path.dirname(file) \ + for file in self.rewrite_files(filelist)] + directories = sorted(set(directories)) + + # Copy files or make symbolic links. + filetable = list() + for src in filelist: + dest = self.rewrite_files([src])[-1] + filetable += [tuple([dest, src])] + for row in filetable: + src = row[1] + dest = row[0] + destpath = joinpath(self.testdir, dest) + dirname = os.path.dirname(destpath) + if not isdir(dirname): + os.makedirs(dirname) + if src.startswith('tests=lib/'): + src = constants.substart('tests=lib/', 'lib/', src) + lookedup, flag = self.filesystem.lookup(src) + if isfile(destpath): + os.remove(destpath) + if flag: + shutil.copy(lookedup, destpath) + else: # if not flag + if symbolic or (lsymbolic and lookedup == joinpath(localdir, src)): + constants.link_relative(lookedup, destpath) + else: + shutil.copy(lookedup, destpath) + + # Create $sourcebase/Makefile.am. + for_test = True + directory = joinpath(self.testdir, sourcebase) + if not isdir(directory): + os.mkdir(directory) + destfile = joinpath(directory, 'Makefile.am') + if single_configure: + emit, uses_subdirs = self.emiter.lib_Makefile_am(destfile, main_modules, + self.moduletable, self.makefiletable, '', for_test) + else: # if not single_configure + emit, uses_subdirs = self.emiter.lib_Makefile_am(destfile, modules, + self.moduletable, self.makefiletable, '', for_test) + with codecs.open(destfile, 'wb', 'UTF-8') as file: + file.write(emit) + any_uses_subdirs = uses_subdirs + + # Create $m4base/Makefile.am. + directory = joinpath(self.testdir, m4base) + if not isdir(directory): + os.mkdir(directory) + destfile = joinpath(directory, 'Makefile.am') + emit = string() + emit += '## Process this file with automake to produce Makefile.in.\n\n' + emit += 'EXTRA_DIST =\n' + for file in filelist: + if file.startswith('m4/'): + file = constants.substart('m4/', '', file) + emit += 'EXTRA_DIST += %s\n' % file + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + with codecs.open(destfile, 'wb', 'UTF-8') as file: + file.write(emit) + + subdirs = [sourcebase, m4base] + subdirs_with_configure_ac = list() + + testsbase_appened = False + inctests = self.config.checkTestFlag(TESTS['tests']) + if inctests: + directory = joinpath(self.testdir, testsbase) + if not isdir(directory): + os.mkdir(directory) + if single_configure: + # Create $testsbase/Makefile.am. + destfile = joinpath(directory, 'Makefile.am') + print(repr(destfile)) + witness_macro = '%stests_WITNESS' % macro_prefix + emit, uses_subdirs = self.emiter.tests_Makefile_am(destfile, + tests_modules, self.makefiletable, witness_macro, for_test) + with codecs.open(destfile, 'wb', 'UTF-8') as file: + file.write(emit) + else: # if not single_configure + # Create $testsbase/Makefile.am. + destfile = joinpath(directory, 'Makefile.am') + libtests = False + self.config.disableLibtests() + emit, uses_subdirs = self.emiter.tests_Makefile_am(destfile, + modules, self.makefiletable, '', for_test) + with codecs.open(destfile, 'wb', 'UTF-8') as file: + file.write(emit) + # Viewed from the $testsbase subdirectory, $auxdir is different. + emit = string() + saved_auxdir = self.config['auxdir'] + testsbase = '%s/' % os.path.normpath(testsbase) + counter = int() + auxdir = string() + finish = (len(testsbase.split('/')) -1) + while counter < finish: + auxdir += '../' + counter += 1 + auxdir = os.path.normpath(joinpath(auxdir, saved_auxdir)) + testsbase = os.path.normpath(testsbase) + self.config.setAuxDir(auxdir) + # Create $testsbase/configure.ac. + emit += '# Process this file with autoconf ' + emit += 'to produce a configure script.\n' + emit += 'AC_INIT([dummy], [0])\n' + emit += 'AC_CONFIG_AUX_DIR([%s])\n' % auxdir + emit += 'AM_INIT_AUTOMAKE\n\n' + emit += 'AC_CONFIG_HEADERS([config.h])\n\n' + emit += 'AC_PROG_CC\n' + emit += 'AC_PROG_INSTALL\n' + emit += 'AC_PROG_MAKE_SET\n' + emit += 'gl_PROG_AR_RANLIB\n\n' + if uses_subdirs: + emit += 'AM_PROG_CC_C_O\n\n' + snippets = list() + for module in modules: + if str(module) in ['gnumakefile', 'maintainer-makefile']: + # These are meant to be used only in the top-level directory. + pass + else: # if str(module) not in ['gnumakefile', 'maintainer-makefile'] + snippet = module.getAutoconfSnippet_Early() + lines = [line for line in snippet.split('\n') if line.strip()] + snippet = '\n'.join(lines) + pattern = compiler('AC_REQUIRE\\(\\[([^()].*?)\\]\\)', re.S | re.M) + snippet = pattern.sub('\\1', snippet) + snippet = snippet.strip() + snippets += [snippet] + snippets = [snippet for snippet in snippets if snippet.strip()] + emit += '%s\n' % '\n'.join(snippets) + if libtool: + emit += 'LT_INIT([win32-dll])\n' + emit += 'LT_LANG([C++])\n' + emit += 'AM_CONDITIONAL([GL_COND_LIBTOOL], [true])\n' + emit += 'gl_cond_libtool=true\n' + else: # if not libtool + emit += 'AM_CONDITIONAL([GL_COND_LIBTOOL], [false])\n' + emit += 'gl_cond_libtool=false\n' + emit += 'gl_libdeps=\n' + emit += 'gl_ltlibdeps=\n' + # Wrap the set of autoconf snippets into an autoconf macro that is then + # invoked. This is needed because autoconf does not support AC_REQUIRE + # at the top level: + # error: AC_REQUIRE(gt_CSHARPCOMP): cannot be used outside of an + # AC_DEFUN'd macro + # but we want the AC_REQUIRE to have its normal meaning (provide one + # expansion of the required macro before the current point, and only + # one expansion total). + emit += 'AC_DEFUN([gl_INIT], [\n' + replace_auxdir = True + emit += "gl_m4_base='../%s'\n" % m4base + emit += self.emiter.initmacro_start(macro_prefix) + # We don't have explicit ordering constraints between the various + # autoconf snippets. It's cleanest to put those of the library before + # those of the tests. + emit += "gl_source_base='../%s'\n" % sourcebase + emit += self.emiter.autoconfSnippets(modules, + self.moduletable, self.assistant, 1, False, False, False, + replace_auxdir) + emit += "gl_source_base='.'" + emit += self.emiter.autoconfSnippets(modules, + self.moduletable, self.assistant, 2, False, False, False, + replace_auxdir) + emit += self.emiter.initmacro_end(macro_prefix) + # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is + # created using libtool, because libtool already handles the + # dependencies. + if not libtool: + libname_upper = libname.upper().replace('-', '_') + emit += ' %s_LIBDEPS="$gl_libdeps"\n' % libname_upper + emit += ' AC_SUBST([%s_LIBDEPS])\n' % libname_upper + emit += ' %s_LTLIBDEPS="$gl_ltlibdeps"\n' % libname_upper + emit += ' AC_SUBST([%s_LTLIBDEPS])\n' % libname_upper + emit += '])\n' + # FIXME use $sourcebase or $testsbase? + emit += self.emiter.initmacro_done(macro_prefix, sourcebase) + emit += '\ngl_INIT\n\n' + # Usually $testsbase/config.h will be a superset of config.h. Verify + # this by "merging" config.h into $testsbase/config.h; look out for gcc + # warnings. + emit += 'AH_TOP([#include \"../config.h\"])\n\n' + emit += 'AC_CONFIG_FILES([Makefile])\n' + emit += 'AC_OUTPUT\n' + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + path = joinpath(self.testdir, testsbase, 'configure.ac') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(emit) + + # Restore changed variables. + self.config.setAuxDir(saved_auxdir) + auxdir = self.config['auxdir'] + subdirs_with_configure_ac += [testsbase] + + subdirs += [testsbase] + testsbase_appened = True + + # Create Makefile.am. + emit = string() + emit += '## Process this file with automake to produce Makefile.in.\n\n' + emit += 'AUTOMAKE_OPTIONS = 1.5 foreign\n\n' + emit += 'SUBDIRS = %s\n\n' % ' '.join(subdirs) + emit += 'ACLOCAL_AMFLAGS = -I %s\n' % m4base + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + path = joinpath(self.testdir, 'Makefile.am') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(emit) + + # Create configure.ac + emit = string() + emit += '# Process this file with autoconf ' + emit += 'to produce a configure script.\n' + emit += 'AC_INIT([dummy], [0])\n' + if auxdir != '.': + emit += 'AC_CONFIG_AUX_DIR([%s])\n' % auxdir + emit += 'AM_INIT_AUTOMAKE\n\n' + emit += 'AC_CONFIG_HEADERS([config.h])\n\n' + emit += 'AC_PROG_CC\n' + emit += 'AC_PROG_INSTALL\n' + emit += 'AC_PROG_MAKE_SET\n\n' + emit += '# For autobuild.\n' + emit += 'AC_CANONICAL_BUILD\n' + emit += 'AC_CANONICAL_HOST\n\n' + emit += 'm4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace\n' + emit += 'm4_pattern_allow([^gl_ES$])dnl a valid locale name\n' + emit += 'm4_pattern_allow([^gl_LIBOBJS$])dnl a variable\n' + emit += 'm4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable\n\n' + emit += 'gl_PROG_AR_RANLIB\n\n' + if any_uses_subdirs: + emit += 'AM_PROG_CC_C_O\n' + snippets = list() + for module in final_modules: + if single_configure: + solution = True + else: # if not single_configure + solution = module.isNonTests() + if solution: + snippet = module.getAutoconfSnippet_Early() + lines = [line for line in snippet.split('\n') if line.strip()] + snippet = '\n'.join(lines) + pattern = compiler('AC_REQUIRE\\(\\[([^()].*?)\\]\\)', re.S | re.M) + snippet = pattern.sub('\\1', snippet) + snippet = snippet.strip() + snippets += [snippet] + snippets = [snippet for snippet in snippets if snippet.strip()] + emit += '%s\n' % '\n'.join(snippets) + if libtool: + emit += 'LT_INIT([win32-dll])\n' + emit += 'LT_LANG([C++])\n' + emit += 'AM_CONDITIONAL([GL_COND_LIBTOOL], [true])\n' + emit += 'gl_cond_libtool=true\n' + else: # if not libtool + emit += 'AM_CONDITIONAL([GL_COND_LIBTOOL], [false])\n' + emit += 'gl_cond_libtool=false\n' + emit += 'gl_libdeps=\n' + emit += 'gl_ltlibdeps=\n' + # Wrap the set of autoconf snippets into an autoconf macro that is then + # invoked. This is needed because autoconf does not support AC_REQUIRE + # at the top level: + # error: AC_REQUIRE(gt_CSHARPCOMP): cannot be used outside of an + # AC_DEFUN'd macro + # but we want the AC_REQUIRE to have its normal meaning (provide one + # expansion of the required macro before the current point, and only one + # expansion total). + emit += 'AC_DEFUN([gl_INIT], [\n' + if auxdir != 'build-aux': + replace_auxdir = True + else: # auxdir == 'build-aux' + replace_auxdir = False + emit += 'gl_m4_base=\'%s\'\n' % m4base + emit += self.emiter.initmacro_start(macro_prefix) + emit += 'gl_source_base=\'%s\'\n' % sourcebase + if single_configure: + emit += self.emiter.autoconfSnippets(main_modules, self.moduletable, + self.assistant, 0, False, False, False, replace_auxdir) + else: # if not single_configure + emit += self.emiter.autoconfSnippets(modules, self.moduletable, + self.assistant, 1, False, False, False, replace_auxdir) + emit += self.emiter.initmacro_end(macro_prefix) + if single_configure: + emit += ' gltests_libdeps=\n' + emit += ' gltests_ltlibdeps=\n' + emit += self.emiter.initmacro_start('%stests' % macro_prefix) + emit += ' gl_source_base=\'%s\'\n' % testsbase + # Define a tests witness macro. + emit += ' %stests_WITNESS=IN_GNULIB_TESTS\n' % macro_prefix + emit += ' AC_SUBST([%stests_WITNESS])\n' % macro_prefix + emit += ' gl_module_indicator_condition=$%stests_WITNESS\n' % \ + macro_prefix + emit += ' m4_pushdef([gl_MODULE_INDICATOR_CONDITION], ' + emit += '[$gl_module_indicator_condition])\n' + snippets = self.emiter.autoconfSnippets(tests_modules, self.moduletable, + self.assistant, 1, True, False, False, replace_auxdir) + emit += snippets.strip() + emit += ' m4_popdef([gl_MODULE_INDICATOR_CONDITION])\n' + emit += self.emiter.initmacro_end('%stests' % macro_prefix) + # _LIBDEPS and _LTLIBDEPS variables are not needed if this library is + # created using libtool, because libtool already handles the dependencies. + if not libtool: + libname_upper = libname.upper().replace('-', '_') + emit += ' %s_LIBDEPS="$gl_libdeps"\n' % libname_upper + emit += ' AC_SUBST([%s_LIBDEPS])\n' % libname_upper + emit += ' %s_LTLIBDEPS="$gl_ltlibdeps"\n' % libname_upper + emit += ' AC_SUBST([%s_LTLIBDEPS])\n' % libname_upper + if single_configure and libtests: + emit += ' LIBTESTS_LIBDEPS="$gltests_libdeps"\n' + emit += ' AC_SUBST([LIBTESTS_LIBDEPS])\n' + emit += '])\n' + emit += self.emiter.initmacro_done(macro_prefix, sourcebase) + if single_configure: + emit += self.emiter.initmacro_done('%stests' % macro_prefix, testsbase) + emit += '\ngl_INIT\n\n' + if subdirs_with_configure_ac: + if single_configure: + emit += 'AC_CONFIG_SUBDIRS([%s])\n' % \ + ' '.join(subdirs_with_configure_ac[:-1]) + else: # if not single_configure + emit += 'AC_CONFIG_SUBDIRS([%s])\n' % \ + ' '.join(subdirs_with_configure_ac) + makefiles = ['Makefile'] + for directory in subdirs: + # For subdirs that have a configure.ac by their own, it's the subdir's + # configure.ac which creates the subdir's Makefile.am, not this one. + makefiles += [joinpath(directory, 'Makefile')] + if not single_configure: + makefiles = makefiles[:-1] + emit += 'AC_CONFIG_FILES([%s])\n' % ' '.join(makefiles) + emit += 'AC_OUTPUT\n' + path = joinpath(self.testdir, 'configure.ac') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(emit) + + # Create autogenerated files. + # Do not use "${AUTORECONF} --force --install", because it may invoke + # autopoint, which brings in older versions of some of our .m4 files. + os.chdir(self.testdir) + # gettext + if isfile(joinpath(m4base, 'gettext.m4')): + args = [UTILS['autopoint'], '--force'] + constants.execute(args, verbose) + for src in os.listdir(m4base): + src = joinpath(m4base, src) + if src.endswith('.m4~'): + dest = src[:-1] + if isfile(dest): + os.remove(dest) + shutil.move(src, dest) + # libtoolize + if libtool: + args = [UTILS['libtoolize'], '--copy'] + constants.execute(args, verbose) + # aclocal + args = [UTILS['aclocal'], '-I', m4base] + constants.execute(args, verbose) + if not isdir('build-aux'): + os.mkdir('build-aux') + # autoconf + args = [UTILS['autoconf']] + constants.execute(args, verbose) + # autoheader + args = [UTILS['autoheader']] + constants.execute(args, verbose) + # automake + args = [UTILS['automake'], '--add-missing', '--copy'] + constants.execute(args, verbose) + os.chdir(DIRS['cwd']) + if inctests and not single_configure: + # Do not use "${AUTORECONF} --force --install", because it may invoke + # autopoint, which brings in older versions of some of our .m4 files. + os.chdir(joinpath(self.testdir, testsbase)) + # gettext + if isfile(joinpath(m4base, 'gettext.m4')): + args = [UTILS['autopoint'], '--force'] + constants.execute(args, verbose) + for src in os.listdir(m4base): + src = joinpath(m4base, src) + if src.endswith('.m4~'): + dest = src[:-1] + if isfile(dest): + os.remove(dest) + shutil.move(src, dest) + # aclocal + args = [UTILS['aclocal'], '-I', joinpath('..', m4base)] + constants.execute(args, verbose) + if not isdir(joinpath('../build-aux')): + os.mkdir('../build-aux') + # autoconf + args = [UTILS['autoconf']] + constants.execute(args, verbose) + # autoheader + args = [UTILS['autoheader']] + constants.execute(args, verbose) + # automake + args = [UTILS['automake'], '--add-missing', '--copy'] + constants.execute(args, verbose) + os.chdir(DIRS['cwd']) + + # Need to run configure and make once, to create built files that are to be + # distributed (such as parse-datetime.c). + path = joinpath(self.testdir, sourcebase, 'Makefile.am') + with codecs.open(path, 'rb', 'UTF-8') as file: + snippet = file.read() + cleaned_files = list() + tests_cleaned_files = list() + built_sources = list() + tests_built_sources = list() + distributed_built_sources = list() + tests_distributed_built_sources = list() + + # Extract the value of "CLEANFILES += ..." and "MOSTLYCLEANFILES += ...". + regex_find = list() + snippet = snippet.replace('\\\n', '') + pattern = compiler('^CLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M) + regex_find += pattern.findall(snippet) + pattern = compiler('^MOSTLYCLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M) + regex_find += pattern.findall(snippet) + regex_find = [line.strip() for line in regex_find if line.strip()] + for part in regex_find: + cleaned_files += \ + [line.strip() for line in part.split(' ') if line.strip()] + + # Extract the value of "BUILT_SOURCES += ...". Remove variable references + # such $(FOO_H) because they don't refer to distributed files. + regex_find = list() + pattern = compiler('^BUILT_SOURCES[\t ]*\\+=(.*?)$', re.S | re.M) + regex_find += pattern.findall(snippet) + regex_find = [line.strip() for line in regex_find if line.strip()] + for part in regex_find: + built_sources += \ + [line.strip() for line in part.split(' ') if line.strip()] + built_sources = [line for line in built_sources \ + if not bool(compiler('[$]\\([A-Za-z0-9_]*\\)$').findall(line))] + distributed_built_sources = [file for file in built_sources \ + if file not in cleaned_files] + + if inctests: + # Likewise for built files in the $testsbase directory. + path = joinpath(self.testdir, testsbase, 'Makefile.am') + with codecs.open(path, 'rb', 'UTF-8') as file: + snippet = file.read() + + # Extract the value of "CLEANFILES += ..." and "MOSTLYCLEANFILES += ...". + regex_find = list() + snippet = snippet.replace('\\\n', '') + pattern = compiler('^CLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M) + regex_find += pattern.findall(snippet) + pattern = compiler('^MOSTLYCLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M) + regex_find += pattern.findall(snippet) + regex_find = [line.strip() for line in regex_find if line.strip()] + for part in regex_find: + tests_cleaned_files += \ + [line.strip() for line in part.split(' ') if line.strip()] + + # Extract the value of "BUILT_SOURCES += ...". Remove variable references + # such $(FOO_H) because they don't refer to distributed files. + regex_find = list() + tests_built_sources = list() + pattern = compiler('^BUILT_SOURCES[\t ]*\\+=(.*?)$', re.S | re.M) + regex_find += pattern.findall(snippet) + regex_find = [line.strip() for line in regex_find if line.strip()] + for part in regex_find: + tests_built_sources += \ + [line.strip() for line in part.split(' ') if line.strip()] + tests_built_sources = [line for line in tests_built_sources \ + if not bool(compiler('[$]\\([A-Za-z0-9_]*\\)$').findall(line))] + tests_distributed_built_sources = [file for file in tests_built_sources \ + if file not in cleaned_files] + + if distributed_built_sources or tests_distributed_built_sources: + os.chdir(self.testdir) + sp.call('./configure') + if distributed_built_sources: + os.chdir(sourcebase) + with codecs.open('Makefile', 'ab', 'UTF-8') as file: + file.write('built_sources: $(BUILT_SOURCES)\n') + args = [UTILS['make'], + 'AUTOCONF=%s' % UTILS['autoconf'], + 'AUTOHEADER=%s' % UTILS['autoheader'], + 'ACLOCAL=%s' % UTILS['aclocal'], + 'AUTOMAKE=%s' % UTILS['automake'], + 'AUTORECONF=%s' % UTILS['autoreconf'], + 'built_sources'] + sp.call(args) + os.chdir('..') + if tests_distributed_built_sources: + os.chdir(testsbase) + with codecs.open('Makefile', 'ab', 'UTF-8') as file: + file.write('built_sources: $(BUILT_SOURCES)\n') + args = [UTILS['make'], + 'AUTOCONF=%s' % UTILS['autoconf'], + 'AUTOHEADER=%s' % UTILS['autoheader'], + 'ACLOCAL=%s' % UTILS['aclocal'], + 'AUTOMAKE=%s' % UTILS['automake'], + 'AUTORECONF=%s' % UTILS['autoreconf'], + 'built_sources'] + sp.call(args) + os.chdir('..') + args = [UTILS['make'], + 'AUTOCONF=%s' % UTILS['autoconf'], + 'AUTOHEADER=%s' % UTILS['autoheader'], + 'ACLOCAL=%s' % UTILS['aclocal'], + 'AUTOMAKE=%s' % UTILS['automake'], + 'AUTORECONF=%s' % UTILS['autoreconf'], + 'AUTOPOINT=%s' % UTILS['autopoint'], + 'LIBTOOLIZE=%s' % UTILS['libtoolize'], + 'distclean'] + sp.call(args) + sp.call(['rm', '-rf', self.config['tempdir']], shell=False) + + +#=============================================================================== +# Define GLMegaTestDir class +#=============================================================================== +class GLMegaTestDir(object): + '''GLMegaTestDir class is used to create a mega scratch package with the + given modules one by one and all together.''' + + def __init__(self, config, megatestdir): + '''GLMegaTestDir.__init__(config, megatestdir) -> GLMegaTestDir + + Create new GLTestDir instance.''' + if type(config) is not GLConfig: + raise(TypeError('config must be a GLConfig, not %s' % \ + type(config).__name__)) + if type(megatestdir) is bytes or type(megatestdir) is string: + if type(megatestdir) is bytes: + megatestdir = megatestdir.decode(ENCS['default']) + self.config = config + self.megatestdir = os.path.normpath(megatestdir) + if not os.path.exists(self.megatestdir): + try: # Try to create directory + os.mkdir(self.megatestdir) + except Exception as error: + raise(GLError(19, self.megatestdir)) + self.emiter = GLEmiter(self.config) + self.filesystem = GLFileSystem(self.config) + self.modulesystem = GLModuleSystem(self.config) + self.moduletable = GLModuleTable(self.config) + self.assistant = GLFileAssistant(self.config) + self.makefiletable = GLMakefileTable(self.config) + + def execute(self): + '''GLMegaTestDir.execute() + + Create a mega scratch package with the given modules one by one and all + together.''' + megasubdirs = list() + modules = [self.modulesystem.find(m) for m in self.config['modules']] + if not modules: + modules = self.modulesystem.list() + modules = [self.modulesystem.find(m) for m in modules] + modules = sorted(set(modules)) + + # First, all modules one by one. + for module in modules: + self.config.setModules([str(module)]) + #GLTestDir(self.config, self.megatestdir).execute() + megasubdirs += [str(module)] + + # Then, all modules all together. + # Except config-h, which breaks all modules which use HAVE_CONFIG_H. + modules = [module for module in modules if str(module) != 'config-h'] + self.config.setModules([str(module) for module in modules]) + #GLTestDir(self.config, self.megatestdir).execute() + megasubdirs += ['ALL'] + + # Create autobuild. + emit = string() + repdict = dict() + repdict['Jan'] = repdict['January'] = '01' + repdict['Feb'] = repdict['February'] = '02' + repdict['Mar'] = repdict['March'] = '03' + repdict['Apr'] = repdict['April'] = '04' + repdict['May'] = repdict['May'] = '05' + repdict['Jun'] = repdict['June'] = '06' + repdict['Jul'] = repdict['July'] = '07' + repdict['Aug'] = repdict['August'] = '08' + repdict['Sep'] = repdict['September'] = '09' + repdict['Oct'] = repdict['October'] = '10' + repdict['Nov'] = repdict['November'] = '11' + repdict['Dec'] = repdict['December'] = '12' + if isfile(joinpath(DIRS['root'], 'CVS', 'Entries')): + vc_witness = joinpath(DIRS['root'], 'CVS', 'Entries') + else: # if not isfile(joinpath(DIRS['root'], 'CVS', 'Entries')) + vc_witness = joinpath(DIRS['root'], '.git', 'refs', 'heads', 'master') + mdate_sh = joinpath(DIRS['root'], 'build-aux', 'mdate-sh') + args = ['sh', mdate_sh, vc_witness] + cvsdate = sp.check_output(args).decode(ENCS['shell']).strip() + for key in repdict: + if len(key) > 3: + cvsdate = cvsdate.replace(key, repdict[key]) + for key in repdict: + cvsdate = cvsdate.replace(key, repdict[key]) + cvsdate = ''.join([date for date in cvsdate.split(' ') if date.strip()]) + cvsdate = '%s%s%s' % (cvsdate[4:], cvsdate[2:4], cvsdate[:2]) + emit += '#!/bin/sh\n' + emit += 'CVSDATE=%s\n' % cvsdate + emit += ': ${MAKE=make}\n' + emit += 'test -d logs || mkdir logs\n' + emit += 'for module in %s; do\n' % ' '.join(megasubdirs) + emit += ' echo "Working on module $module..."\n' + emit += ' safemodule=`echo $module | sed -e \'s|/|-|g\'`\n' + emit += ' (echo "To: address@hidden"\\\n' + emit += ' echo\n' + emit += ' set -x\n' + emit += ' : autobuild project... $module\n' + emit += ' : autobuild revision... cvs-$CVSDATE-000000\n' + emit += ' : autobuild timestamp... `date "+%Y%m%d-%H%M%S"`\n' + emit += ' : autobuild hostname... `hostname`\n' + emit += ' cd $module && ./configure $CONFIGURE_OPTIONS && $MAKE' + emit += ' && $MAKE check && $MAKE distclean\n' + emit += ' echo rc=$?\n' + emit += ' ) 2>&1 | { if test -n "$AUTOBUILD_SUBST"; then ' + emit += 'sed -e "$AUTOBUILD_SUBST"; else cat; fi; } > logs/$safemodule\n' + emit += 'done\n' + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + path = joinpath(self.megatestdir, 'do-autobuild') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(emit) + + # Create Makefile.am. + emit = string() + emit += '## Process this file with automake to produce Makefile.in.\n\n' + emit += 'AUTOMAKE_OPTIONS = 1.5 foreign\n\n' + emit += 'SUBDIRS = %s\n\n' % ' '.join(megasubdirs) + emit += 'EXTRA_DIST = do-autobuild\n' + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + path = joinpath(self.megatestdir, 'Makefile.am') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(emit) + + + emit = string() + emit += '# Process this file with autoconf ' + emit += 'to produce a configure script.\n' + emit += 'AC_INIT([dummy], [0])\n\n' + if auxdir != '.': + emit += 'AC_CONFIG_AUX_DIR([%s])\n' % auxdir + emit += 'AM_INIT_AUTOMAKE\n\n' + emit += 'AC_PROG_MAKE_SET\n\n' + emit += 'AC_CONFIG_SUBDIRS([%s])\n' % ' '.megasubdirs + emit += 'AC_CONFIG_FILES([Makefile])\n' + emit += 'AC_OUTPUT\n' + emit = constants.nlconvert(emit) + if type(emit) is bytes: + emit = emit.decode(ENCS['default']) + path = joinpath(self.megatestdir, 'Makefile.am') + with codecs.open(path, 'wb', 'UTF-8') as file: + file.write(emit) + + # Create autogenerated files. + os.chdir(DIRS['cwd']) + args = [UTILS['aclocal']] + constants.execute(args, verbose) + try: # Try to make a directory + if not isdir('build-aux'): + os,mkdir('build-aux') + except Exception as error: + pass + args = [UTILS['autoconf']] + constants.execute(args, verbose) + args = [UTILS['automake'], '--add-missing', '--copy'] + constants.execute(args, verbose) + sp.call(['rm', '-rf', self.config['tempdir']], shell=False) + diff --git a/pygnulib/GLTestDir.pyc b/pygnulib/GLTestDir.pyc new file mode 100644 index 000000000..ce8871d05 Binary files /dev/null and b/pygnulib/GLTestDir.pyc differ diff --git a/pygnulib/__init__.py b/pygnulib/__init__.py new file mode 100644 index 000000000..5fbb4220a --- /dev/null +++ b/pygnulib/__init__.py @@ -0,0 +1,21 @@ +'''Gnulib - The GNU Portability Library +GNU software has a well-deserved reputation for running on many different types +of systems. While our primary goal is to write software for the GNU system, many +users and developers have been introduced to us through the systems that they +were already using. +Gnulib is a central location for common GNU code, intended to be shared among +GNU packages. GCC has libiberty, but this is hard to disentangle from the GCC +build tree. +Gnulib takes a different approach. Its components are intended to be shared at +the source level, rather than being a library that gets built, installed, and +linked against. Thus, there is no distribution tarball; the idea is to copy +files from Gnulib into your own source tree. +Gnulib also includes copies of a few files purely for convenience: the GNU +coding standards, the GNU maintainer information, the GPL and other licenses (in +Texinfo), assorted configuration scripts, and more. The goal is to provide all +the common infrastructure needed by GNU packages.''' + +__copyright__ = '2012 Free Software Foundation, Inc.' +__author__ = 'Dmitriy Selyutin' +__license__ = 'GNU GPLv3+' + diff --git a/pygnulib/__init__.pyc b/pygnulib/__init__.pyc new file mode 100644 index 000000000..ce2070ddf Binary files /dev/null and b/pygnulib/__init__.pyc differ diff --git a/pygnulib/classes.py b/pygnulib/classes.py new file mode 100644 index 000000000..3756db3c6 --- /dev/null +++ b/pygnulib/classes.py @@ -0,0 +1,77 @@ +#!/usr/bin/python +# encoding: UTF-8 + +'''An easy access to pygnulib classes.''' + +#=============================================================================== +# Define global imports +#=============================================================================== +__all__ = list() + +try: + # Constants + from . import constants + + # Main classes + from .GLConfig import GLConfig + from .GLError import GLError + from .GLInfo import GLInfo + + # File system + from .GLFileSystem import GLFileSystem + from .GLFileSystem import GLFileAssistant + + # Module system + from .GLModuleSystem import GLModule + from .GLModuleSystem import GLModuleSystem + from .GLModuleSystem import GLModuleTable + + # Different modes + from .GLImport import GLImport + from .GLEmiter import GLEmiter + from .GLTestDir import GLTestDir + from .GLTestDir import GLMegaTestDir + + # Other modules + from .GLMakefileTable import GLMakefileTable +except ValueError as error: + # Constants + import constants + + # Main classes + from GLConfig import GLConfig + from GLError import GLError + from GLInfo import GLInfo + + # File system + from GLFileSystem import GLFileSystem + from GLFileSystem import GLFileAssistant + + # Module system + from GLModuleSystem import GLModule + from GLModuleSystem import GLModuleSystem + from GLModuleSystem import GLModuleTable + + # Different modes + from GLImport import GLImport + from GLEmiter import GLEmiter + from GLTestDir import GLTestDir + from GLTestDir import GLMegaTestDir + + # Other modules + from GLMakefileTable import GLMakefileTable + +# Append modules to namespace. +__all__ += ['GLConfig', 'GLError', 'GLInfo'] +__all__ += ['GLFileSystem', 'GLFileAssistant'] +__all__ += ['GLModule', 'GLModuleSystem', 'GLModuleTable'] +__all__ += ['GLImport', 'GLEmiter', 'GLTestDir'] +__all__ += ['GLMakefileTable'] + +#=============================================================================== +# Define module information +#=============================================================================== +__author__ = constants.__author__ +__license__ = constants.__license__ +__copyright__ = constants.__copyright__ + diff --git a/pygnulib/classes.pyc b/pygnulib/classes.pyc new file mode 100644 index 000000000..2263ec9e5 Binary files /dev/null and b/pygnulib/classes.pyc differ diff --git a/pygnulib/constants.py b/pygnulib/constants.py new file mode 100644 index 000000000..d2995c7ab --- /dev/null +++ b/pygnulib/constants.py @@ -0,0 +1,436 @@ +#!/usr/bin/python +# encoding: UTF-8 + +'''An easy access to pygnulib constants.''' + +from __future__ import unicode_literals +#=============================================================================== +# Define global imports +#=============================================================================== +import re +import os +import sys +import platform +import tempfile +import subprocess as sp + + +#=============================================================================== +# Define module information +#=============================================================================== +__all__ = list() +__author__ = \ +[ + 'Bruno Haible', + 'Paul Eggert', + 'Simon Josefsson', + 'Dmitriy Selyutin', +] +__license__ = 'GNU GPLv3+' +__copyright__ = '2012 Free Software Foundation, Inc.' + + +#=============================================================================== +# Backward compatibility +#=============================================================================== +# Check for Python version +if sys.version_info.major == 2: + PYTHON3 = False +else: + PYTHON3 = True + +# Create string compatibility +if not PYTHON3: + string = unicode +else: # if PYTHON3 + string = str + +# Current working directory +if not PYTHON3: + os.getcwdb = os.getcwd + os.getcwd = os.getcwdu + + +#=============================================================================== +# Define global constants +#=============================================================================== +# Declare neccessary variables +APP = dict() # Application +DIRS = dict() # Directories +UTILS = dict() # Utilities +ENCS = dict() # Encodings +FILES = dict() # Files +MODES = dict() # Modes +TESTS = dict() # Tests +NL = ''' +''' # Newline character +ALPHANUMERIC = 'abcdefghijklmnopqrstuvwxyz\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ\ +0123456789' # Alphanumeric characters + +# Set ENCS dictionary +import __main__ as interpreter +if not hasattr(interpreter, '__file__'): + if sys.stdout.encoding != None: + ENCS['default'] = sys.stdout.encoding + else: # sys.stdout.encoding == None + ENCS['default'] = 'UTF-8' +else: # if hasattr(interpreter, '__file__'): + ENCS['default'] = 'UTF-8' +ENCS['system'] = sys.getfilesystemencoding() +ENCS['shell'] = sys.stdout.encoding +if ENCS['shell'] == None: + ENCS['shell'] = 'UTF-8' + +# Set APP dictionary +APP['name'] = sys.argv[0] +if not APP['name']: + APP['name'] = 'gnulib-tool.py' +APP['path'] = os.path.realpath(sys.argv[0]) +if type(APP['name']) is bytes: + APP['name'] = string(APP['name'], ENCS['system']) +if type(APP['path']) is bytes: + APP['path'] = string(APP['path'], ENCS['system']) + +# Set DIRS dictionary +DIRS['root'] = os.path.dirname(APP['path']) +DIRS['cwd'] = os.getcwd() +DIRS['build-aux'] = os.path.join(DIRS['root'], 'build-aux') +DIRS['config'] = os.path.join(DIRS['root'], 'config') +DIRS['doc'] = os.path.join(DIRS['root'], 'doc') +DIRS['lib'] = os.path.join(DIRS['root'], 'lib') +DIRS['m4'] = os.path.join(DIRS['root'], 'm4') +DIRS['modules'] = os.path.join(DIRS['root'], 'modules') +DIRS['tests'] = os.path.join(DIRS['root'], 'tests') +DIRS['git'] = os.path.join(DIRS['root'], '.git') +DIRS['cvs'] = os.path.join(DIRS['root'], 'CVS') + +# Set FILES dictionary +FILES['changelog'] = os.path.join(DIRS['root'], 'ChangeLog') + +# Set MODES dictionary +MODES = \ +{ + 'import': 0, + 'add-import': 1, + 'remove-import': 2, + 'update': 3, +} +MODES['verbose-min'] = -2 +MODES['verbose-default'] = 0 +MODES['verbose-max'] = 2 + +# Set TESTS dictionary +TESTS = \ +{ + 'tests': 0, + 'obsolete': 1, + 'c++-test': 2, + 'cxx-test': 2, + 'c++-tests': 2, + 'cxx-tests': 2, + 'longrunning-test': 3, + 'longrunning-tests': 3, + 'privileged-test': 4, + 'privileged-tests': 4, + 'unportable-test': 5, + 'unportable-tests': 5, + 'all-test': 6, + 'all-tests': 6, +} + +# Define AUTOCONF minimum version +DEFAULT_AUTOCONF_MINVERSION = 2.59 +# You can set AUTOCONFPATH to empty if autoconf 2.57 is already in your PATH +AUTOCONFPATH = '' +# You can set AUTOMAKEPATH to empty if automake 1.9.x is already in your PATH +AUTOMAKEPATH = '' +# You can set GETTEXTPATH to empty if autopoint 0.15 is already in your PATH +GETTEXTPATH = '' +# You can set LIBTOOLPATH to empty if libtoolize 2.x is already in your PATH +LIBTOOLPATH = '' + +# You can also set the variable AUTOCONF individually +if AUTOCONFPATH: + UTILS['autoconf'] = AUTOCONFPATH + 'autoconf' +else: + if os.getenv('AUTOCONF'): + UTILS['autoconf'] = os.getenv('AUTOCONF') + else: + UTILS['autoconf'] = 'autoconf' + +# You can also set the variable AUTORECONF individually +if AUTOCONFPATH: + UTILS['autoreconf'] = AUTOCONFPATH + 'autoreconf' +else: + if os.getenv('AUTORECONF'): + UTILS['autoreconf'] = os.getenv('AUTORECONF') + else: + UTILS['autoreconf'] = 'autoreconf' + +# You can also set the variable AUTOHEADER individually +if AUTOCONFPATH: + UTILS['autoheader'] = AUTOCONFPATH + 'autoheader' +else: + if os.getenv('AUTOHEADER'): + UTILS['autoheader'] = os.getenv('AUTOHEADER') + else: + UTILS['autoheader'] = 'autoheader' + +# You can also set the variable AUTOMAKE individually +if AUTOMAKEPATH: + UTILS['automake'] = AUTOMAKEPATH + 'automake' +else: + if os.getenv('AUTOMAKE'): + UTILS['automake'] = os.getenv('AUTOMAKE') + else: + UTILS['automake'] = 'automake' + +# You can also set the variable ACLOCAL individually +if AUTOMAKEPATH: + UTILS['aclocal'] = AUTOMAKEPATH + 'aclocal' +else: + if os.getenv('ACLOCAL'): + UTILS['aclocal'] = os.getenv('ACLOCAL') + else: + UTILS['aclocal'] = 'aclocal' + +# You can also set the variable AUTOPOINT individually +if GETTEXTPATH: + UTILS['autopoint'] = GETTEXTPATH + 'autopoint' +else: + if os.getenv('AUTOPOINT'): + UTILS['autopoint'] = os.getenv('AUTOPOINT') + else: + UTILS['autopoint'] = 'autopoint' + +# You can also set the variable LIBTOOLIZE individually +if LIBTOOLPATH: + UTILS['libtoolize'] = LIBTOOLPATH + 'libtoolize' +else: + if os.getenv('LIBTOOLIZE'): + UTILS['libtoolize'] = os.getenv('LIBTOOLIZE') + else: + UTILS['libtoolize'] = 'libtoolize' + +# You can also set the variable MAKE +if os.getenv('MAKE'): + UTILS['make'] = os.getenv('MAKE') +else: + UTILS['make'] = 'make' + + +#=============================================================================== +# Define global functions +#=============================================================================== +def execute(args, verbose): + '''Execute the given shell command.''' + if verbose >= 0: + print("executing %s" % ' '.join(args)) + try: # Try to run + retcode = sp.call(args) + except Exception as error: + print(error) + sys.exit(1) + else: + # Commands like automake produce output to stderr even when they succeed. + # Turn this output off if the command succeeds. + temp = tempfile.mktemp() + if type(temp) is bytes: + temp = temp.decode(ENCS['system']) + xargs = '%s > %s 2>&1' % (' '.join(args), temp) + try: # Try to run + retcode = sp.call(xargs, shell=True) + except Exception as error: + print(error) + sys.exit(1) + if retcode == 0: + os.remove(temp) + else: + print("executing %s" % ' '.join(args)) + with codecs.open(temp, 'rb') as file: + cmdout = file.read() + print(cmdout) + os.remove(temp) + sys.exit(retcode) + +def compiler(pattern, flags=0): + '''Compile regex pattern depending on version of Python.''' + if not PYTHON3: + pattern = re.compile(pattern, re.UNICODE | flags) + else: # if PYTHON3 + pattern = re.compile(pattern, flags) + return(pattern) + +def cleaner(sequence): + '''Clean string or list of strings after using regex.''' + if type(sequence) is string: + sequence = sequence.replace('[', '') + sequence = sequence.replace(']', '') + elif type(sequence) is list: + sequence = [value.replace('[', '').replace(']', '') for value in sequence] + sequence = [value.replace('(', '').replace(')', '') for value in sequence] + sequence = [False if value == 'false' else value for value in sequence] + sequence = [True if value == 'true' else value for value in sequence] + sequence = [value.strip() for value in sequence] + return(sequence) + +def joinpath(head, *tail): + '''joinpath(head, *tail) -> string + + Join two or more pathname components, inserting '/' as needed. If any + component is an absolute path, all previous path components will be + discarded. The second argument may be string or list of strings.''' + newtail = list() + if type(head) is bytes: + head = head.decode(ENCS['default']) + for item in tail: + if type(item) is bytes: + item = item.decode(ENCS['default']) + newtail += [item] + result = os.path.normpath(os.path.join(head, *tail)) + if type(result) is bytes: + result = result.decode(ENCS['default']) + return(result) + +def relativize(dir1, dir2): + '''Compute a relative pathname reldir such that dir1/reldir = dir2.''' + dir0 = os.getcwd() + if type(dir1) is bytes: + dir1 = dir1.decode(ENCS['default']) + if type(dir2) is bytes: + dir2 = dir2.decode(ENCS['default']) + while dir1: + dir1 = '%s%s' % (os.path.normpath(dir1), os.path.sep) + dir2 = '%s%s' % (os.path.normpath(dir2), os.path.sep) + if dir1.startswith(os.path.sep): + first = dir1[:dir1.find(os.path.sep, 1)] + else: # if not dir1.startswith('/') + first = dir1[:dir1.find(os.path.sep)] + if first != '.': + if first == '..': + dir2 = os.path.basename(joinpath(dir0, dir2)) + dir0 = os.path.dirname(dir0) + else: # if first != '..' + # Get first component of dir2 + if dir2.startswith(os.path.sep): + first2 = dir2[:dir2.find(os.path.sep, 1)] + else: # if not dir1.startswith('/') + first2 = dir2[:dir2.find(os.path.sep)] + if first == first2: + dir2 = dir2[dir2.find(os.path.sep)+1:] + else: # if first != first2 + dir2 = joinpath('..', dir2) + dir0 = joinpath(dir0, first) + dir1 = dir1[dir1.find(os.path.sep)+1:] + result = os.path.normpath(dir2) + return(result) + +def link_relative(src, dest): + '''Like ln -s, except that src is given relative to the current directory + (or absolute), not given relative to the directory of dest.''' + if type(src) is bytes or type(src) is string: + if type(src) is bytes: + src = src.decode(ENCS['default']) + else: # if src has not bytes or string type + raise(TypeError( + 'src must be a string, not %s' % (type(src).__name__))) + if type(dest) is bytes or type(dest) is string: + if type(dest) is bytes: + dest = dest.decode(ENCS['default']) + else: # if dest has not bytes or string type + raise(TypeError( + 'dest must be a string, not %s' % (type(dest).__name__))) + if src.startswith('/'): + os.symlink(src, dest) + else: # if not src.startswith('/') + if dest.startswith('/'): + if not constants.PYTHON3: + cwd = os.getcwdu() + else: # if constants.PYTHON3 + cwd = os.getcwd() + os.symlink(joinpath(cwd, src), dest) + else: # if not dest.startswith('/') + destdir = os.path.dirname(dest) + if not destdir: + destdir = '.' + if type(destdir) is bytes: + destdir = destdir.decode(ENCS['default']) + src = relativize(destdir, src) + os.symlink(src, dest) + +def link_if_changed(src, dest): + '''Create a symlink, but avoids munging timestamps if the link is correct.''' + if type(src) is bytes: + src = src.decode(ENCS['default']) + if type(dest) is bytes: + dest = dest.decode(ENCS['default']) + ln_target = os.path.realpath(src) + if not (os.path.islink(dest) and src == ln_target): + os.remove(dest) + link_relative(src, dest) + +def filter_filelist(separator, filelist, + prefix, suffix, removed_prefix, removed_suffix, + added_prefix=string(), added_suffix=string()): + '''filter_filelist(*args) -> list + + Filter the given list of files. Filtering: Only the elements starting with + prefix and ending with suffix are considered. Processing: removed_prefix + and removed_suffix are removed from each element, added_prefix and + added_suffix are added to each element.''' + listing = list() + for filename in filelist: + if filename.startswith(prefix) and filename.endswith(suffix): + pattern = compiler('^%s(.*?)%s$' % (removed_prefix, removed_suffix)) + result = pattern.sub('%s\\1%s' % (added_prefix, added_suffix), filename) + listing += [result] + result = separator.join(listing) + return(result) + +def substart(orig, repl, data): + result = data + if data.startswith(orig): + result = repl +data[len(orig):] + return(result) + +def subend(orig, repl, data): + result = data + if data.endswith(orig): + result = repl +data[:len(repl)] + return(result) + +def nlconvert(text): + '''Convert line-endings to specific for this platform.''' + system = platform.system().lower() + text = text.replace('\r\n', '\n') + if system == 'windows': + text = text.replace('\n', '\r\n') + return(text) + +def nlremove(text): + '''Remove empty lines from the source text.''' + text = nlconvert(text) + text = text.replace('\r\n', '\n') + lines = [line for line in text.split('\n') if line != ''] + text = '\n'.join(lines) + text = nlconvert(text) + return(text) + +def nlcount(text): + '''Return count of newlines before and after text.''' + counter = int() + before = int() + after = int() + text = text.replace('\r\n', '\n') + while text[counter] == '\n': + before += 1 + counter += 1 + counter = len(text) -1 + while text[counter] == '\n': + after += 1 + counter -= 1 + print(before, after) + +__all__ += ['APP', 'DIRS', 'FILES', 'MODES', 'UTILS'] + diff --git a/pygnulib/constants.pyc b/pygnulib/constants.pyc new file mode 100644 index 000000000..8c7808bcc Binary files /dev/null and b/pygnulib/constants.pyc differ