blob: 7ed9df4a6040f6a5990c75c4871b3d10614692cb [file] [log] [blame]
# portage.py -- core Portage functionality
# Copyright 1998-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$
from __future__ import print_function
VERSION="$Rev$"[6:-2] + "-svn"
# ===========================================================================
# START OF IMPORTS -- START OF IMPORTS -- START OF IMPORTS -- START OF IMPORT
# ===========================================================================
try:
import sys
import codecs
import copy
import errno
if not hasattr(errno, 'ESTALE'):
# ESTALE may not be defined on some systems, such as interix.
errno.ESTALE = -1
import logging
import re
import time
import types
try:
import cPickle as pickle
except ImportError:
import pickle
import stat
try:
from subprocess import getstatusoutput as subprocess_getstatusoutput
except ImportError:
from commands import getstatusoutput as subprocess_getstatusoutput
try:
from io import StringIO
except ImportError:
# Needed for python-2.6 with USE=build since
# io imports threading which imports thread
# which is unavailable.
from StringIO import StringIO
from time import sleep
from random import shuffle
from itertools import chain
import platform
import warnings
# Temporarily delete these imports, to ensure that only the
# wrapped versions are imported by portage internals.
import os
del os
import shutil
del shutil
except ImportError as e:
sys.stderr.write("\n\n")
sys.stderr.write("!!! Failed to complete python imports. These are internal modules for\n")
sys.stderr.write("!!! python and failure here indicates that you have a problem with python\n")
sys.stderr.write("!!! itself and thus portage is not able to continue processing.\n\n")
sys.stderr.write("!!! You might consider starting python with verbose flags to see what has\n")
sys.stderr.write("!!! gone wrong. Here is the information we got for this exception:\n")
sys.stderr.write(" "+str(e)+"\n\n");
raise
try:
from portage.cache.cache_errors import CacheError
import portage.proxy.lazyimport
import portage.proxy as proxy
proxy.lazyimport.lazyimport(globals(),
'portage.checksum',
'portage.checksum:perform_checksum,perform_md5,prelink_capable',
'portage.cvstree',
'portage.data',
'portage.data:lchown,ostype,portage_gid,portage_uid,secpass,' + \
'uid,userland,userpriv_groups,wheelgid',
'portage.dep',
'portage.dep:best_match_to_list,dep_getcpv,dep_getkey,' + \
'get_operator,isjustname,isspecific,isvalidatom,' + \
'match_from_list,match_to_list',
'portage.eclass_cache',
'portage.env.loaders',
'portage.exception',
'portage.getbinpkg',
'portage.locks',
'portage.locks:lockdir,lockfile,unlockdir,unlockfile',
'portage.mail',
'portage.output',
'portage.output:bold,colorize',
'portage.process',
'portage.process:atexit_register,run_exitfuncs',
'portage.update:dep_transform,fixdbentries,grab_updates,' + \
'parse_updates,update_config_files,update_dbentries,' + \
'update_dbentry',
'portage.util',
'portage.util:atomic_ofstream,apply_secpass_permissions,' + \
'apply_recursive_permissions,dump_traceback,getconfig,' + \
'grabdict,grabdict_package,grabfile,grabfile_package,' + \
'map_dictlist_vals,new_protect_filename,normalize_path,' + \
'pickle_read,pickle_write,stack_dictlist,stack_dicts,' + \
'stack_lists,unique_array,varexpand,writedict,writemsg,' + \
'writemsg_stdout,write_atomic',
'portage.versions',
'portage.versions:best,catpkgsplit,catsplit,endversion_keys,' + \
'suffix_value@endversion,pkgcmp,pkgsplit,vercmp,ververify',
'portage.xpak',
)
import portage.const
from portage.const import VDB_PATH, PRIVATE_PATH, CACHE_PATH, DEPCACHE_PATH, \
USER_CONFIG_PATH, MODULES_FILE_PATH, CUSTOM_PROFILE_PATH, PORTAGE_BASE_PATH, \
PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, PROFILE_PATH, LOCALE_DATA_PATH, \
EBUILD_SH_BINARY, SANDBOX_BINARY, BASH_BINARY, \
MOVE_BINARY, PRELINK_BINARY, WORLD_FILE, MAKE_CONF_FILE, MAKE_DEFAULTS_FILE, \
DEPRECATED_PROFILE_FILE, USER_VIRTUALS_FILE, EBUILD_SH_ENV_FILE, \
INVALID_ENV_FILE, CUSTOM_MIRRORS_FILE, CONFIG_MEMORY_FILE,\
INCREMENTALS, EAPI, MISC_SH_BINARY, REPO_NAME_LOC, REPO_NAME_FILE
from portage.localization import _
except ImportError as e:
sys.stderr.write("\n\n")
sys.stderr.write("!!! Failed to complete portage imports. There are internal modules for\n")
sys.stderr.write("!!! portage and failure here indicates that you have a problem with your\n")
sys.stderr.write("!!! installation of portage. Please try a rescue portage located in the\n")
sys.stderr.write("!!! portage tree under '/usr/portage/sys-apps/portage/files/' (default).\n")
sys.stderr.write("!!! There is a README.RESCUE file that details the steps required to perform\n")
sys.stderr.write("!!! a recovery of portage.\n")
sys.stderr.write(" "+str(e)+"\n\n")
raise
if sys.hexversion >= 0x3000000:
basestring = str
long = int
# Assume utf_8 fs encoding everywhere except in merge code, where the
# user's locale is respected.
_encodings = {
'content' : 'utf_8',
'fs' : 'utf_8',
'merge' : sys.getfilesystemencoding(),
'repo.content' : 'utf_8',
'stdio' : 'utf_8',
}
# This can happen if python is built with USE=build (stage 1).
if _encodings['merge'] is None:
_encodings['merge'] = 'ascii'
if sys.hexversion >= 0x3000000:
def _unicode_encode(s, encoding=_encodings['content'], errors='backslashreplace'):
if isinstance(s, str):
s = s.encode(encoding, errors)
return s
def _unicode_decode(s, encoding=_encodings['content'], errors='replace'):
if isinstance(s, bytes):
s = str(s, encoding=encoding, errors=errors)
return s
else:
def _unicode_encode(s, encoding=_encodings['content'], errors='backslashreplace'):
if isinstance(s, unicode):
s = s.encode(encoding, errors)
return s
def _unicode_decode(s, encoding=_encodings['content'], errors='replace'):
if isinstance(s, bytes):
s = unicode(s, encoding=encoding, errors=errors)
return s
class _unicode_func_wrapper(object):
"""
Wraps a function, converts arguments from unicode to bytes,
and return values to unicode from bytes. Function calls
will raise UnicodeEncodeError if an argument fails to be
encoded with the required encoding. Return values that
are single strings are decoded with errors='replace'. Return
values that are lists of strings are decoded with errors='strict'
and elements that fail to be decoded are omitted from the returned
list.
"""
__slots__ = ('_func', '_encoding')
def __init__(self, func, encoding=_encodings['fs']):
self._func = func
self._encoding = encoding
def __call__(self, *args, **kwargs):
encoding = self._encoding
wrapped_args = [_unicode_encode(x, encoding=encoding, errors='strict')
for x in args]
if kwargs:
wrapped_kwargs = dict(
(k, _unicode_encode(v, encoding=encoding, errors='strict'))
for k, v in kwargs.items())
else:
wrapped_kwargs = {}
rval = self._func(*wrapped_args, **wrapped_kwargs)
if isinstance(rval, (list, tuple)):
decoded_rval = []
for x in rval:
try:
x = _unicode_decode(x, encoding=encoding, errors='strict')
except UnicodeDecodeError:
pass
else:
decoded_rval.append(x)
if isinstance(rval, tuple):
rval = tuple(decoded_rval)
else:
rval = decoded_rval
else:
rval = _unicode_decode(rval, encoding=encoding, errors='replace')
return rval
class _unicode_module_wrapper(object):
"""
Wraps a module and wraps all functions with _unicode_func_wrapper.
"""
__slots__ = ('_mod', '_encoding', '_overrides', '_cache')
def __init__(self, mod, encoding=_encodings['fs'], overrides=None, cache=True):
object.__setattr__(self, '_mod', mod)
object.__setattr__(self, '_encoding', encoding)
object.__setattr__(self, '_overrides', overrides)
if cache:
cache = {}
else:
cache = None
object.__setattr__(self, '_cache', cache)
def __getattribute__(self, attr):
cache = object.__getattribute__(self, '_cache')
if cache is not None:
result = cache.get(attr)
if result is not None:
return result
result = getattr(object.__getattribute__(self, '_mod'), attr)
encoding = object.__getattribute__(self, '_encoding')
overrides = object.__getattribute__(self, '_overrides')
override = None
if overrides is not None:
override = overrides.get(id(result))
if override is not None:
result = override
elif isinstance(result, type):
pass
elif type(result) is types.ModuleType:
result = _unicode_module_wrapper(result,
encoding=encoding, overrides=overrides)
elif hasattr(result, '__call__'):
result = _unicode_func_wrapper(result, encoding=encoding)
if cache is not None:
cache[attr] = result
return result
import os as _os
_os_overrides = {
id(_os.fdopen) : _os.fdopen,
id(_os.popen) : _os.popen,
id(_os.read) : _os.read,
id(_os.system) : _os.system,
}
if hasattr(_os, 'statvfs'):
_os_overrides[id(_os.statvfs)] = _os.statvfs
os = _unicode_module_wrapper(_os, overrides=_os_overrides,
encoding=_encodings['fs'])
_os_merge = _unicode_module_wrapper(_os,
encoding=_encodings['merge'], overrides=_os_overrides)
import shutil as _shutil
shutil = _unicode_module_wrapper(_shutil, encoding=_encodings['fs'])
# Imports below this point rely on the above unicode wrapper definitions.
_selinux = None
selinux = None
_selinux_merge = None
try:
import portage._selinux
selinux = _unicode_module_wrapper(_selinux,
encoding=_encodings['fs'])
_selinux_merge = _unicode_module_wrapper(_selinux,
encoding=_encodings['merge'])
except OSError as e:
sys.stderr.write("!!! SELinux not loaded: %s\n" % str(e))
del e
except ImportError:
pass
from portage.manifest import Manifest
# ===========================================================================
# END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END
# ===========================================================================
def _gen_missing_encodings(missing_encodings):
encodings = {}
if 'ascii' in missing_encodings:
class AsciiIncrementalEncoder(codecs.IncrementalEncoder):
def encode(self, input, final=False):
return codecs.ascii_encode(input, self.errors)[0]
class AsciiIncrementalDecoder(codecs.IncrementalDecoder):
def decode(self, input, final=False):
return codecs.ascii_decode(input, self.errors)[0]
class AsciiStreamWriter(codecs.StreamWriter):
encode = codecs.ascii_encode
class AsciiStreamReader(codecs.StreamReader):
decode = codecs.ascii_decode
codec_info = codecs.CodecInfo(
name='ascii',
encode=codecs.ascii_encode,
decode=codecs.ascii_decode,
incrementalencoder=AsciiIncrementalEncoder,
incrementaldecoder=AsciiIncrementalDecoder,
streamwriter=AsciiStreamWriter,
streamreader=AsciiStreamReader,
)
for alias in ('ascii', '646', 'ansi_x3.4_1968', 'ansi_x3_4_1968',
'ansi_x3.4_1986', 'cp367', 'csascii', 'ibm367', 'iso646_us',
'iso_646.irv_1991', 'iso_ir_6', 'us', 'us_ascii'):
encodings[alias] = codec_info
if 'utf_8' in missing_encodings:
def utf8decode(input, errors='strict'):
return codecs.utf_8_decode(input, errors, True)
class Utf8IncrementalEncoder(codecs.IncrementalEncoder):
def encode(self, input, final=False):
return codecs.utf_8_encode(input, self.errors)[0]
class Utf8IncrementalDecoder(codecs.BufferedIncrementalDecoder):
_buffer_decode = codecs.utf_8_decode
class Utf8StreamWriter(codecs.StreamWriter):
encode = codecs.utf_8_encode
class Utf8StreamReader(codecs.StreamReader):
decode = codecs.utf_8_decode
codec_info = codecs.CodecInfo(
name='utf-8',
encode=codecs.utf_8_encode,
decode=utf8decode,
incrementalencoder=Utf8IncrementalEncoder,
incrementaldecoder=Utf8IncrementalDecoder,
streamreader=Utf8StreamReader,
streamwriter=Utf8StreamWriter,
)
for alias in ('utf_8', 'u8', 'utf', 'utf8', 'utf8_ucs2', 'utf8_ucs4'):
encodings[alias] = codec_info
return encodings
def _ensure_default_encoding():
"""
The python that's inside stage 1 or 2 is built with a minimal
configuration which does not include the /usr/lib/pythonX.Y/encodings
directory. This results in error like the following:
LookupError: no codec search functions registered: can't find encoding
In order to solve this problem, detect it early and manually register
a search function for the ascii and utf_8 codecs. Starting with python-3.0
this problem is more noticeable because of stricter handling of encoding
and decoding between strings of characters and bytes.
"""
default_fallback = 'utf_8'
default_encoding = sys.getdefaultencoding().lower().replace('-', '_')
filesystem_encoding = _encodings['merge'].lower().replace('-', '_')
required_encodings = set(['ascii', 'utf_8'])
required_encodings.add(default_encoding)
required_encodings.add(filesystem_encoding)
missing_encodings = set()
for codec_name in required_encodings:
try:
codecs.lookup(codec_name)
except LookupError:
missing_encodings.add(codec_name)
if not missing_encodings:
return
encodings = _gen_missing_encodings(missing_encodings)
if default_encoding in missing_encodings and \
default_encoding not in encodings:
# Make the fallback codec correspond to whatever name happens
# to be returned by sys.getfilesystemencoding().
try:
encodings[default_encoding] = codecs.lookup(default_fallback)
except LookupError:
encodings[default_encoding] = encodings[default_fallback]
if filesystem_encoding in missing_encodings and \
filesystem_encoding not in encodings:
# Make the fallback codec correspond to whatever name happens
# to be returned by sys.getdefaultencoding().
try:
encodings[filesystem_encoding] = codecs.lookup(default_fallback)
except LookupError:
encodings[filesystem_encoding] = encodings[default_fallback]
def search_function(name):
name = name.lower()
name = name.replace('-', '_')
codec_info = encodings.get(name)
if codec_info is not None:
return codecs.CodecInfo(
name=codec_info.name,
encode=codec_info.encode,
decode=codec_info.decode,
incrementalencoder=codec_info.incrementalencoder,
incrementaldecoder=codec_info.incrementaldecoder,
streamreader=codec_info.streamreader,
streamwriter=codec_info.streamwriter,
)
return None
codecs.register(search_function)
del codec_name, default_encoding, default_fallback, \
filesystem_encoding, missing_encodings, \
required_encodings, search_function
# Do this ASAP since writemsg() might not work without it.
_ensure_default_encoding()
def _shell_quote(s):
"""
Quote a string in double-quotes and use backslashes to
escape any backslashes, double-quotes, dollar signs, or
backquotes in the string.
"""
for letter in "\\\"$`":
if letter in s:
s = s.replace(letter, "\\" + letter)
return "\"%s\"" % s
bsd_chflags = None
if platform.system() in ('FreeBSD',):
class bsd_chflags(object):
@classmethod
def chflags(cls, path, flags, opts=""):
cmd = 'chflags %s %o %s' % (opts, flags, _shell_quote(path))
status, output = subprocess_getstatusoutput(cmd)
if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
return
# Try to generate an ENOENT error if appropriate.
if 'h' in opts:
_os_merge.lstat(path)
else:
_os_merge.stat(path)
# Make sure the binary exists.
if not portage.process.find_binary('chflags'):
raise portage.exception.CommandNotFound('chflags')
# Now we're not sure exactly why it failed or what
# the real errno was, so just report EPERM.
e = OSError(errno.EPERM, output)
e.errno = errno.EPERM
e.filename = path
e.message = output
raise e
@classmethod
def lchflags(cls, path, flags):
return cls.chflags(path, flags, opts='-h')
def load_mod(name):
modname = ".".join(name.split(".")[:-1])
mod = __import__(modname)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def best_from_dict(key, top_dict, key_order, EmptyOnError=1, FullCopy=1, AllowEmpty=1):
for x in key_order:
if x in top_dict and key in top_dict[x]:
if FullCopy:
return copy.deepcopy(top_dict[x][key])
else:
return top_dict[x][key]
if EmptyOnError:
return ""
else:
raise KeyError("Key not found in list; '%s'" % key)
def getcwd():
"this fixes situations where the current directory doesn't exist"
try:
return os.getcwd()
except OSError: #dir doesn't exist
os.chdir("/")
return "/"
getcwd()
def abssymlink(symlink):
"This reads symlinks, resolving the relative symlinks, and returning the absolute."
mylink=os.readlink(symlink)
if mylink[0] != '/':
mydir=os.path.dirname(symlink)
mylink=mydir+"/"+mylink
return os.path.normpath(mylink)
dircache = {}
cacheHit=0
cacheMiss=0
cacheStale=0
def cacheddir(my_original_path, ignorecvs, ignorelist, EmptyOnError, followSymlinks=True):
global cacheHit,cacheMiss,cacheStale
mypath = normalize_path(my_original_path)
if mypath in dircache:
cacheHit += 1
cached_mtime, list, ftype = dircache[mypath]
else:
cacheMiss += 1
cached_mtime, list, ftype = -1, [], []
try:
pathstat = os.stat(mypath)
if stat.S_ISDIR(pathstat[stat.ST_MODE]):
mtime = pathstat.st_mtime
else:
raise portage.exception.DirectoryNotFound(mypath)
except EnvironmentError as e:
if e.errno == portage.exception.PermissionDenied.errno:
raise portage.exception.PermissionDenied(mypath)
del e
return [], []
except portage.exception.PortageException:
return [], []
# Python retuns mtime in seconds, so if it was changed in the last few seconds, it could be invalid
if mtime != cached_mtime or time.time() - mtime < 4:
if mypath in dircache:
cacheStale += 1
try:
list = os.listdir(mypath)
except EnvironmentError as e:
if e.errno != errno.EACCES:
raise
del e
raise portage.exception.PermissionDenied(mypath)
ftype = []
for x in list:
try:
if followSymlinks:
pathstat = os.stat(mypath+"/"+x)
else:
pathstat = os.lstat(mypath+"/"+x)
if stat.S_ISREG(pathstat[stat.ST_MODE]):
ftype.append(0)
elif stat.S_ISDIR(pathstat[stat.ST_MODE]):
ftype.append(1)
elif stat.S_ISLNK(pathstat[stat.ST_MODE]):
ftype.append(2)
else:
ftype.append(3)
except (IOError, OSError):
ftype.append(3)
dircache[mypath] = mtime, list, ftype
ret_list = []
ret_ftype = []
for x in range(0, len(list)):
if list[x] in ignorelist:
pass
elif ignorecvs:
if list[x][:2] != ".#":
ret_list.append(list[x])
ret_ftype.append(ftype[x])
else:
ret_list.append(list[x])
ret_ftype.append(ftype[x])
writemsg("cacheddirStats: H:%d/M:%d/S:%d\n" % (cacheHit, cacheMiss, cacheStale),10)
return ret_list, ret_ftype
_ignorecvs_dirs = ('CVS', 'SCCS', '.svn', '.git')
def listdir(mypath, recursive=False, filesonly=False, ignorecvs=False, ignorelist=[], followSymlinks=True,
EmptyOnError=False, dirsonly=False):
"""
Portage-specific implementation of os.listdir
@param mypath: Path whose contents you wish to list
@type mypath: String
@param recursive: Recursively scan directories contained within mypath
@type recursive: Boolean
@param filesonly; Only return files, not more directories
@type filesonly: Boolean
@param ignorecvs: Ignore CVS directories ('CVS','SCCS','.svn','.git')
@type ignorecvs: Boolean
@param ignorelist: List of filenames/directories to exclude
@type ignorelist: List
@param followSymlinks: Follow Symlink'd files and directories
@type followSymlinks: Boolean
@param EmptyOnError: Return [] if an error occurs (deprecated, always True)
@type EmptyOnError: Boolean
@param dirsonly: Only return directories.
@type dirsonly: Boolean
@rtype: List
@returns: A list of files and directories (or just files or just directories) or an empty list.
"""
list, ftype = cacheddir(mypath, ignorecvs, ignorelist, EmptyOnError, followSymlinks)
if list is None:
list=[]
if ftype is None:
ftype=[]
if not (filesonly or dirsonly or recursive):
return list
if recursive:
x=0
while x<len(ftype):
if ftype[x] == 1 and not \
(ignorecvs and os.path.basename(list[x]) in _ignorecvs_dirs):
l,f = cacheddir(mypath+"/"+list[x], ignorecvs, ignorelist, EmptyOnError,
followSymlinks)
l=l[:]
for y in range(0,len(l)):
l[y]=list[x]+"/"+l[y]
list=list+l
ftype=ftype+f
x+=1
if filesonly:
rlist=[]
for x in range(0,len(ftype)):
if ftype[x]==0:
rlist=rlist+[list[x]]
elif dirsonly:
rlist = []
for x in range(0, len(ftype)):
if ftype[x] == 1:
rlist = rlist + [list[x]]
else:
rlist=list
return rlist
def flatten(mytokens):
"""this function now turns a [1,[2,3]] list into
a [1,2,3] list and returns it."""
newlist=[]
for x in mytokens:
if isinstance(x, list):
newlist.extend(flatten(x))
else:
newlist.append(x)
return newlist
#beautiful directed graph object
class digraph(object):
def __init__(self):
"""Create an empty digraph"""
# { node : ( { child : priority } , { parent : priority } ) }
self.nodes = {}
self.order = []
def add(self, node, parent, priority=0):
"""Adds the specified node with the specified parent.
If the dep is a soft-dep and the node already has a hard
relationship to the parent, the relationship is left as hard."""
if node not in self.nodes:
self.nodes[node] = ({}, {}, node)
self.order.append(node)
if not parent:
return
if parent not in self.nodes:
self.nodes[parent] = ({}, {}, parent)
self.order.append(parent)
priorities = self.nodes[node][1].get(parent)
if priorities is None:
priorities = []
self.nodes[node][1][parent] = priorities
self.nodes[parent][0][node] = priorities
priorities.append(priority)
priorities.sort()
def remove(self, node):
"""Removes the specified node from the digraph, also removing
and ties to other nodes in the digraph. Raises KeyError if the
node doesn't exist."""
if node not in self.nodes:
raise KeyError(node)
for parent in self.nodes[node][1]:
del self.nodes[parent][0][node]
for child in self.nodes[node][0]:
del self.nodes[child][1][node]
del self.nodes[node]
self.order.remove(node)
def difference_update(self, t):
"""
Remove all given nodes from node_set. This is more efficient
than multiple calls to the remove() method.
"""
if isinstance(t, (list, tuple)) or \
not hasattr(t, "__contains__"):
t = frozenset(t)
order = []
for node in self.order:
if node not in t:
order.append(node)
continue
for parent in self.nodes[node][1]:
del self.nodes[parent][0][node]
for child in self.nodes[node][0]:
del self.nodes[child][1][node]
del self.nodes[node]
self.order = order
def remove_edge(self, child, parent):
"""
Remove edge in the direction from child to parent. Note that it is
possible for a remaining edge to exist in the opposite direction.
Any endpoint vertices that become isolated will remain in the graph.
"""
# Nothing should be modified when a KeyError is raised.
for k in parent, child:
if k not in self.nodes:
raise KeyError(k)
# Make sure the edge exists.
if child not in self.nodes[parent][0]:
raise KeyError(child)
if parent not in self.nodes[child][1]:
raise KeyError(parent)
# Remove the edge.
del self.nodes[child][1][parent]
del self.nodes[parent][0][child]
def __iter__(self):
return iter(self.order)
def contains(self, node):
"""Checks if the digraph contains mynode"""
return node in self.nodes
def get(self, key, default=None):
node_data = self.nodes.get(key, self)
if node_data is self:
return default
return node_data[2]
def all_nodes(self):
"""Return a list of all nodes in the graph"""
return self.order[:]
def child_nodes(self, node, ignore_priority=None):
"""Return all children of the specified node"""
if ignore_priority is None:
return list(self.nodes[node][0])
children = []
if hasattr(ignore_priority, '__call__'):
for child, priorities in self.nodes[node][0].items():
for priority in priorities:
if not ignore_priority(priority):
children.append(child)
break
else:
for child, priorities in self.nodes[node][0].items():
if ignore_priority < priorities[-1]:
children.append(child)
return children
def parent_nodes(self, node, ignore_priority=None):
"""Return all parents of the specified node"""
if ignore_priority is None:
return list(self.nodes[node][1])
parents = []
if hasattr(ignore_priority, '__call__'):
for parent, priorities in self.nodes[node][1].items():
for priority in priorities:
if not ignore_priority(priority):
parents.append(parent)
break
else:
for parent, priorities in self.nodes[node][1].items():
if ignore_priority < priorities[-1]:
parents.append(parent)
return parents
def leaf_nodes(self, ignore_priority=None):
"""Return all nodes that have no children
If ignore_soft_deps is True, soft deps are not counted as
children in calculations."""
leaf_nodes = []
if ignore_priority is None:
for node in self.order:
if not self.nodes[node][0]:
leaf_nodes.append(node)
elif hasattr(ignore_priority, '__call__'):
for node in self.order:
is_leaf_node = True
for child, priorities in self.nodes[node][0].items():
for priority in priorities:
if not ignore_priority(priority):
is_leaf_node = False
break
if not is_leaf_node:
break
if is_leaf_node:
leaf_nodes.append(node)
else:
for node in self.order:
is_leaf_node = True
for child, priorities in self.nodes[node][0].items():
if ignore_priority < priorities[-1]:
is_leaf_node = False
break
if is_leaf_node:
leaf_nodes.append(node)
return leaf_nodes
def root_nodes(self, ignore_priority=None):
"""Return all nodes that have no parents.
If ignore_soft_deps is True, soft deps are not counted as
parents in calculations."""
root_nodes = []
if ignore_priority is None:
for node in self.order:
if not self.nodes[node][1]:
root_nodes.append(node)
elif hasattr(ignore_priority, '__call__'):
for node in self.order:
is_root_node = True
for parent, priorities in self.nodes[node][1].items():
for priority in priorities:
if not ignore_priority(priority):
is_root_node = False
break
if not is_root_node:
break
if is_root_node:
root_nodes.append(node)
else:
for node in self.order:
is_root_node = True
for parent, priorities in self.nodes[node][1].items():
if ignore_priority < priorities[-1]:
is_root_node = False
break
if is_root_node:
root_nodes.append(node)
return root_nodes
def is_empty(self):
"""Checks if the digraph is empty"""
return len(self.nodes) == 0
def clone(self):
clone = digraph()
clone.nodes = {}
memo = {}
for children, parents, node in self.nodes.values():
children_clone = {}
for child, priorities in children.items():
priorities_clone = memo.get(id(priorities))
if priorities_clone is None:
priorities_clone = priorities[:]
memo[id(priorities)] = priorities_clone
children_clone[child] = priorities_clone
parents_clone = {}
for parent, priorities in parents.items():
priorities_clone = memo.get(id(priorities))
if priorities_clone is None:
priorities_clone = priorities[:]
memo[id(priorities)] = priorities_clone
parents_clone[parent] = priorities_clone
clone.nodes[node] = (children_clone, parents_clone, node)
clone.order = self.order[:]
return clone
# Backward compatibility
addnode = add
allnodes = all_nodes
allzeros = leaf_nodes
hasnode = contains
__contains__ = contains
empty = is_empty
copy = clone
def delnode(self, node):
try:
self.remove(node)
except KeyError:
pass
def firstzero(self):
leaf_nodes = self.leaf_nodes()
if leaf_nodes:
return leaf_nodes[0]
return None
def hasallzeros(self, ignore_priority=None):
return len(self.leaf_nodes(ignore_priority=ignore_priority)) == \
len(self.order)
def debug_print(self):
def output(s):
writemsg(s, noiselevel=-1)
for node in self.nodes:
output("%s " % (node,))
if self.nodes[node][0]:
output("depends on\n")
else:
output("(no children)\n")
for child, priorities in self.nodes[node][0].items():
output(" %s (%s)\n" % (child, priorities[-1],))
#parse /etc/env.d and generate /etc/profile.env
def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None,
env=None, writemsg_level=None):
if writemsg_level is None:
writemsg_level = portage.util.writemsg_level
if target_root is None:
global settings
target_root = settings["ROOT"]
if prev_mtimes is None:
global mtimedb
prev_mtimes = mtimedb["ldpath"]
if env is None:
env = os.environ
envd_dir = os.path.join(target_root, "etc", "env.d")
portage.util.ensure_dirs(envd_dir, mode=0o755)
fns = listdir(envd_dir, EmptyOnError=1)
fns.sort()
templist = []
for x in fns:
if len(x) < 3:
continue
if not x[0].isdigit() or not x[1].isdigit():
continue
if x.startswith(".") or x.endswith("~") or x.endswith(".bak"):
continue
templist.append(x)
fns = templist
del templist
space_separated = set(["CONFIG_PROTECT", "CONFIG_PROTECT_MASK"])
colon_separated = set(["ADA_INCLUDE_PATH", "ADA_OBJECTS_PATH",
"CLASSPATH", "INFODIR", "INFOPATH", "KDEDIRS", "LDPATH", "MANPATH",
"PATH", "PKG_CONFIG_PATH", "PRELINK_PATH", "PRELINK_PATH_MASK",
"PYTHONPATH", "ROOTPATH"])
config_list = []
for x in fns:
file_path = os.path.join(envd_dir, x)
try:
myconfig = getconfig(file_path, expand=False)
except portage.exception.ParseError as e:
writemsg("!!! '%s'\n" % str(e), noiselevel=-1)
del e
continue
if myconfig is None:
# broken symlink or file removed by a concurrent process
writemsg("!!! File Not Found: '%s'\n" % file_path, noiselevel=-1)
continue
config_list.append(myconfig)
if "SPACE_SEPARATED" in myconfig:
space_separated.update(myconfig["SPACE_SEPARATED"].split())
del myconfig["SPACE_SEPARATED"]
if "COLON_SEPARATED" in myconfig:
colon_separated.update(myconfig["COLON_SEPARATED"].split())
del myconfig["COLON_SEPARATED"]
env = {}
specials = {}
for var in space_separated:
mylist = []
for myconfig in config_list:
if var in myconfig:
for item in myconfig[var].split():
if item and not item in mylist:
mylist.append(item)
del myconfig[var] # prepare for env.update(myconfig)
if mylist:
env[var] = " ".join(mylist)
specials[var] = mylist
for var in colon_separated:
mylist = []
for myconfig in config_list:
if var in myconfig:
for item in myconfig[var].split(":"):
if item and not item in mylist:
mylist.append(item)
del myconfig[var] # prepare for env.update(myconfig)
if mylist:
env[var] = ":".join(mylist)
specials[var] = mylist
for myconfig in config_list:
"""Cumulative variables have already been deleted from myconfig so that
they won't be overwritten by this dict.update call."""
env.update(myconfig)
ldsoconf_path = os.path.join(target_root, "etc", "ld.so.conf")
try:
myld = codecs.open(_unicode_encode(ldsoconf_path,
encoding=_encodings['fs'], errors='strict'),
mode='r', encoding=_encodings['content'], errors='replace')
myldlines=myld.readlines()
myld.close()
oldld=[]
for x in myldlines:
#each line has at least one char (a newline)
if x[0]=="#":
continue
oldld.append(x[:-1])
except (IOError, OSError) as e:
if e.errno != errno.ENOENT:
raise
oldld = None
ld_cache_update=False
newld = specials["LDPATH"]
if (oldld!=newld):
#ld.so.conf needs updating and ldconfig needs to be run
myfd = atomic_ofstream(ldsoconf_path)
myfd.write("# ld.so.conf autogenerated by env-update; make all changes to\n")
myfd.write("# contents of /etc/env.d directory\n")
for x in specials["LDPATH"]:
myfd.write(x+"\n")
myfd.close()
ld_cache_update=True
# Update prelink.conf if we are prelink-enabled
if prelink_capable:
newprelink = atomic_ofstream(
os.path.join(target_root, "etc", "prelink.conf"))
newprelink.write("# prelink.conf autogenerated by env-update; make all changes to\n")
newprelink.write("# contents of /etc/env.d directory\n")
for x in ["/bin","/sbin","/usr/bin","/usr/sbin","/lib","/usr/lib"]:
newprelink.write("-l "+x+"\n");
for x in specials["LDPATH"]+specials["PATH"]+specials["PRELINK_PATH"]:
if not x:
continue
if x[-1]!='/':
x=x+"/"
plmasked=0
for y in specials["PRELINK_PATH_MASK"]:
if not y:
continue
if y[-1]!='/':
y=y+"/"
if y==x[0:len(y)]:
plmasked=1
break
if not plmasked:
newprelink.write("-h "+x+"\n")
for x in specials["PRELINK_PATH_MASK"]:
newprelink.write("-b "+x+"\n")
newprelink.close()
# Portage stores mtimes with 1 second granularity but in >=python-2.5 finer
# granularity is possible. In order to avoid the potential ambiguity of
# mtimes that differ by less than 1 second, sleep here if any of the
# directories have been modified during the current second.
sleep_for_mtime_granularity = False
current_time = long(time.time())
mtime_changed = False
lib_dirs = set()
for lib_dir in portage.util.unique_array(specials["LDPATH"]+['usr/lib','usr/lib64','usr/lib32','lib','lib64','lib32']):
x = os.path.join(target_root, lib_dir.lstrip(os.sep))
try:
newldpathtime = long(os.stat(x).st_mtime)
lib_dirs.add(normalize_path(x))
except OSError as oe:
if oe.errno == errno.ENOENT:
try:
del prev_mtimes[x]
except KeyError:
pass
# ignore this path because it doesn't exist
continue
raise
if newldpathtime == current_time:
sleep_for_mtime_granularity = True
if x in prev_mtimes:
if prev_mtimes[x] == newldpathtime:
pass
else:
prev_mtimes[x] = newldpathtime
mtime_changed = True
else:
prev_mtimes[x] = newldpathtime
mtime_changed = True
if mtime_changed:
ld_cache_update = True
if makelinks and \
not ld_cache_update and \
contents is not None:
libdir_contents_changed = False
for mypath, mydata in contents.items():
if mydata[0] not in ("obj","sym"):
continue
head, tail = os.path.split(mypath)
if head in lib_dirs:
libdir_contents_changed = True
break
if not libdir_contents_changed:
makelinks = False
ldconfig = "/sbin/ldconfig"
if "CHOST" in env and "CBUILD" in env and \
env["CHOST"] != env["CBUILD"]:
from portage.process import find_binary
ldconfig = find_binary("%s-ldconfig" % env["CHOST"])
# Only run ldconfig as needed
if (ld_cache_update or makelinks) and ldconfig:
# ldconfig has very different behaviour between FreeBSD and Linux
if ostype=="Linux" or ostype.lower().endswith("gnu"):
# We can't update links if we haven't cleaned other versions first, as
# an older package installed ON TOP of a newer version will cause ldconfig
# to overwrite the symlinks we just made. -X means no links. After 'clean'
# we can safely create links.
writemsg_level(_(">>> Regenerating %setc/ld.so.cache...\n") % \
(target_root,))
if makelinks:
os.system("cd / ; %s -r '%s'" % (ldconfig, target_root))
else:
os.system("cd / ; %s -X -r '%s'" % (ldconfig, target_root))
elif ostype in ("FreeBSD","DragonFly"):
writemsg_level(_(">>> Regenerating %svar/run/ld-elf.so.hints...\n") % \
target_root)
os.system(("cd / ; %s -elf -i " + \
"-f '%svar/run/ld-elf.so.hints' '%setc/ld.so.conf'") % \
(ldconfig, target_root, target_root))
del specials["LDPATH"]
penvnotice = "# THIS FILE IS AUTOMATICALLY GENERATED BY env-update.\n"
penvnotice += "# DO NOT EDIT THIS FILE. CHANGES TO STARTUP PROFILES\n"
cenvnotice = penvnotice[:]
penvnotice += "# GO INTO /etc/profile NOT /etc/profile.env\n\n"
cenvnotice += "# GO INTO /etc/csh.cshrc NOT /etc/csh.env\n\n"
#create /etc/profile.env for bash support
outfile = atomic_ofstream(os.path.join(target_root, "etc", "profile.env"))
outfile.write(penvnotice)
env_keys = [ x for x in env if x != "LDPATH" ]
env_keys.sort()
for k in env_keys:
v = env[k]
if v.startswith('$') and not v.startswith('${'):
outfile.write("export %s=$'%s'\n" % (k, v[1:]))
else:
outfile.write("export %s='%s'\n" % (k, v))
outfile.close()
#create /etc/csh.env for (t)csh support
outfile = atomic_ofstream(os.path.join(target_root, "etc", "csh.env"))
outfile.write(cenvnotice)
for x in env_keys:
outfile.write("setenv %s '%s'\n" % (x, env[x]))
outfile.close()
if sleep_for_mtime_granularity:
while current_time == long(time.time()):
sleep(1)
def ExtractKernelVersion(base_dir):
"""
Try to figure out what kernel version we are running
@param base_dir: Path to sources (usually /usr/src/linux)
@type base_dir: string
@rtype: tuple( version[string], error[string])
@returns:
1. tuple( version[string], error[string])
Either version or error is populated (but never both)
"""
lines = []
pathname = os.path.join(base_dir, 'Makefile')
try:
f = codecs.open(_unicode_encode(pathname,
encoding=_encodings['fs'], errors='strict'), mode='r',
encoding=_encodings['content'], errors='replace')
except OSError as details:
return (None, str(details))
except IOError as details:
return (None, str(details))
try:
for i in range(4):
lines.append(f.readline())
except OSError as details:
return (None, str(details))
except IOError as details:
return (None, str(details))
lines = [l.strip() for l in lines]
version = ''
#XXX: The following code relies on the ordering of vars within the Makefile
for line in lines:
# split on the '=' then remove annoying whitespace
items = line.split("=")
items = [i.strip() for i in items]
if items[0] == 'VERSION' or \
items[0] == 'PATCHLEVEL':
version += items[1]
version += "."
elif items[0] == 'SUBLEVEL':
version += items[1]
elif items[0] == 'EXTRAVERSION' and \
items[-1] != items[0]:
version += items[1]
# Grab a list of files named localversion* and sort them
localversions = os.listdir(base_dir)
for x in range(len(localversions)-1,-1,-1):
if localversions[x][:12] != "localversion":
del localversions[x]
localversions.sort()
# Append the contents of each to the version string, stripping ALL whitespace
for lv in localversions:
version += "".join( " ".join( grabfile( base_dir+ "/" + lv ) ).split() )
# Check the .config for a CONFIG_LOCALVERSION and append that too, also stripping whitespace
kernelconfig = getconfig(base_dir+"/.config")
if kernelconfig and "CONFIG_LOCALVERSION" in kernelconfig:
version += "".join(kernelconfig["CONFIG_LOCALVERSION"].split())
return (version,None)
def autouse(myvartree, use_cache=1, mysettings=None):
"""
autuse returns a list of USE variables auto-enabled to packages being installed
@param myvartree: Instance of the vartree class (from /var/db/pkg...)
@type myvartree: vartree
@param use_cache: read values from cache
@type use_cache: Boolean
@param mysettings: Instance of config
@type mysettings: config
@rtype: string
@returns: A string containing a list of USE variables that are enabled via use.defaults
"""
if mysettings is None:
global settings
mysettings = settings
if mysettings.profile_path is None:
return ""
myusevars=""
usedefaults = mysettings.use_defs
for myuse in usedefaults:
dep_met = True
for mydep in usedefaults[myuse]:
if not myvartree.dep_match(mydep,use_cache=True):
dep_met = False
break
if dep_met:
myusevars += " "+myuse
return myusevars
def check_config_instance(test):
if not isinstance(test, config):
raise TypeError("Invalid type for config object: %s (should be %s)" % (test.__class__, config))
def _lazy_iuse_regex(iuse_implicit):
"""
The PORTAGE_IUSE value is lazily evaluated since re.escape() is slow
and the value is only used when an ebuild phase needs to be executed
(it's used only to generate QA notices).
"""
# Escape anything except ".*" which is supposed to pass through from
# _get_implicit_iuse().
regex = sorted(re.escape(x) for x in iuse_implicit)
regex = "^(%s)$" % "|".join(regex)
regex = regex.replace("\\.\\*", ".*")
return regex
class _local_repo_config(object):
__slots__ = ('aliases', 'eclass_overrides', 'masters', 'name',)
def __init__(self, name, repo_opts):
self.name = name
aliases = repo_opts.get('aliases')
if aliases is not None:
aliases = tuple(aliases.split())
self.aliases = aliases
eclass_overrides = repo_opts.get('eclass-overrides')
if eclass_overrides is not None:
eclass_overrides = tuple(eclass_overrides.split())
self.eclass_overrides = eclass_overrides
masters = repo_opts.get('masters')
if masters is not None:
masters = tuple(masters.split())
self.masters = masters
class config(object):
"""
This class encompasses the main portage configuration. Data is pulled from
ROOT/PORTDIR/profiles/, from ROOT/etc/make.profile incrementally through all
parent profiles as well as from ROOT/PORTAGE_CONFIGROOT/* for user specified
overrides.
Generally if you need data like USE flags, FEATURES, environment variables,
virtuals ...etc you look in here.
"""
# Don't include anything that could be extremely long here (like SRC_URI)
# since that could cause execve() calls to fail with E2BIG errors. For
# example, see bug #262647.
_setcpv_aux_keys = ('SLOT', 'RESTRICT', 'LICENSE',
'KEYWORDS', 'INHERITED', 'IUSE', 'PROVIDE', 'EAPI',
'PROPERTIES', 'DEFINED_PHASES', 'repository')
_env_blacklist = [
"A", "AA", "CATEGORY", "DEPEND", "DESCRIPTION", "EAPI",
"EBUILD_PHASE", "ED", "EMERGE_FROM", "EPREFIX", "EROOT",
"HOMEPAGE", "INHERITED", "IUSE",
"KEYWORDS", "LICENSE", "PDEPEND", "PF", "PKGUSE",
"PORTAGE_CONFIGROOT", "PORTAGE_IUSE",
"PORTAGE_NONFATAL", "PORTAGE_REPO_NAME",
"PORTAGE_USE", "PROPERTIES", "PROVIDE", "RDEPEND", "RESTRICT",
"ROOT", "SLOT", "SRC_URI"
]
_environ_whitelist = []
# Whitelisted variables are always allowed to enter the ebuild
# environment. Generally, this only includes special portage
# variables. Ebuilds can unset variables that are not whitelisted
# and rely on them remaining unset for future phases, without them
# leaking back in from various locations (bug #189417). It's very
# important to set our special BASH_ENV variable in the ebuild
# environment in order to prevent sandbox from sourcing /etc/profile
# in it's bashrc (causing major leakage).
_environ_whitelist += [
"ACCEPT_LICENSE", "BASH_ENV", "BUILD_PREFIX", "D",
"DISTDIR", "DOC_SYMLINKS_DIR", "EBUILD",
"EBUILD_EXIT_STATUS_FILE", "EBUILD_FORCE_TEST",
"EBUILD_PHASE", "ECLASSDIR", "ECLASS_DEPTH", "EMERGE_FROM",
"FEATURES", "FILESDIR", "HOME", "NOCOLOR", "PATH",
"PKGDIR",
"PKGUSE", "PKG_LOGDIR", "PKG_TMPDIR",
"PORTAGE_ACTUAL_DISTDIR", "PORTAGE_ARCHLIST",
"PORTAGE_BASHRC",
"PORTAGE_BINPKG_FILE", "PORTAGE_BINPKG_TAR_OPTS",
"PORTAGE_BINPKG_TMPFILE",
"PORTAGE_BIN_PATH",
"PORTAGE_BUILDDIR", "PORTAGE_COLORMAP",
"PORTAGE_CONFIGROOT", "PORTAGE_DEBUG", "PORTAGE_DEPCACHEDIR",
"PORTAGE_GID", "PORTAGE_INST_GID", "PORTAGE_INST_UID",
"PORTAGE_IUSE",
"PORTAGE_LOG_FILE", "PORTAGE_MASTER_PID",
"PORTAGE_PYM_PATH", "PORTAGE_QUIET",
"PORTAGE_REPO_NAME", "PORTAGE_RESTRICT",
"PORTAGE_TMPDIR", "PORTAGE_UPDATE_ENV",
"PORTAGE_VERBOSE", "PORTAGE_WORKDIR_MODE",
"PORTDIR", "PORTDIR_OVERLAY", "PREROOTPATH", "PROFILE_PATHS",
"ROOT", "ROOTPATH", "STARTDIR", "T", "TMP", "TMPDIR",
"USE_EXPAND", "USE_ORDER", "WORKDIR",
"XARGS",
]
# user config variables
_environ_whitelist += [
"DOC_SYMLINKS_DIR", "INSTALL_MASK", "PKG_INSTALL_MASK"
]
_environ_whitelist += [
"A", "AA", "CATEGORY", "P", "PF", "PN", "PR", "PV", "PVR"
]
# misc variables inherited from the calling environment
_environ_whitelist += [
"COLORTERM", "DISPLAY", "EDITOR", "LESS",
"LESSOPEN", "LOGNAME", "LS_COLORS", "PAGER",
"TERM", "TERMCAP", "USER",
]
# tempdir settings
_environ_whitelist += [
"TMPDIR", "TEMP", "TMP",
]
# localization settings
_environ_whitelist += [
"LANG", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES",
"LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_PAPER",
"LC_ALL",
]
# other variables inherited from the calling environment
_environ_whitelist += [
"CVS_RSH", "ECHANGELOG_USER",
"GPG_AGENT_INFO",
"SSH_AGENT_PID", "SSH_AUTH_SOCK",
"STY", "WINDOW", "XAUTHORITY",
]
_environ_whitelist = frozenset(_environ_whitelist)
_environ_whitelist_re = re.compile(r'^(CCACHE_|DISTCC_).*')
# Filter selected variables in the config.environ() method so that
# they don't needlessly propagate down into the ebuild environment.
_environ_filter = []
# misc variables inherited from the calling environment
_environ_filter += [
"INFOPATH", "MANPATH",
]
# variables that break bash
_environ_filter += [
"HISTFILE", "POSIXLY_CORRECT",
]
# portage config variables and variables set directly by portage
_environ_filter += [
"ACCEPT_KEYWORDS", "ACCEPT_PROPERTIES", "AUTOCLEAN",
"CLEAN_DELAY", "COLLISION_IGNORE", "CONFIG_PROTECT",
"CONFIG_PROTECT_MASK", "EGENCACHE_DEFAULT_OPTS", "EMERGE_DEFAULT_OPTS",
"EMERGE_LOG_DIR",
"EMERGE_WARNING_DELAY", "FETCHCOMMAND", "FETCHCOMMAND_FTP",
"FETCHCOMMAND_HTTP", "FETCHCOMMAND_SFTP",
"GENTOO_MIRRORS", "NOCONFMEM", "O",
"PORTAGE_BACKGROUND",
"PORTAGE_BINHOST_CHUNKSIZE", "PORTAGE_CALLER",
"PORTAGE_ELOG_CLASSES",
"PORTAGE_ELOG_MAILFROM", "PORTAGE_ELOG_MAILSUBJECT",
"PORTAGE_ELOG_MAILURI", "PORTAGE_ELOG_SYSTEM",
"PORTAGE_FETCH_CHECKSUM_TRY_MIRRORS", "PORTAGE_FETCH_RESUME_MIN_SIZE",
"PORTAGE_GPG_DIR",
"PORTAGE_GPG_KEY", "PORTAGE_IONICE_COMMAND",
"PORTAGE_PACKAGE_EMPTY_ABORT",
"PORTAGE_REPO_DUPLICATE_WARN",
"PORTAGE_RO_DISTDIRS",
"PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS",
"PORTAGE_RSYNC_RETRIES", "PORTAGE_USE", "PORT_LOGDIR",
"QUICKPKG_DEFAULT_OPTS",
"RESUMECOMMAND", "RESUMECOMMAND_HTTP", "RESUMECOMMAND_HTTP",
"RESUMECOMMAND_SFTP", "SYNC", "USE_EXPAND_HIDDEN", "USE_ORDER",
]
_environ_filter = frozenset(_environ_filter)
_undef_lic_groups = set()
_default_globals = (
('ACCEPT_LICENSE', '* -@EULA'),
('ACCEPT_PROPERTIES', '*'),
)
# To enhance usability, make some vars case insensitive
# by forcing them to lower case.
_case_insensitive_vars = ('AUTOCLEAN', 'NOCOLOR',)
def __init__(self, clone=None, mycpv=None, config_profile_path=None,
config_incrementals=None, config_root=None, target_root=None,
local_config=True, env=None):
"""
@param clone: If provided, init will use deepcopy to copy by value the instance.
@type clone: Instance of config class.
@param mycpv: CPV to load up (see setcpv), this is the same as calling init with mycpv=None
and then calling instance.setcpv(mycpv).
@type mycpv: String
@param config_profile_path: Configurable path to the profile (usually PROFILE_PATH from portage.const)
@type config_profile_path: String
@param config_incrementals: List of incremental variables
(defaults to portage.const.INCREMENTALS)
@type config_incrementals: List
@param config_root: path to read local config from (defaults to "/", see PORTAGE_CONFIGROOT)
@type config_root: String
@param target_root: __init__ override of $ROOT env variable.
@type target_root: String
@param local_config: Enables loading of local config (/etc/portage); used most by repoman to
ignore local config (keywording and unmasking)
@type local_config: Boolean
@param env: The calling environment which is used to override settings.
Defaults to os.environ if unspecified.
@type env: dict
"""
# When initializing the global portage.settings instance, avoid
# raising exceptions whenever possible since exceptions thrown
# from 'import portage' or 'import portage.exceptions' statements
# can practically render the api unusable for api consumers.
tolerant = "_initializing_globals" in globals()
self.already_in_regenerate = 0
self.locked = 0
self.mycpv = None
self._setcpv_args_hash = None
self.puse = []
self.modifiedkeys = []
self.uvlist = []
self._accept_chost_re = None
self._accept_license = None
self._accept_license_str = None
self._license_groups = {}
self._accept_properties = None
self.virtuals = {}
self.virts_p = {}
self.dirVirtuals = None
self.v_count = 0
# Virtuals obtained from the vartree
self.treeVirtuals = {}
# Virtuals by user specification. Includes negatives.
self.userVirtuals = {}
# Virtual negatives from user specifications.
self.negVirtuals = {}
# Virtuals added by the depgraph via self.setinst().
self._depgraphVirtuals = {}
self.user_profile_dir = None
self.local_config = local_config
self._local_repo_configs = None
self._local_repo_conf_path = None
if clone:
self.incrementals = copy.deepcopy(clone.incrementals)
self.profile_path = copy.deepcopy(clone.profile_path)
self.user_profile_dir = copy.deepcopy(clone.user_profile_dir)
self.local_config = copy.deepcopy(clone.local_config)
self._local_repo_configs = \
copy.deepcopy(clone._local_repo_configs)
self._local_repo_conf_path = \
copy.deepcopy(clone._local_repo_conf_path)
self.module_priority = copy.deepcopy(clone.module_priority)
self.modules = copy.deepcopy(clone.modules)
self.depcachedir = copy.deepcopy(clone.depcachedir)
self.packages = copy.deepcopy(clone.packages)
self.virtuals = copy.deepcopy(clone.virtuals)
self.dirVirtuals = copy.deepcopy(clone.dirVirtuals)
self.treeVirtuals = copy.deepcopy(clone.treeVirtuals)
self.userVirtuals = copy.deepcopy(clone.userVirtuals)
self.negVirtuals = copy.deepcopy(clone.negVirtuals)
self._depgraphVirtuals = copy.deepcopy(clone._depgraphVirtuals)
self.use_defs = copy.deepcopy(clone.use_defs)
self.usemask = copy.deepcopy(clone.usemask)
self.usemask_list = copy.deepcopy(clone.usemask_list)
self.pusemask_list = copy.deepcopy(clone.pusemask_list)
self.useforce = copy.deepcopy(clone.useforce)
self.useforce_list = copy.deepcopy(clone.useforce_list)
self.puseforce_list = copy.deepcopy(clone.puseforce_list)
self.puse = copy.deepcopy(clone.puse)
self.make_defaults_use = copy.deepcopy(clone.make_defaults_use)
self.pkgprofileuse = copy.deepcopy(clone.pkgprofileuse)
self.mycpv = copy.deepcopy(clone.mycpv)
self._setcpv_args_hash = copy.deepcopy(clone._setcpv_args_hash)
self.configdict = copy.deepcopy(clone.configdict)
self.configlist = [
self.configdict['env.d'],
self.configdict['pkginternal'],
self.configdict['globals'],
self.configdict['defaults'],
self.configdict['conf'],
self.configdict['pkg'],
self.configdict['auto'],
self.configdict['env'],
]
self.lookuplist = self.configlist[:]
self.lookuplist.reverse()
self._use_expand_dict = copy.deepcopy(clone._use_expand_dict)
self.profiles = copy.deepcopy(clone.profiles)
self.backupenv = self.configdict["backupenv"]
self.pusedict = copy.deepcopy(clone.pusedict)
self.categories = copy.deepcopy(clone.categories)
self.pkeywordsdict = copy.deepcopy(clone.pkeywordsdict)
self._pkeywords_list = copy.deepcopy(clone._pkeywords_list)
self.pmaskdict = copy.deepcopy(clone.pmaskdict)
self.punmaskdict = copy.deepcopy(clone.punmaskdict)
self.prevmaskdict = copy.deepcopy(clone.prevmaskdict)
self.pprovideddict = copy.deepcopy(clone.pprovideddict)
self.features = copy.deepcopy(clone.features)
self._accept_license = copy.deepcopy(clone._accept_license)
self._plicensedict = copy.deepcopy(clone._plicensedict)
self._license_groups = copy.deepcopy(clone._license_groups)
self._accept_properties = copy.deepcopy(clone._accept_properties)
self._ppropertiesdict = copy.deepcopy(clone._ppropertiesdict)
else:
def check_var_directory(varname, var):
if not os.path.isdir(var):
writemsg(_("!!! Error: %s='%s' is not a directory. "
"Please correct this.\n") % (varname, var),
noiselevel=-1)
raise portage.exception.DirectoryNotFound(var)
if config_root is None:
config_root = "/"
config_root = normalize_path(os.path.abspath(
config_root)).rstrip(os.path.sep) + os.path.sep
check_var_directory("PORTAGE_CONFIGROOT", config_root)
self.depcachedir = DEPCACHE_PATH
if not config_profile_path:
config_profile_path = \
os.path.join(config_root, PROFILE_PATH)
if os.path.isdir(config_profile_path):
self.profile_path = config_profile_path
else:
self.profile_path = None
else:
self.profile_path = config_profile_path[:]
if config_incrementals is None:
self.incrementals = copy.deepcopy(portage.const.INCREMENTALS)
else:
self.incrementals = copy.deepcopy(config_incrementals)
self.module_priority = ["user","default"]
self.modules = {}
modules_loader = portage.env.loaders.KeyValuePairFileLoader(
os.path.join(config_root, MODULES_FILE_PATH), None, None)
modules_dict, modules_errors = modules_loader.load()
self.modules["user"] = modules_dict
if self.modules["user"] is None:
self.modules["user"] = {}
self.modules["default"] = {
"portdbapi.metadbmodule": "portage.cache.metadata.database",
"portdbapi.auxdbmodule": "portage.cache.flat_hash.database",
}
self.usemask=[]
self.configlist=[]
# back up our incremental variables:
self.configdict={}
self._use_expand_dict = {}
# configlist will contain: [ env.d, globals, defaults, conf, pkg, auto, backupenv, env ]
self.configlist.append({})
self.configdict["env.d"] = self.configlist[-1]
self.configlist.append({})
self.configdict["pkginternal"] = self.configlist[-1]
# The symlink might not exist or might not be a symlink.
if self.profile_path is None:
self.profiles = []
else:
self.profiles = []
def addProfile(currentPath):
parentsFile = os.path.join(currentPath, "parent")
eapi_file = os.path.join(currentPath, "eapi")
try:
eapi = codecs.open(_unicode_encode(eapi_file,
encoding=_encodings['fs'], errors='strict'),
mode='r', encoding=_encodings['content'], errors='replace'
).readline().strip()
except IOError:
pass
else:
if not eapi_is_supported(eapi):
raise portage.exception.ParseError(_(
"Profile contains unsupported "
"EAPI '%s': '%s'") % \
(eapi, os.path.realpath(eapi_file),))
if os.path.exists(parentsFile):
parents = grabfile(parentsFile)
if not parents:
raise portage.exception.ParseError(
_("Empty parent file: '%s'") % parentsFile)
for parentPath in parents:
parentPath = normalize_path(os.path.join(
currentPath, parentPath))
if os.path.exists(parentPath):
addProfile(parentPath)
else:
raise portage.exception.ParseError(
_("Parent '%s' not found: '%s'") % \
(parentPath, parentsFile))
self.profiles.append(currentPath)
try:
addProfile(os.path.realpath(self.profile_path))
except portage.exception.ParseError as e:
writemsg(_("!!! Unable to parse profile: '%s'\n") % \
self.profile_path, noiselevel=-1)
writemsg("!!! ParseError: %s\n" % str(e), noiselevel=-1)
del e
self.profiles = []
if local_config and self.profiles:
custom_prof = os.path.join(
config_root, CUSTOM_PROFILE_PATH)
if os.path.exists(custom_prof):
self.user_profile_dir = custom_prof
self.profiles.append(custom_prof)
del custom_prof
self.packages_list = [grabfile_package(os.path.join(x, "packages")) for x in self.profiles]
self.packages = stack_lists(self.packages_list, incremental=1)
del self.packages_list
#self.packages = grab_stacked("packages", self.profiles, grabfile, incremental_lines=1)
# revmaskdict
self.prevmaskdict={}
for x in self.packages:
# Negative atoms are filtered by the above stack_lists() call.
if not isinstance(x, dep.Atom):
x = dep.Atom(x.lstrip('*'))
self.prevmaskdict.setdefault(x.cp, []).append(x)
self._pkeywords_list = []
rawpkeywords = [grabdict_package(
os.path.join(x, "package.keywords"), recursive=1) \
for x in self.profiles]
for pkeyworddict in rawpkeywords:
cpdict = {}
for k, v in pkeyworddict.items():
cpdict.setdefault(k.cp, {})[k] = v
self._pkeywords_list.append(cpdict)
# get profile-masked use flags -- INCREMENTAL Child over parent
self.usemask_list = [grabfile(os.path.join(x, "use.mask"),
recursive=1) for x in self.profiles]
self.usemask = set(stack_lists(
self.usemask_list, incremental=True))
use_defs_lists = [grabdict(os.path.join(x, "use.defaults")) for x in self.profiles]
self.use_defs = stack_dictlist(use_defs_lists, incremental=True)
del use_defs_lists
self.pusemask_list = []
rawpusemask = [grabdict_package(os.path.join(x, "package.use.mask"),
recursive=1) for x in self.profiles]
for pusemaskdict in rawpusemask:
cpdict = {}
for k, v in pusemaskdict.items():
cpdict.setdefault(k.cp, {})[k] = v
self.pusemask_list.append(cpdict)
del rawpusemask
self.pkgprofileuse = []
rawprofileuse = [grabdict_package(os.path.join(x, "package.use"),
juststrings=True, recursive=1) for x in self.profiles]
for rawpusedict in rawprofileuse:
cpdict = {}
for k, v in rawpusedict.items():
cpdict.setdefault(k.cp, {})[k] = v
self.pkgprofileuse.append(cpdict)
del rawprofileuse
self.useforce_list = [grabfile(os.path.join(x, "use.force"),
recursive=1) for x in self.profiles]
self.useforce = set(stack_lists(
self.useforce_list, incremental=True))
self.puseforce_list = []
rawpuseforce = [grabdict_package(
os.path.join(x, "package.use.force"), recursive=1) \
for x in self.profiles]
for rawpusefdict in rawpuseforce:
cpdict = {}
for k, v in rawpusefdict.items():
cpdict.setdefault(k.cp, {})[k] = v
self.puseforce_list.append(cpdict)
del rawpuseforce
make_conf = getconfig(
os.path.join(config_root, MAKE_CONF_FILE),
tolerant=tolerant, allow_sourcing=True)
if make_conf is None:
make_conf = {}
# Allow ROOT setting to come from make.conf if it's not overridden
# by the constructor argument (from the calling environment).
if target_root is None and "ROOT" in make_conf:
target_root = make_conf["ROOT"]
if not target_root.strip():
target_root = None
if target_root is None:
target_root = "/"
target_root = normalize_path(os.path.abspath(
target_root)).rstrip(os.path.sep) + os.path.sep
portage.util.ensure_dirs(target_root)
check_var_directory("ROOT", target_root)
# The expand_map is used for variable substitution
# in getconfig() calls, and the getconfig() calls
# update expand_map with the value of each variable
# assignment that occurs. Variable substitution occurs
# in the following order, which corresponds to the
# order of appearance in self.lookuplist:
#
# * env.d
# * make.globals
# * make.defaults
# * make.conf
#
# Notably absent is "env", since we want to avoid any
# interaction with the calling environment that might
# lead to unexpected results.
expand_map = {}
env_d = getconfig(os.path.join(target_root, "etc", "profile.env"),
expand=expand_map)
# env_d will be None if profile.env doesn't exist.
if env_d:
self.configdict["env.d"].update(env_d)
expand_map.update(env_d)
# backupenv is used for calculating incremental variables.
if env is None:
env = os.environ
# Avoid potential UnicodeDecodeError exceptions later.
env_unicode = dict((_unicode_decode(k), _unicode_decode(v))
for k, v in env.items())
self.backupenv = env_unicode
if env_d:
# Remove duplicate values so they don't override updated
# profile.env values later (profile.env is reloaded in each
# call to self.regenerate).
for k, v in env_d.items():
try:
if self.backupenv[k] == v:
del self.backupenv[k]
except KeyError:
pass
del k, v
self.configdict["env"] = util.LazyItemsDict(self.backupenv)
# make.globals should not be relative to config_root
# because it only contains constants.
for x in (portage.const.GLOBAL_CONFIG_PATH, "/etc"):
self.mygcfg = getconfig(os.path.join(x, "make.globals"),
expand=expand_map)
if self.mygcfg:
break
if self.mygcfg is None:
self.mygcfg = {}
for k, v in self._default_globals:
self.mygcfg.setdefault(k, v)
self.configlist.append(self.mygcfg)
self.configdict["globals"]=self.configlist[-1]
self.make_defaults_use = []
self.mygcfg = {}
if self.profiles:
mygcfg_dlists = [getconfig(os.path.join(x, "make.defaults"),
expand=expand_map) for x in self.profiles]
for cfg in mygcfg_dlists:
if cfg:
self.make_defaults_use.append(cfg.get("USE", ""))
else:
self.make_defaults_use.append("")
self.mygcfg = stack_dicts(mygcfg_dlists,
incrementals=portage.const.INCREMENTALS)
if self.mygcfg is None:
self.mygcfg = {}
self.configlist.append(self.mygcfg)
self.configdict["defaults"]=self.configlist[-1]
self.mygcfg = getconfig(
os.path.join(config_root, MAKE_CONF_FILE),
tolerant=tolerant, allow_sourcing=True, expand=expand_map)
if self.mygcfg is None:
self.mygcfg = {}
# Don't allow the user to override certain variables in make.conf
profile_only_variables = self.configdict["defaults"].get(
"PROFILE_ONLY_VARIABLES", "").split()
for k in profile_only_variables:
self.mygcfg.pop(k, None)
self.configlist.append(self.mygcfg)
self.configdict["conf"]=self.configlist[-1]
self.configlist.append(util.LazyItemsDict())
self.configdict["pkg"]=self.configlist[-1]
#auto-use:
self.configlist.append({})
self.configdict["auto"]=self.configlist[-1]
self.configdict["backupenv"] = self.backupenv
# Don't allow the user to override certain variables in the env
for k in profile_only_variables:
self.backupenv.pop(k, None)
self.configlist.append(self.configdict["env"])
# make lookuplist for loading package.*
self.lookuplist=self.configlist[:]
self.lookuplist.reverse()
# Blacklist vars that could interfere with portage internals.
for blacklisted in self._env_blacklist:
for cfg in self.lookuplist:
cfg.pop(blacklisted, None)
self.backupenv.pop(blacklisted, None)
del blacklisted, cfg
self["PORTAGE_CONFIGROOT"] = config_root
self.backup_changes("PORTAGE_CONFIGROOT")
self["ROOT"] = target_root
self.backup_changes("ROOT")
self.pusedict = {}
self.pkeywordsdict = {}
self._plicensedict = {}
self._ppropertiesdict = {}
self.punmaskdict = {}
abs_user_config = os.path.join(config_root, USER_CONFIG_PATH)
# locations for "categories" and "arch.list" files
locations = [os.path.join(self["PORTDIR"], "profiles")]
pmask_locations = [os.path.join(self["PORTDIR"], "profiles")]
pmask_locations.extend(self.profiles)
""" repoman controls PORTDIR_OVERLAY via the environment, so no
special cases are needed here."""
overlay_profiles = []
for ov in self["PORTDIR_OVERLAY"].split():
ov = normalize_path(ov)
profiles_dir = os.path.join(ov, "profiles")
if os.path.isdir(profiles_dir):
overlay_profiles.append(profiles_dir)
locations += overlay_profiles
pmask_locations.extend(overlay_profiles)
if local_config:
locations.append(abs_user_config)
pmask_locations.append(abs_user_config)
pusedict = grabdict_package(
os.path.join(abs_user_config, "package.use"), recursive=1)
for k, v in pusedict.items():
self.pusedict.setdefault(k.cp, {})[k] = v
#package.keywords
pkgdict = grabdict_package(
os.path.join(abs_user_config, "package.keywords"),
recursive=1)
for k, v in pkgdict.items():
# default to ~arch if no specific keyword is given
if not v:
mykeywordlist = []
if self.configdict["defaults"] and \
"ACCEPT_KEYWORDS" in self.configdict["defaults"]:
groups = self.configdict["defaults"]["ACCEPT_KEYWORDS"].split()
else:
groups = []
for keyword in groups:
if not keyword[0] in "~-":
mykeywordlist.append("~"+keyword)
v = mykeywordlist
self.pkeywordsdict.setdefault(k.cp, {})[k] = v
#package.license
licdict = grabdict_package(os.path.join(
abs_user_config, "package.license"), recursive=1)
for k, v in licdict.items():
cp = k.cp
cp_dict = self._plicensedict.get(cp)
if not cp_dict:
cp_dict = {}
self._plicensedict[cp] = cp_dict
cp_dict[k] = self.expandLicenseTokens(v)
#package.properties
propdict = grabdict_package(os.path.join(
abs_user_config, "package.properties"), recursive=1)
for k, v in propdict.items():
cp = k.cp
cp_dict = self._ppropertiesdict.get(cp)
if not cp_dict:
cp_dict = {}
self._ppropertiesdict[cp] = cp_dict
cp_dict[k] = v
self._local_repo_configs = {}
self._local_repo_conf_path = \
os.path.join(abs_user_config, 'repos.conf')
try:
from configparser import SafeConfigParser, ParsingError
except ImportError:
from ConfigParser import SafeConfigParser, ParsingError
repo_conf_parser = SafeConfigParser()
try:
repo_conf_parser.readfp(
codecs.open(
_unicode_encode(self._local_repo_conf_path,
encoding=_encodings['fs'], errors='strict'),
mode='r', encoding=_encodings['content'], errors='replace')
)
except EnvironmentError as e:
if e.errno != errno.ENOENT:
raise
del e
except ParsingError as e:
portage.util.writemsg_level(
_("!!! Error parsing '%s': %s\n") % \
(self._local_repo_conf_path, e),
level=logging.ERROR, noiselevel=-1)
del e
else:
repo_defaults = repo_conf_parser.defaults()
if repo_defaults:
self._local_repo_configs['DEFAULT'] = \
_local_repo_config('DEFAULT', repo_defaults)
for repo_name in repo_conf_parser.sections():
repo_opts = repo_defaults.copy()
for opt_name in repo_conf_parser.options(repo_name):
repo_opts[opt_name] = \
repo_conf_parser.get(repo_name, opt_name)
self._local_repo_configs[repo_name] = \
_local_repo_config(repo_name, repo_opts)
#getting categories from an external file now
categories = [grabfile(os.path.join(x, "categories")) for x in locations]
self.categories = tuple(sorted(
stack_lists(categories, incremental=1)))
del categories
archlist = [grabfile(os.path.join(x, "arch.list")) for x in locations]
archlist = stack_lists(archlist, incremental=1)
self.configdict["conf"]["PORTAGE_ARCHLIST"] = " ".join(archlist)
# package.mask and package.unmask
pkgmasklines = []
pkgunmasklines = []
for x in pmask_locations:
pkgmasklines.append(grabfile_package(
os.path.join(x, "package.mask"), recursive=1))
pkgunmasklines.append(grabfile_package(
os.path.join(x, "package.unmask"), recursive=1))
pkgmasklines = stack_lists(pkgmasklines, incremental=1)
pkgunmasklines = stack_lists(pkgunmasklines, incremental=1)
self.pmaskdict = {}
for x in pkgmasklines:
self.pmaskdict.setdefault(x.cp, []).append(x)
for x in pkgunmasklines:
self.punmaskdict.setdefault(x.cp, []).append(x)
pkgprovidedlines = [grabfile(os.path.join(x, "package.provided"), recursive=1) for x in self.profiles]
pkgprovidedlines = stack_lists(pkgprovidedlines, incremental=1)
has_invalid_data = False
for x in range(len(pkgprovidedlines)-1, -1, -1):
myline = pkgprovidedlines[x]
if not isvalidatom("=" + myline):
writemsg(_("Invalid package name in package.provided: %s\n") % \
myline, noiselevel=-1)
has_invalid_data = True
del pkgprovidedlines[x]
continue
cpvr = catpkgsplit(pkgprovidedlines[x])
if not cpvr or cpvr[0] == "null":
writemsg(_("Invalid package name in package.provided: ")+pkgprovidedlines[x]+"\n",
noiselevel=-1)
has_invalid_data = True
del pkgprovidedlines[x]
continue
if cpvr[0] == "virtual":
writemsg(_("Virtual package in package.provided: %s\n") % \
myline, noiselevel=-1)
has_invalid_data = True
del pkgprovidedlines[x]
continue
if has_invalid_data:
writemsg(_("See portage(5) for correct package.provided usage.\n"),
noiselevel=-1)
self.pprovideddict = {}
for x in pkgprovidedlines:
cpv=catpkgsplit(x)
if not x:
continue
mycatpkg = cpv_getkey(x)
if mycatpkg in self.pprovideddict:
self.pprovideddict[mycatpkg].append(x)
else:
self.pprovideddict[mycatpkg]=[x]
# parse licensegroups
license_groups = self._license_groups
for x in locations:
for k, v in grabdict(
os.path.join(x, "license_groups")).items():
license_groups.setdefault(k, []).extend(v)
# reasonable defaults; this is important as without USE_ORDER,
# USE will always be "" (nothing set)!
if "USE_ORDER" not in self:
self.backupenv["USE_ORDER"] = "env:pkg:conf:defaults:pkginternal:env.d"
self["PORTAGE_GID"] = str(portage_gid)
self.backup_changes("PORTAGE_GID")
if self.get("PORTAGE_DEPCACHEDIR", None):
self.depcachedir = self["PORTAGE_DEPCACHEDIR"]
self["PORTAGE_DEPCACHEDIR"] = self.depcachedir
self.backup_changes("PORTAGE_DEPCACHEDIR")
overlays = self.get("PORTDIR_OVERLAY","").split()
if overlays:
new_ov = []
for ov in overlays:
ov = normalize_path(ov)
if os.path.isdir(ov):
new_ov.append(ov)
else:
writemsg(_("!!! Invalid PORTDIR_OVERLAY"
" (not a dir): '%s'\n") % ov, noiselevel=-1)
self["PORTDIR_OVERLAY"] = " ".join(new_ov)
self.backup_changes("PORTDIR_OVERLAY")
if "CBUILD" not in self and "CHOST" in self:
self["CBUILD"] = self["CHOST"]
self.backup_changes("CBUILD")
self["PORTAGE_BIN_PATH"] = PORTAGE_BIN_PATH
self.backup_changes("PORTAGE_BIN_PATH")
self["PORTAGE_PYM_PATH"] = PORTAGE_PYM_PATH
self.backup_changes("PORTAGE_PYM_PATH")
for var in ("PORTAGE_INST_UID", "PORTAGE_INST_GID"):
try:
self[var] = str(int(self.get(var, "0")))
except ValueError:
writemsg(_("!!! %s='%s' is not a valid integer. "
"Falling back to '0'.\n") % (var, self[var]),
noiselevel=-1)
self[var] = "0"
self.backup_changes(var)
# initialize self.features
self.regenerate()
if bsd_chflags:
self.features.add('chflags')
self["FEATURES"] = " ".join(sorted(self.features))
self.backup_changes("FEATURES")
global _glep_55_enabled, _validate_cache_for_unsupported_eapis
if 'parse-eapi-ebuild-head' in self.features:
_validate_cache_for_unsupported_eapis = False
if 'parse-eapi-glep-55' in self.features:
_validate_cache_for_unsupported_eapis = False
_glep_55_enabled = True
for k in self._case_insensitive_vars:
if k in self:
self[k] = self[k].lower()
self.backup_changes(k)
if mycpv:
self.setcpv(mycpv)
def _init_dirs(self):
"""
Create a few directories that are critical to portage operation
"""
if not os.access(self["ROOT"], os.W_OK):
return
# gid, mode, mask, preserve_perms
dir_mode_map = {
"tmp" : ( -1, 0o1777, 0, True),
"var/tmp" : ( -1, 0o1777, 0, True),
PRIVATE_PATH : (portage_gid, 0o2750, 0o2, False),
CACHE_PATH : (portage_gid, 0o755, 0o2, False)
}
for mypath, (gid, mode, modemask, preserve_perms) \
in dir_mode_map.items():
mydir = os.path.join(self["ROOT"], mypath)
if preserve_perms and os.path.isdir(mydir):
# Only adjust permissions on some directories if
# they don't exist yet. This gives freedom to the
# user to adjust permissions to suit their taste.
continue
try:
portage.util.ensure_dirs(mydir, gid=gid, mode=mode, mask=modemask)
except portage.exception.PortageException as e:
writemsg(_("!!! Directory initialization failed: '%s'\n") % mydir,
noiselevel=-1)
writemsg("!!! %s\n" % str(e),
noiselevel=-1)
def expandLicenseTokens(self, tokens):
""" Take a token from ACCEPT_LICENSE or package.license and expand it
if it's a group token (indicated by @) or just return it if it's not a
group. If a group is negated then negate all group elements."""
expanded_tokens = []
for x in tokens:
expanded_tokens.extend(self._expandLicenseToken(x, None))
return expanded_tokens
def _expandLicenseToken(self, token, traversed_groups):
negate = False
rValue = []
if token.startswith("-"):
negate = True
license_name = token[1:]
else:
license_name = token
if not license_name.startswith("@"):
rValue.append(token)
return rValue
group_name = license_name[1:]
if not traversed_groups:
traversed_groups = set()
license_group = self._license_groups.get(group_name)
if group_name in traversed_groups:
writemsg(_("Circular license group reference"
" detected in '%s'\n") % group_name, noiselevel=-1)
rValue.append("@"+group_name)
elif license_group:
traversed_groups.add(group_name)
for l in license_group:
if l.startswith("-"):
writemsg(_("Skipping invalid element %s"
" in license group '%s'\n") % (l, group_name),
noiselevel=-1)
else:
rValue.extend(self._expandLicenseToken(l, traversed_groups))
else:
if self._license_groups and \
group_name not in self._undef_lic_groups:
self._undef_lic_groups.add(group_name)
writemsg(_("Undefined license group '%s'\n") % group_name,
noiselevel=-1)
rValue.append("@"+group_name)
if negate:
rValue = ["-" + token for token in rValue]
return rValue
def validate(self):
"""Validate miscellaneous settings and display warnings if necessary.
(This code was previously in the global scope of portage.py)"""
groups = self["ACCEPT_KEYWORDS"].split()
archlist = self.archlist()
if not archlist:
writemsg(_("--- 'profiles/arch.list' is empty or "
"not available. Empty portage tree?\n"), noiselevel=1)
else:
for group in groups:
if group not in archlist and \
not (group.startswith("-") and group[1:] in archlist) and \
group not in ("*", "~*", "**"):
writemsg(_("!!! INVALID ACCEPT_KEYWORDS: %s\n") % str(group),
noiselevel=-1)
abs_profile_path = os.path.join(self["PORTAGE_CONFIGROOT"],
PROFILE_PATH)
if not self.profile_path or (not os.path.islink(abs_profile_path) and \
not os.path.exists(os.path.join(abs_profile_path, "parent")) and \
os.path.exists(os.path.join(self["PORTDIR"], "profiles"))):
writemsg(_("\a\n\n!!! %s is not a symlink and will probably prevent most merges.\n") % abs_profile_path,
noiselevel=-1)
writemsg(_("!!! It should point into a profile within %s/profiles/\n") % self["PORTDIR"])
writemsg(_("!!! (You can safely ignore this message when syncing. It's harmless.)\n\n\n"))
abs_user_virtuals = os.path.join(self["PORTAGE_CONFIGROOT"],
USER_VIRTUALS_FILE)
if os.path.exists(abs_user_virtuals):
writemsg("\n!!! /etc/portage/virtuals is deprecated in favor of\n")
writemsg("!!! /etc/portage/profile/virtuals. Please move it to\n")
writemsg("!!! this new location.\n\n")
if not process.sandbox_capable and \
("sandbox" in self.features or "usersandbox" in self.features):
if self.profile_path is not None and \
os.path.realpath(self.profile_path) == \
os.path.realpath(os.path.join(
self["PORTAGE_CONFIGROOT"], PROFILE_PATH)):
# Don't show this warning when running repoman and the
# sandbox feature came from a profile that doesn't belong
# to the user.
writemsg(colorize("BAD", _("!!! Problem with sandbox"
" binary. Disabling...\n\n")), noiselevel=-1)
if "fakeroot" in self.features and \
not portage.process.fakeroot_capable:
writemsg(_("!!! FEATURES=fakeroot is enabled, but the "
"fakeroot binary is not installed.\n"), noiselevel=-1)
def loadVirtuals(self,root):
"""Not currently used by portage."""
writemsg("DEPRECATED: portage.config.loadVirtuals\n")
self.getvirtuals(root)
def load_best_module(self,property_string):
best_mod = best_from_dict(property_string,self.modules,self.module_priority)
mod = None
try:
mod = load_mod(best_mod)
except ImportError:
if best_mod.startswith("cache."):
best_mod = "portage." + best_mod
try:
mod = load_mod(best_mod)
except ImportError:
pass
if mod is None:
raise
return mod
def lock(self):
self.locked = 1
def unlock(self):
self.locked = 0
def modifying(self):
if self.locked:
raise Exception(_("Configuration is locked."))
def backup_changes(self,key=None):
self.modifying()
if key and key in self.configdict["env"]:
self.backupenv[key] = copy.deepcopy(self.configdict["env"][key])
else:
raise KeyError(_("No such key defined in environment: %s") % key)
def reset(self,keeping_pkg=0,use_cache=1):
"""
Restore environment from self.backupenv, call self.regenerate()
@param keeping_pkg: Should we keep the set_cpv() data or delete it.
@type keeping_pkg: Boolean
@param use_cache: Should self.regenerate use the cache or not
@type use_cache: Boolean
@rype: None
"""
self.modifying()
self.configdict["env"].clear()
self.configdict["env"].update(self.backupenv)
self.modifiedkeys = []
if not keeping_pkg:
self.mycpv = None
self.puse = ""
self.configdict["pkg"].clear()
self.configdict["pkginternal"].clear()
self.configdict["defaults"]["USE"] = \
" ".join(self.make_defaults_use)
self.usemask = set(stack_lists(
self.usemask_list, incremental=True))
self.useforce = set(stack_lists(
self.useforce_list, incremental=True))
self.regenerate(use_cache=use_cache)
def load_infodir(self,infodir):
warnings.warn("portage.config.load_infodir() is deprecated",
DeprecationWarning)
return 1
class _lazy_vars(object):
__slots__ = ('built_use', 'settings', 'values')
def __init__(self, built_use, settings):
self.built_use = built_use
self.settings = settings
self.values = None
def __getitem__(self, k):
if self.values is None:
self.values = self._init_values()
return self.values[k]
def _init_values(self):
values = {}
settings = self.settings
use = self.built_use
if use is None:
use = frozenset(settings['PORTAGE_USE'].split())
values['ACCEPT_LICENSE'] = self._accept_license(use, settings)
values['PORTAGE_RESTRICT'] = self._restrict(use, settings)
return values
def _accept_license(self, use, settings):
"""
Generate a pruned version of ACCEPT_LICENSE, by intersection with
LICENSE. This is required since otherwise ACCEPT_LICENSE might be
too big (bigger than ARG_MAX), causing execve() calls to fail with
E2BIG errors as in bug #262647.
"""
try:
licenses = set(flatten(
dep.use_reduce(dep.paren_reduce(
settings['LICENSE']),
uselist=use)))
except exception.InvalidDependString:
licenses = set()
licenses.discard('||')
if settings._accept_license:
acceptable_licenses = set()
for x in settings._accept_license:
if x == '*':
acceptable_licenses.update(licenses)
elif x == '-*':
acceptable_licenses.clear()
elif x[:1] == '-':
acceptable_licenses.discard(x[1:])
elif x in licenses:
acceptable_licenses.add(x)
licenses = acceptable_licenses
return ' '.join(sorted(licenses))
def _restrict(self, use, settings):
try:
restrict = set(flatten(
dep.use_reduce(dep.paren_reduce(
settings['RESTRICT']),
uselist=use)))
except exception.InvalidDependString:
restrict = set()
return ' '.join(sorted(restrict))
class _lazy_use_expand(object):
"""
Lazily evaluate USE_EXPAND variables since they are only needed when
an ebuild shell is spawned. Variables values are made consistent with
the previously calculated USE settings.
"""
def __init__(self, use, usemask, iuse_implicit,
use_expand_split, use_expand_dict):
self._use = use
self._usemask = usemask
self._iuse_implicit = iuse_implicit
self._use_expand_split = use_expand_split
self._use_expand_dict = use_expand_dict
def __getitem__(self, key):
prefix = key.lower() + '_'
prefix_len = len(prefix)
expand_flags = set( x[prefix_len:] for x in self._use \
if x[:prefix_len] == prefix )
var_split = self._use_expand_dict.get(key, '').split()
# Preserve the order of var_split because it can matter for things
# like LINGUAS.
var_split = [ x for x in var_split if x in expand_flags ]
var_split.extend(expand_flags.difference(var_split))
has_wildcard = '*' in expand_flags
if has_wildcard:
var_split = [ x for x in var_split if x != "*" ]
has_iuse = set()
for x in self._iuse_implicit:
if x[:prefix_len] == prefix:
has_iuse.add(x[prefix_len:])
if has_wildcard:
# * means to enable everything in IUSE that's not masked
if has_iuse:
usemask = self._usemask
for suffix in has_iuse:
x = prefix + suffix
if x not in usemask:
if suffix not in expand_flags:
var_split.append(suffix)
else:
# If there is a wildcard and no matching flags in IUSE then
# LINGUAS should be unset so that all .mo files are
# installed.
var_split = []
# Make the flags unique and filter them according to IUSE.
# Also, continue to preserve order for things like LINGUAS
# and filter any duplicates that variable may contain.
filtered_var_split = []
remaining = has_iuse.intersection(var_split)
for x in var_split:
if x in remaining:
remaining.remove(x)
filtered_var_split.append(x)
var_split = filtered_var_split
if var_split:
value = ' '.join(var_split)
else:
# Don't export empty USE_EXPAND vars unless the user config
# exports them as empty. This is required for vars such as
# LINGUAS, where unset and empty have different meanings.
if has_wildcard:
# ebuild.sh will see this and unset the variable so
# that things like LINGUAS work properly
value = '*'
else:
if has_iuse:
value = ''
else:
# It's not in IUSE, so just allow the variable content
# to pass through if it is defined somewhere. This
# allows packages that support LINGUAS but don't
# declare it in IUSE to use the variable outside of the
# USE_EXPAND context.
value = None
return value