Exemple #1
0
def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=True):
	"""
	Checks if the systemstate is matching an atom in
	I{vulnerableList} and returns string describing
	the lowest version for the package that matches an atom in
	I{unaffectedList} and is greater than the currently installed
	version. It will return an empty list if the system is affected,
	and no upgrade is possible or None if the system is not affected.
	Both I{vulnerableList} and I{unaffectedList} should have the
	same base package.

	@type	vulnerableList: List of Strings
	@param	vulnerableList: atoms matching vulnerable package versions
	@type	unaffectedList: List of Strings
	@param	unaffectedList: atoms matching unaffected package versions
	@type	portdbapi:	portage.dbapi.porttree.portdbapi
	@param	portdbapi:	Ebuild repository
	@type	vardbapi:	portage.dbapi.vartree.vardbapi
	@param	vardbapi:	Installed package repository
	@type	minimize:	Boolean
	@param	minimize:	True for a least-change upgrade, False for emerge-like algorithm

	@rtype:		String | None
	@return:	the lowest unaffected version that is greater than
				the installed version.
 	"""
	rValue = ""
	v_installed = reduce(operator.add, [match(v, vardbapi) for v in vulnerableList], [])
	u_installed = reduce(operator.add, [match(u, vardbapi) for u in unaffectedList], [])

	# remove all unaffected atoms from vulnerable list
	v_installed = list(set(v_installed).difference(set(u_installed)))

	if not v_installed:
		return None

	# this tuple holds all vulnerable atoms, and the related upgrade atom
	vuln_update = []
	avail_updates = set()
	for u in unaffectedList:
		# TODO: This had match_type="match-all" before. I don't think it should
		# since we disregarded masked items later anyway (match(=rValue, "porttree"))
		avail_updates.update(match(u, portdbapi))
	# if an atom is already installed, we should not consider it for upgrades
	avail_updates.difference_update(u_installed)

	for vuln in v_installed:
		update = ""
		for c in avail_updates:
			c_pv = portage.catpkgsplit(c)
			if vercmp(c.version, vuln.version) > 0 \
					and (update == "" \
						or (minimize ^ (vercmp(c.version, update.version) > 0))) \
					and portdbapi._pkg_str(c, None).slot == vardbapi._pkg_str(vuln, None).slot:
				update = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
				if c_pv[3] != "r0":		# we don't like -r0 for display
					update += "-"+c_pv[3]
		vuln_update.append([vuln, update])

	return vuln_update
Exemple #2
0
    def isVulnerable(self):
        rValue = True

        v = getKernelVersion()
        c = getKernelOptions()

        for vul in self.vul_vers:
            value = vul[0]
            version = vul[1]
            match = False
            if (value == '<' or value == '<=') and vercmp(version, v) == 1:
                match = True
            if (value == '=' or value == '<=' or value == '>=') and vercmp(version, v) == 0:
                match = True
            if (value == '>' or value == '>=') and vercmp(version, v) == -1:
                match = True
            if (value == '!=') and vercmp(version, v) != 0:
                match = True
            if not match:
                rValue = False

        for conf in self.vul_configs:
            match = False
            exists = False
            for conf_current in c:
                if re.match(conf[0], conf_current[1]):
                    exists = True
                    if re.match(conf[1], conf_current[0]):
                        match = True
            if not match and not(exists and conf[0] == ""):
                rValue = False

        return rValue
Exemple #3
0
def getMinUpgrade(vulnerableList,
                  unaffectedList,
                  portdbapi,
                  vardbapi,
                  minimize=True):
    """
	Checks if the systemstate is matching an atom in
	I{vulnerableList} and returns string describing
	the lowest version for the package that matches an atom in 
	I{unaffectedList} and is greater than the currently installed
	version or None if the system is not affected. Both
	I{vulnerableList} and I{unaffectedList} should have the
	same base package.
	
	@type	vulnerableList: List of Strings
	@param	vulnerableList: atoms matching vulnerable package versions
	@type	unaffectedList: List of Strings
	@param	unaffectedList: atoms matching unaffected package versions
	@type	portdbapi:	portage.dbapi.porttree.portdbapi
	@param	portdbapi:	Ebuild repository
	@type	vardbapi:	portage.dbapi.vartree.vardbapi
	@param	vardbapi:	Installed package repository
	@type	minimize:	Boolean
	@param	minimize:	True for a least-change upgrade, False for emerge-like algorithm
	
	@rtype:		String | None
	@return:	the lowest unaffected version that is greater than
				the installed version.
	"""
    rValue = None
    v_installed = []
    u_installed = []
    for v in vulnerableList:
        v_installed += match(v, vardbapi)

    for u in unaffectedList:
        u_installed += match(u, vardbapi)

    install_unaffected = True
    for i in v_installed:
        if i not in u_installed:
            install_unaffected = False

    if install_unaffected:
        return rValue

    for u in unaffectedList:
        mylist = match(u, portdbapi, match_type="match-all")
        for c in mylist:
            i = best(v_installed)
            if vercmp(c.version, i.version) > 0 \
              and (rValue == None \
               or not match("="+rValue, portdbapi) \
               or (minimize ^ (vercmp(c.version, rValue.version) > 0)) \
                and match("="+c, portdbapi)) \
              and portdbapi.aux_get(c, ["SLOT"]) == vardbapi.aux_get(best(v_installed), ["SLOT"]):
                rValue = c
    return rValue
Exemple #4
0
def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=True):
	"""
	Checks if the systemstate is matching an atom in
	I{vulnerableList} and returns string describing
	the lowest version for the package that matches an atom in 
	I{unaffectedList} and is greater than the currently installed
	version or None if the system is not affected. Both
	I{vulnerableList} and I{unaffectedList} should have the
	same base package.
	
	@type	vulnerableList: List of Strings
	@param	vulnerableList: atoms matching vulnerable package versions
	@type	unaffectedList: List of Strings
	@param	unaffectedList: atoms matching unaffected package versions
	@type	portdbapi:	portage.dbapi.porttree.portdbapi
	@param	portdbapi:	Ebuild repository
	@type	vardbapi:	portage.dbapi.vartree.vardbapi
	@param	vardbapi:	Installed package repository
	@type	minimize:	Boolean
	@param	minimize:	True for a least-change upgrade, False for emerge-like algorithm
	
	@rtype:		String | None
	@return:	the lowest unaffected version that is greater than
				the installed version.
	"""
	rValue = None
	v_installed = []
	u_installed = []
	for v in vulnerableList:
		v_installed += match(v, vardbapi)

	for u in unaffectedList:
		u_installed += match(u, vardbapi)
	
	install_unaffected = True
	for i in v_installed:
		if i not in u_installed:
			install_unaffected = False

	if install_unaffected:
		return rValue
	
	for u in unaffectedList:
		mylist = match(u, portdbapi, match_type="match-all")
		for c in mylist:
			i = best(v_installed)
			if vercmp(c.version, i.version) > 0 \
					and (rValue == None \
						or not match("="+rValue, portdbapi) \
						or (minimize ^ (vercmp(c.version, rValue.version) > 0)) \
							and match("="+c, portdbapi)) \
					and portdbapi.aux_get(c, ["SLOT"]) == vardbapi.aux_get(best(v_installed), ["SLOT"]):
				rValue = c
	return rValue
Exemple #5
0
 def __resolve_dependencies(self):
     
     to_install = []
     
     for pkg, comp, version in self.__desc.self_depends:
         
         # no version required, get the latest available
         if version == None:
             to_install.append('%s-%s' % (pkg, self.__dbtree.latest_version(pkg)))
             continue
         
         # here we need to calculate the better version to install
         versions = self.__dbtree.package_versions(pkg)
         
         allowed_versions = []
         
         for _version in versions:
             comparation = vercmp(_version, version)
             if eval('%s %s 0' % (comparation, comp)):
                 allowed_versions.append(_version)
             
         to_install.append('%s-%s' % (pkg, self.__dbtree.version_compare(allowed_versions)))
         
         if len(allowed_versions) == 0:
             raise EbuildException('Can\'t resolve a dependency: %s' % pkg)
     
     # creating the ebuilds for the dependencies, recursivelly
     for ebuild in to_install:
         Ebuild(
             ebuild,
             force = self.__force,
             conf = self.__conf,
             pkg_manager = self.__pkg_manager,
             scm = self.__scm
         ).create()
Exemple #6
0
    def testVerCmpLess(self):
        """
		pre < alpha < beta < rc < p -> test each of these, they are inductive (or should be..)
		"""
        tests = [
            ("4.0", "5.0"),
            ("5", "5.0"),
            ("1.0_pre2", "1.0_p2"),
            ("1.0_alpha2", "1.0_p2"),
            ("1.0_alpha1", "1.0_beta1"),
            ("1.0_beta3", "1.0_rc3"),
            ("1.001000000000000000001", "1.001000000000000000002"),
            ("1.00100000000", "1.0010000000000000001"),
            ("9999", "cvs.9999"),
            ("999999999999999999999999999998",
             "999999999999999999999999999999"),
            ("1.01", "1.1"),
            ("1.0-r0", "1.0-r1"),
            ("1.0", "1.0-r1"),
            ("1.0", "1.0.0"),
            ("1.0b", "1.0.0"),
            ("1_p1", "1b_p1"),
            ("1", "1b"),
            ("1.1", "1.1b"),
            ("12.2b", "12.2.5"),
        ]
        for test in tests:
            self.failIf(vercmp(test[0], test[1]) >= 0,
                        msg="%s > %s? Wrong!" % (test[0], test[1]))
Exemple #7
0
    def testVerCmpEqual(self):

        tests = [("4.0", "4.0"), ("1.0", "1.0"), ("1.0-r0", "1.0"),
                 ("1.0", "1.0-r0"), ("1.0-r0", "1.0-r0"), ("1.0-r1", "1.0-r1")]
        for test in tests:
            self.failIf(vercmp(test[0], test[1]) != 0,
                        msg="%s != %s? Wrong!" % (test[0], test[1]))
Exemple #8
0
def fill_world_ng():
	global world, options

	print "Checking ALL installed packages..."
	if not read_smartworld():
		# SmartWorld file doesn't exist or not valid, let's calculate world and create one
		if options.problematic_only:
			raw_emerge_pattern = re.compile('(?:\[ebuild.I.....\]|\[ebuild....F..\])\s+([^\s]+).*')
		else:
			raw_emerge_pattern = re.compile('\[ebuild.+\]\s+([^\s]+).*')

		if vercmp(portage.VERSION, "2.2") < 0:
			# Portage < 2.2
			raw_pkglist = commands.getstatusoutput('emerge -ep --quiet --nospinner world system')
		else:
			# Portage >= 2.2
			raw_pkglist = commands.getstatusoutput('emerge -ep --quiet --nospinner @world @system')

		if raw_pkglist[0] == 0:
			pkglist = raw_pkglist[1].split('\n')

			for pkg in pkglist:
				match = raw_emerge_pattern.match(pkg)
				if match:
					world.append(match.group(1))
		else:
			raise EwoError('Oops! No world packages list...')

		write_smartworld()
Exemple #9
0
def fill_world_ng():
	global world, options
	
	print "Checking ALL installed packages..."	
	if not read_smartworld():
		# SmartWorld file doesn't exist or not valid, let's calculate world and create one
		if options.problematic_only:
			raw_emerge_pattern = re.compile('(?:\[ebuild.I.....\]|\[ebuild....F..\])\s+([^\s]+).*')
		else:
			raw_emerge_pattern = re.compile('\[ebuild.+\]\s+([^\s]+).*')
		
		if vercmp(portage.VERSION, "2.2") < 0:
			# Portage < 2.2
			raw_pkglist = commands.getstatusoutput('emerge -ep --quiet --nospinner world system')
		else:
			# Portage >= 2.2
			raw_pkglist = commands.getstatusoutput('emerge -ep --quiet --nospinner @world @system')
			
		if raw_pkglist[0] == 0:
			pkglist = raw_pkglist[1].split('\n')
	
			for pkg in pkglist:
				match = raw_emerge_pattern.match(pkg)
				if match:
					world.append(match.group(1))
		else:
			raise EwoError('Oops! No world packages list...')
		
		write_smartworld()
Exemple #10
0
def BestEBuild(ebuilds):
  """Returns the newest EBuild from a list of EBuild objects."""
  from portage.versions import vercmp
  winner = ebuilds[0]
  for ebuild in ebuilds[1:]:
    if vercmp(winner.version, ebuild.version) < 0:
      winner = ebuild
  return winner
def BestEBuild(ebuilds):
    """Returns the newest EBuild from a list of EBuild objects."""
    from portage.versions import vercmp
    winner = ebuilds[0]
    for ebuild in ebuilds[1:]:
        if vercmp(winner.version, ebuild.version) < 0:
            winner = ebuild
    return winner
	def testVerCmpEqual(self):
		
		tests = [ ("4.0", "4.0"),
			("1.0", "1.0"),
			("1.0-r0", "1.0"),
			("1.0", "1.0-r0"),
			("1.0-r0", "1.0-r0"),
			("1.0-r1", "1.0-r1")]
		for test in tests:
			self.failIf( vercmp( test[0], test[1]) != 0, msg="%s != %s? Wrong!" % (test[0],test[1]))
Exemple #13
0
    def __lt__(self, other):
        if not isinstance(other, self.__class__):
            raise TypeError("other isn't of %s type, is %s" % (self.__class__, other.__class__))

        if self.category != other.category:
            return self.category < other.category
        elif self.name != other.name:
            return self.name < other.name
        else:
            # FIXME: this cmp() hack is for vercmp not using -1,0,1
            # See bug 266493; this was fixed in portage-2.2_rc31
            # return vercmp(self.fullversion, other.fullversion)
            return vercmp(self.fullversion, other.fullversion) < 0
Exemple #14
0
 def _reduce(self, atomlist):
     mydict = {}
     for atom in atomlist[:]:
         cpv = self._portdbapi.xmatch("match-all", atom)[0]
         slot = self._portdbapi.aux_get(cpv, ["SLOT"])[0]
         cps = "%s:%s" % (cpv.cp, slot)
         if not cps in mydict:
             mydict[cps] = (atom, cpv)
         else:
             other_cpv = mydict[cps][1]
             if vercmp(cpv.version, other_cpv.version) > 0:
                 atomlist.remove(mydict[cps][0])
                 mydict[cps] = (atom, cpv)
     return atomlist
Exemple #15
0
	def _reduce(self, atomlist):
		mydict = {}
		for atom in atomlist[:]:
			cpv = self._portdbapi.xmatch("match-all", atom)[0]
			slot = self._portdbapi.aux_get(cpv, ["SLOT"])[0]
			cps = "%s:%s" % (cpv.cp, slot)
			if not cps in mydict:
				mydict[cps] = (atom, cpv)
			else:
				other_cpv = mydict[cps][1]
				if vercmp(cpv.version, other_cpv.version) > 0:
					atomlist.remove(mydict[cps][0])
					mydict[cps] = (atom, cpv)
		return atomlist
Exemple #16
0
	def __init__(self, basedir, dbapi):
		def p(x):
			return os.path.join(basedir, x)

		pkw = [p('package.keywords')]
		if vercmp(portage_ver, '2.1.9') >= 0:
			pkw.append(p('package.accept_keywords'))

		self.files = {
			'use': PackageFileSet(p('package.use')),
			'kw': PackageKeywordsFileSet(pkw, dbapi),
			'lic': PackageFileSet(p('package.license')),
			'env': PackageEnvFileSet(p('package.env'))
		}
Exemple #17
0
	def _reduce(self, atomlist):
		mydict = {}
		for atom in atomlist[:]:
			cpv = self._portdbapi.xmatch("match-all", atom)[0]
			pkg = self._portdbapi._pkg_str(cpv, None)
			cps = "%s:%s" % (pkg.cp, pkg.slot)
			if not cps in mydict:
				mydict[cps] = (atom, cpv)
			else:
				other_cpv = mydict[cps][1]
				if vercmp(cpv.version, other_cpv.version) > 0:
					atomlist.remove(mydict[cps][0])
					mydict[cps] = (atom, cpv)
		return atomlist
Exemple #18
0
    def __lt__(self, other):
        if not isinstance(other, self.__class__):
            raise TypeError("other isn't of %s type, is %s" %
                            (self.__class__, other.__class__))

        if self.category != other.category:
            return self.category < other.category
        elif self.name != other.name:
            return self.name < other.name
        else:
            # FIXME: this cmp() hack is for vercmp not using -1,0,1
            # See bug 266493; this was fixed in portage-2.2_rc31
            # return vercmp(self.fullversion, other.fullversion)
            return vercmp(self.fullversion, other.fullversion) < 0
Exemple #19
0
	def _reduce(self, atomlist):
		mydict = {}
		for atom in atomlist[:]:
			cpv = self._portdbapi.xmatch("match-all", atom)[0]
			pkg = self._portdbapi._pkg_str(cpv, None)
			cps = "%s:%s" % (pkg.cp, pkg.slot)
			if not cps in mydict:
				mydict[cps] = (atom, cpv)
			else:
				other_cpv = mydict[cps][1]
				if vercmp(cpv.version, other_cpv.version) > 0:
					atomlist.remove(mydict[cps][0])
					mydict[cps] = (atom, cpv)
		return atomlist
Exemple #20
0
    def testVerCmpEqual(self):

        tests = [
            ("4.0", "4.0"),
            ("1.0", "1.0"),
            ("1.0-r0", "1.0"),
            ("1.0", "1.0-r0"),
            ("1.0-r0", "1.0-r0"),
            ("1.0-r1", "1.0-r1"),
        ]
        for test in tests:
            self.assertFalse(
                vercmp(test[0], test[1]) != 0,
                msg="%s != %s? Wrong!" % (test[0], test[1]),
            )
Exemple #21
0
	def testVerCmpGreater(self):

		tests = [
			("6.0", "5.0"), ("5.0", "5"),
			("1.0-r1", "1.0-r0"),
			("1.0-r1", "1.0"),
			("999999999999999999999999999999", "999999999999999999999999999998"),
			("1.0.0", "1.0"),
			("1.0.0", "1.0b"),
			("1b", "1"),
			("1b_p1", "1_p1"),
			("1.1b", "1.1"),
			("12.2.5", "12.2b"),
		]
		for test in tests:
			self.assertFalse(vercmp(test[0], test[1]) <= 0, msg="%s < %s? Wrong!" % (test[0], test[1]))
Exemple #22
0
    def load(self):
        atoms = []
        xmatch = self._portdb.xmatch
        xmatch_level = "bestmatch-visible"
        cp_list = self._vardb.cp_list
        pkg_str = self._vardb._pkg_str
        for cp in self._vardb.cp_all():
            for cpv in cp_list(cp):
                pkg = pkg_str(cpv, None)
                slot_atom = "%s:%s" % (pkg.cp, pkg.slot)
                ebuild = xmatch(xmatch_level, slot_atom)
                if not ebuild:
                    continue
                if vercmp(cpv.version, ebuild.version) > 0:
                    atoms.append(slot_atom)

        self._setAtoms(atoms)
Exemple #23
0
	def load(self):
		atoms = []
		xmatch = self._portdb.xmatch
		xmatch_level = "bestmatch-visible"
		cp_list = self._vardb.cp_list
		pkg_str = self._vardb._pkg_str
		for cp in self._vardb.cp_all():
			for cpv in cp_list(cp):
				pkg = pkg_str(cpv, None)
				slot_atom = "%s:%s" % (pkg.cp, pkg.slot)
				ebuild = xmatch(xmatch_level, slot_atom)
				if not ebuild:
					continue
				if vercmp(cpv.version, ebuild.version) > 0:
					atoms.append(slot_atom)

		self._setAtoms(atoms)
