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:18
Message-Id: 1342716808.548e6ceede8e8ba8d75492656656275f13b20613.dywi@gentoo
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