4 '''An easy access to pygnulib constants.'''
6 from __future__
import unicode_literals
7 #===============================================================================
8 # Define global imports
9 #===============================================================================
15 import subprocess
as sp
18 #===============================================================================
19 # Define module information
20 #===============================================================================
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:
42 # Create string compatibility
48 # Current working directory
50 os
.getcwdb
= os
.getcwd
51 os
.getcwd
= os
.getcwdu
54 #===============================================================================
55 # Define global constants
56 #===============================================================================
57 # Declare neccessary 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
65 ''' # Newline character
66 ALPHANUMERIC
= 'abcdefghijklmnopqrstuvwxyz\
67 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
68 0123456789' # Alphanumeric characters
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'
85 APP
['name'] = sys
.argv
[0]
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'])
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
115 MODES
['verbose-min'] = -2
116 MODES
['verbose-default'] = 0
117 MODES
['verbose-max'] = 2
119 # Set TESTS dictionary
128 'longrunning-test': 3,
129 'longrunning-tests': 3,
130 'privileged-test': 4,
131 'privileged-tests': 4,
132 'unportable-test': 5,
133 'unportable-tests': 5,
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
142 # You can set AUTOMAKEPATH to empty if automake 1.9.x is already in your PATH
144 # You can set GETTEXTPATH to empty if autopoint 0.15 is already in your PATH
146 # You can set LIBTOOLPATH to empty if libtoolize 2.x is already in your PATH
149 # You can also set the variable AUTOCONF individually
151 UTILS
['autoconf'] = AUTOCONFPATH
+ 'autoconf'
153 if os
.getenv('AUTOCONF'):
154 UTILS
['autoconf'] = os
.getenv('AUTOCONF')
156 UTILS
['autoconf'] = 'autoconf'
158 # You can also set the variable AUTORECONF individually
160 UTILS
['autoreconf'] = AUTOCONFPATH
+ 'autoreconf'
162 if os
.getenv('AUTORECONF'):
163 UTILS
['autoreconf'] = os
.getenv('AUTORECONF')
165 UTILS
['autoreconf'] = 'autoreconf'
167 # You can also set the variable AUTOHEADER individually
169 UTILS
['autoheader'] = AUTOCONFPATH
+ 'autoheader'
171 if os
.getenv('AUTOHEADER'):
172 UTILS
['autoheader'] = os
.getenv('AUTOHEADER')
174 UTILS
['autoheader'] = 'autoheader'
176 # You can also set the variable AUTOMAKE individually
178 UTILS
['automake'] = AUTOMAKEPATH
+ 'automake'
180 if os
.getenv('AUTOMAKE'):
181 UTILS
['automake'] = os
.getenv('AUTOMAKE')
183 UTILS
['automake'] = 'automake'
185 # You can also set the variable ACLOCAL individually
187 UTILS
['aclocal'] = AUTOMAKEPATH
+ 'aclocal'
189 if os
.getenv('ACLOCAL'):
190 UTILS
['aclocal'] = os
.getenv('ACLOCAL')
192 UTILS
['aclocal'] = 'aclocal'
194 # You can also set the variable AUTOPOINT individually
196 UTILS
['autopoint'] = GETTEXTPATH
+ 'autopoint'
198 if os
.getenv('AUTOPOINT'):
199 UTILS
['autopoint'] = os
.getenv('AUTOPOINT')
201 UTILS
['autopoint'] = 'autopoint'
203 # You can also set the variable LIBTOOLIZE individually
205 UTILS
['libtoolize'] = LIBTOOLPATH
+ 'libtoolize'
207 if os
.getenv('LIBTOOLIZE'):
208 UTILS
['libtoolize'] = os
.getenv('LIBTOOLIZE')
210 UTILS
['libtoolize'] = 'libtoolize'
212 # You can also set the variable MAKE
213 if os
.getenv('MAKE'):
214 UTILS
['make'] = os
.getenv('MAKE')
216 UTILS
['make'] = 'make'
219 #===============================================================================
220 # Define global functions
221 #===============================================================================
222 def execute(args
, verbose
):
223 '''Execute the given shell command.'''
225 print("executing %s" % ' '.join(args
))
227 retcode
= sp
.call(args
)
228 except Exception as error
:
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
)
239 retcode
= sp
.call(xargs
, shell
=True)
240 except Exception as error
:
246 print("executing %s" % ' '.join(args
))
247 with codecs
.open(temp
, 'rb') as file:
254 def compiler(pattern
, flags
=0):
255 '''Compile regex pattern depending on version of Python.'''
257 pattern
= re
.compile(pattern
, re
.UNICODE | flags
)
259 pattern
= re
.compile(pattern
, flags
)
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
]
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.'''
286 if type(head
) is bytes
:
287 head
= head
.decode(ENCS
['default'])
289 if type(item
) is bytes
:
290 item
= item
.decode(ENCS
['default'])
292 result
= os
.path
.normpath(os
.path
.join(head
, *tail
))
293 if type(result
) is bytes
:
294 result
= result
.decode(ENCS
['default'])
298 def relativize(dir1
, dir2
):
299 '''Compute a relative pathname reldir such that dir1/reldir = dir2.'''
301 if type(dir1
) is bytes
:
302 dir1
= dir1
.decode(ENCS
['default'])
303 if type(dir2
) is bytes
:
304 dir2
= dir2
.decode(ENCS
['default'])
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
)]
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
)]
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
)
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
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
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
:
353 else: # if constants.PYTHON3
355 os
.symlink(joinpath(cwd
, src
), dest
)
356 else: # if dest is not absolute
357 destdir
= os
.path
.dirname(dest
)
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
):
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.'''
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
)
395 result
= separator
.join(listing
)
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.'''
405 if data
.startswith(orig
):
406 result
= repl
+ data
[len(orig
):]
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.'''
416 if data
.endswith(orig
):
417 result
= data
[:-len(orig
)] + repl
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')
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
)
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
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,
460 match
= pattern
.search(text
, outerpos
)
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)
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
:]
474 match
= pattern
.search(text
, outerpos
)
478 __all__
+= ['APP', 'DIRS', 'MODES', 'UTILS']