| #!/usr/bin/env python |
| # Copyright 1999-2021 Gentoo Authors |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| import argparse |
| import platform |
| import signal |
| import sys |
| import textwrap |
| |
| # This block ensures that ^C interrupts are handled quietly. |
| try: |
| |
| def exithandler(signum, _frame): |
| signal.signal(signal.SIGINT, signal.SIG_IGN) |
| signal.signal(signal.SIGTERM, signal.SIG_IGN) |
| 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(128 + signal.SIGINT) |
| |
| |
| def debug_signal(_signum, _frame): |
| import pdb |
| |
| pdb.set_trace() |
| |
| |
| 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 os |
| from os import path as osp |
| |
| if osp.isfile( |
| osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), ".portage_not_installed") |
| ): |
| sys.path.insert( |
| 0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "lib") |
| ) |
| import portage |
| |
| portage._internal_caller = True |
| from portage import os |
| from portage import _encodings |
| from portage import _shell_quote |
| from portage import _unicode_encode |
| from portage.const import VDB_PATH |
| from portage.exception import ( |
| PermissionDenied, |
| PortageKeyError, |
| PortagePackageException, |
| UnsupportedAPIException, |
| ) |
| from portage.localization import _ |
| import portage.util |
| from portage.util._eventloop.global_event_loop import global_event_loop |
| from _emerge.actions import apply_priorities |
| from _emerge.Package import Package |
| from _emerge.RootConfig import RootConfig |
| |
| portage.process.sanitize_fds() |
| |
| description = "See the ebuild(1) man page for more info" |
| usage = "Usage: ebuild <ebuild file> <command> [command] ..." |
| parser = argparse.ArgumentParser(description=description, usage=usage) |
| |
| force_help = ( |
| "When used together with the digest or manifest " |
| + "command, this option forces regeneration of digests for all " |
| + "distfiles associated with the current ebuild. Any distfiles " |
| + "that do not already exist in ${DISTDIR} will be automatically fetched." |
| ) |
| |
| parser.add_argument("--force", help=force_help, action="store_true") |
| parser.add_argument( |
| "--color", help="enable or disable color output", choices=("y", "n") |
| ) |
| parser.add_argument("--debug", help="show debug output", action="store_true") |
| parser.add_argument("--version", help="show version and exit", action="store_true") |
| parser.add_argument( |
| "--ignore-default-opts", |
| action="store_true", |
| help="do not use the EBUILD_DEFAULT_OPTS environment variable", |
| ) |
| parser.add_argument( |
| "--skip-manifest", help="skip all manifest checks", action="store_true" |
| ) |
| |
| opts, pargs = parser.parse_known_args(args=sys.argv[1:]) |
| |
| |
| def err(txt): |
| portage.writemsg("ebuild: %s\n" % (txt,), noiselevel=-1) |
| sys.exit(1) |
| |
| |
| if opts.version: |
| print("Portage", portage.VERSION) |
| sys.exit(os.EX_OK) |
| |
| if len(pargs) < 2: |
| parser.error("missing required args") |
| |
| if not opts.ignore_default_opts: |
| default_opts = portage.util.shlex_split( |
| portage.settings.get("EBUILD_DEFAULT_OPTS", "") |
| ) |
| opts, pargs = parser.parse_known_args(default_opts + sys.argv[1:]) |
| |
| debug = opts.debug |
| force = opts.force |
| |
| if debug: |
| # Ensure that all config instances have this setting, |
| # including the one that's used by portdbapi for aux_get. |
| os.environ["PORTAGE_DEBUG"] = "1" |
| portage._reset_legacy_globals() |
| |
| # do this _after_ 'import portage' to prevent unnecessary tracing |
| if debug and "python-trace" in portage.features: |
| portage.debug.set_trace(True) |
| |
| 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.backup_changes("NOCOLOR") |
| portage.settings.lock() |
| |
| apply_priorities(portage.settings) |
| |
| ebuild = pargs.pop(0) |
| |
| pf = None |
| if ebuild.endswith(".ebuild"): |
| pf = os.path.basename(ebuild)[:-7] |
| |
| if pf is None: |
| 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. |
| pwd = os.environ.get("PWD", "") |
| 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 ebuild repository, but |
| # subdirectories of the base can be built from symlinks (like crossdev does). |
| 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 != vdb_path and ebuild_portdir not in portage.portdb.porttrees: |
| portdir_overlay = portage.settings.get("PORTDIR_OVERLAY", "") |
| os.environ["PORTDIR_OVERLAY"] = portdir_overlay + " " + _shell_quote(ebuild_portdir) |
| |
| print("Appending %s to PORTDIR_OVERLAY..." % 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): |
| err("%s: does not exist" % (ebuild,)) |
| |
| ebuild_split = ebuild.split("/") |
| cpv = "%s/%s" % (ebuild_split[-3], pf) |
| |
| 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(vdb_path): |
| mytree = "vartree" |
| pkg_type = "installed" |
| |
| portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv, myrepo=myrepo) |
| |
| if os.path.realpath(portage_ebuild) != ebuild: |
| 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, myrepo=myrepo) |
| |
| if not portage_ebuild or portage_ebuild != ebuild: |
| err("%s: does not seem to have a valid PORTDIR structure" % (ebuild,)) |
| |
| if len(pargs) > 1 and "config" in pargs: |
| other_phases = set(pargs) |
| other_phases.difference_update(("clean", "config", "digest", "manifest")) |
| if other_phases: |
| 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 |
| upstream has changed the identity of the distfiles and the user would |
| otherwise have to manually remove the Manifest and files/digest-* files in |
| order to ensure correct results.""" |
| try: |
| portage._doebuild_manifest_exempt_depend += 1 |
| pkgdir = os.path.dirname(myebuild) |
| fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi) |
| 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] |
| for myfile in distfiles: |
| try: |
| del mf.fhashdict["DIST"][myfile] |
| except KeyError: |
| pass |
| mf.write() |
| finally: |
| 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 |
| ) |
| |
| # Make configuration adjustments to portage.portdb.doebuild_settings, |
| # in order to enforce consistency for EBUILD_FORCE_TEST support |
| # (see bug 601466). |
| tmpsettings = portage.portdb.doebuild_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") |
| tmpsettings.features.add("test") |
| portage.writemsg(_("Forcing test.\n"), noiselevel=-1) |
| |
| tmpsettings.features.discard("fail-clean") |
| |
| 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 "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") |
| |
| # Now that configuration adjustments are complete, create a clone of |
| # tmpsettings. The current instance refers to portdb.doebuild_settings, |
| # and we want to avoid the possibility of unintended side-effects. |
| tmpsettings = portage.config(clone=tmpsettings) |
| |
| try: |
| metadata = dict( |
| zip( |
| Package.metadata_keys, |
| portage.db[portage.settings["EROOT"]][mytree].dbapi.aux_get( |
| cpv, Package.metadata_keys, myrepo=myrepo |
| ), |
| ) |
| ) |
| except PortageKeyError: |
| # aux_get failure, message should have been shown on stderr. |
| sys.exit(1) |
| |
| root_config = RootConfig(portage.settings, portage.db[portage.settings["EROOT"]], None) |
| |
| cpv = portage.versions._pkg_str( |
| cpv, |
| metadata=metadata, |
| settings=portage.settings, |
| db=portage.db[portage.settings["EROOT"]][mytree].dbapi, |
| ) |
| |
| 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 "noauto" not in tmpsettings.features |
| and build_dir_phases.intersection(pargs) |
| ): |
| portage.doebuild_environment( |
| ebuild, "setup", portage.root, tmpsettings, debug, 1, portage.portdb |
| ) |
| env_filename = os.path.join(tmpsettings["T"], "environment") |
| if os.path.exists(env_filename): |
| msg = ( |
| "Existing ${T}/environment for '%s' will be sourced. " |
| + "Run 'clean' to start with a fresh environment." |
| ) % (tmpsettings["PF"],) |
| msg = textwrap.wrap(msg, 70) |
| for x in msg: |
| portage.writemsg(">>> %s\n" % x) |
| |
| if ebuild_changed: |
| open( |
| os.path.join(tmpsettings["PORTAGE_BUILDDIR"], ".ebuild_changed"), |
| "w", |
| ).close() |
| |
| |
| checked_for_stale_env = False |
| |
| for arg in pargs: |
| try: |
| if not checked_for_stale_env and arg not in ("digest", "manifest"): |
| # This has to go after manifest generation since otherwise |
| # aux_get() might fail due to invalid ebuild digests. |
| stale_env_warning() |
| checked_for_stale_env = True |
| |
| if arg in ("digest", "manifest") and force: |
| discard_digests(ebuild, tmpsettings, portage.portdb) |
| a = portage.doebuild( |
| ebuild, |
| arg, |
| settings=tmpsettings, |
| debug=debug, |
| tree=mytree, |
| vartree=portage.db[portage.root]["vartree"], |
| ) |
| except KeyboardInterrupt: |
| print("Interrupted.") |
| a = 1 |
| except PortageKeyError: |
| # aux_get error |
| a = 1 |
| except UnsupportedAPIException as e: |
| msg = textwrap.wrap(str(e), 70) |
| del e |
| for x in msg: |
| portage.writemsg("!!! %s\n" % x, noiselevel=-1) |
| a = 1 |
| except PortagePackageException as e: |
| portage.writemsg("!!! %s\n" % (e,), noiselevel=-1) |
| a = 1 |
| except PermissionDenied as e: |
| portage.writemsg("!!! Permission Denied: %s\n" % (e,), noiselevel=-1) |
| a = 1 |
| if a is None: |
| print("Could not run the required binary?") |
| a = 127 |
| if a: |
| global_event_loop().close() |
| sys.exit(a) |
| |
| global_event_loop().close() |