2 # Copyright 2009-2011 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
5 from __future__ import print_function
9 # This block ensures that ^C interrupts are handled quietly.
12 def exithandler(signum,frame):
13 signal.signal(signal.SIGINT, signal.SIG_IGN)
14 signal.signal(signal.SIGTERM, signal.SIG_IGN)
15 sys.exit(128 + signum)
17 signal.signal(signal.SIGINT, exithandler)
18 signal.signal(signal.SIGTERM, exithandler)
20 except KeyboardInterrupt:
21 sys.exit(128 + signal.SIGINT)
34 from os import path as osp
35 sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
38 from portage import os, _encodings, _unicode_encode, _unicode_decode
39 from _emerge.MetadataRegen import MetadataRegen
40 from portage.cache.cache_errors import CacheError, StatCollision
41 from portage.manifest import guessManifestFileType
42 from portage.util import cmp_sort_key, writemsg_level
43 from portage import cpv_getkey
44 from portage.dep import Atom, isjustname
45 from portage.versions import pkgcmp, pkgsplit, vercmp
48 from xml.etree import ElementTree
53 from xml.parsers.expat import ExpatError
57 from repoman.utilities import parse_metadata_use
59 from repoman.utilities import FindVCS
61 if sys.hexversion >= 0x3000000:
65 usage = "egencache [options] <action> ... [atom] ..."
66 parser = optparse.OptionParser(usage=usage)
68 actions = optparse.OptionGroup(parser, 'Actions')
69 actions.add_option("--update",
71 help="update metadata/cache/ (generate as necessary)")
72 actions.add_option("--update-use-local-desc",
74 help="update the use.local.desc file from metadata.xml")
75 actions.add_option("--update-changelogs",
77 help="update the ChangeLog files from SCM logs")
78 parser.add_option_group(actions)
80 common = optparse.OptionGroup(parser, 'Common options')
81 common.add_option("--repo",
83 help="name of repo to operate on (default repo is located at $PORTDIR)")
84 common.add_option("--config-root",
85 help="location of portage config files",
86 dest="portage_configroot")
87 common.add_option("--portdir",
88 help="override the portage tree location",
90 common.add_option("--portdir-overlay",
91 help="override the PORTDIR_OVERLAY variable (requires that --repo is also specified)",
92 dest="portdir_overlay")
93 common.add_option("--tolerant",
95 help="exit successfully if only minor errors occurred")
96 common.add_option("--ignore-default-opts",
98 help="do not use the EGENCACHE_DEFAULT_OPTS environment variable")
99 parser.add_option_group(common)
101 update = optparse.OptionGroup(parser, '--update options')
102 update.add_option("--cache-dir",
103 help="location of the metadata cache",
105 update.add_option("--jobs",
107 help="max ebuild processes to spawn")
108 update.add_option("--load-average",
110 help="max load allowed when spawning multiple jobs",
112 update.add_option("--rsync",
114 help="enable rsync stat collision workaround " + \
115 "for bug 139134 (use with --update)")
116 parser.add_option_group(update)
118 uld = optparse.OptionGroup(parser, '--update-use-local-desc options')
119 uld.add_option("--preserve-comments",
121 help="preserve the comments from the existing use.local.desc file")
122 uld.add_option("--use-local-desc-output",
123 help="output file for use.local.desc data (or '-' for stdout)",
125 parser.add_option_group(uld)
127 options, args = parser.parse_args(args)
132 jobs = int(options.jobs)
137 parser.error("Invalid: --jobs='%s'" % \
145 if options.load_average:
147 load_average = float(options.load_average)
151 if load_average <= 0.0:
152 parser.error("Invalid: --load-average='%s'" % \
153 (options.load_average,))
155 options.load_average = load_average
158 options.load_average = None
160 options.config_root = options.portage_configroot
161 if options.config_root is not None and \
162 not os.path.isdir(options.config_root):
163 parser.error("Not a directory: --config-root='%s'" % \
164 (options.config_root,))
166 if options.cache_dir is not None:
167 if not os.path.isdir(options.cache_dir):
168 parser.error("Not a directory: --cache-dir='%s'" % \
169 (options.cache_dir,))
170 if not os.access(options.cache_dir, os.W_OK):
171 parser.error("Write access denied: --cache-dir='%s'" % \
172 (options.cache_dir,))
174 if options.portdir_overlay is not None and \
175 options.repo is None:
176 parser.error("--portdir-overlay option requires --repo option")
180 atom = portage.dep.Atom(atom)
181 except portage.exception.InvalidAtom:
182 parser.error('Invalid atom: %s' % (atom,))
184 if not isjustname(atom):
185 parser.error('Atom is too specific: %s' % (atom,))
187 if options.update_use_local_desc:
192 parser.error('--update-use-local-desc requires python with USE=xml!')
194 if options.uld_output == '-' and options.preserve_comments:
195 parser.error('--preserve-comments can not be used when outputting to stdout')
197 return parser, options, args
199 class GenCache(object):
200 def __init__(self, portdb, cp_iter=None, max_jobs=None, max_load=None,
202 # The caller must set portdb.porttrees in order to constrain
203 # findname, cp_list, and cpv_list to the desired tree.
204 tree = portdb.porttrees[0]
205 self._portdb = portdb
206 self._eclass_db = portdb._repo_info[tree].eclass_db
207 self._auxdbkeys = portdb._known_keys
208 # We can globally cleanse stale cache only if we
209 # iterate over every single cp.
210 self._global_cleanse = cp_iter is None
211 if cp_iter is not None:
212 self._cp_set = set(cp_iter)
213 cp_iter = iter(self._cp_set)
214 self._cp_missing = self._cp_set.copy()
217 self._cp_missing = set()
218 self._regen = MetadataRegen(portdb, cp_iter=cp_iter,
219 consumer=self._metadata_callback,
220 max_jobs=max_jobs, max_load=max_load)
221 self.returncode = os.EX_OK
222 conf = portdb.repositories.get_repo_for_location(tree)
223 self._trg_caches = tuple(conf.iter_pregenerated_caches(
224 self._auxdbkeys, force=True, readonly=False))
225 if not self._trg_caches:
226 raise Exception("cache formats '%s' aren't supported" %
227 (" ".join(conf.cache_formats),))
230 for trg_cache in self._trg_caches:
231 if hasattr(trg_cache, 'raise_stat_collision'):
232 trg_cache.raise_stat_collision = True
233 # Make _metadata_callback write this cache first, in case
234 # it raises a StatCollision and triggers mtime
236 self._trg_caches = tuple([trg_cache] +
237 [x for x in self._trg_caches if x is not trg_cache])
239 self._existing_nodes = set()
241 def _metadata_callback(self, cpv, repo_path, metadata, ebuild_hash):
242 self._existing_nodes.add(cpv)
243 self._cp_missing.discard(cpv_getkey(cpv))
244 if metadata is not None:
245 if metadata.get('EAPI') == '0':
247 for trg_cache in self._trg_caches:
248 self._write_cache(trg_cache,
249 cpv, repo_path, metadata, ebuild_hash)
251 def _write_cache(self, trg_cache, cpv, repo_path, metadata, ebuild_hash):
253 if not hasattr(trg_cache, 'raise_stat_collision'):
254 # This cache does not avoid redundant writes automatically,
255 # so check for an identical existing entry before writing.
256 # This prevents unnecessary disk writes and can also prevent
257 # unnecessary rsync transfers.
259 dest = trg_cache[cpv]
260 except (KeyError, CacheError):
263 if trg_cache.validate_entry(dest,
264 ebuild_hash, self._eclass_db):
266 for k in self._auxdbkeys:
267 if dest.get(k, '') != metadata.get(k, ''):
274 chf = trg_cache.validation_chf
275 metadata['_%s_' % chf] = getattr(ebuild_hash, chf)
277 trg_cache[cpv] = metadata
278 except StatCollision as sc:
279 # If the content of a cache entry changes and neither the
280 # file mtime nor size changes, it will prevent rsync from
281 # detecting changes. Cache backends may raise this
282 # exception from _setitem() if they detect this type of stat
283 # collision. These exceptions are handled by bumping the
284 # mtime on the ebuild (and the corresponding cache entry).
285 # See bug #139134. It is convenient to include checks for
286 # redundant writes along with the internal StatCollision
287 # detection code, so for caches with the
288 # raise_stat_collision attribute, we do not need to
289 # explicitly check for redundant writes like we do for the
290 # other cache types above.
292 for ec, ec_hash in metadata['_eclasses_'].items():
293 if max_mtime < ec_hash.mtime:
294 max_mtime = ec_hash.mtime
295 if max_mtime == sc.mtime:
297 max_mtime = long(max_mtime)
299 os.utime(ebuild_hash.location, (max_mtime, max_mtime))
303 "%s writing target: %s\n" % (cpv, e),
304 level=logging.ERROR, noiselevel=-1)
306 ebuild_hash.mtime = max_mtime
307 metadata['_mtime_'] = max_mtime
308 trg_cache[cpv] = metadata
309 self._portdb.auxdb[repo_path][cpv] = metadata
311 except CacheError as ce:
314 "%s writing target: %s\n" % (cpv, ce),
315 level=logging.ERROR, noiselevel=-1)
321 def sighandler(signum, frame):
322 signal.signal(signal.SIGINT, signal.SIG_IGN)
323 signal.signal(signal.SIGTERM, signal.SIG_IGN)
324 self._regen.terminate()
325 received_signal.append(128 + signum)
327 earlier_sigint_handler = signal.signal(signal.SIGINT, sighandler)
328 earlier_sigterm_handler = signal.signal(signal.SIGTERM, sighandler)
333 # Restore previous handlers
334 if earlier_sigint_handler is not None:
335 signal.signal(signal.SIGINT, earlier_sigint_handler)
337 signal.signal(signal.SIGINT, signal.SIG_DFL)
338 if earlier_sigterm_handler is not None:
339 signal.signal(signal.SIGTERM, earlier_sigterm_handler)
341 signal.signal(signal.SIGTERM, signal.SIG_DFL)
344 sys.exit(received_signal[0])
346 self.returncode |= self._regen.returncode
348 for trg_cache in self._trg_caches:
349 self._cleanse_cache(trg_cache)
351 def _cleanse_cache(self, trg_cache):
352 cp_missing = self._cp_missing
354 if self._global_cleanse:
356 for cpv in trg_cache:
361 "Unable to parse cp for '%s'\n" % (cpv,),
362 level=logging.ERROR, noiselevel=-1)
365 except CacheError as ce:
368 "Error listing cache entries for " + \
369 "'%s/metadata/cache': %s, continuing...\n" % \
370 (self._portdb.porttree_root, ce),
371 level=logging.ERROR, noiselevel=-1)
374 cp_set = self._cp_set
376 for cpv in trg_cache:
381 "Unable to parse cp for '%s'\n" % (cpv,),
382 level=logging.ERROR, noiselevel=-1)
384 cp_missing.discard(cp)
387 except CacheError as ce:
390 "Error listing cache entries for " + \
391 "'%s/metadata/cache': %s, continuing...\n" % \
392 (self._portdb.porttree_root, ce),
393 level=logging.ERROR, noiselevel=-1)
397 for cp in sorted(cp_missing):
399 "No ebuilds or cache entries found for '%s'\n" % (cp,),
400 level=logging.ERROR, noiselevel=-1)
403 dead_nodes.difference_update(self._existing_nodes)
409 except CacheError as ce:
412 "%s deleting stale cache: %s\n" % (k, ce),
413 level=logging.ERROR, noiselevel=-1)
415 if not trg_cache.autocommits:
418 except CacheError as ce:
421 "committing target: %s\n" % (ce,),
422 level=logging.ERROR, noiselevel=-1)
424 class GenUseLocalDesc(object):
425 def __init__(self, portdb, output=None,
426 preserve_comments=False):
427 self.returncode = os.EX_OK
428 self._portdb = portdb
429 self._output = output
430 self._preserve_comments = preserve_comments
433 repo_path = self._portdb.porttrees[0]
434 ops = {'<':0, '<=':1, '=':2, '>=':3, '>':4}
436 if self._output is None or self._output != '-':
437 if self._output is None:
438 prof_path = os.path.join(repo_path, 'profiles')
439 desc_path = os.path.join(prof_path, 'use.local.desc')
445 desc_path = self._output
448 if self._preserve_comments:
449 # Probe in binary mode, in order to avoid
450 # potential character encoding issues.
451 output = open(_unicode_encode(desc_path,
452 encoding=_encodings['fs'], errors='strict'), 'r+b')
454 output = io.open(_unicode_encode(desc_path,
455 encoding=_encodings['fs'], errors='strict'),
456 mode='w', encoding=_encodings['repo.content'],
457 errors='backslashreplace')
459 if not self._preserve_comments or \
460 os.path.isfile(desc_path):
462 "ERROR: failed to open output file %s: %s\n" \
463 % (desc_path, e), level=logging.ERROR, noiselevel=-1)
467 # Open in r+b mode failed because the file doesn't
468 # exist yet. We can probably recover if we disable
469 # preserve_comments mode now.
471 "WARNING: --preserve-comments enabled, but " + \
472 "output file not found: %s\n" % (desc_path,),
473 level=logging.WARNING, noiselevel=-1)
474 self._preserve_comments = False
476 output = io.open(_unicode_encode(desc_path,
477 encoding=_encodings['fs'], errors='strict'),
478 mode='w', encoding=_encodings['repo.content'],
479 errors='backslashreplace')
482 "ERROR: failed to open output file %s: %s\n" \
483 % (desc_path, e), level=logging.ERROR, noiselevel=-1)
489 if self._preserve_comments:
492 if not output.readline().startswith(b'#'):
498 # Finished probing comments in binary mode, now append
500 output = io.open(_unicode_encode(desc_path,
501 encoding=_encodings['fs'], errors='strict'),
502 mode='a', encoding=_encodings['repo.content'],
503 errors='backslashreplace')
504 output.write(_unicode_decode('\n'))
506 output.write(_unicode_decode('''
507 # This file is deprecated as per GLEP 56 in favor of metadata.xml. Please add
508 # your descriptions to your package's metadata.xml ONLY.
509 # * generated automatically using egencache *
513 # The cmp function no longer exists in python3, so we'll
514 # implement our own here under a slightly different name
515 # since we don't want any confusion given that we never
516 # want to rely on the builtin cmp function.
518 if a is None or b is None:
519 # None can't be compared with other types in python3.
520 if a is None and b is None:
526 return (a > b) - (a < b)
528 class _MetadataTreeBuilder(ElementTree.TreeBuilder):
530 Implements doctype() as required to avoid deprecation warnings
533 def doctype(self, name, pubid, system):
536 for cp in self._portdb.cp_all():
537 metadata_path = os.path.join(repo_path, cp, 'metadata.xml')
539 metadata = ElementTree.parse(metadata_path,
540 parser=ElementTree.XMLParser(
541 target=_MetadataTreeBuilder()))
544 except (ExpatError, EnvironmentError) as e:
546 "ERROR: failed parsing %s/metadata.xml: %s\n" % (cp, e),
547 level=logging.ERROR, noiselevel=-1)
551 usedict = parse_metadata_use(metadata)
552 except portage.exception.ParseError as e:
554 "ERROR: failed parsing %s/metadata.xml: %s\n" % (cp, e),
555 level=logging.ERROR, noiselevel=-1)
558 for flag in sorted(usedict):
559 def atomcmp(atoma, atomb):
560 # None is better than an atom, that's why we reverse the args
561 if atoma is None or atomb is None:
562 return cmp_func(atomb, atoma)
563 # Same for plain PNs (.operator is None then)
564 elif atoma.operator is None or atomb.operator is None:
565 return cmp_func(atomb.operator, atoma.operator)
567 elif atoma.cpv != atomb.cpv:
568 return pkgcmp(pkgsplit(atoma.cpv), pkgsplit(atomb.cpv))
569 # Versions match, let's fallback to operator matching
571 return cmp_func(ops.get(atoma.operator, -1),
572 ops.get(atomb.operator, -1))
579 resdict = usedict[flag]
580 if len(resdict) == 1:
581 resdesc = next(iter(resdict.items()))[1]
584 reskeys = dict((_Atom(k), k) for k in resdict)
585 except portage.exception.InvalidAtom as e:
587 "ERROR: failed parsing %s/metadata.xml: %s\n" % (cp, e),
588 level=logging.ERROR, noiselevel=-1)
590 resdesc = next(iter(resdict.items()))[1]
592 resatoms = sorted(reskeys, key=cmp_sort_key(atomcmp))
593 resdesc = resdict[reskeys[resatoms[-1]]]
595 output.write(_unicode_decode(
596 '%s:%s - %s\n' % (cp, flag, resdesc)))
600 if sys.hexversion < 0x3000000:
601 _filename_base = unicode
605 class _special_filename(_filename_base):
607 Helps to sort file names by file type and other criteria.
609 def __new__(cls, status_change, file_name):
610 return _filename_base.__new__(cls, status_change + file_name)
612 def __init__(self, status_change, file_name):
613 _filename_base.__init__(status_change + file_name)
614 self.status_change = status_change
615 self.file_name = file_name
616 self.file_type = guessManifestFileType(file_name)
618 def file_type_lt(self, a, b):
620 Defines an ordering between file types.
627 if first == "EBUILD":
629 elif first == "MISC":
630 return second in ("EBUILD",)
632 return second in ("EBUILD", "MISC")
633 elif first == "DIST":
634 return second in ("EBUILD", "MISC", "AUX")
638 raise ValueError("Unknown file type '%s'" % first)
640 def __lt__(self, other):
642 Compare different file names, first by file type and then
643 for ebuilds by version and lexicographically for others.
644 EBUILD < MISC < AUX < DIST < None
646 if self.__class__ != other.__class__:
647 raise NotImplementedError
649 # Sort by file type as defined by file_type_lt().
650 if self.file_type_lt(self, other):
652 elif self.file_type_lt(other, self):
655 # Files have the same type.
656 if self.file_type == "EBUILD":
657 # Sort by version. Lowest first.
658 ver = "-".join(pkgsplit(self.file_name[:-7])[1:3])
659 other_ver = "-".join(pkgsplit(other.file_name[:-7])[1:3])
660 return vercmp(ver, other_ver) < 0
662 # Sort lexicographically.
663 return self.file_name < other.file_name
665 class GenChangeLogs(object):
666 def __init__(self, portdb):
667 self.returncode = os.EX_OK
668 self._portdb = portdb
669 self._wrapper = textwrap.TextWrapper(
671 initial_indent = ' ',
672 subsequent_indent = ' '
677 p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
678 return _unicode_decode(p.communicate()[0],
679 encoding=_encodings['stdio'], errors='strict')
681 def generate_changelog(self, cp):
683 output = io.open('ChangeLog',
684 mode='w', encoding=_encodings['repo.content'],
685 errors='backslashreplace')
688 "ERROR: failed to open ChangeLog for %s: %s\n" % (cp,e,),
689 level=logging.ERROR, noiselevel=-1)
693 output.write(_unicode_decode('''
695 # Copyright 1999-%s Gentoo Foundation; Distributed under the GPL v2
698 ''' % (cp, time.strftime('%Y'))).lstrip())
700 # now grab all the commits
701 commits = self.grab(['git', 'rev-list', 'HEAD', '--', '.']).split()
704 # Explaining the arguments:
705 # --name-status to get a list of added/removed files
706 # --no-renames to avoid getting more complex records on the list
707 # --format to get the timestamp, author and commit description
708 # --root to make it work fine even with the initial commit
709 # --relative to get paths relative to ebuilddir
710 # -r (recursive) to get per-file changes
711 # then the commit-id and path.
713 cinfo = self.grab(['git', 'diff-tree', '--name-status', '--no-renames',
714 '--format=%ct %cN <%cE>%n%B', '--root', '--relative', '-r',
715 c, '--', '.']).rstrip('\n').split('\n')
718 # timestamp Author Name <author@email>
728 for n, l in enumerate(reversed(cinfo)):
734 if f[1] == 'Manifest':
735 pass # XXX: remanifest commits?
736 elif f[1] == 'ChangeLog':
738 elif f[0].startswith('A'):
739 changed.append(_special_filename("+", f[1]))
740 elif f[0].startswith('D'):
741 changed.append(_special_filename("-", f[1]))
742 elif f[0].startswith('M'):
743 changed.append(_special_filename("", f[1]))
746 "ERROR: unexpected git file status for %s: %s\n" % (cp,f,),
747 level=logging.ERROR, noiselevel=-1)
753 (ts, author) = cinfo[0].split(' ', 1)
754 date = time.strftime('%d %b %Y', time.gmtime(float(ts)))
756 changed = [str(x) for x in sorted(changed)]
759 # Reverse the sort order for headers.
760 for c in reversed(changed):
761 if c.startswith('+') and c.endswith('.ebuild'):
762 output.write(_unicode_decode(
763 '*%s (%s)\n' % (c[1:-7], date)))
766 output.write(_unicode_decode('\n'))
768 # strip '<cp>: ', '[<cp>] ', and similar
769 body[0] = re.sub(r'^\W*' + re.escape(cp) + r'\W+', '', body[0])
770 # strip trailing newline
774 if body[-1].startswith('git-svn-id:') and not body[-2]:
776 # strip the repoman version/manifest note
777 if body[-1] == ' (Signed Manifest commit)' or body[-1] == ' (Unsigned Manifest commit)':
779 if body[-1].startswith('(Portage version:') and body[-1].endswith(')'):
784 # don't break filenames on hyphens
785 self._wrapper.break_on_hyphens = False
786 output.write(_unicode_decode(
788 '%s; %s %s:' % (date, author, ', '.join(changed)))))
789 # but feel free to break commit messages there
790 self._wrapper.break_on_hyphens = True
791 output.write(_unicode_decode(
792 '\n%s\n\n' % '\n'.join(self._wrapper.fill(x) for x in body)))
797 repo_path = self._portdb.porttrees[0]
800 if 'git' not in FindVCS():
802 "ERROR: --update-changelogs supported only in git repos\n",
803 level=logging.ERROR, noiselevel=-1)
804 self.returncode = 127
807 for cp in self._portdb.cp_all():
808 os.chdir(os.path.join(repo_path, cp))
809 # Determine whether ChangeLog is up-to-date by comparing
810 # the newest commit timestamp with the ChangeLog timestamp.
811 lmod = self.grab(['git', 'log', '--format=%ct', '-1', '.'])
813 # This cp has not been added to the repo.
817 cmod = os.stat('ChangeLog').st_mtime
821 if float(cmod) < float(lmod):
822 self.generate_changelog(cp)
824 def egencache_main(args):
825 parser, options, atoms = parse_args(args)
827 config_root = options.config_root
829 # The calling environment is ignored, so the program is
830 # completely controlled by commandline arguments.
833 if options.repo is None:
834 env['PORTDIR_OVERLAY'] = ''
835 elif options.portdir_overlay:
836 env['PORTDIR_OVERLAY'] = options.portdir_overlay
838 if options.cache_dir is not None:
839 env['PORTAGE_DEPCACHEDIR'] = options.cache_dir
841 if options.portdir is not None:
842 env['PORTDIR'] = options.portdir
844 eprefix = os.environ.get("__PORTAGE_TEST_EPREFIX")
846 settings = portage.config(config_root=config_root,
847 local_config=False, env=env, _eprefix=eprefix)
850 if not options.ignore_default_opts:
851 default_opts = settings.get('EGENCACHE_DEFAULT_OPTS', '').split()
854 parser, options, args = parse_args(default_opts + args)
856 if options.cache_dir is not None:
857 env['PORTAGE_DEPCACHEDIR'] = options.cache_dir
859 settings = portage.config(config_root=config_root,
860 local_config=False, env=env, _eprefix=eprefix)
862 if not options.update and not options.update_use_local_desc \
863 and not options.update_changelogs:
864 parser.error('No action specified')
867 if options.update and 'metadata-transfer' not in settings.features:
868 settings.features.add('metadata-transfer')
872 portdb = portage.portdbapi(mysettings=settings)
875 if options.cache_dir is not None:
876 # already validated earlier
879 # We check write access after the portdbapi constructor
880 # has had an opportunity to create it. This ensures that
881 # we don't use the cache in the "volatile" mode which is
882 # undesirable for egencache.
883 if not os.access(settings["PORTAGE_DEPCACHEDIR"], os.W_OK):
884 writemsg_level("ecachegen: error: " + \
885 "write access denied: %s\n" % (settings["PORTAGE_DEPCACHEDIR"],),
886 level=logging.ERROR, noiselevel=-1)
889 if options.repo is not None:
890 repo_path = portdb.getRepositoryPath(options.repo)
891 if repo_path is None:
892 parser.error("Unable to locate repository named '%s'" % \
896 # Limit ebuilds to the specified repo.
897 portdb.porttrees = [repo_path]
899 portdb.porttrees = [portdb.porttree_root]
906 cp_iter = iter(atoms)
908 gen_cache = GenCache(portdb, cp_iter=cp_iter,
909 max_jobs=options.jobs,
910 max_load=options.load_average,
916 ret.append(gen_cache.returncode)
918 if options.update_use_local_desc:
919 gen_desc = GenUseLocalDesc(portdb,
920 output=options.uld_output,
921 preserve_comments=options.preserve_comments)
923 ret.append(gen_desc.returncode)
925 if options.update_changelogs:
926 gen_clogs = GenChangeLogs(portdb)
928 ret.append(gen_clogs.returncode)
932 if __name__ == "__main__":
933 portage._disable_legacy_globals()
934 portage.util.noiselimit = -1
935 sys.exit(egencache_main(sys.argv[1:]))