blob: d980f4ecaa2f3954ee7fd0c70edbf04bac19b102 [file] [log] [blame]
# -*- coding:utf-8 -*-
import copy
import os
from pprint import pformat
from _emerge.Package import Package
# import our initialized portage instance
from repoman._portage import portage
from repoman.modules.scan.scanbase import ScanBase
from repoman.modules.scan.depend._depend_checks import _depend_checks
from repoman.modules.scan.depend._gen_arches import _gen_arches
from portage.dep import Atom
from portage.package.ebuild.profile_iuse import iter_iuse_vars
from portage.util import getconfig
def sort_key(item):
return item[2].sub_path
class ProfileDependsChecks(ScanBase):
'''Perform dependency checks for the different profiles'''
def __init__(self, **kwargs):
'''Class init
@param qatracker: QATracker instance
@param portdb: portdb instance
@param profiles: dictionary
@param options: cli options
@param repo_settings: repository settings instance
@param include_arches: set
@param caches: dictionary of our caches
@param repoman_incrementals: tuple
@param env: the environment
@param have: dictionary instance
@param dev_keywords: developer profile keywords
@param repo_metadata: dictionary of various repository items.
'''
self.qatracker = kwargs.get('qatracker')
self.portdb = kwargs.get('portdb')
self.profiles = kwargs.get('profiles')
self.options = kwargs.get('options')
self.repo_settings = kwargs.get('repo_settings')
self.include_arches = kwargs.get('include_arches')
self.caches = kwargs.get('caches')
self.repoman_incrementals = kwargs.get('repoman_incrementals')
self.env = kwargs.get('env')
self.have = kwargs.get('have')
self.dev_keywords = kwargs.get('dev_keywords')
self.repo_metadata = kwargs.get('repo_metadata')
def check(self, **kwargs):
'''Perform profile dependant dependency checks
@param arches:
@param pkg: Package in which we check (object).
@param ebuild: Ebuild which we check (object).
@param baddepsyntax: boolean
@param unknown_pkgs: set of tuples (type, atom.unevaluated_atom)
@returns: dictionary
'''
ebuild = kwargs.get('ebuild').get()
pkg = kwargs.get('pkg').get()
unknown_pkgs, baddepsyntax = _depend_checks(
ebuild, pkg, self.portdb, self.qatracker, self.repo_metadata,
self.repo_settings.qadata)
relevant_profiles = []
for keyword, arch, groups in _gen_arches(ebuild, self.options,
self.repo_settings, self.profiles):
if arch not in self.profiles:
# A missing profile will create an error further down
# during the KEYWORDS verification.
continue
if self.include_arches is not None:
if arch not in self.include_arches:
continue
relevant_profiles.extend(
(keyword, groups, prof) for prof in self.profiles[arch])
relevant_profiles.sort(key=sort_key)
for keyword, groups, prof in relevant_profiles:
is_stable_profile = prof.status == "stable"
is_dev_profile = prof.status == "dev" and \
self.options.include_dev
is_exp_profile = prof.status == "exp" and \
self.options.include_exp_profiles == 'y'
if not (is_stable_profile or is_dev_profile or is_exp_profile):
continue
dep_settings = self.caches['arch'].get(prof.sub_path)
if dep_settings is None:
dep_settings = portage.config(
config_profile_path=prof.abs_path,
config_incrementals=self.repoman_incrementals,
config_root=self.repo_settings.config_root,
local_config=False,
_unmatched_removal=self.options.unmatched_removal,
env=self.env, repositories=self.repo_settings.repoman_settings.repositories)
if not prof.abs_path:
self._populate_implicit_iuse(dep_settings,
self.repo_settings.repo_config.eclass_db.porttrees)
dep_settings.categories = self.repo_settings.repoman_settings.categories
if self.options.without_mask:
dep_settings._mask_manager_obj = \
copy.deepcopy(dep_settings._mask_manager)
dep_settings._mask_manager._pmaskdict.clear()
self.caches['arch'][prof.sub_path] = dep_settings
xmatch_cache_key = (prof.sub_path, tuple(groups))
xcache = self.caches['arch_xmatch'].get(xmatch_cache_key)
if xcache is None:
self.portdb.melt()
self.portdb.freeze()
xcache = self.portdb.xcache
xcache.update(self.caches['shared_xmatch'])
self.caches['arch_xmatch'][xmatch_cache_key] = xcache
self.repo_settings.trees[self.repo_settings.root]["porttree"].settings = dep_settings
self.portdb.settings = dep_settings
self.portdb.xcache = xcache
dep_settings["ACCEPT_KEYWORDS"] = " ".join(groups)
# just in case, prevent config.reset() from nuking these.
dep_settings.backup_changes("ACCEPT_KEYWORDS")
# This attribute is used in dbapi._match_use() to apply
# use.stable.{mask,force} settings based on the stable
# status of the parent package. This is required in order
# for USE deps of unstable packages to be resolved correctly,
# since otherwise use.stable.{mask,force} settings of
# dependencies may conflict (see bug #456342).
dep_settings._parent_stable = dep_settings._isStable(pkg)
# Handle package.use*.{force,mask) calculation, for use
# in dep_check.
dep_settings.useforce = dep_settings._use_manager.getUseForce(
pkg, stable=dep_settings._parent_stable)
dep_settings.usemask = dep_settings._use_manager.getUseMask(
pkg, stable=dep_settings._parent_stable)
if not baddepsyntax:
ismasked = not ebuild.archs or \
pkg.cpv not in self.portdb.xmatch("match-visible",
Atom("%s::%s" % (pkg.cp, self.repo_settings.repo_config.name)))
if ismasked:
if not self.have['pmasked']:
self.have['pmasked'] = bool(dep_settings._getMaskAtom(
pkg.cpv, ebuild.metadata))
if self.options.ignore_masked:
continue
# we are testing deps for a masked package; give it some lee-way
suffix = "masked"
matchmode = "minimum-all-ignore-profile"
else:
suffix = ""
matchmode = "minimum-visible"
if not self.have['dev_keywords']:
self.have['dev_keywords'] = \
bool(self.dev_keywords.intersection(ebuild.keywords))
if prof.status == "dev":
suffix = suffix + "indev"
elif prof.status == "exp":
suffix = suffix + "inexp"
for mytype in Package._dep_keys:
mykey = "dependency.bad" + suffix
myvalue = ebuild.metadata[mytype]
if not myvalue:
continue
success, atoms = portage.dep_check(
myvalue, self.portdb, dep_settings,
use="all", mode=matchmode, trees=self.repo_settings.trees)
if success:
if atoms:
# Don't bother with dependency.unknown for
# cases in which *DEPEND.bad is triggered.
for atom in atoms:
# dep_check returns all blockers and they
# aren't counted for *DEPEND.bad, so we
# ignore them here.
if not atom.blocker:
unknown_pkgs.discard(
(mytype, atom.unevaluated_atom))
if not prof.sub_path:
# old-style virtuals currently aren't
# resolvable with empty profile, since
# 'virtuals' mappings are unavailable
# (it would be expensive to search
# for PROVIDE in all ebuilds)
atoms = [
atom for atom in atoms if not (
atom.cp.startswith('virtual/')
and not self.portdb.cp_list(atom.cp))]
# we have some unsolvable deps
# remove ! deps, which always show up as unsatisfiable
all_atoms = [
str(atom.unevaluated_atom)
for atom in atoms if not atom.blocker]
# if we emptied out our list, continue:
if not all_atoms:
continue
# Filter out duplicates. We do this by hand (rather
# than use a set) so the order is stable and better
# matches the order that's in the ebuild itself.
atoms = []
for atom in all_atoms:
if atom not in atoms:
atoms.append(atom)
if self.options.output_style in ['column']:
self.qatracker.add_error(mykey,
"%s: %s: %s(%s) %s"
% (ebuild.relative_path, mytype, keyword,
prof, repr(atoms)))
else:
self.qatracker.add_error(mykey,
"%s: %s: %s(%s)\n%s"
% (ebuild.relative_path, mytype, keyword,
prof, pformat(atoms, indent=6)))
else:
if self.options.output_style in ['column']:
self.qatracker.add_error(mykey,
"%s: %s: %s(%s) %s"
% (ebuild.relative_path, mytype, keyword,
prof, repr(atoms)))
else:
self.qatracker.add_error(mykey,
"%s: %s: %s(%s)\n%s"
% (ebuild.relative_path, mytype, keyword,
prof, pformat(atoms, indent=6)))
if not baddepsyntax and unknown_pkgs:
type_map = {}
for mytype, atom in unknown_pkgs:
type_map.setdefault(mytype, set()).add(atom)
for mytype, atoms in type_map.items():
self.qatracker.add_error(
"dependency.unknown", "%s: %s: %s"
% (ebuild.relative_path, mytype, ", ".join(sorted(atoms))))
return False
@property
def runInEbuilds(self):
'''Ebuild level scans'''
return (True, [self.check])
@staticmethod
def _populate_implicit_iuse(config, repo_locations):
"""
Populate implicit IUSE for the empty profile, see bug 660982.
@param config: config instance for the empty profile
@type config: portage.config
@param repo_locations: locations of repositories containing relevant
implicit IUSE settings
@type repo_locations: list
"""
dest = config.configdict['defaults']
for location in repo_locations:
for parent_dir, dirs, files in os.walk(os.path.join(location, 'profiles')):
src = getconfig(os.path.join(parent_dir, 'make.defaults'))
if not src:
continue
for k, v in iter_iuse_vars(src):
v_before = dest.get(k)
if v_before is not None:
merged_values = set(v_before.split())
merged_values.update(v.split())
v = ' '.join(sorted(merged_values))
dest[k] = v
config.regenerate()
config._init_iuse()