1 |
commit: a60a7f76d1a2dd2d5f4d1bb3d9766a2e803f2655 |
2 |
Author: Andre Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Tue May 29 17:03:43 2012 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Tue May 29 17:03:43 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=a60a7f76 |
7 |
|
8 |
roverlay, ebuild creation: Ebuild.py |
9 |
* module that produces ebuild text lines |
10 |
new file: ebuild.py |
11 |
|
12 |
--- |
13 |
roverlay/ebuild.py | 335 ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
14 |
1 files changed, 335 insertions(+), 0 deletions(-) |
15 |
|
16 |
diff --git a/roverlay/ebuild.py b/roverlay/ebuild.py |
17 |
new file mode 100644 |
18 |
index 0000000..495b189 |
19 |
--- /dev/null |
20 |
+++ b/roverlay/ebuild.py |
21 |
@@ -0,0 +1,335 @@ |
22 |
+# R Overlay -- ebuild creation, ebuild class |
23 |
+# Copyright 2006-2012 Gentoo Foundation |
24 |
+# Distributed under the terms of the GNU General Public License v2 |
25 |
+ |
26 |
+class Ebuild: |
27 |
+ # could move this to const |
28 |
+ EBUILD_INDENT = "\t" |
29 |
+ |
30 |
+ @classmethod |
31 |
+ def __init__ ( self ): |
32 |
+ """Initializes an Ebuild. |
33 |
+ This is an abstraction layer between the verified + calculated data |
34 |
+ and the ebuild data, which can be written into a file / stdout / stdin. |
35 |
+ Most functions here assume that everything is fine when it reaches them. |
36 |
+ """ |
37 |
+ |
38 |
+ # elements in ebuild_data are either a str or a list of str |
39 |
+ self._data = dict () |
40 |
+ self._ebuild_lines = None |
41 |
+ |
42 |
+ # --- end of __init__ (...) --- |
43 |
+ |
44 |
+ @classmethod |
45 |
+ def cleanup ( self ): |
46 |
+ """Removes stored data if ebuild_lines have already been calculated. |
47 |
+ This saves some memory but makes this Ebuild read-only. |
48 |
+ """ |
49 |
+ if self._ebuild_lines: |
50 |
+ del self._data |
51 |
+ self._data = None |
52 |
+ |
53 |
+ # --- end of cleanup (...) --- |
54 |
+ |
55 |
+ @classmethod |
56 |
+ def prepare ( self, force_update=False, cleanup_after=False ): |
57 |
+ """Tells this Ebuild to create ebuild lines. |
58 |
+ |
59 |
+ arguments: |
60 |
+ * force_update -- create ebuild lines if they exist; defaults to False |
61 |
+ and ignored if this Ebuild has been cleaned up |
62 |
+ * cleanup_after -- run cleanup() after successful creation |
63 |
+ |
64 |
+ Returns True if ebuild_lines have been created or if they exist and |
65 |
+ an update has not been enforced. Else returns False. |
66 |
+ """ |
67 |
+ if self._ebuild_lines and not force_update: |
68 |
+ return True |
69 |
+ elif self._data: |
70 |
+ self._ebuild_lines = self._make_ebuild_lines() |
71 |
+ if self._ebuild_lines: |
72 |
+ if cleanup_after: self.cleanup() |
73 |
+ return True |
74 |
+ elif self._ebuild_lines: |
75 |
+ # self._data is None |
76 |
+ return True |
77 |
+ |
78 |
+ |
79 |
+ return False |
80 |
+ |
81 |
+ # --- end of prepare (...) --- |
82 |
+ |
83 |
+ @classmethod |
84 |
+ def add ( self, key, value, append=True ): |
85 |
+ """Adds data to this Ebuild. |
86 |
+ |
87 |
+ arguments: |
88 |
+ * key -- identifier of the data (e.g. DEPEND). |
89 |
+ May be remapped here (e.g. merging 'Title' and 'Description') |
90 |
+ * value -- |
91 |
+ * append -- whether to append values or overwrite existing ones, |
92 |
+ defaults to True. |
93 |
+ |
94 |
+ raises: Exception when ebuild data are readonly |
95 |
+ """ |
96 |
+ if self._data is None: |
97 |
+ # -- todo |
98 |
+ raise Exception ("Ebuild data are readonly.") |
99 |
+ |
100 |
+ if append and key in self._data: |
101 |
+ if isinstance ( self._data [key], list ): |
102 |
+ self._data [key].extend ( value ) |
103 |
+ else: |
104 |
+ self._data [key] = [ self._data [key] ].extend ( value ) |
105 |
+ |
106 |
+ else: |
107 |
+ self._data [key] = value |
108 |
+ |
109 |
+ # --- end of add (...) --- |
110 |
+ |
111 |
+ @classmethod |
112 |
+ def write ( self, file_to_write ): |
113 |
+ """Writes an ebuild file. |
114 |
+ |
115 |
+ arguments: |
116 |
+ * file_to_write -- path to the file that should be written |
117 |
+ """ |
118 |
+ # prepare ebuild lines and open file handle after that |
119 |
+ if self.prepare ( False, False ): |
120 |
+ try: |
121 |
+ fh = open ( file_to_write, 'w' ) |
122 |
+ self.show ( fh ) |
123 |
+ fh.close() |
124 |
+ del fh |
125 |
+ return True |
126 |
+ except IOError as err: |
127 |
+ # ? todo |
128 |
+ raise |
129 |
+ |
130 |
+ else: |
131 |
+ # todo log this |
132 |
+ raise Exception ("cannot write ebuild") |
133 |
+ |
134 |
+ # --- end of write (...) --- |
135 |
+ |
136 |
+ @classmethod |
137 |
+ def show ( self, file_handle ): |
138 |
+ """Prints the ebuild content into a file_handle. |
139 |
+ |
140 |
+ arguments: |
141 |
+ file_handle -- object that has a writelines ( list ) method, e.g. file. |
142 |
+ |
143 |
+ Returns True if writing was successful, else False. |
144 |
+ """ |
145 |
+ if self.prepare ( False, False ): |
146 |
+ lines = [ line + "\n" for line in self._ebuild_lines ] |
147 |
+ file_handle.writelines ( lines ) |
148 |
+ del lines |
149 |
+ return True |
150 |
+ else: |
151 |
+ return False |
152 |
+ |
153 |
+ # --- end of show (...) --- |
154 |
+ |
155 |
+ def suggest_name ( self, fallback_name=None ): |
156 |
+ """Suggests a file name for the ebuild. This is calculated using |
157 |
+ pkg_name/version/revision. Returns a fallback_name if this is not |
158 |
+ possible. |
159 |
+ |
160 |
+ arguments: |
161 |
+ fallback_name -- name to return if no suggestion available, defaults to None |
162 |
+ """ |
163 |
+ |
164 |
+ if 'pkg_name' in self._data and 'pkg_version' in self._data: |
165 |
+ join = [ 'pkg_name' , 'pkg_version' ] |
166 |
+ if 'pkg_revision' in self._data: join.append ('pkg_revision') |
167 |
+ |
168 |
+ return '-' . join ( [ self._data [c] for c in join ] ) |
169 |
+ |
170 |
+ else: |
171 |
+ return fallback_name |
172 |
+ |
173 |
+ # --- end of suggest_name (...) --- |
174 |
+ |
175 |
+ @classmethod |
176 |
+ def _make_ebuild_lines ( self ): |
177 |
+ """Creates text lines for this Ebuild. |
178 |
+ It assumes that enough data to do this are available. Exceptions (KeyError, NameError, ...) |
179 |
+ are passed if that's not the case. |
180 |
+ """ |
181 |
+ |
182 |
+ def get_dep_and_use(): |
183 |
+ """Creates values for the DEPEND, RDEPEND, IUSE and, if possible, |
184 |
+ R_SUGGESTS variables and returns them as dict { VARNAME -> VALUE }. |
185 |
+ """ |
186 |
+ |
187 |
+ # have suggests if they're set and not empty |
188 |
+ have_suggests = bool ( 'RSUGGESTS' in self._data and self._data ['RSUGGESTS'] ) |
189 |
+ |
190 |
+ # set defaults: inherit eclass + include depend in rdepend |
191 |
+ ret = dict ( |
192 |
+ DEPEND = [ '${DEPEND:-}' ], |
193 |
+ RDEPEND = [ '${DEPEND:-}', '${RDEPEND:-}' ], |
194 |
+ IUSE = [ '${IUSE:-}' ], |
195 |
+ ) |
196 |
+ |
197 |
+ if 'DEPEND' in self._data: |
198 |
+ ret ['DEPEND'].extend ( self._data ['DEPEND'] ) |
199 |
+ |
200 |
+ if 'RDEPEND' in self._data: |
201 |
+ ret ['RDEPEND'].extend ( self._data ['RDEPEND'] ) |
202 |
+ |
203 |
+ if have_suggests: |
204 |
+ ret ['R_SUGGESTS'] = self._data ['R_SUGGESTS'] |
205 |
+ |
206 |
+ # +R_SUGGESTS, -R_SUGGESTS? |
207 |
+ ret ['IUSE'].append ( 'R_suggests' ) |
208 |
+ # do these braces help or confuse? TODO FIXME |
209 |
+ ret ['RDEPEND'].append ( '( R_suggests ? ${R_SUGGESTS} )' ) |
210 |
+ |
211 |
+ return ret |
212 |
+ |
213 |
+ # --- end of get_dep_and_use () --- |
214 |
+ |
215 |
+ def make_var ( varname, value=None, oneline_list=True, indent_list=True, indent_level=0 ): |
216 |
+ """Creates a <name>=<value> statement for ebuilds. |
217 |
+ |
218 |
+ arguments: |
219 |
+ * varname -- name of the variable |
220 |
+ * value -- value of the variable. This has to be either None (the default), |
221 |
+ str, or list of str. |
222 |
+ * oneline_list -- if value is a list: controls whether it's components should be |
223 |
+ put into one line (True) or multiple (False). Defaults to True. |
224 |
+ * indent_list -- if value is a list and not oneline_list: |
225 |
+ controls whether each value line should be indentend |
226 |
+ (by indent_level + 1) or not ("by 0"). Defaults to True. |
227 |
+ * indent_level -- current indentation level, defaults to 0 |
228 |
+ |
229 |
+ """ |
230 |
+ |
231 |
+ # assumption: value is either None, scalar with str representation or list of str |
232 |
+ var_value = None |
233 |
+ |
234 |
+ if not value: |
235 |
+ var_value = "" |
236 |
+ |
237 |
+ elif isinstance ( value, list ): |
238 |
+ if oneline_list: |
239 |
+ var_value = ' '.join ( value ) |
240 |
+ elif indent_list: |
241 |
+ var_value = ('\n' + (indent_level + 1) * EBUILD_INDENT).join ( value ) |
242 |
+ else: |
243 |
+ '\n'.join ( value ) |
244 |
+ |
245 |
+ else: |
246 |
+ var_value = str ( value ) |
247 |
+ |
248 |
+ return indent_level * EBUILD_INDENT + varname + '"' + value_str + '"' |
249 |
+ |
250 |
+ # --- end of make_var (...) --- |
251 |
+ |
252 |
+ def remove_newlines ( line_list ): |
253 |
+ """ |
254 |
+ Removes leading, ending and repeated empty/whitespace lines in line_list. |
255 |
+ |
256 |
+ arguments: |
257 |
+ * line_list |
258 |
+ """ |
259 |
+ lines = [] |
260 |
+ line = None |
261 |
+ last_line_empty = False |
262 |
+ |
263 |
+ for line in line_list: |
264 |
+ line = line.rstrip() |
265 |
+ # re.sub \n{2,} \n :: FIXME? |
266 |
+ |
267 |
+ if line: |
268 |
+ last_line_empty = False |
269 |
+ elif not last_line_empty: |
270 |
+ last_line_empty = True |
271 |
+ else: |
272 |
+ continue |
273 |
+ |
274 |
+ lines.append ( line ) |
275 |
+ |
276 |
+ # remove last line if empty |
277 |
+ ##if last_line_empty: (?) |
278 |
+ if len ( lines ) and not lines [-1]: |
279 |
+ del lines [-1] |
280 |
+ |
281 |
+ return lines |
282 |
+ |
283 |
+ # --- end of remove_newlines (...) --- |
284 |
+ |
285 |
+ def add_easyvar ( ebuild_content, varname, value_key=None, add_newline=False): |
286 |
+ """Adds a 'simple' variable to the ebuild lines. This means that it |
287 |
+ can directly be taken from self._data [value_key]. This method assumes |
288 |
+ that value_key exists in self._data, any exceptions (KeyError) will be passed. |
289 |
+ |
290 |
+ arguments: |
291 |
+ * ebuild_content -- list of ebuild text lines, will be modified directly, |
292 |
+ so copy it before calling addvar if you need the original list. |
293 |
+ * varname -- name of the variable. Nothing happens if this is None. |
294 |
+ * value_key -- key of the value, defaults to varname if it is None |
295 |
+ * add_newline -- adds a newline after the var statement, defaults to False |
296 |
+ |
297 |
+ Returns given list (ebuild_content), which will then have been modified. |
298 |
+ """ |
299 |
+ |
300 |
+ if not varname is None: |
301 |
+ if value_key is None: |
302 |
+ ebuild_content.append ( make_var ( varname, self._data [varname] ) ) |
303 |
+ else: |
304 |
+ ebuild_content.append ( make_var ( varname, self._data [value_key] ) ) |
305 |
+ |
306 |
+ if add_newline: |
307 |
+ ebuild_content.append ( "" ) |
308 |
+ |
309 |
+ return ebuild_content |
310 |
+ |
311 |
+ # --- end of add_easyvar (...) --- |
312 |
+ |
313 |
+ try: |
314 |
+ ebuild_lines = [] |
315 |
+ |
316 |
+ if 'ebuild_header' in self._data: |
317 |
+ ebuild_lines = self._data ['ebuild_header'] |
318 |
+ ebuild_lines.append ( "" ) |
319 |
+ |
320 |
+ add_easyvar ( ebuild_lines, "PKG_FILE" ) |
321 |
+ if 'PKG_ORIGIN' in self._data: |
322 |
+ add_easyvar ( ebuild_lines, "PKG_ORIGIN", None, True ) |
323 |
+ |
324 |
+ add_easyvar ( ebuild_lines, "DESCRIPTION" ) |
325 |
+ |
326 |
+ if 'SRC_URI' in self._data: |
327 |
+ add_easyvar ( ebuild_lines, "SRC_URI" ) |
328 |
+ else: |
329 |
+ # > calculate SRC_URI using self._data ['origin'] |
330 |
+ ebuild_lines.append ( make_var ( "SRC_URI" , None ) ) |
331 |
+ # (temporary, todo) setting restrict to fetch |
332 |
+ ebuild_lines.append ( make_var ( "RESTRICT" , "fetch" ) ) |
333 |
+ |
334 |
+ ebuild_lines.append ( "" ) |
335 |
+ |
336 |
+ # LICENSE ? |
337 |
+ |
338 |
+ dep_and_use = get_dep_and_use () |
339 |
+ |
340 |
+ ebuild_lines.append ( make_var ( "IUSE", dep_and_use ['IUSE'], True ) ) |
341 |
+ |
342 |
+ if 'R_SUGGESTS' in dep_and_use: |
343 |
+ ebuild_lines.append ( make_var ( "R_SUGGESTS", dep_and_use ['R_SUGGESTS'], False ) ) |
344 |
+ |
345 |
+ ebuild_lines.append ( make_var ( "DEPEND", dep_and_use ['DEPEND'], False ) ) |
346 |
+ ebuild_lines.append ( make_var ( "RDEPEND", dep_and_use ['RDEPEND'], False ) ) |
347 |
+ |
348 |
+ del dep_and_use |
349 |
+ return remove_newlines ( ebuild_lines ) |
350 |
+ |
351 |
+ except Exception as err: |
352 |
+ # log this |
353 |
+ ## .. |
354 |
+ raise |
355 |
+ |
356 |
+ # --- end of make_ebuild_lines (...) --- |