repoman: fix PORTDIR{,_OVERLAY} interference
[portage.git] / bin / ebuild
index fa7c3d5360588b6508daaaa0e3ed7c124dec4c4e..4fdc762419854b8cd29dd09a1756a7600b7f760c 100755 (executable)
@@ -1,29 +1,41 @@
 #!/usr/bin/python -O
-# Copyright 1999-2006 Gentoo Foundation
+# Copyright 1999-2013 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
-# $Id$
 
+from __future__ import print_function
+
+import platform
+import signal
 import sys
 # This block ensures that ^C interrupts are handled quietly.
 try:
-       import signal
 
        def exithandler(signum,frame):
                signal.signal(signal.SIGINT, signal.SIG_IGN)
                signal.signal(signal.SIGTERM, signal.SIG_IGN)
-               sys.exit(1)
+               sys.exit(128 + signum)
 
        signal.signal(signal.SIGINT, exithandler)
        signal.signal(signal.SIGTERM, exithandler)
+       # Prevent "[Errno 32] Broken pipe" exceptions when
+       # writing to a pipe.
+       signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
 except KeyboardInterrupt:
-       sys.exit(1)
+       sys.exit(128 + signal.SIGINT)
 
 def debug_signal(signum, frame):
        import pdb
        pdb.set_trace()
-signal.signal(signal.SIGUSR1, debug_signal)
 
+if platform.python_implementation() == 'Jython':
+       debug_signum = signal.SIGUSR2 # bug #424259
+else:
+       debug_signum = signal.SIGUSR1
+
+signal.signal(debug_signum, debug_signal)
+
+import io
 import optparse
 import os
 
@@ -37,8 +49,12 @@ force_help = "When used together with the digest or manifest " + \
        "that do not already exist in ${DISTDIR} will be automatically fetched."
 
 parser.add_option("--force", help=force_help, action="store_true", dest="force")
+parser.add_option("--color", help="enable or disable color output",
+       type="choice", choices=("y", "n"))
 parser.add_option("--debug", help="show debug output",
        action="store_true", dest="debug")
+parser.add_option("--version", help="show version and exit",
+       action="store_true", dest="version")
 parser.add_option("--ignore-default-opts",
        action="store_true",
        help="do not use the EBUILD_DEFAULT_OPTS environment variable")
