#!/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__ = '2002-2017 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 necessary variables APP = dict() # Application DIRS = dict() # Directories UTILS = dict() # Utilities ENCS = dict() # Encodings 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 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('/') or (len(src) >= 2 and src[1] == ':'): os.symlink(src, dest) else: # if src is not absolute if dest.startswith('/') or (len(dest) >= 2 and dest[1] == ':'): if not constants.PYTHON3: cwd = os.getcwdu() else: # if constants.PYTHON3 cwd = os.getcwd() os.symlink(joinpath(cwd, src), dest) else: # if dest is not absolute 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): '''Replaces the start portion of a string. Returns data with orig replaced by repl, but only at the beginning of data. Like data.replace(orig,repl), except only at the beginning of data.''' result = data if data.startswith(orig): result = repl + data[len(orig):] return(result) def subend(orig, repl, data): '''Replaces the end portion of a string. Returns data with orig replaced by repl, but only at the end of data. Like data.replace(orig,repl), except only at the end of data.''' result = data if data.endswith(orig): result = data[:-len(orig)] + 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 remove_backslash_newline(text): '''Given a multiline string text, join lines: When a line ends in a backslash, remove the backslash and join the next line to it.''' return text.replace('\\\n', '') def combine_lines(text): '''Given a multiline string text, join lines by spaces: When a line ends in a backslash, remove the backslash and join the next line to it, inserting a space between them.''' return text.replace('\\\n', ' ') def combine_lines_matching(pattern, text): '''Given a multiline string text, join lines by spaces, when the first such line matches a given RegexObject pattern. When a line that matches the pattern ends in a backslash, remove the backslash and join the next line to it, inserting a space between them. When a line that is the result of such a join ends in a backslash, proceed likewise.''' outerpos = 0 match = pattern.search(text, outerpos) while match: (startpos, pos) = match.span() # Look how far the continuation lines extend. pos = text.find('\n',pos) while pos > 0 and text[pos-1] == '\\': pos = text.find('\n',pos+1) if pos < 0: pos = len(text) # Perform a combine_lines throughout the continuation lines. partdone = text[:startpos] + combine_lines(text[startpos:pos]) outerpos = len(partdone) text = partdone + text[pos:] # Next round. match = pattern.search(text, outerpos) return text __all__ += ['APP', 'DIRS', 'MODES', 'UTILS']