Exemple #24
0
	def testVerNotEqual(self):

		tests = [
			("1", "2"), ("1.0_alpha", "1.0_pre"), ("1.0_beta", "1.0_alpha"),
			("0", "0.0"),
			("1.0-r0", "1.0-r1"),
			("1.0-r1", "1.0-r0"),
			("1.0", "1.0-r1"),
			("1.0-r1", "1.0"),
			("1.0", "1.0.0"),
			("1_p1", "1b_p1"),
			("1b", "1"),
			("1.1b", "1.1"),
			("12.2b", "12.2"),
		]
		for test in tests:
			self.assertFalse(vercmp(test[0], test[1]) == 0, msg="%s == %s? Wrong!" % (test[0], test[1]))
Exemple #25
0
	def load(self):
		atoms = []
		xmatch = self._portdb.xmatch
		xmatch_level = "bestmatch-visible"
		cp_list = self._vardb.cp_list
		aux_get = self._vardb.aux_get
		aux_keys = ["SLOT"]
		for cp in self._vardb.cp_all():
			for cpv in cp_list(cp):
				slot, = aux_get(cpv, aux_keys)
				slot_atom = "%s:%s" % (cp, slot)
				ebuild = xmatch(xmatch_level, slot_atom)
				if not ebuild:
					continue
				if vercmp(cpv.version, ebuild.version) > 0:
					atoms.append(slot_atom)

		self._setAtoms(atoms)
Exemple #26
0
    def load(self):
        atoms = []
        xmatch = self._portdb.xmatch
        xmatch_level = "bestmatch-visible"
        cp_list = self._vardb.cp_list
        aux_get = self._vardb.aux_get
        aux_keys = ["SLOT"]
        for cp in self._vardb.cp_all():
            for cpv in cp_list(cp):
                slot, = aux_get(cpv, aux_keys)
                slot_atom = "%s:%s" % (cp, slot)
                ebuild = xmatch(xmatch_level, slot_atom)
                if not ebuild:
                    continue
                if vercmp(cpv.version, ebuild.version) > 0:
                    atoms.append(slot_atom)

        self._setAtoms(atoms)
    def testVerCmpGreater(self):

        tests = [
            ("6.0", "5.0"),
            ("5.0", "5"),
            ("1.0-r1", "1.0-r0"),
            ("1.0-r1", "1.0"),
            ("999999999999999999999999999999",
             "999999999999999999999999999998"),
            ("1.0.0", "1.0"),
            ("1.0.0", "1.0b"),
            ("1b", "1"),
            ("1b_p1", "1_p1"),
            ("1.1b", "1.1"),
            ("12.2.5", "12.2b"),
        ]
        for test in tests:
            self.assertFalse(vercmp(test[0], test[1]) <= 0,
                             msg="%s < %s? Wrong!" % (test[0], test[1]))
    def testVerNotEqual(self):

        tests = [
            ("1", "2"),
            ("1.0_alpha", "1.0_pre"),
            ("1.0_beta", "1.0_alpha"),
            ("0", "0.0"),
            ("1.0-r0", "1.0-r1"),
            ("1.0-r1", "1.0-r0"),
            ("1.0", "1.0-r1"),
            ("1.0-r1", "1.0"),
            ("1.0", "1.0.0"),
            ("1_p1", "1b_p1"),
            ("1b", "1"),
            ("1.1b", "1.1"),
            ("12.2b", "12.2"),
        ]
        for test in tests:
            self.assertFalse(vercmp(test[0], test[1]) == 0,
                             msg="%s == %s? Wrong!" % (test[0], test[1]))
Exemple #29
0
	def match(self, other):
		"""See whether a passed in VersionMatch or CPV instance matches self.

		Example usage:
			>>> from gentoolkit.versionmatch import VersionMatch
			>>> from gentoolkit.cpv import CPV
			>>> VersionMatch(CPV('foo/bar-1.5'), op='>').match(
			... VersionMatch(CPV('foo/bar-2.0')))
			True

		@type other: gentoolkit.versionmatch.VersionMatch OR
		   gentoolkit.cpv.CPV
		@param other: version to compare with self's version
		@rtype: bool
		"""

		if self.droprevision:
			ver1, ver2 = self.version, other.version
		else:
			ver1, ver2 = self.fullversion, other.fullversion

		return vercmp(ver2, ver1) in self.values
Exemple #30
0
	def testVerCmpLess(self):
		"""
		pre < alpha < beta < rc < p -> test each of these, they are inductive (or should be..)
		"""
		tests = [
			("4.0", "5.0"), ("5", "5.0"), ("1.0_pre2", "1.0_p2"),
			("1.0_alpha2", "1.0_p2"), ("1.0_alpha1", "1.0_beta1"), ("1.0_beta3", "1.0_rc3"),
			("1.001000000000000000001", "1.001000000000000000002"),
			("1.00100000000", "1.0010000000000000001"),
			("999999999999999999999999999998", "999999999999999999999999999999"),
			("1.01", "1.1"),
			("1.0-r0", "1.0-r1"),
			("1.0", "1.0-r1"),
			("1.0", "1.0.0"),
			("1.0b", "1.0.0"),
			("1_p1", "1b_p1"),
			("1", "1b"),
			("1.1", "1.1b"),
			("12.2b", "12.2.5"),
		]
		for test in tests:
			self.assertFalse(vercmp(test[0], test[1]) >= 0, msg="%s > %s? Wrong!" % (test[0], test[1]))
Exemple #31
0
    def match(self, other):
        """See whether a passed in VersionMatch or CPV instance matches self.

        Example usage:
                >>> from gentoolkit.versionmatch import VersionMatch
                >>> from gentoolkit.cpv import CPV
                >>> VersionMatch(CPV('foo/bar-1.5'), op='>').match(
                ... VersionMatch(CPV('foo/bar-2.0')))
                True

        @type other: gentoolkit.versionmatch.VersionMatch OR
           gentoolkit.cpv.CPV
        @param other: version to compare with self's version
        @rtype: bool
        """

        if self.droprevision:
            ver1, ver2 = self.version, other.version
        else:
            ver1, ver2 = self.fullversion, other.fullversion

        return vercmp(ver2, ver1) in self.values
Exemple #32
0
	def __lt__(self, other):
		"""
		Compare different file names, first by file type and then
		for ebuilds by version and lexicographically for others.
		EBUILD < MISC < AUX < DIST < None
		"""
		if self.__class__ != other.__class__:
			raise NotImplementedError

		# Sort by file type as defined by _file_type_lt().
		if self._file_type_lt(self, other):
			return True
		elif self._file_type_lt(other, self):
			return False

		# Files have the same type.
		if self.file_type == "EBUILD":
			# Sort by version. Lowest first.
			ver = "-".join(pkgsplit(self.file_name[:-7])[1:3])
			other_ver = "-".join(pkgsplit(other.file_name[:-7])[1:3])
			return vercmp(ver, other_ver) < 0
		else:
			# Sort lexicographically.
			return self.file_name < other.file_name
Exemple #33
0
 def __automask(myroot):
     cfg = config(config_root=unicode(myroot), target_root=unicode(myroot))
     for directory in cfg.profiles:
         mask_pkgs = Packages()
         unmask_pkgs = Packages()
         pf = PackagesFile(directory)
         for package in pf.list_pkgs().list():
             if package.version:
                 if package.operator == '=' and not package.removal:
                     mask_pkgs += Package(package.name, version=package.version, operator='>')
                 if package.operator == '=' and package.removal:
                     lowest_version = str()
                     for pkg in pf.list_pkgs().lookup(package.name):
                         lowest_version = package.version if vercmp(
                             package.version, pkg.version
                         ) <= 0 else pkg.version
                     if package.version == lowest_version:
                         continue
                     else:
                         unmask_pkgs += Package(package.name, version=lowest_version, operator='<=')
         pm = PackageMaskFile(directory)
         pm.update(mask_pkgs)
         pu = PackageUnmaskFile(directory)
         pu.update(unmask_pkgs)
Exemple #34
0
    def __lt__(self, other):
        """
        Compare different file names, first by file type and then
        for ebuilds by version and lexicographically for others.
        EBUILD < MISC < AUX < DIST < None
        """
        if self.__class__ != other.__class__:
            raise NotImplementedError

        # Sort by file type as defined by _file_type_lt().
        if self._file_type_lt(self, other):
            return True
        if self._file_type_lt(other, self):
            return False

        # Files have the same type.
        if self.file_type == "EBUILD":
            # Sort by version. Lowest first.
            ver = "-".join(pkgsplit(self.file_name[:-7])[1:3])
            other_ver = "-".join(pkgsplit(other.file_name[:-7])[1:3])
            return vercmp(ver, other_ver) < 0

        # Sort lexicographically.
        return self.file_name < other.file_name
Exemple #35
0
 def __lt__(self, other):
     return vercmp(str(self), str(other)) < 0
Exemple #36
0
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None,
	minimize_slots=False):
	"""
	Takes an unreduced and reduced deplist and removes satisfied dependencies.
	Returned deplist contains steps that must be taken to satisfy dependencies.
	"""
	if trees is None:
		trees = portage.db
	writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
	if not reduced or unreduced == ["||"] or dep_eval(reduced):
		return []

	if unreduced[0] != "||":
		unresolved = []
		for x, satisfied in zip(unreduced, reduced):
			if isinstance(x, list):
				unresolved += dep_zapdeps(x, satisfied, myroot,
					use_binaries=use_binaries, trees=trees,
					minimize_slots=minimize_slots)
			elif not satisfied:
				unresolved.append(x)
		return unresolved

	# We're at a ( || atom ... ) type level and need to make a choice
	deps = unreduced[1:]
	satisfieds = reduced[1:]

	# Our preference order is for an the first item that:
	# a) contains all unmasked packages with the same key as installed packages
	# b) contains all unmasked packages
	# c) contains masked installed packages
	# d) is the first item

	preferred_installed = []
	preferred_in_graph = []
	preferred_any_slot = []
	preferred_non_installed = []
	unsat_use_in_graph = []
	unsat_use_installed = []
	unsat_use_non_installed = []
	other_installed = []
	other_installed_some = []
	other_installed_any_slot = []
	other = []

	# unsat_use_* must come after preferred_non_installed
	# for correct ordering in cases like || ( foo[a] foo[b] ).
	choice_bins = (
		preferred_in_graph,
		preferred_installed,
		preferred_any_slot,
		preferred_non_installed,
		unsat_use_in_graph,
		unsat_use_installed,
		unsat_use_non_installed,
		other_installed,
		other_installed_some,
		other_installed_any_slot,
		other,
	)

	# Alias the trees we'll be checking availability against
	parent   = trees[myroot].get("parent")
	priority = trees[myroot].get("priority")
	graph_db = trees[myroot].get("graph_db")
	graph    = trees[myroot].get("graph")
	pkg_use_enabled = trees[myroot].get("pkg_use_enabled")
	want_update_pkg = trees[myroot].get("want_update_pkg")
	downgrade_probe = trees[myroot].get("downgrade_probe")
	vardb = None
	if "vartree" in trees[myroot]:
		vardb = trees[myroot]["vartree"].dbapi
	if use_binaries:
		mydbapi = trees[myroot]["bintree"].dbapi
	else:
		mydbapi = trees[myroot]["porttree"].dbapi

	try:
		mydbapi_match_pkgs = mydbapi.match_pkgs
	except AttributeError:
		def mydbapi_match_pkgs(atom):
			return [mydbapi._pkg_str(cpv, atom.repo)
				for cpv in mydbapi.match(atom)]

	# Sort the deps into installed, not installed but already
	# in the graph and other, not installed and not in the graph
	# and other, with values of [[required_atom], availablility]
	for x, satisfied in zip(deps, satisfieds):
		if isinstance(x, list):
			atoms = dep_zapdeps(x, satisfied, myroot,
				use_binaries=use_binaries, trees=trees,
				minimize_slots=minimize_slots)
		else:
			atoms = [x]
		if vardb is None:
			# When called by repoman, we can simply return the first choice
			# because dep_eval() handles preference selection.
			return atoms

		all_available = True
		all_use_satisfied = True
		all_use_unmasked = True
		conflict_downgrade = False
		installed_downgrade = False
		slot_atoms = collections.defaultdict(list)
		slot_map = {}
		cp_map = {}
		for atom in atoms:
			if atom.blocker:
				continue
			# Ignore USE dependencies here since we don't want USE
			# settings to adversely affect || preference evaluation.
			avail_pkg = mydbapi_match_pkgs(atom.without_use)
			if avail_pkg:
				avail_pkg = avail_pkg[-1] # highest (ascending order)
				avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))
			if not avail_pkg:
				all_available = False
				all_use_satisfied = False
				break

			if graph_db is not None and downgrade_probe is not None:
				slot_matches = graph_db.match_pkgs(avail_slot)
				if (len(slot_matches) > 1 and
					avail_pkg < slot_matches[-1] and
					not downgrade_probe(avail_pkg)):
					# If a downgrade is not desirable, then avoid a
					# choice that pulls in a lower version involved
					# in a slot conflict (bug #531656).
					conflict_downgrade = True

			if atom.use:
				avail_pkg_use = mydbapi_match_pkgs(atom)
				if not avail_pkg_use:
					all_use_satisfied = False

					if pkg_use_enabled is not None:
						# Check which USE flags cause the match to fail,
						# so we can prioritize choices that do not
						# require changes to use.mask or use.force
						# (see bug #515584).
						violated_atom = atom.violated_conditionals(
							pkg_use_enabled(avail_pkg),
							avail_pkg.iuse.is_valid_flag)

						# Note that violated_atom.use can be None here,
						# since evaluation can collapse conditional USE
						# deps that cause the match to fail due to
						# missing IUSE (match uses atom.unevaluated_atom
						# to detect such missing IUSE).
						if violated_atom.use is not None:
							for flag in violated_atom.use.enabled:
								if flag in avail_pkg.use.mask:
									all_use_unmasked = False
									break
							else:
								for flag in violated_atom.use.disabled:
									if flag in avail_pkg.use.force and \
										flag not in avail_pkg.use.mask:
										all_use_unmasked = False
										break
				else:
					# highest (ascending order)
					avail_pkg_use = avail_pkg_use[-1]
					if avail_pkg_use != avail_pkg:
						avail_pkg = avail_pkg_use
					avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))

			if downgrade_probe is not None and graph is not None:
				highest_in_slot = mydbapi_match_pkgs(avail_slot)
				highest_in_slot = (highest_in_slot[-1]
					if highest_in_slot else None)
				if (avail_pkg and highest_in_slot and
					avail_pkg < highest_in_slot and
					not downgrade_probe(avail_pkg) and
					(highest_in_slot.installed or
					highest_in_slot in graph)):
					installed_downgrade = True

			slot_map[avail_slot] = avail_pkg
			slot_atoms[avail_slot].append(atom)
			highest_cpv = cp_map.get(avail_pkg.cp)
			all_match_current = None
			all_match_previous = None
			if (highest_cpv is not None and
				highest_cpv.slot == avail_pkg.slot):
				# If possible, make the package selection internally
				# consistent by choosing a package that satisfies all
				# atoms which match a package in the same slot. Later on,
				# the package version chosen here is used in the
				# has_upgrade/has_downgrade logic to prefer choices with
				# upgrades, and a package choice that is not internally
				# consistent will lead the has_upgrade/has_downgrade logic
				# to produce invalid results (see bug 600346).
				all_match_current = all(a.match(avail_pkg)
					for a in slot_atoms[avail_slot])
				all_match_previous = all(a.match(highest_cpv)
					for a in slot_atoms[avail_slot])
				if all_match_previous and not all_match_current:
					continue

			current_higher = (highest_cpv is None or
				vercmp(avail_pkg.version, highest_cpv.version) > 0)

			if current_higher or (all_match_current and not all_match_previous):
				cp_map[avail_pkg.cp] = avail_pkg

		new_slot_count = (len(slot_map) if graph_db is None else
			sum(not graph_db.match_pkgs(slot_atom) for slot_atom in slot_map
			if not slot_atom.cp.startswith("virtual/")))

		this_choice = _dep_choice(atoms=atoms, slot_map=slot_map,
			cp_map=cp_map, all_available=all_available,
			all_installed_slots=False,
			new_slot_count=new_slot_count)
		if all_available:
			# The "all installed" criterion is not version or slot specific.
			# If any version of a package is already in the graph then we
			# assume that it is preferred over other possible packages choices.
			all_installed = True
			for atom in set(Atom(atom.cp) for atom in atoms \
				if not atom.blocker):
				# New-style virtuals have zero cost to install.
				if not vardb.match(atom) and not atom.startswith("virtual/"):
					all_installed = False
					break
			all_installed_slots = False
			if all_installed:
				all_installed_slots = True
				for slot_atom in slot_map:
					# New-style virtuals have zero cost to install.
					if not vardb.match(slot_atom) and \
						not slot_atom.startswith("virtual/"):
						all_installed_slots = False
						break
			this_choice.all_installed_slots = all_installed_slots
			if graph_db is None:
				if all_use_satisfied:
					if all_installed:
						if all_installed_slots:
							preferred_installed.append(this_choice)
						else:
							preferred_any_slot.append(this_choice)
					else:
						preferred_non_installed.append(this_choice)
				else:
					if not all_use_unmasked:
						other.append(this_choice)
					elif all_installed_slots:
						unsat_use_installed.append(this_choice)
					else:
						unsat_use_non_installed.append(this_choice)
			elif conflict_downgrade or installed_downgrade:
				other.append(this_choice)
			else:
				all_in_graph = True
				for atom in atoms:
					# New-style virtuals have zero cost to install.
					if atom.blocker or atom.cp.startswith("virtual/"):
						continue
					# We check if the matched package has actually been
					# added to the digraph, in order to distinguish between
					# those packages and installed packages that may need
					# to be uninstalled in order to resolve blockers.
					if not any(pkg in graph for pkg in
						graph_db.match_pkgs(atom)):
						all_in_graph = False
						break
				circular_atom = None
				if not (parent is None or priority is None) and \
					(parent.onlydeps or
					(all_in_graph and priority.buildtime and
					not (priority.satisfied or priority.optional))):
						# Check if the atom would result in a direct circular
						# dependency and try to avoid that if it seems likely
						# to be unresolvable. This is only relevant for
						# buildtime deps that aren't already satisfied by an
						# installed package.
						cpv_slot_list = [parent]
						for atom in atoms:
							if atom.blocker:
								continue
							if vardb.match(atom):
								# If the atom is satisfied by an installed
								# version then it's not a circular dep.
								continue
							if atom.cp != parent.cp:
								continue
							if match_from_list(atom, cpv_slot_list):
								circular_atom = atom
								break
				if circular_atom is not None:
					other.append(this_choice)
				else:
					if all_use_satisfied:
						if all_in_graph:
							preferred_in_graph.append(this_choice)
						elif all_installed:
							if all_installed_slots:
								preferred_installed.append(this_choice)
							elif parent is None or want_update_pkg is None:
								preferred_any_slot.append(this_choice)
							else:
								# When appropriate, prefer a slot that is not
								# installed yet for bug #478188.
								want_update = True
								for slot_atom, avail_pkg in slot_map.items():
									if avail_pkg in graph:
										continue
									# New-style virtuals have zero cost to install.
									if slot_atom.startswith("virtual/") or \
										vardb.match(slot_atom):
										continue
									if not want_update_pkg(parent, avail_pkg):
										want_update = False
										break

								if want_update:
									preferred_installed.append(this_choice)
								else:
									preferred_any_slot.append(this_choice)
						else:
							preferred_non_installed.append(this_choice)
					else:
						if not all_use_unmasked:
							other.append(this_choice)
						elif all_in_graph:
							unsat_use_in_graph.append(this_choice)
						elif all_installed_slots:
							unsat_use_installed.append(this_choice)
						else:
							unsat_use_non_installed.append(this_choice)
		else:
			all_installed = True
			some_installed = False
			for atom in atoms:
				if not atom.blocker:
					if vardb.match(atom):
						some_installed = True
					else:
						all_installed = False

			if all_installed:
				this_choice.all_installed_slots = True
				other_installed.append(this_choice)
			elif some_installed:
				other_installed_some.append(this_choice)

			# Use Atom(atom.cp) for a somewhat "fuzzy" match, since
			# the whole atom may be too specific. For example, see
			# bug #522652, where using the whole atom leads to an
			# unsatisfiable choice.
			elif any(vardb.match(Atom(atom.cp)) for atom in atoms
				if not atom.blocker):
				other_installed_any_slot.append(this_choice)
			else:
				other.append(this_choice)

	# Prefer choices which contain upgrades to higher slots. This helps
	# for deps such as || ( foo:1 foo:2 ), where we want to prefer the
	# atom which matches the higher version rather than the atom furthest
	# to the left. Sorting is done separately for each of choice_bins, so
	# as not to interfere with the ordering of the bins. Because of the
	# bin separation, the main function of this code is to allow
	# --depclean to remove old slots (rather than to pull in new slots).
	for choices in choice_bins:
		if len(choices) < 2:
			continue

		sort_keys = []
		# Prefer choices with all_installed_slots for bug #480736.
		sort_keys.append(lambda x: not x.all_installed_slots)

		if minimize_slots:
			# Prefer choices having fewer new slots. When used with DNF form,
			# this can eliminate unecessary packages that depclean would
			# ultimately eliminate (see bug 632026). Only use this behavior
			# when deemed necessary by the caller, since this will discard the
			# order specified in the ebuild, and the preferences specified
			# there can serve as a crucial sources of guidance (see bug 645002).

			# NOTE: Under some conditions, new_slot_count value may have some
			# variance from one calculation to the next because it depends on
			# the order that packages are added to the graph. This variance can
			# contribute to outcomes that appear to be random. Meanwhile,
			# the order specified in the ebuild is without variance, so it
			# does not have this problem.
			sort_keys.append(lambda x: x.new_slot_count)

		choices.sort(key=lambda x: tuple(f(x) for f in sort_keys))
		for choice_1 in choices[1:]:
			cps = set(choice_1.cp_map)
			for choice_2 in choices:
				if choice_1 is choice_2:
					# choice_1 will not be promoted, so move on
					break
				intersecting_cps = cps.intersection(choice_2.cp_map)
				if not intersecting_cps:
					continue
				has_upgrade = False
				has_downgrade = False
				for cp in intersecting_cps:
					version_1 = choice_1.cp_map[cp]
					version_2 = choice_2.cp_map[cp]
					difference = vercmp(version_1.version, version_2.version)
					if difference != 0:
						if difference > 0:
							has_upgrade = True
						else:
							has_downgrade = True
							break
				if has_upgrade and not has_downgrade:
					# promote choice_1 in front of choice_2
					choices.remove(choice_1)
					index_2 = choices.index(choice_2)
					choices.insert(index_2, choice_1)
					break

	for allow_masked in (False, True):
		for choices in choice_bins:
			for choice in choices:
				if choice.all_available or allow_masked:
					return choice.atoms

	assert(False) # This point should not be reachable
