Gentoo Archives: gentoo-commits

From: "André Erdmann" <dywi@×××××××.de>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/overlay/pkgdir/
Date: Wed, 28 Aug 2013 15:54:39
Message-Id: 1377705107.c55acecf0d7d58ec898a5c9bbd36086fc5ee52f6.dywi@gentoo
1 commit: c55acecf0d7d58ec898a5c9bbd36086fc5ee52f6
2 Author: André Erdmann <dywi <AT> mailerd <DOT> de>
3 AuthorDate: Wed Aug 28 15:51:47 2013 +0000
4 Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
5 CommitDate: Wed Aug 28 15:51:47 2013 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=c55acecf
7
8 overlay: (partially) parse ebuild files
9
10 This commit adds a parser that parses an ebuild file partially. More concrete,
11 it reads $SRC_URI, which is required distmap-related operations, e.g. for
12 reliably handling package file collisions due to multiple categories.
13
14 ---
15 roverlay/overlay/pkgdir/ebuildparser.py | 283 ++++++++++++++++++++++++++++++++
16 1 file changed, 283 insertions(+)
17
18 diff --git a/roverlay/overlay/pkgdir/ebuildparser.py b/roverlay/overlay/pkgdir/ebuildparser.py
19 new file mode 100644
20 index 0000000..79e6f15
21 --- /dev/null
22 +++ b/roverlay/overlay/pkgdir/ebuildparser.py
23 @@ -0,0 +1,283 @@
24 +# R overlay -- overlay package, package directory, "minimal" ebuild parser
25 +# -*- coding: utf-8 -*-
26 +# Copyright (C) 2012, 2013 André Erdmann <dywi@×××××××.de>
27 +# Distributed under the terms of the GNU General Public License;
28 +# either version 2 of the License, or (at your option) any later version.
29 +
30 +from __future__ import print_function
31 +
32 +import os
33 +import shlex
34 +import string
35 +
36 +import roverlay.util.objects
37 +import roverlay.strutil
38 +
39 +
40 +STR_FORMATTER = string.Formatter()
41 +VFORMAT = STR_FORMATTER.vformat
42 +
43 +class ParserException ( Exception ):
44 + pass
45 +# --- end of ParserException ---
46 +
47 +
48 +class SrcUriEntry ( object ):
49 +
50 + def __init__ ( self, src_uri, output_file=None ):
51 + super ( SrcUriEntry, self ).__init__()
52 + self.uri = src_uri
53 + if output_file:
54 + self.have_local = True
55 + self.local_file = output_file
56 + else:
57 + self.have_local = False
58 + self.local_file = src_uri.rpartition ( '/' ) [-1]
59 + # --- end of __init__ (...) ---
60 +
61 + def __str__ ( self ):
62 + if self.have_local:
63 + return "{URI} -> {f}".format ( URI=self.uri, f=self.local_file )
64 + else:
65 + return self.uri
66 + # --- end of __str__ (...) ---
67 +
68 +# --- end of SrcUriEntry ---
69 +
70 +
71 +
72 +class EbuildParser ( object ):
73 +
74 + @classmethod
75 + def from_file ( cls, filepath, vartable=None ):
76 + instance = cls ( filepath, vartable=vartable )
77 + instance.read()
78 + return instance
79 + # --- end of from_file (...) ---
80 +
81 + def __init__ ( self, filepath, vartable=None ):
82 + super ( EbuildParser, self ).__init__()
83 + self.filepath = filepath
84 + self.vartable = vartable
85 + # --- end of __init__ (...) ---
86 +
87 + def _read_tokens ( self, breakparse=None ):
88 + with open ( self.filepath, 'rt' ) as FH:
89 + reader = shlex.shlex ( FH )
90 + reader.whitespace_split = False
91 + reader.wordchars += ' ,./$()[]:+-@*~<>'
92 +
93 + token = reader.get_token()
94 + if breakparse is None:
95 + while token:
96 + yield token
97 + token = reader.get_token()
98 + else:
99 + while token and not breakparse ( token ):
100 + yield token
101 + token = reader.get_token()
102 + # --- end of _read_tokens (...) ---
103 +
104 + def _read_variables ( self, do_unquote=True ):
105 + # assumption: no (important) variables after the first function
106 +
107 +
108 + # read all tokens and store them in a list
109 + # this allows to look back/ahead
110 + tokens = list ( self._read_tokens (
111 + breakparse=( lambda s: ( len(s) > 2 and s[-2:] == '()' ) )
112 + ) )
113 +
114 +
115 + varname = None
116 + data = dict()
117 +
118 + last_index = len ( tokens ) - 1
119 + ignore_next = False
120 + is_bash_array = False
121 + # 0 -> no value read, 1-> have value(s), 2 -> reject token
122 + value_mode = 0
123 +
124 + for index, token in enumerate ( tokens ):
125 + if ignore_next:
126 + ignore_next = False
127 + pass
128 +
129 + elif index < last_index and tokens [index+1] == '=':
130 + # lookahead result: token is a varname
131 + ignore_next = True
132 + is_bash_array = False
133 + value_mode = 0
134 + varname = token
135 + data [token] = None
136 +
137 + elif value_mode == 0:
138 + if value_mode == 0 and token == '()':
139 + is_bash_array = True
140 + value_mode = 2
141 + data [varname] = []
142 +
143 + elif value_mode == 0 and token == '(':
144 + is_bash_array = True
145 + value_mode = 1
146 + data [varname] = []
147 +
148 + else:
149 + data [varname] = token
150 + value_mode = 1
151 +
152 + elif value_mode > 1:
153 + pass
154 +
155 + elif is_bash_array:
156 + # impiles value_mode != 0
157 +
158 + if token == ')':
159 + value_mode = 2
160 + else:
161 + data [varname].append ( token )
162 +
163 +# else:
164 +# pass
165 +
166 +
167 + if do_unquote:
168 + return {
169 + varname: (
170 + roverlay.strutil.unquote ( value ) if isinstance ( value, str )
171 + else list (
172 + roverlay.strutil.unquote ( item ) for item in value
173 + )
174 + )
175 + for varname, value in data.items()
176 + }
177 + else:
178 + return data
179 + # --- end of _read_variables (...) ---
180 +
181 + def _get_src_uri_entries ( self, value ):
182 + assert isinstance ( value, str )
183 +
184 + src_uri = None
185 + want_local_file = False
186 +
187 + for s in value.split():
188 + if not s or s[-1] == '?' or s in { '(', ')' }:
189 + pass
190 +
191 + elif want_local_file:
192 + yield SrcUriEntry ( src_uri, s )
193 + want_local_file = False
194 + src_uri = None
195 +
196 + elif s == '->':
197 + if src_uri is None:
198 + raise Exception (
199 + "SRC_URI: arrow operator -> without preceding URI"
200 + )
201 + else:
202 + want_local_file = True
203 +
204 + else:
205 + if src_uri is not None:
206 + yield SrcUriEntry ( src_uri )
207 + src_uri = s
208 +
209 + # -- end for
210 +
211 + if want_local_file:
212 + raise Exception ( "SRC_URI: arrow operator -> without local file" )
213 +
214 + elif src_uri is not None:
215 + yield SrcUriEntry ( src_uri )
216 + # --- end of _get_src_uri_entries (...) ---
217 +
218 + @roverlay.util.objects.abstractmethod
219 + def read ( self ):
220 + pass
221 + # --- end of read (...) ---
222 +
223 +# --- end of EbuildParser ---
224 +
225 +
226 +class SrcUriParser ( EbuildParser ):
227 +
228 + def __init__ ( self, filepath, vartable=None ):
229 + super ( SrcUriParser, self ).__init__ ( filepath, vartable=vartable )
230 + self.src_uri = None
231 + # --- end of __init__ (...) ---
232 +
233 + def iter_entries ( self ):
234 + if self.src_uri:
235 + for entry in self.src_uri:
236 + yield entry
237 + # --- end of _iterate (...) ---
238 +
239 + def iter_local_files ( self, ignore_unparseable=False ):
240 + def convert_chars_with_vars ( text ):
241 + mode = 0
242 + for index, char in enumerate ( text ):
243 +
244 + if mode == 0:
245 + if char == '$':
246 + mode = 1
247 + else:
248 + yield char
249 +
250 + elif mode == 1:
251 + if char == '{':
252 + yield char
253 + mode = 2
254 + else:
255 + raise ParserException (
256 + 'cannot convert variable starting at '
257 + 'position {:d} in {}'.format ( index, text )
258 + )
259 +
260 + elif mode == 2 and char in { '/', }:
261 + raise ParserException (
262 + 'unsupported char {} inside variable at '
263 + 'position {:d} in {}'.format ( char, index, text )
264 + )
265 +
266 + else:
267 + yield char
268 + # --- end of convert_chars_with_vars (...) ---
269 +
270 + varstr = lambda s: VFORMAT (
271 + ''.join ( convert_chars_with_vars ( s ) ), (), self.vartable
272 + )
273 +
274 + if self.src_uri:
275 + for entry in self.src_uri:
276 + local_file = entry.local_file
277 + if '$' in local_file:
278 + # TODO: need vartable (dict(P->,PN->))
279 + #
280 + if ignore_unparseable:
281 + try:
282 + yield varstr ( local_file )
283 + except ParserException:
284 + if not ignore_unparseable:
285 + raise
286 + else:
287 + yield varstr ( local_file )
288 +
289 + else:
290 + yield local_file
291 + # --- end of iter_local_files (...) ---
292 +
293 + def __iter__ ( self ):
294 + return self.iter_entries()
295 + # --- end of __iter__ (...) ---
296 +
297 + def read ( self ):
298 + data = self._read_variables()
299 +
300 + if 'SRC_URI' in data:
301 + self.src_uri = list (
302 + self._get_src_uri_entries ( data ['SRC_URI'] )
303 + )
304 + # --- end of read (...) ---
305 +
306 +# --- end of SrcUriParser ---