3 # This is a replacement for the formerly bash-written function pl_parsePkgs ()
5 # Usage: $0 [-a arch] default_arch keyword fcdistro pldistro pkgs-file[..s]
6 # default_arch is $pl_DISTRO_ARCH, but can be overridden
9 #################### original language was (in this example, keyword=package)
10 ## to add to all distros
12 ## to add in one distro
14 ## to remove in one distro
16 #################### replacement language
21 ## add in fedora distros starting with f10
22 # +package>=f10: p1 p2
25 ## ditto but remove instead
26 # -package=centos5: p1 p2
27 # -package<=centos5: p1 p2
29 # pylint: disable=c0111
33 from sys import stderr
34 from optparse import OptionParser
37 default_arch = 'x86_64'
38 known_archs = ['i386', 'i686', 'x86_64']
39 default_fcdistro = 'f37'
42 # oldies but we have references to that in the pkgs files
43 'f8', 'f10', 'f12', 'f14', 'f16', 'f18',
44 'f20', 'f21', 'f22', 'f23', 'f24', 'f25', 'f27',
45 # these ones are still relevant;
46 # f32 is mentioned to be able to use create-vms with that distro
47 # as we're running into issues to build a minimal f33 from a f29 host
48 'f29', 'f31', 'f32', 'f33', 'f35', 'f37',
60 default_pldistro = 'onelab'
63 'group', 'groupname', 'groupdesc',
64 'package', 'pip', 'gem',
65 'nodeyumexclude', 'plcyumexclude', 'yumexclude',
66 'precious', 'junk', 'mirror',
70 m_fcdistro_cutter = re.compile('([a-z]+)([0-9]+)')
75 def __init__(self, arch, fcdistro, pldistro, keyword, inputs, options):
77 self.fcdistro = fcdistro
78 self.pldistro = pldistro
79 self.keyword = keyword
81 # for verbose, new_line, and the like
82 self.options = options
84 for known in known_fcdistros:
87 (distro, version) = m_fcdistro_cutter.match(fcdistro).groups()
88 # debian-like names can't use numbering
95 self.version = int(version)
97 print('unrecognized fcdistro', fcdistro, file=stderr)
100 # qualifier is either '>=','<=', or '='
101 def match(self, qualifier, version):
103 return self.version == version
104 elif qualifier == '>=':
105 return self.version >= version
106 elif qualifier == '<=':
107 return self.version <= version
110 'Internal error - unexpected qualifier {}'.format(qualifier))
112 m_comment = re.compile(r'\A\s*#')
113 m_blank = re.compile(r'\A\s*\Z')
115 m_ident = re.compile(r'\A'+re_ident+r'\Z')
116 re_qualified = r'\s*'
117 re_qualified += r'(?P<plus_minus>[+-]?)'
118 re_qualified += r'\s*'
119 re_qualified += r'(?P<keyword>{re_ident})'.format(re_ident=re_ident)
120 re_qualified += r'\s*'
121 re_qualified += r'(?P<qualifier>>=|<=|=)'
122 re_qualified += r'\s*'
123 re_qualified += r'(?P<fcdistro>{re_ident}[0-9]+)'.format(re_ident=re_ident)
124 re_qualified += r'\s*'
125 m_qualified = re.compile(r'\A{re_qualified}\Z'.format(re_qualified=re_qualified))
127 re_old = '[a-z]+[+-][a-z]+[0-9]+'
128 m_old = re.compile(r'\A{}\Z'.format(re_old))
130 # returns a tuple (included, excluded)
131 def parse(self, filename):
136 with open(filename) as feed:
137 for lineno, line in enumerate(feed, 1):
139 if self.m_comment.match(line) or self.m_blank.match(line):
142 lefts, rights = line.split(':', 1)
143 for left in lefts.split():
144 ########## single ident
145 if self.m_ident.match(left):
146 if left not in known_keywords:
147 raise Exception("Unknown keyword {left}".format(**locals()))
148 elif left == self.keyword:
149 included += rights.split()
151 m = self.m_qualified.match(left)
153 (plus_minus, kw, qual, fcdistro) = m.groups()
154 if kw not in known_keywords:
155 raise Exception("Unknown keyword in {left}".format(**locals()))
156 if fcdistro not in known_fcdistros:
157 raise Exception('Unknown fcdistro {fcdistro}'.format(**locals()))
158 # skip if another keyword
159 if kw != self.keyword: continue
160 # does this fcdistro match ?
161 (distro, version) = m_fcdistro_cutter.match(fcdistro).groups()
162 version = int (version)
163 # skip if another distro family
164 if distro != self.distro: continue
165 # skip if the qualifier does not fit
166 if not self.match (qual, version):
167 if self.options.verbose:
168 print('{filename}:{lineno}:qualifer {left} does not apply'
169 .format(**locals()), file=stderr)
171 # we're in, let's add (default) or remove (if plus_minus is minus)
172 if plus_minus == '-':
173 if self.options.verbose:
174 print('{filename}:{lineno}: from {left}, excluding {rights}'
175 .format(**locals()), file=stderr)
176 excluded += rights.split()
178 if self.options.verbose:
179 print('{filename}:{lineno}: from {left}, including {rights}'\
180 .format(**locals()), file=stderr)
181 included += rights.split()
182 elif self.m_old.match(left):
183 raise Exception('Old-fashioned syntax not supported anymore {left}'.\
186 raise Exception('error in left expression {left}'.format(**locals()))
188 except Exception as e:
190 print("{filename}:{lineno}:syntax error: {e}".format(**locals()), file=stderr)
191 except Exception as exc:
193 print('Could not parse file', filename, exc, file=stderr)
194 return (ok, included, excluded)
200 for input in self.inputs:
201 (o, i, e) = self.parse (input)
205 # avoid set operations that would not preserve order
206 results = [x for x in included if x not in excluded]
208 results = [x.replace('@arch@', self.arch)
209 .replace('@fcdistro@', self.fcdistro)
210 .replace('@pldistro@', self.pldistro) for x in results]
211 if self.options.sort_results:
213 # default is space-separated
214 if not self.options.new_line:
215 print(" ".join(results))
216 # but for tests results are printed each on a line
218 for result in results:
223 usage = "Usage: %prog [options] keyword input[...]"
224 parser = OptionParser(usage=usage)
226 '-a', '--arch', dest='arch', action='store', default=default_arch,
227 help='target arch, e.g. i386 or x86_64, default={}'.format(default_arch))
229 '-f', '--fcdistro', dest='fcdistro', action='store', default=default_fcdistro,
230 help='fcdistro, e.g. f12 or centos5')
232 '-d', '--pldistro', dest='pldistro', action='store', default=default_pldistro,
233 help='pldistro, e.g. onelab or planetlab')
235 '-v', '--verbose', dest='verbose', action='store_true', default=False,
236 help='verbose when using qualifiers')
238 '-n', '--new-line', dest='new_line', action='store_true', default=False,
239 help='print outputs separated with newlines rather than with a space')
241 '-u', '--no-sort', dest='sort_results', default=True, action='store_false',
242 help='keep results in the same order as in the inputs')
243 (options, args) = parser.parse_args()
246 parser.print_help(file=stderr)
250 if options.arch not in known_archs:
251 print('Unsupported arch', options.arch, file=stderr)
252 parser.print_help(file=stderr)
254 if options.arch == 'i686':
255 options.arch = 'i386'
256 if options.fcdistro not in known_fcdistros:
257 print('Unsupported fcdistro', options.fcdistro, file=stderr)
258 parser.print_help(file=stderr)
261 pkgs = PkgsParser(options.arch, options.fcdistro, options.pldistro,
262 keyword, inputs, options)
269 if __name__ == '__main__':