'portage.dep:best_match_to_list,dep_getcpv,dep_getkey,' + \
'flatten,get_operator,isjustname,isspecific,isvalidatom,' + \
'match_from_list,match_to_list',
+ 'portage.dep.dep_check:dep_check,dep_eval,dep_wordreduce,dep_zapdeps',
'portage.eclass_cache',
'portage.env.loaders',
'portage.exception',
'portage.package.ebuild.digestcheck:digestcheck',
'portage.package.ebuild.digestgen:digestgen',
'portage.package.ebuild.fetch:fetch',
+ 'portage.package.ebuild.getmaskingreason:getmaskingreason',
+ 'portage.package.ebuild.getmaskingstatus:getmaskingstatus',
'portage.package.ebuild.prepare_build_dirs:prepare_build_dirs',
'portage.process',
'portage.process:atexit_register,run_exitfuncs',
'portage.util.env_update:env_update',
'portage.util.ExtractKernelVersion:ExtractKernelVersion',
'portage.util.listdir:cacheddir,listdir',
+ 'portage.util.movefile:movefile',
'portage.versions',
'portage.versions:best,catpkgsplit,catsplit,cpv_getkey,' + \
'cpv_getkey@getCPFromCPV,endversion_keys,' + \
raise portage.exception.PortageException(
"mv '%s' '%s'" % (src, dest))
-def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
- hardlink_candidates=None, encoding=_encodings['fs']):
- """moves a file from src to dest, preserving all permissions and attributes; mtime will
- be preserved even when moving across filesystems. Returns true on success and false on
- failure. Move is atomic."""
- #print "movefile("+str(src)+","+str(dest)+","+str(newmtime)+","+str(sstat)+")"
-
- if mysettings is None:
- global settings
- mysettings = settings
-
- selinux_enabled = mysettings.selinux_enabled()
- if selinux_enabled:
- selinux = _unicode_module_wrapper(_selinux, encoding=encoding)
-
- lchown = _unicode_func_wrapper(data.lchown, encoding=encoding)
- os = _unicode_module_wrapper(_os,
- encoding=encoding, overrides=_os_overrides)
- shutil = _unicode_module_wrapper(_shutil, encoding=encoding)
-
- try:
- if not sstat:
- sstat=os.lstat(src)
-
- except SystemExit as e:
- raise
- except Exception as e:
- print(_("!!! Stating source file failed... movefile()"))
- print("!!!",e)
- return None
-
- destexists=1
- try:
- dstat=os.lstat(dest)
- except (OSError, IOError):
- dstat=os.lstat(os.path.dirname(dest))
- destexists=0
-
- if bsd_chflags:
- if destexists and dstat.st_flags != 0:
- bsd_chflags.lchflags(dest, 0)
- # Use normal stat/chflags for the parent since we want to
- # follow any symlinks to the real parent directory.
- pflags = os.stat(os.path.dirname(dest)).st_flags
- if pflags != 0:
- bsd_chflags.chflags(os.path.dirname(dest), 0)
-
- if destexists:
- if stat.S_ISLNK(dstat[stat.ST_MODE]):
- try:
- os.unlink(dest)
- destexists=0
- except SystemExit as e:
- raise
- except Exception as e:
- pass
-
- if stat.S_ISLNK(sstat[stat.ST_MODE]):
- try:
- target=os.readlink(src)
- if mysettings and mysettings["D"]:
- if target.find(mysettings["D"])==0:
- target=target[len(mysettings["D"]):]
- if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
- os.unlink(dest)
- if selinux_enabled:
- selinux.symlink(target, dest, src)
- else:
- os.symlink(target,dest)
- lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
- # utime() only works on the target of a symlink, so it's not
- # possible to perserve mtime on symlinks.
- return os.lstat(dest)[stat.ST_MTIME]
- except SystemExit as e:
- raise
- except Exception as e:
- print(_("!!! failed to properly create symlink:"))
- print("!!!",dest,"->",target)
- print("!!!",e)
- return None
-
- hardlinked = False
- # Since identical files might be merged to multiple filesystems,
- # so os.link() calls might fail for some paths, so try them all.
- # For atomic replacement, first create the link as a temp file
- # and them use os.rename() to replace the destination.
- if hardlink_candidates:
- head, tail = os.path.split(dest)
- hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \
- (tail, os.getpid()))
- try:
- os.unlink(hardlink_tmp)
- except OSError as e:
- if e.errno != errno.ENOENT:
- writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \
- (hardlink_tmp,), noiselevel=-1)
- writemsg("!!! %s\n" % (e,), noiselevel=-1)
- return None
- del e
- for hardlink_src in hardlink_candidates:
- try:
- os.link(hardlink_src, hardlink_tmp)
- except OSError:
- continue
- else:
- try:
- os.rename(hardlink_tmp, dest)
- except OSError as e:
- writemsg(_("!!! Failed to rename %s to %s\n") % \
- (hardlink_tmp, dest), noiselevel=-1)
- writemsg("!!! %s\n" % (e,), noiselevel=-1)
- return None
- hardlinked = True
- break
-
- renamefailed=1
- if hardlinked:
- renamefailed = False
- if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
- try:
- if selinux_enabled:
- ret = selinux.rename(src, dest)
- else:
- ret=os.rename(src,dest)
- renamefailed=0
- except OSError as e:
- if e.errno != errno.EXDEV:
- # Some random error.
- print(_("!!! Failed to move %(src)s to %(dest)s") % {"src": src, "dest": dest})
- print("!!!",e)
- return None
- # Invalid cross-device-link 'bind' mounted or actually Cross-Device
- if renamefailed:
- didcopy=0
- if stat.S_ISREG(sstat[stat.ST_MODE]):
- try: # For safety copy then move it over.
- if selinux_enabled:
- selinux.copyfile(src, dest + "#new")
- selinux.rename(dest + "#new", dest)
- else:
- shutil.copyfile(src,dest+"#new")
- os.rename(dest+"#new",dest)
- didcopy=1
- except SystemExit as e:
- raise
- except Exception as e:
- print(_('!!! copy %(src)s -> %(dest)s failed.') % {"src": src, "dest": dest})
- print("!!!",e)
- return None
- else:
- #we don't yet handle special, so we need to fall back to /bin/mv
- a = process.spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
- if a != os.EX_OK:
- writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1)
- writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
- {"src": _unicode_decode(src, encoding=encoding),
- "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
- writemsg("!!! %s\n" % a, noiselevel=-1)
- return None # failure
- try:
- if didcopy:
- if stat.S_ISLNK(sstat[stat.ST_MODE]):
- lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
- else:
- os.chown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
- os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
- os.unlink(src)
- except SystemExit as e:
- raise
- except Exception as e:
- print(_("!!! Failed to chown/chmod/unlink in movefile()"))
- print("!!!",dest)
- print("!!!",e)
- return None
-
- # Always use stat_obj[stat.ST_MTIME] for the integral timestamp which
- # is returned, since the stat_obj.st_mtime float attribute rounds *up*
- # if the nanosecond part of the timestamp is 999999881 ns or greater.
- try:
- if hardlinked:
- newmtime = os.stat(dest)[stat.ST_MTIME]
- else:
- # Note: It is not possible to preserve nanosecond precision
- # (supported in POSIX.1-2008 via utimensat) with the IEEE 754
- # double precision float which only has a 53 bit significand.
- if newmtime is not None:
- os.utime(dest, (newmtime, newmtime))
- else:
- newmtime = sstat[stat.ST_MTIME]
- if renamefailed:
- # If rename succeeded then timestamps are automatically
- # preserved with complete precision because the source
- # and destination inode are the same. Otherwise, round
- # down to the nearest whole second since python's float
- # st_mtime cannot be used to preserve the st_mtim.tv_nsec
- # field with complete precision. Note that we have to use
- # stat_obj[stat.ST_MTIME] here because the float
- # stat_obj.st_mtime rounds *up* sometimes.
- os.utime(dest, (newmtime, newmtime))
- except OSError:
- # The utime can fail here with EPERM even though the move succeeded.
- # Instead of failing, use stat to return the mtime if possible.
- try:
- newmtime = os.stat(dest)[stat.ST_MTIME]
- except OSError as e:
- writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
- writemsg("!!! %s\n" % dest, noiselevel=-1)
- writemsg("!!! %s\n" % str(e), noiselevel=-1)
- return None
-
- if bsd_chflags:
- # Restore the flags we saved before moving
- if pflags:
- bsd_chflags.chflags(os.path.dirname(dest), pflags)
-
- return newmtime
-
def merge(mycat, mypkg, pkgloc, infloc, myroot, mysettings, myebuild=None,
mytree=None, mydbapi=None, vartree=None, prev_mtimes=None, blockers=None,
scheduler=None):
newsplit.append(x)
return newsplit
-def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/",
- trees=None, use_mask=None, use_force=None, **kwargs):
- """
- In order to solve bug #141118, recursively expand new-style virtuals so
- as to collapse one or more levels of indirection, generating an expanded
- search space. In dep_zapdeps, new-style virtuals will be assigned
- zero cost regardless of whether or not they are currently installed. Virtual
- blockers are supported but only when the virtual expands to a single
- atom because it wouldn't necessarily make sense to block all the components
- of a compound virtual. When more than one new-style virtual is matched,
- the matches are sorted from highest to lowest versions and the atom is
- expanded to || ( highest match ... lowest match )."""
- newsplit = []
- mytrees = trees[myroot]
- portdb = mytrees["porttree"].dbapi
- atom_graph = mytrees.get("atom_graph")
- parent = mytrees.get("parent")
- virt_parent = mytrees.get("virt_parent")
- graph_parent = None
- eapi = None
- if parent is not None:
- if virt_parent is not None:
- graph_parent = virt_parent
- eapi = virt_parent[0].metadata['EAPI']
- else:
- graph_parent = parent
- eapi = parent.metadata["EAPI"]
- repoman = not mysettings.local_config
- if kwargs["use_binaries"]:
- portdb = trees[myroot]["bintree"].dbapi
- myvirtuals = mysettings.getvirtuals()
- pprovideddict = mysettings.pprovideddict
- myuse = kwargs["myuse"]
- for x in mysplit:
- if x == "||":
- newsplit.append(x)
- continue
- elif isinstance(x, list):
- newsplit.append(_expand_new_virtuals(x, edebug, mydbapi,
- mysettings, myroot=myroot, trees=trees, use_mask=use_mask,
- use_force=use_force, **kwargs))
- continue
-
- if not isinstance(x, portage.dep.Atom):
- try:
- x = portage.dep.Atom(x)
- except portage.exception.InvalidAtom:
- if portage.dep._dep_check_strict:
- raise portage.exception.ParseError(
- _("invalid atom: '%s'") % x)
- else:
- # Only real Atom instances are allowed past this point.
- continue
- else:
- if x.blocker and x.blocker.overlap.forbid and \
- eapi in ("0", "1") and portage.dep._dep_check_strict:
- raise portage.exception.ParseError(
- _("invalid atom: '%s'") % (x,))
- if x.use and eapi in ("0", "1") and \
- portage.dep._dep_check_strict:
- raise portage.exception.ParseError(
- _("invalid atom: '%s'") % (x,))
-
- if repoman and x.use and x.use.conditional:
- evaluated_atom = portage.dep.remove_slot(x)
- if x.slot:
- evaluated_atom += ":%s" % x.slot
- evaluated_atom += str(x.use._eval_qa_conditionals(
- use_mask, use_force))
- x = portage.dep.Atom(evaluated_atom)
-
- if not repoman and \
- myuse is not None and isinstance(x, portage.dep.Atom) and x.use:
- if x.use.conditional:
- x = x.evaluate_conditionals(myuse)
-
- mykey = x.cp
- if not mykey.startswith("virtual/"):
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- continue
- mychoices = myvirtuals.get(mykey, [])
- if x.blocker:
- # Virtual blockers are no longer expanded here since
- # the un-expanded virtual atom is more useful for
- # maintaining a cache of blocker atoms.
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- continue
-
- if repoman or not hasattr(portdb, 'match_pkgs'):
- if portdb.cp_list(x.cp):
- newsplit.append(x)
- else:
- # TODO: Add PROVIDE check for repoman.
- a = []
- for y in mychoices:
- a.append(dep.Atom(x.replace(x.cp, y.cp, 1)))
- if not a:
- newsplit.append(x)
- elif len(a) == 1:
- newsplit.append(a[0])
- else:
- newsplit.append(['||'] + a)
- continue
-
- pkgs = []
- # Ignore USE deps here, since otherwise we might not
- # get any matches. Choices with correct USE settings
- # will be preferred in dep_zapdeps().
- matches = portdb.match_pkgs(x.without_use)
- # Use descending order to prefer higher versions.
- matches.reverse()
- for pkg in matches:
- # only use new-style matches
- if pkg.cp.startswith("virtual/"):
- pkgs.append(pkg)
- if not (pkgs or mychoices):
- # This one couldn't be expanded as a new-style virtual. Old-style
- # virtuals have already been expanded by dep_virtual, so this one
- # is unavailable and dep_zapdeps will identify it as such. The
- # atom is not eliminated here since it may still represent a
- # dependency that needs to be satisfied.
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- continue
-
- a = []
- for pkg in pkgs:
- virt_atom = '=' + pkg.cpv
- if x.use:
- virt_atom += str(x.use)
- virt_atom = dep.Atom(virt_atom)
- # According to GLEP 37, RDEPEND is the only dependency
- # type that is valid for new-style virtuals. Repoman
- # should enforce this.
- depstring = pkg.metadata['RDEPEND']
- pkg_kwargs = kwargs.copy()
- pkg_kwargs["myuse"] = pkg.use.enabled
- if edebug:
- util.writemsg_level(_("Virtual Parent: %s\n") \
- % (pkg,), noiselevel=-1, level=logging.DEBUG)
- util.writemsg_level(_("Virtual Depstring: %s\n") \
- % (depstring,), noiselevel=-1, level=logging.DEBUG)
-
- # Set EAPI used for validation in dep_check() recursion.
- mytrees["virt_parent"] = (pkg, virt_atom)
-
- try:
- mycheck = dep_check(depstring, mydbapi, mysettings,
- myroot=myroot, trees=trees, **pkg_kwargs)
- finally:
- # Restore previous EAPI after recursion.
- if virt_parent is not None:
- mytrees["virt_parent"] = virt_parent
- else:
- del mytrees["virt_parent"]
-
- if not mycheck[0]:
- raise portage.exception.ParseError(
- "%s: %s '%s'" % (y[0], mycheck[1], depstring))
-
- # pull in the new-style virtual
- mycheck[1].append(virt_atom)
- a.append(mycheck[1])
- if atom_graph is not None:
- atom_graph.add(virt_atom, graph_parent)
- # Plain old-style virtuals. New-style virtuals are preferred.
- if not pkgs:
- for y in mychoices:
- new_atom = dep.Atom(x.replace(x.cp, y.cp, 1))
- matches = portdb.match(new_atom)
- # portdb is an instance of depgraph._dep_check_composite_db, so
- # USE conditionals are already evaluated.
- if matches and mykey in \
- portdb.aux_get(matches[-1], ['PROVIDE'])[0].split():
- a.append(new_atom)
- if atom_graph is not None:
- atom_graph.add(new_atom, graph_parent)
-
- if not a and mychoices:
- # Check for a virtual package.provided match.
- for y in mychoices:
- new_atom = dep.Atom(x.replace(x.cp, y.cp, 1))
- if match_from_list(new_atom,
- pprovideddict.get(new_atom.cp, [])):
- a.append(new_atom)
- if atom_graph is not None:
- atom_graph.add(new_atom, graph_parent)
-
- if not a:
- newsplit.append(x)
- if atom_graph is not None:
- atom_graph.add(x, graph_parent)
- elif len(a) == 1:
- newsplit.append(a[0])
- else:
- newsplit.append(['||'] + a)
-
- return newsplit
-
-def dep_eval(deplist):
- if not deplist:
- return 1
- if deplist[0]=="||":
- #or list; we just need one "1"
- for x in deplist[1:]:
- if isinstance(x, list):
- if dep_eval(x)==1:
- return 1
- elif x==1:
- return 1
- #XXX: unless there's no available atoms in the list
- #in which case we need to assume that everything is
- #okay as some ebuilds are relying on an old bug.
- if len(deplist) == 1:
- return 1
- return 0
- else:
- for x in deplist:
- if isinstance(x, list):
- if dep_eval(x)==0:
- return 0
- elif x==0 or x==2:
- return 0
- return 1
-
-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:
- global db
- trees = 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 = []
-
- # 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,
- )
-
- # 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")
- 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_slot = dep.Atom("%s:%s" % (atom.cp,
- mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
- 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_slot = dep.Atom("%s:%s" % (atom.cp,
- mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
-
- slot_map[avail_slot] = avail_pkg
- pkg_cp = cpv_getkey(avail_pkg)
- highest_cpv = cp_map.get(pkg_cp)
- if highest_cpv is None or \
- pkgcmp(catpkgsplit(avail_pkg)[1:],
- catpkgsplit(highest_cpv)[1:]) > 0:
- cp_map[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(dep.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 not graph_db.match(slot_atom) and \
- not slot_atom.startswith("virtual/"):
- all_in_graph = False
- break
- circular_atom = None
- if all_in_graph:
- if parent is None or priority is None:
- pass
- elif priority.buildtime:
- # 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:
- 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 = pkgcmp(catpkgsplit(version_1)[1:],
- catpkgsplit(version_2)[1:])
- 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
-
-def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
- use_cache=1, use_binaries=0, myroot="/", trees=None):
- """Takes a depend string and parses the condition."""
- edebug = mysettings.get("PORTAGE_DEBUG", None) == "1"
- #check_config_instance(mysettings)
- if trees is None:
- trees = globals()["db"]
- if use=="yes":
- if myuse is None:
- #default behavior
- myusesplit = mysettings["PORTAGE_USE"].split()
- else:
- myusesplit = myuse
- # We've been given useflags to use.
- #print "USE FLAGS PASSED IN."
- #print myuse
- #if "bindist" in myusesplit:
- # print "BINDIST is set!"
- #else:
- # print "BINDIST NOT set."
- else:
- #we are being run by autouse(), don't consult USE vars yet.
- # WE ALSO CANNOT USE SETTINGS
- myusesplit=[]
-
- #convert parenthesis to sublists
- try:
- mysplit = portage.dep.paren_reduce(depstring)
- except portage.exception.InvalidDependString as e:
- return [0, str(e)]
-
- mymasks = set()
- useforce = set()
- useforce.add(mysettings["ARCH"])
- if use == "all":
- # This masking/forcing is only for repoman. In other cases, relevant
- # masking/forcing should have already been applied via
- # config.regenerate(). Also, binary or installed packages may have
- # been built with flags that are now masked, and it would be
- # inconsistent to mask them now. Additionally, myuse may consist of
- # flags from a parent package that is being merged to a $ROOT that is
- # different from the one that mysettings represents.
- mymasks.update(mysettings.usemask)
- mymasks.update(mysettings.archlist())
- mymasks.discard(mysettings["ARCH"])
- useforce.update(mysettings.useforce)
- useforce.difference_update(mymasks)
- try:
- mysplit = portage.dep.use_reduce(mysplit, uselist=myusesplit,
- masklist=mymasks, matchall=(use=="all"), excludeall=useforce)
- except portage.exception.InvalidDependString as e:
- return [0, str(e)]
-
- # Do the || conversions
- mysplit=portage.dep.dep_opconvert(mysplit)
-
- if mysplit == []:
- #dependencies were reduced to nothing
- return [1,[]]
-
- # Recursively expand new-style virtuals so as to
- # collapse one or more levels of indirection.
- try:
- mysplit = _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings,
- use=use, mode=mode, myuse=myuse,
- use_force=useforce, use_mask=mymasks, use_cache=use_cache,
- use_binaries=use_binaries, myroot=myroot, trees=trees)
- except portage.exception.ParseError as e:
- return [0, str(e)]
-
- mysplit2=mysplit[:]
- mysplit2=dep_wordreduce(mysplit2,mysettings,mydbapi,mode,use_cache=use_cache)
- if mysplit2 is None:
- return [0, _("Invalid token")]
-
- writemsg("\n\n\n", 1)
- writemsg("mysplit: %s\n" % (mysplit), 1)
- writemsg("mysplit2: %s\n" % (mysplit2), 1)
-
- try:
- selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
- use_binaries=use_binaries, trees=trees)
- except portage.exception.InvalidAtom as e:
- if portage.dep._dep_check_strict:
- raise # This shouldn't happen.
- # dbapi.match() failed due to an invalid atom in
- # the dependencies of an installed package.
- return [0, _("Invalid atom: '%s'") % (e,)]
-
- return [1, selected_atoms]
-
-def dep_wordreduce(mydeplist,mysettings,mydbapi,mode,use_cache=1):
- "Reduces the deplist to ones and zeros"
- deplist=mydeplist[:]
- for mypos, token in enumerate(deplist):
- if isinstance(deplist[mypos], list):
- #recurse
- deplist[mypos]=dep_wordreduce(deplist[mypos],mysettings,mydbapi,mode,use_cache=use_cache)
- elif deplist[mypos]=="||":
- pass
- elif token[:1] == "!":
- deplist[mypos] = False
- else:
- mykey = deplist[mypos].cp
- if mysettings and mykey in mysettings.pprovideddict and \
- match_from_list(deplist[mypos], mysettings.pprovideddict[mykey]):
- deplist[mypos]=True
- elif mydbapi is None:
- # Assume nothing is satisfied. This forces dep_zapdeps to
- # return all of deps the deps that have been selected
- # (excluding those satisfied by package.provided).
- deplist[mypos] = False
- else:
- if mode:
- x = mydbapi.xmatch(mode, deplist[mypos])
- if mode.startswith("minimum-"):
- mydep = []
- if x:
- mydep.append(x)
- else:
- mydep = x
- else:
- mydep=mydbapi.match(deplist[mypos],use_cache=use_cache)
- if mydep!=None:
- tmp=(len(mydep)>=1)
- if deplist[mypos][0]=="!":
- tmp=False
- deplist[mypos]=tmp
- else:
- #encountered invalid string
- return None
- return deplist
-
-def getmaskingreason(mycpv, metadata=None, settings=None, portdb=None, return_location=False):
- from portage.util import grablines
- if settings is None:
- settings = globals()["settings"]
- if portdb is None:
- portdb = globals()["portdb"]
- mysplit = catpkgsplit(mycpv)
- if not mysplit:
- raise ValueError(_("invalid CPV: %s") % mycpv)
- if metadata is None:
- db_keys = list(portdb._aux_cache_keys)
- try:
- metadata = dict(zip(db_keys, portdb.aux_get(mycpv, db_keys)))
- except KeyError:
- if not portdb.cpv_exists(mycpv):
- raise
- if metadata is None:
- # Can't access SLOT due to corruption.
- cpv_slot_list = [mycpv]
- else:
- cpv_slot_list = ["%s:%s" % (mycpv, metadata["SLOT"])]
- mycp=mysplit[0]+"/"+mysplit[1]
-
- # XXX- This is a temporary duplicate of code from the config constructor.
- locations = [os.path.join(settings["PORTDIR"], "profiles")]
- locations.extend(settings.profiles)
- for ov in settings["PORTDIR_OVERLAY"].split():
- profdir = os.path.join(normalize_path(ov), "profiles")
- if os.path.isdir(profdir):
- locations.append(profdir)
- locations.append(os.path.join(settings["PORTAGE_CONFIGROOT"],
- USER_CONFIG_PATH))
- locations.reverse()
- pmasklists = [(x, grablines(os.path.join(x, "package.mask"), recursive=1)) for x in locations]
-
- if mycp in settings.pmaskdict:
- for x in settings.pmaskdict[mycp]:
- if match_from_list(x, cpv_slot_list):
- for pmask in pmasklists:
- comment = ""
- comment_valid = -1
- pmask_filename = os.path.join(pmask[0], "package.mask")
- for i in range(len(pmask[1])):
- l = pmask[1][i].strip()
- if l == "":
- comment = ""
- comment_valid = -1
- elif l[0] == "#":
- comment += (l+"\n")
- comment_valid = i + 1
- elif l == x:
- if comment_valid != i:
- comment = ""
- if return_location:
- return (comment, pmask_filename)
- else:
- return comment
- elif comment_valid != -1:
- # Apparently this comment applies to muliple masks, so
- # it remains valid until a blank line is encountered.
- comment_valid += 1
- if return_location:
- return (None, None)
- else:
- return None
-
-def getmaskingstatus(mycpv, settings=None, portdb=None):
- if settings is None:
- settings = config(clone=globals()["settings"])
- if portdb is None:
- portdb = globals()["portdb"]
-
- metadata = None
- installed = False
- if not isinstance(mycpv, basestring):
- # emerge passed in a Package instance
- pkg = mycpv
- mycpv = pkg.cpv
- metadata = pkg.metadata
- installed = pkg.installed
-
- mysplit = catpkgsplit(mycpv)
- if not mysplit:
- raise ValueError(_("invalid CPV: %s") % mycpv)
- if metadata is None:
- db_keys = list(portdb._aux_cache_keys)
- try:
- metadata = dict(zip(db_keys, portdb.aux_get(mycpv, db_keys)))
- except KeyError:
- if not portdb.cpv_exists(mycpv):
- raise
- return ["corruption"]
- if "?" in metadata["LICENSE"]:
- settings.setcpv(mycpv, mydb=metadata)
- metadata["USE"] = settings["PORTAGE_USE"]
- else:
- metadata["USE"] = ""
- mycp=mysplit[0]+"/"+mysplit[1]
-
- rValue = []
-
- # profile checking
- if settings._getProfileMaskAtom(mycpv, metadata):
- rValue.append("profile")
-
- # package.mask checking
- if settings._getMaskAtom(mycpv, metadata):
- rValue.append("package.mask")
-
- # keywords checking
- eapi = metadata["EAPI"]
- mygroups = settings._getKeywords(mycpv, metadata)
- licenses = metadata["LICENSE"]
- properties = metadata["PROPERTIES"]
- slot = metadata["SLOT"]
- if eapi.startswith("-"):
- eapi = eapi[1:]
- if not eapi_is_supported(eapi):
- return ["EAPI %s" % eapi]
- elif _eapi_is_deprecated(eapi) and not installed:
- return ["EAPI %s" % eapi]
- egroups = settings.configdict["backupenv"].get(
- "ACCEPT_KEYWORDS", "").split()
- pgroups = settings["ACCEPT_KEYWORDS"].split()
- myarch = settings["ARCH"]
- if pgroups and myarch not in pgroups:
- """For operating systems other than Linux, ARCH is not necessarily a
- valid keyword."""
- myarch = pgroups[0].lstrip("~")
-
- cp = cpv_getkey(mycpv)
- pkgdict = settings.pkeywordsdict.get(cp)
- matches = False
- if pkgdict:
- cpv_slot_list = ["%s:%s" % (mycpv, metadata["SLOT"])]
- for atom, pkgkeywords in pkgdict.items():
- if match_from_list(atom, cpv_slot_list):
- matches = True
- pgroups.extend(pkgkeywords)
- if matches or egroups:
- pgroups.extend(egroups)
- inc_pgroups = set()
- for x in pgroups:
- if x.startswith("-"):
- if x == "-*":
- inc_pgroups.clear()
- else:
- inc_pgroups.discard(x[1:])
- else:
- inc_pgroups.add(x)
- pgroups = inc_pgroups
- del inc_pgroups
-
- kmask = "missing"
-
- if '**' in pgroups:
- kmask = None
- else:
- for keyword in pgroups:
- if keyword in mygroups:
- kmask = None
- break
-
- if kmask:
- fallback = None
- for gp in mygroups:
- if gp=="*":
- kmask=None
- break
- elif gp=="-"+myarch and myarch in pgroups:
- kmask="-"+myarch
- break
- elif gp=="~"+myarch and myarch in pgroups:
- kmask="~"+myarch
- break
-
- try:
- missing_licenses = settings._getMissingLicenses(mycpv, metadata)
- if missing_licenses:
- allowed_tokens = set(["||", "(", ")"])
- allowed_tokens.update(missing_licenses)
- license_split = licenses.split()
- license_split = [x for x in license_split \
- if x in allowed_tokens]
- msg = license_split[:]
- msg.append("license(s)")
- rValue.append(" ".join(msg))
- except portage.exception.InvalidDependString as e:
- rValue.append("LICENSE: "+str(e))
-
- try:
- missing_properties = settings._getMissingProperties(mycpv, metadata)
- if missing_properties:
- allowed_tokens = set(["||", "(", ")"])
- allowed_tokens.update(missing_properties)
- properties_split = properties.split()
- properties_split = [x for x in properties_split \
- if x in allowed_tokens]
- msg = properties_split[:]
- msg.append("properties")
- rValue.append(" ".join(msg))
- except portage.exception.InvalidDependString as e:
- rValue.append("PROPERTIES: "+str(e))
-
- # Only show KEYWORDS masks for installed packages
- # if they're not masked for any other reason.
- if kmask and (not installed or not rValue):
- rValue.append(kmask+" keyword")
-
- return rValue
-
auxdbkeys = (
'DEPEND', 'RDEPEND', 'SLOT', 'SRC_URI',
'RESTRICT', 'HOMEPAGE', 'LICENSE', 'DESCRIPTION',
import portage
portage.proxy.lazyimport.lazyimport(globals(),
- 'portage.dbapi.dep_expand:dep_expand',
+ 'portage.dbapi.dep_expand:_dep_expand',
'portage.dep:match_from_list',
'portage.locks:unlockfile',
'portage.output:colorize',
Returns:
a list of packages that match origdep
"""
- mydep = dep_expand(origdep, mydb=self, settings=self.settings)
+ mydep = _dep_expand(origdep, mydb=self, settings=self.settings)
return list(self._iter_match(mydep,
self.cp_list(mydep.cp, use_cache=use_cache)))
InvalidData, InvalidPackageName, \
FileNotFound, PermissionDenied, UnsupportedAPIException
from portage.localization import _
+from portage.util.movefile import movefile
-from portage import abssymlink, movefile, _movefile, bsd_chflags
+from portage import abssymlink, _movefile, bsd_chflags
# This is a special version of the os module, wrapped for unicode support.
from portage import os
--- /dev/null
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+__all__ = ['dep_check', 'dep_eval', 'dep_wordreduce', 'dep_zapdeps']
+
+import logging
+
+import portage
+from portage.dep import Atom, dep_opconvert, match_from_list, paren_reduce, \
+ remove_slot, use_reduce
+from portage.exception import InvalidAtom, InvalidDependString, ParseError
+from portage.localization import _
+from portage.util import writemsg, writemsg_level
+from portage.versions import catpkgsplit, cpv_getkey, pkgcmp
+
+def _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings, myroot="/",
+ trees=None, use_mask=None, use_force=None, **kwargs):
+ """
+ In order to solve bug #141118, recursively expand new-style virtuals so
+ as to collapse one or more levels of indirection, generating an expanded
+ search space. In dep_zapdeps, new-style virtuals will be assigned
+ zero cost regardless of whether or not they are currently installed. Virtual
+ blockers are supported but only when the virtual expands to a single
+ atom because it wouldn't necessarily make sense to block all the components
+ of a compound virtual. When more than one new-style virtual is matched,
+ the matches are sorted from highest to lowest versions and the atom is
+ expanded to || ( highest match ... lowest match )."""
+ newsplit = []
+ mytrees = trees[myroot]
+ portdb = mytrees["porttree"].dbapi
+ atom_graph = mytrees.get("atom_graph")
+ parent = mytrees.get("parent")
+ virt_parent = mytrees.get("virt_parent")
+ graph_parent = None
+ eapi = None
+ if parent is not None:
+ if virt_parent is not None:
+ graph_parent = virt_parent
+ eapi = virt_parent[0].metadata['EAPI']
+ else:
+ graph_parent = parent
+ eapi = parent.metadata["EAPI"]
+ repoman = not mysettings.local_config
+ if kwargs["use_binaries"]:
+ portdb = trees[myroot]["bintree"].dbapi
+ myvirtuals = mysettings.getvirtuals()
+ pprovideddict = mysettings.pprovideddict
+ myuse = kwargs["myuse"]
+ for x in mysplit:
+ if x == "||":
+ newsplit.append(x)
+ continue
+ elif isinstance(x, list):
+ newsplit.append(_expand_new_virtuals(x, edebug, mydbapi,
+ mysettings, myroot=myroot, trees=trees, use_mask=use_mask,
+ use_force=use_force, **kwargs))
+ continue
+
+ if not isinstance(x, Atom):
+ try:
+ x = Atom(x)
+ except InvalidAtom:
+ if portage.dep._dep_check_strict:
+ raise ParseError(
+ _("invalid atom: '%s'") % x)
+ else:
+ # Only real Atom instances are allowed past this point.
+ continue
+ else:
+ if x.blocker and x.blocker.overlap.forbid and \
+ eapi in ("0", "1") and portage.dep._dep_check_strict:
+ raise ParseError(
+ _("invalid atom: '%s'") % (x,))
+ if x.use and eapi in ("0", "1") and \
+ portage.dep._dep_check_strict:
+ raise ParseError(
+ _("invalid atom: '%s'") % (x,))
+
+ if repoman and x.use and x.use.conditional:
+ evaluated_atom = remove_slot(x)
+ if x.slot:
+ evaluated_atom += ":%s" % x.slot
+ evaluated_atom += str(x.use._eval_qa_conditionals(
+ use_mask, use_force))
+ x = Atom(evaluated_atom)
+
+ if not repoman and \
+ myuse is not None and isinstance(x, Atom) and x.use:
+ if x.use.conditional:
+ x = x.evaluate_conditionals(myuse)
+
+ mykey = x.cp
+ if not mykey.startswith("virtual/"):
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ continue
+ mychoices = myvirtuals.get(mykey, [])
+ if x.blocker:
+ # Virtual blockers are no longer expanded here since
+ # the un-expanded virtual atom is more useful for
+ # maintaining a cache of blocker atoms.
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ continue
+
+ if repoman or not hasattr(portdb, 'match_pkgs'):
+ if portdb.cp_list(x.cp):
+ newsplit.append(x)
+ else:
+ # TODO: Add PROVIDE check for repoman.
+ a = []
+ for y in mychoices:
+ a.append(Atom(x.replace(x.cp, y.cp, 1)))
+ if not a:
+ newsplit.append(x)
+ elif len(a) == 1:
+ newsplit.append(a[0])
+ else:
+ newsplit.append(['||'] + a)
+ continue
+
+ pkgs = []
+ # Ignore USE deps here, since otherwise we might not
+ # get any matches. Choices with correct USE settings
+ # will be preferred in dep_zapdeps().
+ matches = portdb.match_pkgs(x.without_use)
+ # Use descending order to prefer higher versions.
+ matches.reverse()
+ for pkg in matches:
+ # only use new-style matches
+ if pkg.cp.startswith("virtual/"):
+ pkgs.append(pkg)
+ if not (pkgs or mychoices):
+ # This one couldn't be expanded as a new-style virtual. Old-style
+ # virtuals have already been expanded by dep_virtual, so this one
+ # is unavailable and dep_zapdeps will identify it as such. The
+ # atom is not eliminated here since it may still represent a
+ # dependency that needs to be satisfied.
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ continue
+
+ a = []
+ for pkg in pkgs:
+ virt_atom = '=' + pkg.cpv
+ if x.use:
+ virt_atom += str(x.use)
+ virt_atom = Atom(virt_atom)
+ # According to GLEP 37, RDEPEND is the only dependency
+ # type that is valid for new-style virtuals. Repoman
+ # should enforce this.
+ depstring = pkg.metadata['RDEPEND']
+ pkg_kwargs = kwargs.copy()
+ pkg_kwargs["myuse"] = pkg.use.enabled
+ if edebug:
+ writemsg_level(_("Virtual Parent: %s\n") \
+ % (pkg,), noiselevel=-1, level=logging.DEBUG)
+ writemsg_level(_("Virtual Depstring: %s\n") \
+ % (depstring,), noiselevel=-1, level=logging.DEBUG)
+
+ # Set EAPI used for validation in dep_check() recursion.
+ mytrees["virt_parent"] = (pkg, virt_atom)
+
+ try:
+ mycheck = dep_check(depstring, mydbapi, mysettings,
+ myroot=myroot, trees=trees, **pkg_kwargs)
+ finally:
+ # Restore previous EAPI after recursion.
+ if virt_parent is not None:
+ mytrees["virt_parent"] = virt_parent
+ else:
+ del mytrees["virt_parent"]
+
+ if not mycheck[0]:
+ raise ParseError(
+ "%s: %s '%s'" % (y[0], mycheck[1], depstring))
+
+ # pull in the new-style virtual
+ mycheck[1].append(virt_atom)
+ a.append(mycheck[1])
+ if atom_graph is not None:
+ atom_graph.add(virt_atom, graph_parent)
+ # Plain old-style virtuals. New-style virtuals are preferred.
+ if not pkgs:
+ for y in mychoices:
+ new_atom = Atom(x.replace(x.cp, y.cp, 1))
+ matches = portdb.match(new_atom)
+ # portdb is an instance of depgraph._dep_check_composite_db, so
+ # USE conditionals are already evaluated.
+ if matches and mykey in \
+ portdb.aux_get(matches[-1], ['PROVIDE'])[0].split():
+ a.append(new_atom)
+ if atom_graph is not None:
+ atom_graph.add(new_atom, graph_parent)
+
+ if not a and mychoices:
+ # Check for a virtual package.provided match.
+ for y in mychoices:
+ new_atom = Atom(x.replace(x.cp, y.cp, 1))
+ if match_from_list(new_atom,
+ pprovideddict.get(new_atom.cp, [])):
+ a.append(new_atom)
+ if atom_graph is not None:
+ atom_graph.add(new_atom, graph_parent)
+
+ if not a:
+ newsplit.append(x)
+ if atom_graph is not None:
+ atom_graph.add(x, graph_parent)
+ elif len(a) == 1:
+ newsplit.append(a[0])
+ else:
+ newsplit.append(['||'] + a)
+
+ return newsplit
+
+def dep_eval(deplist):
+ if not deplist:
+ return 1
+ if deplist[0]=="||":
+ #or list; we just need one "1"
+ for x in deplist[1:]:
+ if isinstance(x, list):
+ if dep_eval(x)==1:
+ return 1
+ elif x==1:
+ return 1
+ #XXX: unless there's no available atoms in the list
+ #in which case we need to assume that everything is
+ #okay as some ebuilds are relying on an old bug.
+ if len(deplist) == 1:
+ return 1
+ return 0
+ else:
+ for x in deplist:
+ if isinstance(x, list):
+ if dep_eval(x)==0:
+ return 0
+ elif x==0 or x==2:
+ return 0
+ return 1
+
+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 = []
+
+ # 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,
+ )
+
+ # 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")
+ 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_slot = Atom("%s:%s" % (atom.cp,
+ mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
+ 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_slot = Atom("%s:%s" % (atom.cp,
+ mydbapi.aux_get(avail_pkg, ["SLOT"])[0]))
+
+ slot_map[avail_slot] = avail_pkg
+ pkg_cp = cpv_getkey(avail_pkg)
+ highest_cpv = cp_map.get(pkg_cp)
+ if highest_cpv is None or \
+ pkgcmp(catpkgsplit(avail_pkg)[1:],
+ catpkgsplit(highest_cpv)[1:]) > 0:
+ cp_map[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 not graph_db.match(slot_atom) and \
+ not slot_atom.startswith("virtual/"):
+ all_in_graph = False
+ break
+ circular_atom = None
+ if all_in_graph:
+ if parent is None or priority is None:
+ pass
+ elif priority.buildtime:
+ # 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:
+ 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 = pkgcmp(catpkgsplit(version_1)[1:],
+ catpkgsplit(version_2)[1:])
+ 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
+
+def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
+ use_cache=1, use_binaries=0, myroot="/", trees=None):
+ """Takes a depend string and parses the condition."""
+ edebug = mysettings.get("PORTAGE_DEBUG", None) == "1"
+ #check_config_instance(mysettings)
+ if trees is None:
+ trees = globals()["db"]
+ if use=="yes":
+ if myuse is None:
+ #default behavior
+ myusesplit = mysettings["PORTAGE_USE"].split()
+ else:
+ myusesplit = myuse
+ # We've been given useflags to use.
+ #print "USE FLAGS PASSED IN."
+ #print myuse
+ #if "bindist" in myusesplit:
+ # print "BINDIST is set!"
+ #else:
+ # print "BINDIST NOT set."
+ else:
+ #we are being run by autouse(), don't consult USE vars yet.
+ # WE ALSO CANNOT USE SETTINGS
+ myusesplit=[]
+
+ #convert parenthesis to sublists
+ try:
+ mysplit = paren_reduce(depstring)
+ except InvalidDependString as e:
+ return [0, str(e)]
+
+ mymasks = set()
+ useforce = set()
+ useforce.add(mysettings["ARCH"])
+ if use == "all":
+ # This masking/forcing is only for repoman. In other cases, relevant
+ # masking/forcing should have already been applied via
+ # config.regenerate(). Also, binary or installed packages may have
+ # been built with flags that are now masked, and it would be
+ # inconsistent to mask them now. Additionally, myuse may consist of
+ # flags from a parent package that is being merged to a $ROOT that is
+ # different from the one that mysettings represents.
+ mymasks.update(mysettings.usemask)
+ mymasks.update(mysettings.archlist())
+ mymasks.discard(mysettings["ARCH"])
+ useforce.update(mysettings.useforce)
+ useforce.difference_update(mymasks)
+ try:
+ mysplit = use_reduce(mysplit, uselist=myusesplit,
+ masklist=mymasks, matchall=(use=="all"), excludeall=useforce)
+ except InvalidDependString as e:
+ return [0, str(e)]
+
+ # Do the || conversions
+ mysplit = dep_opconvert(mysplit)
+
+ if mysplit == []:
+ #dependencies were reduced to nothing
+ return [1,[]]
+
+ # Recursively expand new-style virtuals so as to
+ # collapse one or more levels of indirection.
+ try:
+ mysplit = _expand_new_virtuals(mysplit, edebug, mydbapi, mysettings,
+ use=use, mode=mode, myuse=myuse,
+ use_force=useforce, use_mask=mymasks, use_cache=use_cache,
+ use_binaries=use_binaries, myroot=myroot, trees=trees)
+ except ParseError as e:
+ return [0, str(e)]
+
+ mysplit2=mysplit[:]
+ mysplit2=dep_wordreduce(mysplit2,mysettings,mydbapi,mode,use_cache=use_cache)
+ if mysplit2 is None:
+ return [0, _("Invalid token")]
+
+ writemsg("\n\n\n", 1)
+ writemsg("mysplit: %s\n" % (mysplit), 1)
+ writemsg("mysplit2: %s\n" % (mysplit2), 1)
+
+ try:
+ selected_atoms = dep_zapdeps(mysplit, mysplit2, myroot,
+ use_binaries=use_binaries, trees=trees)
+ except InvalidAtom as e:
+ if portage.dep._dep_check_strict:
+ raise # This shouldn't happen.
+ # dbapi.match() failed due to an invalid atom in
+ # the dependencies of an installed package.
+ return [0, _("Invalid atom: '%s'") % (e,)]
+
+ return [1, selected_atoms]
+
+def dep_wordreduce(mydeplist,mysettings,mydbapi,mode,use_cache=1):
+ "Reduces the deplist to ones and zeros"
+ deplist=mydeplist[:]
+ for mypos, token in enumerate(deplist):
+ if isinstance(deplist[mypos], list):
+ #recurse
+ deplist[mypos]=dep_wordreduce(deplist[mypos],mysettings,mydbapi,mode,use_cache=use_cache)
+ elif deplist[mypos]=="||":
+ pass
+ elif token[:1] == "!":
+ deplist[mypos] = False
+ else:
+ mykey = deplist[mypos].cp
+ if mysettings and mykey in mysettings.pprovideddict and \
+ match_from_list(deplist[mypos], mysettings.pprovideddict[mykey]):
+ deplist[mypos]=True
+ elif mydbapi is None:
+ # Assume nothing is satisfied. This forces dep_zapdeps to
+ # return all of deps the deps that have been selected
+ # (excluding those satisfied by package.provided).
+ deplist[mypos] = False
+ else:
+ if mode:
+ x = mydbapi.xmatch(mode, deplist[mypos])
+ if mode.startswith("minimum-"):
+ mydep = []
+ if x:
+ mydep.append(x)
+ else:
+ mydep = x
+ else:
+ mydep=mydbapi.match(deplist[mypos],use_cache=use_cache)
+ if mydep!=None:
+ tmp=(len(mydep)>=1)
+ if deplist[mypos][0]=="!":
+ tmp=False
+ deplist[mypos]=tmp
+ else:
+ #encountered invalid string
+ return None
+ return deplist
--- /dev/null
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+__all__ = ['getmaskingreason']
+
+import portage
+from portage import os
+from portage.const import USER_CONFIG_PATH
+from portage.dep import match_from_list
+from portage.localization import _
+from portage.util import grablines, normalize_path
+from portage.versions import catpkgsplit
+
+def getmaskingreason(mycpv, metadata=None, settings=None, portdb=None, return_location=False):
+ if settings is None:
+ settings = portage.settings
+ if portdb is None:
+ portdb = portage.portdb
+ mysplit = catpkgsplit(mycpv)
+ if not mysplit:
+ raise ValueError(_("invalid CPV: %s") % mycpv)
+ if metadata is None:
+ db_keys = list(portdb._aux_cache_keys)
+ try:
+ metadata = dict(zip(db_keys, portdb.aux_get(mycpv, db_keys)))
+ except KeyError:
+ if not portdb.cpv_exists(mycpv):
+ raise
+ if metadata is None:
+ # Can't access SLOT due to corruption.
+ cpv_slot_list = [mycpv]
+ else:
+ cpv_slot_list = ["%s:%s" % (mycpv, metadata["SLOT"])]
+ mycp=mysplit[0]+"/"+mysplit[1]
+
+ # XXX- This is a temporary duplicate of code from the config constructor.
+ locations = [os.path.join(settings["PORTDIR"], "profiles")]
+ locations.extend(settings.profiles)
+ for ov in settings["PORTDIR_OVERLAY"].split():
+ profdir = os.path.join(normalize_path(ov), "profiles")
+ if os.path.isdir(profdir):
+ locations.append(profdir)
+ locations.append(os.path.join(settings["PORTAGE_CONFIGROOT"],
+ USER_CONFIG_PATH))
+ locations.reverse()
+ pmasklists = [(x, grablines(os.path.join(x, "package.mask"), recursive=1)) for x in locations]
+
+ if mycp in settings.pmaskdict:
+ for x in settings.pmaskdict[mycp]:
+ if match_from_list(x, cpv_slot_list):
+ for pmask in pmasklists:
+ comment = ""
+ comment_valid = -1
+ pmask_filename = os.path.join(pmask[0], "package.mask")
+ for i in range(len(pmask[1])):
+ l = pmask[1][i].strip()
+ if l == "":
+ comment = ""
+ comment_valid = -1
+ elif l[0] == "#":
+ comment += (l+"\n")
+ comment_valid = i + 1
+ elif l == x:
+ if comment_valid != i:
+ comment = ""
+ if return_location:
+ return (comment, pmask_filename)
+ else:
+ return comment
+ elif comment_valid != -1:
+ # Apparently this comment applies to muliple masks, so
+ # it remains valid until a blank line is encountered.
+ comment_valid += 1
+ if return_location:
+ return (None, None)
+ else:
+ return None
--- /dev/null
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+__all__ = ['getmaskingstatus']
+
+import portage
+from portage import eapi_is_supported, _eapi_is_deprecated
+from portage.dep import match_from_list
+from portage.localization import _
+from portage.package.ebuild.config import config
+from portage.versions import catpkgsplit, cpv_getkey
+
+def getmaskingstatus(mycpv, settings=None, portdb=None):
+ if settings is None:
+ settings = config(clone=portage.settings)
+ if portdb is None:
+ portdb = portage.portdb
+
+ metadata = None
+ installed = False
+ if not isinstance(mycpv, basestring):
+ # emerge passed in a Package instance
+ pkg = mycpv
+ mycpv = pkg.cpv
+ metadata = pkg.metadata
+ installed = pkg.installed
+
+ mysplit = catpkgsplit(mycpv)
+ if not mysplit:
+ raise ValueError(_("invalid CPV: %s") % mycpv)
+ if metadata is None:
+ db_keys = list(portdb._aux_cache_keys)
+ try:
+ metadata = dict(zip(db_keys, portdb.aux_get(mycpv, db_keys)))
+ except KeyError:
+ if not portdb.cpv_exists(mycpv):
+ raise
+ return ["corruption"]
+ if "?" in metadata["LICENSE"]:
+ settings.setcpv(mycpv, mydb=metadata)
+ metadata["USE"] = settings["PORTAGE_USE"]
+ else:
+ metadata["USE"] = ""
+
+ rValue = []
+
+ # profile checking
+ if settings._getProfileMaskAtom(mycpv, metadata):
+ rValue.append("profile")
+
+ # package.mask checking
+ if settings._getMaskAtom(mycpv, metadata):
+ rValue.append("package.mask")
+
+ # keywords checking
+ eapi = metadata["EAPI"]
+ mygroups = settings._getKeywords(mycpv, metadata)
+ licenses = metadata["LICENSE"]
+ properties = metadata["PROPERTIES"]
+ if eapi.startswith("-"):
+ eapi = eapi[1:]
+ if not eapi_is_supported(eapi):
+ return ["EAPI %s" % eapi]
+ elif _eapi_is_deprecated(eapi) and not installed:
+ return ["EAPI %s" % eapi]
+ egroups = settings.configdict["backupenv"].get(
+ "ACCEPT_KEYWORDS", "").split()
+ pgroups = settings["ACCEPT_KEYWORDS"].split()
+ myarch = settings["ARCH"]
+ if pgroups and myarch not in pgroups:
+ """For operating systems other than Linux, ARCH is not necessarily a
+ valid keyword."""
+ myarch = pgroups[0].lstrip("~")
+
+ cp = cpv_getkey(mycpv)
+ pkgdict = settings.pkeywordsdict.get(cp)
+ matches = False
+ if pkgdict:
+ cpv_slot_list = ["%s:%s" % (mycpv, metadata["SLOT"])]
+ for atom, pkgkeywords in pkgdict.items():
+ if match_from_list(atom, cpv_slot_list):
+ matches = True
+ pgroups.extend(pkgkeywords)
+ if matches or egroups:
+ pgroups.extend(egroups)
+ inc_pgroups = set()
+ for x in pgroups:
+ if x.startswith("-"):
+ if x == "-*":
+ inc_pgroups.clear()
+ else:
+ inc_pgroups.discard(x[1:])
+ else:
+ inc_pgroups.add(x)
+ pgroups = inc_pgroups
+ del inc_pgroups
+
+ kmask = "missing"
+
+ if '**' in pgroups:
+ kmask = None
+ else:
+ for keyword in pgroups:
+ if keyword in mygroups:
+ kmask = None
+ break
+
+ if kmask:
+ for gp in mygroups:
+ if gp=="*":
+ kmask=None
+ break
+ elif gp=="-"+myarch and myarch in pgroups:
+ kmask="-"+myarch
+ break
+ elif gp=="~"+myarch and myarch in pgroups:
+ kmask="~"+myarch
+ break
+
+ try:
+ missing_licenses = settings._getMissingLicenses(mycpv, metadata)
+ if missing_licenses:
+ allowed_tokens = set(["||", "(", ")"])
+ allowed_tokens.update(missing_licenses)
+ license_split = licenses.split()
+ license_split = [x for x in license_split \
+ if x in allowed_tokens]
+ msg = license_split[:]
+ msg.append("license(s)")
+ rValue.append(" ".join(msg))
+ except portage.exception.InvalidDependString as e:
+ rValue.append("LICENSE: "+str(e))
+
+ try:
+ missing_properties = settings._getMissingProperties(mycpv, metadata)
+ if missing_properties:
+ allowed_tokens = set(["||", "(", ")"])
+ allowed_tokens.update(missing_properties)
+ properties_split = properties.split()
+ properties_split = [x for x in properties_split \
+ if x in allowed_tokens]
+ msg = properties_split[:]
+ msg.append("properties")
+ rValue.append(" ".join(msg))
+ except portage.exception.InvalidDependString as e:
+ rValue.append("PROPERTIES: "+str(e))
+
+ # Only show KEYWORDS masks for installed packages
+ # if they're not masked for any other reason.
+ if kmask and (not installed or not rValue):
+ rValue.append(kmask+" keyword")
+
+ return rValue
--- /dev/null
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+__all__ = ['movefile']
+
+import errno
+import os as _os
+import shutil as _shutil
+import stat
+
+import portage
+from portage import bsd_chflags, _encodings, _os_overrides, _selinux, \
+ _unicode_decode, _unicode_func_wrapper, _unicode_module_wrapper
+from portage.const import MOVE_BINARY
+from portage.localization import _
+from portage.process import spawn
+from portage.util import writemsg
+
+def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
+ hardlink_candidates=None, encoding=_encodings['fs']):
+ """moves a file from src to dest, preserving all permissions and attributes; mtime will
+ be preserved even when moving across filesystems. Returns true on success and false on
+ failure. Move is atomic."""
+ #print "movefile("+str(src)+","+str(dest)+","+str(newmtime)+","+str(sstat)+")"
+
+ if mysettings is None:
+ mysettings = portage.settings
+
+ selinux_enabled = mysettings.selinux_enabled()
+ if selinux_enabled:
+ selinux = _unicode_module_wrapper(_selinux, encoding=encoding)
+
+ lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding)
+ os = _unicode_module_wrapper(_os,
+ encoding=encoding, overrides=_os_overrides)
+ shutil = _unicode_module_wrapper(_shutil, encoding=encoding)
+
+ try:
+ if not sstat:
+ sstat=os.lstat(src)
+
+ except SystemExit as e:
+ raise
+ except Exception as e:
+ print(_("!!! Stating source file failed... movefile()"))
+ print("!!!",e)
+ return None
+
+ destexists=1
+ try:
+ dstat=os.lstat(dest)
+ except (OSError, IOError):
+ dstat=os.lstat(os.path.dirname(dest))
+ destexists=0
+
+ if bsd_chflags:
+ if destexists and dstat.st_flags != 0:
+ bsd_chflags.lchflags(dest, 0)
+ # Use normal stat/chflags for the parent since we want to
+ # follow any symlinks to the real parent directory.
+ pflags = os.stat(os.path.dirname(dest)).st_flags
+ if pflags != 0:
+ bsd_chflags.chflags(os.path.dirname(dest), 0)
+
+ if destexists:
+ if stat.S_ISLNK(dstat[stat.ST_MODE]):
+ try:
+ os.unlink(dest)
+ destexists=0
+ except SystemExit as e:
+ raise
+ except Exception as e:
+ pass
+
+ if stat.S_ISLNK(sstat[stat.ST_MODE]):
+ try:
+ target=os.readlink(src)
+ if mysettings and mysettings["D"]:
+ if target.find(mysettings["D"])==0:
+ target=target[len(mysettings["D"]):]
+ if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
+ os.unlink(dest)
+ if selinux_enabled:
+ selinux.symlink(target, dest, src)
+ else:
+ os.symlink(target,dest)
+ lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+ # utime() only works on the target of a symlink, so it's not
+ # possible to perserve mtime on symlinks.
+ return os.lstat(dest)[stat.ST_MTIME]
+ except SystemExit as e:
+ raise
+ except Exception as e:
+ print(_("!!! failed to properly create symlink:"))
+ print("!!!",dest,"->",target)
+ print("!!!",e)
+ return None
+
+ hardlinked = False
+ # Since identical files might be merged to multiple filesystems,
+ # so os.link() calls might fail for some paths, so try them all.
+ # For atomic replacement, first create the link as a temp file
+ # and them use os.rename() to replace the destination.
+ if hardlink_candidates:
+ head, tail = os.path.split(dest)
+ hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \
+ (tail, os.getpid()))
+ try:
+ os.unlink(hardlink_tmp)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \
+ (hardlink_tmp,), noiselevel=-1)
+ writemsg("!!! %s\n" % (e,), noiselevel=-1)
+ return None
+ del e
+ for hardlink_src in hardlink_candidates:
+ try:
+ os.link(hardlink_src, hardlink_tmp)
+ except OSError:
+ continue
+ else:
+ try:
+ os.rename(hardlink_tmp, dest)
+ except OSError as e:
+ writemsg(_("!!! Failed to rename %s to %s\n") % \
+ (hardlink_tmp, dest), noiselevel=-1)
+ writemsg("!!! %s\n" % (e,), noiselevel=-1)
+ return None
+ hardlinked = True
+ break
+
+ renamefailed=1
+ if hardlinked:
+ renamefailed = False
+ if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
+ try:
+ if selinux_enabled:
+ selinux.rename(src, dest)
+ else:
+ os.rename(src,dest)
+ renamefailed=0
+ except OSError as e:
+ if e.errno != errno.EXDEV:
+ # Some random error.
+ print(_("!!! Failed to move %(src)s to %(dest)s") % {"src": src, "dest": dest})
+ print("!!!",e)
+ return None
+ # Invalid cross-device-link 'bind' mounted or actually Cross-Device
+ if renamefailed:
+ didcopy=0
+ if stat.S_ISREG(sstat[stat.ST_MODE]):
+ try: # For safety copy then move it over.
+ if selinux_enabled:
+ selinux.copyfile(src, dest + "#new")
+ selinux.rename(dest + "#new", dest)
+ else:
+ shutil.copyfile(src,dest+"#new")
+ os.rename(dest+"#new",dest)
+ didcopy=1
+ except SystemExit as e:
+ raise
+ except Exception as e:
+ print(_('!!! copy %(src)s -> %(dest)s failed.') % {"src": src, "dest": dest})
+ print("!!!",e)
+ return None
+ else:
+ #we don't yet handle special, so we need to fall back to /bin/mv
+ a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
+ if a != os.EX_OK:
+ writemsg(_("!!! Failed to move special file:\n"), noiselevel=-1)
+ writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
+ {"src": _unicode_decode(src, encoding=encoding),
+ "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
+ writemsg("!!! %s\n" % a, noiselevel=-1)
+ return None # failure
+ try:
+ if didcopy:
+ if stat.S_ISLNK(sstat[stat.ST_MODE]):
+ lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+ else:
+ os.chown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+ os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
+ os.unlink(src)
+ except SystemExit as e:
+ raise
+ except Exception as e:
+ print(_("!!! Failed to chown/chmod/unlink in movefile()"))
+ print("!!!",dest)
+ print("!!!",e)
+ return None
+
+ # Always use stat_obj[stat.ST_MTIME] for the integral timestamp which
+ # is returned, since the stat_obj.st_mtime float attribute rounds *up*
+ # if the nanosecond part of the timestamp is 999999881 ns or greater.
+ try:
+ if hardlinked:
+ newmtime = os.stat(dest)[stat.ST_MTIME]
+ else:
+ # Note: It is not possible to preserve nanosecond precision
+ # (supported in POSIX.1-2008 via utimensat) with the IEEE 754
+ # double precision float which only has a 53 bit significand.
+ if newmtime is not None:
+ os.utime(dest, (newmtime, newmtime))
+ else:
+ newmtime = sstat[stat.ST_MTIME]
+ if renamefailed:
+ # If rename succeeded then timestamps are automatically
+ # preserved with complete precision because the source
+ # and destination inode are the same. Otherwise, round
+ # down to the nearest whole second since python's float
+ # st_mtime cannot be used to preserve the st_mtim.tv_nsec
+ # field with complete precision. Note that we have to use
+ # stat_obj[stat.ST_MTIME] here because the float
+ # stat_obj.st_mtime rounds *up* sometimes.
+ os.utime(dest, (newmtime, newmtime))
+ except OSError:
+ # The utime can fail here with EPERM even though the move succeeded.
+ # Instead of failing, use stat to return the mtime if possible.
+ try:
+ newmtime = os.stat(dest)[stat.ST_MTIME]
+ except OSError as e:
+ writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
+ writemsg("!!! %s\n" % dest, noiselevel=-1)
+ writemsg("!!! %s\n" % str(e), noiselevel=-1)
+ return None
+
+ if bsd_chflags:
+ # Restore the flags we saved before moving
+ if pflags:
+ bsd_chflags.chflags(os.path.dirname(dest), pflags)
+
+ return newmtime