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/, roverlay/overlay/
Date: Mon, 30 Jul 2012 08:53:39
Message-Id: 1342630009.bd0d4d01b9b32f82040f0782fab40b957f70eb94.dywi@gentoo
1 commit: bd0d4d01b9b32f82040f0782fab40b957f70eb94
2 Author: André Erdmann <dywi <AT> mailerd <DOT> de>
3 AuthorDate: Wed Jul 18 16:46:49 2012 +0000
4 Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
5 CommitDate: Wed Jul 18 16:46:49 2012 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=bd0d4d01
7
8 incremental overlay writing (at runtime)
9
10 Packages can now be written directly after adding them to the overlay.
11 Also removed unused code / code duplicates, e.g. show() and write() in
12 package.py.
13
14 geändert: roverlay/overlay/category.py
15 geändert: roverlay/overlay/creator.py
16 geändert: roverlay/overlay/package.py
17 geändert: roverlay/overlay/root.py
18 geändert: roverlay/packageinfo.py
19
20 ---
21 roverlay/overlay/category.py | 85 +++++++++++----
22 roverlay/overlay/creator.py | 21 ++--
23 roverlay/overlay/package.py | 252 ++++++++++++++++++++++++------------------
24 roverlay/overlay/root.py | 141 ++++++++++++++++--------
25 roverlay/packageinfo.py | 26 +++--
26 5 files changed, 328 insertions(+), 197 deletions(-)
27
28 diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py
29 index 34864b9..afa2456 100644
30 --- a/roverlay/overlay/category.py
31 +++ b/roverlay/overlay/category.py
32 @@ -33,6 +33,19 @@ class Category ( object ):
33 self.physical_location = directory
34 # --- end of __init__ (...) ---
35
36 + def has ( self, subdir ):
37 + return subdir in self._subdirs
38 + # --- end of has (...) ---
39 +
40 + def list_packages ( self, print_category=True ):
41 + if print_category:
42 + for package in self._subdirs.keys():
43 + yield self.name + os.sep + package
44 + else:
45 + for package in self._subdirs.keys():
46 + yield package
47 + # --- end of list_packages (...) ---
48 +
49 def has_dir ( self, _dir ):
50 return os.path.isdir ( self.physical_location + os.sep + _dir )
51 # --- end of has_category (...) ---
52 @@ -43,10 +56,9 @@ class Category ( object ):
53 yield self._get_package_dir ( x )
54 # --- end of _scan_packages (...) ---
55
56 - def scan ( self ):
57 + def scan ( self, **kw ):
58 for pkg in self._scan_packages():
59 - print ( pkg.name )
60 - pkg.scan()
61 + pkg.scan ( **kw )
62 # --- end of scan (...) ---
63
64 def empty ( self ):
65 @@ -64,8 +76,7 @@ class Category ( object ):
66 self._subdirs [pkg_name] = PackageDir (
67 pkg_name,
68 self.logger,
69 - None if self.physical_location is None else \
70 - os.path.join ( self.physical_location, pkg_name )
71 + self.physical_location + os.sep + pkg_name
72 )
73 finally:
74 self._lock.release()
75 @@ -73,7 +84,7 @@ class Category ( object ):
76 return self._subdirs [pkg_name]
77 # --- end of _get_package_dir (...) ---
78
79 - def add ( self, package_info ):
80 + def add ( self, package_info, write_after_add=False, header=None ):
81 """Adds a package to this category.
82
83 arguments:
84 @@ -81,9 +92,14 @@ class Category ( object ):
85
86 returns: success
87 """
88 - return self._get_package_dir (
89 - package_info ['name']
90 - ).add ( package_info )
91 + subdir = self._get_package_dir ( package_info ['name'] )
92 + if subdir.add ( package_info ):
93 + if write_after_add:
94 + roverlay.util.dodir ( subdir.physical_location )
95 + subdir.write_incremental ( default_header=header )
96 + return True
97 + else:
98 + return False
99 # --- end of add (...) ---
100
101 def generate_metadata ( self, **metadata_kw ):
102 @@ -99,18 +115,18 @@ class Category ( object ):
103 package.generate_metadata ( **metadata_kw )
104 # --- end of generate_metadata (...) ---
105
106 - def generate_manifest ( self, **manifest_kw ):
107 + def write_manifest ( self, **manifest_kw ):
108 """Generates Manifest files for all packages in this category.
109 Manifest files are automatically created when calling write().
110
111 arguments:
112 - * **manifest_kw -- see PackageDir.generate_manifest(...)
113 + * **manifest_kw -- see PackageDir.write_manifest(...)
114
115 returns: None (implicit)
116 """
117 for package in self._subdirs.values():
118 - package.generate_manifest ( **manifest_kw )
119 - # --- end of generate_manifest (...) ---
120 + package.write_manifest ( **manifest_kw )
121 + # --- end of write_manifest (...) ---
122
123 def show ( self, **show_kw ):
124 """Prints this category (its ebuild and metadata files).
125 @@ -122,10 +138,16 @@ class Category ( object ):
126 # --- end of show (...) ---
127
128 def _run_write_queue ( self, q, write_kw ):
129 + """Calls <package>.write for every <package> received from the queue.
130 +
131 + arguments:
132 + * q -- queue
133 + * write_kw --
134 + """
135 try:
136 while not q.empty():
137 pkg = q.get_nowait()
138 - pkg.write ( **write_kw )
139 + pkg.write ( write_manifest=False, **write_kw )
140
141 except queue.Empty:
142 pass
143 @@ -134,6 +156,21 @@ class Category ( object ):
144
145 # --- end of _run_write_queue (...) ---
146
147 + def write_incremental ( self, **write_kw ):
148 + """Writes this category incrementally."""
149 + try:
150 + with self._lock:
151 + # new package dirs could be added during overlay writing,
152 + # so collect the list of package dirs before iterating over it
153 + subdirs = tuple ( self._subdirs.values() )
154 +
155 + for package in subdirs:
156 + roverlay.util.dodir ( package.physical_location )
157 + package.write_incremental ( **write_kw )
158 + except Exception as e:
159 + self.logger.exception ( e )
160 + # --- end of write_incremental (...) ---
161 +
162 def write ( self, **write_kw ):
163 """Writes this category to its filesystem location.
164
165 @@ -148,14 +185,15 @@ class Category ( object ):
166
167 # writing 1..self.__class__.WRITE_JOBCOUNT package dirs at once
168
169 - write_queue = queue.Queue()
170 - for package in self._subdirs.values():
171 - if package.physical_location and not package.empty():
172 + modified_packages = tuple (
173 + p for p in self._subdirs.values() if p.modified
174 + )
175 + if len ( modified_packages ) > 0:
176 + write_queue = queue.Queue()
177 + for package in modified_packages:
178 roverlay.util.dodir ( package.physical_location )
179 write_queue.put_nowait ( package )
180
181 -
182 - if not write_queue.empty():
183 workers = (
184 threading.Thread (
185 target=self._run_write_queue,
186 @@ -168,9 +206,14 @@ class Category ( object ):
187
188 if hasattr ( self, 'RERAISE_EXCEPTION' ):
189 raise self.RERAISE_EXCEPTION
190 +
191 + # write manifest files
192 + for package in modified_packages:
193 + package.write_manifest()
194 +
195 else:
196 for package in self._subdirs.values():
197 - if package.physical_location and not package.empty():
198 + if package.modified:
199 roverlay.util.dodir ( package.physical_location )
200 - package.write ( **write_kw )
201 + package.write ( write_manifest=True, **write_kw )
202 # --- end of write (...) ---
203
204 diff --git a/roverlay/overlay/creator.py b/roverlay/overlay/creator.py
205 index 600a078..3a6e0ed 100644
206 --- a/roverlay/overlay/creator.py
207 +++ b/roverlay/overlay/creator.py
208 @@ -108,6 +108,7 @@ class OverlayCreator ( object ):
209 self._runlock = threading.RLock()
210
211 self.can_write_overlay = OVERLAY_WRITE_ALLOWED
212 + self.write_incremental = True
213
214 self.closed = False
215
216 @@ -223,7 +224,6 @@ class OverlayCreator ( object ):
217 self.package_added.inc()
218 # --- end of add_packages (...) ---
219
220 -
221 def write_overlay ( self, incremental=False ):
222 """Writes the overlay.
223
224 @@ -232,12 +232,7 @@ class OverlayCreator ( object ):
225 """
226 if self.can_write_overlay:
227 start = time.time()
228 - if incremental:
229 - # this will fail 'cause not implemented
230 - self.overlay.write_incremental()
231 - else:
232 - self.overlay.write()
233 -
234 + self.overlay.write()
235 self._timestamp ( "overlay written", start )
236 else:
237 self.logger.warning ( "Not allowed to write overlay!" )
238 @@ -394,13 +389,17 @@ class OverlayCreator ( object ):
239 # * request an incremental write to save memory etc.
240
241 # if <>:
242 - if package_info ['ebuild'] is None:
243 - self.create_fail.inc()
244 - else:
245 + if package_info ['ebuild'] is not None:
246 self.create_success.inc()
247 - if self.overlay.add ( package_info ):
248 + if self.overlay.add (
249 + package_info,
250 + write_after_add=self.write_incremental and self.can_write_overlay
251 + ):
252 self.overlay_added.inc()
253
254 + else:
255 + self.create_fail.inc()
256 +
257 # --- end of _add_to_overlay (...) ---
258
259 def _get_worker ( self, start_now=False, use_threads=True ):
260
261 diff --git a/roverlay/overlay/package.py b/roverlay/overlay/package.py
262 index ceee371..c0b4294 100644
263 --- a/roverlay/overlay/package.py
264 +++ b/roverlay/overlay/package.py
265 @@ -6,9 +6,9 @@ import threading
266 import os
267 import sys
268
269 -from roverlay.metadata import MetadataJob
270 -from roverlay import manifest
271 -from roverlay.packageinfo import PackageInfo
272 +from roverlay import manifest
273 +from roverlay.packageinfo import PackageInfo
274 +from roverlay.overlay.metadata import MetadataJob
275
276 SUPPRESS_EXCEPTIONS = True
277 EBUILD_SUFFIX = '.ebuild'
278 @@ -33,10 +33,16 @@ class PackageDir ( object ):
279 self._metadata = None
280 self.physical_location = directory
281
282 + self._package_for_manifest = None
283 +
284 # used to track changes for this package dir
285 self.modified = False
286 # --- end of __init__ (...) ---
287
288 + def list_versions ( self ):
289 + return self._packages.keys()
290 + # --- end of list_versions (...) ---
291 +
292 def has_manifest ( self ):
293 return os.path.isfile (
294 self.physical_location + os.sep + 'Manifest'
295 @@ -56,6 +62,7 @@ class PackageDir ( object ):
296 # --- end of get_ebuilds (...) ---
297
298 def _scan_ebuilds ( self ):
299 + """Searches for ebuilds in self.physical_location."""
300 elen = len ( EBUILD_SUFFIX )
301
302 for f in os.listdir ( self.physical_location ):
303 @@ -79,11 +86,13 @@ class PackageDir ( object ):
304
305 # --- end of _scan_ebuilds (...) ---
306
307 - def scan ( self ):
308 + def scan ( self, **kw ):
309 + """Scans the filesystem location of this package for existing
310 + ebuilds and adds them.
311 + """
312 for pvr in self._scan_ebuilds():
313 if pvr not in self._packages:
314 - print ( pvr )
315 - p = PackageInfo ( physical=True, pvr=pvr )
316 + p = PackageInfo ( physical_only=True, pvr=pvr )
317 self._packages [ p ['ebuild_verstr'] ] = p
318 # --- end of scan (...) ---
319
320 @@ -92,104 +101,124 @@ class PackageDir ( object ):
321 return len ( self._packages ) == 0
322 # --- end of empty (...) ---
323
324 - def _get_metadata_filepath ( self ):
325 - """Returns the path to the metadata file."""
326 - return os.path.join (
327 - '??' if self.physical_location is None else self.physical_location,
328 - self._metadata.filename
329 - )
330 - # --- end of _get_metadata_filepath (...) ---
331 -
332 def _get_ebuild_filepath ( self, pvr ):
333 """Returns the path to the ebuild file.
334
335 arguments:
336 * pvr -- version number with the revision (${PVR} in ebuilds)
337 """
338 - filename = "%s-%s.ebuild" % ( self.name, pvr )
339 - return os.path.join (
340 - '??' if self.physical_location is None else self.physical_location,
341 - filename
342 + return "{root}{sep}{PN}-{PVR}{EBUILD_SUFFIX}".format (
343 + root=self.physical_location, sep=os.sep,
344 + PN=self.name, PVR=pvr, EBUILD_SUFFIX=EBUILD_SUFFIX
345 )
346 # --- end of _get_ebuild_filepath (...) ---
347
348 - def write ( self, default_header=None ):
349 - """Writes this directory to its (existent!) filesystem location.
350 -
351 - returns: None (implicit)
352 -
353 - raises:
354 - * Exception if no directory assigned
355 - * IOError
356 - """
357 - if self.physical_location is None:
358 - raise Exception ( "cannot write - no directory assigned!" )
359 + def write_incremental ( self, default_header ):
360 + self.write_ebuilds ( header=default_header, overwrite=False )
361 + # --- end of write_incremental (...) ---
362
363 - self._lock.acquire()
364 + def write_metadata ( self, shared_fh=None ):
365 + """Writes metadata for this package."""
366 try:
367 - self._regen_metadata()
368
369 - # mkdir not required here, overlay.Category does this
370 + self._regen_metadata()
371
372 - # write ebuilds
373 - for ver, p_info in self._packages.items():
374 - fh = None
375 - try:
376 - efile = self._get_ebuild_filepath ( ver )
377 + if shared_fh is None:
378 + fh = open (
379 + self.physical_location + os.sep + self._metadata.filename, 'w'
380 + )
381 + else:
382 + fh = shared_fh
383
384 - ebuild = p_info ['ebuild']
385 + self._metadata.write ( fh )
386
387 - fh = open ( efile, 'w' )
388 - fh.write ( default_header )
389 - fh.write ( '\n\n' )
390 - fh.write ( str ( ebuild ) )
391 - fh.write ( '\n' )
392 + except IOError as e:
393
394 - if fh: fh.close()
395 + self.logger.error (
396 + "Failed to write metadata file {}.".format ( mfile )
397 + )
398 + self.logger.exception ( e )
399
400 - # adjust owner/perm? TODO
401 - # chmod 0644 or 0444
402 - # chown 250.250
403 + finally:
404 + if shared_fh is None and 'fh' in locals() and fh:
405 + fh.close()
406 + # --- end of write_metadata (...) ---
407
408 - # this marks the package as 'written to fs'
409 - p_info.set_writeable()
410 - p_info ['ebuild_file'] = efile
411 - p_info.set_readonly()
412 + def write_ebuild ( self, efile, ebuild, header, shared_fh=None ):
413 + """Writes an ebuild.
414
415 - self.logger.info ( "Wrote ebuild %s." % efile )
416 - except IOError as e:
417 - if fh: fh.close()
418 - self.logger.error ( "Couldn't write ebuild %s." % efile )
419 - self.logger.exception ( e )
420 + arguments:
421 + * efile -- file to write
422 + * ebuild -- ebuild object to write (has to have a __str__ method)
423 + * header -- ebuild header to write (^)
424 + * shared_fh -- optional, see write_ebuilds()
425 + """
426 + _success = False
427 + try:
428 + fh = open ( efile, 'w' ) if shared_fh is None else shared_fh
429 + if header is not None:
430 + fh.write ( str ( header ) )
431 + fh.write ( '\n\n' )
432 + fh.write ( str ( ebuild ) )
433 + fh.write ( '\n' )
434 +
435 + # adjust owner/perm? TODO
436 + #if shared_fh is None:
437 + # chmod 0644 or 0444
438 + # chown 250.250
439 + _success = True
440 + except IOError as e:
441 + self.logger.exception ( e )
442 + finally:
443 + if shared_fh is None and 'fh' in locals() and fh:
444 + fh.close()
445
446 - # write metadata
447 - fh = None
448 - try:
449 - mfile = self._get_metadata_filepath()
450 + return _success
451 + # --- end of write_ebuild (...) ---
452
453 - fh = open ( mfile, 'w' )
454 - self._metadata.write ( fh )
455 - if fh: fh.close()
456 + def write_ebuilds ( self, header, shared_fh=None, overwrite=True ):
457 + """Writes all ebuilds.
458
459 - except IOError as e:
460 - if fh: fh.close()
461 - self.logger.error ( "Failed to write metadata at %s." % mfile )
462 - self.logger.exception ( e )
463 + arguments:
464 + * header -- ebuild header
465 + * shared_fh -- if set and not None: don't use own file handles (i.e.
466 + write files), write everything into shared_fh
467 + """
468 + for ver, p_info in self._packages.items():
469 + if not p_info ['physical_only'] and p_info ['ebuild']:
470 + efile = self._get_ebuild_filepath ( ver )
471 +
472 + if not overwrite and efile == p_info ['ebuild_file']:
473 + print ( efile + " exists, skipping write()." )
474 +
475 +
476 + elif self.write_ebuild (
477 + efile, p_info ['ebuild'], header, shared_fh
478 + ):
479 + if shared_fh is None:
480 + # this marks the package as 'written to fs'
481 + p_info.update_now (
482 + ebuild_file=efile,
483 + remove_auto='ebuild_written'
484 + )
485
486 - # FIXME cannot write manifest here when using threads,
487 - # multiprocessed manifest writing is not supported, gives errors like
488 - # 'OSError: [Errno 17] File exists: '/var/cache/edb/dep/tmp/<overlay>'
489 - #self.generate_manifest()
490 + self._package_for_manifest = p_info
491
492 - finally:
493 - self._lock.release()
494 - # --- end of write (...) ---
495 + self.logger.info ( "Wrote ebuild {}.".format ( efile ) )
496 + else:
497 + self.logger.error (
498 + "Couldn't write ebuild {}.".format ( efile )
499 + )
500 + # --- end of write_ebuilds (...) ---
501
502 - def show ( self, stream=sys.stderr, default_header=None ):
503 - """Prints this dir (the ebuilds and the metadata) into a stream.
504 + def write ( self,
505 + default_header=None, write_manifest=True, shared_fh=None
506 + ):
507 + """Writes this directory to its (existent!) filesystem location.
508
509 arguments:
510 - * stream -- stream to use, defaults to sys.stderr
511 + * default_header -- ebuild header to write
512 + * write_manifest -- if set and False: don't write the Manifest file
513
514 returns: None (implicit)
515
516 @@ -198,30 +227,35 @@ class PackageDir ( object ):
517 """
518 self._lock.acquire()
519 try:
520 - self._regen_metadata()
521 + # mkdir not required here, overlay.Category does this
522
523 + # write ebuilds
524 + self.write_ebuilds ( header=default_header, shared_fh=shared_fh )
525
526 - for ver, p_info in self._packages.items():
527 - efile = self._get_ebuild_filepath ( ver )
528 - ebuild = p_info ['ebuild']
529 + # write metadata
530 + self.write_metadata ( shared_fh=shared_fh )
531
532 - stream.write ( "[BEGIN ebuild %s]\n" % efile )
533 + if write_manifest and shared_fh is not None:
534 + self.write_manifest()
535
536 - stream.write ( default_header )
537 - stream.write ( '\n\n' )
538 - stream.write ( str ( ebuild ) )
539 - stream.write ( '\n' )
540 + finally:
541 + self._lock.release()
542 + # --- end of write (...) ---
543
544 - stream.write ( "[END ebuild %s]\n" % efile )
545 + def show ( self, stream=sys.stderr, default_header=None ):
546 + """Prints this dir (the ebuilds and the metadata) into a stream.
547
548 - mfile = self._get_metadata_filepath()
549 + arguments:
550 + * stream -- stream to use, defaults to sys.stderr
551
552 - stream.write ( "[BEGIN %s]\n" % mfile )
553 - self._metadata.write ( stream )
554 - stream.write ( "[END %s]\n" % mfile )
555 + returns: None (implicit)
556
557 - finally:
558 - self._lock.release()
559 + raises:
560 + * IOError
561 + """
562 + self.write (
563 + default_header=default_header, shared_fh=stream, write_manifest=False
564 + )
565 # --- end of show (...) ---
566
567 def _latest_package ( self, pkg_filter=None, use_lock=False ):
568 @@ -267,7 +301,7 @@ class PackageDir ( object ):
569 if shortver in self._packages:
570 self.logger.info (
571 "'{PN}-{PVR}.ebuild' already exists, cannot add it!".format (
572 - { 'PN' : self.name, 'PVR' : shortver }
573 + PN=self.name, PVR=shortver
574 )
575 )
576 return True
577 @@ -282,6 +316,7 @@ class PackageDir ( object ):
578 self._lock.acquire()
579 if not already_exists():
580 self._packages [shortver] = package_info
581 + self.modified = True
582 _success = True
583 finally:
584 self._lock.release()
585 @@ -310,10 +345,9 @@ class PackageDir ( object ):
586 * use_old_metadata -- TODO in metadata
587 """
588 if use_old_metadata or use_all_packages:
589 - raise Exception ( "using >1 package for metadata.xml is TODO!" )
590 + raise Exception ( "using >1 package for metadata.xml is TODO!" )
591
592 - if skip_if_existent and not self._metadata is None:
593 - return
594 + if skip_if_existent and not self._metadata is None: return
595
596 self._lock.acquire()
597 try:
598 @@ -332,19 +366,16 @@ class PackageDir ( object ):
599 self._lock.release()
600 # --- end of generate_metadata (...) ---
601
602 - def generate_manifest ( self ):
603 - """Generates the Manifest file for this package.
604 + def write_manifest ( self ):
605 + """Generates and writes the Manifest file for this package.
606
607 expects: called in self.write(), after writing metadata/ebuilds
608
609 returns: None (implicit)
610
611 raises:
612 - * Exception if not physical
613 * Exception if no ebuild exists
614 """
615 - if self.physical_location is None:
616 - raise Exception ( "no directory assigned." )
617
618 # it should be sufficient to call create_manifest for one ebuild,
619 # choosing the latest one here that exists in self.physical_location.
620 @@ -352,12 +383,12 @@ class PackageDir ( object ):
621 # metadata.xml's full path cannot be used for manifest creation here
622 # 'cause DISTDIR would be unknown
623 #
624 - pkg_info_for_manifest = self._latest_package (
625 - pkg_filter=lambda pkg : not pkg ['ebuild_file'] is None,
626 - use_lock=True
627 - )
628 +# pkg_info_for_manifest = self._latest_package (
629 +# pkg_filter=lambda pkg : pkg ['ebuild_file'] is not None,
630 +# use_lock=True
631 +# )
632
633 - if pkg_info_for_manifest is None:
634 + if self._package_for_manifest is None:
635 # ? FIXME
636 raise Exception (
637 "No ebuild written so far! I really don't know what do to!"
638 @@ -365,6 +396,9 @@ class PackageDir ( object ):
639 else:
640 # TODO: manifest creation interface is single threaded,
641 # may want to 'fix' this later
642 - manifest.create_manifest ( pkg_info_for_manifest, nofail=False )
643 + manifest.create_manifest (
644 + self._package_for_manifest, nofail=False,
645 + #ebuild_file=...
646 + )
647
648 - # --- end of generate_manifest (...) ---
649 + # --- end of write_manifest (...) ---
650
651 diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py
652 index 77036cb..26be02a 100644
653 --- a/roverlay/overlay/root.py
654 +++ b/roverlay/overlay/root.py
655 @@ -16,6 +16,45 @@ DEFAULT_USE_DESC = '\n'.join ( [
656 'R_suggests - install recommended packages'
657 ] )
658
659 +class EbuildHeader ( object ):
660 + def __init__ ( self, default_header ):
661 + self.default_header = default_header
662 + self.eclasses = ()
663 +
664 + self._cached_header = None
665 + # --- end of __init__ (...) ---
666 +
667 + def set_eclasses ( self, eclass_names ):
668 + self.eclasses = eclass_names
669 + # --- end of set_eclasses (...) ---
670 +
671 + def get ( self, use_cached=True ):
672 + if self._cached_header is None or not use_cached:
673 + self._cached_header = self._make()
674 + return self._cached_header
675 + # --- end of get (...) ---
676 +
677 + def _make ( self ):
678 + if self.eclasses:
679 + inherit = 'inherit ' + ' '.join ( self.eclasses )
680 + else:
681 + inherit = None
682 +
683 + # header and inherit is expected and therefore the first condition here
684 + if inherit and self.default_header:
685 + return self.default_header + '\n' + inherit
686 +
687 + elif inherit:
688 + return inherit
689 +
690 + elif self.default_header:
691 + return self.default_header
692 +
693 + else:
694 + return None
695 + # --- end of _make (...) ---
696 +
697 +
698 class Overlay ( object ):
699
700 def __init__ (
701 @@ -24,6 +63,10 @@ class Overlay ( object ):
702 default_category, eclass_files,
703 ebuild_header
704 ):
705 + if directory is None:
706 + raise Exception (
707 + "support for overlays without filesystem location has been dropped"
708 + )
709
710 self.name = name
711 self.logger = logger.getChild ( 'overlay' )
712 @@ -31,27 +74,43 @@ class Overlay ( object ):
713 self.default_category = default_category
714 self.eclass_files = eclass_files
715
716 - self.eclass_names = None
717 self._profiles_dir = self.physical_location + os.sep + 'profiles'
718 self._catlock = threading.Lock()
719 self._categories = dict()
720 - self._default_header = ebuild_header
721 + self._header = EbuildHeader ( ebuild_header )
722 + self._get_header = self._header.get
723 +
724 + # fixme or ignore: calculating eclass names twice,
725 + # once here and another time when calling _init_overlay
726 + self._header.set_eclasses ( tuple (
727 + self._get_eclass_import_info ( only_eclass_names=True )
728 + ) )
729 +
730 + self._incremental_write_lock = threading.Lock()
731 +
732
733
734 #self.scan()
735 #raise Exception ( "^" )
736 # --- end of __init__ (...) ---
737
738 - def scan ( self ):
739 + def scan ( self, **kw ):
740 if os.path.isdir ( self.physical_location ):
741 for cat in self._scan_categories():
742 try:
743 - print ( cat.name )
744 - cat.scan()
745 + cat.scan ( **kw )
746 except Exception as e:
747 self.logger.exception ( e )
748 +# for package in sorted ( self.list_packages() ):
749 +# print ( package )
750 # --- end of scan (...) ---
751
752 + def list_packages ( self ):
753 + for cat in self._categories.values():
754 + for package in cat.list_packages():
755 + yield package
756 + # --- end of list_packages (...) ---
757 +
758 def has_dir ( self, _dir ):
759 return os.path.isdir ( self.physical_location + os.sep + _dir )
760 # --- end of has_category (...) ---
761 @@ -76,8 +135,7 @@ class Overlay ( object ):
762 self._categories [category] = Category (
763 category,
764 self.logger,
765 - None if self.physical_location is None else \
766 - self.physical_location + os.sep + category
767 + self.physical_location + os.sep + category
768 )
769 finally:
770 self._catlock.release()
771 @@ -85,7 +143,7 @@ class Overlay ( object ):
772 return self._categories [category]
773 # --- end of _get_category (...) ---
774
775 - def add ( self, package_info, category=None ):
776 + def add ( self, package_info, write_after_add=False, category=None ):
777 """Adds a package to this overlay.
778
779 arguments:
780 @@ -95,9 +153,17 @@ class Overlay ( object ):
781
782 returns: True if successfully added else False
783 """
784 - return self._get_category (
785 + cat = self._get_category (
786 self.default_category if category is None else category
787 - ) . add ( package_info )
788 + )
789 +
790 + if write_after_add:
791 + util.dodir ( cat.physical_location, mkdir_p=True )
792 + return cat.add (
793 + package_info, write_after_add=True, header = self._get_header()
794 + )
795 + else:
796 + return cat.add ( package_info, write_after_add=False )
797 # --- end of add (...) ---
798
799 def show ( self, **show_kw ):
800 @@ -108,6 +174,9 @@ class Overlay ( object ):
801
802 returns: None (implicit)
803 """
804 + if not self._header.eclasses: self._header.set_eclasses (
805 + tuple ( self._get_eclass_import_info ( only_eclass_names=True ) )
806 + )
807 for cat in self._categories.values():
808 cat.show ( default_header=self._get_header() )
809 # --- end of show (...) ---
810 @@ -130,7 +199,7 @@ class Overlay ( object ):
811 self._init_overlay ( reimport_eclass=True, make_profiles_dir=True )
812
813 for cat in self._categories.values():
814 - if cat.physical_location and not cat.empty():
815 + if not cat.empty():
816 util.dodir ( cat.physical_location )
817 cat.write ( default_header=self._get_header() )
818
819 @@ -145,7 +214,18 @@ class Overlay ( object ):
820 package infos.
821 * This has to be thread safe
822 """
823 - raise Exception ( "method stub" )
824 + if not self._incremental_write_lock.acquire():
825 + # another incremental write is running, drop this request
826 + return
827 +
828 + try:
829 + util.dodir ( self.physical_location )
830 + cats = tuple ( self._categories.values() )
831 + for cat in cats:
832 + util.dodir ( cat.physical_location )
833 + cat.write_incremental ( default_header=self._get_header() )
834 + finally:
835 + self._incremental_write_lock.release()
836 # --- end of write_incremental (...) ---
837
838 def generate_metadata ( self, **metadata_kw ):
839 @@ -199,7 +279,7 @@ class Overlay ( object ):
840 """
841 fh = None
842 try:
843 - fh = open ( os.path.join ( self._profiles_dir, filename ), 'w' )
844 + fh = open ( self._profiles_dir + os.sep + filename, 'w' )
845 if to_write:
846 # else touch file
847 fh.write ( to_write )
848 @@ -286,19 +366,19 @@ class Overlay ( object ):
849
850 if self.eclass_files:
851 # import eclass files
852 - eclass_dir = os.path.join ( self.physical_location, 'eclass' )
853 + eclass_dir = self.physical_location + os.sep + 'eclass'
854 try:
855 eclass_names = list()
856 util.dodir ( eclass_dir )
857
858 for destname, eclass in self._get_eclass_import_info ( False ):
859 - dest = os.path.join ( eclass_dir, destname + '.eclass' )
860 + dest = eclass_dir + os.sep + destname + '.eclass'
861 if reimport_eclass or not os.path.isfile ( dest ):
862 shutil.copyfile ( eclass, dest )
863
864 eclass_names.append ( destname )
865
866 - self.eclass_names = frozenset ( eclass_names )
867 + self._header.set_eclasses ( frozenset ( eclass_names ) )
868
869 except Exception as e:
870 self.logger.critical ( "Cannot import eclass files!" )
871 @@ -314,12 +394,8 @@ class Overlay ( object ):
872 * make_profiles_dir -- if True: create the profiles/ dir now
873
874 raises:
875 - * Exception if no physical location assigned
876 * IOError
877 """
878 - if self.physical_location is None:
879 - raise Exception ( "no directory assigned." )
880 -
881 try:
882 # mkdir overlay root
883 util.dodir ( self.physical_location, mkdir_p=True )
884 @@ -335,28 +411,3 @@ class Overlay ( object ):
885 self.logger.critical ( "^failed to init overlay" )
886 raise
887 # --- end of _init_overlay (...) ---
888 -
889 - def _get_header ( self ):
890 - """Returns the ebuild header (including inherit <eclasses>)."""
891 - if self.eclass_names is None:
892 - # writing is possibly disabled since eclass files have not been
893 - # imported (or show() used before write())
894 - inherit = ' '.join ( self._get_eclass_import_info ( True ) )
895 - else:
896 - inherit = ' '.join ( self.eclass_names )
897 -
898 - inherit = "inherit " + inherit if inherit else None
899 -
900 - # header and inherit is expected and therefore the first condition here
901 - if inherit and self._default_header:
902 - return '\n'.join (( self._default_header, '', inherit ))
903 -
904 - elif inherit:
905 - return inherit
906 -
907 - elif self._default_header:
908 - return self._default_header
909 -
910 - else:
911 - return None
912 - # --- end of _get_header (...) ---
913
914 diff --git a/roverlay/packageinfo.py b/roverlay/packageinfo.py
915 index 96cdb37..a2910c5 100644
916 --- a/roverlay/packageinfo.py
917 +++ b/roverlay/packageinfo.py
918 @@ -157,8 +157,8 @@ class PackageInfo ( object ):
919 # 'has_suggests' not in self._info -> assume False
920 return False
921
922 - elif key_low == 'physical':
923 - # 'physical' not in self._info -> assume False
924 + elif key_low == 'physical_only':
925 + # 'physical_only' not in self._info -> assume False
926 return False
927
928 elif key_low == 'src_uri':
929 @@ -240,8 +240,11 @@ class PackageInfo ( object ):
930 elif key == 'ebuild':
931 self ['ebuild'] = value
932
933 - elif key == 'physical':
934 - self ['physical'] = value
935 + elif key == 'ebuild_file':
936 + self ['ebuild_file'] = value
937 +
938 + elif key == 'physical_only':
939 + self ['physical_only'] = value
940
941 elif key == 'pvr':
942 self._use_pvr ( value )
943 @@ -266,7 +269,7 @@ class PackageInfo ( object ):
944 self._remove_auto ( value )
945
946 else:
947 - LOGGER.error ( "unknown info key %s!" % key )
948 + LOGGER.error ( "unknown info key {}!".format ( key ) )
949
950 self._update_lock.release()
951 # --- end of update (**kw) ---
952 @@ -288,8 +291,8 @@ class PackageInfo ( object ):
953
954 if not sepa:
955 # file name unexpected, tarball extraction will (probably) fail
956 - LOGGER.error ( "unexpected file name '%s'." % filename )
957 - raise Exception ( "cannot use file '%s'." % filename )
958 + LOGGER.error ( "unexpected file name {!r}.".format ( filename ) )
959 + raise Exception ( "cannot use file {!r}.".format ( filename ) )
960 return
961
962 version_str = PackageInfo.EBUILDVER_REGEX.sub ( '.', package_version )
963 @@ -338,7 +341,7 @@ class PackageInfo ( object ):
964 after entering status 'ebuild_status' (like ebuild in overlay and
965 written -> don't need the ebuild string etc.)
966 """
967 - raise Exception ( "method stub" )
968 + print ( "PackageInfo._remove_auto: method stub, request ignored." )
969 # --- end of _remove_auto (...) ---
970
971 def _use_filepath ( self, _filepath ):
972 @@ -356,7 +359,8 @@ class PackageInfo ( object ):
973 # --- end of _use_filepath (...) ---
974
975 def __str__ ( self ):
976 - return "<PackageInfo for %s>" % self.get (
977 - 'package_file', fallback_value='[unknown file]', do_fallback=True
978 - )
979 + return "<PackageInfo for {pkg}>".format (
980 + pkg=self.get (
981 + 'package_file', fallback_value='[unknown file]', do_fallback=True
982 + ) )
983 # --- end of __str__ (...) ---