blob: 39fe08f002ee72bb61f3d2566fa0791edfdb7027 [file] [log] [blame]
#!/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']