libc-config: Fix __GNUC_PREREQ macro.
[gnulib.git] / pygnulib / GLModuleSystem.py
blob59109cce7ed4fbbe03f1b05b9663a47f8d85ac04
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 if ac_version == 2.59:
549 result += [joinpath('m4', 'onceonly.m4')]
550 self.cache['files'] = list(result)
551 return(list(self.cache['files']))
553 def getDependencies(self):
554 '''GLModule.getDependencies() -> list
556 Return list of dependencies.
557 GLConfig: localdir.'''
558 localdir = self.config['localdir']
559 result = list()
560 section = 'Depends-on:'
561 if 'dependencies' not in self.cache:
562 if section not in self.content:
563 depmodules = list()
564 else: # if section in self.content
565 snippet = self.content.split(section)[-1]
566 snippet = snippet.replace('\r\n', '\n')
567 lines = ['%s\n' % line for line in snippet.split('\n')]
568 parts = list()
569 for line in lines:
570 regex = '^(Description|Comment|Status|Notice|Applicability|'
571 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
572 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
573 pattern = compiler(regex)
574 findflag = pattern.findall(line)
575 if findflag:
576 break
577 parts += [line]
578 modules = ''.join(parts)
579 modules = [line for line in modules.split(
580 '\n') if line.strip()]
581 modules = [
582 module for module in modules if not module.startswith('#')]
583 for line in modules:
584 split = [part for part in line.split(' ') if part.strip()]
585 if len(split) == 1:
586 module = line.strip()
587 condition = None
588 else: # if len(split) != 1
589 module = split[0]
590 condition = split[1]
591 if type(condition) is bytes:
592 condition = condition.decode(ENCS['default'])
593 result += [tuple([self.modulesystem.find(module), condition])]
594 self.cache['dependencies'] = result
595 return(list(self.cache['dependencies']))
597 def getAutoconfSnippet_Early(self):
598 '''GLModule.getAutoconfSnippet_Early() -> string
600 Return autoconf-early snippet.'''
601 section = 'configure.ac-early:'
602 if 'autoconf-early' not in self.cache:
603 if section not in self.content:
604 result = string()
605 else: # if section in self.content
606 snippet = self.content.split(section)[-1]
607 snippet = snippet.replace('\r\n', '\n')
608 lines = ['%s\n' % line for line in snippet.split('\n')]
609 parts = list()
610 for line in lines:
611 regex = '^(Description|Comment|Status|Notice|Applicability|'
612 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
613 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
614 pattern = compiler(regex)
615 findflag = pattern.findall(line)
616 if findflag:
617 break
618 parts += [line]
619 result = ''.join(parts)
620 self.cache['autoconf-early'] = result
621 return(self.cache['autoconf-early'])
623 def getAutoconfSnippet(self):
624 '''GLModule.getAutoconfSnippet() -> string
626 Return autoconf snippet.'''
627 section = 'configure.ac:'
628 if 'autoconf' not in self.cache:
629 if section not in self.content:
630 result = string()
631 else: # if section in self.content
632 snippet = self.content.split(section)[-1]
633 snippet = snippet.replace('\r\n', '\n')
634 lines = ['%s\n' % line for line in snippet.split('\n')]
635 parts = list()
636 for line in lines:
637 regex = '^(Description|Comment|Status|Notice|Applicability|'
638 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
639 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
640 pattern = compiler(regex)
641 findflag = pattern.findall(line)
642 if findflag:
643 break
644 parts += [line]
645 result = ''.join(parts)
646 self.cache['autoconf'] = result
647 return(self.cache['autoconf'])
649 def getAutomakeSnippet(self):
650 '''getAutomakeSnippet() -> string
652 Get automake snippet.
653 GLConfig: auxdir, ac_version.'''
654 result = string() # Define stack variable
655 conditional = self.getAutomakeSnippet_Conditional()
656 if conditional.strip():
657 result += self.getAutomakeSnippet_Conditional()
658 else: # if not conditional.strip()
659 result += '\n'
660 result += self.getAutomakeSnippet_Unconditional()
661 return(result)
663 def getAutomakeSnippet_Conditional(self):
664 '''GLModule.getAutomakeSnippet_Conditional() -> string
666 Return conditional automake snippet.'''
667 section = 'Makefile.am:'
668 if 'makefile-conditional' not in self.cache:
669 if section not in self.content:
670 result = string()
671 else: # if section in self.content
672 snippet = self.content.split(section)[-1]
673 snippet = snippet.replace('\r\n', '\n')
674 lines = ['%s\n' % line for line in snippet.split('\n')]
675 parts = list()
676 for line in lines:
677 regex = '^(Description|Comment|Status|Notice|Applicability|'
678 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
679 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
680 pattern = compiler(regex)
681 findflag = pattern.findall(line)
682 if findflag:
683 break
684 parts += [line]
685 result = ''.join(parts)
686 self.cache['makefile-conditional'] = result
687 return(self.cache['makefile-conditional'])
689 def getAutomakeSnippet_Unconditional(self):
690 '''GLModule.getAutomakeSnippet_Unconditional() -> string
692 Return unconditional automake snippet.
693 GLConfig: auxdir, ac_version.'''
694 auxdir = self.config['auxdir']
695 ac_version = self.config['ac_version']
696 result = string()
697 if 'makefile-unconditional' not in self.cache:
698 if self.isTests():
699 files = self.getFiles()
700 extra_files = filter_filelist(constants.NL, files,
701 'tests/', '', 'tests/', '').split(constants.NL)
702 extra_files = sorted(set(extra_files))
703 if extra_files:
704 result += string('EXTRA_DIST += %s' %
705 ' '.join(extra_files))
706 result += constants.NL * 2
707 else: # if not tests module
708 # TODO: unconditional automake snippet for nontests modules
709 snippet = self.getAutomakeSnippet_Conditional()
710 snippet = constants.combine_lines(snippet)
711 pattern = compiler(
712 '^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M)
713 mentioned_files = pattern.findall(snippet)
714 if mentioned_files != list():
715 mentioned_files = mentioned_files[-1].split(' ')
716 mentioned_files = [f.strip() for f in mentioned_files]
717 mentioned_files = [f for f in mentioned_files if f != '']
718 mentioned_files = sorted(set(mentioned_files))
719 all_files = self.getFiles()
720 lib_files = filter_filelist(constants.NL, all_files,
721 'lib/', '', 'lib/', '').split(constants.NL)
722 extra_files = [
723 f for f in lib_files if f not in mentioned_files]
724 extra_files = sorted(set(extra_files))
725 if extra_files != [''] and extra_files:
726 result += string('EXTRA_DIST += %s' %
727 ' '.join(extra_files))
728 result += '\n\n'
729 # Synthesize also an EXTRA_lib_SOURCES augmentation
730 if str(self) != 'relocatable-prog-wrapper' and str(self) != 'pt_chown':
731 extra_files = filter_filelist(constants.NL, extra_files,
732 '', '.c', '', '').split(constants.NL)
733 extra_files = sorted(set(extra_files))
734 if extra_files != ['']:
735 result += string('EXTRA_lib_SOURCES += %s' %
736 ' '.join(extra_files))
737 result += '\n\n'
738 # Synthesize an EXTRA_DIST augmentation also for the files in build-aux
739 buildaux_files = filter_filelist(constants.NL, all_files,
740 'build-aux/', '', 'build-aux/', '').split(constants.NL)
741 buildaux_files = sorted(set(buildaux_files))
742 if buildaux_files != ['']:
743 buildaux_files = ''.join(buildaux_files)
744 buildaux_files = joinpath(
745 '$(top_srcdir)', auxdir, buildaux_files)
746 result += string('EXTRA_DIST += %s' % buildaux_files)
747 result += '\n\n'
748 # Synthesize an EXTRA_DIST augmentation also for the files from top/.
749 top_files = filter_filelist(constants.NL, all_files,
750 'top/', '', 'top/', '').split(constants.NL)
751 top_files = sorted(set(top_files))
752 if top_files != ['']:
753 top_files = ''.join(top_files)
754 top_files = joinpath('$(top_srcdir)', top_files)
755 result += string('EXTRA_DIST += %s' % top_files)
756 result += '\n\n'
757 result = constants.nlconvert(result)
758 self.cache['makefile-unconditional'] = result
759 return(self.cache['makefile-unconditional'])
761 def getInclude(self):
762 '''GLModule.getInclude() -> string
764 Return include directive.'''
765 section = 'Include:'
766 if 'include' not in self.cache:
767 if section not in self.content:
768 result = string()
769 else: # if section in self.content
770 snippet = self.content.split(section)[-1]
771 snippet = snippet.replace('\r\n', '\n')
772 lines = ['%s\n' % line for line in snippet.split('\n')]
773 parts = list()
774 for line in lines:
775 regex = '^(Description|Comment|Status|Notice|Applicability|'
776 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
777 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
778 pattern = compiler(regex)
779 findflag = pattern.findall(line)
780 if findflag:
781 break
782 parts += [line]
783 result = ''.join(parts)
784 result = result.strip()
785 pattern = compiler('^(["<].*?[>"])', re.S | re.M)
786 result = pattern.sub('#include \\1', result)
787 self.cache['include'] = result
788 return(self.cache['include'])
790 def getLink(self):
791 '''GLModule.getLink() -> string
793 Return link directive.'''
794 section = 'Link:'
795 if 'link' not in self.cache:
796 if section not in self.content:
797 result = string()
798 else: # if section in self.content
799 snippet = self.content.split(section)[-1]
800 snippet = snippet.replace('\r\n', '\n')
801 lines = ['%s\n' % line for line in snippet.split('\n')]
802 parts = list()
803 for line in lines:
804 regex = '^(Description|Comment|Status|Notice|Applicability|'
805 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
806 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
807 pattern = compiler(regex)
808 findflag = pattern.findall(line)
809 if findflag:
810 break
811 parts += [line]
812 parts = [part.strip() for part in parts if part.strip()]
813 result = ''.join(parts)
814 self.cache['link'] = result
815 return(self.cache['link'])
817 def getLicense(self):
818 '''GLModule.getLicense(self) -> string
820 Get license and warn user if module lacks a license.'''
821 license = self.getLicense_Raw()
822 if not self.isTests():
823 if not license:
824 if self.config['errors']:
825 raise(GLError(18, string(self)))
826 else: # if not self.config['errors']
827 sys.stderr.write('gnulib-tool: warning: ')
828 sys.stderr.write('module %s lacks a license\n' % str(self))
829 if not license:
830 license = 'GPL'
831 return(license)
833 def getLicense_Raw(self):
834 '''GLModule.getLicense_Raw() -> string
836 Return module license.'''
837 section = 'License:'
838 if 'license' not in self.cache:
839 if section not in self.content:
840 result = string()
841 else: # if section in self.content
842 pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
843 pattern = compiler(pattern, re.S | re.M)
844 result = pattern.findall(self.content)
845 if type(result) is list:
846 if not result:
847 result = string()
848 else: # if result
849 result = result[-1]
850 result = result.strip()
851 self.cache['license'] = result
852 return(self.cache['license'])
854 def getMaintainer(self):
855 '''GLModule.getMaintainer() -> string
857 Return maintainer directive.'''
858 section = 'Maintainer:'
859 if 'maintainer' not in self.cache:
860 if section not in self.content:
861 result = string()
862 else: # if section in self.content
863 snippet = self.content.split(section)[-1]
864 snippet = snippet.replace('\r\n', '\n')
865 lines = ['%s\n' % line for line in snippet.split('\n')]
866 parts = list()
867 for line in lines:
868 regex = '^(Description|Comment|Status|Notice|Applicability|'
869 regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
870 regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
871 pattern = compiler(regex)
872 findflag = pattern.findall(line)
873 if findflag:
874 break
875 parts += [line]
876 result = ''.join(parts)
877 result = result.strip()
878 self.cache['maintainer'] = result
879 return(self.cache['maintainer'])
882 #===============================================================================
883 # Define GLModuleTable class
884 #===============================================================================
885 class GLModuleTable(object):
886 '''GLModuleTable is used to work with the list of the modules.'''
888 def __init__(self, config, avoids=list()):
889 '''GLModuleTable.__init__(config, avoids) -> GLModuleTable
891 Create new GLModuleTable instance. If modules are specified, then add
892 every module from iterable as unconditional module. If avoids is specified,
893 then in transitive_closure every dependency which is in avoids won't be
894 included in the final modules list. If testflags iterable is enabled, then
895 don't add module which status is in the testflags. If conddeps are enabled,
896 then store condition for each dependency if it has a condition.
897 The only necessary argument is localdir, which is needed just to create
898 modulesystem instance to look for dependencies.'''
899 self.avoids = list() # Avoids
900 self.dependers = dict() # Dependencies
901 self.conditionals = dict() # Conditional modules
902 self.unconditionals = dict() # Unconditional modules
903 self.base_modules = list() # Base modules
904 self.main_modules = list() # Main modules
905 self.tests_modules = list() # Tests modules
906 self.final_modules = list() # Final modules
907 if type(config) is not GLConfig:
908 raise(TypeError('config must be a GLConfig, not %s' %
909 type(config).__name__))
910 for avoid in avoids:
911 if type(avoid) is not GLModule:
912 raise(TypeError('each avoid must be a GLModule instance'))
913 self.avoids += [avoids]
914 self.config = config
915 self.filesystem = GLFileSystem(self.config)
916 self.modulesystem = GLModuleSystem(self.config)
918 def __repr__(self):
919 '''x.__repr__() <==> repr(x)'''
920 result = '<pygnulib.GLModuleTable %s>' % hex(id(self))
921 return(result)
923 def __getitem__(self, y):
924 '''x.__getitem__(y) <==> x[y]'''
925 if y in ['base', 'final', 'main', 'tests', 'avoids']:
926 if y == 'base':
927 return(self.getBaseModules())
928 elif y == 'final':
929 return(self.getFinalModules())
930 elif y == 'main':
931 return(self.getMainModules())
932 elif y == 'tests':
933 return(self.getTestsModules())
934 else: # if y == 'avoids'
935 return(self.getAvoids())
936 else: # if y is not in list
937 raise(KeyError('GLModuleTable does not contain key: %s' % repr(y)))
939 def addConditional(self, parent, module, condition):
940 '''GLModuleTable.addConditional(module, condition)
942 Add new conditional dependency from parent to module with condition.'''
943 if type(parent) is not GLModule:
944 raise(TypeError('parent must be a GLModule, not %s' %
945 type(parent).__name__))
946 if type(module) is not GLModule:
947 raise(TypeError('module must be a GLModule, not %s' %
948 type(module).__name__))
949 if type(condition) is bytes or type(condition) is string \
950 or condition == True:
951 if type(condition) is bytes:
952 condition = condition.decode(ENCS['default'])
953 else: # if condition has not bytes or string type or is not True
954 raise(TypeError('condition must be a string or True, not %s' %
955 type(condition).__name__))
956 if not str(module) in self.unconditionals:
957 if str(module) not in self.dependers:
958 self.dependers[module] = list()
959 self.dependers[module] += [module]
960 key = '%s---%s' % (str(parent), str(module))
961 self.conditionals[key] = condition
963 def addUnconditional(self, module):
964 '''GLModuleTable.addUnconditional(module)
966 Add module as unconditional dependency.'''
967 if type(module) is not GLModule:
968 raise(TypeError('module must be a GLModule, not %s' %
969 type(module).__name__))
970 if str(module) in self.dependers:
971 self.dependers.pop(str(module))
972 self.unconditionals[str(module)] = True
974 def isConditional(self, module):
975 '''GLModuleTable.isConditional(module) -> bool
977 Check whether module is unconditional.'''
978 if type(module) is not GLModule:
979 raise(TypeError('module must be a GLModule, not %s' %
980 type(module).__name__))
981 result = str(module) in self.dependers
982 return(result)
984 def getCondition(self, parent, module):
985 '''GLModuleTable.getCondition(module) -> string or True
987 Return condition from parent to module. Condition can be string or True.
988 If module is not in the list of conddeps, method returns None.'''
989 if type(parent) is not GLModule:
990 raise(TypeError('parent must be a GLModule, not %s' %
991 type(parent).__name__))
992 if type(module) is not GLModule:
993 raise(TypeError('module must be a GLModule, not %s' %
994 type(module).__name__))
995 key = '%s---%s' % (str(parent), str(module))
996 result = None
997 if key in self.conditionals:
998 result = self.conditionals[key]
999 return(result)
1001 def transitive_closure(self, modules):
1002 '''GLModuleTable.transitive_closure(modules) -> list
1004 Use transitive closure to add module and its dependencies. Add every
1005 module and its dependencies from modules list, but do not add dependencies
1006 which contain in avoids list. If any testflag is enabled, then do not add
1007 dependencies which have the status as this flag. If conddeps are enabled,
1008 then store condition for each dependency if it has a condition. This method
1009 is used to update final list of modules. Method returns list of modules.
1010 GLConfig: testflags.'''
1011 for module in modules:
1012 if type(module) is not GLModule:
1013 raise(TypeError('each module must be a GLModule instance'))
1014 handledmodules = list()
1015 inmodules = modules
1016 outmodules = list()
1017 if self.config['conddeps']:
1018 for module in modules:
1019 self.addUnconditional(module)
1020 while inmodules:
1021 inmodules_this_round = inmodules
1022 inmodules = list()
1023 for module in inmodules_this_round:
1024 outmodules += [module]
1025 if self.config['conddeps']:
1026 automake_snippet = \
1027 module.getAutomakeSnippet_Conditional()
1028 pattern = compiler('^if')
1029 if not pattern.findall(automake_snippet):
1030 self.addUnconditional(module)
1031 conditional = self.isConditional(module)
1032 dependencies = module.getDependencies()
1033 depmodules = [pair[0] for pair in dependencies]
1034 conditions = [pair[1] for pair in dependencies]
1035 if TESTS['tests'] in self.config['testflags']:
1036 testsname = module.getTestsName()
1037 if self.modulesystem.exists(testsname):
1038 testsmodule = self.modulesystem.find(testsname)
1039 depmodules += [testsmodule]
1040 conditions += [None]
1041 for depmodule in depmodules:
1042 include = True
1043 includes = list()
1044 status = depmodule.getStatus()
1045 for word in status:
1046 if word == 'obsolete':
1047 if TESTS['obsolete'] in self.config['testflags'] or \
1048 TESTS['all-test'] in self.config['testflags']:
1049 includes += [False]
1050 elif word == 'c++-test':
1051 if TESTS['c++-test'] in self.config['testflags'] or \
1052 TESTS['all-test'] in self.config['testflags']:
1053 includes += [False]
1054 elif word == 'longrunning-test':
1055 if TESTS['longrunning-test'] in self.config['testflags'] or \
1056 TESTS['all-test'] in self.config['testflags']:
1057 includes += [False]
1058 elif word == 'privileged-test':
1059 if TESTS['privileged-test'] in self.config['testflags'] or \
1060 TESTS['all-test'] in self.config['testflags']:
1061 includes += [False]
1062 elif word == 'all-test':
1063 if TESTS['all-test'] in self.config['testflags'] or \
1064 TESTS['all-test'] in self.config['testflags']:
1065 includes += [False]
1066 else: # if any other word
1067 if word.endswith('-tests'):
1068 if TESTS['all-test'] in self.config['testflags']:
1069 includes += [False]
1070 include = any(includes)
1071 if include and depmodule not in self.avoids:
1072 inmodules += [depmodule]
1073 if self.config['conddeps']:
1074 index = depmodules.index(depmodule)
1075 condition = conditions[index]
1076 if condition:
1077 self.addConditional(
1078 module, depmodule, condition)
1079 else: # if condition
1080 if conditional:
1081 self.addConditional(
1082 module, depmodule, True)
1083 else: # if not conditional
1084 self.addUnconditional(module)
1085 listing = list() # Create empty list
1086 inmodules = sorted(set(inmodules))
1087 handledmodules = sorted(set(handledmodules + inmodules_this_round))
1088 inmodules = \
1089 [ # Begin to filter inmodules
1090 module for module in inmodules if module not in handledmodules
1091 ] # Finish to filter inmodules
1092 inmodules = sorted(set(inmodules))
1093 modules = sorted(set(outmodules))
1094 self.modules = modules
1095 return(list(modules))
1097 def transitive_closure_separately(self, basemodules, finalmodules):
1098 '''GLModuleTable.transitive_closure_separately(*args, **kwargs) -> tuple
1100 Determine main module list and tests-related module list separately.
1101 The main module list is the transitive closure of the specified modules,
1102 ignoring tests modules. Its lib/* sources go into $sourcebase/. If lgpl is
1103 specified, it will consist only of LGPLed source.
1104 The tests-related module list is the transitive closure of the specified
1105 modules, including tests modules, minus the main module list excluding
1106 modules of applicability 'all'. Its lib/* sources (brought in through
1107 dependencies of *-tests modules) go into $testsbase/. It may contain GPLed
1108 source, even if lgpl is specified.
1109 Arguments are basemodules and finalmodules, where basemodules argument
1110 represents modules specified by user and finalmodules represents modules
1111 list after previous transitive_closure.
1112 Method returns tuple which contains two lists: the list of main modules and
1113 the list of tests-related modules. Both lists contain dependencies.
1114 GLConfig: testflags.'''
1115 inctests = False
1116 main_modules = list()
1117 tests_modules = list()
1118 if TESTS['tests'] in self.config['testflags']:
1119 self.config['testflags'].pop(TESTS['tests'])
1120 inctests = True
1121 for module in basemodules:
1122 if type(module) is not GLModule:
1123 raise(TypeError('each module must be a GLModule instance'))
1124 for module in finalmodules:
1125 if type(module) is not GLModule:
1126 raise(TypeError('each module must be a GLModule instance'))
1127 main_modules = self.transitive_closure(basemodules)
1128 tests_modules = \
1129 [m for m in finalmodules if m not in main_modules] + \
1130 [m for m in main_modules if m.getApplicability() != 'main']
1131 tests_modules = sorted(set(tests_modules))
1132 if inctests:
1133 testflags = sorted(
1134 set(self.config['testflags'] + [TESTS['tests']]))
1135 self.config.setTestFlags(testflags)
1136 result = tuple([main_modules, tests_modules])
1137 return(result)
1139 def add_dummy(self, modules):
1140 '''GLModuleTable.add_dummy(modules) -> list
1142 Add dummy package to list of modules if dummy package is needed. If not,
1143 return original list of modules.
1144 GLConfig: auxdir, ac_version.'''
1145 auxdir = self.config['auxdir']
1146 ac_version = self.config['ac_version']
1147 have_lib_sources = False
1148 for module in modules:
1149 if type(module) is not GLModule:
1150 raise(TypeError('each module must be a GLModule instance'))
1151 snippet = module.getAutomakeSnippet()
1152 snippet = constants.remove_backslash_newline(snippet)
1153 pattern = compiler(
1154 '^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M)
1155 files = pattern.findall(snippet)
1156 if files: # if source files were found
1157 files = files[-1].split(' ')
1158 for file in files:
1159 if not file.endswith('.h'):
1160 have_lib_sources = True
1161 break
1162 if not have_lib_sources:
1163 dummy = self.modulesystem.find('dummy')
1164 modules = sorted(set(modules + [dummy]))
1165 return(list(modules))
1167 def filelist(self, modules):
1168 '''GLModuleTable.filelist(modules) -> list
1170 Determine the final file list for the given list of modules. The list of
1171 modules must already include dependencies.
1172 GLConfig: ac_version.'''
1173 ac_version = self.config['ac_version']
1174 filelist = list()
1175 for module in modules:
1176 if type(module) is not GLModule:
1177 raise(TypeError('each module must be a GLModule instance'))
1178 listings = [module.getFiles() for module in modules]
1179 for listing in listings:
1180 for file in listing:
1181 if file not in filelist:
1182 filelist += [file]
1183 return(filelist)
1185 def filelist_separately(self, main_modules, tests_modules):
1186 '''GLModuleTable.filelist_separately(**kwargs) -> list
1188 Determine the final file lists. They must be computed separately, because
1189 files in lib/* go into $sourcebase/ if they are in the main file list but
1190 into $testsbase/ if they are in the tests-related file list. Furthermore
1191 lib/dummy.c can be in both.'''
1192 ac_version = self.config['ac_version']
1193 main_filelist = self.filelist(main_modules)
1194 tests_filelist = self.filelist(tests_modules)
1195 tests_filelist = \
1196 [ # Begin to sort filelist
1197 file.replace('lib/', 'tests=lib/', 1) \
1198 if file.startswith('lib/') else file
1199 for file in tests_filelist
1200 ] # Finish to sort filelist
1201 result = tuple([main_filelist, tests_filelist])
1202 return(result)
1204 def getAvoids(self):
1205 '''GLModuleTable.getAvoids() -> list
1207 Return list of avoids.'''
1208 return(list(self.avoids))
1210 def setAvoids(self, modules):
1211 '''GLModuleTable.setAvoids(modules)
1213 Specify list of avoids.'''
1214 for module in modules:
1215 if type(module) is not GLModule:
1216 raise(TypeError('each module must be a GLModule instance'))
1217 self.avoids = sorted(set(modules))
1219 def getBaseModules(self):
1220 '''GLModuleTable.getBaseModules() -> list
1222 Return list of base modules.'''
1223 return(list(self.base_modules))
1225 def setBaseModules(self, modules):
1226 '''GLModuleTable.setBaseModules(modules)
1228 Specify list of base modules.'''
1229 for module in modules:
1230 if type(module) is not GLModule:
1231 raise(TypeError('each module must be a GLModule instance'))
1232 self.base_modules = sorted(set(modules))
1234 def getFinalModules(self):
1235 '''GLModuleTable.getFinalModules() -> list
1237 Return list of final modules.'''
1238 return(list(self.final_modules))
1240 def setFinalModules(self, modules):
1241 '''GLModuleTable.setFinalModules(modules)
1243 Specify list of final modules.'''
1244 for module in modules:
1245 if type(module) is not GLModule:
1246 raise(TypeError('each module must be a GLModule instance'))
1247 self.final_modules = sorted(set(modules))
1249 def getMainModules(self):
1250 '''GLModuleTable.getMainModules() -> list
1252 Return list of main modules.'''
1253 return(list(self.main_modules))
1255 def setMainModules(self, modules):
1256 '''GLModuleTable.setMainModules(modules)
1258 Specify list of main modules.'''
1259 for module in modules:
1260 if type(module) is not GLModule:
1261 raise(TypeError('each module must be a GLModule instance'))
1262 self.main_modules = sorted(set(modules))
1264 def getTestsModules(self):
1265 '''GLModuleTable.getTestsModules() -> list
1267 Return list of tests modules.'''
1268 return(list(self.tests_modules))
1270 def setTestsModules(self, modules):
1271 '''GLModuleTable.setTestsModules(modules)
1273 Specify list of tests modules.'''
1274 for module in modules:
1275 if type(module) is not GLModule:
1276 raise(TypeError('each module must be a GLModule instance'))
1277 self.tests_modules = sorted(set(modules))