ebuild: fetch: Flatten conditionals in _get_fetch_resume_size
[portage.git] / pym / portage / __init__.py
1 # portage.py -- core Portage functionality
2 # Copyright 1998-2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 from __future__ import unicode_literals
6
7 VERSION="HEAD"
8
9 # ===========================================================================
10 # START OF IMPORTS -- START OF IMPORTS -- START OF IMPORTS -- START OF IMPORT
11 # ===========================================================================
12
13 try:
14         import sys
15         import errno
16         if not hasattr(errno, 'ESTALE'):
17                 # ESTALE may not be defined on some systems, such as interix.
18                 errno.ESTALE = -1
19         import re
20         import types
21         import platform
22
23         # Temporarily delete these imports, to ensure that only the
24         # wrapped versions are imported by portage internals.
25         import os
26         del os
27         import shutil
28         del shutil
29
30 except ImportError as e:
31         sys.stderr.write("\n\n")
32         sys.stderr.write("!!! Failed to complete python imports. These are internal modules for\n")
33         sys.stderr.write("!!! python and failure here indicates that you have a problem with python\n")
34         sys.stderr.write("!!! itself and thus portage is not able to continue processing.\n\n")
35
36         sys.stderr.write("!!! You might consider starting python with verbose flags to see what has\n")
37         sys.stderr.write("!!! gone wrong. Here is the information we got for this exception:\n")
38         sys.stderr.write("    "+str(e)+"\n\n");
39         raise
40
41 try:
42
43         import portage.proxy.lazyimport
44         import portage.proxy as proxy
45         proxy.lazyimport.lazyimport(globals(),
46                 'portage.cache.cache_errors:CacheError',
47                 'portage.checksum',
48                 'portage.checksum:perform_checksum,perform_md5,prelink_capable',
49                 'portage.cvstree',
50                 'portage.data',
51                 'portage.data:lchown,ostype,portage_gid,portage_uid,secpass,' + \
52                         'uid,userland,userpriv_groups,wheelgid',
53                 'portage.dbapi',
54                 'portage.dbapi.bintree:bindbapi,binarytree',
55                 'portage.dbapi.cpv_expand:cpv_expand',
56                 'portage.dbapi.dep_expand:dep_expand',
57                 'portage.dbapi.porttree:close_portdbapi_caches,FetchlistDict,' + \
58                         'portagetree,portdbapi',
59                 'portage.dbapi.vartree:dblink,merge,unmerge,vardbapi,vartree',
60                 'portage.dbapi.virtual:fakedbapi',
61                 'portage.dep',
62                 'portage.dep:best_match_to_list,dep_getcpv,dep_getkey,' + \
63                         'flatten,get_operator,isjustname,isspecific,isvalidatom,' + \
64                         'match_from_list,match_to_list',
65                 'portage.dep.dep_check:dep_check,dep_eval,dep_wordreduce,dep_zapdeps',
66                 'portage.eclass_cache',
67                 'portage.elog',
68                 'portage.exception',
69                 'portage.getbinpkg',
70                 'portage.locks',
71                 'portage.locks:lockdir,lockfile,unlockdir,unlockfile',
72                 'portage.mail',
73                 'portage.manifest:Manifest',
74                 'portage.output',
75                 'portage.output:bold,colorize',
76                 'portage.package.ebuild.doebuild:doebuild,' + \
77                         'doebuild_environment,spawn,spawnebuild',
78                 'portage.package.ebuild.config:autouse,best_from_dict,' + \
79                         'check_config_instance,config',
80                 'portage.package.ebuild.deprecated_profile_check:' + \
81                         'deprecated_profile_check',
82                 'portage.package.ebuild.digestcheck:digestcheck',
83                 'portage.package.ebuild.digestgen:digestgen',
84                 'portage.package.ebuild.fetch:fetch',
85                 'portage.package.ebuild.getmaskingreason:getmaskingreason',
86                 'portage.package.ebuild.getmaskingstatus:getmaskingstatus',
87                 'portage.package.ebuild.prepare_build_dirs:prepare_build_dirs',
88                 'portage.process',
89                 'portage.process:atexit_register,run_exitfuncs',
90                 'portage.update:dep_transform,fixdbentries,grab_updates,' + \
91                         'parse_updates,update_config_files,update_dbentries,' + \
92                         'update_dbentry',
93                 'portage.util',
94                 'portage.util:atomic_ofstream,apply_secpass_permissions,' + \
95                         'apply_recursive_permissions,dump_traceback,getconfig,' + \
96                         'grabdict,grabdict_package,grabfile,grabfile_package,' + \
97                         'map_dictlist_vals,new_protect_filename,normalize_path,' + \
98                         'pickle_read,pickle_write,stack_dictlist,stack_dicts,' + \
99                         'stack_lists,unique_array,varexpand,writedict,writemsg,' + \
100                         'writemsg_stdout,write_atomic',
101                 'portage.util.digraph:digraph',
102                 'portage.util.env_update:env_update',
103                 'portage.util.ExtractKernelVersion:ExtractKernelVersion',
104                 'portage.util.listdir:cacheddir,listdir',
105                 'portage.util.movefile:movefile',
106                 'portage.util.mtimedb:MtimeDB',
107                 'portage.versions',
108                 'portage.versions:best,catpkgsplit,catsplit,cpv_getkey,' + \
109                         'cpv_getkey@getCPFromCPV,endversion_keys,' + \
110                         'suffix_value@endversion,pkgcmp,pkgsplit,vercmp,ververify',
111                 'portage.xpak',
112                 'subprocess',
113                 'time',
114         )
115
116         try:
117                 from collections import OrderedDict
118         except ImportError:
119                 proxy.lazyimport.lazyimport(globals(),
120                         'portage.cache.mappings:OrderedDict')
121
122         import portage.const
123         from portage.const import VDB_PATH, PRIVATE_PATH, CACHE_PATH, DEPCACHE_PATH, \
124                 USER_CONFIG_PATH, MODULES_FILE_PATH, CUSTOM_PROFILE_PATH, PORTAGE_BASE_PATH, \
125                 PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, PROFILE_PATH, LOCALE_DATA_PATH, \
126                 EBUILD_SH_BINARY, SANDBOX_BINARY, BASH_BINARY, \
127                 MOVE_BINARY, PRELINK_BINARY, WORLD_FILE, MAKE_CONF_FILE, MAKE_DEFAULTS_FILE, \
128                 DEPRECATED_PROFILE_FILE, USER_VIRTUALS_FILE, EBUILD_SH_ENV_FILE, \
129                 INVALID_ENV_FILE, CUSTOM_MIRRORS_FILE, CONFIG_MEMORY_FILE,\
130                 INCREMENTALS, EAPI, MISC_SH_BINARY, REPO_NAME_LOC, REPO_NAME_FILE
131
132 except ImportError as e:
133         sys.stderr.write("\n\n")
134         sys.stderr.write("!!! Failed to complete portage imports. There are internal modules for\n")
135         sys.stderr.write("!!! portage and failure here indicates that you have a problem with your\n")
136         sys.stderr.write("!!! installation of portage. Please try a rescue portage located in the\n")
137         sys.stderr.write("!!! portage tree under '/usr/portage/sys-apps/portage/files/' (default).\n")
138         sys.stderr.write("!!! There is a README.RESCUE file that details the steps required to perform\n")
139         sys.stderr.write("!!! a recovery of portage.\n")
140         sys.stderr.write("    "+str(e)+"\n\n")
141         raise
142
143 if sys.hexversion >= 0x3000000:
144         basestring = str
145         long = int
146
147 # We use utf_8 encoding everywhere. Previously, we used
148 # sys.getfilesystemencoding() for the 'merge' encoding, but that had
149 # various problems:
150 #
151 #   1) If the locale is ever changed then it can cause orphan files due
152 #      to changed character set translation.
153 #
154 #   2) Ebuilds typically install files with utf_8 encoded file names,
155 #      and then portage would be forced to rename those files to match
156 #      sys.getfilesystemencoding(), possibly breaking things.
157 #
158 #   3) Automatic translation between encodings can lead to nonsensical
159 #      file names when the source encoding is unknown by portage.
160 #
161 #   4) It's inconvenient for ebuilds to convert the encodings of file
162 #      names to match the current locale, and upstreams typically encode
163 #      file names with utf_8 encoding.
164 #
165 # So, instead of relying on sys.getfilesystemencoding(), we avoid the above
166 # problems by using a constant utf_8 'merge' encoding for all locales, as
167 # discussed in bug #382199 and bug #381509.
168 _encodings = {
169         'content'                : 'utf_8',
170         'fs'                     : 'utf_8',
171         'merge'                  : 'utf_8',
172         'repo.content'           : 'utf_8',
173         'stdio'                  : 'utf_8',
174 }
175
176 if sys.hexversion >= 0x3000000:
177
178         def _decode_argv(argv):
179                 # With Python 3, the surrogateescape encoding error handler makes it
180                 # possible to access the original argv bytes, which can be useful
181                 # if their actual encoding does no match the filesystem encoding.
182                 fs_encoding = sys.getfilesystemencoding()
183                 return [_unicode_decode(x.encode(fs_encoding, 'surrogateescape'))
184                         for x in argv]
185
186         def _unicode_encode(s, encoding=_encodings['content'], errors='backslashreplace'):
187                 if isinstance(s, str):
188                         s = s.encode(encoding, errors)
189                 return s
190
191         def _unicode_decode(s, encoding=_encodings['content'], errors='replace'):
192                 if isinstance(s, bytes):
193                         s = str(s, encoding=encoding, errors=errors)
194                 return s
195
196         _native_string = _unicode_decode
197 else:
198
199         def _decode_argv(argv):
200                 return [_unicode_decode(x) for x in argv]
201
202         def _unicode_encode(s, encoding=_encodings['content'], errors='backslashreplace'):
203                 if isinstance(s, unicode):
204                         s = s.encode(encoding, errors)
205                 return s
206
207         def _unicode_decode(s, encoding=_encodings['content'], errors='replace'):
208                 if isinstance(s, bytes):
209                         s = unicode(s, encoding=encoding, errors=errors)
210                 return s
211
212         _native_string = _unicode_encode
213
214 if sys.hexversion >= 0x20605f0:
215         def _native_kwargs(kwargs):
216                 return kwargs
217 else:
218         # Avoid "TypeError: keywords must be strings" issue triggered
219         # by unicode_literals: http://bugs.python.org/issue4978
220         def _native_kwargs(kwargs):
221                 return dict((_native_string(k), v) for k, v in kwargs.iteritems())
222
223 class _unicode_func_wrapper(object):
224         """
225         Wraps a function, converts arguments from unicode to bytes,
226         and return values to unicode from bytes. Function calls
227         will raise UnicodeEncodeError if an argument fails to be
228         encoded with the required encoding. Return values that
229         are single strings are decoded with errors='replace'. Return 
230         values that are lists of strings are decoded with errors='strict'
231         and elements that fail to be decoded are omitted from the returned
232         list.
233         """
234         __slots__ = ('_func', '_encoding')
235
236         def __init__(self, func, encoding=_encodings['fs']):
237                 self._func = func
238                 self._encoding = encoding
239
240         def _process_args(self, args, kwargs):
241
242                 encoding = self._encoding
243                 wrapped_args = [_unicode_encode(x, encoding=encoding, errors='strict')
244                         for x in args]
245                 if kwargs:
246                         wrapped_kwargs = dict(
247                                 (k, _unicode_encode(v, encoding=encoding, errors='strict'))
248                                 for k, v in kwargs.items())
249                 else:
250                         wrapped_kwargs = {}
251
252                 return (wrapped_args, wrapped_kwargs)
253
254         def __call__(self, *args, **kwargs):
255
256                 encoding = self._encoding
257                 wrapped_args, wrapped_kwargs = self._process_args(args, kwargs)
258
259                 rval = self._func(*wrapped_args, **wrapped_kwargs)
260
261                 # Don't use isinstance() since we don't want to convert subclasses
262                 # of tuple such as posix.stat_result in Python >=3.2.
263                 if rval.__class__ in (list, tuple):
264                         decoded_rval = []
265                         for x in rval:
266                                 try:
267                                         x = _unicode_decode(x, encoding=encoding, errors='strict')
268                                 except UnicodeDecodeError:
269                                         pass
270                                 else:
271                                         decoded_rval.append(x)
272
273                         if isinstance(rval, tuple):
274                                 rval = tuple(decoded_rval)
275                         else:
276                                 rval = decoded_rval
277                 else:
278                         rval = _unicode_decode(rval, encoding=encoding, errors='replace')
279
280                 return rval
281
282 class _unicode_module_wrapper(object):
283         """
284         Wraps a module and wraps all functions with _unicode_func_wrapper.
285         """
286         __slots__ = ('_mod', '_encoding', '_overrides', '_cache')
287
288         def __init__(self, mod, encoding=_encodings['fs'], overrides=None, cache=True):
289                 object.__setattr__(self, '_mod', mod)
290                 object.__setattr__(self, '_encoding', encoding)
291                 object.__setattr__(self, '_overrides', overrides)
292                 if cache:
293                         cache = {}
294                 else:
295                         cache = None
296                 object.__setattr__(self, '_cache', cache)
297
298         def __getattribute__(self, attr):
299                 cache = object.__getattribute__(self, '_cache')
300                 if cache is not None:
301                         result = cache.get(attr)
302                         if result is not None:
303                                 return result
304                 result = getattr(object.__getattribute__(self, '_mod'), attr)
305                 encoding = object.__getattribute__(self, '_encoding')
306                 overrides = object.__getattribute__(self, '_overrides')
307                 override = None
308                 if overrides is not None:
309                         override = overrides.get(id(result))
310                 if override is not None:
311                         result = override
312                 elif isinstance(result, type):
313                         pass
314                 elif type(result) is types.ModuleType:
315                         result = _unicode_module_wrapper(result,
316                                 encoding=encoding, overrides=overrides)
317                 elif hasattr(result, '__call__'):
318                         result = _unicode_func_wrapper(result, encoding=encoding)
319                 if cache is not None:
320                         cache[attr] = result
321                 return result
322
323 import os as _os
324 _os_overrides = {
325         id(_os.fdopen)        : _os.fdopen,
326         id(_os.popen)         : _os.popen,
327         id(_os.read)          : _os.read,
328         id(_os.system)        : _os.system,
329 }
330
331
332 try:
333         _os_overrides[id(_os.mkfifo)] = _os.mkfifo
334 except AttributeError:
335         pass # Jython
336
337 if hasattr(_os, 'statvfs'):
338         _os_overrides[id(_os.statvfs)] = _os.statvfs
339
340 os = _unicode_module_wrapper(_os, overrides=_os_overrides,
341         encoding=_encodings['fs'])
342 _os_merge = _unicode_module_wrapper(_os,
343         encoding=_encodings['merge'], overrides=_os_overrides)
344
345 import shutil as _shutil
346 shutil = _unicode_module_wrapper(_shutil, encoding=_encodings['fs'])
347
348 # Imports below this point rely on the above unicode wrapper definitions.
349 try:
350         __import__('selinux')
351         import portage._selinux
352         selinux = _unicode_module_wrapper(_selinux,
353                 encoding=_encodings['fs'])
354         _selinux_merge = _unicode_module_wrapper(_selinux,
355                 encoding=_encodings['merge'])
356 except (ImportError, OSError) as e:
357         if isinstance(e, OSError):
358                 sys.stderr.write("!!! SELinux not loaded: %s\n" % str(e))
359         del e
360         _selinux = None
361         selinux = None
362         _selinux_merge = None
363
364 # ===========================================================================
365 # END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END OF IMPORTS -- END
366 # ===========================================================================
367
368 _python_interpreter = os.path.realpath(sys.executable)
369 _bin_path = PORTAGE_BIN_PATH
370 _pym_path = PORTAGE_PYM_PATH
371 _working_copy = VERSION == "HEAD"
372
373 # Api consumers included in portage should set this to True.
374 _internal_caller = False
375
376 _sync_disabled_warnings = False
377
378 def _get_stdin():
379         """
380         Buggy code in python's multiprocessing/process.py closes sys.stdin
381         and reassigns it to open(os.devnull), but fails to update the
382         corresponding __stdin__ reference. So, detect that case and handle
383         it appropriately.
384         """
385         if not sys.__stdin__.closed:
386                 return sys.__stdin__
387         return sys.stdin
388
389 _shell_quote_re = re.compile(r"[\s><=*\\\"'$`]")
390
391 def _shell_quote(s):
392         """
393         Quote a string in double-quotes and use backslashes to
394         escape any backslashes, double-quotes, dollar signs, or
395         backquotes in the string.
396         """
397         if _shell_quote_re.search(s) is None:
398                 return s
399         for letter in "\\\"$`":
400                 if letter in s:
401                         s = s.replace(letter, "\\" + letter)
402         return "\"%s\"" % s
403
404 bsd_chflags = None
405
406 if platform.system() in ('FreeBSD',):
407
408         class bsd_chflags(object):
409
410                 @classmethod
411                 def chflags(cls, path, flags, opts=""):
412                         cmd = ['chflags']
413                         if opts:
414                                 cmd.append(opts)
415                         cmd.append('%o' % (flags,))
416                         cmd.append(path)
417
418                         if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000:
419                                 # Python 3.1 _execvp throws TypeError for non-absolute executable
420                                 # path passed as bytes (see http://bugs.python.org/issue8513).
421                                 fullname = process.find_binary(cmd[0])
422                                 if fullname is None:
423                                         raise exception.CommandNotFound(cmd[0])
424                                 cmd[0] = fullname
425
426                         encoding = _encodings['fs']
427                         cmd = [_unicode_encode(x, encoding=encoding, errors='strict')
428                                 for x in cmd]
429                         proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
430                                 stderr=subprocess.STDOUT)
431                         output = proc.communicate()[0]
432                         status = proc.wait()
433                         if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
434                                 return
435                         # Try to generate an ENOENT error if appropriate.
436                         if 'h' in opts:
437                                 _os_merge.lstat(path)
438                         else:
439                                 _os_merge.stat(path)
440                         # Make sure the binary exists.
441                         if not portage.process.find_binary('chflags'):
442                                 raise portage.exception.CommandNotFound('chflags')
443                         # Now we're not sure exactly why it failed or what
444                         # the real errno was, so just report EPERM.
445                         output = _unicode_decode(output, encoding=encoding)
446                         e = OSError(errno.EPERM, output)
447                         e.errno = errno.EPERM
448                         e.filename = path
449                         e.message = output
450                         raise e
451
452                 @classmethod
453                 def lchflags(cls, path, flags):
454                         return cls.chflags(path, flags, opts='-h')
455
456 def load_mod(name):
457         modname = ".".join(name.split(".")[:-1])
458         mod = __import__(modname)
459         components = name.split('.')
460         for comp in components[1:]:
461                 mod = getattr(mod, comp)
462         return mod
463
464 def getcwd():
465         "this fixes situations where the current directory doesn't exist"
466         try:
467                 return os.getcwd()
468         except OSError: #dir doesn't exist
469                 os.chdir("/")
470                 return "/"
471 getcwd()
472
473 def abssymlink(symlink, target=None):
474         """
475         This reads symlinks, resolving the relative symlinks,
476         and returning the absolute.
477         @param symlink: path of symlink (must be absolute)
478         @param target: the target of the symlink (as returned
479                 by readlink)
480         @rtype: str
481         @return: the absolute path of the symlink target
482         """
483         if target is not None:
484                 mylink = target
485         else:
486                 mylink = os.readlink(symlink)
487         if mylink[0] != '/':
488                 mydir=os.path.dirname(symlink)
489                 mylink=mydir+"/"+mylink
490         return os.path.normpath(mylink)
491
492 _doebuild_manifest_exempt_depend = 0
493
494 _testing_eapis = frozenset(["4-python", "4-slot-abi", "5-progress", "5-hdepend"])
495 _deprecated_eapis = frozenset(["4_pre1", "3_pre2", "3_pre1", "5_pre1", "5_pre2"])
496 _supported_eapis = frozenset([str(x) for x in range(portage.const.EAPI)] + list(_testing_eapis) + list(_deprecated_eapis))
497
498 def _eapi_is_deprecated(eapi):
499         return eapi in _deprecated_eapis
500
501 def eapi_is_supported(eapi):
502         if not isinstance(eapi, basestring):
503                 # Only call str() when necessary since with python2 it
504                 # can trigger UnicodeEncodeError if EAPI is corrupt.
505                 eapi = str(eapi)
506         eapi = eapi.strip()
507
508         if _eapi_is_deprecated(eapi):
509                 return True
510
511         if eapi in _testing_eapis:
512                 return True
513
514         try:
515                 eapi = int(eapi)
516         except ValueError:
517                 eapi = -1
518         if eapi < 0:
519                 return False
520         return eapi <= portage.const.EAPI
521
522 # This pattern is specified by PMS section 7.3.1.
523 _pms_eapi_re = re.compile(r"^[ \t]*EAPI=(['\"]?)([A-Za-z0-9+_.-]*)\1[ \t]*([ \t]#.*)?$")
524 _comment_or_blank_line = re.compile(r"^\s*(#.*)?$")
525
526 def _parse_eapi_ebuild_head(f):
527         eapi = None
528         eapi_lineno = None
529         lineno = 0
530         for line in f:
531                 lineno += 1
532                 m = _comment_or_blank_line.match(line)
533                 if m is None:
534                         eapi_lineno = lineno
535                         m = _pms_eapi_re.match(line)
536                         if m is not None:
537                                 eapi = m.group(2)
538                         break
539
540         return (eapi, eapi_lineno)
541
542 def _movefile(src, dest, **kwargs):
543         """Calls movefile and raises a PortageException if an error occurs."""
544         if movefile(src, dest, **kwargs) is None:
545                 raise portage.exception.PortageException(
546                         "mv '%s' '%s'" % (src, dest))
547
548 auxdbkeys = (
549   'DEPEND',    'RDEPEND',   'SLOT',      'SRC_URI',
550         'RESTRICT',  'HOMEPAGE',  'LICENSE',   'DESCRIPTION',
551         'KEYWORDS',  'INHERITED', 'IUSE', 'REQUIRED_USE',
552         'PDEPEND',   'PROVIDE', 'EAPI',
553         'PROPERTIES', 'DEFINED_PHASES', 'HDEPEND', 'UNUSED_04',
554         'UNUSED_03', 'UNUSED_02', 'UNUSED_01',
555 )
556 auxdbkeylen=len(auxdbkeys)
557
558 def portageexit():
559         pass
560
561 class _trees_dict(dict):
562         __slots__ = ('_running_eroot', '_target_eroot',)
563         def __init__(self, *pargs, **kargs):
564                 dict.__init__(self, *pargs, **kargs)
565                 self._running_eroot = None
566                 self._target_eroot = None
567
568 def create_trees(config_root=None, target_root=None, trees=None, env=None,
569         eprefix=None):
570
571         if trees is None:
572                 trees = _trees_dict()
573         elif not isinstance(trees, _trees_dict):
574                 # caller passed a normal dict or something,
575                 # but we need a _trees_dict instance
576                 trees = _trees_dict(trees)
577
578         if env is None:
579                 env = os.environ
580
581         settings = config(config_root=config_root, target_root=target_root,
582                 env=env, eprefix=eprefix)
583         settings.lock()
584
585         trees._target_eroot = settings['EROOT']
586         myroots = [(settings['EROOT'], settings)]
587         if settings["ROOT"] == "/" and settings["EPREFIX"] == const.EPREFIX:
588                 trees._running_eroot = trees._target_eroot
589         else:
590
591                 # When ROOT != "/" we only want overrides from the calling
592                 # environment to apply to the config that's associated
593                 # with ROOT != "/", so pass a nearly empty dict for the env parameter.
594                 clean_env = {}
595                 for k in ('PATH', 'PORTAGE_GRPNAME', 'PORTAGE_REPOSITORIES', 'PORTAGE_USERNAME',
596                         'PYTHONPATH', 'SSH_AGENT_PID', 'SSH_AUTH_SOCK', 'TERM',
597                         'ftp_proxy', 'http_proxy', 'no_proxy',
598                         '__PORTAGE_TEST_HARDLINK_LOCKS'):
599                         v = settings.get(k)
600                         if v is not None:
601                                 clean_env[k] = v
602                 settings = config(config_root=None, target_root="/",
603                         env=clean_env, eprefix=None)
604                 settings.lock()
605                 trees._running_eroot = settings['EROOT']
606                 myroots.append((settings['EROOT'], settings))
607
608         for myroot, mysettings in myroots:
609                 trees[myroot] = portage.util.LazyItemsDict(trees.get(myroot, {}))
610                 trees[myroot].addLazySingleton("virtuals", mysettings.getvirtuals)
611                 trees[myroot].addLazySingleton(
612                         "vartree", vartree, categories=mysettings.categories,
613                                 settings=mysettings)
614                 trees[myroot].addLazySingleton("porttree",
615                         portagetree, settings=mysettings)
616                 trees[myroot].addLazySingleton("bintree",
617                         binarytree, pkgdir=mysettings["PKGDIR"], settings=mysettings)
618         return trees
619
620 if VERSION == 'HEAD':
621         class _LazyVersion(proxy.objectproxy.ObjectProxy):
622                 def _get_target(self):
623                         global VERSION
624                         if VERSION is not self:
625                                 return VERSION
626                         if os.path.isdir(os.path.join(PORTAGE_BASE_PATH, '.git')):
627                                 encoding = _encodings['fs']
628                                 cmd = [BASH_BINARY, "-c", ("cd %s ; git describe --tags || exit $? ; " + \
629                                         "if [ -n \"`git diff-index --name-only --diff-filter=M HEAD`\" ] ; " + \
630                                         "then echo modified ; git rev-list --format=%%ct -n 1 HEAD ; fi ; " + \
631                                         "exit 0") % _shell_quote(PORTAGE_BASE_PATH)]
632                                 cmd = [_unicode_encode(x, encoding=encoding, errors='strict')
633                                         for x in cmd]
634                                 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
635                                         stderr=subprocess.STDOUT)
636                                 output = _unicode_decode(proc.communicate()[0], encoding=encoding)
637                                 status = proc.wait()
638                                 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
639                                         output_lines = output.splitlines()
640                                         if output_lines:
641                                                 version_split = output_lines[0].split('-')
642                                                 if version_split:
643                                                         VERSION = version_split[0].lstrip('v')
644                                                         patchlevel = False
645                                                         if len(version_split) > 1:
646                                                                 patchlevel = True
647                                                                 VERSION = "%s_p%s" %(VERSION, version_split[1])
648                                                         if len(output_lines) > 1 and output_lines[1] == 'modified':
649                                                                 head_timestamp = None
650                                                                 if len(output_lines) > 3:
651                                                                         try:
652                                                                                 head_timestamp = long(output_lines[3])
653                                                                         except ValueError:
654                                                                                 pass
655                                                                 timestamp = long(time.time())
656                                                                 if head_timestamp is not None and timestamp > head_timestamp:
657                                                                         timestamp = timestamp - head_timestamp
658                                                                 if not patchlevel:
659                                                                         VERSION = "%s_p0" % (VERSION,)
660                                                                 VERSION = "%s_p%d" % (VERSION, timestamp)
661                                                         return VERSION
662                         VERSION = 'HEAD'
663                         return VERSION
664         VERSION = _LazyVersion()
665
666 _legacy_global_var_names = ("archlist", "db", "features",
667         "groups", "mtimedb", "mtimedbfile", "pkglines",
668         "portdb", "profiledir", "root", "selinux_enabled",
669         "settings", "thirdpartymirrors")
670
671 def _reset_legacy_globals():
672
673         global _legacy_globals_constructed
674         _legacy_globals_constructed = set()
675         for k in _legacy_global_var_names:
676                 globals()[k] = _LegacyGlobalProxy(k)
677
678 class _LegacyGlobalProxy(proxy.objectproxy.ObjectProxy):
679
680         __slots__ = ('_name',)
681
682         def __init__(self, name):
683                 proxy.objectproxy.ObjectProxy.__init__(self)
684                 object.__setattr__(self, '_name', name)
685
686         def _get_target(self):
687                 name = object.__getattribute__(self, '_name')
688                 from portage._legacy_globals import _get_legacy_global
689                 return _get_legacy_global(name)
690
691 _reset_legacy_globals()
692
693 def _disable_legacy_globals():
694         """
695         This deletes the ObjectProxy instances that are used
696         for lazy initialization of legacy global variables.
697         The purpose of deleting them is to prevent new code
698         from referencing these deprecated variables.
699         """
700         global _legacy_global_var_names
701         for k in _legacy_global_var_names:
702                 globals().pop(k, None)