Exemple #37
0
    def _prepare_conflict_msg_and_check_for_specificity(self):
        """
		Print all slot conflicts in a human readable way.
		"""
        _pkg_use_enabled = self.depgraph._pkg_use_enabled
        msg = self.conflict_msg
        indent = "  "
        msg.append("\n!!! Multiple package instances within a single " + \
         "package slot have been pulled\n")
        msg.append("!!! into the dependency graph, resulting" + \
         " in a slot conflict:\n\n")

        for (slot_atom, root), pkgs \
         in self.slot_collision_info.items():
            msg.append(str(slot_atom))
            if root != '/':
                msg.append(" for %s" % (root, ))
            msg.append("\n\n")

            for pkg in pkgs:
                msg.append(indent)
                msg.append(str(pkg))
                parent_atoms = self.all_parents.get(pkg)
                if parent_atoms:
                    #Create a list of collision reasons and map them to sets
                    #of atoms.
                    #Possible reasons:
                    #	("version", "ge") for operator >=, >
                    #	("version", "eq") for operator =, ~
                    #	("version", "le") for operator <=, <
                    #	("use", "<some use flag>") for unmet use conditionals
                    collision_reasons = {}
                    num_all_specific_atoms = 0

                    for ppkg, atom in parent_atoms:
                        atom_set = InternalPackageSet(initial_atoms=(atom, ))
                        atom_without_use_set = InternalPackageSet(
                            initial_atoms=(atom.without_use, ))

                        for other_pkg in pkgs:
                            if other_pkg == pkg:
                                continue

                            if not atom_without_use_set.findAtomForPackage(other_pkg, \
                             modified_use=_pkg_use_enabled(other_pkg)):
                                #The version range does not match.
                                sub_type = None
                                if atom.operator in (">=", ">"):
                                    sub_type = "ge"
                                elif atom.operator in ("=", "~"):
                                    sub_type = "eq"
                                elif atom.operator in ("<=", "<"):
                                    sub_type = "le"

                                atoms = collision_reasons.get(
                                    ("version", sub_type), set())
                                atoms.add((ppkg, atom, other_pkg))
                                num_all_specific_atoms += 1
                                collision_reasons[("version",
                                                   sub_type)] = atoms
                            elif not atom_set.findAtomForPackage(other_pkg, \
                             modified_use=_pkg_use_enabled(other_pkg)):
                                #Use conditionals not met.
                                violated_atom = atom.violated_conditionals(_pkg_use_enabled(other_pkg), \
                                 other_pkg.iuse.is_valid_flag)
                                for flag in violated_atom.use.enabled.union(
                                        violated_atom.use.disabled):
                                    atoms = collision_reasons.get(
                                        ("use", flag), set())
                                    atoms.add((ppkg, atom, other_pkg))
                                    collision_reasons[("use", flag)] = atoms
                                num_all_specific_atoms += 1

                    msg.append(" pulled in by\n")

                    selected_for_display = set()

                    for (type, sub_type), parents in collision_reasons.items():
                        #From each (type, sub_type) pair select at least one atom.
                        #Try to select as few atoms as possible

                        if type == "version":
                            #Find the atom with version that is as far away as possible.
                            best_matches = {}
                            for ppkg, atom, other_pkg in parents:
                                if atom.cp in best_matches:
                                    cmp = vercmp( \
                                     cpv_getversion(atom.cpv), \
                                     cpv_getversion(best_matches[atom.cp][1].cpv))

                                    if (sub_type == "ge" and  cmp > 0) \
                                     or (sub_type == "le" and cmp < 0) \
                                     or (sub_type == "eq" and cmp > 0):
                                        best_matches[atom.cp] = (ppkg, atom)
                                else:
                                    best_matches[atom.cp] = (ppkg, atom)
                            selected_for_display.update(best_matches.values())
                        elif type == "use":
                            #Prefer atoms with unconditional use deps over, because it's
                            #not possible to change them on the parent, which means there
                            #are fewer possible solutions.
                            use = sub_type
                            hard_matches = set()
                            conditional_matches = set()
                            for ppkg, atom, other_pkg in parents:
                                parent_use = None
                                if isinstance(ppkg, Package):
                                    parent_use = _pkg_use_enabled(ppkg)
                                violated_atom = atom.unevaluated_atom.violated_conditionals( \
                                 _pkg_use_enabled(other_pkg), other_pkg.iuse.is_valid_flag,
                                 parent_use=parent_use)
                                if use in violated_atom.use.enabled.union(
                                        violated_atom.use.disabled):
                                    hard_matches.add((ppkg, atom))
                                else:
                                    conditional_matches.add((ppkg, atom))

                            if hard_matches:
                                matches = hard_matches
                            else:
                                matches = conditional_matches

                            if not selected_for_display.intersection(matches):
                                selected_for_display.add(matches.pop())

                    def highlight_violations(atom, version, use=[]):
                        """Colorize parts of an atom"""
                        atom_str = str(atom)
                        if version:
                            op = atom.operator
                            ver = cpv_getversion(atom.cpv)
                            slot = atom.slot
                            atom_str = atom_str.replace(
                                op, colorize("BAD", op), 1)

                            start = atom_str.rfind(ver)
                            end = start + len(ver)
                            atom_str = atom_str[:start] + \
                             colorize("BAD", ver) + \
                             atom_str[end+1:]
                            if slot:
                                atom_str = atom_str.replace(
                                    ":" + slot, colorize("BAD", ":" + slot))

                        if use and atom.use.tokens:
                            use_part_start = atom_str.find("[")
                            use_part_end = atom_str.find("]")

                            new_tokens = []
                            for token in atom.use.tokens:
                                if token.lstrip("-!").rstrip("=?") in use:
                                    new_tokens.append(colorize("BAD", token))
                                else:
                                    new_tokens.append(token)

                            atom_str = atom_str[:use_part_start] \
                             + "[%s]" % (",".join(new_tokens),) + \
                             atom_str[use_part_end+1:]

                        return atom_str

                    for parent_atom in selected_for_display:
                        parent, atom = parent_atom
                        msg.append(2 * indent)
                        if isinstance(parent, (PackageArg, AtomArg)):
                            # For PackageArg and AtomArg types, it's
                            # redundant to display the atom attribute.
                            msg.append(str(parent))
                        else:
                            # Display the specific atom from SetArg or
                            # Package types.
                            version_violated = False
                            use = []
                            for type, sub_type in collision_reasons:
                                if type == "version":
                                    for x in collision_reasons[(type,
                                                                sub_type)]:
                                        if ppkg == x[0] and atom == x[1]:
                                            version_violated = True
                                elif type == "use":
                                    use.append(sub_type)

                            atom_str = highlight_violations(
                                atom.unevaluated_atom, version_violated, use)

                            if version_violated:
                                self.is_a_version_conflict = True

                            msg.append("%s required by %s" %
                                       (atom_str, parent))
                        msg.append("\n")

                    if not selected_for_display:
                        msg.append(2 * indent)
                        msg.append(
                            "(no parents that aren't satisfied by other packages in this slot)\n"
                        )
                        self.conflict_is_unspecific = True

                    omitted_parents = num_all_specific_atoms - len(
                        selected_for_display)
                    if omitted_parents:
                        msg.append(2 * indent)
                        if len(selected_for_display) > 1:
                            msg.append(
                                "(and %d more with the same problems)\n" %
                                omitted_parents)
                        else:
                            msg.append(
                                "(and %d more with the same problem)\n" %
                                omitted_parents)
                else:
                    msg.append(" (no parents)\n")
                msg.append("\n")
        msg.append("\n")
Exemple #38
0
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
    """
	Takes an unreduced and reduced deplist and removes satisfied dependencies.
	Returned deplist contains steps that must be taken to satisfy dependencies.
	"""
    if trees is None:
        trees = portage.db
    writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
    if not reduced or unreduced == ["||"] or dep_eval(reduced):
        return []

    if unreduced[0] != "||":
        unresolved = []
        for x, satisfied in zip(unreduced, reduced):
            if isinstance(x, list):
                unresolved += dep_zapdeps(x, satisfied, myroot, use_binaries=use_binaries, trees=trees)
            elif not satisfied:
                unresolved.append(x)
        return unresolved

        # We're at a ( || atom ... ) type level and need to make a choice
    deps = unreduced[1:]
    satisfieds = reduced[1:]

    # Our preference order is for an the first item that:
    # a) contains all unmasked packages with the same key as installed packages
    # b) contains all unmasked packages
    # c) contains masked installed packages
    # d) is the first item

    preferred_installed = []
    preferred_in_graph = []
    preferred_any_slot = []
    preferred_non_installed = []
    unsat_use_in_graph = []
    unsat_use_installed = []
    unsat_use_non_installed = []
    other_installed = []
    other_installed_some = []
    other = []

    # unsat_use_* must come after preferred_non_installed
    # for correct ordering in cases like || ( foo[a] foo[b] ).
    choice_bins = (
        preferred_in_graph,
        preferred_installed,
        preferred_any_slot,
        preferred_non_installed,
        unsat_use_in_graph,
        unsat_use_installed,
        unsat_use_non_installed,
        other_installed,
        other_installed_some,
        other,
    )

    # Alias the trees we'll be checking availability against
    parent = trees[myroot].get("parent")
    priority = trees[myroot].get("priority")
    graph_db = trees[myroot].get("graph_db")
    graph = trees[myroot].get("graph")
    want_update_pkg = trees[myroot].get("want_update_pkg")
    vardb = None
    if "vartree" in trees[myroot]:
        vardb = trees[myroot]["vartree"].dbapi
    if use_binaries:
        mydbapi = trees[myroot]["bintree"].dbapi
    else:
        mydbapi = trees[myroot]["porttree"].dbapi

    try:
        mydbapi_match_pkgs = mydbapi.match_pkgs
    except AttributeError:

        def mydbapi_match_pkgs(atom):
            return [mydbapi._pkg_str(cpv, atom.repo) for cpv in mydbapi.match(atom)]

            # Sort the deps into installed, not installed but already
            # in the graph and other, not installed and not in the graph
            # and other, with values of [[required_atom], availablility]

    for x, satisfied in zip(deps, satisfieds):
        if isinstance(x, list):
            atoms = dep_zapdeps(x, satisfied, myroot, use_binaries=use_binaries, trees=trees)
        else:
            atoms = [x]
        if vardb is None:
            # When called by repoman, we can simply return the first choice
            # because dep_eval() handles preference selection.
            return atoms

        all_available = True
        all_use_satisfied = True
        slot_map = {}
        cp_map = {}
        for atom in atoms:
            if atom.blocker:
                continue
                # Ignore USE dependencies here since we don't want USE
                # settings to adversely affect || preference evaluation.
            avail_pkg = mydbapi_match_pkgs(atom.without_use)
            if avail_pkg:
                avail_pkg = avail_pkg[-1]  # highest (ascending order)
                avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))
            if not avail_pkg:
                all_available = False
                all_use_satisfied = False
                break

            if atom.use:
                avail_pkg_use = mydbapi_match_pkgs(atom)
                if not avail_pkg_use:
                    all_use_satisfied = False
                else:
                    # highest (ascending order)
                    avail_pkg_use = avail_pkg_use[-1]
                    if avail_pkg_use != avail_pkg:
                        avail_pkg = avail_pkg_use
                    avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))

            slot_map[avail_slot] = avail_pkg
            highest_cpv = cp_map.get(avail_pkg.cp)
            if highest_cpv is None or vercmp(avail_pkg.version, highest_cpv.version) > 0:
                cp_map[avail_pkg.cp] = avail_pkg

        this_choice = _dep_choice(
            atoms=atoms, slot_map=slot_map, cp_map=cp_map, all_available=all_available, all_installed_slots=False
        )
        if all_available:
            # The "all installed" criterion is not version or slot specific.
            # If any version of a package is already in the graph then we
            # assume that it is preferred over other possible packages choices.
            all_installed = True
            for atom in set(Atom(atom.cp) for atom in atoms if not atom.blocker):
                # New-style virtuals have zero cost to install.
                if not vardb.match(atom) and not atom.startswith("virtual/"):
                    all_installed = False
                    break
            all_installed_slots = False
            if all_installed:
                all_installed_slots = True
                for slot_atom in slot_map:
                    # New-style virtuals have zero cost to install.
                    if not vardb.match(slot_atom) and not slot_atom.startswith("virtual/"):
                        all_installed_slots = False
                        break
            this_choice.all_installed_slots = all_installed_slots
            if graph_db is None:
                if all_use_satisfied:
                    if all_installed:
                        if all_installed_slots:
                            preferred_installed.append(this_choice)
                        else:
                            preferred_any_slot.append(this_choice)
                    else:
                        preferred_non_installed.append(this_choice)
                else:
                    if all_installed_slots:
                        unsat_use_installed.append(this_choice)
                    else:
                        unsat_use_non_installed.append(this_choice)
            else:
                all_in_graph = True
                for atom in atoms:
                    # New-style virtuals have zero cost to install.
                    if atom.blocker or atom.cp.startswith("virtual/"):
                        continue
                        # We check if the matched package has actually been
                        # added to the digraph, in order to distinguish between
                        # those packages and installed packages that may need
                        # to be uninstalled in order to resolve blockers.
                    if not any(pkg in graph for pkg in graph_db.match_pkgs(atom)):
                        all_in_graph = False
                        break
                circular_atom = None
                if all_in_graph:
                    if parent is None or priority is None:
                        pass
                    elif priority.buildtime and not (priority.satisfied or priority.optional):
                        # Check if the atom would result in a direct circular
                        # dependency and try to avoid that if it seems likely
                        # to be unresolvable. This is only relevant for
                        # buildtime deps that aren't already satisfied by an
                        # installed package.
                        cpv_slot_list = [parent]
                        for atom in atoms:
                            if atom.blocker:
                                continue
                            if vardb.match(atom):
                                # If the atom is satisfied by an installed
                                # version then it's not a circular dep.
                                continue
                            if atom.cp != parent.cp:
                                continue
                            if match_from_list(atom, cpv_slot_list):
                                circular_atom = atom
                                break
                if circular_atom is not None:
                    other.append(this_choice)
                else:
                    if all_use_satisfied:
                        if all_in_graph:
                            preferred_in_graph.append(this_choice)
                        elif all_installed:
                            if all_installed_slots:
                                preferred_installed.append(this_choice)
                            elif parent is None or want_update_pkg is None:
                                preferred_any_slot.append(this_choice)
                            else:
                                # When appropriate, prefer a slot that is not
                                # installed yet for bug #478188.
                                want_update = True
                                for slot_atom, avail_pkg in slot_map.items():
                                    if avail_pkg in graph:
                                        continue
                                        # New-style virtuals have zero cost to install.
                                    if slot_atom.startswith("virtual/") or vardb.match(slot_atom):
                                        continue
                                    if not want_update_pkg(parent, avail_pkg):
                                        want_update = False
                                        break

                                if want_update:
                                    preferred_installed.append(this_choice)
                                else:
                                    preferred_any_slot.append(this_choice)
                        else:
                            preferred_non_installed.append(this_choice)
                    else:
                        if all_in_graph:
                            unsat_use_in_graph.append(this_choice)
                        elif all_installed_slots:
                            unsat_use_installed.append(this_choice)
                        else:
                            unsat_use_non_installed.append(this_choice)
        else:
            all_installed = True
            some_installed = False
            for atom in atoms:
                if not atom.blocker:
                    if vardb.match(atom):
                        some_installed = True
                    else:
                        all_installed = False

            if all_installed:
                this_choice.all_installed_slots = True
                other_installed.append(this_choice)
            elif some_installed:
                other_installed_some.append(this_choice)
            else:
                other.append(this_choice)

                # Prefer choices which contain upgrades to higher slots. This helps
                # for deps such as || ( foo:1 foo:2 ), where we want to prefer the
                # atom which matches the higher version rather than the atom furthest
                # to the left. Sorting is done separately for each of choice_bins, so
                # as not to interfere with the ordering of the bins. Because of the
                # bin separation, the main function of this code is to allow
                # --depclean to remove old slots (rather than to pull in new slots).
    for choices in choice_bins:
        if len(choices) < 2:
            continue
            # Prefer choices with all_installed_slots for bug #480736.
        choices.sort(key=operator.attrgetter("all_installed_slots"), reverse=True)
        for choice_1 in choices[1:]:
            cps = set(choice_1.cp_map)
            for choice_2 in choices:
                if choice_1 is choice_2:
                    # choice_1 will not be promoted, so move on
                    break
                intersecting_cps = cps.intersection(choice_2.cp_map)
                if not intersecting_cps:
                    continue
                has_upgrade = False
                has_downgrade = False
                for cp in intersecting_cps:
                    version_1 = choice_1.cp_map[cp]
                    version_2 = choice_2.cp_map[cp]
                    difference = vercmp(version_1.version, version_2.version)
                    if difference != 0:
                        if difference > 0:
                            has_upgrade = True
                        else:
                            has_downgrade = True
                            break
                if has_upgrade and not has_downgrade:
                    # promote choice_1 in front of choice_2
                    choices.remove(choice_1)
                    index_2 = choices.index(choice_2)
                    choices.insert(index_2, choice_1)
                    break

    for allow_masked in (False, True):
        for choices in choice_bins:
            for choice in choices:
                if choice.all_available or allow_masked:
                    return choice.atoms

    assert False  # This point should not be reachable
