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/metadata/, roverlay/overlay/
Date: Mon, 30 Jul 2012 08:53:54
Message-Id: 1343061110.b60d7f137f1f99f4b4afe85d1f82ace045d6b211.dywi@gentoo
1 commit: b60d7f137f1f99f4b4afe85d1f82ace045d6b211
2 Author: André Erdmann <dywi <AT> mailerd <DOT> de>
3 AuthorDate: Mon Jul 23 16:31:50 2012 +0000
4 Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
5 CommitDate: Mon Jul 23 16:31:50 2012 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=b60d7f13
7
8 incremental overlay writing: keep n ebuilds
9
10 Added keep_nth_latest(n) which keeps the n-th latest ebuilds
11 of a PackageDir and removes all others.
12 Also added fs_cleanup() that removes "empty" (no ebuilds) package dirs from
13 the filesystem.
14
15 geändert: roverlay/overlay/category.py
16 geändert: roverlay/overlay/creator.py
17 geändert: roverlay/overlay/metadata/abstractnodes.py
18 geändert: roverlay/overlay/metadata/nodes.py
19 geändert: roverlay/overlay/package.py
20 geändert: roverlay/overlay/root.py
21
22 ---
23 roverlay/overlay/category.py | 7 +
24 roverlay/overlay/creator.py | 1 +
25 roverlay/overlay/metadata/abstractnodes.py | 2 +-
26 roverlay/overlay/metadata/nodes.py | 3 +-
27 roverlay/overlay/package.py | 153 +++++++++-
28 roverlay/overlay/root.py | 428 +++++++++++++---------------
29 6 files changed, 352 insertions(+), 242 deletions(-)
30
31 diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py
32 index 1d0bf97..33149c4 100644
33 --- a/roverlay/overlay/category.py
34 +++ b/roverlay/overlay/category.py
35 @@ -104,6 +104,13 @@ class Category ( object ):
36 return os.path.isdir ( self.physical_location + os.sep + _dir )
37 # --- end of has_category (...) ---
38
39 + def keep_nth_latest ( self, *args, **kwargs ):
40 + """See package.py:PackageDir:keep_nth_latest."""
41 + for subdir in self._subdirs.values():
42 + subdir.keep_nth_latest ( *args, **kwargs )
43 + subdir.fs_cleanup()
44 + # --- end of keep_nth_latest (...) ---
45 +
46 def list_packages ( self, for_deprules=False ):
47 """Lists all packages in this category.
48 Yields <category>/<package name> or a dict (see for_deprules below).
49
50 diff --git a/roverlay/overlay/creator.py b/roverlay/overlay/creator.py
51 index 114f12d..9de063c 100644
52 --- a/roverlay/overlay/creator.py
53 +++ b/roverlay/overlay/creator.py
54 @@ -304,6 +304,7 @@ class OverlayCreator ( object ):
55
56 self._close_workers()
57 close_resolver()
58 + self.overlay.keep_nth_latest ( n=1 )
59 self.closed = True
60 # --- end of close (...) ---
61
62
63 diff --git a/roverlay/overlay/metadata/abstractnodes.py b/roverlay/overlay/metadata/abstractnodes.py
64 index a344fc1..284ea20 100644
65 --- a/roverlay/overlay/metadata/abstractnodes.py
66 +++ b/roverlay/overlay/metadata/abstractnodes.py
67 @@ -242,7 +242,7 @@ class MetadataLeaf ( _MetadataBasicNode ):
68 self._do_verify()
69 if self.print_node_name:
70 return "{indent}<{name}{flags}>{value}</{name}>".format (
71 - indent=self._indent,
72 + indent=self.indent,
73 name=self.name,
74 flags=self._flagstr(),
75 value=self._value_str(),
76
77 diff --git a/roverlay/overlay/metadata/nodes.py b/roverlay/overlay/metadata/nodes.py
78 index 02d5482..0002267 100644
79 --- a/roverlay/overlay/metadata/nodes.py
80 +++ b/roverlay/overlay/metadata/nodes.py
81 @@ -22,7 +22,8 @@ class MetadataRoot ( MetadataNodeNamedAccess ):
82
83 def __init__ ( self ):
84 super ( MetadataRoot, self ) . __init__ ( 'pkgmetadata' )
85 - self.priority = 0
86 + self.priority = 0
87 + self.allow_empty = True
88 # --- end of __init__ (...) ---
89
90 def empty ( self ):
91
92 diff --git a/roverlay/overlay/package.py b/roverlay/overlay/package.py
93 index 2ea4cb2..a2e4d9b 100644
94 --- a/roverlay/overlay/package.py
95 +++ b/roverlay/overlay/package.py
96 @@ -1,14 +1,15 @@
97 import os
98 import sys
99 import threading
100 +import shutil
101
102 +from roverlay import util
103 from roverlay.overlay import manifest
104 from roverlay.packageinfo import PackageInfo
105 from roverlay.overlay.metadata import MetadataJob
106
107 SUPPRESS_EXCEPTIONS = True
108
109 -
110 class PackageDir ( object ):
111 EBUILD_SUFFIX = '.ebuild'
112
113 @@ -48,6 +49,22 @@ class PackageDir ( object ):
114 self._need_metadata = False
115 # --- end of __init__ (...) ---
116
117 + def _remove_ebuild_file ( self, pkg_info ):
118 + """Removes the ebuild file of a pkg_info object.
119 + Returns True on success, else False.
120 + """
121 + try:
122 + efile = pkg_info ['ebuild_file']
123 + if efile is not None:
124 + os.unlink ( efile )
125 + # Manifest file has to be updated
126 + self._need_manifest = True
127 + return True
128 + except Exception as e:
129 + self.logger.exception ( e )
130 + return False
131 + # --- end of remove_ebuild_file (...) ---
132 +
133 def add ( self, package_info, add_if_physical=False ):
134 """Adds a package to this PackageDir.
135
136 @@ -105,19 +122,51 @@ class PackageDir ( object ):
137
138 def empty ( self ):
139 """Returns True if no ebuilds stored, else False.
140 - Note that "not empty" does not mean "have ebuilds to write".
141 + Note that "not empty" doesn't mean "has ebuilds to write" or "has
142 + ebuilds written", use the modified attribute for the former, and the
143 + has_ebuilds() function for the latter one.
144 """
145 return len ( self._packages ) == 0
146 # --- end of empty (...) ---
147
148 def finalize_write_incremental ( self ):
149 + """Method that finalizes incremental writing, i.e. write outstanding
150 + ebuilds and write metadata.xml, Manifest.
151 + """
152 with self._lock:
153 - if self._need_metadata:
154 - self.write_metadata()
155 - if self._need_manifest:
156 - self.write_manifest()
157 + if self.has_ebuilds():
158 + if self.modified:
159 + self.write_ebuilds ( overwrite=False )
160 + if self._need_metadata:
161 + self.write_metadata()
162 + if self._need_manifest:
163 + self.write_manifest()
164 + else:
165 + self.logger.critical (
166 + "<<todo>>: please clean up this dir: {}.".format (
167 + self.physical_location
168 + ) )
169 # --- end of finalize_write_incremental (...) ---
170
171 + def fs_cleanup ( self ):
172 + """Cleans up the filesystem location of this package dir.
173 + To be called after keep_nth_latest, calls finalize_write_incremental().
174 + """
175 + def rmtree_error ( function, path, excinfo ):
176 + """rmtree onerror function that simply logs the exception"""
177 + self.logger.exception ( excinfo )
178 + # --- end of rmtree_error (...) ---
179 +
180 + with self._lock:
181 + if self.has_ebuilds():
182 + # !!! FIXME this doesn't work if no ebuilds written, but
183 + # old ones removed -> DISTDIR unknown during Manifest creation
184 + self.finalize_write_incremental()
185 + elif os.path.isdir ( self.physical_location ):
186 + # destroy self.physical_location
187 + shutil.rmtree ( self.physical_location, onerror=rmtree_error )
188 + # --- end of fs_cleanup (...) ---
189 +
190 def generate_metadata ( self, skip_if_existent, **ignored_kw ):
191 """Creates metadata for this package.
192
193 @@ -129,6 +178,70 @@ class PackageDir ( object ):
194 self._metadata.update_using_iterable ( self._packages.values() )
195 # --- end of generate_metadata (...) ---
196
197 + def has_ebuilds ( self ):
198 + """Returns True if this PackageDir has any ebuild files (filesystem)."""
199 + for p in self._packages.values():
200 + if p ['physical_only'] or p.has ( 'ebuild' ):
201 + return True
202 + return False
203 + # --- end of has_ebuilds (...) ---
204 +
205 + def keep_nth_latest ( self, n, cautious=True ):
206 + """Keeps the n-th latest ebuild files, removing all other packages,
207 + physically (filesystem) as well as from this PackageDir object.
208 +
209 + arguments:
210 + * n -- # of packages/ebuilds to keep
211 + * cautious -- if True: be extra careful, verify that ebuilds exist
212 + """
213 +
214 + # create the list of packages to iterate over,
215 + # * package has to have an ebuild_file
216 + # * sort them by version in reverse order (latest package gets index 0)
217 + packages = reversed ( sorted (
218 + filter (
219 + lambda p : p [1] ['ebuild_file'] is not None,
220 + self._packages.items()
221 + ),
222 + key=lambda p : p [1] ['version']
223 + ) )
224 +
225 + kept = 0
226 + ecount = 0
227 + if not cautious:
228 + # could use a slice here, too
229 + for pvr, pkg in packages:
230 + ecount += 1
231 + if kept < n:
232 + printself.logger.debug ( "Keeping {pvr}.".format ( pvr=pvr ) )
233 + kept += 1
234 + else:
235 + self.logger.debug ( "Removing {pvr}.".format ( pvr=pvr ) )
236 + self.purge_package ( pvr )
237 + else:
238 + for pvr, pkg in packages:
239 + ecount += 1
240 + if os.path.isfile ( pkg ['ebuild_file'] ):
241 + if kept < n:
242 + self.logger.debug ( "Keeping {pvr}.".format ( pvr=pvr ) )
243 + kept += 1
244 + else:
245 + self.logger.debug ( "Removing {pvr}.".format ( pvr=pvr ) )
246 + self.purge_package ( pvr )
247 + else:
248 + self.logger.error (
249 + "{efile} is assumed to exist as file but doesn't!".format (
250 + efile=pkg ['ebuild_file']
251 + ) )
252 +
253 + # FIXME/IGNORE: this doesn't count inexistent files as kept
254 + self.logger.debug (
255 + "Kept {kept}/{total} ebuilds.".format ( kept=kept, total=ecount )
256 + )
257 +
258 + # FIXME: Manifest is now invalid and dir could be "empty" (no ebuilds)
259 + # --- end of keep_nth_latest (...) ---
260 +
261 def list_versions ( self ):
262 return self._packages.keys()
263 # --- end of list_versions (...) ---
264 @@ -144,6 +257,21 @@ class PackageDir ( object ):
265 return True
266 # --- end of new_ebuild (...) ---
267
268 + def purge_package ( self, pvr ):
269 + """Removes the PackageInfo with key pvr entirely from this PackageDir,
270 + including its ebuild file.
271 + Returns: removed PackageInfo object or None.
272 + """
273 + try:
274 + p = self._packages [pvr]
275 + del self._packages [pvr]
276 + self._remove_ebuild_file ( p )
277 + return p
278 + except Exception as e:
279 + self.logger.exception ( e )
280 + return None
281 + # --- end of purge_package (...) ---
282 +
283 def scan ( self, **kw ):
284 """Scans the filesystem location of this package for existing
285 ebuilds and adds them.
286 @@ -168,7 +296,7 @@ class PackageDir ( object ):
287 # not an ebuild
288 pass
289 elif pn == self.name:
290 - yield pvr
291 + yield ( pvr, self.physical_location + os.sep + f )
292 else:
293 # $PN does not match directory name, warn about that
294 self.logger.warning (
295 @@ -183,9 +311,11 @@ class PackageDir ( object ):
296
297 # ignore directories without a Manifest file
298 if os.path.isfile ( self.physical_location + os.sep + 'Manifest' ):
299 - for pvr in scan_ebuilds():
300 + for pvr, efile in scan_ebuilds():
301 if pvr not in self._packages:
302 - p = PackageInfo ( physical_only=True, pvr=pvr )
303 + p = PackageInfo (
304 + physical_only=True, pvr=pvr, ebuild_file=efile
305 + )
306 self._packages [ p ['ebuild_verstr'] ] = p
307 # --- end of scan (...) ---
308
309 @@ -260,6 +390,7 @@ class PackageDir ( object ):
310 """
311 _success = False
312 try:
313 + util.dodir ( self.physical_location )
314 fh = open ( efile, 'w' ) if shared_fh is None else shared_fh
315 if ebuild_header is not None:
316 fh.write ( str ( ebuild_header ) )
317 @@ -380,7 +511,7 @@ class PackageDir ( object ):
318 else:
319 self._metadata.show ( shared_fh )
320 return True
321 - except:
322 - # already logged
323 + except Exception as e:
324 + self.logger.exception ( e )
325 return False
326 # --- end of write_metadata (...) ---
327
328 diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py
329 index 51239e2..363bdb5 100644
330 --- a/roverlay/overlay/root.py
331 +++ b/roverlay/overlay/root.py
332 @@ -27,11 +27,6 @@ class Overlay ( object ):
333 ebuild_header,
334 incremental
335 ):
336 - if directory is None:
337 - raise Exception (
338 - "support for overlays without filesystem location has been dropped"
339 - )
340 -
341 self.name = name
342 self.logger = logger.getChild ( 'overlay' )
343 self.physical_location = directory
344 @@ -47,7 +42,7 @@ class Overlay ( object ):
345
346 # fixme or ignore: calculating eclass names twice,
347 # once here and another time when calling _init_overlay
348 - self._header.set_eclasses ( set (
349 + self._header.set_eclasses ( frozenset (
350 self._get_eclass_import_info ( only_eclass_names=True )
351 ) )
352
353 @@ -56,44 +51,10 @@ class Overlay ( object ):
354
355 self._incremental_write_lock = threading.Lock()
356 self.scan()
357 - self._init_overlay ( reimport_eclass=True, make_profiles_dir=True )
358 + self._init_overlay ( reimport_eclass=True )
359
360 # --- end of __init__ (...) ---
361
362 - def scan ( self, **kw ):
363 - if os.path.isdir ( self.physical_location ):
364 - for cat in self._scan_categories():
365 - try:
366 - cat.scan ( **kw )
367 - except Exception as e:
368 - self.logger.exception ( e )
369 -# for package in sorted ( self.list_packages() ):
370 -# print ( package )
371 - # --- end of scan (...) ---
372 -
373 - def list_packages ( self, for_deprules=True ):
374 - for cat in self._categories.values():
375 - for package in cat.list_packages ( for_deprules=True ):
376 - yield package
377 - # --- end of list_packages (...) ---
378 -
379 - def list_rule_kwargs ( self ):
380 - for cat in self._categories.values():
381 - for kwargs in cat.list_packages ( for_deprules=True ):
382 - yield kwargs
383 - # --- end of list_rule_kwargs (...) ---
384 -
385 - def has_dir ( self, _dir ):
386 - return os.path.isdir ( self.physical_location + os.sep + _dir )
387 - # --- end of has_category (...) ---
388 -
389 - def _scan_categories ( self ):
390 - for x in os.listdir ( self.physical_location ):
391 - # FIXME could use a better check here
392 - if '-' in x and self.has_dir ( x ):
393 - yield self._get_category ( x )
394 - # --- end of _scan_categories (...) ---
395 -
396 def _get_category ( self, category ):
397 """Returns a reference to the given category. Creates it if necessary.
398
399 @@ -120,191 +81,6 @@ class Overlay ( object ):
400 return self._categories [category]
401 # --- end of _get_category (...) ---
402
403 - def add ( self, package_info, category=None ):
404 - """Adds a package to this overlay.
405 -
406 - arguments:
407 - * package_info -- PackageInfo of the package to add
408 - * category -- category where the pkg should be put in, defaults to
409 - self.default_category
410 -
411 - returns: True if successfully added else False
412 - """
413 - cat = self._get_category (
414 - self.default_category if category is None else category
415 - )
416 - return cat.add ( package_info )
417 - # --- end of add (...) ---
418 -
419 - def show ( self, **show_kw ):
420 - """Presents the ebuilds/metadata stored in this overlay.
421 -
422 - arguments:
423 - * **show_kw -- keywords for package.PackageDir.show(...)
424 -
425 - returns: None (implicit)
426 - """
427 - if not self._header.eclasses: self._header.set_eclasses (
428 - tuple ( self._get_eclass_import_info ( only_eclass_names=True ) )
429 - )
430 - for cat in self._categories.values():
431 - cat.show ( **show_kw )
432 - # --- end of show (...) ---
433 -
434 - def write ( self, **write_kw ):
435 - """Writes the overlay to its physical location (filesystem), including
436 - metadata and Manifest files.
437 -
438 - arguments:
439 - * **write_kw -- keywords for package.PackageDir.write(...)
440 -
441 - returns: None (implicit)
442 -
443 - raises: IOError
444 -
445 - ! TODO/FIXME/DOC: This is not thread-safe, it's expected to be called
446 - when ebuild creation is done.
447 - """
448 - raise Exception ( "^,^" )
449 - # writing profiles/ here, rewriting categories/ later
450 - self._init_overlay ( reimport_eclass=True, make_profiles_dir=True )
451 -
452 - for cat in self._categories.values():
453 - if not cat.empty():
454 - util.dodir ( cat.physical_location )
455 - cat.write ( **write_kw )
456 -
457 - self._write_categories ( only_active=True )
458 - # --- end of write (...) ---
459 -
460 - def write_incremental ( self, **write_kw ):
461 - """Writes all ebuilds that have been added since the last
462 - write_incremental call.
463 - TODO:
464 - * This could be useful to save some mem by removing already written
465 - package infos.
466 - * This has to be thread safe
467 - """
468 - if not self._incremental_write_lock.acquire():
469 - # another incremental write is running, drop this request
470 - return
471 -
472 - try:
473 - util.dodir ( self.physical_location )
474 - cats = tuple ( self._categories.values() )
475 - for cat in cats:
476 - util.dodir ( cat.physical_location )
477 - cat.write_incremental ( **write_kw )
478 - finally:
479 - self._incremental_write_lock.release()
480 - # --- end of write_incremental (...) ---
481 -
482 - def finalize_write_incremental ( self ):
483 - """Writes metadata + Manifest for all packages."""
484 - self._write_categories ( only_active=True )
485 - for cat in self._categories.values():
486 - cat.finalize_write_incremental()
487 - # --- end of finalize_incremental (...) ---
488 -
489 - def generate_metadata ( self, **metadata_kw ):
490 - """Tells the overlay's categories to create metadata.
491 - You don't have to call this before write()/show() unless you want to use
492 - special metadata options.
493 -
494 - arguments:
495 - * **metadata_kw -- keywords for package.PackageDir.generate_metadata(...)
496 -
497 - returns: None (implicit)
498 - """
499 - for cat in self._categories.values():
500 - cat.generate_metadata ( **metadata_kw )
501 - # --- end of generate_metadata (...) ---
502 -
503 - def generate_manifest ( self, **manifest_kw ):
504 - """Generates Manifest files for all ebuilds in this overlay that exist
505 - physically/in filesystem.
506 - Manifest files are automatically created when calling write().
507 -
508 - arguments:
509 - * **manifest_kw -- see PackageDir.generate_manifest(...)
510 -
511 - returns: None (implicit)
512 - """
513 - for cat in self._categories.values():
514 - cat.generate_manifest ( **manifest_kw )
515 - # --- end of generate_manifest (...) ---
516 -
517 - def _write_profiles_dir ( self, only_active_categories=True ):
518 - """Creates and updates the profiles/ dir.
519 -
520 - arguments:
521 - * only_active_categories -- if True: do not list categories without
522 - ebuilds in profiles/categories
523 - """
524 - # profiles/
525 - util.dodir ( self._profiles_dir )
526 - self._write_repo_name()
527 - self._write_categories ( only_active=only_active_categories )
528 - self._write_usedesc()
529 - # --- end of _write_profiles_dir (...) ---
530 -
531 - def _write_profiles_file ( self, filename, to_write ):
532 - """Writes a file in profiles/.
533 -
534 - arguments:
535 - * filename -- name of the file to write (including file extension)
536 - * to_write -- string to write (don't forget newline at the end)
537 - """
538 - fh = None
539 - try:
540 - fh = open ( self._profiles_dir + os.sep + filename, 'w' )
541 - if to_write:
542 - # else touch file
543 - fh.write ( to_write )
544 - except IOError as e:
545 - self.logger.exception ( e )
546 - raise
547 - finally:
548 - if fh: fh.close()
549 - # --- end of _write_profiles_file (...) ---
550 -
551 - def _write_repo_name ( self ):
552 - """Writes profiles/repo_name."""
553 - self._write_profiles_file ( 'repo_name', self.name + '\n' )
554 - # --- end of _write_repo_name (...) ---
555 -
556 - def _write_categories ( self, only_active=True ):
557 - """Writes profiles/categories.
558 -
559 - arguments:
560 - * only_active -- exclude categories without ebuilds
561 - """
562 - cats = None
563 - if only_active:
564 - cats = [
565 - name for name, category
566 - in self._categories.items() if not category.empty()
567 - ]
568 - else:
569 - cats = list ( self._categories.keys() )
570 -
571 - if cats:
572 - self._write_profiles_file (
573 - 'categories',
574 - '\n'.join ( cats ) + '\n'
575 - )
576 - # --- end of _write_categories (...) ---
577 -
578 - def _write_usedesc ( self ):
579 - """Writes profiles/use.desc."""
580 - use_desc = config.get (
581 - 'OVERLAY.use_desc',
582 - fallback_value=DEFAULT_USE_DESC
583 - )
584 - if use_desc:
585 - self._write_profiles_file ( 'use.desc', use_desc + '\n' )
586 - # --- end of _write_usedesc (...) ---
587 -
588 def _get_eclass_import_info ( self, only_eclass_names=False ):
589 """Yields eclass import information (eclass names and files).
590
591 @@ -363,7 +139,7 @@ class Overlay ( object ):
592 raise
593 # --- end of _import_eclass (...) ---
594
595 - def _init_overlay ( self, reimport_eclass, make_profiles_dir ):
596 + def _init_overlay ( self, reimport_eclass ):
597 """Initializes the overlay at its physical/filesystem location.
598
599 arguments:
600 @@ -374,14 +150,58 @@ class Overlay ( object ):
601 raises:
602 * IOError
603 """
604 + def write_profiles_dir():
605 + """Creates and updates the profiles/ dir."""
606 + def write_profiles_file ( filename, to_write ):
607 + """Writes a file in profiles/.
608 +
609 + arguments:
610 + * filename -- name of the file to write (including file extension)
611 + * to_write -- string to write (don't forget newline at the end)
612 + """
613 + fh = None
614 + try:
615 + fh = open ( self._profiles_dir + os.sep + filename, 'w' )
616 + if to_write:
617 + # else touch file
618 + fh.write ( to_write )
619 + except IOError as e:
620 + self.logger.exception ( e )
621 + raise
622 + finally:
623 + if fh: fh.close()
624 + # --- end of write_profiles_file (...) ---
625 +
626 + # always use the default category (+write it into profiles/categories)
627 + self._get_category ( self.default_category )
628 +
629 + # profiles/
630 + util.dodir ( self._profiles_dir )
631 +
632 + # profiless/repo_name
633 + write_profiles_file ( 'repo_name', self.name + '\n' )
634 +
635 + # profiles/categories
636 + cats = '\n'.join ( self._categories.keys() )
637 + if cats:
638 + write_profiles_file ( 'categories', cats + '\n' )
639 +
640 + # profiles/use.desc
641 + use_desc = config.get (
642 + 'OVERLAY.use_desc',
643 + fallback_value=DEFAULT_USE_DESC
644 + )
645 + if use_desc:
646 + write_profiles_file ( 'use.desc', use_desc + '\n' )
647 + # --- end of write_profiles_dir (...) ---
648 +
649 try:
650 # mkdir overlay root
651 util.dodir ( self.physical_location, mkdir_p=True )
652
653 self._import_eclass ( reimport_eclass )
654
655 - if make_profiles_dir:
656 - self._write_profiles_dir ( only_active_categories=False )
657 + write_profiles_dir()
658
659 except IOError as e:
660
661 @@ -389,3 +209,153 @@ class Overlay ( object ):
662 self.logger.critical ( "^failed to init overlay" )
663 raise
664 # --- end of _init_overlay (...) ---
665 +
666 + def add ( self, package_info, category=None ):
667 + """Adds a package to this overlay.
668 +
669 + arguments:
670 + * package_info -- PackageInfo of the package to add
671 + * category -- category where the pkg should be put in, defaults to
672 + self.default_category
673 +
674 + returns: True if successfully added else False
675 + """
676 + cat = self._get_category (
677 + self.default_category if category is None else category
678 + )
679 + return cat.add ( package_info )
680 + # --- end of add (...) ---
681 +
682 + def finalize_write_incremental ( self ):
683 + """Writes metadata + Manifest for all packages."""
684 + for cat in self._categories.values():
685 + cat.finalize_write_incremental()
686 + # --- end of finalize_incremental (...) ---
687 +
688 + def generate_manifest ( self, **manifest_kw ):
689 + """Generates Manifest files for all ebuilds in this overlay that exist
690 + physically/in filesystem.
691 + Manifest files are automatically created when calling write().
692 +
693 + arguments:
694 + * **manifest_kw -- see PackageDir.generate_manifest(...)
695 +
696 + returns: None (implicit)
697 + """
698 + for cat in self._categories.values():
699 + cat.generate_manifest ( **manifest_kw )
700 + # --- end of generate_manifest (...) ---
701 +
702 + def generate_metadata ( self, **metadata_kw ):
703 + """Tells the overlay's categories to create metadata.
704 + You don't have to call this before write()/show() unless you want to use
705 + special metadata options.
706 +
707 + arguments:
708 + * **metadata_kw -- keywords for package.PackageDir.generate_metadata(...)
709 +
710 + returns: None (implicit)
711 + """
712 + for cat in self._categories.values():
713 + cat.generate_metadata ( **metadata_kw )
714 + # --- end of generate_metadata (...) ---
715 +
716 + def has_dir ( self, _dir ):
717 + return os.path.isdir ( self.physical_location + os.sep + _dir )
718 + # --- end of has_category (...) ---
719 +
720 + def keep_nth_latest ( self, *args, **kwargs ):
721 + """See package.py:PackageDir:keep_nth_latest."""
722 + for cat in self._categories.values():
723 + cat.keep_nth_latest ( *args, **kwargs )
724 + # --- end of keep_nth_latest (...) ---
725 +
726 + def list_packages ( self, for_deprules=True ):
727 + for cat in self._categories.values():
728 + for package in cat.list_packages ( for_deprules=True ):
729 + yield package
730 + # --- end of list_packages (...) ---
731 +
732 + def list_rule_kwargs ( self ):
733 + for cat in self._categories.values():
734 + for kwargs in cat.list_packages ( for_deprules=True ):
735 + yield kwargs
736 + # --- end of list_rule_kwargs (...) ---
737 +
738 + def scan ( self, **kw ):
739 + def scan_categories():
740 + for x in os.listdir ( self.physical_location ):
741 + # FIXME could use a better check here
742 + if '-' in x and self.has_dir ( x ):
743 + yield self._get_category ( x )
744 + # --- end of scan_categories (...) ---
745 +
746 + if os.path.isdir ( self.physical_location ):
747 + for cat in scan_categories():
748 + try:
749 + cat.scan ( **kw )
750 + except Exception as e:
751 + self.logger.exception ( e )
752 + # --- end of scan (...) ---
753 +
754 + def show ( self, **show_kw ):
755 + """Presents the ebuilds/metadata stored in this overlay.
756 +
757 + arguments:
758 + * **show_kw -- keywords for package.PackageDir.show(...)
759 +
760 + returns: None (implicit)
761 + """
762 + if not self._header.eclasses: self._header.set_eclasses (
763 + tuple ( self._get_eclass_import_info ( only_eclass_names=True ) )
764 + )
765 + for cat in self._categories.values():
766 + cat.show ( **show_kw )
767 + # --- end of show (...) ---
768 +
769 + def write ( self, **write_kw ):
770 + """Writes the overlay to its physical location (filesystem), including
771 + metadata and Manifest files.
772 +
773 + arguments:
774 + * **write_kw -- keywords for package.PackageDir.write(...)
775 +
776 + returns: None (implicit)
777 +
778 + raises: IOError
779 +
780 + ! TODO/FIXME/DOC: This is not thread-safe, it's expected to be called
781 + when ebuild creation is done.
782 + """
783 + raise Exception ( "to be removed/replaced" )
784 + # writing profiles/ here, rewriting categories/ later
785 + self._init_overlay ( reimport_eclass=True )
786 +
787 + for cat in self._categories.values():
788 + if not cat.empty():
789 + util.dodir ( cat.physical_location )
790 + cat.write ( **write_kw )
791 + # --- end of write (...) ---
792 +
793 + def write_incremental ( self, **write_kw ):
794 + """Writes all ebuilds that have been modified since the last write call.
795 + Note that there are currently two modes of incremental writing:
796 + (a) per-PackageDir incremental writing triggered by the new_ebuild()
797 + event method and (b) "batched" incremental writing (this method) which
798 + writes all modified PackageDirs.
799 + """
800 + # FIXME merge with write(), making incremental writing the only option
801 + # FIXME finalize_write_incremental?
802 + if not self._incremental_write_lock.acquire():
803 + # another incremental write is running, drop this request
804 + return
805 +
806 + try:
807 + util.dodir ( self.physical_location )
808 + cats = tuple ( self._categories.values() )
809 + for cat in cats:
810 + util.dodir ( cat.physical_location )
811 + cat.write_incremental ( **write_kw )
812 + finally:
813 + self._incremental_write_lock.release()
814 + # --- end of write_incremental (...) ---