1 |
commit: 548e6ceede8e8ba8d75492656656275f13b20613 |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Thu Jul 19 16:53:08 2012 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Thu Jul 19 16:53:28 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=548e6cee |
7 |
|
8 |
incremental overlay writing |
9 |
|
10 |
prepare for OverlayCreator |
11 |
|
12 |
geändert: roverlay/overlay/category.py |
13 |
geändert: roverlay/overlay/creator.py |
14 |
geändert: roverlay/overlay/metadata/__init__.py |
15 |
geändert: roverlay/overlay/metadata/nodes.py |
16 |
geändert: roverlay/overlay/package.py |
17 |
geändert: roverlay/overlay/root.py |
18 |
|
19 |
--- |
20 |
roverlay/overlay/category.py | 24 +- |
21 |
roverlay/overlay/creator.py | 11 +- |
22 |
roverlay/overlay/metadata/__init__.py | 84 ++++-- |
23 |
roverlay/overlay/metadata/nodes.py | 40 +-- |
24 |
roverlay/overlay/package.py | 535 +++++++++++++++------------------ |
25 |
roverlay/overlay/root.py | 23 +- |
26 |
6 files changed, 354 insertions(+), 363 deletions(-) |
27 |
|
28 |
diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py |
29 |
index afa2456..9cf8ad9 100644 |
30 |
--- a/roverlay/overlay/category.py |
31 |
+++ b/roverlay/overlay/category.py |
32 |
@@ -68,6 +68,13 @@ class Category ( object ): |
33 |
not False in ( d.empty() for d in self._subdirs.values() ) |
34 |
# --- end of empty (...) --- |
35 |
|
36 |
+ def finalize_write_incremental ( self ): |
37 |
+ for subdir in self._subdirs.values(): |
38 |
+ if subdir.modified: |
39 |
+ subdir.write_incremental() |
40 |
+ subdir.finalize_write_incremental() |
41 |
+ # --- end of finalize_write_incremental (...) --- |
42 |
+ |
43 |
def _get_package_dir ( self, pkg_name ): |
44 |
if not pkg_name in self._subdirs: |
45 |
self._lock.acquire() |
46 |
@@ -84,7 +91,7 @@ class Category ( object ): |
47 |
return self._subdirs [pkg_name] |
48 |
# --- end of _get_package_dir (...) --- |
49 |
|
50 |
- def add ( self, package_info, write_after_add=False, header=None ): |
51 |
+ def add ( self, package_info, **pkg_add_kw ): |
52 |
"""Adds a package to this category. |
53 |
|
54 |
arguments: |
55 |
@@ -93,13 +100,7 @@ class Category ( object ): |
56 |
returns: success |
57 |
""" |
58 |
subdir = self._get_package_dir ( package_info ['name'] ) |
59 |
- if subdir.add ( package_info ): |
60 |
- if write_after_add: |
61 |
- roverlay.util.dodir ( subdir.physical_location ) |
62 |
- subdir.write_incremental ( default_header=header ) |
63 |
- return True |
64 |
- else: |
65 |
- return False |
66 |
+ return subdir.add ( package_info, **pkg_add_kw ) |
67 |
# --- end of add (...) --- |
68 |
|
69 |
def generate_metadata ( self, **metadata_kw ): |
70 |
@@ -164,9 +165,10 @@ class Category ( object ): |
71 |
# so collect the list of package dirs before iterating over it |
72 |
subdirs = tuple ( self._subdirs.values() ) |
73 |
|
74 |
- for package in subdirs: |
75 |
- roverlay.util.dodir ( package.physical_location ) |
76 |
- package.write_incremental ( **write_kw ) |
77 |
+ for subdir in subdirs: |
78 |
+ if subdir.modified: |
79 |
+ roverlay.util.dodir ( subdir.physical_location ) |
80 |
+ subdir.write_incremental ( **write_kw ) |
81 |
except Exception as e: |
82 |
self.logger.exception ( e ) |
83 |
# --- end of write_incremental (...) --- |
84 |
|
85 |
diff --git a/roverlay/overlay/creator.py b/roverlay/overlay/creator.py |
86 |
index 3a6e0ed..dd7c11e 100644 |
87 |
--- a/roverlay/overlay/creator.py |
88 |
+++ b/roverlay/overlay/creator.py |
89 |
@@ -214,15 +214,17 @@ class OverlayCreator ( object ): |
90 |
|
91 |
def add_package_file ( self, package_file ): |
92 |
"""Adds a single R package.""" |
93 |
+ raise Exception ( "to be removed" ) |
94 |
self._pkg_queue.put ( PackageInfo ( filepath=package_file ) ) |
95 |
self.package_added.inc() |
96 |
- # --- end of add_package (...) --- |
97 |
+ # --- end of add_package_file (...) --- |
98 |
|
99 |
def add_package_files ( self, *package_files ): |
100 |
"""Adds multiple R packages.""" |
101 |
+ raise Exception ( "to be removed" ) |
102 |
for p in package_files: self.add_package_file ( p ) |
103 |
self.package_added.inc() |
104 |
- # --- end of add_packages (...) --- |
105 |
+ # --- end of add_package_files (...) --- |
106 |
|
107 |
def write_overlay ( self, incremental=False ): |
108 |
"""Writes the overlay. |
109 |
@@ -232,7 +234,10 @@ class OverlayCreator ( object ): |
110 |
""" |
111 |
if self.can_write_overlay: |
112 |
start = time.time() |
113 |
- self.overlay.write() |
114 |
+ if incremental: |
115 |
+ self.overlay.write_incremental() |
116 |
+ else: |
117 |
+ self.overlay.write() |
118 |
self._timestamp ( "overlay written", start ) |
119 |
else: |
120 |
self.logger.warning ( "Not allowed to write overlay!" ) |
121 |
|
122 |
diff --git a/roverlay/overlay/metadata/__init__.py b/roverlay/overlay/metadata/__init__.py |
123 |
index e4938b5..58fc414 100644 |
124 |
--- a/roverlay/overlay/metadata/__init__.py |
125 |
+++ b/roverlay/overlay/metadata/__init__.py |
126 |
@@ -11,37 +11,53 @@ USE_FULL_DESCRIPTION = True |
127 |
class MetadataJob ( object ): |
128 |
"""R package description data -> metadata.xml interface.""" |
129 |
|
130 |
- def __init__ ( self, logger ): |
131 |
+ def __init__ ( self, filepath, logger ): |
132 |
"""Initializes a MetadataJob. |
133 |
|
134 |
arguments: |
135 |
- * logger -- parent logger to use |
136 |
+ * filepath -- path where the metadata file will be written to |
137 |
+ * logger -- parent logger to use |
138 |
""" |
139 |
- self.logger = logger.getChild ( 'metadata' ) |
140 |
- self._metadata = nodes.MetadataRoot() |
141 |
- # reserved for future usage ("dominant ebuilds": when ebuildjobs |
142 |
- # share one metadata instance etc.) |
143 |
- self.package_info = None |
144 |
- self.filename = 'metadata.xml' |
145 |
+ self.logger = logger.getChild ( 'metadata' ) |
146 |
+ self._package_info = None |
147 |
+ self.filepath = filepath |
148 |
+ # no longer storing self._metadata, which will only be created twice |
149 |
+ # when running show() (expected 1x write per PackageInfo instance) |
150 |
# --- end of __init__ (...) --- |
151 |
|
152 |
- def update ( self, package_info ): |
153 |
- """Updates the metadata using the given description data. |
154 |
+ def empty ( self ): |
155 |
+ return self._package_info is None |
156 |
+ # --- end of empty (...) --- |
157 |
|
158 |
- It's expected that this method is called when Ebuild creation is done. |
159 |
+ def update ( self, package_info ): |
160 |
+ """Updates the metadata. |
161 |
+ Actually, this won't create any metadata, it will only set the |
162 |
+ PackageInfo object to be used for metadata creation. |
163 |
|
164 |
arguments: |
165 |
- * desc_data -- description data read from R package |
166 |
- * package_info -- reserved for future usage |
167 |
- |
168 |
- returns: None (implicit) |
169 |
+ * package_info -- |
170 |
""" |
171 |
- data = package_info ['desc_data'] |
172 |
+ if package_info.has ( 'desc_data' ) and \ |
173 |
+ package_info.compare_version ( self._package_info ) > 0: |
174 |
+ self._package_info = package_info |
175 |
+ # --- end of update (...) --- |
176 |
|
177 |
- mref = self._metadata |
178 |
+ def update_using_iterable ( self, package_info_iter ): |
179 |
+ for package_info in package_info_iter: |
180 |
+ self.update ( package_info ) |
181 |
+ # --- end of update_using_iterable (...) --- |
182 |
|
183 |
- max_textline_width = roverlay.config.get ( 'METADATA.linewidth', 65 ) |
184 |
+ def _create ( self ): |
185 |
+ """Creates metadata (MetadataRoot) using the stored PackageInfo. |
186 |
+ |
187 |
+ It's expected that this method is called when Ebuild creation is done. |
188 |
+ |
189 |
+ returns: created metadata |
190 |
+ """ |
191 |
+ mref = nodes.MetadataRoot() |
192 |
+ data = self._package_info ['desc_data'] |
193 |
|
194 |
+ max_textline_width = roverlay.config.get ( 'METADATA.linewidth', 65 ) |
195 |
|
196 |
description = None |
197 |
|
198 |
@@ -67,22 +83,38 @@ class MetadataJob ( object ): |
199 |
#if package_info ['has_suggests']: |
200 |
# mref.add_useflag ( 'R_suggests', 'install optional dependencies' ) |
201 |
|
202 |
+ return mref |
203 |
# --- end of update (...) --- |
204 |
|
205 |
- def write ( self, _file ): |
206 |
+ def _write ( self, fh ): |
207 |
"""Writes the metadata into a file. |
208 |
|
209 |
arguments: |
210 |
* _file -- file to write, either a file handle or string in which case |
211 |
a file object will be created |
212 |
|
213 |
- returns: True if writing succeeds, else False |
214 |
+ returns: True if writing succeeds, else False |
215 |
|
216 |
- raises: Exception if no metadata to write |
217 |
- """ |
218 |
- if self._metadata.empty(): |
219 |
- raise Exception ( "not enough metadata to write!" ) |
220 |
- #return False |
221 |
+ raises: Exception if no metadata to write |
222 |
+ """ |
223 |
+ if self._create().write_file ( fh ): |
224 |
+ return True |
225 |
else: |
226 |
- return self._metadata.write_file ( _file ) |
227 |
+ raise Exception ( "not enough metadata to write!" ) |
228 |
+ # --- end of _write (...) --- |
229 |
+ |
230 |
+ show = _write |
231 |
+ |
232 |
+ def write ( self ): |
233 |
+ _success = False |
234 |
+ try: |
235 |
+ fh = open ( self.filepath, 'w' ) |
236 |
+ self._write ( fh ) |
237 |
+ _success = True |
238 |
+ except any as e: |
239 |
+ self.logger.exception ( e ) |
240 |
+ finally: |
241 |
+ if 'fh' in locals() and fh: fh.close() |
242 |
+ |
243 |
+ return _success |
244 |
# --- end of write (...) --- |
245 |
|
246 |
diff --git a/roverlay/overlay/metadata/nodes.py b/roverlay/overlay/metadata/nodes.py |
247 |
index 6f27deb..02d5482 100644 |
248 |
--- a/roverlay/overlay/metadata/nodes.py |
249 |
+++ b/roverlay/overlay/metadata/nodes.py |
250 |
@@ -57,46 +57,24 @@ class MetadataRoot ( MetadataNodeNamedAccess ): |
251 |
return use_node |
252 |
# --- end of add_useflag (...) --- |
253 |
|
254 |
- def write_file ( self, _file ): |
255 |
+ def write_file ( self, fh ): |
256 |
"""Writes the metadata to a file. |
257 |
|
258 |
arguments: |
259 |
- * _file -- either a File object or a string |
260 |
+ * fh -- a File object |
261 |
|
262 |
returns: success True/False |
263 |
|
264 |
raises: *passes IOError |
265 |
""" |
266 |
- to_write = util.ascii_filter ( self.to_str() ) |
267 |
- |
268 |
- own_fh = False |
269 |
- fh = None |
270 |
- success = False |
271 |
- |
272 |
- newline = '\n' |
273 |
- |
274 |
- try: |
275 |
- if isinstance ( _file, str ): |
276 |
- own_fh = True |
277 |
- fh = open ( _file, 'w' ) |
278 |
- else: |
279 |
- fh = _file |
280 |
- |
281 |
- |
282 |
+ if not self.empty(): |
283 |
fh.write ( MetadataRoot.HEADER ) |
284 |
- fh.write ( newline ) |
285 |
- fh.write ( to_write ) |
286 |
- fh.write ( newline ) |
287 |
- |
288 |
- success = True |
289 |
- |
290 |
- except IOError: |
291 |
- # log this TODO |
292 |
- pass |
293 |
- finally: |
294 |
- if own_fh and fh: fh.close() |
295 |
- |
296 |
- return success |
297 |
+ fh.write ( '\n' ) |
298 |
+ fh.write ( util.ascii_filter ( self.to_str() ) ) |
299 |
+ fh.write ( '\n' ) |
300 |
+ return True |
301 |
+ else: |
302 |
+ return False |
303 |
# --- end of write_file (...) --- |
304 |
|
305 |
|
306 |
|
307 |
diff --git a/roverlay/overlay/package.py b/roverlay/overlay/package.py |
308 |
index c0b4294..71b7ea9 100644 |
309 |
--- a/roverlay/overlay/package.py |
310 |
+++ b/roverlay/overlay/package.py |
311 |
@@ -1,20 +1,18 @@ |
312 |
-# R Overlay -- overlay module, package dir (subdir of category) |
313 |
-# Copyright 2006-2012 Gentoo Foundation |
314 |
-# Distributed under the terms of the GNU General Public License v2 |
315 |
+# replacement for package.py |
316 |
|
317 |
-import threading |
318 |
import os |
319 |
import sys |
320 |
+import threading |
321 |
|
322 |
from roverlay import manifest |
323 |
from roverlay.packageinfo import PackageInfo |
324 |
from roverlay.overlay.metadata import MetadataJob |
325 |
|
326 |
SUPPRESS_EXCEPTIONS = True |
327 |
-EBUILD_SUFFIX = '.ebuild' |
328 |
|
329 |
-class PackageDir ( object ): |
330 |
|
331 |
+class PackageDir ( object ): |
332 |
+ EBUILD_SUFFIX = '.ebuild' |
333 |
|
334 |
def __init__ ( self, name, logger, directory ): |
335 |
"""Initializes a PackageDir which contains ebuilds, metadata and |
336 |
@@ -30,47 +28,131 @@ class PackageDir ( object ): |
337 |
self._lock = threading.RLock() |
338 |
# { <version> : <PackageInfo> } |
339 |
self._packages = dict() |
340 |
- self._metadata = None |
341 |
self.physical_location = directory |
342 |
|
343 |
- self._package_for_manifest = None |
344 |
+ self._metadata = MetadataJob ( |
345 |
+ filepath = self.physical_location + os.sep + 'metadata.xml', |
346 |
+ logger = self.logger |
347 |
+ ) |
348 |
+ |
349 |
+ # <dir>/<PN>-<PVR>.ebuild |
350 |
+ self.ebuild_filepath_format = \ |
351 |
+ self.physical_location + os.sep + \ |
352 |
+ self.name + "-{PVR}" + self.__class__.EBUILD_SUFFIX |
353 |
|
354 |
# used to track changes for this package dir |
355 |
- self.modified = False |
356 |
+ self.modified = False |
357 |
+ self._manifest_package = None |
358 |
+ self._need_manifest = False |
359 |
+ self._need_metadata = False |
360 |
# --- end of __init__ (...) --- |
361 |
|
362 |
- def list_versions ( self ): |
363 |
- return self._packages.keys() |
364 |
- # --- end of list_versions (...) --- |
365 |
+ def new_ebuild ( self ): |
366 |
+ """Called when a new ebuild has been created for this PackageDir.""" |
367 |
+ self._need_manifest = True |
368 |
+ self.modified = True |
369 |
+ # --- end of new_ebuild (...) --- |
370 |
|
371 |
- def has_manifest ( self ): |
372 |
- return os.path.isfile ( |
373 |
- self.physical_location + os.sep + 'Manifest' |
374 |
- ) |
375 |
- # --- end of has_manifest (...) --- |
376 |
+ def add ( self, package_info, add_if_physical=False ): |
377 |
+ """Adds a package to this PackageDir. |
378 |
|
379 |
- def has_metadata ( self ): |
380 |
- return os.path.isfile ( |
381 |
- self.physical_location + os.sep + 'metadata.xml' |
382 |
- ) |
383 |
- # --- end of has_metadata (...) --- |
384 |
+ arguments: |
385 |
+ * package_info -- |
386 |
+ * add_if_physical -- add package even if it exists as ebuild file |
387 |
+ (-> overwrite old ebuilds) |
388 |
+ |
389 |
+ returns: success as bool |
390 |
+ |
391 |
+ raises: Exception when ebuild already exists. |
392 |
+ """ |
393 |
+ shortver = package_info ['ebuild_verstr'] |
394 |
+ _success = False |
395 |
+ try: |
396 |
+ self._lock.acquire() |
397 |
+ if shortver in self._packages: |
398 |
+ # package exists, check if it existed before script invocation |
399 |
+ if self._packages [shortver] ['physical_only']: |
400 |
+ if not skip_if_physical: |
401 |
+ # ignore ebuilds that exist as file |
402 |
+ self._packages [shortver] = package_info |
403 |
+ _success = True |
404 |
+ else: |
405 |
+ self.logger.debug ( |
406 |
+ "'{PN}-{PVR}.ebuild' exists as file, skipping.".format ( |
407 |
+ PN=self.name, PVR=shortver |
408 |
+ ) |
409 |
+ ) |
410 |
+ else: |
411 |
+ # package has been added to this overlay before |
412 |
+ self.logger.info ( |
413 |
+ "'{PN}-{PVR}.ebuild' already exists, cannot add it!".format ( |
414 |
+ PN=self.name, PVR=shortver |
415 |
+ ) |
416 |
+ ) |
417 |
+ else: |
418 |
+ self._packages [shortver] = package_info |
419 |
+ _success = True |
420 |
+ |
421 |
+ finally: |
422 |
+ self._lock.release() |
423 |
+ |
424 |
+ return _success |
425 |
+ # --- end of add (...) --- |
426 |
+ |
427 |
+ def empty ( self ): |
428 |
+ """Returns True if no ebuilds stored, else False. |
429 |
+ Note that "not empty" does not mean "have ebuilds to write". |
430 |
+ """ |
431 |
+ return len ( self._packages ) == 0 |
432 |
+ # --- end of empty (...) --- |
433 |
+ |
434 |
+ def finalize_write_incremental ( self ): |
435 |
+ with self._lock: |
436 |
+ if self._need_metadata: |
437 |
+ self.write_metadata() |
438 |
+ if self._need_manifest: |
439 |
+ self.write_manifest() |
440 |
+ # --- end of finalize_write_incremental (...) --- |
441 |
+ |
442 |
+ def generate_metadata ( self, skip_if_existent, **ignored_kw ): |
443 |
+ """Creates metadata for this package. |
444 |
+ |
445 |
+ arguments: |
446 |
+ * skip_if_existent -- do not create if metadata already exist |
447 |
+ """ |
448 |
+ with self._lock: |
449 |
+ if self._metadata.empty() or not skip_if_existent: |
450 |
+ self._metadata.update_using_iterable ( self._packages.values() ) |
451 |
+ # --- end of generate_metadata (...) --- |
452 |
|
453 |
- def get_ebuilds ( self ): |
454 |
- for x in os.listdir ( self.physical_location ): |
455 |
- if x.endswith ( EBUILD_SUFFIX ): |
456 |
- yield self.physical_location + os.sep + x |
457 |
- # --- end of get_ebuilds (...) --- |
458 |
+ def list_versions ( self ): |
459 |
+ return self._packages.keys() |
460 |
+ # --- end of list_versions (...) --- |
461 |
|
462 |
- def _scan_ebuilds ( self ): |
463 |
- """Searches for ebuilds in self.physical_location.""" |
464 |
- elen = len ( EBUILD_SUFFIX ) |
465 |
+ def scan ( self, **kw ): |
466 |
+ """Scans the filesystem location of this package for existing |
467 |
+ ebuilds and adds them. |
468 |
+ """ |
469 |
+ def scan_ebuilds(): |
470 |
+ """Searches for ebuilds in self.physical_location.""" |
471 |
+ elen = len ( self.__class__.EBUILD_SUFFIX ) |
472 |
+ def ebuild_split_pvr ( _file ): |
473 |
+ if _file.endswith ( self.__class__.EBUILD_SUFFIX ): |
474 |
+ return _file [ : - elen ].split ( '-', 1 ) |
475 |
+ else: |
476 |
+ return ( None, None ) |
477 |
+ # --- end of is_ebuild (...) --- |
478 |
|
479 |
- for f in os.listdir ( self.physical_location ): |
480 |
- if f.endswith ( EBUILD_SUFFIX ): |
481 |
+ # assuming that self.physical_location exists |
482 |
+ # (should be verified by category.py:Category) |
483 |
+ for f in os.listdir ( self.physical_location ): |
484 |
try: |
485 |
# filename without suffix ~= ${PF} := ${PN}-${PVR} |
486 |
- pn, pvr = f [ : - elen ].split ( '-', 1 ) |
487 |
- if pn == self.name: |
488 |
+ pn, pvr = ebuild_split_pvr ( f ) |
489 |
+ if pn is None: |
490 |
+ # not an ebuild |
491 |
+ pass |
492 |
+ elif pn == self.name: |
493 |
yield pvr |
494 |
else: |
495 |
# $PN does not match directory name, warn about that |
496 |
@@ -78,298 +160,161 @@ class PackageDir ( object ): |
497 |
"$PN does not match directory name, ignoring {!r}.".\ |
498 |
format ( f ) |
499 |
) |
500 |
- |
501 |
except: |
502 |
self.logger.warning ( |
503 |
"ebuild {!r} has an invalid file name!".format ( f ) |
504 |
) |
505 |
+ # --- end of scan_ebuilds (...) --- |
506 |
|
507 |
- # --- end of _scan_ebuilds (...) --- |
508 |
- |
509 |
- def scan ( self, **kw ): |
510 |
- """Scans the filesystem location of this package for existing |
511 |
- ebuilds and adds them. |
512 |
- """ |
513 |
- for pvr in self._scan_ebuilds(): |
514 |
+ for pvr in scan_ebuilds(): |
515 |
if pvr not in self._packages: |
516 |
p = PackageInfo ( physical_only=True, pvr=pvr ) |
517 |
self._packages [ p ['ebuild_verstr'] ] = p |
518 |
# --- end of scan (...) --- |
519 |
|
520 |
- def empty ( self ): |
521 |
- """Returns True if no ebuilds stored, else False.""" |
522 |
- return len ( self._packages ) == 0 |
523 |
- # --- end of empty (...) --- |
524 |
- |
525 |
- def _get_ebuild_filepath ( self, pvr ): |
526 |
- """Returns the path to the ebuild file. |
527 |
- |
528 |
- arguments: |
529 |
- * pvr -- version number with the revision (${PVR} in ebuilds) |
530 |
- """ |
531 |
- return "{root}{sep}{PN}-{PVR}{EBUILD_SUFFIX}".format ( |
532 |
- root=self.physical_location, sep=os.sep, |
533 |
- PN=self.name, PVR=pvr, EBUILD_SUFFIX=EBUILD_SUFFIX |
534 |
- ) |
535 |
- # --- end of _get_ebuild_filepath (...) --- |
536 |
- |
537 |
- def write_incremental ( self, default_header ): |
538 |
- self.write_ebuilds ( header=default_header, overwrite=False ) |
539 |
- # --- end of write_incremental (...) --- |
540 |
- |
541 |
- def write_metadata ( self, shared_fh=None ): |
542 |
- """Writes metadata for this package.""" |
543 |
- try: |
544 |
- |
545 |
- self._regen_metadata() |
546 |
- |
547 |
- if shared_fh is None: |
548 |
- fh = open ( |
549 |
- self.physical_location + os.sep + self._metadata.filename, 'w' |
550 |
- ) |
551 |
- else: |
552 |
- fh = shared_fh |
553 |
- |
554 |
- self._metadata.write ( fh ) |
555 |
- |
556 |
- except IOError as e: |
557 |
- |
558 |
- self.logger.error ( |
559 |
- "Failed to write metadata file {}.".format ( mfile ) |
560 |
- ) |
561 |
- self.logger.exception ( e ) |
562 |
- |
563 |
- finally: |
564 |
- if shared_fh is None and 'fh' in locals() and fh: |
565 |
- fh.close() |
566 |
- # --- end of write_metadata (...) --- |
567 |
- |
568 |
- def write_ebuild ( self, efile, ebuild, header, shared_fh=None ): |
569 |
- """Writes an ebuild. |
570 |
+ def show ( self, stream=sys.stderr, default_header=None ): |
571 |
+ """Prints this dir (the ebuilds and the metadata) into a stream. |
572 |
|
573 |
arguments: |
574 |
- * efile -- file to write |
575 |
- * ebuild -- ebuild object to write (has to have a __str__ method) |
576 |
- * header -- ebuild header to write (^) |
577 |
- * shared_fh -- optional, see write_ebuilds() |
578 |
- """ |
579 |
- _success = False |
580 |
- try: |
581 |
- fh = open ( efile, 'w' ) if shared_fh is None else shared_fh |
582 |
- if header is not None: |
583 |
- fh.write ( str ( header ) ) |
584 |
- fh.write ( '\n\n' ) |
585 |
- fh.write ( str ( ebuild ) ) |
586 |
- fh.write ( '\n' ) |
587 |
- |
588 |
- # adjust owner/perm? TODO |
589 |
- #if shared_fh is None: |
590 |
- # chmod 0644 or 0444 |
591 |
- # chown 250.250 |
592 |
- _success = True |
593 |
- except IOError as e: |
594 |
- self.logger.exception ( e ) |
595 |
- finally: |
596 |
- if shared_fh is None and 'fh' in locals() and fh: |
597 |
- fh.close() |
598 |
- |
599 |
- return _success |
600 |
- # --- end of write_ebuild (...) --- |
601 |
+ * stream -- stream to use, defaults to sys.stderr |
602 |
|
603 |
- def write_ebuilds ( self, header, shared_fh=None, overwrite=True ): |
604 |
- """Writes all ebuilds. |
605 |
+ returns: None (implicit) |
606 |
|
607 |
- arguments: |
608 |
- * header -- ebuild header |
609 |
- * shared_fh -- if set and not None: don't use own file handles (i.e. |
610 |
- write files), write everything into shared_fh |
611 |
+ raises: |
612 |
+ * IOError |
613 |
""" |
614 |
- for ver, p_info in self._packages.items(): |
615 |
- if not p_info ['physical_only'] and p_info ['ebuild']: |
616 |
- efile = self._get_ebuild_filepath ( ver ) |
617 |
- |
618 |
- if not overwrite and efile == p_info ['ebuild_file']: |
619 |
- print ( efile + " exists, skipping write()." ) |
620 |
- |
621 |
- |
622 |
- elif self.write_ebuild ( |
623 |
- efile, p_info ['ebuild'], header, shared_fh |
624 |
- ): |
625 |
- if shared_fh is None: |
626 |
- # this marks the package as 'written to fs' |
627 |
- p_info.update_now ( |
628 |
- ebuild_file=efile, |
629 |
- remove_auto='ebuild_written' |
630 |
- ) |
631 |
- |
632 |
- self._package_for_manifest = p_info |
633 |
- |
634 |
- self.logger.info ( "Wrote ebuild {}.".format ( efile ) ) |
635 |
- else: |
636 |
- self.logger.error ( |
637 |
- "Couldn't write ebuild {}.".format ( efile ) |
638 |
- ) |
639 |
- # --- end of write_ebuilds (...) --- |
640 |
+ return self.write ( default_header=default_header, shared_fh=stream ) |
641 |
+ # --- end of show (...) --- |
642 |
|
643 |
def write ( self, |
644 |
- default_header=None, write_manifest=True, shared_fh=None |
645 |
+ default_header=None, shared_fh=None, |
646 |
+ write_ebuilds=True, write_manifest=True, write_metadata=True, |
647 |
+ overwrite_ebuilds=True |
648 |
): |
649 |
"""Writes this directory to its (existent!) filesystem location. |
650 |
|
651 |
arguments: |
652 |
* default_header -- ebuild header to write |
653 |
- * write_manifest -- if set and False: don't write the Manifest file |
654 |
+ * shared_fh -- if set and not None: write everyting into <fh> |
655 |
+ * write_ebuilds -- if set and False: don't write ebuilds |
656 |
+ * write_manifest -- if set and False: don't write the Manifest file |
657 |
+ * write_metadata -- if set and False: don't write the metadata file |
658 |
+ * overwrite_ebuilds -- if set and False: don't overwrite ebuilds |
659 |
|
660 |
returns: None (implicit) |
661 |
|
662 |
raises: |
663 |
- * IOError |
664 |
+ * IOError (?) |
665 |
""" |
666 |
- self._lock.acquire() |
667 |
- try: |
668 |
+ with self._lock: |
669 |
# mkdir not required here, overlay.Category does this |
670 |
|
671 |
# write ebuilds |
672 |
- self.write_ebuilds ( header=default_header, shared_fh=shared_fh ) |
673 |
+ if write_ebuilds: |
674 |
+ self.write_ebuilds ( |
675 |
+ default_header=default_header, shared_fh=shared_fh, |
676 |
+ overwrite=overwrite_ebuilds |
677 |
+ ) |
678 |
|
679 |
# write metadata |
680 |
- self.write_metadata ( shared_fh=shared_fh ) |
681 |
+ if write_metadata: |
682 |
+ self.write_metadata ( shared_fh=shared_fh ) |
683 |
|
684 |
- if write_manifest and shared_fh is not None: |
685 |
+ # write manifest (only if shared_fh is None) |
686 |
+ if write_manifest and shared_fh is None: |
687 |
self.write_manifest() |
688 |
- |
689 |
- finally: |
690 |
- self._lock.release() |
691 |
# --- end of write (...) --- |
692 |
|
693 |
- def show ( self, stream=sys.stderr, default_header=None ): |
694 |
- """Prints this dir (the ebuilds and the metadata) into a stream. |
695 |
- |
696 |
- arguments: |
697 |
- * stream -- stream to use, defaults to sys.stderr |
698 |
- |
699 |
- returns: None (implicit) |
700 |
- |
701 |
- raises: |
702 |
- * IOError |
703 |
- """ |
704 |
- self.write ( |
705 |
- default_header=default_header, shared_fh=stream, write_manifest=False |
706 |
- ) |
707 |
- # --- end of show (...) --- |
708 |
- |
709 |
- def _latest_package ( self, pkg_filter=None, use_lock=False ): |
710 |
- """Returns the package info with the highest version number. |
711 |
- |
712 |
- arguments: |
713 |
- * pkg_filter -- either None or a callable, |
714 |
- None: do not filter packages |
715 |
- else: ignore package if it does not pass the filter |
716 |
- * use_lock -- if True: hold lock while searching |
717 |
- """ |
718 |
- first = True |
719 |
- retver = None |
720 |
- retpkg = None |
721 |
- |
722 |
- if use_lock: self._lock.acquire() |
723 |
- try: |
724 |
- for p in self._packages.values(): |
725 |
- if pkg_filter is None or pkg_filter ( p ): |
726 |
- newver = p ['version'] |
727 |
- if first or newver > retver: |
728 |
- retver = newver |
729 |
- retpkg = p |
730 |
- first = False |
731 |
- finally: |
732 |
- if use_lock: self._lock.release() |
733 |
- return retpkg |
734 |
- # --- end of _latest_package (...) --- |
735 |
- |
736 |
- def add ( self, package_info ): |
737 |
- """Adds a package to this PackageDir. |
738 |
+ def write_ebuilds ( self, default_header, overwrite, shared_fh=None ): |
739 |
+ """Writes all ebuilds. |
740 |
|
741 |
arguments: |
742 |
- * package_info -- |
743 |
- |
744 |
- returns: success as bool |
745 |
- |
746 |
- raises: Exception when ebuild already exists. |
747 |
+ * default_header -- ebuild header |
748 |
+ * shared_fh -- if set and not None: don't use own file handles |
749 |
+ (i.e. write files), write everything into shared_fh |
750 |
+ * overwrite -- write ebuilds that have been written before, |
751 |
+ defaults to True |
752 |
""" |
753 |
- shortver = package_info ['ebuild_verstr'] |
754 |
- |
755 |
- def already_exists (): |
756 |
- if shortver in self._packages: |
757 |
- self.logger.info ( |
758 |
- "'{PN}-{PVR}.ebuild' already exists, cannot add it!".format ( |
759 |
- PN=self.name, PVR=shortver |
760 |
- ) |
761 |
- ) |
762 |
- return True |
763 |
- else: |
764 |
- return False |
765 |
- # --- end of already_exists (...) --- |
766 |
- |
767 |
- _success = False |
768 |
- |
769 |
- if not already_exists(): |
770 |
+ def write_ebuild ( efile, ebuild ): |
771 |
+ """Writes an ebuild. |
772 |
+ |
773 |
+ arguments: |
774 |
+ * efile -- file to write |
775 |
+ * ebuild -- ebuild object to write (has to have a __str__ method) |
776 |
+ * (default_header from write_ebuilds()) |
777 |
+ * (shared_fh from write_ebuilds()) |
778 |
+ """ |
779 |
+ _success = False |
780 |
try: |
781 |
- self._lock.acquire() |
782 |
- if not already_exists(): |
783 |
- self._packages [shortver] = package_info |
784 |
- self.modified = True |
785 |
- _success = True |
786 |
+ fh = open ( efile, 'w' ) if shared_fh is None else shared_fh |
787 |
+ if default_header is not None: |
788 |
+ fh.write ( str ( default_header ) ) |
789 |
+ fh.write ( '\n\n' ) |
790 |
+ fh.write ( str ( ebuild ) ) |
791 |
+ fh.write ( '\n' ) |
792 |
+ |
793 |
+ # adjust owner/perm? TODO |
794 |
+ #if shared_fh is None: |
795 |
+ # chmod 0644 or 0444 |
796 |
+ # chown 250.250 |
797 |
+ _success = True |
798 |
+ except IOError as e: |
799 |
+ self.logger.exception ( e ) |
800 |
finally: |
801 |
- self._lock.release() |
802 |
+ if shared_fh is None and 'fh' in locals() and fh: |
803 |
+ fh.close() |
804 |
|
805 |
- return _success |
806 |
- # --- end of add (...) --- |
807 |
+ return _success |
808 |
+ # --- end of write_ebuild (...) --- |
809 |
|
810 |
- def _regen_metadata ( self ): |
811 |
- """Regenerates the metadata.""" |
812 |
- self.generate_metadata ( |
813 |
- skip_if_existent=True, |
814 |
- use_all_packages=False, |
815 |
- use_old_metadata=False |
816 |
- ) |
817 |
- # --- end of _regen_metadata (...) --- |
818 |
+ def ebuilds_to_write(): |
819 |
+ """Yields all ebuilds that are ready to be written.""" |
820 |
|
821 |
- def generate_metadata ( |
822 |
- self, |
823 |
- skip_if_existent=False, use_all_packages=False, use_old_metadata=False |
824 |
- ): |
825 |
- """Creates metadata for this package. |
826 |
+ for ver, p_info in self._packages.items(): |
827 |
+ if p_info.has ( 'ebuild' ) and not p_info ['physical_only']: |
828 |
+ efile = self.ebuild_filepath_format.format ( PVR=ver ) |
829 |
|
830 |
- arguments: |
831 |
- * skip_if_existent -- do not create if metadata already exist |
832 |
- * use_all_packages -- TODO in metadata |
833 |
- * use_old_metadata -- TODO in metadata |
834 |
- """ |
835 |
- if use_old_metadata or use_all_packages: |
836 |
- raise Exception ( "using >1 package for metadata.xml is TODO!" ) |
837 |
+ if efile != p_info ['ebuild_file'] or overwrite: |
838 |
+ yield ( efile, p_info ) |
839 |
+ # else efile exists |
840 |
+ # --- end of ebuilds_to_write (...) --- |
841 |
|
842 |
- if skip_if_existent and not self._metadata is None: return |
843 |
+ all_ebuilds_written = True |
844 |
|
845 |
- self._lock.acquire() |
846 |
- try: |
847 |
+ for efile, p_info in ebuilds_to_write(): |
848 |
+ if write_ebuild ( efile, p_info ['ebuild'] ): |
849 |
+ self._need_manifest = True |
850 |
+ |
851 |
+ # update metadata for each successfully written ebuild |
852 |
+ # (self._metadata knows how to handle this request) |
853 |
+ self._metadata.update ( p_info ) |
854 |
|
855 |
- if self._metadata is None or not use_old_metadata: |
856 |
- del self._metadata |
857 |
- self._metadata = MetadataJob ( self.logger ) |
858 |
+ if shared_fh is None: |
859 |
+ # this marks the package as 'written to fs' |
860 |
+ p_info.update_now ( |
861 |
+ ebuild_file=efile, |
862 |
+ remove_auto='ebuild_written' |
863 |
+ ) |
864 |
|
865 |
- if use_all_packages: |
866 |
- for p_info in self._packages: |
867 |
- self._metadata.update ( p_info ) |
868 |
+ self.logger.info ( "Wrote ebuild {}.".format ( efile ) ) |
869 |
else: |
870 |
- self._metadata.update ( self._latest_package() ) |
871 |
+ all_ebuilds_written = False |
872 |
+ self.logger.error ( |
873 |
+ "Couldn't write ebuild {}.".format ( efile ) |
874 |
+ ) |
875 |
|
876 |
- finally: |
877 |
- self._lock.release() |
878 |
- # --- end of generate_metadata (...) --- |
879 |
+ self.modified = not all_ebuilds_written |
880 |
+ # --- end of write_ebuilds (...) --- |
881 |
+ |
882 |
+ def write_incremental ( self, default_header ): |
883 |
+ with self._lock: |
884 |
+ self.write_ebuilds ( default_header=default_header, overwrite=False ) |
885 |
+ # --- end of write_incremental (...) --- |
886 |
|
887 |
def write_manifest ( self ): |
888 |
"""Generates and writes the Manifest file for this package. |
889 |
|
890 |
- expects: called in self.write(), after writing metadata/ebuilds |
891 |
+ expects: called after writing metadata/ebuilds |
892 |
|
893 |
returns: None (implicit) |
894 |
|
895 |
@@ -378,27 +323,47 @@ class PackageDir ( object ): |
896 |
""" |
897 |
|
898 |
# it should be sufficient to call create_manifest for one ebuild, |
899 |
- # choosing the latest one here that exists in self.physical_location. |
900 |
+ # choosing the latest one that exists in self.physical_location and |
901 |
+ # has enough data (DISTDIR, EBUILD_FILE) for this task. |
902 |
# |
903 |
# metadata.xml's full path cannot be used for manifest creation here |
904 |
# 'cause DISTDIR would be unknown |
905 |
# |
906 |
-# pkg_info_for_manifest = self._latest_package ( |
907 |
-# pkg_filter=lambda pkg : pkg ['ebuild_file'] is not None, |
908 |
-# use_lock=True |
909 |
-# ) |
910 |
|
911 |
- if self._package_for_manifest is None: |
912 |
- # ? FIXME |
913 |
+ if self._manifest_package is not None: |
914 |
+ manifest.create_manifest ( self._manifest_package, nofail=False ) |
915 |
+ self._need_manifest = False |
916 |
+ else: |
917 |
raise Exception ( |
918 |
"No ebuild written so far! I really don't know what do to!" |
919 |
) |
920 |
- else: |
921 |
- # TODO: manifest creation interface is single threaded, |
922 |
- # may want to 'fix' this later |
923 |
- manifest.create_manifest ( |
924 |
- self._package_for_manifest, nofail=False, |
925 |
- #ebuild_file=... |
926 |
+ # --- end of write_manifest (...) --- |
927 |
+ |
928 |
+ def write_metadata ( self, shared_fh=None ): |
929 |
+ """Writes metadata for this package.""" |
930 |
+ if self._manifest_package is None and not shared_fh: |
931 |
+ self.logger.error ( |
932 |
+ 'write_metadata() requested, but no ebuild written so far! ' |
933 |
+ 'This will most likely result in a corrupt Manifest.' |
934 |
) |
935 |
|
936 |
- # --- end of write_manifest (...) --- |
937 |
+ try: |
938 |
+ self.generate_metadata ( skip_if_existent=True ) |
939 |
+ |
940 |
+ if self._metadata.write() \ |
941 |
+ if shared_fh is None else self._metadata.show ( shared_fh ) \ |
942 |
+ : |
943 |
+ self._need_metadata = False |
944 |
+ self._need_manifest = True |
945 |
+ return True |
946 |
+ else: |
947 |
+ self.logger.error ( |
948 |
+ "Failed to write metadata file {}.".format ( |
949 |
+ self._metadata.filepath |
950 |
+ ) |
951 |
+ ) |
952 |
+ return False |
953 |
+ except: |
954 |
+ # already logged |
955 |
+ return False |
956 |
+ # --- end of write_metadata (...) --- |
957 |
|
958 |
diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py |
959 |
index 26be02a..e0446df 100644 |
960 |
--- a/roverlay/overlay/root.py |
961 |
+++ b/roverlay/overlay/root.py |
962 |
@@ -74,6 +74,8 @@ class Overlay ( object ): |
963 |
self.default_category = default_category |
964 |
self.eclass_files = eclass_files |
965 |
|
966 |
+ self.ignore_existing_ebuilds = False |
967 |
+ |
968 |
self._profiles_dir = self.physical_location + os.sep + 'profiles' |
969 |
self._catlock = threading.Lock() |
970 |
self._categories = dict() |
971 |
@@ -143,7 +145,7 @@ class Overlay ( object ): |
972 |
return self._categories [category] |
973 |
# --- end of _get_category (...) --- |
974 |
|
975 |
- def add ( self, package_info, write_after_add=False, category=None ): |
976 |
+ def add ( self, package_info, category=None ): |
977 |
"""Adds a package to this overlay. |
978 |
|
979 |
arguments: |
980 |
@@ -158,12 +160,9 @@ class Overlay ( object ): |
981 |
) |
982 |
|
983 |
if write_after_add: |
984 |
- util.dodir ( cat.physical_location, mkdir_p=True ) |
985 |
- return cat.add ( |
986 |
- package_info, write_after_add=True, header = self._get_header() |
987 |
- ) |
988 |
- else: |
989 |
- return cat.add ( package_info, write_after_add=False ) |
990 |
+ raise Exception ( "add~write_after_add: to be removed." ) |
991 |
+ |
992 |
+ return cat.add ( package_info, write_after_add=False ) |
993 |
# --- end of add (...) --- |
994 |
|
995 |
def show ( self, **show_kw ): |
996 |
@@ -228,6 +227,16 @@ class Overlay ( object ): |
997 |
self._incremental_write_lock.release() |
998 |
# --- end of write_incremental (...) --- |
999 |
|
1000 |
+ def finalize_write_incremental ( self ): |
1001 |
+ """Writes metadata + Manifest for all packages.""" |
1002 |
+ self._init_overlay ( reimport_eclass=True, make_profiles_dir=True ) |
1003 |
+ |
1004 |
+ for cat in self._categories.values(): |
1005 |
+ cat.finalize_write_incremental() |
1006 |
+ |
1007 |
+ self._write_categories ( only_active=True ) |
1008 |
+ # --- end of finalize_incremental (...) --- |
1009 |
+ |
1010 |
def generate_metadata ( self, **metadata_kw ): |
1011 |
"""Tells the overlay's categories to create metadata. |
1012 |
You don't have to call this before write()/show() unless you want to use |