unigbrk/uc-grapheme-breaks: Fix build failure.
[gnulib.git] / pygnulib / constants.py
blob39fe08f002ee72bb61f3d2566fa0791edfdb7027
1 #!/usr/bin/python
2 # encoding: UTF-8
4 '''An easy access to pygnulib constants.'''
6 from __future__ import unicode_literals
7 #===============================================================================
8 # Define global imports
9 #===============================================================================
10 import re
11 import os
12 import sys
13 import platform
14 import tempfile
15 import subprocess as sp
18 #===============================================================================
19 # Define module information
20 #===============================================================================
21 __all__ = list()
22 __author__ = \
24 'Bruno Haible',
25 'Paul Eggert',
26 'Simon Josefsson',
27 'Dmitriy Selyutin',
29 __license__ = 'GNU GPLv3+'
30 __copyright__ = '2002-2017 Free Software Foundation, Inc.'
33 #===============================================================================
34 # Backward compatibility
35 #===============================================================================
36 # Check for Python version
37 if sys.version_info.major == 2:
38 PYTHON3 = False
39 else:
40 PYTHON3 = True
42 # Create string compatibility
43 if not PYTHON3:
44 string = unicode
45 else: # if PYTHON3
46 string = str
48 # Current working directory
49 if not PYTHON3:
50 os.getcwdb = os.getcwd
51 os.getcwd = os.getcwdu
54 #===============================================================================
55 # Define global constants
56 #===============================================================================
57 # Declare necessary variables
58 APP = dict() # Application
59 DIRS = dict() # Directories
60 UTILS = dict() # Utilities
61 ENCS = dict() # Encodings
62 MODES = dict() # Modes
63 TESTS = dict() # Tests
64 NL = '''
65 ''' # Newline character
66 ALPHANUMERIC = 'abcdefghijklmnopqrstuvwxyz\
67 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
68 0123456789' # Alphanumeric characters
70 # Set ENCS dictionary
71 import __main__ as interpreter
72 if not hasattr(interpreter, '__file__'):
73 if sys.stdout.encoding != None:
74 ENCS['default'] = sys.stdout.encoding
75 else: # sys.stdout.encoding == None
76 ENCS['default'] = 'UTF-8'
77 else: # if hasattr(interpreter, '__file__'):
78 ENCS['default'] = 'UTF-8'
79 ENCS['system'] = sys.getfilesystemencoding()
80 ENCS['shell'] = sys.stdout.encoding
81 if ENCS['shell'] == None:
82 ENCS['shell'] = 'UTF-8'
84 # Set APP dictionary
85 APP['name'] = sys.argv[0]
86 if not APP['name']:
87 APP['name'] = 'gnulib-tool.py'
88 APP['path'] = os.path.realpath(sys.argv[0])
89 if type(APP['name']) is bytes:
90 APP['name'] = string(APP['name'], ENCS['system'])
91 if type(APP['path']) is bytes:
92 APP['path'] = string(APP['path'], ENCS['system'])
94 # Set DIRS dictionary
95 DIRS['root'] = os.path.dirname(APP['path'])
96 DIRS['cwd'] = os.getcwd()
97 DIRS['build-aux'] = os.path.join(DIRS['root'], 'build-aux')
98 DIRS['config'] = os.path.join(DIRS['root'], 'config')
99 DIRS['doc'] = os.path.join(DIRS['root'], 'doc')
100 DIRS['lib'] = os.path.join(DIRS['root'], 'lib')
101 DIRS['m4'] = os.path.join(DIRS['root'], 'm4')
102 DIRS['modules'] = os.path.join(DIRS['root'], 'modules')
103 DIRS['tests'] = os.path.join(DIRS['root'], 'tests')
104 DIRS['git'] = os.path.join(DIRS['root'], '.git')
105 DIRS['cvs'] = os.path.join(DIRS['root'], 'CVS')
107 # Set MODES dictionary
108 MODES = \
110 'import': 0,
111 'add-import': 1,
112 'remove-import': 2,
113 'update': 3,
115 MODES['verbose-min'] = -2
116 MODES['verbose-default'] = 0
117 MODES['verbose-max'] = 2
119 # Set TESTS dictionary
120 TESTS = \
122 'tests': 0,
123 'obsolete': 1,
124 'c++-test': 2,
125 'cxx-test': 2,
126 'c++-tests': 2,
127 'cxx-tests': 2,
128 'longrunning-test': 3,
129 'longrunning-tests': 3,
130 'privileged-test': 4,
131 'privileged-tests': 4,
132 'unportable-test': 5,
133 'unportable-tests': 5,
134 'all-test': 6,
135 'all-tests': 6,
138 # Define AUTOCONF minimum version
139 DEFAULT_AUTOCONF_MINVERSION = 2.59
140 # You can set AUTOCONFPATH to empty if autoconf 2.57 is already in your PATH
141 AUTOCONFPATH = ''
142 # You can set AUTOMAKEPATH to empty if automake 1.9.x is already in your PATH
143 AUTOMAKEPATH = ''
144 # You can set GETTEXTPATH to empty if autopoint 0.15 is already in your PATH
145 GETTEXTPATH = ''
146 # You can set LIBTOOLPATH to empty if libtoolize 2.x is already in your PATH
147 LIBTOOLPATH = ''
149 # You can also set the variable AUTOCONF individually
150 if AUTOCONFPATH:
151 UTILS['autoconf'] = AUTOCONFPATH + 'autoconf'
152 else:
153 if os.getenv('AUTOCONF'):
154 UTILS['autoconf'] = os.getenv('AUTOCONF')
155 else:
156 UTILS['autoconf'] = 'autoconf'
158 # You can also set the variable AUTORECONF individually
159 if AUTOCONFPATH:
160 UTILS['autoreconf'] = AUTOCONFPATH + 'autoreconf'
161 else:
162 if os.getenv('AUTORECONF'):
163 UTILS['autoreconf'] = os.getenv('AUTORECONF')
164 else:
165 UTILS['autoreconf'] = 'autoreconf'
167 # You can also set the variable AUTOHEADER individually
168 if AUTOCONFPATH:
169 UTILS['autoheader'] = AUTOCONFPATH + 'autoheader'
170 else:
171 if os.getenv('AUTOHEADER'):
172 UTILS['autoheader'] = os.getenv('AUTOHEADER')
173 else:
174 UTILS['autoheader'] = 'autoheader'
176 # You can also set the variable AUTOMAKE individually
177 if AUTOMAKEPATH:
178 UTILS['automake'] = AUTOMAKEPATH + 'automake'
179 else:
180 if os.getenv('AUTOMAKE'):
181 UTILS['automake'] = os.getenv('AUTOMAKE')
182 else:
183 UTILS['automake'] = 'automake'
185 # You can also set the variable ACLOCAL individually
186 if AUTOMAKEPATH:
187 UTILS['aclocal'] = AUTOMAKEPATH + 'aclocal'
188 else:
189 if os.getenv('ACLOCAL'):
190 UTILS['aclocal'] = os.getenv('ACLOCAL')
191 else:
192 UTILS['aclocal'] = 'aclocal'
194 # You can also set the variable AUTOPOINT individually
195 if GETTEXTPATH:
196 UTILS['autopoint'] = GETTEXTPATH + 'autopoint'
197 else:
198 if os.getenv('AUTOPOINT'):
199 UTILS['autopoint'] = os.getenv('AUTOPOINT')
200 else:
201 UTILS['autopoint'] = 'autopoint'
203 # You can also set the variable LIBTOOLIZE individually
204 if LIBTOOLPATH:
205 UTILS['libtoolize'] = LIBTOOLPATH + 'libtoolize'
206 else:
207 if os.getenv('LIBTOOLIZE'):
208 UTILS['libtoolize'] = os.getenv('LIBTOOLIZE')
209 else:
210 UTILS['libtoolize'] = 'libtoolize'
212 # You can also set the variable MAKE
213 if os.getenv('MAKE'):
214 UTILS['make'] = os.getenv('MAKE')
215 else:
216 UTILS['make'] = 'make'
219 #===============================================================================
220 # Define global functions
221 #===============================================================================
222 def execute(args, verbose):
223 '''Execute the given shell command.'''
224 if verbose >= 0:
225 print("executing %s" % ' '.join(args))
226 try: # Try to run
227 retcode = sp.call(args)
228 except Exception as error:
229 print(error)
230 sys.exit(1)
231 else:
232 # Commands like automake produce output to stderr even when they succeed.
233 # Turn this output off if the command succeeds.
234 temp = tempfile.mktemp()
235 if type(temp) is bytes:
236 temp = temp.decode(ENCS['system'])
237 xargs = '%s > %s 2>&1' % (' '.join(args), temp)
238 try: # Try to run
239 retcode = sp.call(xargs, shell=True)
240 except Exception as error:
241 print(error)
242 sys.exit(1)
243 if retcode == 0:
244 os.remove(temp)
245 else:
246 print("executing %s" % ' '.join(args))
247 with codecs.open(temp, 'rb') as file:
248 cmdout = file.read()
249 print(cmdout)
250 os.remove(temp)
251 sys.exit(retcode)
254 def compiler(pattern, flags=0):
255 '''Compile regex pattern depending on version of Python.'''
256 if not PYTHON3:
257 pattern = re.compile(pattern, re.UNICODE | flags)
258 else: # if PYTHON3
259 pattern = re.compile(pattern, flags)
260 return(pattern)
263 def cleaner(sequence):
264 '''Clean string or list of strings after using regex.'''
265 if type(sequence) is string:
266 sequence = sequence.replace('[', '')
267 sequence = sequence.replace(']', '')
268 elif type(sequence) is list:
269 sequence = [value.replace('[', '').replace(']', '')
270 for value in sequence]
271 sequence = [value.replace('(', '').replace(')', '')
272 for value in sequence]
273 sequence = [False if value == 'false' else value for value in sequence]
274 sequence = [True if value == 'true' else value for value in sequence]
275 sequence = [value.strip() for value in sequence]
276 return(sequence)
279 def joinpath(head, *tail):
280 '''joinpath(head, *tail) -> string
282 Join two or more pathname components, inserting '/' as needed. If any
283 component is an absolute path, all previous path components will be
284 discarded. The second argument may be string or list of strings.'''
285 newtail = list()
286 if type(head) is bytes:
287 head = head.decode(ENCS['default'])
288 for item in tail:
289 if type(item) is bytes:
290 item = item.decode(ENCS['default'])
291 newtail += [item]
292 result = os.path.normpath(os.path.join(head, *tail))
293 if type(result) is bytes:
294 result = result.decode(ENCS['default'])
295 return(result)
298 def relativize(dir1, dir2):
299 '''Compute a relative pathname reldir such that dir1/reldir = dir2.'''
300 dir0 = os.getcwd()
301 if type(dir1) is bytes:
302 dir1 = dir1.decode(ENCS['default'])
303 if type(dir2) is bytes:
304 dir2 = dir2.decode(ENCS['default'])
305 while dir1:
306 dir1 = '%s%s' % (os.path.normpath(dir1), os.path.sep)
307 dir2 = '%s%s' % (os.path.normpath(dir2), os.path.sep)
308 if dir1.startswith(os.path.sep):
309 first = dir1[:dir1.find(os.path.sep, 1)]
310 else: # if not dir1.startswith('/')
311 first = dir1[:dir1.find(os.path.sep)]
312 if first != '.':
313 if first == '..':
314 dir2 = os.path.basename(joinpath(dir0, dir2))
315 dir0 = os.path.dirname(dir0)
316 else: # if first != '..'
317 # Get first component of dir2
318 if dir2.startswith(os.path.sep):
319 first2 = dir2[:dir2.find(os.path.sep, 1)]
320 else: # if not dir1.startswith('/')
321 first2 = dir2[:dir2.find(os.path.sep)]
322 if first == first2:
323 dir2 = dir2[dir2.find(os.path.sep) + 1:]
324 else: # if first != first2
325 dir2 = joinpath('..', dir2)
326 dir0 = joinpath(dir0, first)
327 dir1 = dir1[dir1.find(os.path.sep) + 1:]
328 result = os.path.normpath(dir2)
329 return(result)
332 def link_relative(src, dest):
333 '''Like ln -s, except that src is given relative to the current directory
334 (or absolute), not given relative to the directory of dest.'''
335 if type(src) is bytes or type(src) is string:
336 if type(src) is bytes:
337 src = src.decode(ENCS['default'])
338 else: # if src has not bytes or string type
339 raise(TypeError(
340 'src must be a string, not %s' % (type(src).__name__)))
341 if type(dest) is bytes or type(dest) is string:
342 if type(dest) is bytes:
343 dest = dest.decode(ENCS['default'])
344 else: # if dest has not bytes or string type
345 raise(TypeError(
346 'dest must be a string, not %s' % (type(dest).__name__)))
347 if src.startswith('/') or (len(src) >= 2 and src[1] == ':'):
348 os.symlink(src, dest)
349 else: # if src is not absolute
350 if dest.startswith('/') or (len(dest) >= 2 and dest[1] == ':'):
351 if not constants.PYTHON3:
352 cwd = os.getcwdu()
353 else: # if constants.PYTHON3
354 cwd = os.getcwd()
355 os.symlink(joinpath(cwd, src), dest)
356 else: # if dest is not absolute
357 destdir = os.path.dirname(dest)
358 if not destdir:
359 destdir = '.'
360 if type(destdir) is bytes:
361 destdir = destdir.decode(ENCS['default'])
362 src = relativize(destdir, src)
363 os.symlink(src, dest)
366 def link_if_changed(src, dest):
367 '''Create a symlink, but avoids munging timestamps if the link is correct.'''
368 if type(src) is bytes:
369 src = src.decode(ENCS['default'])
370 if type(dest) is bytes:
371 dest = dest.decode(ENCS['default'])
372 ln_target = os.path.realpath(src)
373 if not (os.path.islink(dest) and src == ln_target):
374 os.remove(dest)
375 link_relative(src, dest)
378 def filter_filelist(separator, filelist,
379 prefix, suffix, removed_prefix, removed_suffix,
380 added_prefix=string(), added_suffix=string()):
381 '''filter_filelist(*args) -> list
383 Filter the given list of files. Filtering: Only the elements starting with
384 prefix and ending with suffix are considered. Processing: removed_prefix
385 and removed_suffix are removed from each element, added_prefix and
386 added_suffix are added to each element.'''
387 listing = list()
388 for filename in filelist:
389 if filename.startswith(prefix) and filename.endswith(suffix):
390 pattern = compiler('^%s(.*?)%s$' %
391 (removed_prefix, removed_suffix))
392 result = pattern.sub('%s\\1%s' %
393 (added_prefix, added_suffix), filename)
394 listing += [result]
395 result = separator.join(listing)
396 return(result)
399 def substart(orig, repl, data):
400 '''Replaces the start portion of a string.
402 Returns data with orig replaced by repl, but only at the beginning of data.
403 Like data.replace(orig,repl), except only at the beginning of data.'''
404 result = data
405 if data.startswith(orig):
406 result = repl + data[len(orig):]
407 return(result)
410 def subend(orig, repl, data):
411 '''Replaces the end portion of a string.
413 Returns data with orig replaced by repl, but only at the end of data.
414 Like data.replace(orig,repl), except only at the end of data.'''
415 result = data
416 if data.endswith(orig):
417 result = data[:-len(orig)] + repl
418 return(result)
421 def nlconvert(text):
422 '''Convert line-endings to specific for this platform.'''
423 system = platform.system().lower()
424 text = text.replace('\r\n', '\n')
425 if system == 'windows':
426 text = text.replace('\n', '\r\n')
427 return(text)
430 def nlremove(text):
431 '''Remove empty lines from the source text.'''
432 text = nlconvert(text)
433 text = text.replace('\r\n', '\n')
434 lines = [line for line in text.split('\n') if line != '']
435 text = '\n'.join(lines)
436 text = nlconvert(text)
437 return(text)
440 def remove_backslash_newline(text):
441 '''Given a multiline string text, join lines:
442 When a line ends in a backslash, remove the backslash and join the next
443 line to it.'''
444 return text.replace('\\\n', '')
446 def combine_lines(text):
447 '''Given a multiline string text, join lines by spaces:
448 When a line ends in a backslash, remove the backslash and join the next
449 line to it, inserting a space between them.'''
450 return text.replace('\\\n', ' ')
452 def combine_lines_matching(pattern, text):
453 '''Given a multiline string text, join lines by spaces, when the first
454 such line matches a given RegexObject pattern.
455 When a line that matches the pattern ends in a backslash, remove the
456 backslash and join the next line to it, inserting a space between them.
457 When a line that is the result of such a join ends in a backslash,
458 proceed likewise.'''
459 outerpos = 0
460 match = pattern.search(text, outerpos)
461 while match:
462 (startpos, pos) = match.span()
463 # Look how far the continuation lines extend.
464 pos = text.find('\n',pos)
465 while pos > 0 and text[pos-1] == '\\':
466 pos = text.find('\n',pos+1)
467 if pos < 0:
468 pos = len(text)
469 # Perform a combine_lines throughout the continuation lines.
470 partdone = text[:startpos] + combine_lines(text[startpos:pos])
471 outerpos = len(partdone)
472 text = partdone + text[pos:]
473 # Next round.
474 match = pattern.search(text, outerpos)
475 return text
478 __all__ += ['APP', 'DIRS', 'MODES', 'UTILS']