1 |
commit: ee870b2a6f8a3df4a794dc82fda1bd63a4f6d147 |
2 |
Author: André Erdmann <dywi <AT> mailerd <DOT> de> |
3 |
AuthorDate: Tue Jul 24 15:39:07 2012 +0000 |
4 |
Commit: André Erdmann <dywi <AT> mailerd <DOT> de> |
5 |
CommitDate: Tue Jul 24 15:39:07 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=ee870b2a |
7 |
|
8 |
incremental overlay writing: merge write() funcs |
9 |
|
10 |
* one write function for overlay/{root,category,package}.py |
11 |
that replaces write_incremental, write, finalize_write_incremental |
12 |
|
13 |
* also fixed some issues (proper PackageDir cleanup, make threaded writing |
14 |
controllable) |
15 |
|
16 |
geändert: roverlay/overlay/category.py |
17 |
geändert: roverlay/overlay/creator.py |
18 |
geändert: roverlay/overlay/manifest/__init__.py |
19 |
geändert: roverlay/overlay/package.py |
20 |
geändert: roverlay/overlay/root.py |
21 |
|
22 |
--- |
23 |
roverlay/overlay/category.py | 141 +++++++---------- |
24 |
roverlay/overlay/creator.py | 29 +--- |
25 |
roverlay/overlay/manifest/__init__.py | 4 +- |
26 |
roverlay/overlay/package.py | 278 +++++++++++++++++++++------------ |
27 |
roverlay/overlay/root.py | 96 +++--------- |
28 |
5 files changed, 266 insertions(+), 282 deletions(-) |
29 |
|
30 |
diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py |
31 |
index 33149c4..fab6373 100644 |
32 |
--- a/roverlay/overlay/category.py |
33 |
+++ b/roverlay/overlay/category.py |
34 |
@@ -12,8 +12,6 @@ except ImportError: |
35 |
|
36 |
from roverlay.overlay.package import PackageDir |
37 |
|
38 |
-import roverlay.util |
39 |
- |
40 |
class Category ( object ): |
41 |
|
42 |
WRITE_JOBCOUNT = 3 |
43 |
@@ -49,8 +47,6 @@ class Category ( object ): |
44 |
incremental = self.incremental |
45 |
) |
46 |
self._subdirs [pkg_name] = newpkg |
47 |
- if self.incremental: |
48 |
- roverlay.util.dodir ( newpkg.physical_location ) |
49 |
finally: |
50 |
self._lock.release() |
51 |
|
52 |
@@ -76,26 +72,6 @@ class Category ( object ): |
53 |
not False in ( d.empty() for d in self._subdirs.values() ) |
54 |
# --- end of empty (...) --- |
55 |
|
56 |
- def finalize_write_incremental ( self ): |
57 |
- for subdir in self._subdirs.values(): |
58 |
- if subdir.modified: |
59 |
- subdir.write_incremental() |
60 |
- subdir.finalize_write_incremental() |
61 |
- # --- end of finalize_write_incremental (...) --- |
62 |
- |
63 |
- def generate_metadata ( self, **metadata_kw ): |
64 |
- """Generates metadata for all packages in this category. |
65 |
- Metadata are automatically generated when calling write(). |
66 |
- |
67 |
- arguments: |
68 |
- * **metadata_kw -- see PackageDir.generate_metadata(...) |
69 |
- |
70 |
- returns: None (implicit) |
71 |
- """ |
72 |
- for package in self._subdirs.values(): |
73 |
- package.generate_metadata ( **metadata_kw ) |
74 |
- # --- end of generate_metadata (...) --- |
75 |
- |
76 |
def has ( self, subdir ): |
77 |
return subdir in self._subdirs |
78 |
# --- end of has (...) --- |
79 |
@@ -104,13 +80,6 @@ class Category ( object ): |
80 |
return os.path.isdir ( self.physical_location + os.sep + _dir ) |
81 |
# --- end of has_category (...) --- |
82 |
|
83 |
- def keep_nth_latest ( self, *args, **kwargs ): |
84 |
- """See package.py:PackageDir:keep_nth_latest.""" |
85 |
- for subdir in self._subdirs.values(): |
86 |
- subdir.keep_nth_latest ( *args, **kwargs ) |
87 |
- subdir.fs_cleanup() |
88 |
- # --- end of keep_nth_latest (...) --- |
89 |
- |
90 |
def list_packages ( self, for_deprules=False ): |
91 |
"""Lists all packages in this category. |
92 |
Yields <category>/<package name> or a dict (see for_deprules below). |
93 |
@@ -130,6 +99,14 @@ class Category ( object ): |
94 |
yield self.name + os.sep + name |
95 |
# --- end of list_packages (...) --- |
96 |
|
97 |
+ def remove_empty ( self ): |
98 |
+ """This removes all empty PackageDirs.""" |
99 |
+ with self._lock: |
100 |
+ for key in tuple ( self._subdirs.keys() ): |
101 |
+ if self._subdirs [key].check_empty(): |
102 |
+ del self._subdirs [key] |
103 |
+ # --- end of remove_empty (...) --- |
104 |
+ |
105 |
def scan ( self, **kw ): |
106 |
"""Scans this category for existing ebuilds.""" |
107 |
for subdir in os.listdir ( self.physical_location ): |
108 |
@@ -146,7 +123,7 @@ class Category ( object ): |
109 |
package.show ( **show_kw ) |
110 |
# --- end of show (...) --- |
111 |
|
112 |
- def write ( self, **write_kw ): |
113 |
+ def write ( self, overwrite_ebuilds, keep_n_ebuilds, cautious ): |
114 |
"""Writes this category to its filesystem location. |
115 |
|
116 |
returns: None (implicit) |
117 |
@@ -156,75 +133,75 @@ class Category ( object ): |
118 |
|
119 |
arguments: |
120 |
* q -- queue |
121 |
- * write_kw -- |
122 |
+ * write_kw -- keywords for write(...) |
123 |
""" |
124 |
try: |
125 |
while not q.empty(): |
126 |
- pkg = q.get_nowait() |
127 |
- pkg.write ( write_manifest=False, **write_kw ) |
128 |
- |
129 |
+ try: |
130 |
+ pkg = q.get_nowait() |
131 |
+ # remove manifest writing from threaded writing since it's |
132 |
+ # single-threaded |
133 |
+ pkg.write ( write_manifest=False, **write_kw ) |
134 |
+ #except ( Exception, KeyboardInterrupt ) as e: |
135 |
+ except Exception as e: |
136 |
+ # FIXME: reintroduce RERAISE |
137 |
+ self.logger.exception ( e ) |
138 |
except queue.Empty: |
139 |
pass |
140 |
- except ( Exception, KeyboardInterrupt ) as e: |
141 |
- self.RERAISE_EXCEPTION = e |
142 |
# --- end of run_write_queue (...) --- |
143 |
|
144 |
+ if len ( self._subdirs ) == 0: return |
145 |
+ |
146 |
+ # determine write keyword args |
147 |
+ write_kwargs = dict ( |
148 |
+ overwrite_ebuilds = overwrite_ebuilds, |
149 |
+ keep_n_ebuilds = keep_n_ebuilds, |
150 |
+ cautious = cautious, |
151 |
+ ) |
152 |
+ |
153 |
+ # start writing: |
154 |
+ |
155 |
max_jobs = self.__class__.WRITE_JOBCOUNT |
156 |
|
157 |
- # todo len.. > 3: what's an reasonable number of min package dirs to |
158 |
- # start threaded writing? |
159 |
- if max_jobs > 1 and len ( self._subdirs ) > 3: |
160 |
+ # FIXME/TODO: what's an reasonable number of min package dirs to |
161 |
+ # start threaded writing? |
162 |
+ # Ignoring it for now (and expecting enough pkg dirs) |
163 |
+ if max_jobs > 1: |
164 |
|
165 |
- # writing 1..self.__class__.WRITE_JOBCOUNT package dirs at once |
166 |
+ # writing <=max_jobs package dirs at once |
167 |
|
168 |
- modified_packages = tuple ( |
169 |
- p for p in self._subdirs.values() if p.modified |
170 |
- ) |
171 |
- if len ( modified_packages ) > 0: |
172 |
- write_queue = queue.Queue() |
173 |
- for package in modified_packages: |
174 |
- roverlay.util.dodir ( package.physical_location ) |
175 |
- write_queue.put_nowait ( package ) |
176 |
+ # don't create more workers than write jobs available |
177 |
+ max_jobs = min ( max_jobs, len ( self._subdirs ) ) |
178 |
|
179 |
- workers = ( |
180 |
- threading.Thread ( |
181 |
- target=run_write_queue, |
182 |
- args=( write_queue, write_kw ) |
183 |
- ) for n in range ( max_jobs ) |
184 |
- ) |
185 |
+ write_queue = queue.Queue() |
186 |
+ for package in self._subdirs.values(): |
187 |
+ write_queue.put_nowait ( package ) |
188 |
|
189 |
- for w in workers: w.start() |
190 |
- for w in workers: w.join() |
191 |
+ workers = frozenset ( |
192 |
+ threading.Thread ( |
193 |
+ target=run_write_queue, |
194 |
+ args=( write_queue, write_kwargs ) |
195 |
+ ) for n in range ( max_jobs ) |
196 |
+ ) |
197 |
+ |
198 |
+ for w in workers: w.start() |
199 |
+ for w in workers: w.join() |
200 |
|
201 |
- if hasattr ( self, 'RERAISE_EXCEPTION' ): |
202 |
- raise self.RERAISE_EXCEPTION |
203 |
+ self.remove_empty() |
204 |
|
205 |
- # write manifest files |
206 |
- for package in modified_packages: |
207 |
- package.write_manifest() |
208 |
+ # write manifest files |
209 |
+ # fixme: debug print |
210 |
+ #self.logger.info ( "Writing Manifest files for {}".format ( name ) ) |
211 |
+ print ( "Writing Manifest files ..." ) |
212 |
+ for package in self._subdirs.values(): |
213 |
+ package.write_manifest ( ignore_empty=True ) |
214 |
|
215 |
else: |
216 |
for package in self._subdirs.values(): |
217 |
- if package.modified: |
218 |
- roverlay.util.dodir ( package.physical_location ) |
219 |
- package.write ( write_manifest=True, **write_kw ) |
220 |
- # --- end of write (...) --- |
221 |
+ package.write ( **write_kwargs ) |
222 |
|
223 |
- def write_incremental ( self, **write_kw ): |
224 |
- """Writes this category incrementally.""" |
225 |
- try: |
226 |
- with self._lock: |
227 |
- # new package dirs could be added during overlay writing, |
228 |
- # so collect the list of package dirs before iterating over it |
229 |
- subdirs = tuple ( self._subdirs.values() ) |
230 |
- |
231 |
- for subdir in subdirs: |
232 |
- if subdir.modified: |
233 |
- roverlay.util.dodir ( subdir.physical_location ) |
234 |
- subdir.write_incremental ( **write_kw ) |
235 |
- except Exception as e: |
236 |
- self.logger.exception ( e ) |
237 |
- # --- end of write_incremental (...) --- |
238 |
+ self.remove_empty() |
239 |
+ # --- end of write (...) --- |
240 |
|
241 |
def write_manifest ( self, **manifest_kw ): |
242 |
"""Generates Manifest files for all packages in this category. |
243 |
|
244 |
diff --git a/roverlay/overlay/creator.py b/roverlay/overlay/creator.py |
245 |
index 9de063c..d12e0a5 100644 |
246 |
--- a/roverlay/overlay/creator.py |
247 |
+++ b/roverlay/overlay/creator.py |
248 |
@@ -214,32 +214,15 @@ class OverlayCreator ( object ): |
249 |
self.package_added.inc() |
250 |
# --- end of add_package (...) --- |
251 |
|
252 |
- def add_package_file ( self, package_file ): |
253 |
- """Adds a single R package.""" |
254 |
- raise Exception ( "to be removed" ) |
255 |
- self._pkg_queue.put ( PackageInfo ( filepath=package_file ) ) |
256 |
- self.package_added.inc() |
257 |
- # --- end of add_package_file (...) --- |
258 |
- |
259 |
- def add_package_files ( self, *package_files ): |
260 |
- """Adds multiple R packages.""" |
261 |
- raise Exception ( "to be removed" ) |
262 |
- for p in package_files: self.add_package_file ( p ) |
263 |
- self.package_added.inc() |
264 |
- # --- end of add_package_files (...) --- |
265 |
- |
266 |
def write_overlay ( self ): |
267 |
"""Writes the overlay. |
268 |
|
269 |
arguments: |
270 |
""" |
271 |
if self.can_write_overlay: |
272 |
- if self.write_incremental: |
273 |
- self.overlay.finalize_write_incremental() |
274 |
- else: |
275 |
- start = time.time() |
276 |
- self.overlay.write() |
277 |
- self._timestamp ( "overlay written", start ) |
278 |
+ start = time.time() |
279 |
+ self.overlay.write() |
280 |
+ self._timestamp ( "overlay written", start ) |
281 |
else: |
282 |
self.logger.warning ( "Not allowed to write overlay!" ) |
283 |
# --- end of write_overlay (...) --- |
284 |
@@ -304,7 +287,6 @@ class OverlayCreator ( object ): |
285 |
|
286 |
self._close_workers() |
287 |
close_resolver() |
288 |
- self.overlay.keep_nth_latest ( n=1 ) |
289 |
self.closed = True |
290 |
# --- end of close (...) --- |
291 |
|
292 |
@@ -429,8 +411,9 @@ class OverlayCreator ( object ): |
293 |
if self.NUMTHREADS > 0: |
294 |
start = time.time() |
295 |
self.logger.warning ( |
296 |
- "Running in concurrent mode with %i threads." % self.NUMTHREADS |
297 |
- ) |
298 |
+ "Running in concurrent mode with {num} threads.".format ( |
299 |
+ num=self.NUMTHREADS |
300 |
+ ) ) |
301 |
self._workers = frozenset ( |
302 |
self._get_worker ( start_now=True ) \ |
303 |
for n in range ( self.NUMTHREADS ) |
304 |
|
305 |
diff --git a/roverlay/overlay/manifest/__init__.py b/roverlay/overlay/manifest/__init__.py |
306 |
index 9a49c7f..d9f9c0a 100644 |
307 |
--- a/roverlay/overlay/manifest/__init__.py |
308 |
+++ b/roverlay/overlay/manifest/__init__.py |
309 |
@@ -11,8 +11,6 @@ _manifest_creation = helpers.ExternalManifestCreation() |
310 |
# for one directory/overlay |
311 |
_manifest_lock = threading.Lock() |
312 |
|
313 |
- |
314 |
- |
315 |
def create_manifest ( package_info_list, nofail=False ): |
316 |
"""Creates a Manifest for package_info, using the <<best>> implementation |
317 |
available. |
318 |
@@ -35,4 +33,6 @@ def create_manifest ( package_info_list, nofail=False ): |
319 |
raise |
320 |
finally: |
321 |
_manifest_lock.release() |
322 |
+ |
323 |
+ return ret |
324 |
# --- end of create_manifest (...) --- |
325 |
|
326 |
diff --git a/roverlay/overlay/package.py b/roverlay/overlay/package.py |
327 |
index a2e4d9b..d2662bd 100644 |
328 |
--- a/roverlay/overlay/package.py |
329 |
+++ b/roverlay/overlay/package.py |
330 |
@@ -120,6 +120,21 @@ class PackageDir ( object ): |
331 |
return False |
332 |
# --- end of add (...) --- |
333 |
|
334 |
+ def check_empty ( self ): |
335 |
+ """Similar to empty(), |
336 |
+ but also removes the directory of this PackageDir. |
337 |
+ """ |
338 |
+ if len ( self._packages ) == 0: |
339 |
+ if os.path.isdir ( self.physical_location ): |
340 |
+ try: |
341 |
+ os.rmdir ( self.physical_location ) |
342 |
+ except Exception as e: |
343 |
+ self.logger.exception ( e ) |
344 |
+ return True |
345 |
+ else: |
346 |
+ return False |
347 |
+ # --- end of check_empty (...) --- |
348 |
+ |
349 |
def empty ( self ): |
350 |
"""Returns True if no ebuilds stored, else False. |
351 |
Note that "not empty" doesn't mean "has ebuilds to write" or "has |
352 |
@@ -129,25 +144,6 @@ class PackageDir ( object ): |
353 |
return len ( self._packages ) == 0 |
354 |
# --- end of empty (...) --- |
355 |
|
356 |
- def finalize_write_incremental ( self ): |
357 |
- """Method that finalizes incremental writing, i.e. write outstanding |
358 |
- ebuilds and write metadata.xml, Manifest. |
359 |
- """ |
360 |
- with self._lock: |
361 |
- if self.has_ebuilds(): |
362 |
- if self.modified: |
363 |
- self.write_ebuilds ( overwrite=False ) |
364 |
- if self._need_metadata: |
365 |
- self.write_metadata() |
366 |
- if self._need_manifest: |
367 |
- self.write_manifest() |
368 |
- else: |
369 |
- self.logger.critical ( |
370 |
- "<<todo>>: please clean up this dir: {}.".format ( |
371 |
- self.physical_location |
372 |
- ) ) |
373 |
- # --- end of finalize_write_incremental (...) --- |
374 |
- |
375 |
def fs_cleanup ( self ): |
376 |
"""Cleans up the filesystem location of this package dir. |
377 |
To be called after keep_nth_latest, calls finalize_write_incremental(). |
378 |
@@ -158,11 +154,9 @@ class PackageDir ( object ): |
379 |
# --- end of rmtree_error (...) --- |
380 |
|
381 |
with self._lock: |
382 |
- if self.has_ebuilds(): |
383 |
- # !!! FIXME this doesn't work if no ebuilds written, but |
384 |
- # old ones removed -> DISTDIR unknown during Manifest creation |
385 |
- self.finalize_write_incremental() |
386 |
- elif os.path.isdir ( self.physical_location ): |
387 |
+ if os.path.isdir ( self.physical_location ) \ |
388 |
+ and not self.has_ebuilds() \ |
389 |
+ : |
390 |
# destroy self.physical_location |
391 |
shutil.rmtree ( self.physical_location, onerror=rmtree_error ) |
392 |
# --- end of fs_cleanup (...) --- |
393 |
@@ -193,53 +187,58 @@ class PackageDir ( object ): |
394 |
arguments: |
395 |
* n -- # of packages/ebuilds to keep |
396 |
* cautious -- if True: be extra careful, verify that ebuilds exist |
397 |
+ as file; note that this will ignore all |
398 |
+ ebuilds that haven't been written to the file- |
399 |
+ system yet (which implies an extra overhead, |
400 |
+ you'll have to write all ebuilds first) |
401 |
""" |
402 |
- |
403 |
- # create the list of packages to iterate over, |
404 |
- # * package has to have an ebuild_file |
405 |
- # * sort them by version in reverse order (latest package gets index 0) |
406 |
+ def is_ebuild_cautious ( p_tuple ): |
407 |
+ # package has to have an ebuild_file that exists |
408 |
+ efile = p_tuple [1] ['ebuild_file' ] |
409 |
+ if efile is not None: |
410 |
+ return os.path.isfile ( efile ) |
411 |
+ else: |
412 |
+ return False |
413 |
+ # --- end of is_ebuild_cautious (...) --- |
414 |
+ |
415 |
+ def is_ebuild ( p_tuple ): |
416 |
+ # package has to have an ebuild_file or an ebuild entry |
417 |
+ return ( |
418 |
+ p_tuple [1] ['ebuild_file'] or p_tuple [1] ['ebuild'] |
419 |
+ ) is not None |
420 |
+ # --- end of is_ebuild (...) --- |
421 |
+ |
422 |
+ # create the list of packages to iterate over (cautious/non-cautious), |
423 |
+ # sort them by version in reverse order |
424 |
packages = reversed ( sorted ( |
425 |
filter ( |
426 |
- lambda p : p [1] ['ebuild_file'] is not None, |
427 |
- self._packages.items() |
428 |
+ function=is_ebuild if not cautious else is_ebuild_cautious, |
429 |
+ iterable=self._packages.items() |
430 |
), |
431 |
key=lambda p : p [1] ['version'] |
432 |
) ) |
433 |
|
434 |
+ if n < 1: |
435 |
+ raise Exception ( "Must keep more than zero ebuilds." ) |
436 |
+ |
437 |
kept = 0 |
438 |
ecount = 0 |
439 |
- if not cautious: |
440 |
- # could use a slice here, too |
441 |
- for pvr, pkg in packages: |
442 |
- ecount += 1 |
443 |
- if kept < n: |
444 |
- printself.logger.debug ( "Keeping {pvr}.".format ( pvr=pvr ) ) |
445 |
- kept += 1 |
446 |
- else: |
447 |
- self.logger.debug ( "Removing {pvr}.".format ( pvr=pvr ) ) |
448 |
- self.purge_package ( pvr ) |
449 |
- else: |
450 |
- for pvr, pkg in packages: |
451 |
- ecount += 1 |
452 |
- if os.path.isfile ( pkg ['ebuild_file'] ): |
453 |
- if kept < n: |
454 |
- self.logger.debug ( "Keeping {pvr}.".format ( pvr=pvr ) ) |
455 |
- kept += 1 |
456 |
- else: |
457 |
- self.logger.debug ( "Removing {pvr}.".format ( pvr=pvr ) ) |
458 |
- self.purge_package ( pvr ) |
459 |
- else: |
460 |
- self.logger.error ( |
461 |
- "{efile} is assumed to exist as file but doesn't!".format ( |
462 |
- efile=pkg ['ebuild_file'] |
463 |
- ) ) |
464 |
|
465 |
- # FIXME/IGNORE: this doesn't count inexistent files as kept |
466 |
+ for pvr, pkg in packages: |
467 |
+ ecount += 1 |
468 |
+ if kept < n: |
469 |
+ self.logger.debug ( "Keeping {pvr}.".format ( pvr=pvr ) ) |
470 |
+ kept += 1 |
471 |
+ else: |
472 |
+ self.logger.debug ( "Removing {pvr}.".format ( pvr=pvr ) ) |
473 |
+ self.purge_package ( pvr ) |
474 |
+ |
475 |
self.logger.debug ( |
476 |
"Kept {kept}/{total} ebuilds.".format ( kept=kept, total=ecount ) |
477 |
) |
478 |
|
479 |
# FIXME: Manifest is now invalid and dir could be "empty" (no ebuilds) |
480 |
+ # FIXME: force metadata regeneration |
481 |
# --- end of keep_nth_latest (...) --- |
482 |
|
483 |
def list_versions ( self ): |
484 |
@@ -252,7 +251,8 @@ class PackageDir ( object ): |
485 |
self._need_metadata = True |
486 |
self.modified = True |
487 |
if self.runtime_incremental: |
488 |
- return self.write_incremental() |
489 |
+ with self._lock: |
490 |
+ return self.write_ebuilds ( overwrite=False ) |
491 |
else: |
492 |
return True |
493 |
# --- end of new_ebuild (...) --- |
494 |
@@ -325,48 +325,104 @@ class PackageDir ( object ): |
495 |
arguments: |
496 |
* stream -- stream to use, defaults to sys.stderr |
497 |
|
498 |
- returns: None (implicit) |
499 |
+ returns: True |
500 |
|
501 |
raises: |
502 |
- * IOError |
503 |
+ * passes all exceptions (IOError, ..) |
504 |
""" |
505 |
- return self.write ( shared_fh=stream ) |
506 |
+ self.write_ebuilds ( overwrite=True, shared_fh=stream ) |
507 |
+ self.write_metadata ( shared_fh=stream ) |
508 |
+ return True |
509 |
# --- end of show (...) --- |
510 |
|
511 |
+ def virtual_cleanup ( self ): |
512 |
+ """Removes all PackageInfos from this structure that don't have an |
513 |
+ 'ebuild_file' entry. |
514 |
+ """ |
515 |
+ with self._lock: |
516 |
+ # keyset may change during this method |
517 |
+ for pvr in tuple ( self._packages.keys() ): |
518 |
+ if self._packages [pvr] ['ebuild_file'] is None: |
519 |
+ del self._packages [pvr] |
520 |
+ # -- lock |
521 |
+ # --- end of virtual_cleanup (...) --- |
522 |
+ |
523 |
def write ( self, |
524 |
- shared_fh=None, overwrite_ebuilds=True, |
525 |
- write_ebuilds=True, write_manifest=True, write_metadata=True |
526 |
+ overwrite_ebuilds=False, |
527 |
+ write_ebuilds=True, write_manifest=True, write_metadata=True, |
528 |
+ cleanup=True, keep_n_ebuilds=None, cautious=True |
529 |
): |
530 |
"""Writes this directory to its (existent!) filesystem location. |
531 |
|
532 |
arguments: |
533 |
- * shared_fh -- if set and not None: write everyting into <fh> |
534 |
* write_ebuilds -- if set and False: don't write ebuilds |
535 |
* write_manifest -- if set and False: don't write the Manifest file |
536 |
* write_metadata -- if set and False: don't write the metadata file |
537 |
- * overwrite_ebuilds -- if set and False: don't overwrite ebuilds |
538 |
- |
539 |
- returns: None (implicit) |
540 |
+ * overwrite_ebuilds -- whether to overwrite ebuilds, |
541 |
+ None means autodetect, enable overwriting |
542 |
+ if not modified since last write |
543 |
+ Defaults to False |
544 |
+ * cleanup -- clean up after writing |
545 |
+ Defaults to True |
546 |
+ * keep_n_ebuilds -- # of ebuilds to keep (remove all others), |
547 |
+ Defaults to None (disable) and implies cleanup |
548 |
+ * cautious -- be cautious when keeping the nth latest ebuilds, |
549 |
+ this has some overhead |
550 |
+ Defaults to True |
551 |
+ |
552 |
+ returns: success (True/False) |
553 |
|
554 |
raises: |
555 |
- * IOError (?) |
556 |
+ * passes IOError |
557 |
""" |
558 |
- with self._lock: |
559 |
- # mkdir not required here, overlay.Category does this |
560 |
+ # NOTE, replaces: |
561 |
+ # * old write: overwrite_ebuilds=True |
562 |
+ # * finalize_write_incremental : no extra args |
563 |
+ # * write_incremental : write_manifest=False, write_metadata=False, |
564 |
+ # cleanup=False (or use write_ebuilds) |
565 |
+ # BREAKS: show(), which has its own method/function now |
566 |
|
567 |
- # write ebuilds |
568 |
- if write_ebuilds: |
569 |
- self.write_ebuilds ( |
570 |
- overwrite=overwrite_ebuilds, shared_fh=shared_fh |
571 |
- ) |
572 |
+ cleanup = cleanup or ( keep_n_ebuilds is not None ) |
573 |
|
574 |
- # write metadata |
575 |
- if write_metadata: |
576 |
- self.write_metadata ( shared_fh=shared_fh ) |
577 |
+ success = True |
578 |
+ with self._lock: |
579 |
+ if self.has_ebuilds(): |
580 |
+ # not cautious: remove ebuilds before writing them |
581 |
+ if not cautious and keep_n_ebuilds is not None: |
582 |
+ self.keep_nth_latest ( n=keep_n_ebuilds, cautious=False ) |
583 |
+ |
584 |
+ # write ebuilds |
585 |
+ if self.modified and write_ebuilds: |
586 |
+ success = self.write_ebuilds ( |
587 |
+ # None ~ not modified |
588 |
+ overwrite = overwrite_ebuilds \ |
589 |
+ if overwrite_ebuilds is not None \ |
590 |
+ else not self.modified |
591 |
+ ) |
592 |
|
593 |
- # write manifest (only if shared_fh is None) |
594 |
- if write_manifest and shared_fh is None: |
595 |
- self.write_manifest() |
596 |
+ # cautious: remove ebuilds after writing them |
597 |
+ if cautious and keep_n_ebuilds is not None: |
598 |
+ self.keep_nth_latest ( n=keep_n_ebuilds, cautious=True ) |
599 |
+ |
600 |
+ # write metadata |
601 |
+ if self._need_metadata and write_metadata: |
602 |
+ # don't mess around with short-circuit bool evaluation |
603 |
+ if not self.write_metadata(): |
604 |
+ success = False |
605 |
+ |
606 |
+ # write manifest (only if shared_fh is None) |
607 |
+ if self._need_manifest and write_manifest: |
608 |
+ if not self.write_manifest(): |
609 |
+ success = False |
610 |
+ # -- has_ebuilds? |
611 |
+ |
612 |
+ if cleanup: |
613 |
+ self.virtual_cleanup() |
614 |
+ self.fs_cleanup() |
615 |
+ |
616 |
+ # FIXME / TODO call fs_cleanup |
617 |
+ # -- lock |
618 |
+ return success |
619 |
# --- end of write (...) --- |
620 |
|
621 |
def write_ebuilds ( self, overwrite, shared_fh=None ): |
622 |
@@ -390,7 +446,6 @@ class PackageDir ( object ): |
623 |
""" |
624 |
_success = False |
625 |
try: |
626 |
- util.dodir ( self.physical_location ) |
627 |
fh = open ( efile, 'w' ) if shared_fh is None else shared_fh |
628 |
if ebuild_header is not None: |
629 |
fh.write ( str ( ebuild_header ) ) |
630 |
@@ -426,7 +481,14 @@ class PackageDir ( object ): |
631 |
|
632 |
all_ebuilds_written = True |
633 |
|
634 |
+ # don't call dodir if shared_fh is set |
635 |
+ hasdir = bool ( shared_fh is not None ) |
636 |
+ |
637 |
for efile, p_info in ebuilds_to_write(): |
638 |
+ if not hasdir: |
639 |
+ util.dodir ( self.physical_location, mkdir_p=True ) |
640 |
+ hasdir = True |
641 |
+ |
642 |
if write_ebuild ( efile, p_info ['ebuild'] ): |
643 |
self._need_manifest = True |
644 |
|
645 |
@@ -452,17 +514,12 @@ class PackageDir ( object ): |
646 |
return all_ebuilds_written |
647 |
# --- end of write_ebuilds (...) --- |
648 |
|
649 |
- def write_incremental ( self ): |
650 |
- with self._lock: |
651 |
- return self.write_ebuilds ( overwrite=False ) |
652 |
- # --- end of write_incremental (...) --- |
653 |
- |
654 |
- def write_manifest ( self ): |
655 |
+ def write_manifest ( self, ignore_empty=False ): |
656 |
"""Generates and writes the Manifest file for this package. |
657 |
|
658 |
expects: called after writing metadata/ebuilds |
659 |
|
660 |
- returns: None (implicit) |
661 |
+ returns: success (True/False) |
662 |
|
663 |
raises: |
664 |
* Exception if no ebuild exists |
665 |
@@ -471,47 +528,64 @@ class PackageDir ( object ): |
666 |
# it should be sufficient to call create_manifest for one ebuild, |
667 |
# choosing the latest one that exists in self.physical_location and |
668 |
# has enough data (DISTDIR, EBUILD_FILE) for this task. |
669 |
+ # Additionally, all DISTDIRs (multiple repos, sub directories) have |
670 |
+ # to be collected and passed to Manifest creation. |
671 |
+ # => collect suitable PackageInfo objects from self._packages |
672 |
# |
673 |
- # metadata.xml's full path cannot be used for manifest creation here |
674 |
- # 'cause DISTDIR would be unknown |
675 |
- # |
676 |
- |
677 |
- # collect suitable PackageInfo instances |
678 |
pkgs_for_manifest = tuple ( |
679 |
p for p in self._packages.values() \ |
680 |
if p.has ( 'distdir', 'ebuild_file' ) |
681 |
) |
682 |
|
683 |
if pkgs_for_manifest: |
684 |
- manifest.create_manifest ( pkgs_for_manifest, nofail=False ) |
685 |
- self._need_manifest = False |
686 |
+ if manifest.create_manifest ( pkgs_for_manifest, nofail=False ): |
687 |
+ self._need_manifest = False |
688 |
+ return True |
689 |
+ elif ignore_empty: |
690 |
+ return True |
691 |
else: |
692 |
+ # FIXME: debug statements |
693 |
+ # FIXME: remove excpetion, maybe delete Manifest in this case,.. |
694 |
+ for pvr, p in self._packages.items(): |
695 |
+ print ( "{} {} ebuild={} efile={} has={}".format ( |
696 |
+ pvr, p, p.has ('ebuild'), p ['ebuild_file'], self.has_ebuilds() |
697 |
+ ) ) |
698 |
+ |
699 |
raise Exception ( |
700 |
- "No ebuild written so far! I really don't know what do to!" |
701 |
- ) |
702 |
+ 'In {mydir}: No ebuild written so far! ' |
703 |
+ 'I really don\'t know what do to!'.format ( |
704 |
+ mydir=self.physical_location |
705 |
+ ) ) |
706 |
+ |
707 |
+ return False |
708 |
# --- end of write_manifest (...) --- |
709 |
|
710 |
def write_metadata ( self, shared_fh=None ): |
711 |
- """Writes metadata for this package.""" |
712 |
+ """Writes metadata for this package. |
713 |
+ |
714 |
+ returns: success (True/False) |
715 |
+ """ |
716 |
+ success = False |
717 |
try: |
718 |
self.generate_metadata ( skip_if_existent=True ) |
719 |
|
720 |
if shared_fh is None: |
721 |
+ util.dodir ( self.physical_location, mkdir_p=True ) |
722 |
if self._metadata.write(): |
723 |
self._need_metadata = False |
724 |
self._need_manifest = True |
725 |
- return True |
726 |
+ success = True |
727 |
else: |
728 |
self.logger.error ( |
729 |
"Failed to write metadata file {}.".format ( |
730 |
self._metadata.filepath |
731 |
) |
732 |
) |
733 |
- return False |
734 |
else: |
735 |
self._metadata.show ( shared_fh ) |
736 |
- return True |
737 |
+ success = True |
738 |
except Exception as e: |
739 |
self.logger.exception ( e ) |
740 |
- return False |
741 |
+ |
742 |
+ return success |
743 |
# --- end of write_metadata (...) --- |
744 |
|
745 |
diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py |
746 |
index 363bdb5..219fd70 100644 |
747 |
--- a/roverlay/overlay/root.py |
748 |
+++ b/roverlay/overlay/root.py |
749 |
@@ -48,10 +48,10 @@ class Overlay ( object ): |
750 |
|
751 |
self.incremental = incremental |
752 |
if self.incremental: |
753 |
- |
754 |
- self._incremental_write_lock = threading.Lock() |
755 |
+ # this is multiple-run incremental writing (in contrast to runtime |
756 |
+ # incremental writing, which writes ebuilds as soon as they're |
757 |
+ # ready) FIXME: split incremental <-> runtime_incremental |
758 |
self.scan() |
759 |
- self._init_overlay ( reimport_eclass=True ) |
760 |
|
761 |
# --- end of __init__ (...) --- |
762 |
|
763 |
@@ -73,8 +73,6 @@ class Overlay ( object ): |
764 |
incremental=self.incremental |
765 |
) |
766 |
self._categories [category] = newcat |
767 |
- if self.incremental: |
768 |
- util.dodir ( newcat.physical_location ) |
769 |
finally: |
770 |
self._catlock.release() |
771 |
|
772 |
@@ -226,50 +224,10 @@ class Overlay ( object ): |
773 |
return cat.add ( package_info ) |
774 |
# --- end of add (...) --- |
775 |
|
776 |
- def finalize_write_incremental ( self ): |
777 |
- """Writes metadata + Manifest for all packages.""" |
778 |
- for cat in self._categories.values(): |
779 |
- cat.finalize_write_incremental() |
780 |
- # --- end of finalize_incremental (...) --- |
781 |
- |
782 |
- def generate_manifest ( self, **manifest_kw ): |
783 |
- """Generates Manifest files for all ebuilds in this overlay that exist |
784 |
- physically/in filesystem. |
785 |
- Manifest files are automatically created when calling write(). |
786 |
- |
787 |
- arguments: |
788 |
- * **manifest_kw -- see PackageDir.generate_manifest(...) |
789 |
- |
790 |
- returns: None (implicit) |
791 |
- """ |
792 |
- for cat in self._categories.values(): |
793 |
- cat.generate_manifest ( **manifest_kw ) |
794 |
- # --- end of generate_manifest (...) --- |
795 |
- |
796 |
- def generate_metadata ( self, **metadata_kw ): |
797 |
- """Tells the overlay's categories to create metadata. |
798 |
- You don't have to call this before write()/show() unless you want to use |
799 |
- special metadata options. |
800 |
- |
801 |
- arguments: |
802 |
- * **metadata_kw -- keywords for package.PackageDir.generate_metadata(...) |
803 |
- |
804 |
- returns: None (implicit) |
805 |
- """ |
806 |
- for cat in self._categories.values(): |
807 |
- cat.generate_metadata ( **metadata_kw ) |
808 |
- # --- end of generate_metadata (...) --- |
809 |
- |
810 |
def has_dir ( self, _dir ): |
811 |
return os.path.isdir ( self.physical_location + os.sep + _dir ) |
812 |
# --- end of has_category (...) --- |
813 |
|
814 |
- def keep_nth_latest ( self, *args, **kwargs ): |
815 |
- """See package.py:PackageDir:keep_nth_latest.""" |
816 |
- for cat in self._categories.values(): |
817 |
- cat.keep_nth_latest ( *args, **kwargs ) |
818 |
- # --- end of keep_nth_latest (...) --- |
819 |
- |
820 |
def list_packages ( self, for_deprules=True ): |
821 |
for cat in self._categories.values(): |
822 |
for package in cat.list_packages ( for_deprules=True ): |
823 |
@@ -313,9 +271,9 @@ class Overlay ( object ): |
824 |
cat.show ( **show_kw ) |
825 |
# --- end of show (...) --- |
826 |
|
827 |
- def write ( self, **write_kw ): |
828 |
+ def write ( self ): |
829 |
"""Writes the overlay to its physical location (filesystem), including |
830 |
- metadata and Manifest files. |
831 |
+ metadata and Manifest files as well as cleanup actions. |
832 |
|
833 |
arguments: |
834 |
* **write_kw -- keywords for package.PackageDir.write(...) |
835 |
@@ -327,35 +285,27 @@ class Overlay ( object ): |
836 |
! TODO/FIXME/DOC: This is not thread-safe, it's expected to be called |
837 |
when ebuild creation is done. |
838 |
""" |
839 |
- raise Exception ( "to be removed/replaced" ) |
840 |
- # writing profiles/ here, rewriting categories/ later |
841 |
self._init_overlay ( reimport_eclass=True ) |
842 |
|
843 |
for cat in self._categories.values(): |
844 |
- if not cat.empty(): |
845 |
- util.dodir ( cat.physical_location ) |
846 |
- cat.write ( **write_kw ) |
847 |
+ cat.write ( |
848 |
+ overwrite_ebuilds=False, |
849 |
+ keep_n_ebuilds=config.get ( 'OVERLAY.keep_nth_latest', None ), |
850 |
+ cautious=True |
851 |
+ ) |
852 |
# --- end of write (...) --- |
853 |
|
854 |
- def write_incremental ( self, **write_kw ): |
855 |
- """Writes all ebuilds that have been modified since the last write call. |
856 |
- Note that there are currently two modes of incremental writing: |
857 |
- (a) per-PackageDir incremental writing triggered by the new_ebuild() |
858 |
- event method and (b) "batched" incremental writing (this method) which |
859 |
- writes all modified PackageDirs. |
860 |
- """ |
861 |
- # FIXME merge with write(), making incremental writing the only option |
862 |
- # FIXME finalize_write_incremental? |
863 |
- if not self._incremental_write_lock.acquire(): |
864 |
- # another incremental write is running, drop this request |
865 |
- return |
866 |
+ def write_manifest ( self, **manifest_kw ): |
867 |
+ """Generates Manifest files for all ebuilds in this overlay that exist |
868 |
+ physically/in filesystem. |
869 |
+ Manifest files are automatically created when calling write(). |
870 |
|
871 |
- try: |
872 |
- util.dodir ( self.physical_location ) |
873 |
- cats = tuple ( self._categories.values() ) |
874 |
- for cat in cats: |
875 |
- util.dodir ( cat.physical_location ) |
876 |
- cat.write_incremental ( **write_kw ) |
877 |
- finally: |
878 |
- self._incremental_write_lock.release() |
879 |
- # --- end of write_incremental (...) --- |
880 |
+ arguments: |
881 |
+ * **manifest_kw -- see PackageDir.generate_manifest(...) |
882 |
+ |
883 |
+ returns: None (implicit) |
884 |
+ """ |
885 |
+ # FIXME: it would be good to ensure that profiles/categories exist |
886 |
+ for cat in self._categories.values(): |
887 |
+ cat.write_manifest ( **manifest_kw ) |
888 |
+ # --- end of write_manifest (...) --- |