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__ (...) --- |