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 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']
558 section
= 'Depends-on:'
559 if 'dependencies' not in self
.cache
:
560 if section
not in self
.content
:
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')]
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
)
576 modules
= ''.join(parts
)
577 modules
= [line
for line
in modules
.split(
578 '\n') if line
.strip()]
580 module
for module
in modules
if not module
.startswith('#')]
582 split
= [part
for part
in line
.split(' ') if part
.strip()]
584 module
= line
.strip()
586 else: # if len(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
:
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')]
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
)
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
:
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')]
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
)
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()
658 result
+= self
.getAutomakeSnippet_Unconditional()
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
:
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')]
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
)
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']
695 if 'makefile-unconditional' not in self
.cache
:
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
))
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
)
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
)
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
))
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
))
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
)
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
)
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.'''
764 if 'include' not in self
.cache
:
765 if section
not in self
.content
:
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')]
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
)
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'])
789 '''GLModule.getLink() -> string
791 Return link directive.'''
793 if 'link' not in self
.cache
:
794 if section
not in self
.content
:
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')]
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
)
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():
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
))
831 def getLicense_Raw(self
):
832 '''GLModule.getLicense_Raw() -> string
834 Return module license.'''
836 if 'license' not in self
.cache
:
837 if section
not in self
.content
:
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:
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
:
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')]
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
)
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
__))
909 if type(avoid
) is not GLModule
:
910 raise(TypeError('each avoid must be a GLModule instance'))
911 self
.avoids
+= [avoids
]
913 self
.filesystem
= GLFileSystem(self
.config
)
914 self
.modulesystem
= GLModuleSystem(self
.config
)
917 '''x.__repr__() <==> repr(x)'''
918 result
= '<pygnulib.GLModuleTable %s>' % hex(id(self
))
921 def __getitem__(self
, y
):
922 '''x.__getitem__(y) <==> x[y]'''
923 if y
in ['base', 'final', 'main', 'tests', 'avoids']:
925 return(self
.getBaseModules())
927 return(self
.getFinalModules())
929 return(self
.getMainModules())
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
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
))
995 if key
in self
.conditionals
:
996 result
= self
.conditionals
[key
]
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()
1015 if self
.config
['conddeps']:
1016 for module
in modules
:
1017 self
.addUnconditional(module
)
1019 inmodules_this_round
= inmodules
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
:
1042 status
= depmodule
.getStatus()
1044 if word
== 'obsolete':
1045 if TESTS
['obsolete'] in self
.config
['testflags'] or \
1046 TESTS
['all-test'] in self
.config
['testflags']:
1048 elif word
== 'c++-test':
1049 if TESTS
['c++-test'] in self
.config
['testflags'] or \
1050 TESTS
['all-test'] in self
.config
['testflags']:
1052 elif word
== 'longrunning-test':
1053 if TESTS
['longrunning-test'] in self
.config
['testflags'] or \
1054 TESTS
['all-test'] in self
.config
['testflags']:
1056 elif word
== 'privileged-test':
1057 if TESTS
['privileged-test'] in self
.config
['testflags'] or \
1058 TESTS
['all-test'] in self
.config
['testflags']:
1060 elif word
== 'all-test':
1061 if TESTS
['all-test'] in self
.config
['testflags'] or \
1062 TESTS
['all-test'] in self
.config
['testflags']:
1064 else: # if any other word
1065 if word
.endswith('-tests'):
1066 if TESTS
['all-test'] in self
.config
['testflags']:
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
]
1075 self
.addConditional(
1076 module
, depmodule
, condition
)
1077 else: # if condition
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
))
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.'''
1114 main_modules
= list()
1115 tests_modules
= list()
1116 if TESTS
['tests'] in self
.config
['testflags']:
1117 self
.config
['testflags'].pop(TESTS
['tests'])
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
)
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
))
1132 set(self
.config
['testflags'] + [TESTS
['tests']]))
1133 self
.config
.setTestFlags(testflags
)
1134 result
= tuple([main_modules
, tests_modules
])
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
)
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(' ')
1157 if not file.endswith('.h'):
1158 have_lib_sources
= True
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']
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
:
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
)
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
])
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
))