@@ -47,64 +63,77 @@ parser.add_option("--skip-manifest", help="skip all manifest checks",
 
 opts, pargs = parser.parse_args(args=sys.argv[1:])
 
-if len(pargs) < 2:
-       parser.error("missing required args")
-
-if "merge" in pargs:
-       print("Disabling noauto in features... merge disables it. (qmerge doesn't)")
-       os.environ["FEATURES"] = os.environ.get("FEATURES", "") + " -noauto"
+from os import path as osp
+pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")
+sys.path.insert(0, pym_path)
+import portage
+portage._internal_caller = True
+from portage import os
+from portage import _encodings
+from portage import _shell_quote
+from portage import _unicode_decode
+from portage import _unicode_encode
+from portage.const import VDB_PATH
+from _emerge.Package import Package
+from _emerge.RootConfig import RootConfig
+
+def err(txt):
+       portage.writemsg('ebuild: %s\n' % (txt,), noiselevel=-1)
+       sys.exit(1)
 
-os.environ["PORTAGE_CALLER"]="ebuild"
-try:
-       import portage
-except ImportError:
-       from os import path as osp
-       sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
-       import portage
+if opts.version:
+       print("Portage", portage.VERSION)
+       sys.exit(os.EX_OK)
 
-from portage import os
+if len(pargs) < 2:
+       parser.error("missing required args")
 
 if not opts.ignore_default_opts:
-       default_opts = portage.settings.get("EBUILD_DEFAULT_OPTS", "").split()
+       default_opts = portage.util.shlex_split(
+               portage.settings.get("EBUILD_DEFAULT_OPTS", ""))
        opts, pargs = parser.parse_args(default_opts + sys.argv[1:])
 
 debug = opts.debug
 force = opts.force
 
 import portage.util, portage.const
-import portage.dep
-portage.dep._dep_check_strict = True
 
 # do this _after_ 'import portage' to prevent unnecessary tracing
 if debug and "python-trace" in portage.features:
        import portage.debug
        portage.debug.set_trace(True)
 
-if portage.settings["NOCOLOR"] in ("yes","true") or not sys.stdout.isatty():
+if not opts.color == 'y' and \
+       (opts.color == 'n' or \
+       portage.settings.get('NOCOLOR') in ('yes', 'true') or \
+       portage.settings.get('TERM') == 'dumb' or \
+       not sys.stdout.isatty()):
        portage.output.nocolor()
+       portage.settings.unlock()
+       portage.settings['NOCOLOR'] = 'true'
+       portage.settings.lock()
 
 ebuild = pargs.pop(0)
 
 pf = None
-if 'parse-eapi-glep-55' in portage.settings.features:
-       pf, eapi = portage._split_ebuild_name_glep55(
-               os.path.basename(ebuild))
-elif ebuild.endswith(".ebuild"):
+if ebuild.endswith(".ebuild"):
        pf = os.path.basename(ebuild)[:-7]
 
 if pf is None:
-       portage.writemsg("'%s' does not end with '.ebuild'.\n" % \
-               (ebuild,), noiselevel=-1)
-       sys.exit(1)
+       err("%s: does not end with '.ebuild'" % (ebuild,))
 
 if not os.path.isabs(ebuild):
        mycwd = os.getcwd()
        # Try to get the non-canonical path from the PWD evironment variable, since
        # the canonical path returned from os.getcwd() may may be unusable in
        # cases where the directory stucture is built from symlinks.
-       if "PWD" in os.environ and os.environ["PWD"] != mycwd and \
-               os.path.realpath(os.environ["PWD"]) == mycwd:
-               mycwd = portage.normalize_path(os.environ["PWD"])
+       pwd = os.environ.get('PWD', '')
+       if sys.hexversion < 0x3000000:
+               pwd = _unicode_decode(pwd, encoding=_encodings['content'],
+                       errors='strict')
+       if pwd and pwd != mycwd and \
+               os.path.realpath(pwd) == mycwd:
+               mycwd = portage.normalize_path(pwd)
        ebuild = os.path.join(mycwd, ebuild)
 ebuild = portage.normalize_path(ebuild)
 # portdbapi uses the canonical path for the base of the portage tree, but
@@ -112,50 +141,64 @@ ebuild = portage.normalize_path(ebuild)
 ebuild_portdir = os.path.realpath(
        os.path.dirname(os.path.dirname(os.path.dirname(ebuild))))
 ebuild = os.path.join(ebuild_portdir, *ebuild.split(os.path.sep)[-3:])
+vdb_path = os.path.realpath(os.path.join(portage.settings['EROOT'], VDB_PATH))
 
 # Make sure that portdb.findname() returns the correct ebuild.
-if ebuild_portdir not in portage.portdb.porttrees:
-       os.environ["PORTDIR_OVERLAY"] = \
-               os.environ.get("PORTDIR_OVERLAY","") + " " + ebuild_portdir
+if ebuild_portdir != vdb_path and \
+       ebuild_portdir not in portage.portdb.porttrees:
+       portdir_overlay = portage.settings.get("PORTDIR_OVERLAY", "")
+       if sys.hexversion >= 0x3000000:
+               os.environ["PORTDIR_OVERLAY"] = \
+                       portdir_overlay + \
+                       " " + _shell_quote(ebuild_portdir)
+       else:
+               os.environ["PORTDIR_OVERLAY"] = \
+                       _unicode_encode(portdir_overlay,
+                       encoding=_encodings['content'], errors='strict') + \
+                       " " + _unicode_encode(_shell_quote(ebuild_portdir),
+                       encoding=_encodings['content'], errors='strict')
+
        print("Appending %s to PORTDIR_OVERLAY..." % ebuild_portdir)
-       portage.close_portdbapi_caches()
-       reload(portage)
-del portage.portdb.porttrees[1:]
-if ebuild_portdir != portage.portdb.porttree_root:
-       portage.portdb.porttrees.append(ebuild_portdir)
+       portage._reset_legacy_globals()
+
+myrepo = None
+if ebuild_portdir != vdb_path:
+       myrepo = portage.portdb.getRepositoryName(ebuild_portdir)
 
 if not os.path.exists(ebuild):
-       print("'%s' does not exist." % ebuild)
-       sys.exit(1)
+       err('%s: does not exist' % (ebuild,))
 
 ebuild_split = ebuild.split("/")
 cpv = "%s/%s" % (ebuild_split[-3], pf)
 
-if not portage.catpkgsplit(cpv):
-       print("!!! %s does not follow correct package syntax." % (cpv))
-       sys.exit(1)
+with io.open(_unicode_encode(ebuild, encoding=_encodings['fs'], errors='strict'),
+       mode='r', encoding=_encodings['repo.content'], errors='replace') as f:
+       eapi = portage._parse_eapi_ebuild_head(f)[0]
+if eapi is None:
+       eapi = "0"
+if not portage.catpkgsplit(cpv, eapi=eapi):
+       err('%s: %s: does not follow correct package syntax' % (ebuild, cpv))
 
-if ebuild.startswith(os.path.join(portage.root, portage.const.VDB_PATH)):
+if ebuild.startswith(vdb_path):
        mytree = "vartree"
+       pkg_type = "installed"
 
-       portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv)
+       portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv, myrepo=myrepo)
 
        if os.path.realpath(portage_ebuild) != ebuild:
-               print("!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild))
-               sys.exit(1)
+               err('Portage seems to think that %s is at %s' % (cpv, portage_ebuild))
 
 else:
        mytree = "porttree"
+       pkg_type = "ebuild"
 
-       portage_ebuild = portage.portdb.findname(cpv)
+       portage_ebuild = portage.portdb.findname(cpv, myrepo=myrepo)
 
        if not portage_ebuild or portage_ebuild != ebuild:
-               print("!!! %s does not seem to have a valid PORTDIR structure." % ebuild)
-               sys.exit(1)
+               err('%s: does not seem to have a valid PORTDIR structure' % (ebuild,))
 
 if len(pargs) > 1 and "config" in pargs:
-       print("config must be called on it's own, not combined with any other phase")
-       sys.exit(1)
+       err('"config" must not be called with any other phase')
 
 def discard_digests(myebuild, mysettings, mydbapi):
        """Discard all distfiles digests for the given ebuild.  This is useful when
@@ -166,9 +209,10 @@ def discard_digests(myebuild, mysettings, mydbapi):
                portage._doebuild_manifest_exempt_depend += 1
                pkgdir = os.path.dirname(myebuild)
                fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi)
-               from portage.manifest import Manifest
-               mf = Manifest(pkgdir, mysettings["DISTDIR"],
-                       fetchlist_dict=fetchlist_dict, manifest1_compat=False)
+               mf = mysettings.repositories.get_repo_for_location(
+                       os.path.dirname(os.path.dirname(pkgdir)))
+               mf = mf.load_manifest(pkgdir, mysettings["DISTDIR"],
+                       fetchlist_dict=fetchlist_dict)
                mf.create(requiredDistfiles=None,
                        assumeDistHashesSometimes=True, assumeDistHashesAlways=True)
                distfiles = fetchlist_dict[cpv]
@@ -182,36 +226,68 @@ def discard_digests(myebuild, mysettings, mydbapi):
                portage._doebuild_manifest_exempt_depend -= 1
 
 portage.settings.validate() # generate warning messages if necessary
+
+build_dir_phases = set(["setup", "unpack", "prepare", "configure", "compile",
+       "test", "install", "package", "rpm", "merge", "qmerge"])
+
+# If the current metadata is invalid then force the ebuild to be
+# sourced again even if $T/environment already exists.
+ebuild_changed = False
+if mytree == "porttree" and build_dir_phases.intersection(pargs):
+       ebuild_changed = \
+               portage.portdb._pull_valid_cache(cpv, ebuild, ebuild_portdir)[0] is None
+
 tmpsettings = portage.config(clone=portage.settings)
 tmpsettings["PORTAGE_VERBOSE"] = "1"
 tmpsettings.backup_changes("PORTAGE_VERBOSE")
+
+if opts.skip_manifest:
+       tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
+       tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
+
+if opts.skip_manifest or \
+       "digest" in tmpsettings.features or \
+       "digest" in pargs or \
+       "manifest" in pargs:
+       portage._doebuild_manifest_exempt_depend += 1
+
 if "test" in pargs:
        # This variable is a signal to config.regenerate() to
        # indicate that the test phase should be enabled regardless
        # of problems such as masked "test" USE flag.
        tmpsettings["EBUILD_FORCE_TEST"] = "1"
        tmpsettings.backup_changes("EBUILD_FORCE_TEST")
-       if "test" not in tmpsettings.features:
-               tmpsettings.features.add("test")
-               tmpsettings["FEATURES"] = " ".join(sorted(tmpsettings.features))
-               tmpsettings.backup_changes("FEATURES")
+       tmpsettings.features.add("test")
 
-if opts.skip_manifest:
-       tmpsettings["EBUILD_SKIP_MANIFEST"] = "1"
-       tmpsettings.backup_changes("EBUILD_SKIP_MANIFEST")
-       portage._doebuild_manifest_exempt_depend += 1
+tmpsettings.features.discard("fail-clean")
 
-build_dir_phases = set(["setup", "unpack", "prepare", "configure", "compile",
-       "test", "install", "package", "rpm", "merge", "qmerge"])
+if "merge" in pargs and "noauto" in tmpsettings.features:
+       print("Disabling noauto in features... merge disables it. (qmerge doesn't)")
+       tmpsettings.features.discard("noauto")
 
-# If the current metadata is invalid then force the ebuild to be
-# sourced again even if $T/environment already exists.
-ebuild_changed = False
-if build_dir_phases.intersection(pargs):
-       metadata, st, emtime = \
-               portage.portdb._pull_valid_cache(cpv, ebuild, ebuild_portdir)
-       if metadata is None:
-               ebuild_changed = True
+try:
+       metadata = dict(zip(Package.metadata_keys,
+               portage.db[portage.settings['EROOT']][mytree].dbapi.aux_get(
+               cpv, Package.metadata_keys, myrepo=myrepo)))
+except KeyError:
+       # aux_get failure, message should have been shown on stderr.
+       sys.exit(1)
+
+root_config = RootConfig(portage.settings,
+       portage.db[portage.settings['EROOT']], None)
+
+pkg = Package(built=(pkg_type != "ebuild"), cpv=cpv,
+       installed=(pkg_type=="installed"),
+       metadata=metadata, root_config=root_config,
+       type_name=pkg_type)
+
+# Apply package.env and repo-level settings. This allows per-package
+# FEATURES and other variables (possibly PORTAGE_TMPDIR) to be
+# available as soon as possible. Also, note that the only way to ensure
+# that setcpv gets metadata from the correct repository is to pass in
+# a Package instance, as we do here (previously we had to modify
+# portdb.porttrees in order to accomplish this).
+tmpsettings.setcpv(pkg)
 
 def stale_env_warning():
        if "clean" not in pargs and \
@@ -235,6 +311,13 @@ def stale_env_warning():
 
 from portage.exception import PermissionDenied, \
        PortagePackageException, UnsupportedAPIException
+
+if 'digest' in tmpsettings.features:
+       if pargs and pargs[0] not in ("digest", "manifest"):
+               pargs = ['digest'] + pargs
+       # We only need to build digests on the first pass.
+       tmpsettings.features.discard('digest')
+
 checked_for_stale_env = False
 
 for arg in pargs:
@@ -247,8 +330,9 @@ for arg in pargs:
 
                if arg in ("digest", "manifest") and force:
                        discard_digests(ebuild, tmpsettings, portage.portdb)
-               a = portage.doebuild(ebuild, arg, portage.root, tmpsettings,
-                       debug=debug, tree=mytree)
+               a = portage.doebuild(ebuild, arg, settings=tmpsettings,
+                       debug=debug, tree=mytree,
+                       vartree=portage.db[portage.root]['vartree'])
        except KeyboardInterrupt:
                print("Interrupted.")
                a = 1