Exemple #39
0
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
    """
	Takes an unreduced and reduced deplist and removes satisfied dependencies.
	Returned deplist contains steps that must be taken to satisfy dependencies.
	"""
    if trees is None:
        trees = portage.db
    writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
    if not reduced or unreduced == ["||"] or dep_eval(reduced):
        return []

    if unreduced[0] != "||":
        unresolved = []
        for x, satisfied in zip(unreduced, reduced):
            if isinstance(x, list):
                unresolved += dep_zapdeps(x,
                                          satisfied,
                                          myroot,
                                          use_binaries=use_binaries,
                                          trees=trees)
            elif not satisfied:
                unresolved.append(x)
        return unresolved

    # We're at a ( || atom ... ) type level and need to make a choice
    deps = unreduced[1:]
    satisfieds = reduced[1:]

    # Our preference order is for an the first item that:
    # a) contains all unmasked packages with the same key as installed packages
    # b) contains all unmasked packages
    # c) contains masked installed packages
    # d) is the first item

    preferred_installed = []
    preferred_in_graph = []
    preferred_any_slot = []
    preferred_non_installed = []
    unsat_use_in_graph = []
    unsat_use_installed = []
    unsat_use_non_installed = []
    other_installed = []
    other_installed_some = []
    other_installed_any_slot = []
    other = []

    # unsat_use_* must come after preferred_non_installed
    # for correct ordering in cases like || ( foo[a] foo[b] ).
    choice_bins = (
        preferred_in_graph,
        preferred_installed,
        preferred_any_slot,
        preferred_non_installed,
        unsat_use_in_graph,
        unsat_use_installed,
        unsat_use_non_installed,
        other_installed,
        other_installed_some,
        other_installed_any_slot,
        other,
    )

    # Alias the trees we'll be checking availability against
    parent = trees[myroot].get("parent")
    priority = trees[myroot].get("priority")
    graph_db = trees[myroot].get("graph_db")
    graph = trees[myroot].get("graph")
    pkg_use_enabled = trees[myroot].get("pkg_use_enabled")
    want_update_pkg = trees[myroot].get("want_update_pkg")
    downgrade_probe = trees[myroot].get("downgrade_probe")
    vardb = None
    if "vartree" in trees[myroot]:
        vardb = trees[myroot]["vartree"].dbapi
    if use_binaries:
        mydbapi = trees[myroot]["bintree"].dbapi
    else:
        mydbapi = trees[myroot]["porttree"].dbapi

    try:
        mydbapi_match_pkgs = mydbapi.match_pkgs
    except AttributeError:

        def mydbapi_match_pkgs(atom):
            return [
                mydbapi._pkg_str(cpv, atom.repo) for cpv in mydbapi.match(atom)
            ]

    # Sort the deps into installed, not installed but already
    # in the graph and other, not installed and not in the graph
    # and other, with values of [[required_atom], availablility]
    for x, satisfied in zip(deps, satisfieds):
        if isinstance(x, list):
            atoms = dep_zapdeps(x,
                                satisfied,
                                myroot,
                                use_binaries=use_binaries,
                                trees=trees)
        else:
            atoms = [x]
        if vardb is None:
            # When called by repoman, we can simply return the first choice
            # because dep_eval() handles preference selection.
            return atoms

        all_available = True
        all_use_satisfied = True
        all_use_unmasked = True
        conflict_downgrade = False
        slot_map = {}
        cp_map = {}
        for atom in atoms:
            if atom.blocker:
                continue
            # Ignore USE dependencies here since we don't want USE
            # settings to adversely affect || preference evaluation.
            avail_pkg = mydbapi_match_pkgs(atom.without_use)
            if avail_pkg:
                avail_pkg = avail_pkg[-1]  # highest (ascending order)
                avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))
            if not avail_pkg:
                all_available = False
                all_use_satisfied = False
                break

            if graph_db is not None and downgrade_probe is not None:
                slot_matches = graph_db.match_pkgs(avail_slot)
                if (len(slot_matches) > 1 and avail_pkg < slot_matches[-1]
                        and not downgrade_probe(avail_pkg)):
                    # If a downgrade is not desirable, then avoid a
                    # choice that pulls in a lower version involved
                    # in a slot conflict (bug #531656).
                    conflict_downgrade = True

            if atom.use:
                avail_pkg_use = mydbapi_match_pkgs(atom)
                if not avail_pkg_use:
                    all_use_satisfied = False

                    if pkg_use_enabled is not None:
                        # Check which USE flags cause the match to fail,
                        # so we can prioritize choices that do not
                        # require changes to use.mask or use.force
                        # (see bug #515584).
                        violated_atom = atom.violated_conditionals(
                            pkg_use_enabled(avail_pkg),
                            avail_pkg.iuse.is_valid_flag)

                        # Note that violated_atom.use can be None here,
                        # since evaluation can collapse conditional USE
                        # deps that cause the match to fail due to
                        # missing IUSE (match uses atom.unevaluated_atom
                        # to detect such missing IUSE).
                        if violated_atom.use is not None:
                            for flag in violated_atom.use.enabled:
                                if flag in avail_pkg.use.mask:
                                    all_use_unmasked = False
                                    break
                            else:
                                for flag in violated_atom.use.disabled:
                                    if flag in avail_pkg.use.force and \
                                     flag not in avail_pkg.use.mask:
                                        all_use_unmasked = False
                                        break
                else:
                    # highest (ascending order)
                    avail_pkg_use = avail_pkg_use[-1]
                    if avail_pkg_use != avail_pkg:
                        avail_pkg = avail_pkg_use
                    avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))

            slot_map[avail_slot] = avail_pkg
            highest_cpv = cp_map.get(avail_pkg.cp)
            if highest_cpv is None or \
             vercmp(avail_pkg.version, highest_cpv.version) > 0:
                cp_map[avail_pkg.cp] = avail_pkg

        this_choice = _dep_choice(atoms=atoms,
                                  slot_map=slot_map,
                                  cp_map=cp_map,
                                  all_available=all_available,
                                  all_installed_slots=False)
        if all_available:
            # The "all installed" criterion is not version or slot specific.
            # If any version of a package is already in the graph then we
            # assume that it is preferred over other possible packages choices.
            all_installed = True
            for atom in set(Atom(atom.cp) for atom in atoms \
             if not atom.blocker):
                # New-style virtuals have zero cost to install.
                if not vardb.match(atom) and not atom.startswith("virtual/"):
                    all_installed = False
                    break
            all_installed_slots = False
            if all_installed:
                all_installed_slots = True
                for slot_atom in slot_map:
                    # New-style virtuals have zero cost to install.
                    if not vardb.match(slot_atom) and \
                     not slot_atom.startswith("virtual/"):
                        all_installed_slots = False
                        break
            this_choice.all_installed_slots = all_installed_slots
            if graph_db is None:
                if all_use_satisfied:
                    if all_installed:
                        if all_installed_slots:
                            preferred_installed.append(this_choice)
                        else:
                            preferred_any_slot.append(this_choice)
                    else:
                        preferred_non_installed.append(this_choice)
                else:
                    if not all_use_unmasked:
                        other.append(this_choice)
                    elif all_installed_slots:
                        unsat_use_installed.append(this_choice)
                    else:
                        unsat_use_non_installed.append(this_choice)
            elif conflict_downgrade:
                other.append(this_choice)
            else:
                all_in_graph = True
                for atom in atoms:
                    # New-style virtuals have zero cost to install.
                    if atom.blocker or atom.cp.startswith("virtual/"):
                        continue
                    # We check if the matched package has actually been
                    # added to the digraph, in order to distinguish between
                    # those packages and installed packages that may need
                    # to be uninstalled in order to resolve blockers.
                    if not any(pkg in graph
                               for pkg in graph_db.match_pkgs(atom)):
                        all_in_graph = False
                        break
                circular_atom = None
                if not (parent is None or priority is None) and \
                 (parent.onlydeps or
                 (all_in_graph and priority.buildtime and
                 not (priority.satisfied or priority.optional))):
                    # Check if the atom would result in a direct circular
                    # dependency and try to avoid that if it seems likely
                    # to be unresolvable. This is only relevant for
                    # buildtime deps that aren't already satisfied by an
                    # installed package.
                    cpv_slot_list = [parent]
                    for atom in atoms:
                        if atom.blocker:
                            continue
                        if vardb.match(atom):
                            # If the atom is satisfied by an installed
                            # version then it's not a circular dep.
                            continue
                        if atom.cp != parent.cp:
                            continue
                        if match_from_list(atom, cpv_slot_list):
                            circular_atom = atom
                            break
                if circular_atom is not None:
                    other.append(this_choice)
                else:
                    if all_use_satisfied:
                        if all_in_graph:
                            preferred_in_graph.append(this_choice)
                        elif all_installed:
                            if all_installed_slots:
                                preferred_installed.append(this_choice)
                            elif parent is None or want_update_pkg is None:
                                preferred_any_slot.append(this_choice)
                            else:
                                # When appropriate, prefer a slot that is not
                                # installed yet for bug #478188.
                                want_update = True
                                for slot_atom, avail_pkg in slot_map.items():
                                    if avail_pkg in graph:
                                        continue
                                    # New-style virtuals have zero cost to install.
                                    if slot_atom.startswith("virtual/") or \
                                     vardb.match(slot_atom):
                                        continue
                                    if not want_update_pkg(parent, avail_pkg):
                                        want_update = False
                                        break

                                if want_update:
                                    preferred_installed.append(this_choice)
                                else:
                                    preferred_any_slot.append(this_choice)
                        else:
                            preferred_non_installed.append(this_choice)
                    else:
                        if not all_use_unmasked:
                            other.append(this_choice)
                        elif all_in_graph:
                            unsat_use_in_graph.append(this_choice)
                        elif all_installed_slots:
                            unsat_use_installed.append(this_choice)
                        else:
                            unsat_use_non_installed.append(this_choice)
        else:
            all_installed = True
            some_installed = False
            for atom in atoms:
                if not atom.blocker:
                    if vardb.match(atom):
                        some_installed = True
                    else:
                        all_installed = False

            if all_installed:
                this_choice.all_installed_slots = True
                other_installed.append(this_choice)
            elif some_installed:
                other_installed_some.append(this_choice)

            # Use Atom(atom.cp) for a somewhat "fuzzy" match, since
            # the whole atom may be too specific. For example, see
            # bug #522652, where using the whole atom leads to an
            # unsatisfiable choice.
            elif any(
                    vardb.match(Atom(atom.cp)) for atom in atoms
                    if not atom.blocker):
                other_installed_any_slot.append(this_choice)
            else:
                other.append(this_choice)

    # Prefer choices which contain upgrades to higher slots. This helps
    # for deps such as || ( foo:1 foo:2 ), where we want to prefer the
    # atom which matches the higher version rather than the atom furthest
    # to the left. Sorting is done separately for each of choice_bins, so
    # as not to interfere with the ordering of the bins. Because of the
    # bin separation, the main function of this code is to allow
    # --depclean to remove old slots (rather than to pull in new slots).
    for choices in choice_bins:
        if len(choices) < 2:
            continue
        # Prefer choices with all_installed_slots for bug #480736.
        choices.sort(key=operator.attrgetter('all_installed_slots'),
                     reverse=True)
        for choice_1 in choices[1:]:
            cps = set(choice_1.cp_map)
            for choice_2 in choices:
                if choice_1 is choice_2:
                    # choice_1 will not be promoted, so move on
                    break
                intersecting_cps = cps.intersection(choice_2.cp_map)
                if not intersecting_cps:
                    continue
                has_upgrade = False
                has_downgrade = False
                for cp in intersecting_cps:
                    version_1 = choice_1.cp_map[cp]
                    version_2 = choice_2.cp_map[cp]
                    difference = vercmp(version_1.version, version_2.version)
                    if difference != 0:
                        if difference > 0:
                            has_upgrade = True
                        else:
                            has_downgrade = True
                            break
                if has_upgrade and not has_downgrade:
                    # promote choice_1 in front of choice_2
                    choices.remove(choice_1)
                    index_2 = choices.index(choice_2)
                    choices.insert(index_2, choice_1)
                    break

    for allow_masked in (False, True):
        for choices in choice_bins:
            for choice in choices:
                if choice.all_available or allow_masked:
                    return choice.atoms

    assert (False)  # This point should not be reachable
Exemple #40
0
 def up2date(self):
     if not self.error:
         return vercmp(self.gentoo_version, self.upstream_version) == 0
Exemple #41
0
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None):
    """
	Takes an unreduced and reduced deplist and removes satisfied dependencies.
	Returned deplist contains steps that must be taken to satisfy dependencies.
	"""
    if trees is None:
        trees = portage.db
    writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
    if not reduced or unreduced == ["||"] or dep_eval(reduced):
        return []

    if unreduced[0] != "||":
        unresolved = []
        for x, satisfied in zip(unreduced, reduced):
            if isinstance(x, list):
                unresolved += dep_zapdeps(x,
                                          satisfied,
                                          myroot,
                                          use_binaries=use_binaries,
                                          trees=trees)
            elif not satisfied:
                unresolved.append(x)
        return unresolved

    # We're at a ( || atom ... ) type level and need to make a choice
    deps = unreduced[1:]
    satisfieds = reduced[1:]

    # Our preference order is for an the first item that:
    # a) contains all unmasked packages with the same key as installed packages
    # b) contains all unmasked packages
    # c) contains masked installed packages
    # d) is the first item

    preferred_installed = []
    preferred_in_graph = []
    preferred_any_slot = []
    preferred_non_installed = []
    unsat_use_in_graph = []
    unsat_use_installed = []
    unsat_use_non_installed = []
    other_installed = []
    other_installed_some = []
    other = []

    # unsat_use_* must come after preferred_non_installed
    # for correct ordering in cases like || ( foo[a] foo[b] ).
    choice_bins = (
        preferred_in_graph,
        preferred_installed,
        preferred_any_slot,
        preferred_non_installed,
        unsat_use_in_graph,
        unsat_use_installed,
        unsat_use_non_installed,
        other_installed,
        other_installed_some,
        other,
    )

    # Alias the trees we'll be checking availability against
    parent = trees[myroot].get("parent")
    priority = trees[myroot].get("priority")
    graph_db = trees[myroot].get("graph_db")
    graph = trees[myroot].get("graph")
    vardb = None
    if "vartree" in trees[myroot]:
        vardb = trees[myroot]["vartree"].dbapi
    if use_binaries:
        mydbapi = trees[myroot]["bintree"].dbapi
    else:
        mydbapi = trees[myroot]["porttree"].dbapi

    # Sort the deps into installed, not installed but already
    # in the graph and other, not installed and not in the graph
    # and other, with values of [[required_atom], availablility]
    for x, satisfied in zip(deps, satisfieds):
        if isinstance(x, list):
            atoms = dep_zapdeps(x,
                                satisfied,
                                myroot,
                                use_binaries=use_binaries,
                                trees=trees)
        else:
            atoms = [x]
        if vardb is None:
            # When called by repoman, we can simply return the first choice
            # because dep_eval() handles preference selection.
            return atoms

        all_available = True
        all_use_satisfied = True
        slot_map = {}
        cp_map = {}
        for atom in atoms:
            if atom.blocker:
                continue
            # Ignore USE dependencies here since we don't want USE
            # settings to adversely affect || preference evaluation.
            avail_pkg = mydbapi.match(atom.without_use)
            if avail_pkg:
                avail_pkg = avail_pkg[-1]  # highest (ascending order)
                avail_pkg = mydbapi._pkg_str(avail_pkg, atom.repo)
                avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))
            if not avail_pkg:
                all_available = False
                all_use_satisfied = False
                break

            if atom.use:
                avail_pkg_use = mydbapi.match(atom)
                if not avail_pkg_use:
                    all_use_satisfied = False
                else:
                    # highest (ascending order)
                    avail_pkg_use = avail_pkg_use[-1]
                    if avail_pkg_use != avail_pkg:
                        avail_pkg = avail_pkg_use
                    avail_pkg = mydbapi._pkg_str(avail_pkg, atom.repo)
                    avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))

            slot_map[avail_slot] = avail_pkg
            highest_cpv = cp_map.get(avail_pkg.cp)
            if highest_cpv is None or \
             vercmp(avail_pkg.version, highest_cpv.version) > 0:
                cp_map[avail_pkg.cp] = avail_pkg

        this_choice = (atoms, slot_map, cp_map, all_available)
        if all_available:
            # The "all installed" criterion is not version or slot specific.
            # If any version of a package is already in the graph then we
            # assume that it is preferred over other possible packages choices.
            all_installed = True
            for atom in set(Atom(atom.cp) for atom in atoms \
             if not atom.blocker):
                # New-style virtuals have zero cost to install.
                if not vardb.match(atom) and not atom.startswith("virtual/"):
                    all_installed = False
                    break
            all_installed_slots = False
            if all_installed:
                all_installed_slots = True
                for slot_atom in slot_map:
                    # New-style virtuals have zero cost to install.
                    if not vardb.match(slot_atom) and \
                     not slot_atom.startswith("virtual/"):
                        all_installed_slots = False
                        break
            if graph_db is None:
                if all_use_satisfied:
                    if all_installed:
                        if all_installed_slots:
                            preferred_installed.append(this_choice)
                        else:
                            preferred_any_slot.append(this_choice)
                    else:
                        preferred_non_installed.append(this_choice)
                else:
                    if all_installed_slots:
                        unsat_use_installed.append(this_choice)
                    else:
                        unsat_use_non_installed.append(this_choice)
            else:
                all_in_graph = True
                for slot_atom in slot_map:
                    # New-style virtuals have zero cost to install.
                    if slot_atom.startswith("virtual/"):
                        continue
                    # We check if the matched package has actually been
                    # added to the digraph, in order to distinguish between
                    # those packages and installed packages that may need
                    # to be uninstalled in order to resolve blockers.
                    graph_matches = graph_db.match_pkgs(slot_atom)
                    if not graph_matches or graph_matches[-1] not in graph:
                        all_in_graph = False
                        break
                circular_atom = None
                if all_in_graph:
                    if parent is None or priority is None:
                        pass
                    elif priority.buildtime and \
                     not (priority.satisfied or priority.optional):
                        # Check if the atom would result in a direct circular
                        # dependency and try to avoid that if it seems likely
                        # to be unresolvable. This is only relevant for
                        # buildtime deps that aren't already satisfied by an
                        # installed package.
                        cpv_slot_list = [parent]
                        for atom in atoms:
                            if atom.blocker:
                                continue
                            if vardb.match(atom):
                                # If the atom is satisfied by an installed
                                # version then it's not a circular dep.
                                continue
                            if atom.cp != parent.cp:
                                continue
                            if match_from_list(atom, cpv_slot_list):
                                circular_atom = atom
                                break
                if circular_atom is not None:
                    other.append(this_choice)
                else:
                    if all_use_satisfied:
                        if all_in_graph:
                            preferred_in_graph.append(this_choice)
                        elif all_installed:
                            if all_installed_slots:
                                preferred_installed.append(this_choice)
                            else:
                                preferred_any_slot.append(this_choice)
                        else:
                            preferred_non_installed.append(this_choice)
                    else:
                        if all_in_graph:
                            unsat_use_in_graph.append(this_choice)
                        elif all_installed_slots:
                            unsat_use_installed.append(this_choice)
                        else:
                            unsat_use_non_installed.append(this_choice)
        else:
            all_installed = True
            some_installed = False
            for atom in atoms:
                if not atom.blocker:
                    if vardb.match(atom):
                        some_installed = True
                    else:
                        all_installed = False

            if all_installed:
                other_installed.append(this_choice)
            elif some_installed:
                other_installed_some.append(this_choice)
            else:
                other.append(this_choice)

    # Prefer choices which contain upgrades to higher slots. This helps
    # for deps such as || ( foo:1 foo:2 ), where we want to prefer the
    # atom which matches the higher version rather than the atom furthest
    # to the left. Sorting is done separately for each of choice_bins, so
    # as not to interfere with the ordering of the bins. Because of the
    # bin separation, the main function of this code is to allow
    # --depclean to remove old slots (rather than to pull in new slots).
    for choices in choice_bins:
        if len(choices) < 2:
            continue
        for choice_1 in choices[1:]:
            atoms_1, slot_map_1, cp_map_1, all_available_1 = choice_1
            cps = set(cp_map_1)
            for choice_2 in choices:
                if choice_1 is choice_2:
                    # choice_1 will not be promoted, so move on
                    break
                atoms_2, slot_map_2, cp_map_2, all_available_2 = choice_2
                intersecting_cps = cps.intersection(cp_map_2)
                if not intersecting_cps:
                    continue
                has_upgrade = False
                has_downgrade = False
                for cp in intersecting_cps:
                    version_1 = cp_map_1[cp]
                    version_2 = cp_map_2[cp]
                    difference = vercmp(version_1.version, version_2.version)
                    if difference != 0:
                        if difference > 0:
                            has_upgrade = True
                        else:
                            has_downgrade = True
                            break
                if has_upgrade and not has_downgrade:
                    # promote choice_1 in front of choice_2
                    choices.remove(choice_1)
                    index_2 = choices.index(choice_2)
                    choices.insert(index_2, choice_1)
                    break

    for allow_masked in (False, True):
        for choices in choice_bins:
            for atoms, slot_map, cp_map, all_available in choices:
                if all_available or allow_masked:
                    return atoms

    assert (False)  # This point should not be reachable
