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