unigbrk/uc-grapheme-breaks: Fix build failure.
[gnulib.git] / pygnulib / GLModuleSystem.py
blobf2f9df4ac96edf658287dd28b62ac63373b887fe
1 #!/usr/bin/python
2 # encoding: UTF-8
4 #===============================================================================
5 # Define global imports
6 #===============================================================================
7 import os
8 import re
9 import sys
10 import codecs
11 import hashlib
12 import subprocess as sp
13 from . import constants
14 from .GLError import GLError
15 from .GLConfig import GLConfig
16 from .GLFileSystem import GLFileSystem
19 #===============================================================================
20 # Define module information
21 #===============================================================================
22 __author__ = constants.__author__
23 __license__ = constants.__license__
24 __copyright__ = constants.__copyright__
27 #===============================================================================
28 # Define global constants
29 #===============================================================================
30 PYTHON3 = constants.PYTHON3
31 NoneType = type(None)
32 APP = constants.APP
33 DIRS = constants.DIRS
34 ENCS = constants.ENCS
35 UTILS = constants.UTILS
36 MODES = constants.MODES
37 TESTS = constants.TESTS
38 compiler = constants.compiler
39 joinpath = constants.joinpath
40 cleaner = constants.cleaner
41 string = constants.string
42 isabs = os.path.isabs
43 isdir = os.path.isdir
44 isfile = os.path.isfile
45 normpath = os.path.normpath
46 relpath = os.path.relpath
47 filter_filelist = constants.filter_filelist
50 #===============================================================================
51 # Define GLModuleSystem class
52 #===============================================================================
53 class GLModuleSystem(object):
54 '''GLModuleSystem is used to operate with module system using dynamic
55 searching and patching.'''
57 def __init__(self, config):
58 '''GLModuleSystem.__init__(config) -> GLModuleSystem
60 Create new GLModuleSystem instance. Some functions use GLFileSystem class
61 to look up a file in localdir or gnulib directories, or combine it through
62 'patch' utility.'''
63 self.args = dict()
64 if type(config) is not GLConfig:
65 raise(TypeError('config must be a GLConfig, not %s' %
66 type(config).__name__))
67 self.config = config
68 self.filesystem = GLFileSystem(self.config)
70 def __repr__(self):
71 '''x.__repr__ <==> repr(x)'''
72 result = '<pygnulib.GLModuleSystem %s>' % hex(id(self))
73 return(result)
75 def exists(self, module):
76 '''GLModuleSystem.exists(module) -> bool
78 Check whether the given module exists.
79 GLConfig: localdir.'''
80 if type(module) is bytes or string:
81 if type(module) is bytes:
82 module = module.decode(ENCS['default'])
83 else: # if module has not bytes or string type
84 raise(TypeError(
85 'module must be a string, not %s' % type(module).__name__))
86 result = bool()
87 badnames = ['ChangeLog', 'COPYING', 'README', 'TEMPLATE',
88 'TEMPLATE-EXTENDED', 'TEMPLATE-TESTS']
89 if isfile(joinpath(DIRS['modules'], module)) or \
90 all([ # Begin all(iterable) function
91 self.config['localdir'],
92 isdir(joinpath(self.config['localdir'], 'modules')),
93 isfile(
94 joinpath(self.config['localdir'], 'modules', module))
95 ]): # Close all(iterable) function
96 if module not in badnames:
97 result = True
98 return(result)
100 def find(self, module):
101 '''GLModuleSystem.find(module) -> GLModule
103 Find the given module.'''
104 if type(module) is bytes or string:
105 if type(module) is bytes:
106 module = module.decode(ENCS['default'])
107 else: # if module has not bytes or string type
108 raise(TypeError(
109 'module must be a string, not %s' % type(module).__name__))
110 if self.exists(module):
111 path, istemp = self.filesystem.lookup(joinpath('modules', module))
112 result = GLModule(self.config, path, istemp)
113 return(result)
114 else: # if not self.exists(module)
115 if self.config['errors']:
116 raise(GLError(3, module))
117 else: # if not self.config['errors']
118 sys.stderr.write('gnulib-tool: warning: ')
119 sys.stderr.write('file %s does not exist\n' % str(module))
121 def list(self):
122 '''GLModuleSystem.list() -> list
124 Return the available module names as tuple. We could use a combination
125 of os.walk() function and re module. However, it takes too much time to
126 complete, so this version uses subprocess to run shell commands.'''
127 result = string()
128 listing = list()
129 localdir = self.config['localdir']
130 find_args = ['find', 'modules', '-type', 'f', '-print']
131 sed_args = \
133 'sed',
134 '-e', r's,^modules/,,',
135 '-e', r'/^ChangeLog$/d',
136 '-e', r'/\/ChangeLog$/d',
137 '-e', r'/^COPYING$/d',
138 '-e', r'/\/COPYING$/d',
139 '-e', r'/^README$/d',
140 '-e', r'/\/README$/d',
141 '-e', r'/^TEMPLATE$/d',
142 '-e', r'/^TEMPLATE-EXTENDED$/d',
143 '-e', r'/^TEMPLATE-TESTS$/d',
144 '-e', r'/^\..*/d',
145 '-e', r'/~$/d',
146 '-e', r'/-tests$/d',
149 # Read modules from gnulib root directory.
150 os.chdir(constants.DIRS['root'])
151 find = sp.Popen(find_args, stdout=sp.PIPE)
152 result += find.stdout.read().decode("UTF-8")
154 # Read modules from local directory.
155 if localdir and isdir(joinpath(localdir, 'modules')):
156 os.chdir(localdir)
157 find = sp.Popen(find_args, stdout=sp.PIPE)
158 result += find.stdout.read().decode("UTF-8")
159 sed_args += ['-e', r's,\.diff$,,']
161 # Save the list of the modules to file.
162 os.chdir(DIRS['cwd'])
163 path = joinpath(self.config['tempdir'], 'list')
164 with codecs.open(path, 'wb', 'UTF-8') as file:
165 file.write(result)
167 # Filter the list of the modules.
168 stdin = codecs.open(path, 'rb', 'UTF-8')
169 sed = sp.Popen(sed_args, stdin=stdin, stdout=sp.PIPE)
170 result = sed.stdout.read().decode("UTF-8")
171 stdin.close()
172 os.remove(path)
173 listing = [line for line in result.split('\n') if line.strip()]
174 listing = sorted(set(listing))
175 return(listing)
178 #===============================================================================
179 # Define GLModule class
180 #===============================================================================
181 class GLModule(object):
182 '''GLModule is used to create a module object from the file with the given
183 path. GLModule can get all information about module, get its dependencies,
184 files, etc.'''
186 def __init__(self, config, module, patched=False):
187 '''GLModule.__init__(config, module[, patched]) -> GLModule
189 Create new GLModule instance. Arguments are module and patched, where
190 module is a string representing the path to the module and patched is a
191 bool indicating that module was created after applying patch.'''
192 self.args = dict()
193 self.cache = dict()
194 self.content = string()
195 if type(config) is not GLConfig:
196 raise(TypeError('config must be a GLConfig, not %s' %
197 type(config).__name__))
198 if type(module) is bytes or type(module) is string:
199 if type(module) is bytes:
200 module = module.decode(ENCS['default'])
201 else: # if module has not bytes or string type
202 raise(TypeError('module must be a string, not %s' %
203 type(module).__name__))
204 if type(patched) is not bool:
205 raise(TypeError('patched must be a bool, not %s' %
206 type(module).__name__))
207 self.module = module
208 self.patched = patched
209 self.config = config
210 self.filesystem = GLFileSystem(self.config)
211 self.modulesystem = GLModuleSystem(self.config)
212 with codecs.open(module, 'rb', 'UTF-8') as file:
213 self.content = file.read()
214 self.regex = '(?:Description:|Comment:|Status:|Notice:|Applicability:|\
215 Files:|Depends-on:|configure\\.ac-early:|configure\\.ac:|Makefile\\.am:|\
216 Include:|Link:|License:|Maintainer:)'
218 def __eq__(self, module):
219 '''x.__eq__(y) <==> x==y'''
220 result = bool()
221 if type(module) is GLModule:
222 if self.module == module.module:
223 result = True
224 return(result)
226 def __ne__(self, module):
227 '''x.__ne__(y) <==> x!=y'''
228 result = bool()
229 if type(module) is GLModule:
230 if self.module != module.module:
231 result = True
232 return(result)
234 def __ge__(self, module):
235 '''x.__ge__(y) <==> x>=y'''
236 result = bool()
237 if type(module) is GLModule:
238 if self.module >= module.module:
239 result = True
240 return(result)
242 def __gt__(self, module):
243 '''x.__gt__(y) <==> x>y'''
244 result = bool()
245 if type(module) is GLModule:
246 if self.module > module.module:
247 result = True
248 return(result)
250 def __hash__(self):
251 '''x.__hash__() <==> hash(x)'''
252 module = hash(self.module)
253 patched = hash(self.patched)
254 result = module ^ patched
255 return(result)
257 def __le__(self, module):
258 '''x.__le__(y) <==> x<=y'''
259 result = bool()
260 if type(module) is GLModule:
261 if self.module <= module.module:
262 result = True
263 return(result)
265 def __lt__(self, module):
266 '''x.__lt__(y) <==> x<y'''
267 result = bool()
268 if type(module) is GLModule:
269 if self.module < module.module:
270 result = True
271 return(result)
273 def __str__(self):
274 '''x.__str__() <==> str(x)'''
275 result = self.getName()
276 return(result)
278 def __repr__(self):
279 '''x.__repr__ <==> repr(x)'''
280 result = '<pygnulib.GLModule %s %s>' % \
281 (repr(self.getName()), hex(id(self)))
282 return(result)
284 def getName(self):
285 '''GLModule.getName() -> string
287 Return the name of the module.'''
288 pattern = compiler(joinpath('modules', '(.*?)$'))
289 result = pattern.findall(self.module)[0]
290 return(result)
292 def isPatched(self):
293 '''GLModule.isPatched() -> bool
295 Check whether module was created after applying patch.'''
296 return(self.patched)
298 def isTests(self):
299 '''GLModule.isTests() -> bool
301 Check whether module is a -tests version of module.'''
302 result = self.getName().endswith('-tests')
303 return(result)
305 def isNonTests(self):
306 '''GLModule.isTests() -> bool
308 Check whether module is not a -tests version of module.'''
309 result = not(self.isTests())
310 return(result)
312 def getTestsName(self):
313 '''Return -tests version of the module name.'''
314 result = self.getName()
315 if not result.endswith('-tests'):
316 result += '-tests'
317 return(result)
319 def getTestsModule(self):
320 '''Return -tests version of the module as GLModule.'''
321 result = self.modulesystem.find(self.getTestsName())
322 return(result)
324 def getShellFunc(self):
325 '''GLModule.getShellFunc() -> string
327 Computes the shell function name that will contain the m4 macros for the
328 module.'''
329 isalnum = True
330 macro_prefix = self.config['macro_prefix']
331 for char in str(module):
332 if char not in constants.ALPHANUMERIC:
333 isalnum = False
334 break
335 if isalnum:
336 module = str(self)
337 else: # if not isalnum
338 module = '%s\n' % str(self)
339 if type(module) is string:
340 module = module.encode(ENCS['default'])
341 module = hashlib.md5(module).hexdigest()
342 result = 'func_%s_gnulib_m4code_%s' % (macro_prefix, module)
343 if type(result) is bytes:
344 result = result.decode(ENCS['default'])
345 return(result)
347 def getShellVar(self):
348 '''GLModule.getShellVar() -> string
350 Compute the shell variable name the will be set to true once the m4 macros
351 for the module have been executed.'''
352 isalnum = True
353 macro_prefix = self.config['macro_prefix']
354 for char in str(module):
355 if char not in constants.ALPHANUMERIC:
356 isalnum = False
357 break
358 if isalnum:
359 module = str(self)
360 else: # if not isalnum
361 module = '%s\n' % str(self)
362 if type(module) is string:
363 module = module.encode(ENCS['default'])
364 module = hashlib.md5(module).hexdigest()
365 result = '%s_gnulib_enabled_%s' % (macro_prefix, module)
366 if type(result) is bytes:
367 result = result.decode(ENCS['default'])
368 return(result)
370 def getConditionalName(self):
371 '''GLModule.getConditionalName() -> string
373 Return the automake conditional name.
374 GLConfig: macro_prefix.'''
375 macro_prefix = self.config['macro_prefix']
376 nonascii = \
377 [ # Begin to filter non-ascii chars
378 char for char in self.getName() if char not in \
379 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
380 ] # Finish to filter non-ascii chars
381 if nonascii:
382 name = self.getName().encode(ENCS['default'])
383 name = hashlib.md5(name).hexdigest()
384 conditional = '%s_GNULIB_ENABLED_%s' % (macro_prefix, name)
385 else: # if not nonascii
386 result = '%s_GNULIB_ENABLED_%s' (macro_prefix, name)
387 if type(result) is bytes:
388 result = result.decode(ENCS['default'])
389 return(result)
391 def getDescription(self):
392 '''GLModule.getDescription() -> string
394 Return description of the module.'''
395 section = 'Description:'
396 if 'description' not in self.cache:
397 if section not in self.content:
398 result = string()
399 else: # if section in self.content
400 pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
401 pattern = compiler(pattern, re.S | re.M)
402 result = pattern.findall(self.content)
403 if type(result) is list:
404 if not result:
405 result = string()
406 else: # if result
407 result = result[-1]
408 result = result.strip()
409 self.cache['description'] = result
410 return(self.cache['description'])
412 def getComment(self):
413 '''GLModule.getComment() -> string
415 Return comment to module.'''
416 section = 'Comment:'
417 if 'comment' not in self.cache:
418 if section not in self.content:
419 result = string()
420 else: # if section in self.content
421 pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
422 pattern = compiler(pattern, re.S | re.M)
423 result = pattern.findall(self.content)
424 if type(result) is list:
425 if not result:
426 result = string()
427 else: # if result
428 result = result[-1]
429 result = result.strip()
430 self.cache['comment'] = result
431 return(self.cache['comment'])
433 def getStatus(self):
434 '''GLModule.getStatus() -> string
436 Return module status.'''
437 section = 'Status:'
438 if 'status' not in self.cache:
439 if section not in self.content:
440 result = string()
441 else: # if section in self.content
442 snippet = self.content.split(section)[-1]
443 snippet = snippet.replace('\r\n', '\n')
444 lines = ['%s\n' % line for line in snippet.split('\n')]
445 parts = list()
446 for line in lines:
447 regex = '^(Description|Comment|Status|Notice|Applicability|'
448 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
449 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
450 pattern = compiler(regex)
451 findflag = pattern.findall(line)
452 if findflag:
453 break
454 parts += [line]
455 result = [part.strip() for part in parts if part.strip()]
456 self.cache['status'] = list(result)
457 return(list(self.cache['status']))
459 def getNotice(self):
460 '''GLModule.getNotice() -> string
462 Return notice to module.'''
463 section = 'Notice:'
464 if 'notice' not in self.cache:
465 if section not in self.content:
466 result = string()
467 else: # if section in self.content
468 snippet = self.content.split(section)[-1]
469 snippet = snippet.replace('\r\n', '\n')
470 lines = ['%s\n' % line for line in snippet.split('\n')]
471 parts = list()
472 for line in lines:
473 regex = '^(Description|Comment|Status|Notice|Applicability|'
474 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
475 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
476 pattern = compiler(regex)
477 findflag = pattern.findall(line)
478 if findflag:
479 break
480 parts += [line]
481 result = ''.join(parts)
482 self.cache['notice'] = result
483 return(self.cache['notice'])
485 def getApplicability(self):
486 '''GLModule.getApplicability() -> string
488 Return applicability of module.'''
489 section = 'Applicability:'
490 if 'applicability' not in self.cache:
491 if section not in self.content:
492 result = string()
493 else: # if section in self.content
494 snippet = self.content.split(section)[-1]
495 snippet = snippet.replace('\r\n', '\n')
496 lines = ['%s\n' % line for line in snippet.split('\n')]
497 parts = list()
498 for line in lines:
499 regex = '^(Description|Comment|Status|Notice|Applicability|'
500 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
501 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
502 pattern = compiler(regex)
503 findflag = pattern.findall(line)
504 if findflag:
505 break
506 parts += [line]
507 parts = [part.strip() for part in parts]
508 result = ''.join(parts)
509 if not result.strip():
510 if self.getName().endswith('-tests'):
511 result = 'tests'
512 else: # if not self.getName().endswith('-tests')
513 result = 'main'
514 if type(result) is bytes:
515 result = result.decode(ENCS['default'])
516 result = result.strip()
517 self.cache['applicability'] = result
518 return(self.cache['applicability'])
520 def getFiles(self):
521 '''GLModule.getFiles() -> list
523 Return list of files.
524 GLConfig: ac_version.'''
525 ac_version = self.config['ac_version']
526 section = 'Files:'
527 result = list()
528 if 'files' not in self.cache:
529 if section not in self.content:
530 result = list()
531 else: # if section in self.content
532 snippet = self.content.split(section)[-1]
533 snippet = snippet.replace('\r\n', '\n')
534 lines = ['%s\n' % line for line in snippet.split('\n')]
535 parts = list()
536 for line in lines:
537 regex = '^(Description|Comment|Status|Notice|Applicability|'
538 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
539 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
540 pattern = compiler(regex)
541 findflag = pattern.findall(line)
542 if findflag:
543 break
544 parts += [line]
545 result = [part.strip() for part in parts if part.strip()]
546 result += [joinpath('m4', '00gnulib.m4')]
547 result += [joinpath('m4', 'gnulib-common.m4')]
548 self.cache['files'] = list(result)
549 return(list(self.cache['files']))
551 def getDependencies(self):
552 '''GLModule.getDependencies() -> list
554 Return list of dependencies.
555 GLConfig: localdir.'''
556 localdir = self.config['localdir']
557 result = list()
558 section = 'Depends-on:'
559 if 'dependencies' not in self.cache:
560 if section not in self.content:
561 depmodules = list()
562 else: # if section in self.content
563 snippet = self.content.split(section)[-1]
564 snippet = snippet.replace('\r\n', '\n')
565 lines = ['%s\n' % line for line in snippet.split('\n')]
566 parts = list()
567 for line in lines:
568 regex = '^(Description|Comment|Status|Notice|Applicability|'
569 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
570 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
571 pattern = compiler(regex)
572 findflag = pattern.findall(line)
573 if findflag:
574 break
575 parts += [line]
576 modules = ''.join(parts)
577 modules = [line for line in modules.split(
578 '\n') if line.strip()]
579 modules = [
580 module for module in modules if not module.startswith('#')]
581 for line in modules:
582 split = [part for part in line.split(' ') if part.strip()]
583 if len(split) == 1:
584 module = line.strip()
585 condition = None
586 else: # if len(split) != 1
587 module = split[0]
588 condition = split[1]
589 if type(condition) is bytes:
590 condition = condition.decode(ENCS['default'])
591 result += [tuple([self.modulesystem.find(module), condition])]
592 self.cache['dependencies'] = result
593 return(list(self.cache['dependencies']))
595 def getAutoconfSnippet_Early(self):
596 '''GLModule.getAutoconfSnippet_Early() -> string
598 Return autoconf-early snippet.'''
599 section = 'configure.ac-early:'
600 if 'autoconf-early' not in self.cache:
601 if section not in self.content:
602 result = string()
603 else: # if section in self.content
604 snippet = self.content.split(section)[-1]
605 snippet = snippet.replace('\r\n', '\n')
606 lines = ['%s\n' % line for line in snippet.split('\n')]
607 parts = list()
608 for line in lines:
609 regex = '^(Description|Comment|Status|Notice|Applicability|'
610 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
611 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
612 pattern = compiler(regex)
613 findflag = pattern.findall(line)
614 if findflag:
615 break
616 parts += [line]
617 result = ''.join(parts)
618 self.cache['autoconf-early'] = result
619 return(self.cache['autoconf-early'])
621 def getAutoconfSnippet(self):
622 '''GLModule.getAutoconfSnippet() -> string
624 Return autoconf snippet.'''
625 section = 'configure.ac:'
626 if 'autoconf' not in self.cache:
627 if section not in self.content:
628 result = string()
629 else: # if section in self.content
630 snippet = self.content.split(section)[-1]
631 snippet = snippet.replace('\r\n', '\n')
632 lines = ['%s\n' % line for line in snippet.split('\n')]
633 parts = list()
634 for line in lines:
635 regex = '^(Description|Comment|Status|Notice|Applicability|'
636 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
637 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
638 pattern = compiler(regex)
639 findflag = pattern.findall(line)
640 if findflag:
641 break
642 parts += [line]
643 result = ''.join(parts)
644 self.cache['autoconf'] = result
645 return(self.cache['autoconf'])
647 def getAutomakeSnippet(self):
648 '''getAutomakeSnippet() -> string
650 Get automake snippet.
651 GLConfig: auxdir, ac_version.'''
652 result = string() # Define stack variable
653 conditional = self.getAutomakeSnippet_Conditional()
654 if conditional.strip():
655 result += self.getAutomakeSnippet_Conditional()
656 else: # if not conditional.strip()
657 result += '\n'
658 result += self.getAutomakeSnippet_Unconditional()
659 return(result)
661 def getAutomakeSnippet_Conditional(self):
662 '''GLModule.getAutomakeSnippet_Conditional() -> string
664 Return conditional automake snippet.'''
665 section = 'Makefile.am:'
666 if 'makefile-conditional' not in self.cache:
667 if section not in self.content:
668 result = string()
669 else: # if section in self.content
670 snippet = self.content.split(section)[-1]
671 snippet = snippet.replace('\r\n', '\n')
672 lines = ['%s\n' % line for line in snippet.split('\n')]
673 parts = list()
674 for line in lines:
675 regex = '^(Description|Comment|Status|Notice|Applicability|'
676 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
677 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
678 pattern = compiler(regex)
679 findflag = pattern.findall(line)
680 if findflag:
681 break
682 parts += [line]
683 result = ''.join(parts)
684 self.cache['makefile-conditional'] = result
685 return(self.cache['makefile-conditional'])
687 def getAutomakeSnippet_Unconditional(self):
688 '''GLModule.getAutomakeSnippet_Unconditional() -> string
690 Return unconditional automake snippet.
691 GLConfig: auxdir, ac_version.'''
692 auxdir = self.config['auxdir']
693 ac_version = self.config['ac_version']
694 result = string()
695 if 'makefile-unconditional' not in self.cache:
696 if self.isTests():
697 files = self.getFiles()
698 extra_files = filter_filelist(constants.NL, files,
699 'tests/', '', 'tests/', '').split(constants.NL)
700 extra_files = sorted(set(extra_files))
701 if extra_files:
702 result += string('EXTRA_DIST += %s' %
703 ' '.join(extra_files))
704 result += constants.NL * 2
705 else: # if not tests module
706 # TODO: unconditional automake snippet for nontests modules
707 snippet = self.getAutomakeSnippet_Conditional()
708 snippet = constants.combine_lines(snippet)
709 pattern = compiler(
710 '^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M)
711 mentioned_files = pattern.findall(snippet)
712 if mentioned_files != list():
713 mentioned_files = mentioned_files[-1].split(' ')
714 mentioned_files = [f.strip() for f in mentioned_files]
715 mentioned_files = [f for f in mentioned_files if f != '']
716 mentioned_files = sorted(set(mentioned_files))
717 all_files = self.getFiles()
718 lib_files = filter_filelist(constants.NL, all_files,
719 'lib/', '', 'lib/', '').split(constants.NL)
720 extra_files = [
721 f for f in lib_files if f not in mentioned_files]
722 extra_files = sorted(set(extra_files))
723 if extra_files != [''] and extra_files:
724 result += string('EXTRA_DIST += %s' %
725 ' '.join(extra_files))
726 result += '\n\n'
727 # Synthesize also an EXTRA_lib_SOURCES augmentation
728 if str(self) != 'relocatable-prog-wrapper' and str(self) != 'pt_chown':
729 extra_files = filter_filelist(constants.NL, extra_files,
730 '', '.c', '', '').split(constants.NL)
731 extra_files = sorted(set(extra_files))
732 if extra_files != ['']:
733 result += string('EXTRA_lib_SOURCES += %s' %
734 ' '.join(extra_files))
735 result += '\n\n'
736 # Synthesize an EXTRA_DIST augmentation also for the files in build-aux
737 buildaux_files = filter_filelist(constants.NL, all_files,
738 'build-aux/', '', 'build-aux/', '').split(constants.NL)
739 buildaux_files = sorted(set(buildaux_files))
740 if buildaux_files != ['']:
741 buildaux_files = ''.join(buildaux_files)
742 buildaux_files = joinpath(
743 '$(top_srcdir)', auxdir, buildaux_files)
744 result += string('EXTRA_DIST += %s' % buildaux_files)
745 result += '\n\n'
746 # Synthesize an EXTRA_DIST augmentation also for the files from top/.
747 top_files = filter_filelist(constants.NL, all_files,
748 'top/', '', 'top/', '').split(constants.NL)
749 top_files = sorted(set(top_files))
750 if top_files != ['']:
751 top_files = ''.join(top_files)
752 top_files = joinpath('$(top_srcdir)', top_files)
753 result += string('EXTRA_DIST += %s' % top_files)
754 result += '\n\n'
755 result = constants.nlconvert(result)
756 self.cache['makefile-unconditional'] = result
757 return(self.cache['makefile-unconditional'])
759 def getInclude(self):
760 '''GLModule.getInclude() -> string
762 Return include directive.'''
763 section = 'Include:'
764 if 'include' not in self.cache:
765 if section not in self.content:
766 result = string()
767 else: # if section in self.content
768 snippet = self.content.split(section)[-1]
769 snippet = snippet.replace('\r\n', '\n')
770 lines = ['%s\n' % line for line in snippet.split('\n')]
771 parts = list()
772 for line in lines:
773 regex = '^(Description|Comment|Status|Notice|Applicability|'
774 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
775 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
776 pattern = compiler(regex)
777 findflag = pattern.findall(line)
778 if findflag:
779 break
780 parts += [line]
781 result = ''.join(parts)
782 result = result.strip()
783 pattern = compiler('^(["<].*?[>"])', re.S | re.M)
784 result = pattern.sub('#include \\1', result)
785 self.cache['include'] = result
786 return(self.cache['include'])
788 def getLink(self):
789 '''GLModule.getLink() -> string
791 Return link directive.'''
792 section = 'Link:'
793 if 'link' not in self.cache:
794 if section not in self.content:
795 result = string()
796 else: # if section in self.content
797 snippet = self.content.split(section)[-1]
798 snippet = snippet.replace('\r\n', '\n')
799 lines = ['%s\n' % line for line in snippet.split('\n')]
800 parts = list()
801 for line in lines:
802 regex = '^(Description|Comment|Status|Notice|Applicability|'
803 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
804 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
805 pattern = compiler(regex)
806 findflag = pattern.findall(line)
807 if findflag:
808 break
809 parts += [line]
810 parts = [part.strip() for part in parts if part.strip()]
811 result = ''.join(parts)
812 self.cache['link'] = result
813 return(self.cache['link'])
815 def getLicense(self):
816 '''GLModule.getLicense(self) -> string
818 Get license and warn user if module lacks a license.'''
819 license = self.getLicense_Raw()
820 if not self.isTests():
821 if not license:
822 if self.config['errors']:
823 raise(GLError(18, string(self)))
824 else: # if not self.config['errors']
825 sys.stderr.write('gnulib-tool: warning: ')
826 sys.stderr.write('module %s lacks a license\n' % str(self))
827 if not license:
828 license = 'GPL'
829 return(license)
831 def getLicense_Raw(self):
832 '''GLModule.getLicense_Raw() -> string
834 Return module license.'''
835 section = 'License:'
836 if 'license' not in self.cache:
837 if section not in self.content:
838 result = string()
839 else: # if section in self.content
840 pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
841 pattern = compiler(pattern, re.S | re.M)
842 result = pattern.findall(self.content)
843 if type(result) is list:
844 if not result:
845 result = string()
846 else: # if result
847 result = result[-1]
848 result = result.strip()
849 self.cache['license'] = result
850 return(self.cache['license'])
852 def getMaintainer(self):
853 '''GLModule.getMaintainer() -> string
855 Return maintainer directive.'''
856 section = 'Maintainer:'
857 if 'maintainer' not in self.cache:
858 if section not in self.content:
859 result = string()
860 else: # if section in self.content
861 snippet = self.content.split(section)[-1]
862 snippet = snippet.replace('\r\n', '\n')
863 lines = ['%s\n' % line for line in snippet.split('\n')]
864 parts = list()
865 for line in lines:
866 regex = '^(Description|Comment|Status|Notice|Applicability|'
867 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
868 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
869 pattern = compiler(regex)
870 findflag = pattern.findall(line)
871 if findflag:
872 break
873 parts += [line]
874 result = ''.join(parts)
875 result = result.strip()
876 self.cache['maintainer'] = result
877 return(self.cache['maintainer'])
880 #===============================================================================
881 # Define GLModuleTable class
882 #===============================================================================
883 class GLModuleTable(object):
884 '''GLModuleTable is used to work with the list of the modules.'''
886 def __init__(self, config, avoids=list()):
887 '''GLModuleTable.__init__(config, avoids) -> GLModuleTable
889 Create new GLModuleTable instance. If modules are specified, then add
890 every module from iterable as unconditional module. If avoids is specified,
891 then in transitive_closure every dependency which is in avoids won't be
892 included in the final modules list. If testflags iterable is enabled, then
893 don't add module which status is in the testflags. If conddeps are enabled,
894 then store condition for each dependency if it has a condition.
895 The only necessary argument is localdir, which is needed just to create
896 modulesystem instance to look for dependencies.'''
897 self.avoids = list() # Avoids
898 self.dependers = dict() # Dependencies
899 self.conditionals = dict() # Conditional modules
900 self.unconditionals = dict() # Unconditional modules
901 self.base_modules = list() # Base modules
902 self.main_modules = list() # Main modules
903 self.tests_modules = list() # Tests modules
904 self.final_modules = list() # Final modules
905 if type(config) is not GLConfig:
906 raise(TypeError('config must be a GLConfig, not %s' %
907 type(config).__name__))
908 for avoid in avoids:
909 if type(avoid) is not GLModule:
910 raise(TypeError('each avoid must be a GLModule instance'))
911 self.avoids += [avoids]
912 self.config = config
913 self.filesystem = GLFileSystem(self.config)
914 self.modulesystem = GLModuleSystem(self.config)
916 def __repr__(self):
917 '''x.__repr__() <==> repr(x)'''
918 result = '<pygnulib.GLModuleTable %s>' % hex(id(self))
919 return(result)
921 def __getitem__(self, y):
922 '''x.__getitem__(y) <==> x[y]'''
923 if y in ['base', 'final', 'main', 'tests', 'avoids']:
924 if y == 'base':
925 return(self.getBaseModules())
926 elif y == 'final':
927 return(self.getFinalModules())
928 elif y == 'main':
929 return(self.getMainModules())
930 elif y == 'tests':
931 return(self.getTestsModules())
932 else: # if y == 'avoids'
933 return(self.getAvoids())
934 else: # if y is not in list
935 raise(KeyError('GLModuleTable does not contain key: %s' % repr(y)))
937 def addConditional(self, parent, module, condition):
938 '''GLModuleTable.addConditional(module, condition)
940 Add new conditional dependency from parent to module with condition.'''
941 if type(parent) is not GLModule:
942 raise(TypeError('parent must be a GLModule, not %s' %
943 type(parent).__name__))
944 if type(module) is not GLModule:
945 raise(TypeError('module must be a GLModule, not %s' %
946 type(module).__name__))
947 if type(condition) is bytes or type(condition) is string \
948 or condition == True:
949 if type(condition) is bytes:
950 condition = condition.decode(ENCS['default'])
951 else: # if condition has not bytes or string type or is not True
952 raise(TypeError('condition must be a string or True, not %s' %
953 type(condition).__name__))
954 if not str(module) in self.unconditionals:
955 if str(module) not in self.dependers:
956 self.dependers[module] = list()
957 self.dependers[module] += [module]
958 key = '%s---%s' % (str(parent), str(module))
959 self.conditionals[key] = condition
961 def addUnconditional(self, module):
962 '''GLModuleTable.addUnconditional(module)
964 Add module as unconditional dependency.'''
965 if type(module) is not GLModule:
966 raise(TypeError('module must be a GLModule, not %s' %
967 type(module).__name__))
968 if str(module) in self.dependers:
969 self.dependers.pop(str(module))
970 self.unconditionals[str(module)] = True
972 def isConditional(self, module):
973 '''GLModuleTable.isConditional(module) -> bool
975 Check whether module is unconditional.'''
976 if type(module) is not GLModule:
977 raise(TypeError('module must be a GLModule, not %s' %
978 type(module).__name__))
979 result = str(module) in self.dependers
980 return(result)
982 def getCondition(self, parent, module):
983 '''GLModuleTable.getCondition(module) -> string or True
985 Return condition from parent to module. Condition can be string or True.
986 If module is not in the list of conddeps, method returns None.'''
987 if type(parent) is not GLModule:
988 raise(TypeError('parent must be a GLModule, not %s' %
989 type(parent).__name__))
990 if type(module) is not GLModule:
991 raise(TypeError('module must be a GLModule, not %s' %
992 type(module).__name__))
993 key = '%s---%s' % (str(parent), str(module))
994 result = None
995 if key in self.conditionals:
996 result = self.conditionals[key]
997 return(result)
999 def transitive_closure(self, modules):
1000 '''GLModuleTable.transitive_closure(modules) -> list
1002 Use transitive closure to add module and its dependencies. Add every
1003 module and its dependencies from modules list, but do not add dependencies
1004 which contain in avoids list. If any testflag is enabled, then do not add
1005 dependencies which have the status as this flag. If conddeps are enabled,
1006 then store condition for each dependency if it has a condition. This method
1007 is used to update final list of modules. Method returns list of modules.
1008 GLConfig: testflags.'''
1009 for module in modules:
1010 if type(module) is not GLModule:
1011 raise(TypeError('each module must be a GLModule instance'))
1012 handledmodules = list()
1013 inmodules = modules
1014 outmodules = list()
1015 if self.config['conddeps']:
1016 for module in modules:
1017 self.addUnconditional(module)
1018 while inmodules:
1019 inmodules_this_round = inmodules
1020 inmodules = list()
1021 for module in inmodules_this_round:
1022 outmodules += [module]
1023 if self.config['conddeps']:
1024 automake_snippet = \
1025 module.getAutomakeSnippet_Conditional()
1026 pattern = compiler('^if')
1027 if not pattern.findall(automake_snippet):
1028 self.addUnconditional(module)
1029 conditional = self.isConditional(module)
1030 dependencies = module.getDependencies()
1031 depmodules = [pair[0] for pair in dependencies]
1032 conditions = [pair[1] for pair in dependencies]
1033 if TESTS['tests'] in self.config['testflags']:
1034 testsname = module.getTestsName()
1035 if self.modulesystem.exists(testsname):
1036 testsmodule = self.modulesystem.find(testsname)
1037 depmodules += [testsmodule]
1038 conditions += [None]
1039 for depmodule in depmodules:
1040 include = True
1041 includes = list()
1042 status = depmodule.getStatus()
1043 for word in status:
1044 if word == 'obsolete':
1045 if TESTS['obsolete'] in self.config['testflags'] or \
1046 TESTS['all-test'] in self.config['testflags']:
1047 includes += [False]
1048 elif word == 'c++-test':
1049 if TESTS['c++-test'] in self.config['testflags'] or \
1050 TESTS['all-test'] in self.config['testflags']:
1051 includes += [False]
1052 elif word == 'longrunning-test':
1053 if TESTS['longrunning-test'] in self.config['testflags'] or \
1054 TESTS['all-test'] in self.config['testflags']:
1055 includes += [False]
1056 elif word == 'privileged-test':
1057 if TESTS['privileged-test'] in self.config['testflags'] or \
1058 TESTS['all-test'] in self.config['testflags']:
1059 includes += [False]
1060 elif word == 'all-test':
1061 if TESTS['all-test'] in self.config['testflags'] or \
1062 TESTS['all-test'] in self.config['testflags']:
1063 includes += [False]
1064 else: # if any other word
1065 if word.endswith('-tests'):
1066 if TESTS['all-test'] in self.config['testflags']:
1067 includes += [False]
1068 include = any(includes)
1069 if include and depmodule not in self.avoids:
1070 inmodules += [depmodule]
1071 if self.config['conddeps']:
1072 index = depmodules.index(depmodule)
1073 condition = conditions[index]
1074 if condition:
1075 self.addConditional(
1076 module, depmodule, condition)
1077 else: # if condition
1078 if conditional:
1079 self.addConditional(
1080 module, depmodule, True)
1081 else: # if not conditional
1082 self.addUnconditional(module)
1083 listing = list() # Create empty list
1084 inmodules = sorted(set(inmodules))
1085 handledmodules = sorted(set(handledmodules + inmodules_this_round))
1086 inmodules = \
1087 [ # Begin to filter inmodules
1088 module for module in inmodules if module not in handledmodules
1089 ] # Finish to filter inmodules
1090 inmodules = sorted(set(inmodules))
1091 modules = sorted(set(outmodules))
1092 self.modules = modules
1093 return(list(modules))
1095 def transitive_closure_separately(self, basemodules, finalmodules):
1096 '''GLModuleTable.transitive_closure_separately(*args, **kwargs) -> tuple
1098 Determine main module list and tests-related module list separately.
1099 The main module list is the transitive closure of the specified modules,
1100 ignoring tests modules. Its lib/* sources go into $sourcebase/. If lgpl is
1101 specified, it will consist only of LGPLed source.
1102 The tests-related module list is the transitive closure of the specified
1103 modules, including tests modules, minus the main module list excluding
1104 modules of applicability 'all'. Its lib/* sources (brought in through
1105 dependencies of *-tests modules) go into $testsbase/. It may contain GPLed
1106 source, even if lgpl is specified.
1107 Arguments are basemodules and finalmodules, where basemodules argument
1108 represents modules specified by user and finalmodules represents modules
1109 list after previous transitive_closure.
1110 Method returns tuple which contains two lists: the list of main modules and
1111 the list of tests-related modules. Both lists contain dependencies.
1112 GLConfig: testflags.'''
1113 inctests = False
1114 main_modules = list()
1115 tests_modules = list()
1116 if TESTS['tests'] in self.config['testflags']:
1117 self.config['testflags'].pop(TESTS['tests'])
1118 inctests = True
1119 for module in basemodules:
1120 if type(module) is not GLModule:
1121 raise(TypeError('each module must be a GLModule instance'))
1122 for module in finalmodules:
1123 if type(module) is not GLModule:
1124 raise(TypeError('each module must be a GLModule instance'))
1125 main_modules = self.transitive_closure(basemodules)
1126 tests_modules = \
1127 [m for m in finalmodules if m not in main_modules] + \
1128 [m for m in main_modules if m.getApplicability() != 'main']
1129 tests_modules = sorted(set(tests_modules))
1130 if inctests:
1131 testflags = sorted(
1132 set(self.config['testflags'] + [TESTS['tests']]))
1133 self.config.setTestFlags(testflags)
1134 result = tuple([main_modules, tests_modules])
1135 return(result)
1137 def add_dummy(self, modules):
1138 '''GLModuleTable.add_dummy(modules) -> list
1140 Add dummy package to list of modules if dummy package is needed. If not,
1141 return original list of modules.
1142 GLConfig: auxdir, ac_version.'''
1143 auxdir = self.config['auxdir']
1144 ac_version = self.config['ac_version']
1145 have_lib_sources = False
1146 for module in modules:
1147 if type(module) is not GLModule:
1148 raise(TypeError('each module must be a GLModule instance'))
1149 snippet = module.getAutomakeSnippet()
1150 snippet = constants.remove_backslash_newline(snippet)
1151 pattern = compiler(
1152 '^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M)
1153 files = pattern.findall(snippet)
1154 if files: # if source files were found
1155 files = files[-1].split(' ')
1156 for file in files:
1157 if not file.endswith('.h'):
1158 have_lib_sources = True
1159 break
1160 if not have_lib_sources:
1161 dummy = self.modulesystem.find('dummy')
1162 modules = sorted(set(modules + [dummy]))
1163 return(list(modules))
1165 def filelist(self, modules):
1166 '''GLModuleTable.filelist(modules) -> list
1168 Determine the final file list for the given list of modules. The list of
1169 modules must already include dependencies.
1170 GLConfig: ac_version.'''
1171 ac_version = self.config['ac_version']
1172 filelist = list()
1173 for module in modules:
1174 if type(module) is not GLModule:
1175 raise(TypeError('each module must be a GLModule instance'))
1176 listings = [module.getFiles() for module in modules]
1177 for listing in listings:
1178 for file in listing:
1179 if file not in filelist:
1180 filelist += [file]
1181 return(filelist)
1183 def filelist_separately(self, main_modules, tests_modules):
1184 '''GLModuleTable.filelist_separately(**kwargs) -> list
1186 Determine the final file lists. They must be computed separately, because
1187 files in lib/* go into $sourcebase/ if they are in the main file list but
1188 into $testsbase/ if they are in the tests-related file list. Furthermore
1189 lib/dummy.c can be in both.'''
1190 ac_version = self.config['ac_version']
1191 main_filelist = self.filelist(main_modules)
1192 tests_filelist = self.filelist(tests_modules)
1193 tests_filelist = \
1194 [ # Begin to sort filelist
1195 file.replace('lib/', 'tests=lib/', 1) \
1196 if file.startswith('lib/') else file
1197 for file in tests_filelist
1198 ] # Finish to sort filelist
1199 result = tuple([main_filelist, tests_filelist])
1200 return(result)
1202 def getAvoids(self):
1203 '''GLModuleTable.getAvoids() -> list
1205 Return list of avoids.'''
1206 return(list(self.avoids))
1208 def setAvoids(self, modules):
1209 '''GLModuleTable.setAvoids(modules)
1211 Specify list of avoids.'''
1212 for module in modules:
1213 if type(module) is not GLModule:
1214 raise(TypeError('each module must be a GLModule instance'))
1215 self.avoids = sorted(set(modules))
1217 def getBaseModules(self):
1218 '''GLModuleTable.getBaseModules() -> list
1220 Return list of base modules.'''
1221 return(list(self.base_modules))
1223 def setBaseModules(self, modules):
1224 '''GLModuleTable.setBaseModules(modules)
1226 Specify list of base modules.'''
1227 for module in modules:
1228 if type(module) is not GLModule:
1229 raise(TypeError('each module must be a GLModule instance'))
1230 self.base_modules = sorted(set(modules))
1232 def getFinalModules(self):
1233 '''GLModuleTable.getFinalModules() -> list
1235 Return list of final modules.'''
1236 return(list(self.final_modules))
1238 def setFinalModules(self, modules):
1239 '''GLModuleTable.setFinalModules(modules)
1241 Specify list of final modules.'''
1242 for module in modules:
1243 if type(module) is not GLModule:
1244 raise(TypeError('each module must be a GLModule instance'))
1245 self.final_modules = sorted(set(modules))
1247 def getMainModules(self):
1248 '''GLModuleTable.getMainModules() -> list
1250 Return list of main modules.'''
1251 return(list(self.main_modules))
1253 def setMainModules(self, modules):
1254 '''GLModuleTable.setMainModules(modules)
1256 Specify list of main modules.'''
1257 for module in modules:
1258 if type(module) is not GLModule:
1259 raise(TypeError('each module must be a GLModule instance'))
1260 self.main_modules = sorted(set(modules))
1262 def getTestsModules(self):
1263 '''GLModuleTable.getTestsModules() -> list
1265 Return list of tests modules.'''
1266 return(list(self.tests_modules))
1268 def setTestsModules(self, modules):
1269 '''GLModuleTable.setTestsModules(modules)
1271 Specify list of tests modules.'''
1272 for module in modules:
1273 if type(module) is not GLModule:
1274 raise(TypeError('each module must be a GLModule instance'))
1275 self.tests_modules = sorted(set(modules))