Exemple #42
0
def pkgcmp_atom(pkgdir, pkg):
	ebuilds = []

	for ent in listdir(pkgdir):
		if ent.endswith(".ebuild"):
			ebuilds.append(ent)

	ppkg = basename(strip_atoms(pkg))

	revre = re.compile( ("^" + re.escape(ppkg) + "(-r\d+)?.ebuild$") )

#	print "DBG: checking for %s" % pkg
#	print "DBG: Got %i ebuilds:" % len(ebuilds)
#	print ebuilds

	for ebuild in ebuilds:
		# workaround? for - prefix
		if pkg.startswith( "-" ):
			pkg = pkg[1:]

		if pkg.startswith( ("=", "~") ):
			if pkg.startswith("~"):
				if revre.match(ebuild):
#					print "DBG: revmatch '%s' '%s'" % (pkg, ebuild)
					return 1
				else:
#					print "DBG: revmatch continue"
					continue
			if pkg.endswith("*"):
				if ebuild.startswith(ppkg):
#					print "DBG: startswith '%s' '%s'" % (pkg, ebuild)
					return 1
				else:
#					print "DBG: startswith continue"
					continue
			else:
				if ebuild == (ppkg + ".ebuild"):
#					print "DBG: '%s' == '%s'" % (ppkg, ppkg)
					return 1
				else:
#					print "DBG: == continue"
					continue

		if pkg.startswith( (">=", ">", "<=", "<") ):
			plain = strip_atoms(pkg)

			mypkg = pkgsplit(plain)
			ourpkg = pkgsplit(ebuild.rstrip(".ebuild"))

			mypkgv = mypkg[1]
			if mypkg[2] != "r0":
				mypkgv = mypkgv + "-" + mypkg[2]

			ourpkgv = ourpkg[1]
			if ourpkg[2] != "r0":
				ourpkgv = ourpkgv + "-" + ourpkg[2]

#			print "MYPKGV:", mypkgv, "OURPKGV:", ourpkgv, "RESULT 'vercmp('%s', '%s'): %i" % (mypkgv, ourpkgv, vercmp(mypkgv, ourpkgv))

			if pkg.startswith(">="):
				if vercmp(mypkgv, ourpkgv) <= 0:
#					print "HIT: '%s' >= '%s'" % (ourpkg, mypkg)
					return 1
				else:
#					print ">= continue"
					continue
			if pkg.startswith(">") and not pkg.startswith(">="):
				if vercmp(mypkgv, ourpkgv) < 0:
#					print "HIT: '%s' > '%s'" % (ourpkg, mypkg)
					return 1
				else:
#					print "> continue"
					continue
			if pkg.startswith("<="):
				if vercmp(mypkgv, ourpkgv) >= 0:
#					print "HIT: '%s' <= '%s'" % (ourpkg, mypkg)
					return 1
				else:
#					print "<= continue"
					continue
			if pkg.startswith("<") and not pkg.startswith("<="):
				if vercmp(mypkgv, ourpkgv) > 0:
#					print "HIT: '%s' < '%s'" % (ourpkg, mypkg)
					return 1
				else:
#					print "< continue"
					continue

#	print "Nothing found... '%s' is invalid" % pkg
	return 0
	def _prepare_conflict_msg_and_check_for_specificity(self):
		"""
		Print all slot conflicts in a human readable way.
		"""
		_pkg_use_enabled = self.depgraph._pkg_use_enabled
		msg = self.conflict_msg
		indent = "  "
		msg.append("\n!!! Multiple package instances within a single " + \
			"package slot have been pulled\n")
		msg.append("!!! into the dependency graph, resulting" + \
			" in a slot conflict:\n\n")

		for (slot_atom, root), pkgs \
			in self.slot_collision_info.items():
			msg.append(str(slot_atom))
			if root != '/':
				msg.append(" for %s" % (root,))
			msg.append("\n\n")

			for pkg in pkgs:
				msg.append(indent)
				msg.append(str(pkg))
				parent_atoms = self.all_parents.get(pkg)
				if parent_atoms:
					#Create a list of collision reasons and map them to sets
					#of atoms.
					#Possible reasons:
					#	("version", "ge") for operator >=, >
					#	("version", "eq") for operator =, ~
					#	("version", "le") for operator <=, <
					#	("use", "<some use flag>") for unmet use conditionals
					collision_reasons = {}
					num_all_specific_atoms = 0

					for ppkg, atom in parent_atoms:
						atom_set = InternalPackageSet(initial_atoms=(atom,))
						atom_without_use_set = InternalPackageSet(initial_atoms=(atom.without_use,))

						for other_pkg in pkgs:
							if other_pkg == pkg:
								continue

							if not atom_without_use_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
								#The version range does not match.
								sub_type = None
								if atom.operator in (">=", ">"):
									sub_type = "ge"
								elif atom.operator in ("=", "~"):
									sub_type = "eq"
								elif atom.operator in ("<=", "<"):
									sub_type = "le"

								atoms = collision_reasons.get(("version", sub_type), set())
								atoms.add((ppkg, atom, other_pkg))
								num_all_specific_atoms += 1
								collision_reasons[("version", sub_type)] = atoms
							elif not atom_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
								#Use conditionals not met.
								violated_atom = atom.violated_conditionals(_pkg_use_enabled(other_pkg), \
									other_pkg.iuse.is_valid_flag)
								for flag in violated_atom.use.enabled.union(violated_atom.use.disabled):
									atoms = collision_reasons.get(("use", flag), set())
									atoms.add((ppkg, atom, other_pkg))
									collision_reasons[("use", flag)] = atoms
								num_all_specific_atoms += 1

					msg.append(" pulled in by\n")

					selected_for_display = set()

					for (type, sub_type), parents in collision_reasons.items():
						#From each (type, sub_type) pair select at least one atom.
						#Try to select as few atoms as possible

						if type == "version":
							#Find the atom with version that is as far away as possible.
							best_matches = {}
							for ppkg, atom, other_pkg in parents:
								if atom.cp in best_matches:
									cmp = vercmp( \
										cpv_getversion(atom.cpv), \
										cpv_getversion(best_matches[atom.cp][1].cpv))

									if (sub_type == "ge" and  cmp > 0) \
										or (sub_type == "le" and cmp < 0) \
										or (sub_type == "eq" and cmp > 0):
										best_matches[atom.cp] = (ppkg, atom)
								else:
									best_matches[atom.cp] = (ppkg, atom)
							selected_for_display.update(best_matches.values())
						elif type == "use":
							#Prefer atoms with unconditional use deps over, because it's
							#not possible to change them on the parent, which means there
							#are fewer possible solutions.
							use = sub_type
							hard_matches = set()
							conditional_matches = set()
							for ppkg, atom, other_pkg in parents:
								parent_use = None
								if isinstance(ppkg, Package):
									parent_use = _pkg_use_enabled(ppkg)
								violated_atom = atom.unevaluated_atom.violated_conditionals( \
									_pkg_use_enabled(other_pkg), other_pkg.iuse.is_valid_flag,
									parent_use=parent_use)
								if use in violated_atom.use.enabled.union(violated_atom.use.disabled):
									hard_matches.add((ppkg, atom))
								else:
									conditional_matches.add((ppkg, atom))

							if hard_matches:
								matches = hard_matches
							else:
								matches = conditional_matches
							
							if not selected_for_display.intersection(matches):
								selected_for_display.add(matches.pop())

					def highlight_violations(atom, version, use=[]):
						"""Colorize parts of an atom"""
						atom_str = str(atom)
						if version:
							op = atom.operator
							ver = cpv_getversion(atom.cpv)
							slot = atom.slot
							atom_str = atom_str.replace(op, colorize("BAD", op), 1)
							
							start = atom_str.rfind(ver)
							end = start + len(ver)
							atom_str = atom_str[:start] + \
								colorize("BAD", ver) + \
								atom_str[end+1:]
							if slot:
								atom_str = atom_str.replace(":" + slot, colorize("BAD", ":" + slot))
						
						if use and atom.use.tokens:
							use_part_start = atom_str.find("[")
							use_part_end = atom_str.find("]")
							
							new_tokens = []
							for token in atom.use.tokens:
								if token.lstrip("-!").rstrip("=?") in use:
									new_tokens.append(colorize("BAD", token))
								else:
									new_tokens.append(token)

							atom_str = atom_str[:use_part_start] \
								+ "[%s]" % (",".join(new_tokens),) + \
								atom_str[use_part_end+1:]
						
						return atom_str

					for parent_atom in selected_for_display:
						parent, atom = parent_atom
						msg.append(2*indent)
						if isinstance(parent,
							(PackageArg, AtomArg)):
							# For PackageArg and AtomArg types, it's
							# redundant to display the atom attribute.
							msg.append(str(parent))
						else:
							# Display the specific atom from SetArg or
							# Package types.
							version_violated = False
							use = []
							for type, sub_type in collision_reasons:
								if type == "version":
									for x in collision_reasons[(type, sub_type)]:
										if ppkg == x[0] and atom == x[1]:
											version_violated = True
								elif type == "use":
									use.append(sub_type)

							atom_str = highlight_violations(atom.unevaluated_atom, version_violated, use)

							if version_violated:
								self.is_a_version_conflict = True

							msg.append("%s required by %s" % (atom_str, parent))
						msg.append("\n")
					
					if not selected_for_display:
						msg.append(2*indent)
						msg.append("(no parents that aren't satisfied by other packages in this slot)\n")
						self.conflict_is_unspecific = True
					
					omitted_parents = num_all_specific_atoms - len(selected_for_display)
					if omitted_parents:
						msg.append(2*indent)
						if len(selected_for_display) > 1:
							msg.append("(and %d more with the same problems)\n" % omitted_parents)
						else:
							msg.append("(and %d more with the same problem)\n" % omitted_parents)
				else:
					msg.append(" (no parents)\n")
				msg.append("\n")
		msg.append("\n")
