# In order to ensure correct interaction between ebuilds and
# eclasses, they need to be unset before this process of
# interaction begins.
- unset DEPEND RDEPEND PDEPEND INHERITED IUSE REQUIRED_USE \
+ unset EAPI DEPEND RDEPEND PDEPEND INHERITED IUSE REQUIRED_USE \
ECLASS E_IUSE E_REQUIRED_USE E_DEPEND E_RDEPEND E_PDEPEND
if [[ $PORTAGE_DEBUG != 1 || ${-/x/} != $- ]] ; then
rm "$PORTAGE_BUILDDIR/.ebuild_changed"
fi
- [[ -n $EAPI ]] || EAPI=0
+ [ "${EAPI+set}" = set ] || EAPI=0
if has "$EAPI" 0 1 2 3 3_pre2 ; then
export RDEPEND=${RDEPEND-${DEPEND}}
PROPERTIES DEFINED_PHASES UNUSED_05 UNUSED_04
UNUSED_03 UNUSED_02 UNUSED_01"
- [ -n "${EAPI}" ] || EAPI=0
+ [ "${EAPI+set}" = set ] || EAPI=0
# The extra $(echo) commands remove newlines.
if [ -n "${dbkey}" ] ; then
"LICENSE.virtual":"Virtuals that have a non-empty LICENSE variable",
"DESCRIPTION.missing":"Ebuilds that have a missing or empty DESCRIPTION variable",
"DESCRIPTION.toolong":"DESCRIPTION is over %d characters" % max_desc_len,
- "EAPI.definition":"EAPI definition does not conform to PMS section 8.3.1 (first non-comment, non-blank line)",
+ "EAPI.definition":"EAPI definition does not conform to PMS section 7.3.1 (first non-comment, non-blank line)",
"EAPI.deprecated":"Ebuilds that use features that are deprecated in the current EAPI",
"EAPI.incompatible":"Ebuilds that use features that are only available with a different EAPI",
"EAPI.unsupported":"Ebuilds that have an unsupported EAPI version (you must upgrade portage)",
# Default user options
FEATURES="assume-digests binpkg-logs distlocks ebuild-locks
- fixlafiles news parallel-fetch protect-owned
+ fixlafiles news parallel-fetch parse-eapi-ebuild-head protect-owned
sandbox sfperms strict unknown-features-warn unmerge-logs
unmerge-orphans userfetch"
\fIebuild\-locks\fR.
.TP
.B parse\-eapi\-ebuild\-head
-Parse \fBEAPI\fR from the head of the ebuild (first 30 lines). This feature
-is only intended for experimental purposes and should not be enabled under
-normal circumstances.
+Parse \fBEAPI\fR from the head of the ebuild as specified in PMS section
+7.3.1, and treat non\-conformant ebuilds as invalid. This feature is
+enabled by default, and will soon become enabled unconditionally.
.TP
.B prelink\-checksums
If \fBprelink\fR(8) is installed then use it to undo any prelinks on files
Ebuilds that have a missing or empty DESCRIPTION variable
.TP
.B EAPI.definition
-EAPI definition does not conform to PMS section 8.3.1 (first
+EAPI definition does not conform to PMS section 7.3.1 (first
non\-comment, non\-blank line)
.TP
.B EAPI.deprecated
from portage import _encodings
from portage import _unicode_decode
from portage import _unicode_encode
+from portage.dep import _repo_separator
+from portage.elog import elog_process
+from portage.elog.messages import eerror
import errno
import fcntl
import io
+import textwrap
class EbuildMetadataPhase(SubProcess):
used to extract metadata from the ebuild.
"""
- __slots__ = ("cpv", "eapi", "ebuild_hash", "fd_pipes", "metadata_callback",
- "metadata", "portdb", "repo_path", "settings") + \
- ("_raw_metadata",)
+ __slots__ = ("cpv", "ebuild_hash", "fd_pipes",
+ "metadata_callback", "metadata", "portdb", "repo_path", "settings") + \
+ ("_eapi", "_eapi_lineno", "_raw_metadata",)
_file_names = ("ebuild",)
_files_dict = slot_dict_class(_file_names, prefix="")
settings.setcpv(self.cpv)
ebuild_path = self.ebuild_hash.location
- # the caller can pass in eapi in order to avoid
- # redundant _parse_eapi_ebuild_head calls
- eapi = self.eapi
- if eapi is None and \
- 'parse-eapi-ebuild-head' in settings.features:
- with io.open(_unicode_encode(ebuild_path,
- encoding=_encodings['fs'], errors='strict'),
- mode='r', encoding=_encodings['repo.content'],
- errors='replace') as f:
- eapi = portage._parse_eapi_ebuild_head(f)
-
- if eapi is not None:
- if not portage.eapi_is_supported(eapi):
- self.metadata = self.metadata_callback(self.cpv,
- self.repo_path, {'EAPI' : eapi}, self.ebuild_hash)
- self._set_returncode((self.pid, os.EX_OK << 8))
- self.wait()
- return
-
- settings.configdict['pkg']['EAPI'] = eapi
+ with io.open(_unicode_encode(ebuild_path,
+ encoding=_encodings['fs'], errors='strict'),
+ mode='r', encoding=_encodings['repo.content'],
+ errors='replace') as f:
+ self._eapi, self._eapi_lineno = portage._parse_eapi_ebuild_head(f)
+
+ parsed_eapi = self._eapi
+ if parsed_eapi is None:
+ parsed_eapi = "0"
+
+ if not parsed_eapi:
+ # An empty EAPI setting is invalid.
+ self._eapi_invalid(None)
+ self._set_returncode((self.pid, 1 << 8))
+ self.wait()
+ return
+
+ if not portage.eapi_is_supported(parsed_eapi):
+ self.metadata = self.metadata_callback(self.cpv,
+ self.repo_path, {'EAPI' : parsed_eapi}, self.ebuild_hash)
+ self._set_returncode((self.pid, os.EX_OK << 8))
+ self.wait()
+ return
+
+ settings.configdict['pkg']['EAPI'] = parsed_eapi
debug = settings.get("PORTAGE_DEBUG") == "1"
master_fd = None
# number of lines is incorrect.
self.returncode = 1
else:
- metadata = zip(portage.auxdbkeys, metadata_lines)
- self.metadata = self.metadata_callback(self.cpv,
- self.repo_path, metadata, self.ebuild_hash)
+ metadata_valid = True
+ metadata = dict(zip(portage.auxdbkeys, metadata_lines))
+ parsed_eapi = self._eapi
+ if parsed_eapi is None:
+ parsed_eapi = "0"
+ if portage.eapi_is_supported(metadata["EAPI"]) and \
+ metadata["EAPI"] != parsed_eapi:
+ self._eapi_invalid(metadata)
+ if 'parse-eapi-ebuild-head' in self.settings.features:
+ metadata_valid = False
+
+ if metadata_valid:
+ self.metadata = self.metadata_callback(self.cpv,
+ self.repo_path, metadata, self.ebuild_hash)
+ else:
+ self.returncode = 1
+
+ def _eapi_invalid(self, metadata):
+
+ repo_name = self.portdb.getRepositoryName(self.repo_path)
+ msg = []
+ msg.extend(textwrap.wrap(("EAPI assignment in ebuild '%s%s%s' does not"
+ " conform with PMS section 7.3.1:") %
+ (self.cpv, _repo_separator, repo_name), 70))
+
+ if not self._eapi:
+ # None means the assignment was not found, while an
+ # empty string indicates an (invalid) empty assingment.
+ msg.append(
+ "\tvalid EAPI assignment must"
+ " occur on or before line: %s" %
+ self._eapi_lineno)
+ else:
+ msg.append(("\tbash returned EAPI '%s' which does not match "
+ "assignment on line: %s") %
+ (metadata["EAPI"], self._eapi_lineno))
+
+ if 'parse-eapi-ebuild-head' in self.settings.features:
+ msg.extend(textwrap.wrap(("NOTE: This error will soon"
+ " become unconditionally fatal in a future version of Portage,"
+ " but at this time, it can by made non-fatal by setting"
+ " FEATURES=-parse-eapi-ebuild-head in"
+ " make.conf."), 70))
+ else:
+ msg.extend(textwrap.wrap(("NOTE: This error will soon"
+ " become unconditionally fatal in a future version of Portage."
+ " At the earliest opportunity, please enable"
+ " FEATURES=parse-eapi-ebuild-head in make.conf in order to"
+ " make this error fatal."), 70))
+
+ for line in msg:
+ eerror(line, phase="other", key=self.cpv)
+ elog_process(self.cpv, self.settings,
+ phasefilter=("other",))
from portage.localization import _
from portage import os
from portage import shutil
-from portage import _unicode_decode
+from portage import eapi_is_supported, _unicode_decode
from portage.cache.cache_errors import CacheError
from portage.const import GLOBAL_CONFIG_PATH
from portage.const import _ENABLE_DYN_LINK_MAP, _ENABLE_SET_CONFIG
if onProgress is not None:
onProgress(maxval, curval)
- from portage import eapi_is_supported, \
- _validate_cache_for_unsupported_eapis
-
# TODO: Display error messages, but do not interfere with the progress bar.
# Here's how:
# 1) erase the progress bar
eapi = eapi.lstrip('-')
eapi_supported = eapi_is_supported(eapi)
if not eapi_supported:
- if not _validate_cache_for_unsupported_eapis:
- continue
+ continue
dest = None
try:
return False
return eapi <= portage.const.EAPI
-# Generally, it's best not to assume that cache entries for unsupported EAPIs
-# can be validated. However, the current package manager specification does not
-# guarantee that the EAPI can be parsed without sourcing the ebuild, so
-# it's too costly to discard existing cache entries for unsupported EAPIs.
-# Therefore, by default, assume that cache entries for unsupported EAPIs can be
-# validated. If FEATURES=parse-eapi-* is enabled, this assumption is discarded
-# since the EAPI can be determined without the incurring the cost of sourcing
-# the ebuild.
-_validate_cache_for_unsupported_eapis = True
-
-_parse_eapi_ebuild_head_re = re.compile(r'^EAPI=[\'"]?([^\'"#]*)')
-_parse_eapi_ebuild_head_max_lines = 30
+# This pattern is specified by PMS section 7.3.1.
+_pms_eapi_re = re.compile(r"^[ \t]*EAPI=(['\"]?)([A-Za-z0-9+_.-]*)\1[ \t]*(#.*)?$")
+_comment_or_blank_line = re.compile(r"^\s*(#.*)?$")
def _parse_eapi_ebuild_head(f):
- count = 0
+ eapi = None
+ eapi_lineno = None
+ lineno = 0
for line in f:
- m = _parse_eapi_ebuild_head_re.match(line)
- if m is not None:
- return m.group(1).strip()
- count += 1
- if count >= _parse_eapi_ebuild_head_max_lines:
+ lineno += 1
+ m = _comment_or_blank_line.match(line)
+ if m is None:
+ eapi_lineno = lineno
+ m = _pms_eapi_re.match(line)
+ if m is not None:
+ eapi = m.group(2)
break
- return '0'
+
+ return (eapi, eapi_lineno)
def _movefile(src, dest, **kwargs):
"""Calls movefile and raises a PortageException if an error occurs."""
raise KeyError(mycpv)
self.doebuild_settings.setcpv(mycpv)
- eapi = None
-
- if eapi is None and \
- 'parse-eapi-ebuild-head' in self.doebuild_settings.features:
- with io.open(_unicode_encode(myebuild,
- encoding=_encodings['fs'], errors='strict'),
- mode='r', encoding=_encodings['repo.content'],
- errors='replace') as f:
- eapi = portage._parse_eapi_ebuild_head(f)
-
- if eapi is not None:
- self.doebuild_settings.configdict['pkg']['EAPI'] = eapi
-
- if eapi is not None and not portage.eapi_is_supported(eapi):
- mydata = self._metadata_callback(
- mycpv, mylocation, {'EAPI':eapi}, ebuild_hash)
- else:
- proc = EbuildMetadataPhase(cpv=mycpv, eapi=eapi,
- ebuild_hash=ebuild_hash,
- metadata_callback=self._metadata_callback, portdb=self,
- repo_path=mylocation,
- scheduler=PollScheduler().sched_iface,
- settings=self.doebuild_settings)
- proc.start()
- proc.wait()
+ proc = EbuildMetadataPhase(cpv=mycpv,
+ ebuild_hash=ebuild_hash,
+ metadata_callback=self._metadata_callback, portdb=self,
+ repo_path=mylocation,
+ scheduler=PollScheduler().sched_iface,
+ settings=self.doebuild_settings)
+
+ proc.start()
+ proc.wait()
- if proc.returncode != os.EX_OK:
- self._broken_ebuilds.add(myebuild)
- raise KeyError(mycpv)
+ if proc.returncode != os.EX_OK:
+ self._broken_ebuilds.add(myebuild)
+ raise KeyError(mycpv)
- mydata = proc.metadata
+ mydata = proc.metadata
mydata["repository"] = self.repositories.get_name_for_location(mylocation)
mydata["_mtime_"] = ebuild_hash.mtime
if bsd_chflags:
self.features.add('chflags')
- if 'parse-eapi-ebuild-head' in self.features:
- portage._validate_cache_for_unsupported_eapis = False
-
self._iuse_implicit_match = _iuse_implicit_match_cache(self)
self._validate_commands()
# when uninstalling a package that has corrupt EAPI metadata.
eapi = None
if mydo == 'depend' and 'EAPI' not in mysettings.configdict['pkg']:
- if eapi is None and 'parse-eapi-ebuild-head' in mysettings.features:
+ if eapi is None:
with io.open(_unicode_encode(ebuild_path,
encoding=_encodings['fs'], errors='strict'),
mode='r', encoding=_encodings['content'],
errors='replace') as f:
- eapi = _parse_eapi_ebuild_head(f)
+ eapi, eapi_lineno = _parse_eapi_ebuild_head(f)
+ if eapi is None:
+ eapi = "0"
if eapi is not None:
if not eapi_is_supported(eapi):
import re
import time
import repoman.errors as errors
+import portage
from portage.eapi import eapi_supports_prefix, eapi_has_implicit_rdepend, \
eapi_has_src_prepare_and_src_configure, eapi_has_dosed_dohard, \
eapi_exports_AA, eapi_exports_KV
class EapiDefinition(LineCheck):
"""
- Check that EAPI assignment conforms to PMS section 8.3.1
+ Check that EAPI assignment conforms to PMS section 7.3.1
(first non-comment, non-blank line).
"""
repoman_check_name = 'EAPI.definition'
ignore_comment = True
-
- # This pattern is specified by PMS section 8.3.1.
- _eapi_re = re.compile(r"^[ \t]*EAPI=(['\"]?)([A-Za-z0-9+_.-]*)\1[ \t]*(#.*)?$")
+ _eapi_re = portage._pms_eapi_re
def new(self, pkg):
self._cached_eapi = pkg.metadata['EAPI']