4 #===============================================================================
5 # Define global imports
6 #===============================================================================
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
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
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
64 if type(config
) is not GLConfig
:
65 raise(TypeError('config must be a GLConfig, not %s' %
66 type(config
).__name
__))
68 self
.filesystem
= GLFileSystem(self
.config
)
71 '''x.__repr__ <==> repr(x)'''
72 result
= '<pygnulib.GLModuleSystem %s>' % hex(id(self
))
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
85 'module must be a string, not %s' % type(module
).__name
__))
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')),
94 joinpath(self
.config
['localdir'], 'modules', module
))
95 ]): # Close all(iterable) function
96 if module
not in badnames
:
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
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
)
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
))
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.'''
129 localdir
= self
.config
['localdir']
130 find_args
= ['find', 'modules', '-type', 'f', '-print']
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',
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')):
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:
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")
173 listing
= [line
for line
in result
.split('\n') if line
.strip()]
174 listing
= sorted(set(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,
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.'''
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
__))
208 self
.patched
= patched
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'''
221 if type(module
) is GLModule
:
222 if self
.module
== module
.module
:
226 def __ne__(self
, module
):
227 '''x.__ne__(y) <==> x!=y'''
229 if type(module
) is GLModule
:
230 if self
.module
!= module
.module
:
234 def __ge__(self
, module
):
235 '''x.__ge__(y) <==> x>=y'''
237 if type(module
) is GLModule
:
238 if self
.module
>= module
.module
:
242 def __gt__(self
, module
):
243 '''x.__gt__(y) <==> x>y'''
245 if type(module
) is GLModule
:
246 if self
.module
> module
.module
:
251 '''x.__hash__() <==> hash(x)'''
252 module
= hash(self
.module
)
253 patched
= hash(self
.patched
)
254 result
= module ^ patched
257 def __le__(self
, module
):
258 '''x.__le__(y) <==> x<=y'''
260 if type(module
) is GLModule
:
261 if self
.module
<= module
.module
:
265 def __lt__(self
, module
):
266 '''x.__lt__(y) <==> x<y'''
268 if type(module
) is GLModule
:
269 if self
.module
< module
.module
:
274 '''x.__str__() <==> str(x)'''
275 result
= self
.getName()
279 '''x.__repr__ <==> repr(x)'''
280 result
= '<pygnulib.GLModule %s %s>' % \
281 (repr(self
.getName()), hex(id(self
)))
285 '''GLModule.getName() -> string
287 Return the name of the module.'''
288 pattern
= compiler(joinpath('modules', '(.*?)$'))
289 result
= pattern
.findall(self
.module
)[0]
293 '''GLModule.isPatched() -> bool
295 Check whether module was created after applying patch.'''
299 '''GLModule.isTests() -> bool
301 Check whether module is a -tests version of module.'''
302 result
= self
.getName().endswith('-tests')
305 def isNonTests(self
):
306 '''GLModule.isTests() -> bool
308 Check whether module is not a -tests version of module.'''
309 result
= not(self
.isTests())
312 def getTestsName(self
):
313 '''Return -tests version of the module name.'''
314 result
= self
.getName()
315 if not result
.endswith('-tests'):
319 def getTestsModule(self
):
320 '''Return -tests version of the module as GLModule.'''
321 result
= self
.modulesystem
.find(self
.getTestsName())
324 def getShellFunc(self
):
325 '''GLModule.getShellFunc() -> string
327 Computes the shell function name that will contain the m4 macros for the
330 macro_prefix
= self
.config
['macro_prefix']
331 for char
in str(module
):
332 if char
not in constants
.ALPHANUMERIC
:
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'])
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.'''
353 macro_prefix
= self
.config
['macro_prefix']
354 for char
in str(module
):
355 if char
not in constants
.ALPHANUMERIC
:
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'])
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']
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
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'])
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
:
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:
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.'''
417 if 'comment' not in self
.cache
:
418 if section
not in self
.content
:
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:
429 result
= result
.strip()
430 self
.cache
['comment'] = result
431 return(self
.cache
['comment'])
434 '''GLModule.getStatus() -> string
436 Return module status.'''
438 if 'status' not in self
.cache
:
439 if section
not in self
.content
:
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')]
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
)
455 result
= [part
.strip() for part
in parts
if part
.strip()]
456 self
.cache
['status'] = list(result
)
457 return(list(self
.cache
['status']))
460 '''GLModule.getNotice() -> string
462 Return notice to module.'''
464 if 'notice' not in self
.cache
:
465 if section
not in self
.content
:
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')]
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
)
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
:
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')]
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
)
507 parts
= [part
.strip() for part
in parts
]
508 result
= ''.join(parts
)
509 if not result
.strip():
510 if self
.getName().endswith('-tests'):
512 else: # if not self.getName().endswith('-tests')
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'])
521 '''GLModule.getFiles() -> list
523 Return list of files.
524 GLConfig: ac_version.'''
525 ac_version
= self
.config
['ac_version']
528 if 'files' not in self
.cache
:
529 if section
not in self
.content
:
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')]
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
)
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']
560 section
= 'Depends-on:'
561 if 'dependencies' not in self
.cache
:
562 if section
not in self
.content
:
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')]
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
)
578 modules
= ''.join(parts
)
579 modules
= [line
for line
in modules
.split(
580 '\n') if line
.strip()]
582 module
for module
in modules
if not module
.startswith('#')]
584 split
= [part
for part
in line
.split(' ') if part
.strip()]
586 module
= line
.strip()
588 else: # if len(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
:
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')]
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
)
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
:
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')]
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
)
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()
660 result
+= self
.getAutomakeSnippet_Unconditional()
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
:
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')]
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
)
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']
697 if 'makefile-unconditional' not in self
.cache
:
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
))
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
)
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
)
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
))
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
))
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
)
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
)
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.'''
766 if 'include' not in self
.cache
:
767 if section
not in self
.content
:
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')]
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
)
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'])
791 '''GLModule.getLink() -> string
793 Return link directive.'''
795 if 'link' not in self
.cache
:
796 if section
not in self
.content
:
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')]
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
)
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():
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
))
833 def getLicense_Raw(self
):
834 '''GLModule.getLicense_Raw() -> string
836 Return module license.'''
838 if 'license' not in self
.cache
:
839 if section
not in self
.content
:
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:
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
:
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')]
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
)
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
__))
911 if type(avoid
) is not GLModule
:
912 raise(TypeError('each avoid must be a GLModule instance'))
913 self
.avoids
+= [avoids
]
915 self
.filesystem
= GLFileSystem(self
.config
)
916 self
.modulesystem
= GLModuleSystem(self
.config
)
919 '''x.__repr__() <==> repr(x)'''
920 result
= '<pygnulib.GLModuleTable %s>' % hex(id(self
))
923 def __getitem__(self
, y
):
924 '''x.__getitem__(y) <==> x[y]'''
925 if y
in ['base', 'final', 'main', 'tests', 'avoids']:
927 return(self
.getBaseModules())
929 return(self
.getFinalModules())
931 return(self
.getMainModules())
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
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
))
997 if key
in self
.conditionals
:
998 result
= self
.conditionals
[key
]
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()
1017 if self
.config
['conddeps']:
1018 for module
in modules
:
1019 self
.addUnconditional(module
)
1021 inmodules_this_round
= inmodules
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
:
1044 status
= depmodule
.getStatus()
1046 if word
== 'obsolete':
1047 if TESTS
['obsolete'] in self
.config
['testflags'] or \
1048 TESTS
['all-test'] in self
.config
['testflags']:
1050 elif word
== 'c++-test':
1051 if TESTS
['c++-test'] in self
.config
['testflags'] or \
1052 TESTS
['all-test'] in self
.config
['testflags']:
1054 elif word
== 'longrunning-test':
1055 if TESTS
['longrunning-test'] in self
.config
['testflags'] or \
1056 TESTS
['all-test'] in self
.config
['testflags']:
1058 elif word
== 'privileged-test':
1059 if TESTS
['privileged-test'] in self
.config
['testflags'] or \
1060 TESTS
['all-test'] in self
.config
['testflags']:
1062 elif word
== 'all-test':
1063 if TESTS
['all-test'] in self
.config
['testflags'] or \
1064 TESTS
['all-test'] in self
.config
['testflags']:
1066 else: # if any other word
1067 if word
.endswith('-tests'):
1068 if TESTS
['all-test'] in self
.config
['testflags']:
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
]
1077 self
.addConditional(
1078 module
, depmodule
, condition
)
1079 else: # if condition
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
))
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.'''
1116 main_modules
= list()
1117 tests_modules
= list()
1118 if TESTS
['tests'] in self
.config
['testflags']:
1119 self
.config
['testflags'].pop(TESTS
['tests'])
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
)
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
))
1134 set(self
.config
['testflags'] + [TESTS
['tests']]))
1135 self
.config
.setTestFlags(testflags
)
1136 result
= tuple([main_modules
, tests_modules
])
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
)
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(' ')
1159 if not file.endswith('.h'):
1160 have_lib_sources
= True
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']
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
:
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
)
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
])
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
))