Exemple #44
0
	def _prepare_conflict_msg_and_check_for_specificity(self):
		"""
		Print all slot conflicts in a human readable way.
		"""
		_pkg_use_enabled = self.depgraph._pkg_use_enabled
		usepkgonly = "--usepkgonly" in self.myopts
		need_rebuild = {}
		verboseconflicts = "--verbose-conflicts" in self.myopts
		any_omitted_parents = False
		msg = self.conflict_msg
		indent = "  "
		msg.append("\n!!! Multiple package instances within a single " + \
			"package slot have been pulled\n")
		msg.append("!!! into the dependency graph, resulting" + \
			" in a slot conflict:\n\n")

		for root, slot_atom, pkgs in self.all_conflicts:
			msg.append("%s" % (slot_atom,))
			if root != self.depgraph._frozen_config._running_root.root:
				msg.append(" for %s" % (root,))
			msg.append("\n\n")

			for pkg in pkgs:
				msg.append(indent)
				msg.append("%s" % (pkg,))
				parent_atoms = self.all_parents.get(pkg)
				if parent_atoms:
					#Create a list of collision reasons and map them to sets
					#of atoms.
					#Possible reasons:
					#	("version", "ge") for operator >=, >
					#	("version", "eq") for operator =, ~
					#	("version", "le") for operator <=, <
					#	("use", "<some use flag>") for unmet use conditionals
					collision_reasons = {}
					num_all_specific_atoms = 0

					for ppkg, atom in parent_atoms:
						if not atom.soname:
							atom_set = InternalPackageSet(
								initial_atoms=(atom,))
							atom_without_use_set = InternalPackageSet(
								initial_atoms=(atom.without_use,))
							atom_without_use_and_slot_set = \
								InternalPackageSet(initial_atoms=(
								atom.without_use.without_slot,))

						for other_pkg in pkgs:
							if other_pkg == pkg:
								continue

							if atom.soname:
								# The soname does not match.
								key = ("soname", atom)
								atoms = collision_reasons.get(key, set())
								atoms.add((ppkg, atom, other_pkg))
								num_all_specific_atoms += 1
								collision_reasons[key] = atoms
							elif not atom_without_use_and_slot_set.findAtomForPackage(other_pkg,
								modified_use=_pkg_use_enabled(other_pkg)):
								if atom.operator is not None:
									# The version range does not match.
									sub_type = None
									if atom.operator in (">=", ">"):
										sub_type = "ge"
									elif atom.operator in ("=", "~"):
										sub_type = "eq"
									elif atom.operator in ("<=", "<"):
										sub_type = "le"

									key = ("version", sub_type)
									atoms = collision_reasons.get(key, set())
									atoms.add((ppkg, atom, other_pkg))
									num_all_specific_atoms += 1
									collision_reasons[key] = atoms

							elif not atom_without_use_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
									# The slot and/or sub_slot does not match.
									key = ("slot", (atom.slot, atom.sub_slot, atom.slot_operator))
									atoms = collision_reasons.get(key, set())
									atoms.add((ppkg, atom, other_pkg))
									num_all_specific_atoms += 1
									collision_reasons[key] = atoms

							elif not atom_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
								missing_iuse = other_pkg.iuse.get_missing_iuse(
									atom.unevaluated_atom.use.required)
								if missing_iuse:
									for flag in missing_iuse:
										atoms = collision_reasons.get(("use", flag), set())
										atoms.add((ppkg, atom, other_pkg))
										collision_reasons[("use", flag)] = atoms
									num_all_specific_atoms += 1
								else:
									#Use conditionals not met.
									violated_atom = atom.violated_conditionals(_pkg_use_enabled(other_pkg), \
										other_pkg.iuse.is_valid_flag)
									if violated_atom.use is None:
										# Something like bug #453400 caused the
										# above findAtomForPackage call to
										# return None unexpectedly.
										msg = ("\n\n!!! BUG: Detected "
											"USE dep match inconsistency:\n"
											"\tppkg: %s\n"
											"\tviolated_atom: %s\n"
											"\tatom: %s unevaluated: %s\n"
											"\tother_pkg: %s IUSE: %s USE: %s\n" %
											(ppkg,
											violated_atom,
											atom,
											atom.unevaluated_atom,
											other_pkg,
											sorted(other_pkg.iuse.all),
											sorted(_pkg_use_enabled(other_pkg))))
										writemsg(msg, noiselevel=-2)
										raise AssertionError(
											'BUG: USE dep match inconsistency')
									for flag in violated_atom.use.enabled.union(violated_atom.use.disabled):
										atoms = collision_reasons.get(("use", flag), set())
										atoms.add((ppkg, atom, other_pkg))
										collision_reasons[("use", flag)] = atoms
									num_all_specific_atoms += 1
							elif isinstance(ppkg, AtomArg) and other_pkg.installed:
								parent_atoms = collision_reasons.get(("AtomArg", None), set())
								parent_atoms.add((ppkg, atom))
								collision_reasons[("AtomArg", None)] = parent_atoms
								num_all_specific_atoms += 1

					msg.append(" pulled in by\n")

					selected_for_display = set()
					unconditional_use_deps = set()

					for (type, sub_type), parents in collision_reasons.items():
						#From each (type, sub_type) pair select at least one atom.
						#Try to select as few atoms as possible

						if type == "version":
							#Find the atom with version that is as far away as possible.
							best_matches = {}
							for ppkg, atom, other_pkg in parents:
								if atom.cp in best_matches:
									cmp = vercmp( \
										cpv_getversion(atom.cpv), \
										cpv_getversion(best_matches[atom.cp][1].cpv))

									if (sub_type == "ge" and  cmp > 0) \
										or (sub_type == "le" and cmp < 0) \
										or (sub_type == "eq" and cmp > 0):
										best_matches[atom.cp] = (ppkg, atom)
								else:
									best_matches[atom.cp] = (ppkg, atom)
								if verboseconflicts:
									selected_for_display.add((ppkg, atom))
							if not verboseconflicts:
								selected_for_display.update(
										best_matches.values())
						elif type in ("soname", "slot"):
							# Check for packages that might need to
							# be rebuilt, but cannot be rebuilt for
							# some reason.
							for ppkg, atom, other_pkg in parents:
								if not (isinstance(ppkg, Package) and ppkg.installed):
									continue
								if not (atom.soname or atom.slot_operator_built):
									continue
								if self.depgraph._frozen_config.excluded_pkgs.findAtomForPackage(ppkg,
									modified_use=self.depgraph._pkg_use_enabled(ppkg)):
									selected_for_display.add((ppkg, atom))
									need_rebuild[ppkg] = 'matched by --exclude argument'
								elif self.depgraph._frozen_config.useoldpkg_atoms.findAtomForPackage(ppkg,
									modified_use=self.depgraph._pkg_use_enabled(ppkg)):
									selected_for_display.add((ppkg, atom))
									need_rebuild[ppkg] = 'matched by --useoldpkg-atoms argument'
								elif usepkgonly:
									# This case is tricky, so keep quiet in order to avoid false-positives.
									pass
								elif not self.depgraph._equiv_ebuild_visible(ppkg):
									selected_for_display.add((ppkg, atom))
									need_rebuild[ppkg] = 'ebuild is masked or unavailable'

							for ppkg, atom, other_pkg in parents:
								selected_for_display.add((ppkg, atom))
								if not verboseconflicts:
									break
						elif type == "use":
							#Prefer atoms with unconditional use deps over, because it's
							#not possible to change them on the parent, which means there
							#are fewer possible solutions.
							use = sub_type
							for ppkg, atom, other_pkg in parents:
								missing_iuse = other_pkg.iuse.get_missing_iuse(
									atom.unevaluated_atom.use.required)
								if missing_iuse:
									unconditional_use_deps.add((ppkg, atom))
								else:
									parent_use = None
									if isinstance(ppkg, Package):
										parent_use = _pkg_use_enabled(ppkg)
									violated_atom = atom.unevaluated_atom.violated_conditionals(
										_pkg_use_enabled(other_pkg),
										other_pkg.iuse.is_valid_flag,
										parent_use=parent_use)
									# It's possible for autounmask to change
									# parent_use such that the unevaluated form
									# of the atom now matches, even though the
									# earlier evaluated form (from before
									# autounmask changed parent_use) does not.
									# In this case (see bug #374423), it's
									# expected that violated_atom.use is None.
									# Since the atom now matches, we don't want
									# to display it in the slot conflict
									# message, so we simply ignore it and rely
									# on the autounmask display to communicate
									# the necessary USE change to the user.
									if violated_atom.use is None:
										continue
									if use in violated_atom.use.enabled or \
										use in violated_atom.use.disabled:
										unconditional_use_deps.add((ppkg, atom))
								# When USE flags are removed, it can be
								# essential to see all broken reverse
								# dependencies here, so don't omit any.
								# If the list is long, people can simply
								# use a pager.
								selected_for_display.add((ppkg, atom))
						elif type == "AtomArg":
							for ppkg, atom in parents:
								selected_for_display.add((ppkg, atom))

					def highlight_violations(atom, version, use, slot_violated):
						"""Colorize parts of an atom"""
						atom_str = "%s" % (atom,)
						colored_idx = set()
						if version:
							op = atom.operator
							ver = None
							if atom.cp != atom.cpv:
								ver = cpv_getversion(atom.cpv)
							slot = atom.slot
							sub_slot = atom.sub_slot
							slot_operator = atom.slot_operator

							if op == "=*":
								op = "="
								ver += "*"

							slot_str = ""
							if slot:
								slot_str = ":" + slot
							if sub_slot:
								slot_str += "/" + sub_slot
							if slot_operator:
								slot_str += slot_operator

							# Compute color_idx before adding the color codes
							# as these change the indices of the letters.
							if op is not None:
								colored_idx.update(range(len(op)))

							if ver is not None:
								start = atom_str.rfind(ver)
								end = start + len(ver)
								colored_idx.update(range(start, end))

							if slot_str:
								ii = atom_str.find(slot_str)
								colored_idx.update(range(ii, ii + len(slot_str)))


							if op is not None:
								atom_str = atom_str.replace(op, colorize("BAD", op), 1)

							if ver is not None:
								start = atom_str.rfind(ver)
								end = start + len(ver)
								atom_str = atom_str[:start] + \
									colorize("BAD", ver) + \
									atom_str[end:]

							if slot_str:
								atom_str = atom_str.replace(slot_str, colorize("BAD", slot_str), 1)

						elif slot_violated:
							slot = atom.slot
							sub_slot = atom.sub_slot
							slot_operator = atom.slot_operator

							slot_str = ""
							if slot:
								slot_str = ":" + slot
							if sub_slot:
								slot_str += "/" + sub_slot
							if slot_operator:
								slot_str += slot_operator

							if slot_str:
								ii = atom_str.find(slot_str)
								colored_idx.update(range(ii, ii + len(slot_str)))
								atom_str = atom_str.replace(slot_str, colorize("BAD", slot_str), 1)
						
						if use and atom.use.tokens:
							use_part_start = atom_str.find("[")
							use_part_end = atom_str.find("]")
							
							new_tokens = []
							# Compute start index in non-colored atom.
							ii = str(atom).find("[") +  1
							for token in atom.use.tokens:
								if token.lstrip("-!").rstrip("=?") in use:
									new_tokens.append(colorize("BAD", token))
									colored_idx.update(range(ii, ii + len(token)))
								else:
									new_tokens.append(token)
								ii += 1 + len(token)

							atom_str = atom_str[:use_part_start] \
								+ "[%s]" % (",".join(new_tokens),) + \
								atom_str[use_part_end+1:]
						
						return atom_str, colored_idx

					# Show unconditional use deps first, since those
					# are more problematic than the conditional kind.
					ordered_list = list(unconditional_use_deps)
					if len(selected_for_display) > len(unconditional_use_deps):
						for parent_atom in selected_for_display:
							if parent_atom not in unconditional_use_deps:
								ordered_list.append(parent_atom)
					for parent_atom in ordered_list:
						parent, atom = parent_atom
						if atom.soname:
							msg.append("%s required by %s\n" %
								(atom, parent))
						elif isinstance(parent, PackageArg):
							# For PackageArg it's
							# redundant to display the atom attribute.
							msg.append("%s\n" % (parent,))
						elif isinstance(parent, AtomArg):
							msg.append(2*indent)
							msg.append("%s (Argument)\n" % (atom,))
						else:
							# Display the specific atom from SetArg or
							# Package types.
							version_violated = False
							slot_violated = False
							use = []
							for (type, sub_type), parents in collision_reasons.items():
								for x in parents:
									if parent == x[0] and atom == x[1]:
										if type == "version":
											version_violated = True
										elif type == "slot":
											slot_violated = True
										elif type == "use":
											use.append(sub_type)
										break

							atom_str, colored_idx = highlight_violations(atom.unevaluated_atom,
								version_violated, use, slot_violated)

							if version_violated or slot_violated:
								self.is_a_version_conflict = True

							cur_line = "%s required by %s\n" % (atom_str, parent)
							marker_line = ""
							for ii in range(len(cur_line)):
								if ii in colored_idx:
									marker_line += "^"
								else:
									marker_line += " "
							marker_line += "\n"
							msg.append(2*indent)
							msg.append(cur_line)
							msg.append(2*indent)
							msg.append(marker_line)

					if not selected_for_display:
						msg.append(2*indent)
						msg.append("(no parents that aren't satisfied by other packages in this slot)\n")
						self.conflict_is_unspecific = True
					
					omitted_parents = num_all_specific_atoms - len(selected_for_display)
					if omitted_parents:
						any_omitted_parents = True
						msg.append(2*indent)
						if len(selected_for_display) > 1:
							msg.append("(and %d more with the same problems)\n" % omitted_parents)
						else:
							msg.append("(and %d more with the same problem)\n" % omitted_parents)
				else:
					msg.append(" (no parents)\n")
				msg.append("\n")

		if any_omitted_parents:
			msg.append(colorize("INFORM",
				"NOTE: Use the '--verbose-conflicts'"
				" option to display parents omitted above"))
			msg.append("\n")

		if need_rebuild:
			msg.append("\n!!! The slot conflict(s) shown above involve package(s) which may need to\n")
			msg.append("!!! be rebuilt in order to solve the conflict(s). However, the following\n")
			msg.append("!!! package(s) cannot be rebuilt for the reason(s) shown:\n\n")
			for ppkg, reason in need_rebuild.items():
				msg.append("%s%s: %s\n" % (indent, ppkg, reason))
			msg.append("\n")

		msg.append("\n")
Exemple #45
0
 def __gt__(self, other):
     return vercmp(self, other) > 0
Exemple #46
0
def getMinUpgrade(vulnerableList,
                  unaffectedList,
                  portdbapi,
                  vardbapi,
                  minimize=True):
    """
	Checks if the systemstate is matching an atom in
	I{vulnerableList} and returns string describing
	the lowest version for the package that matches an atom in
	I{unaffectedList} and is greater than the currently installed
	version. It will return an empty list if the system is affected,
	and no upgrade is possible or None if the system is not affected.
	Both I{vulnerableList} and I{unaffectedList} should have the
	same base package.

	@type	vulnerableList: List of Strings
	@param	vulnerableList: atoms matching vulnerable package versions
	@type	unaffectedList: List of Strings
	@param	unaffectedList: atoms matching unaffected package versions
	@type	portdbapi:	portage.dbapi.porttree.portdbapi
	@param	portdbapi:	Ebuild repository
	@type	vardbapi:	portage.dbapi.vartree.vardbapi
	@param	vardbapi:	Installed package repository
	@type	minimize:	Boolean
	@param	minimize:	True for a least-change upgrade, False for emerge-like algorithm

	@rtype:		String | None
	@return:	the lowest unaffected version that is greater than
				the installed version.
 	"""
    rValue = ""
    v_installed = reduce(operator.add,
                         [match(v, vardbapi) for v in vulnerableList], [])
    u_installed = reduce(operator.add,
                         [match(u, vardbapi) for u in unaffectedList], [])

    # remove all unaffected atoms from vulnerable list
    v_installed = list(set(v_installed).difference(set(u_installed)))

    if not v_installed:
        return None

    # this tuple holds all vulnerable atoms, and the related upgrade atom
    vuln_update = []
    avail_updates = set()
    for u in unaffectedList:
        # TODO: This had match_type="match-all" before. I don't think it should
        # since we disregarded masked items later anyway (match(=rValue, "porttree"))
        avail_updates.update(match(u, portdbapi))
    # if an atom is already installed, we should not consider it for upgrades
    avail_updates.difference_update(u_installed)

    for vuln in v_installed:
        update = ""
        for c in avail_updates:
            c_pv = portage.catpkgsplit(c)
            if vercmp(c.version, vuln.version) > 0 \
              and (update == "" \
               or (minimize ^ (vercmp(c.version, update.version) > 0))) \
              and portdbapi._pkg_str(c, None).slot == vardbapi._pkg_str(vuln, None).slot:
                update = c_pv[0] + "/" + c_pv[1] + "-" + c_pv[2]
                if c_pv[3] != "r0":  # we don't like -r0 for display
                    update += "-" + c_pv[3]
                update = portdbapi._pkg_str(update, None)
        vuln_update.append([vuln, update])

    return vuln_update
Exemple #47
0
 def __lt__(self, other):
     return vercmp(self, other) < 0
Exemple #48
0
    def available_portage_update(self, detected=False, init=False):
        """
        Check if an update to portage is available.
        """

        # TODO: be more verbose for debug !
        name = 'available_portage_update'
        logger = logging.getLogger(f'{self.__logger_name}{name}::')

        logger.debug(f"Running with detected={detected}, init={init}")

        self.available = False
        self.latest = False
        self.current = False

        # First, any way get installed and latest
        current = vardbapi().match('portage')[0]
        latest = portdbapi().xmatch('bestmatch-visible', 'portage')

        # Then just compare
        # From site-packages/portage/versions.py
        #   @param mypkg: either a pv or cpv
        #   @return:
        #   1. None if input is invalid.
        #   2. (pn, ver, rev) if input is pv
        #   3. (cp, ver, rev) if input is a cpv
        result = pkgcmp(pkgsplit(latest), pkgsplit(current))
        # From site-packages/portage/versions.py
        # Parameters:
        # pkg1 (list (example: ['test', '1.0', 'r1'])) -
        #                           package to compare with
        # pkg2 (list (example: ['test', '1.0', 'r1'])) -
        #                           package to compare againts
        # Returns: None or integer
        # None if package names are not the same
        # 1 if pkg1 is greater than pkg2
        # -1 if pkg1 is less than pkg2
        # 0 if pkg1 equals pkg2
        if result == None or result == -1:
            msg = 'no result (package names are not the same ?!)'
            if result == -1:
                msg = ('the latest version available is lower than the'
                       ' one installed...')
            logger.error("FAILED to compare versions when obtaining update "
                         "informations for the portage package.")
            logger.error(f"Result is: {msg}")
            logger.error(f"Current portage version: {current}, latest:"
                         f" {latest}.")
            # Return is ignored for the moment...
            # TODO ??
            return

        # Split current version
        # as we don't know yet if latest > current
        split = pkgsplit(current)
        self.current = split[1]
        if not split[2] == 'r0':
            self.current = '-'.join(split[-2:])

        # Check if an update to portage is available
        if result:
            # Now, split latest because > current
            split = pkgsplit(latest)
            self.latest = split[1]
            if not split[2] == 'r0':
                self.latest = '-'.join(split[-2:])
            logger.debug(f"Found an update to portage (from {self.current}"
                         f" to {self.latest}).")
            # Print only one time when program start
            if init:
                logger.info("Found an update to portage (from "
                            f"{self.current} to {self.latest}).")
            self.available = True
        else:
            logger.debug("No update to portage package is available"
                         f" (current version: {self.current})")
            self.available = False

        # For detected we have to compare current extracted
        # version and last current know version (so from
        # self.portage['current']. Both versions are already
        # split
        if detected:
            # From site-packages/portage/versions.py
            #   Compare two versions
            #   Example usage:
            #       >>> from portage.versions import vercmp
            #       >>> vercmp('1.0-r1','1.2-r3')
            #       negative number
            #       >>> vercmp('1.3','1.2-r3')
            #       positive number
            #       >>> vercmp('1.0_p3','1.0_p3')
            #       0
            #   @param pkg1: version to compare with
            #       (see ver_regexp in portage.versions.py)
            #   @type pkg1: string (example: "2.1.2-r3")
            #   @param pkg2: version to compare againts
            #       (see ver_regexp in portage.versions.py)
            #   @type pkg2: string (example: "2.1.2_rc5")
            #   @rtype: None or float
            #   @return:
            #   1. positive if ver1 is greater than ver2
            #   2. negative if ver1 is less than ver2
            #   3. 0 if ver1 equals ver2
            #   4. None if ver1 or ver2 are invalid
            #       (see ver_regexp in portage.versions.py)
            compare = vercmp(self.portage['current'], self.current)
            msg = False
            add_msg = ''
            if compare < 0:
                # If not available and it have been updated
                # than it have been updated to latest one
                if not self.available:
                    add_msg = 'latest '
                msg = (f"The portage package has been updated (from "
                       f"{self.portage['current']} to "
                       f"{add_msg}{self.current}).")
            elif compare > 0:
                # Same here but reversed: if it was not
                # available (self.portage['available'])
                # and now it is (self.available) than
                # it have been downgraded from latest.
                if not self.portage['available'] and self.available:
                    add_msg = 'latest '
                msg = (f"The portage package has been downgraded (from "
                       f"{add_msg}{self.portage['current']} to "
                       f"{self.current}).")
            elif compare == 0:
                # This have been aborted
                msg = ("The portage package process has been aborted.")

            # Just skipp if msg = False
            # so that mean compare == None
            if msg:
                logger.info(msg)

        tosave = []
        # Update only if change
        for key in 'current', 'latest', 'available':
            if not self.portage[key] == getattr(self, key):
                # This print if there a new version of portage available
                # even if there is already an older version available
                # TEST: if checking only for key latest than it could
                # be == to current so check also result.
                if key == 'latest' and result:
                    logger.info("Found an update to portage (from "
                                f"{self.current} to {self.latest}).")

                self.portage[key] = getattr(self, key)
                tosave.append([f'portage {key}', self.portage[key]])

        if tosave:
            self.stateinfo.save(*tosave)
    eoutput.ebegin("Finding broken GIR files")
files_list = set()
for dir in gir_dirs:
    # Walk the gir directories to find files
    for (path, dirs, files) in os.walk(osp.join(root, dir)):
        for f in files:
            if not f.endswith('.gir'):
                continue
            if force:
                files_list.add(osp.join(path, f))
                continue
            spinner.update()
            # Get the .gir version
            version = get_version(osp.join(path, f))
            # If not the same version as GIRepository.gir, rebuild it
            if vercmp(girversion, version) != 0:
                eoutput.ewarn("GIR file to be rebuilt: " + \
                                         osp.join(path, f))
                files_list.add(osp.join(path, f))
eoutput.eend(0)

# FIXME: Doesn't warn if it was unable to assign a file to a package
rebuild_list = set()
if files_list:
    eoutput.ebegin("Assigning files to packages")
    files_assigned = set()
    for cpv in vardbapi.cpv_all():
        spinner.update()
        # If some of the files of this package are in the gir file list
        files_owned = get_contents(cpv).intersection(files_list)
        if files_owned:
