| # Copyright 1999-2020 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| |
| from portage.dep import Atom, _repo_separator |
| from portage.exception import InvalidData |
| |
| |
| def create_world_atom(pkg, args_set, root_config, before_install=False): |
| """Create a new atom for the world file if one does not exist. If the |
| argument atom is precise enough to identify a specific slot then a slot |
| atom will be returned. Atoms that are in the system set may also be stored |
| in world since a user might want to select multiple slots of a slotted |
| package like gcc for example. Unslotted system packages will not be |
| stored in world.""" |
| |
| arg_atom = args_set.findAtomForPackage(pkg) |
| if not arg_atom: |
| return None |
| cp = arg_atom.cp |
| new_world_atom = cp |
| if arg_atom.repo: |
| new_world_atom += _repo_separator + arg_atom.repo |
| sets = root_config.sets |
| portdb = root_config.trees["porttree"].dbapi |
| vardb = root_config.trees["vartree"].dbapi |
| |
| if arg_atom.repo is not None: |
| repos = [arg_atom.repo] |
| else: |
| # Iterate over portdbapi.porttrees, since it's common to |
| # tweak this attribute in order to adjust match behavior. |
| repos = [] |
| for tree in portdb.porttrees: |
| repos.append(portdb.repositories.get_name_for_location(tree)) |
| |
| available_slots = set() |
| for cpv in portdb.match(Atom(cp)): |
| for repo in repos: |
| try: |
| available_slots.add(portdb._pkg_str(str(cpv), repo).slot) |
| except (KeyError, InvalidData): |
| pass |
| |
| slotted = len(available_slots) > 1 or ( |
| len(available_slots) == 1 and "0" not in available_slots |
| ) |
| if not slotted: |
| # check the vdb in case this is multislot |
| available_slots = set( |
| vardb._pkg_str(cpv, None).slot for cpv in vardb.match(Atom(cp)) |
| ) |
| slotted = len(available_slots) > 1 or ( |
| len(available_slots) == 1 and "0" not in available_slots |
| ) |
| if slotted and arg_atom.without_repo != cp: |
| # If the user gave a specific atom, store it as a |
| # slot atom in the world file. |
| slot_atom = pkg.slot_atom |
| |
| # For USE=multislot, there are a couple of cases to |
| # handle here: |
| # |
| # 1) SLOT="0", but the real SLOT spontaneously changed to some |
| # unknown value, so just record an unslotted atom. |
| # |
| # 2) SLOT comes from an installed package and there is no |
| # matching SLOT in the ebuild repository. |
| # |
| # Make sure that the slot atom is available in either the |
| # portdb or the vardb, since otherwise the user certainly |
| # doesn't want the SLOT atom recorded in the world file |
| # (case 1 above). If it's only available in the vardb, |
| # the user may be trying to prevent a USE=multislot |
| # package from being removed by --depclean (case 2 above). |
| |
| mydb = portdb |
| if not portdb.match(slot_atom): |
| # SLOT seems to come from an installed multislot package |
| mydb = vardb |
| # If there is no installed package matching the SLOT atom, |
| # it probably changed SLOT spontaneously due to USE=multislot, |
| # so just record an unslotted atom. |
| if vardb.match(slot_atom) or before_install: |
| # Now verify that the argument is precise |
| # enough to identify a specific slot. |
| matches = mydb.match(arg_atom) |
| matched_slots = set() |
| if before_install: |
| matched_slots.add(pkg.slot) |
| if mydb is vardb: |
| for cpv in matches: |
| matched_slots.add(mydb._pkg_str(cpv, None).slot) |
| else: |
| for cpv in matches: |
| for repo in repos: |
| try: |
| matched_slots.add(portdb._pkg_str(str(cpv), repo).slot) |
| except (KeyError, InvalidData): |
| pass |
| |
| if len(matched_slots) == 1: |
| new_world_atom = slot_atom |
| if arg_atom.repo: |
| new_world_atom += _repo_separator + arg_atom.repo |
| |
| if new_world_atom == sets["selected"].findAtomForPackage(pkg): |
| # Both atoms would be identical, so there's nothing to add. |
| return None |
| if not slotted and not arg_atom.repo: |
| # Don't exclude slotted atoms for system packages from world, since |
| # a user might want to select multiple slots of a slotted package like |
| # gcc for example. |
| system_atom = sets["system"].findAtomForPackage(pkg) |
| if system_atom: |
| if not system_atom.cp.startswith("virtual/"): |
| return None |
| # System virtuals aren't safe to exclude from world since they can |
| # match multiple old-style virtuals but only one of them will be |
| # pulled in by update or depclean. |
| providers = portdb.settings.getvirtuals().get(system_atom.cp) |
| if providers and len(providers) == 1 and providers[0].cp == arg_atom.cp: |
| return None |
| return new_world_atom |