blob: ffb7672d3dc9b5e7cae77e9755165f28b50e690a [file] [log] [blame]
from __future__ import print_function
import sys
import signal
import logging
import operator
import portage
from portage import os
from portage import eapi_is_supported
from portage.util import writemsg_level
from portage.cache.cache_errors import CacheError
from _emerge.ProgressHandler import ProgressHandler
from portage.eclass_cache import hashed_path
def action_metadata(settings, portdb, myopts, porttrees=None):
if porttrees is None:
porttrees = portdb.porttrees
portage.writemsg_stdout("\n>>> Updating Portage cache\n")
cachedir = os.path.normpath(settings.depcachedir)
if cachedir in ["/", "/bin", "/dev", "/etc", "/home",
"/lib", "/opt", "/proc", "/root", "/sbin",
"/sys", "/tmp", "/usr", "/var"]:
print("!!! PORTAGE_DEPCACHEDIR IS SET TO A PRIMARY " + \
"ROOT DIRECTORY ON YOUR SYSTEM.", file=sys.stderr)
print("!!! This is ALMOST CERTAINLY NOT what you want: '%s'" % cachedir, file=sys.stderr)
sys.exit(73)
if not os.path.exists(cachedir):
os.makedirs(cachedir)
auxdbkeys = portdb._known_keys
class TreeData(object):
__slots__ = ('dest_db', 'eclass_db', 'path', 'src_db', 'valid_nodes')
def __init__(self, dest_db, eclass_db, path, src_db):
self.dest_db = dest_db
self.eclass_db = eclass_db
self.path = path
self.src_db = src_db
self.valid_nodes = set()
porttrees_data = []
for path in porttrees:
src_db = portdb._pregen_auxdb.get(path)
if src_db is None:
# portdbapi does not populate _pregen_auxdb
# when FEATURES=metadata-transfer is enabled
src_db = portdb._create_pregen_cache(path)
if src_db is not None:
eclass_db = portdb.repositories.get_repo_for_location(path).eclass_db
# Update eclass data which may be stale after sync.
eclass_db.update_eclasses()
porttrees_data.append(TreeData(portdb.auxdb[path], eclass_db, path, src_db))
porttrees = [tree_data.path for tree_data in porttrees_data]
quiet = settings.get('TERM') == 'dumb' or \
'--quiet' in myopts or \
not sys.stdout.isatty()
onProgress = None
if not quiet:
progressBar = portage.output.TermProgressBar()
progressHandler = ProgressHandler()
onProgress = progressHandler.onProgress
def display():
progressBar.set(progressHandler.curval, progressHandler.maxval)
progressHandler.display = display
def sigwinch_handler(signum, frame):
lines, progressBar.term_columns = \
portage.output.get_term_size()
signal.signal(signal.SIGWINCH, sigwinch_handler)
# Temporarily override portdb.porttrees so portdb.cp_all()
# will only return the relevant subset.
portdb_porttrees = portdb.porttrees
portdb.porttrees = porttrees
try:
cp_all = portdb.cp_all()
finally:
portdb.porttrees = portdb_porttrees
curval = 0
maxval = len(cp_all)
if onProgress is not None:
onProgress(maxval, curval)
# TODO: Display error messages, but do not interfere with the progress bar.
# Here's how:
# 1) erase the progress bar
# 2) show the error message
# 3) redraw the progress bar on a new line
for cp in cp_all:
for tree_data in porttrees_data:
src_chf = tree_data.src_db.validation_chf
dest_chf = tree_data.dest_db.validation_chf
dest_chf_key = '_%s_' % dest_chf
dest_chf_getter = operator.attrgetter(dest_chf)
for cpv in portdb.cp_list(cp, mytree=tree_data.path):
tree_data.valid_nodes.add(cpv)
try:
src = tree_data.src_db[cpv]
except (CacheError, KeyError):
continue
ebuild_location = portdb.findname(cpv, mytree=tree_data.path)
if ebuild_location is None:
continue
ebuild_hash = hashed_path(ebuild_location)
try:
if not tree_data.src_db.validate_entry(src,
ebuild_hash, tree_data.eclass_db):
continue
except CacheError:
continue
eapi = src.get('EAPI')
if not eapi:
eapi = '0'
eapi_supported = eapi_is_supported(eapi)
if not eapi_supported:
continue
dest = None
try:
dest = tree_data.dest_db[cpv]
except (KeyError, CacheError):
pass
for d in (src, dest):
if d is not None and d.get('EAPI') in ('', '0'):
del d['EAPI']
if src_chf != 'mtime':
# src may contain an irrelevant _mtime_ which corresponds
# to the time that the cache entry was written
src.pop('_mtime_', None)
if src_chf != dest_chf:
# populate src entry with dest_chf_key
# (the validity of the dest_chf that we generate from the
# ebuild here relies on the fact that we already used
# validate_entry to validate the ebuild with src_chf)
src[dest_chf_key] = dest_chf_getter(ebuild_hash)
if dest is not None:
if not (dest.get(dest_chf_key) == src[dest_chf_key] and \
tree_data.eclass_db.validate_and_rewrite_cache(
dest['_eclasses_'], tree_data.dest_db.validation_chf,
tree_data.dest_db.store_eclass_paths) is not None and \
set(dest['_eclasses_']) == set(src['_eclasses_'])):
dest = None
else:
# We don't want to skip the write unless we're really
# sure that the existing cache is identical, so don't
# trust _mtime_ and _eclasses_ alone.
for k in auxdbkeys:
if dest.get(k, '') != src.get(k, ''):
dest = None
break
if dest is not None:
# The existing data is valid and identical,
# so there's no need to overwrite it.
continue
try:
tree_data.dest_db[cpv] = src
except CacheError:
# ignore it; can't do anything about it.
pass
curval += 1
if onProgress is not None:
onProgress(maxval, curval)
if onProgress is not None:
onProgress(maxval, curval)
for tree_data in porttrees_data:
try:
dead_nodes = set(tree_data.dest_db)
except CacheError as e:
writemsg_level("Error listing cache entries for " + \
"'%s': %s, continuing...\n" % (tree_data.path, e),
level=logging.ERROR, noiselevel=-1)
del e
else:
dead_nodes.difference_update(tree_data.valid_nodes)
for cpv in dead_nodes:
try:
del tree_data.dest_db[cpv]
except (KeyError, CacheError):
pass
if not quiet:
# make sure the final progress is displayed
progressHandler.display()
print()
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
portdb.flush_cache()
sys.stdout.flush()