Exemple #50
0
def dep_zapdeps(unreduced,
                reduced,
                myroot,
                use_binaries=0,
                trees=None,
                minimize_slots=False):
    """
	Takes an unreduced and reduced deplist and removes satisfied dependencies.
	Returned deplist contains steps that must be taken to satisfy dependencies.
	"""
    if trees is None:
        trees = portage.db
    writemsg("ZapDeps -- %s\n" % (use_binaries), 2)
    if not reduced or unreduced == ["||"] or dep_eval(reduced):
        return []

    if unreduced[0] != "||":
        unresolved = []
        for x, satisfied in zip(unreduced, reduced):
            if isinstance(x, list):
                unresolved += dep_zapdeps(x,
                                          satisfied,
                                          myroot,
                                          use_binaries=use_binaries,
                                          trees=trees,
                                          minimize_slots=minimize_slots)
            elif not satisfied:
                unresolved.append(x)
        return unresolved

    # We're at a ( || atom ... ) type level and need to make a choice
    deps = unreduced[1:]
    satisfieds = reduced[1:]

    # Our preference order is for an the first item that:
    # a) contains all unmasked packages with the same key as installed packages
    # b) contains all unmasked packages
    # c) contains masked installed packages
    # d) is the first item

    preferred_in_graph = []
    preferred_installed = preferred_in_graph
    preferred_any_slot = preferred_in_graph
    preferred_non_installed = []
    unsat_use_in_graph = []
    unsat_use_installed = []
    unsat_use_non_installed = []
    other_installed = []
    other_installed_some = []
    other_installed_any_slot = []
    other = []

    # unsat_use_* must come after preferred_non_installed
    # for correct ordering in cases like || ( foo[a] foo[b] ).
    choice_bins = (
        preferred_in_graph,
        preferred_non_installed,
        unsat_use_in_graph,
        unsat_use_installed,
        unsat_use_non_installed,
        other_installed,
        other_installed_some,
        other_installed_any_slot,
        other,
    )

    # Alias the trees we'll be checking availability against
    parent = trees[myroot].get("parent")
    virt_parent = trees[myroot].get("virt_parent")
    priority = trees[myroot].get("priority")
    graph_db = trees[myroot].get("graph_db")
    graph = trees[myroot].get("graph")
    pkg_use_enabled = trees[myroot].get("pkg_use_enabled")
    graph_interface = trees[myroot].get("graph_interface")
    downgrade_probe = trees[myroot].get("downgrade_probe")
    circular_dependency = trees[myroot].get("circular_dependency")
    vardb = None
    if "vartree" in trees[myroot]:
        vardb = trees[myroot]["vartree"].dbapi
    if use_binaries:
        mydbapi = trees[myroot]["bintree"].dbapi
    else:
        mydbapi = trees[myroot]["porttree"].dbapi

    try:
        mydbapi_match_pkgs = mydbapi.match_pkgs
    except AttributeError:

        def mydbapi_match_pkgs(atom):
            return [
                mydbapi._pkg_str(cpv, atom.repo) for cpv in mydbapi.match(atom)
            ]

    # Sort the deps into installed, not installed but already
    # in the graph and other, not installed and not in the graph
    # and other, with values of [[required_atom], availablility]
    for x, satisfied in zip(deps, satisfieds):
        if isinstance(x, list):
            atoms = dep_zapdeps(x,
                                satisfied,
                                myroot,
                                use_binaries=use_binaries,
                                trees=trees,
                                minimize_slots=minimize_slots)
        else:
            atoms = [x]
        if vardb is None:
            # When called by repoman, we can simply return the first choice
            # because dep_eval() handles preference selection.
            return atoms

        all_available = True
        all_use_satisfied = True
        all_use_unmasked = True
        conflict_downgrade = False
        installed_downgrade = False
        slot_atoms = collections.defaultdict(list)
        slot_map = {}
        cp_map = {}
        for atom in atoms:
            if atom.blocker:
                continue

            # It's not a downgrade if parent is replacing child.
            replacing = (parent and graph_interface
                         and graph_interface.will_replace_child(
                             parent, myroot, atom))
            # Ignore USE dependencies here since we don't want USE
            # settings to adversely affect || preference evaluation.
            avail_pkg = mydbapi_match_pkgs(atom.without_use)
            if not avail_pkg and replacing:
                avail_pkg = [replacing]
            if avail_pkg:
                avail_pkg = avail_pkg[-1]  # highest (ascending order)
                avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))
            if not avail_pkg:
                all_available = False
                all_use_satisfied = False
                break

            if not replacing and graph_db is not None and downgrade_probe is not None:
                slot_matches = graph_db.match_pkgs(avail_slot)
                if (len(slot_matches) > 1 and avail_pkg < slot_matches[-1]
                        and not downgrade_probe(avail_pkg)):
                    # If a downgrade is not desirable, then avoid a
                    # choice that pulls in a lower version involved
                    # in a slot conflict (bug #531656).
                    conflict_downgrade = True

            if atom.use:
                avail_pkg_use = mydbapi_match_pkgs(atom)
                if not avail_pkg_use:
                    all_use_satisfied = False

                    if pkg_use_enabled is not None:
                        # Check which USE flags cause the match to fail,
                        # so we can prioritize choices that do not
                        # require changes to use.mask or use.force
                        # (see bug #515584).
                        violated_atom = atom.violated_conditionals(
                            pkg_use_enabled(avail_pkg),
                            avail_pkg.iuse.is_valid_flag)

                        # Note that violated_atom.use can be None here,
                        # since evaluation can collapse conditional USE
                        # deps that cause the match to fail due to
                        # missing IUSE (match uses atom.unevaluated_atom
                        # to detect such missing IUSE).
                        if violated_atom.use is not None:
                            for flag in violated_atom.use.enabled:
                                if flag in avail_pkg.use.mask:
                                    all_use_unmasked = False
                                    break
                            else:
                                for flag in violated_atom.use.disabled:
                                    if flag in avail_pkg.use.force and \
                                     flag not in avail_pkg.use.mask:
                                        all_use_unmasked = False
                                        break
                else:
                    # highest (ascending order)
                    avail_pkg_use = avail_pkg_use[-1]
                    if avail_pkg_use != avail_pkg:
                        avail_pkg = avail_pkg_use
                    avail_slot = Atom("%s:%s" % (atom.cp, avail_pkg.slot))

            if not replacing and downgrade_probe is not None and graph is not None:
                highest_in_slot = mydbapi_match_pkgs(avail_slot)
                highest_in_slot = (highest_in_slot[-1]
                                   if highest_in_slot else None)
                if (avail_pkg and highest_in_slot
                        and avail_pkg < highest_in_slot
                        and not downgrade_probe(avail_pkg) and
                    (highest_in_slot.installed or highest_in_slot in graph)):
                    installed_downgrade = True

            slot_map[avail_slot] = avail_pkg
            slot_atoms[avail_slot].append(atom)
            highest_cpv = cp_map.get(avail_pkg.cp)
            all_match_current = None
            all_match_previous = None
            if (highest_cpv is not None
                    and highest_cpv.slot == avail_pkg.slot):
                # If possible, make the package selection internally
                # consistent by choosing a package that satisfies all
                # atoms which match a package in the same slot. Later on,
                # the package version chosen here is used in the
                # has_upgrade/has_downgrade logic to prefer choices with
                # upgrades, and a package choice that is not internally
                # consistent will lead the has_upgrade/has_downgrade logic
                # to produce invalid results (see bug 600346).
                all_match_current = all(
                    a.match(avail_pkg) for a in slot_atoms[avail_slot])
                all_match_previous = all(
                    a.match(highest_cpv) for a in slot_atoms[avail_slot])
                if all_match_previous and not all_match_current:
                    continue

            current_higher = (
                highest_cpv is None
                or vercmp(avail_pkg.version, highest_cpv.version) > 0)

            if current_higher or (all_match_current
                                  and not all_match_previous):
                cp_map[avail_pkg.cp] = avail_pkg

        want_update = False
        if graph_interface is None or graph_interface.removal_action:
            new_slot_count = len(slot_map)
        else:
            new_slot_count = 0
            for slot_atom, avail_pkg in slot_map.items():
                if parent is not None and graph_interface.want_update_pkg(
                        parent, avail_pkg):
                    want_update = True
                if (not slot_atom.cp.startswith("virtual/")
                        and not graph_db.match_pkgs(slot_atom)):
                    new_slot_count += 1

        this_choice = _dep_choice(atoms=atoms,
                                  slot_map=slot_map,
                                  cp_map=cp_map,
                                  all_available=all_available,
                                  all_installed_slots=False,
                                  new_slot_count=new_slot_count,
                                  all_in_graph=False,
                                  want_update=want_update)
        if all_available:
            # The "all installed" criterion is not version or slot specific.
            # If any version of a package is already in the graph then we
            # assume that it is preferred over other possible packages choices.
            all_installed = True
            for atom in set(Atom(atom.cp) for atom in atoms \
             if not atom.blocker):
                # New-style virtuals have zero cost to install.
                if not vardb.match(atom) and not atom.startswith("virtual/"):
                    all_installed = False
                    break
            all_installed_slots = False
            if all_installed:
                all_installed_slots = True
                for slot_atom in slot_map:
                    # New-style virtuals have zero cost to install.
                    if not vardb.match(slot_atom) and \
                     not slot_atom.startswith("virtual/"):
                        all_installed_slots = False
                        break
            this_choice.all_installed_slots = all_installed_slots
            if graph_db is None:
                if all_use_satisfied:
                    if all_installed:
                        if all_installed_slots:
                            preferred_installed.append(this_choice)
                        else:
                            preferred_any_slot.append(this_choice)
                    else:
                        preferred_non_installed.append(this_choice)
                else:
                    if not all_use_unmasked:
                        other.append(this_choice)
                    elif all_installed_slots:
                        unsat_use_installed.append(this_choice)
                    else:
                        unsat_use_non_installed.append(this_choice)
            elif conflict_downgrade or installed_downgrade:
                other.append(this_choice)
            else:
                all_in_graph = True
                for atom in atoms:
                    # New-style virtuals have zero cost to install.
                    if atom.blocker or atom.cp.startswith("virtual/"):
                        continue
                    # We check if the matched package has actually been
                    # added to the digraph, in order to distinguish between
                    # those packages and installed packages that may need
                    # to be uninstalled in order to resolve blockers.
                    if not any(pkg in graph
                               for pkg in graph_db.match_pkgs(atom)):
                        all_in_graph = False
                        break
                this_choice.all_in_graph = all_in_graph

                circular_atom = None
                if parent and parent.onlydeps:
                    # Check if the atom would result in a direct circular
                    # dependency and avoid that for --onlydeps arguments
                    # since it can defeat the purpose of --onlydeps.
                    # This check should only be used for --onlydeps
                    # arguments, since it can interfere with circular
                    # dependency backtracking choices, causing the test
                    # case for bug 756961 to fail.
                    cpv_slot_list = [parent]
                    for atom in atoms:
                        if atom.blocker:
                            continue
                        if vardb.match(atom):
                            # If the atom is satisfied by an installed
                            # version then it's not a circular dep.
                            continue
                        if atom.cp != parent.cp:
                            continue
                        if match_from_list(atom, cpv_slot_list):
                            circular_atom = atom
                            break
                if circular_atom is None and circular_dependency is not None:
                    for circular_child in itertools.chain(
                            circular_dependency.get(parent, []),
                            circular_dependency.get(virt_parent, [])):
                        for atom in atoms:
                            if not atom.blocker and atom.match(circular_child):
                                circular_atom = atom
                                break
                        if circular_atom is not None:
                            break

                if circular_atom is not None:
                    other.append(this_choice)
                else:
                    if all_use_satisfied:
                        if all_in_graph:
                            preferred_in_graph.append(this_choice)
                        elif all_installed:
                            if all_installed_slots:
                                preferred_installed.append(this_choice)
                            else:
                                preferred_any_slot.append(this_choice)
                        else:
                            preferred_non_installed.append(this_choice)
                    else:
                        if not all_use_unmasked:
                            other.append(this_choice)
                        elif all_in_graph:
                            unsat_use_in_graph.append(this_choice)
                        elif all_installed_slots:
                            unsat_use_installed.append(this_choice)
                        else:
                            unsat_use_non_installed.append(this_choice)
        else:
            all_installed = True
            some_installed = False
            for atom in atoms:
                if not atom.blocker:
                    if vardb.match(atom):
                        some_installed = True
                    else:
                        all_installed = False

            if all_installed:
                this_choice.all_installed_slots = True
                other_installed.append(this_choice)
            elif some_installed:
                other_installed_some.append(this_choice)

            # Use Atom(atom.cp) for a somewhat "fuzzy" match, since
            # the whole atom may be too specific. For example, see
            # bug #522652, where using the whole atom leads to an
            # unsatisfiable choice.
            elif any(
                    vardb.match(Atom(atom.cp)) for atom in atoms
                    if not atom.blocker):
                other_installed_any_slot.append(this_choice)
            else:
                other.append(this_choice)

    # Prefer choices which contain upgrades to higher slots. This helps
    # for deps such as || ( foo:1 foo:2 ), where we want to prefer the
    # atom which matches the higher version rather than the atom furthest
    # to the left. Sorting is done separately for each of choice_bins, so
    # as not to interfere with the ordering of the bins. Because of the
    # bin separation, the main function of this code is to allow
    # --depclean to remove old slots (rather than to pull in new slots).
    for choices in choice_bins:
        if len(choices) < 2:
            continue

        if minimize_slots:
            # Prefer choices having fewer new slots. When used with DNF form,
            # this can eliminate unecessary packages that depclean would
            # ultimately eliminate (see bug 632026). Only use this behavior
            # when deemed necessary by the caller, since this will discard the
            # order specified in the ebuild, and the preferences specified
            # there can serve as a crucial sources of guidance (see bug 645002).

            # NOTE: Under some conditions, new_slot_count value may have some
            # variance from one calculation to the next because it depends on
            # the order that packages are added to the graph. This variance can
            # contribute to outcomes that appear to be random. Meanwhile,
            # the order specified in the ebuild is without variance, so it
            # does not have this problem.
            choices.sort(key=operator.attrgetter('new_slot_count'))

        for choice_1 in choices[1:]:
            cps = set(choice_1.cp_map)
            for choice_2 in choices:
                if choice_1 is choice_2:
                    # choice_1 will not be promoted, so move on
                    break
                if (
                        # Prefer choices where all_installed_slots is True, except
                        # in cases where we want to upgrade to a new slot as in
                        # bug 706278. Don't compare new_slot_count here since that
                        # would aggressively override the preference order defined
                        # in the ebuild, breaking the test case for bug 645002.
                    (choice_1.all_installed_slots
                     and not choice_2.all_installed_slots
                     and not choice_2.want_update)):
                    # promote choice_1 in front of choice_2
                    choices.remove(choice_1)
                    index_2 = choices.index(choice_2)
                    choices.insert(index_2, choice_1)
                    break

                intersecting_cps = cps.intersection(choice_2.cp_map)
                has_upgrade = False
                has_downgrade = False
                for cp in intersecting_cps:
                    version_1 = choice_1.cp_map[cp]
                    version_2 = choice_2.cp_map[cp]
                    difference = vercmp(version_1.version, version_2.version)
                    if difference != 0:
                        if difference > 0:
                            has_upgrade = True
                        else:
                            has_downgrade = True

                if (
                        # Prefer upgrades.
                    (has_upgrade and not has_downgrade)

                        # Prefer choices where all packages have been pulled into
                        # the graph, except for choices that eliminate upgrades.
                        or (choice_1.all_in_graph and not choice_2.all_in_graph
                            and not (has_downgrade and not has_upgrade))):
                    # promote choice_1 in front of choice_2
                    choices.remove(choice_1)
                    index_2 = choices.index(choice_2)
                    choices.insert(index_2, choice_1)
                    break

    for allow_masked in (False, True):
        for choices in choice_bins:
            for choice in choices:
                if choice.all_available or allow_masked:
                    return choice.atoms

    assert False  # This point should not be reachable
	def _prepare_conflict_msg_and_check_for_specificity(self):
		"""
		Print all slot conflicts in a human readable way.
		"""
		_pkg_use_enabled = self.depgraph._pkg_use_enabled
		usepkgonly = "--usepkgonly" in self.myopts
		need_rebuild = {}
		verboseconflicts = "--verbose-conflicts" in self.myopts
		any_omitted_parents = False
		msg = self.conflict_msg
		indent = "  "
		msg.append("\n!!! Multiple package instances within a single " + \
			"package slot have been pulled\n")
		msg.append("!!! into the dependency graph, resulting" + \
			" in a slot conflict:\n\n")

		for root, slot_atom, pkgs in self.all_conflicts:
			msg.append("%s" % (slot_atom,))
			if root != self.depgraph._frozen_config._running_root.root:
				msg.append(" for %s" % (root,))
			msg.append("\n\n")

			for pkg in pkgs:
				msg.append(indent)
				msg.append("%s %s" % (pkg, pkg_use_display(pkg,
					self.depgraph._frozen_config.myopts,
					modified_use=self.depgraph._pkg_use_enabled(pkg))))
				parent_atoms = self.all_parents.get(pkg)
				if parent_atoms:
					#Create a list of collision reasons and map them to sets
					#of atoms.
					#Possible reasons:
					#	("version", "ge") for operator >=, >
					#	("version", "eq") for operator =, ~
					#	("version", "le") for operator <=, <
					#	("use", "<some use flag>") for unmet use conditionals
					collision_reasons = {}
					num_all_specific_atoms = 0

					for ppkg, atom in parent_atoms:
						if not atom.soname:
							atom_set = InternalPackageSet(
								initial_atoms=(atom,))
							atom_without_use_set = InternalPackageSet(
								initial_atoms=(atom.without_use,))
							atom_without_use_and_slot_set = \
								InternalPackageSet(initial_atoms=(
								atom.without_use.without_slot,))

						for other_pkg in pkgs:
							if other_pkg == pkg:
								continue

							if atom.soname:
								# The soname does not match.
								key = ("soname", atom)
								atoms = collision_reasons.get(key, set())
								atoms.add((ppkg, atom, other_pkg))
								num_all_specific_atoms += 1
								collision_reasons[key] = atoms
							elif not atom_without_use_and_slot_set.findAtomForPackage(other_pkg,
								modified_use=_pkg_use_enabled(other_pkg)):
								if atom.operator is not None:
									# The version range does not match.
									sub_type = None
									if atom.operator in (">=", ">"):
										sub_type = "ge"
									elif atom.operator in ("=", "~"):
										sub_type = "eq"
									elif atom.operator in ("<=", "<"):
										sub_type = "le"

									key = ("version", sub_type)
									atoms = collision_reasons.get(key, set())
									atoms.add((ppkg, atom, other_pkg))
									num_all_specific_atoms += 1
									collision_reasons[key] = atoms

							elif not atom_without_use_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
									# The slot and/or sub_slot does not match.
									key = ("slot", (atom.slot, atom.sub_slot, atom.slot_operator))
									atoms = collision_reasons.get(key, set())
									atoms.add((ppkg, atom, other_pkg))
									num_all_specific_atoms += 1
									collision_reasons[key] = atoms

							elif not atom_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
								missing_iuse = other_pkg.iuse.get_missing_iuse(
									atom.unevaluated_atom.use.required)
								if missing_iuse:
									for flag in missing_iuse:
										atoms = collision_reasons.get(("use", flag), set())
										atoms.add((ppkg, atom, other_pkg))
										collision_reasons[("use", flag)] = atoms
									num_all_specific_atoms += 1
								else:
									#Use conditionals not met.
									violated_atom = atom.violated_conditionals(_pkg_use_enabled(other_pkg), \
										other_pkg.iuse.is_valid_flag)
									if violated_atom.use is None:
										# Something like bug #453400 caused the
										# above findAtomForPackage call to
										# return None unexpectedly.
										msg = ("\n\n!!! BUG: Detected "
											"USE dep match inconsistency:\n"
											"\tppkg: %s\n"
											"\tviolated_atom: %s\n"
											"\tatom: %s unevaluated: %s\n"
											"\tother_pkg: %s IUSE: %s USE: %s\n" %
											(ppkg,
											violated_atom,
											atom,
											atom.unevaluated_atom,
											other_pkg,
											sorted(other_pkg.iuse.all),
											sorted(_pkg_use_enabled(other_pkg))))
										writemsg(msg, noiselevel=-2)
										raise AssertionError(
											'BUG: USE dep match inconsistency')
									for flag in violated_atom.use.enabled.union(violated_atom.use.disabled):
										atoms = collision_reasons.get(("use", flag), set())
										atoms.add((ppkg, atom, other_pkg))
										collision_reasons[("use", flag)] = atoms
									num_all_specific_atoms += 1
							elif isinstance(ppkg, AtomArg) and other_pkg.installed:
								parent_atoms = collision_reasons.get(("AtomArg", None), set())
								parent_atoms.add((ppkg, atom))
								collision_reasons[("AtomArg", None)] = parent_atoms
								num_all_specific_atoms += 1

					msg.append(" pulled in by\n")

					selected_for_display = set()
					unconditional_use_deps = set()

					for (type, sub_type), parents in collision_reasons.items():
						#From each (type, sub_type) pair select at least one atom.
						#Try to select as few atoms as possible

						if type == "version":
							#Find the atom with version that is as far away as possible.
							best_matches = {}
							for ppkg, atom, other_pkg in parents:
								if atom.cp in best_matches:
									cmp = vercmp( \
										cpv_getversion(atom.cpv), \
										cpv_getversion(best_matches[atom.cp][1].cpv))

									if (sub_type == "ge" and  cmp > 0) \
										or (sub_type == "le" and cmp < 0) \
										or (sub_type == "eq" and cmp > 0):
										best_matches[atom.cp] = (ppkg, atom)
								else:
									best_matches[atom.cp] = (ppkg, atom)
								if verboseconflicts:
									selected_for_display.add((ppkg, atom))
							if not verboseconflicts:
								selected_for_display.update(
										best_matches.values())
						elif type in ("soname", "slot"):
							# Check for packages that might need to
							# be rebuilt, but cannot be rebuilt for
							# some reason.
							for ppkg, atom, other_pkg in parents:
								if not (isinstance(ppkg, Package) and ppkg.installed):
									continue
								if not (atom.soname or atom.slot_operator_built):
									continue
								if self.depgraph._frozen_config.excluded_pkgs.findAtomForPackage(ppkg,
									modified_use=self.depgraph._pkg_use_enabled(ppkg)):
									selected_for_display.add((ppkg, atom))
									need_rebuild[ppkg] = 'matched by --exclude argument'
								elif self.depgraph._frozen_config.useoldpkg_atoms.findAtomForPackage(ppkg,
									modified_use=self.depgraph._pkg_use_enabled(ppkg)):
									selected_for_display.add((ppkg, atom))
									need_rebuild[ppkg] = 'matched by --useoldpkg-atoms argument'
								elif usepkgonly:
									# This case is tricky, so keep quiet in order to avoid false-positives.
									pass
								elif not self.depgraph._equiv_ebuild_visible(ppkg):
									selected_for_display.add((ppkg, atom))
									need_rebuild[ppkg] = 'ebuild is masked or unavailable'

							for ppkg, atom, other_pkg in parents:
								selected_for_display.add((ppkg, atom))
								if not verboseconflicts:
									break
						elif type == "use":
							#Prefer atoms with unconditional use deps over, because it's
							#not possible to change them on the parent, which means there
							#are fewer possible solutions.
							use = sub_type
							for ppkg, atom, other_pkg in parents:
								missing_iuse = other_pkg.iuse.get_missing_iuse(
									atom.unevaluated_atom.use.required)
								if missing_iuse:
									unconditional_use_deps.add((ppkg, atom))
								else:
									parent_use = None
									if isinstance(ppkg, Package):
										parent_use = _pkg_use_enabled(ppkg)
									violated_atom = atom.unevaluated_atom.violated_conditionals(
										_pkg_use_enabled(other_pkg),
										other_pkg.iuse.is_valid_flag,
										parent_use=parent_use)
									# It's possible for autounmask to change
									# parent_use such that the unevaluated form
									# of the atom now matches, even though the
									# earlier evaluated form (from before
									# autounmask changed parent_use) does not.
									# In this case (see bug #374423), it's
									# expected that violated_atom.use is None.
									# Since the atom now matches, we don't want
									# to display it in the slot conflict
									# message, so we simply ignore it and rely
									# on the autounmask display to communicate
									# the necessary USE change to the user.
									if violated_atom.use is None:
										continue
									if use in violated_atom.use.enabled or \
										use in violated_atom.use.disabled:
										unconditional_use_deps.add((ppkg, atom))
								# When USE flags are removed, it can be
								# essential to see all broken reverse
								# dependencies here, so don't omit any.
								# If the list is long, people can simply
								# use a pager.
								selected_for_display.add((ppkg, atom))
						elif type == "AtomArg":
							for ppkg, atom in parents:
								selected_for_display.add((ppkg, atom))

					def highlight_violations(atom, version, use, slot_violated):
						"""Colorize parts of an atom"""
						atom_str = "%s" % (atom,)
						colored_idx = set()
						if version:
							op = atom.operator
							ver = None
							if atom.cp != atom.cpv:
								ver = cpv_getversion(atom.cpv)
							slot = atom.slot
							sub_slot = atom.sub_slot
							slot_operator = atom.slot_operator

							if op == "=*":
								op = "="
								ver += "*"

							slot_str = ""
							if slot:
								slot_str = ":" + slot
							if sub_slot:
								slot_str += "/" + sub_slot
							if slot_operator:
								slot_str += slot_operator

							# Compute color_idx before adding the color codes
							# as these change the indices of the letters.
							if op is not None:
								colored_idx.update(range(len(op)))

							if ver is not None:
								start = atom_str.rfind(ver)
								end = start + len(ver)
								colored_idx.update(range(start, end))

							if slot_str:
								ii = atom_str.find(slot_str)
								colored_idx.update(range(ii, ii + len(slot_str)))


							if op is not None:
								atom_str = atom_str.replace(op, colorize("BAD", op), 1)

							if ver is not None:
								start = atom_str.rfind(ver)
								end = start + len(ver)
								atom_str = atom_str[:start] + \
									colorize("BAD", ver) + \
									atom_str[end:]

							if slot_str:
								atom_str = atom_str.replace(slot_str, colorize("BAD", slot_str), 1)

						elif slot_violated:
							slot = atom.slot
							sub_slot = atom.sub_slot
							slot_operator = atom.slot_operator

							slot_str = ""
							if slot:
								slot_str = ":" + slot
							if sub_slot:
								slot_str += "/" + sub_slot
							if slot_operator:
								slot_str += slot_operator

							if slot_str:
								ii = atom_str.find(slot_str)
								colored_idx.update(range(ii, ii + len(slot_str)))
								atom_str = atom_str.replace(slot_str, colorize("BAD", slot_str), 1)
						
						if use and atom.use.tokens:
							use_part_start = atom_str.find("[")
							use_part_end = atom_str.find("]")
							
							new_tokens = []
							# Compute start index in non-colored atom.
							ii = str(atom).find("[") +  1
							for token in atom.use.tokens:
								if token.lstrip("-!").rstrip("=?") in use:
									new_tokens.append(colorize("BAD", token))
									colored_idx.update(range(ii, ii + len(token)))
								else:
									new_tokens.append(token)
								ii += 1 + len(token)

							atom_str = atom_str[:use_part_start] \
								+ "[%s]" % (",".join(new_tokens),) + \
								atom_str[use_part_end+1:]
						
						return atom_str, colored_idx

					# Show unconditional use deps first, since those
					# are more problematic than the conditional kind.
					ordered_list = list(unconditional_use_deps)
					if len(selected_for_display) > len(unconditional_use_deps):
						for parent_atom in selected_for_display:
							if parent_atom not in unconditional_use_deps:
								ordered_list.append(parent_atom)
					for parent_atom in ordered_list:
						parent, atom = parent_atom
						if isinstance(parent, Package):
							use_display = pkg_use_display(parent,
								self.depgraph._frozen_config.myopts,
								modified_use=self.depgraph._pkg_use_enabled(parent))
						else:
							use_display = ""
						if atom.soname:
							msg.append("%s required by %s %s\n" %
								(atom, parent, use_display))
						elif isinstance(parent, PackageArg):
							# For PackageArg it's
							# redundant to display the atom attribute.
							msg.append("%s\n" % (parent,))
						elif isinstance(parent, AtomArg):
							msg.append(2*indent)
							msg.append("%s (Argument)\n" % (atom,))
						else:
							# Display the specific atom from SetArg or
							# Package types.
							version_violated = False
							slot_violated = False
							use = []
							for (type, sub_type), parents in collision_reasons.items():
								for x in parents:
									if parent == x[0] and atom == x[1]:
										if type == "version":
											version_violated = True
										elif type == "slot":
											slot_violated = True
										elif type == "use":
											use.append(sub_type)
										break

							atom_str, colored_idx = highlight_violations(atom.unevaluated_atom,
								version_violated, use, slot_violated)

							if version_violated or slot_violated:
								self.is_a_version_conflict = True

							cur_line = "%s required by %s %s\n" % (atom_str, parent, use_display)
							marker_line = ""
							for ii in range(len(cur_line)):
								if ii in colored_idx:
									marker_line += "^"
								else:
									marker_line += " "
							marker_line += "\n"
							msg.append(2*indent)
							msg.append(cur_line)
							msg.append(2*indent)
							msg.append(marker_line)

					if not selected_for_display:
						msg.append(2*indent)
						msg.append("(no parents that aren't satisfied by other packages in this slot)\n")
						self.conflict_is_unspecific = True
					
					omitted_parents = num_all_specific_atoms - len(selected_for_display)
					if omitted_parents:
						any_omitted_parents = True
						msg.append(2*indent)
						if len(selected_for_display) > 1:
							msg.append("(and %d more with the same problems)\n" % omitted_parents)
						else:
							msg.append("(and %d more with the same problem)\n" % omitted_parents)
				else:
					msg.append(" (no parents)\n")
				msg.append("\n")

		if any_omitted_parents:
			msg.append(colorize("INFORM",
				"NOTE: Use the '--verbose-conflicts'"
				" option to display parents omitted above"))
			msg.append("\n")

		if need_rebuild:
			msg.append("\n!!! The slot conflict(s) shown above involve package(s) which may need to\n")
			msg.append("!!! be rebuilt in order to solve the conflict(s). However, the following\n")
			msg.append("!!! package(s) cannot be rebuilt for the reason(s) shown:\n\n")
			for ppkg, reason in need_rebuild.items():
				msg.append("%s%s: %s\n" % (indent, ppkg, reason))
			msg.append("\n")

		msg.append("\n")
Exemple #52
0
	def _prepare_conflict_msg_and_check_for_specificity(self):
		"""
		Print all slot conflicts in a human readable way.
		"""
		_pkg_use_enabled = self.depgraph._pkg_use_enabled
		verboseconflicts = "--verbose-conflicts" in self.myopts
		msg = self.conflict_msg
		indent = "  "
		msg.append("\n!!! Multiple package instances within a single " + \
			"package slot have been pulled\n")
		msg.append("!!! into the dependency graph, resulting" + \
			" in a slot conflict:\n\n")

		for (slot_atom, root), pkgs \
			in self.slot_collision_info.items():
			msg.append("%s" % (slot_atom,))
			if root != self.depgraph._frozen_config._running_root.root:
				msg.append(" for %s" % (root,))
			msg.append("\n\n")

			for pkg in pkgs:
				msg.append(indent)
				msg.append("%s" % (pkg,))
				parent_atoms = self.all_parents.get(pkg)
				if parent_atoms:
					#Create a list of collision reasons and map them to sets
					#of atoms.
					#Possible reasons:
					#	("version", "ge") for operator >=, >
					#	("version", "eq") for operator =, ~
					#	("version", "le") for operator <=, <
					#	("use", "<some use flag>") for unmet use conditionals
					collision_reasons = {}
					num_all_specific_atoms = 0

					for ppkg, atom in parent_atoms:
						atom_set = InternalPackageSet(initial_atoms=(atom,))
						atom_without_use_set = InternalPackageSet(initial_atoms=(atom.without_use,))

						for other_pkg in pkgs:
							if other_pkg == pkg:
								continue

							if not atom_without_use_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
								if atom.operator is not None:
									# The version range does not match.
									sub_type = None
									if atom.operator in (">=", ">"):
										sub_type = "ge"
									elif atom.operator in ("=", "~"):
										sub_type = "eq"
									elif atom.operator in ("<=", "<"):
										sub_type = "le"

									key = ("version", sub_type)
									atoms = collision_reasons.get(key, set())
									atoms.add((ppkg, atom, other_pkg))
									num_all_specific_atoms += 1
									collision_reasons[key] = atoms
								else:
									# The sub_slot does not match.
									key = ("sub-slot", atom.sub_slot)
									atoms = collision_reasons.get(key, set())
									atoms.add((ppkg, atom, other_pkg))
									num_all_specific_atoms += 1
									collision_reasons[key] = atoms

							elif not atom_set.findAtomForPackage(other_pkg, \
								modified_use=_pkg_use_enabled(other_pkg)):
								missing_iuse = other_pkg.iuse.get_missing_iuse(
									atom.unevaluated_atom.use.required)
								if missing_iuse:
									for flag in missing_iuse:
										atoms = collision_reasons.get(("use", flag), set())
										atoms.add((ppkg, atom, other_pkg))
										collision_reasons[("use", flag)] = atoms
									num_all_specific_atoms += 1
								else:
									#Use conditionals not met.
									violated_atom = atom.violated_conditionals(_pkg_use_enabled(other_pkg), \
										other_pkg.iuse.is_valid_flag)
									if violated_atom.use is None:
										# Something like bug #453400 caused the
										# above findAtomForPackage call to
										# return None unexpectedly.
										msg = ("\n\n!!! BUG: Detected "
											"USE dep match inconsistency:\n"
											"\tppkg: %s\n"
											"\tviolated_atom: %s\n"
											"\tatom: %s unevaluated: %s\n"
											"\tother_pkg: %s IUSE: %s USE: %s\n" %
											(ppkg,
											violated_atom,
											atom,
											atom.unevaluated_atom,
											other_pkg,
											sorted(other_pkg.iuse.all),
											sorted(_pkg_use_enabled(other_pkg))))
										writemsg(msg, noiselevel=-2)
										raise AssertionError(
											'BUG: USE dep match inconsistency')
									for flag in violated_atom.use.enabled.union(violated_atom.use.disabled):
										atoms = collision_reasons.get(("use", flag), set())
										atoms.add((ppkg, atom, other_pkg))
										collision_reasons[("use", flag)] = atoms
									num_all_specific_atoms += 1

					msg.append(" pulled in by\n")

					selected_for_display = set()
					unconditional_use_deps = set()

					for (type, sub_type), parents in collision_reasons.items():
						#From each (type, sub_type) pair select at least one atom.
						#Try to select as few atoms as possible

						if type == "version":
							#Find the atom with version that is as far away as possible.
							best_matches = {}
							for ppkg, atom, other_pkg in parents:
								if atom.cp in best_matches:
									cmp = vercmp( \
										cpv_getversion(atom.cpv), \
										cpv_getversion(best_matches[atom.cp][1].cpv))

									if (sub_type == "ge" and  cmp > 0) \
										or (sub_type == "le" and cmp < 0) \
										or (sub_type == "eq" and cmp > 0):
										best_matches[atom.cp] = (ppkg, atom)
								else:
									best_matches[atom.cp] = (ppkg, atom)
								if verboseconflicts:
									selected_for_display.add((ppkg, atom))
							if not verboseconflicts:
								selected_for_display.update(
										best_matches.values())
						elif type == "sub-slot":
							for ppkg, atom, other_pkg in parents:
								selected_for_display.add((ppkg, atom))
						elif type == "use":
							#Prefer atoms with unconditional use deps over, because it's
							#not possible to change them on the parent, which means there
							#are fewer possible solutions.
							use = sub_type
							for ppkg, atom, other_pkg in parents:
								missing_iuse = other_pkg.iuse.get_missing_iuse(
									atom.unevaluated_atom.use.required)
								if missing_iuse:
									unconditional_use_deps.add((ppkg, atom))
								else:
									parent_use = None
									if isinstance(ppkg, Package):
										parent_use = _pkg_use_enabled(ppkg)
									violated_atom = atom.unevaluated_atom.violated_conditionals(
										_pkg_use_enabled(other_pkg),
										other_pkg.iuse.is_valid_flag,
										parent_use=parent_use)
									# It's possible for autounmask to change
									# parent_use such that the unevaluated form
									# of the atom now matches, even though the
									# earlier evaluated form (from before
									# autounmask changed parent_use) does not.
									# In this case (see bug #374423), it's
									# expected that violated_atom.use is None.
									# Since the atom now matches, we don't want
									# to display it in the slot conflict
									# message, so we simply ignore it and rely
									# on the autounmask display to communicate
									# the necessary USE change to the user.
									if violated_atom.use is None:
										continue
									if use in violated_atom.use.enabled or \
										use in violated_atom.use.disabled:
										unconditional_use_deps.add((ppkg, atom))
								# When USE flags are removed, it can be
								# essential to see all broken reverse
								# dependencies here, so don't omit any.
								# If the list is long, people can simply
								# use a pager.
								selected_for_display.add((ppkg, atom))

					def highlight_violations(atom, version, use=[]):
						"""Colorize parts of an atom"""
						atom_str = "%s" % (atom,)
						if version:
							op = atom.operator
							ver = None
							if atom.cp != atom.cpv:
								ver = cpv_getversion(atom.cpv)
							slot = atom.slot

							if op == "=*":
								op = "="
								ver += "*"

							if op is not None:
								atom_str = atom_str.replace(op, colorize("BAD", op), 1)

							if ver is not None:
								start = atom_str.rfind(ver)
								end = start + len(ver)
								atom_str = atom_str[:start] + \
									colorize("BAD", ver) + \
									atom_str[end:]
							if slot:
								atom_str = atom_str.replace(":" + slot, colorize("BAD", ":" + slot))
						
						if use and atom.use.tokens:
							use_part_start = atom_str.find("[")
							use_part_end = atom_str.find("]")
							
							new_tokens = []
							for token in atom.use.tokens:
								if token.lstrip("-!").rstrip("=?") in use:
									new_tokens.append(colorize("BAD", token))
								else:
									new_tokens.append(token)

							atom_str = atom_str[:use_part_start] \
								+ "[%s]" % (",".join(new_tokens),) + \
								atom_str[use_part_end+1:]
						
						return atom_str

					# Show unconditional use deps first, since those
					# are more problematic than the conditional kind.
					ordered_list = list(unconditional_use_deps)
					if len(selected_for_display) > len(unconditional_use_deps):
						for parent_atom in selected_for_display:
							if parent_atom not in unconditional_use_deps:
								ordered_list.append(parent_atom)
					for parent_atom in ordered_list:
						parent, atom = parent_atom
						msg.append(2*indent)
						if isinstance(parent,
							(PackageArg, AtomArg)):
							# For PackageArg and AtomArg types, it's
							# redundant to display the atom attribute.
							msg.append("%s" % (parent,))
						else:
							# Display the specific atom from SetArg or
							# Package types.
							version_violated = False
							sub_slot_violated = False
							use = []
							for (type, sub_type), parents in collision_reasons.items():
								for x in parents:
									if parent == x[0] and atom == x[1]:
										if type == "version":
											version_violated = True
										elif type == "sub-slot":
											sub_slot_violated = True
										elif type == "use":
											use.append(sub_type)
										break

							atom_str = highlight_violations(atom.unevaluated_atom, version_violated, use)

							if version_violated or sub_slot_violated:
								self.is_a_version_conflict = True

							msg.append("%s required by %s" % (atom_str, parent))
						msg.append("\n")
					
					if not selected_for_display:
						msg.append(2*indent)
						msg.append("(no parents that aren't satisfied by other packages in this slot)\n")
						self.conflict_is_unspecific = True
					
					omitted_parents = num_all_specific_atoms - len(selected_for_display)
					if omitted_parents:
						msg.append(2*indent)
						if len(selected_for_display) > 1:
							msg.append("(and %d more with the same problems)\n" % omitted_parents)
						else:
							msg.append("(and %d more with the same problem)\n" % omitted_parents)
				else:
					msg.append(" (no parents)\n")
				msg.append("\n")
		msg.append("\n")
Exemple #53
0
    eoutput.ebegin("Finding broken GIR files")
files_list = set()
for dir in gir_dirs:
    # Walk the gir directories to find files
    for (path, dirs, files) in os.walk(osp.join(root, dir)):
        for f in files:
            if not f.endswith('.gir'):
                continue
            if force:
                files_list.add(osp.join(path, f))
                continue
            spinner.update()
            # Get the .gir version
            version = get_version(osp.join(path, f))
            # If not the same version as GIRepository.gir, rebuild it
            if vercmp(girversion, version) != 0:
                eoutput.ewarn("GIR file to be rebuilt: " + \
                                         osp.join(path, f))
                files_list.add(osp.join(path, f))
eoutput.eend(0)

# FIXME: Doesn't warn if it was unable to assign a file to a package
rebuild_list = set()
if files_list:
    eoutput.ebegin("Assigning files to packages")
    files_assigned = set()
    for cpv in vardbapi.cpv_all():
        spinner.update()
        # If some of the files of this package are in the gir file list
        files_owned = get_contents(cpv).intersection(files_list)
        if files_owned: