Source code for pkgcore.ebuild.eapi

import os
import re
import subprocess
import sys
from collections import defaultdict
from functools import partial
from weakref import WeakValueDictionary

from snakeoil import klass
from snakeoil.demandload import demand_compile_regexp
from snakeoil.mappings import ImmutableDict, OrderedFrozenSet, inject_getitem_as_getattr
from snakeoil.osutils import pjoin
from snakeoil.process.spawn import bash_version

LATEST_PMS_EAPI_VER = "8"


[docs] def get_latest_PMS_eapi(): """return the latest PMS EAPI object known to this version of pkgcore""" return get_eapi(LATEST_PMS_EAPI_VER)
from ..log import logger from . import atom, const demand_compile_regexp("_valid_EAPI_regex", r"^[A-Za-z0-9_][A-Za-z0-9+_.-]*$") demand_compile_regexp("_valid_use_flag", r"^[A-Za-z0-9][A-Za-z0-9+_@-]*$") eapi_optionals = ImmutableDict( { # Controls whether PROPERTIES and RESTRICT are accumulated across eclasses. "accumulate_properties_restrict": False, # Controls what version of bash compatibility to force; see PMS. "bash_compat": "3.2", # Controls whether -r is allowed for dodoc. "dodoc_allow_recursive": False, # Controls the language awareness of doman; see PMS. "doman_language_detect": False, # Controls whether -i18n option is allowed. "doman_language_override": False, # Controls whether dosym -r option is allowed. "dosym_relative": False, # Controls whether an ebuild_phase function exists for ebuild consumption. "ebuild_phase_func": False, # Controls whether REPLACING vars are exported to ebuilds; see PMS. "exports_replacing": False, # Controls of whether failglob is enabled globally; see PMS. "global_failglob": False, # Controls whether MERGE vars are exported to ebuilds; see PMS. "has_merge_type": False, # Controls whether PORTDIR and ECLASSDIR are exported to ebuilds; see PMS. "has_portdir": True, # Controls whether DESTTREE and INSDESTTREE are exported during src_install; see PMS. "has_desttree": True, # Controls whether atoms support USE dependencies. "has_use_deps": False, # Controls wheter atoms support slot dependencies", "has_slot_deps": False, # Controls whether ROOT, EROOT, D, and ED end with a trailing slash; see PMS. "trailing_slash": os.sep, # Controls whether SYSROOT, ESYSROOT, and BROOT are defined; see PMS. "has_sysroot": False, # Controls whether package.provided files in profiles are supported; see PMS. "profile_pkg_provided": True, # Controls whether package.mask and other files in profiles can # be directories; see PMS. "has_profile_data_dirs": False, # Controls whether REQUIRED_USE is supported, enforcing constraints on # allowed use configuration states. "has_required_use": False, # Controls whether USE dependency defaults are supported, see PMS. "has_use_dep_defaults": False, # Controls whether ENV_UNSET is supported, see PMS. "has_env_unset": False, # Controls whether AA env var is exported to ebuilds; this is a flattened # listing of each filename in SRC_URI. "has_AA": True, # Controls whether KV (kernel version; see PMS for details) is exported. "has_KV": True, # Controls whether or not pkgcore, or extensions loaded, actually fully # support this EAPI. "is_supported": True, # Controls whether IUSE defaults are supported; see PMS. "iuse_defaults": False, # Controls whether new* style bash functions can take their content input # from stdin, rather than an explicit ondisk file. "new_reads_stdin": False, # Controls whether utilities die on failure; see PMS. "nonfatal": True, # Controls whether die supports a nonfatal option; see PMS. "nonfatal_die": False, # Controls whether this EAPI supports prefix related variables/settings; # prefix awareness basically. See PMS for full details. "prefix_capable": False, # Controls whether profile-defined IUSE injection is supported. "profile_iuse_injection": False, # Controls whether profiles support package.use.stable.* and use.stable.* files. "profile_stable_use": False, # Controls whether has_version/best_version supports --host-root option; see PMS. "query_host_root": False, # Controls whether has_version/best_version supports -b/-d/-r options; see PMS. "query_deps": False, # Controls whether SLOT values can actually be multi-part; see PMS EAPI 5. # This is related to ABI breakage detection. "sub_slotting": False, # Controls whether REQUIRED_USE supports the ?? operator. "required_use_one_of": False, # Controls whether SRC_URI supports the '->' operator for url filename renaming. "src_uri_renames": False, # Controls whether SRC_URI supports fetch+ and mirror+ prefixes. "src_uri_unrestrict": False, # Controls whether strong blockers- hard deps of "things are broken after this merge" are supported for atom syntax. "strong_blockers": False, # Controls whether or not use dependency atoms are able to control their enforced # value relative to another; standard use deps just enforce either on or off; EAPIs # supporting this allow syntax that can enforce (for example) X to be on if Y is on. # See PMS EAPI 4 for full details. "transitive_use_atoms": False, # Controls whether or DEFINED_PHASES is mandated for this EAPI; if so, then we can # trust the cache definition and skip invoking those phases if they're not defined. # If the EAPI didn't mandate this var, then we can do our inference, but generally will # invoke the phase in the absense of that metadata var since we have no other choice. "trust_defined_phases_cache": False, # Controls whether unpack supports absolute paths; see PMS. "unpack_absolute_paths": False, # Controls whether unpack supports absolute paths; see PMS. "unpack_case_insensitive": False, # Regular expression to filter out valid update files "update_regex": re.compile(r"^([1-4])Q-(\d{4})$"), # Controls whether user patches are supported. "user_patches": False, } ) class _optionals_cls(ImmutableDict): inject_getitem_as_getattr(locals())
[docs] class EAPI(metaclass=klass.immutable_instance): known_eapis = WeakValueDictionary() unknown_eapis = WeakValueDictionary() def __init__( self, magic, parent=None, phases=(), default_phases=(), mandatory_keys=(), dep_keys=(), metadata_keys=(), eclass_keys=(), tracked_attributes=(), archive_exts=(), optionals=None, ebd_env_options=None, ): sf = object.__setattr__ sf(self, "_magic", str(magic)) sf(self, "_parent", parent) sf(self, "phases", ImmutableDict(phases)) sf(self, "phases_rev", ImmutableDict((v, k) for k, v in self.phases.items())) # We track the phases that have a default implementation- this is # primarily due to DEFINED_PHASES cache values not including it. sf(self, "default_phases", frozenset(default_phases)) sf(self, "mandatory_keys", frozenset(mandatory_keys)) sf(self, "dep_keys", frozenset(dep_keys)) sf( self, "metadata_keys", (self.mandatory_keys | self.dep_keys | frozenset(metadata_keys)), ) # variables that eclasses have access to (used by pkgcheck eclass inherit checks) sf( self, "eclass_keys", self.mandatory_keys | self.dep_keys | frozenset(eclass_keys), ) sf( self, "tracked_attributes", (frozenset(tracked_attributes) | frozenset(x.lower() for x in dep_keys)), ) sf(self, "archive_exts", frozenset(archive_exts)) if optionals is None: optionals = {} sf(self, "options", _optionals_cls(optionals)) if ebd_env_options is None: ebd_env_options = () sf(self, "_ebd_env_options", ebd_env_options)
[docs] @classmethod def register(cls, *args, **kwds): eapi = cls(*args, **kwds) pre_existing = cls.known_eapis.get(eapi._magic) if pre_existing is not None: raise ValueError( f"EAPI '{eapi}' is already known/instantiated- {pre_existing!r}" ) if ( getattr(eapi.options, "bash_compat", False) and bash_version() < eapi.options.bash_compat ): # hard exit if the system doesn't have an adequate bash installed raise SystemExit( f"EAPI '{eapi}' requires >=bash-{eapi.options.bash_compat}, " f"system version: {bash_version()}" ) cls.known_eapis[eapi._magic] = eapi # generate EAPI bash libs when running from git repo eapi.bash_libs() return eapi
@klass.jit_attr def is_supported(self): """Check if an EAPI is supported.""" if EAPI.known_eapis.get(self._magic) is not None: if not self.options.is_supported: logger.warning(f"EAPI '{self}' isn't fully supported") sys.stderr.flush() return True return False @klass.jit_attr def bash_funcs_global(self): """Internally implemented global EAPI specific functions to skip when exporting.""" # TODO: This is currently duplicated across EAPI objs, but # instead could be cached to a class attr. funcs = pjoin(const.EBD_PATH, ".generated", "funcs", "global") if not os.path.exists(funcs): # we're probably running in a cacheless git repo, so generate a cached version try: os.makedirs(os.path.dirname(funcs), exist_ok=True) with open(funcs, "w") as f: subprocess.run( [pjoin(const.EBD_PATH, "generate_global_func_list")], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate list of global EAPI '{self}' specific functions: {str(e)}" ) with open(funcs, "r") as f: return frozenset(line.strip() for line in f) @klass.jit_attr def bash_funcs(self): """Internally implemented EAPI specific functions to skip when exporting.""" funcs = pjoin(const.EBD_PATH, ".generated", "funcs", self._magic) if not os.path.exists(funcs): # we're probably running in a cacheless git repo, so generate a cached version try: os.makedirs(os.path.dirname(funcs), exist_ok=True) with open(funcs, "w") as f: subprocess.run( [pjoin(const.EBD_PATH, "generate_eapi_func_list"), self._magic], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate list of EAPI '{self}' specific functions: {str(e)}" ) with open(funcs, "r") as f: return frozenset(line.strip() for line in f) @klass.jit_attr def bash_cmds_internal(self): """EAPI specific commands for this EAPI.""" cmds = pjoin(const.EBD_PATH, ".generated", "cmds", self._magic, "internal") if not os.path.exists(cmds): # we're probably running in a cacheless git repo, so generate a cached version try: os.makedirs(os.path.dirname(cmds), exist_ok=True) with open(cmds, "w") as f: subprocess.run( [ pjoin(const.EBD_PATH, "generate_eapi_cmd_list"), "-i", self._magic, ], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate list of EAPI {self} internal commands: {str(e)}" ) with open(cmds, "r") as f: return frozenset(line.strip() for line in f) @klass.jit_attr def bash_cmds_deprecated(self): """EAPI specific commands deprecated for this EAPI.""" cmds = pjoin(const.EBD_PATH, ".generated", "cmds", self._magic, "deprecated") if not os.path.exists(cmds): # we're probably running in a cacheless git repo, so generate a cached version try: os.makedirs(os.path.dirname(cmds), exist_ok=True) with open(cmds, "w") as f: subprocess.run( [ pjoin(const.EBD_PATH, "generate_eapi_cmd_list"), "-d", self._magic, ], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate list of EAPI {self} deprecated commands: {str(e)}" ) with open(cmds, "r") as f: return frozenset(line.strip() for line in f) @klass.jit_attr def bash_cmds_banned(self): """EAPI specific commands banned for this EAPI.""" cmds = pjoin(const.EBD_PATH, ".generated", "cmds", self._magic, "banned") if not os.path.exists(cmds): # we're probably running in a cacheless git repo, so generate a cached version try: os.makedirs(os.path.dirname(cmds), exist_ok=True) with open(cmds, "w") as f: subprocess.run( [ pjoin(const.EBD_PATH, "generate_eapi_cmd_list"), "-b", self._magic, ], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate list of EAPI {self} banned commands: {str(e)}" ) with open(cmds, "r") as f: return frozenset(line.strip() for line in f)
[docs] def bash_libs(self): """Generate internally implemented EAPI specific bash libs required by the ebd.""" eapi_global_lib = pjoin( const.EBD_PATH, ".generated", "libs", self._magic, "global" ) script = pjoin(const.EBD_PATH, "generate_eapi_lib") # skip generation when installing as the install process takes care of it if not os.path.exists(script): return if not os.path.exists(eapi_global_lib): try: os.makedirs(os.path.dirname(eapi_global_lib), exist_ok=True) with open(eapi_global_lib, "w") as f: subprocess.run( [script, "-s", "global", self._magic], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate EAPI '{self}' global lib: {str(e)}" ) for phase in self.phases.values(): eapi_lib = pjoin(const.EBD_PATH, ".generated", "libs", self._magic, phase) if not os.path.exists(eapi_lib): try: os.makedirs(os.path.dirname(eapi_lib), exist_ok=True) with open(eapi_lib, "w") as f: subprocess.run( [script, "-s", phase, self._magic], cwd=const.EBD_PATH, stdout=f, ) except (IOError, subprocess.CalledProcessError) as e: raise Exception( f"failed to generate EAPI '{self}' phase {phase} lib: {str(e)}" )
@klass.jit_attr def archive_exts_regex_pattern(self): """Regex pattern for supported archive extensions.""" pattern = "|".join(map(re.escape, self.archive_exts)) if self.options.unpack_case_insensitive: return f"(?i:({pattern}))" return f"({pattern})" @klass.jit_attr def archive_exts_regex(self): """Regex matching strings ending with supported archive extensions.""" return re.compile(rf"{self.archive_exts_regex_pattern}$") @klass.jit_attr def valid_slot_regex(self): """Regex matching valid SLOT values.""" valid_slot = r"[A-Za-z0-9_][A-Za-z0-9+_.-]*" if self.options.sub_slotting: valid_slot += rf"(/{valid_slot})?" return re.compile(rf"^{valid_slot}$") @klass.jit_attr def atom_kls(self): return partial(atom.atom, eapi=self._magic)
[docs] def interpret_cache_defined_phases(self, sequence): phases = set(sequence) if not self.options.trust_defined_phases_cache: if not phases: # run them all; cache was generated # by a pm that didn't support DEFINED_PHASES return frozenset(self.phases) phases.discard("-") return frozenset(phases)
def __str__(self): return self._magic @klass.jit_attr def inherits(self): """Ordered set containing an EAPI's inheritance tree. Note that this assumes a simple, linear inheritance tree. """ eapis = [self] eapi = self while eapi := eapi._parent: eapis.append(eapi) return OrderedFrozenSet(eapis) @klass.jit_attr def helpers(self): """Phase to directory mapping for EAPI specific helpers to add to $PATH.""" paths = defaultdict(list) for eapi in self.inherits: paths["global"].append(pjoin(const.EBUILD_HELPERS_PATH, "common")) helper_dir = pjoin(const.EBUILD_HELPERS_PATH, eapi._magic) for dirpath, dirnames, filenames in os.walk(helper_dir): if not filenames: continue if dirpath == helper_dir: paths["global"].append(dirpath) else: phase = os.path.basename(dirpath) if phase in self.phases_rev: paths[phase].append(dirpath) else: raise ValueError(f"unknown phase: {phase!r}") return ImmutableDict((k, tuple(v)) for k, v in paths.items()) @klass.jit_attr def ebd_env(self): """Dictionary of EAPI options passed to the ebd environment.""" d = {} for k in self._ebd_env_options: d[f"PKGCORE_{k.upper()}"] = str(getattr(self.options, k)).lower() d["PKGCORE_EAPI_INHERITS"] = " ".join(x._magic for x in self.inherits) d["EAPI"] = self._magic return ImmutableDict(d)
[docs] def is_valid_use_flag(self, s: str) -> bool: """returns True if the flag is parsable under this EAPI""" return _valid_use_flag.match(s) is not None
[docs] def get_eapi(magic, suppress_unsupported=True): """Return EAPI object for a given identifier.""" if _valid_EAPI_regex.match(magic) is None: eapi_str = f" {magic!r}" if magic else "" raise ValueError(f"invalid EAPI{eapi_str}") eapi = EAPI.known_eapis.get(magic) if eapi is None and suppress_unsupported: eapi = EAPI.unknown_eapis.get(magic) if eapi is None: eapi = EAPI(magic=magic, optionals={"is_supported": False}) EAPI.unknown_eapis[eapi._magic] = eapi return eapi
def _shorten_phase_name(func_name): if func_name.startswith(("src_", "pkg_")): return func_name[4:] return func_name def _mk_phase_func_map(*sequence): return {_shorten_phase_name(x): x for x in sequence} def _combine_dicts(*mappings): return {k: v for d in mappings for k, v in d.items()} # Note that pkg_setup is forced by default since this is how our env setup occurs. common_default_phases = tuple( _shorten_phase_name(x) for x in ("pkg_setup", "src_unpack", "src_compile", "src_test", "pkg_nofetch") ) common_phases = ( "pkg_setup", "pkg_config", "pkg_info", "pkg_nofetch", "pkg_prerm", "pkg_postrm", "pkg_preinst", "pkg_postinst", "src_unpack", "src_compile", "src_test", "src_install", ) common_mandatory_metadata_keys = ( "DESCRIPTION", "HOMEPAGE", "IUSE", "KEYWORDS", "LICENSE", "SLOT", "SRC_URI", ) common_dep_keys = ( "DEPEND", "RDEPEND", "PDEPEND", ) common_metadata_keys = ( "RESTRICT", "PROPERTIES", "DEFINED_PHASES", "INHERIT", "INHERITED", "EAPI", ) common_eclass_keys = ("S", "RESTRICT", "PROPERTIES", "ECONF_SOURCE") common_tracked_attributes = ( "cflags", "cbuild", "chost", "ctarget", "cxxflags", "defined_phases", "description", "eapi", "distfiles", "fullslot", "homepage", "inherited", "iuse", "keywords", "ldflags", "license", "properties", "restrict", "source_repository", ) common_archive_exts = ( ".tar", ".tar.gz", ".tgz", ".tar.Z", ".tar.z", ".tar.bz2", ".tbz2", ".tbz", ".zip", ".ZIP", ".jar", ".gz", ".Z", ".z", ".bz2", ".rar", ".RAR", ".lha", ".LHa", ".LHA", ".lzh", ".a", ".deb", ".tar.lzma", ".lzma", ".7z", ".7Z", ) # Boolean variables exported to the bash side, e.g. ebuild_phase_func is # exported as PKGCORE_EBUILD_PHASE_FUNC. common_env_optionals = ( "bash_compat", "ebuild_phase_func", "global_failglob", "new_reads_stdin", "nonfatal", "nonfatal_die", "has_desttree", "accumulate_properties_restrict", ) eapi0 = EAPI.register( magic="0", parent=None, phases=_mk_phase_func_map(*common_phases), default_phases=_mk_phase_func_map(*common_default_phases), mandatory_keys=common_mandatory_metadata_keys, dep_keys=common_dep_keys, metadata_keys=common_metadata_keys, eclass_keys=common_eclass_keys, tracked_attributes=common_tracked_attributes, archive_exts=common_archive_exts, optionals=eapi_optionals, ebd_env_options=common_env_optionals, ) eapi1 = EAPI.register( magic="1", parent=eapi0, phases=eapi0.phases, default_phases=eapi0.default_phases, mandatory_keys=eapi0.mandatory_keys, dep_keys=eapi0.dep_keys, metadata_keys=eapi0.metadata_keys, eclass_keys=eapi0.eclass_keys, tracked_attributes=eapi0.tracked_attributes, archive_exts=eapi0.archive_exts, optionals=_combine_dicts( eapi0.options, dict( iuse_defaults=True, has_slot_deps=True, ), ), ebd_env_options=eapi0._ebd_env_options, ) eapi2 = EAPI.register( magic="2", parent=eapi1, phases=_combine_dicts( eapi1.phases, _mk_phase_func_map("src_prepare", "src_configure") ), default_phases=eapi1.default_phases.union( list(map(_shorten_phase_name, ["src_prepare", "src_configure"])) ), mandatory_keys=eapi1.mandatory_keys, dep_keys=eapi1.dep_keys, metadata_keys=eapi1.metadata_keys, eclass_keys=eapi1.eclass_keys, tracked_attributes=eapi1.tracked_attributes, archive_exts=eapi1.archive_exts, optionals=_combine_dicts( eapi1.options, dict( has_use_deps=True, strong_blockers=True, doman_language_detect=True, transitive_use_atoms=True, src_uri_renames=True, ), ), ebd_env_options=eapi1._ebd_env_options, ) eapi3 = EAPI.register( magic="3", parent=eapi2, phases=eapi2.phases, default_phases=eapi2.default_phases, mandatory_keys=eapi2.mandatory_keys, dep_keys=eapi2.dep_keys, metadata_keys=eapi2.metadata_keys, eclass_keys=eapi2.eclass_keys | frozenset(["EPREFIX", "ED", "EROOT"]), tracked_attributes=eapi2.tracked_attributes, archive_exts=eapi2.archive_exts | frozenset([".tar.xz", ".xz"]), optionals=_combine_dicts( eapi2.options, dict( prefix_capable=True, ), ), ebd_env_options=eapi2._ebd_env_options, ) eapi4 = EAPI.register( magic="4", parent=eapi3, phases=_combine_dicts(eapi3.phases, _mk_phase_func_map("pkg_pretend")), default_phases=eapi3.default_phases.union([_shorten_phase_name("src_install")]), mandatory_keys=eapi3.mandatory_keys, dep_keys=eapi3.dep_keys, metadata_keys=eapi3.metadata_keys | frozenset(["REQUIRED_USE"]), eclass_keys=eapi3.eclass_keys | frozenset(["DOCS", "REQUIRED_USE"]), tracked_attributes=eapi3.tracked_attributes, archive_exts=eapi3.archive_exts, optionals=_combine_dicts( eapi3.options, dict( dodoc_allow_recursive=True, doman_language_override=True, nonfatal=False, exports_replacing=True, has_AA=False, has_KV=False, has_merge_type=True, has_required_use=True, has_use_dep_defaults=True, trust_defined_phases_cache=True, ), ), ebd_env_options=eapi3._ebd_env_options, ) eapi5 = EAPI.register( magic="5", parent=eapi4, phases=eapi4.phases, default_phases=eapi4.default_phases, mandatory_keys=eapi4.mandatory_keys, dep_keys=eapi4.dep_keys, metadata_keys=eapi4.metadata_keys, eclass_keys=eapi4.eclass_keys, tracked_attributes=eapi4.tracked_attributes | frozenset(["iuse_effective"]), archive_exts=eapi4.archive_exts, optionals=_combine_dicts( eapi4.options, dict( ebuild_phase_func=True, profile_iuse_injection=True, profile_stable_use=True, query_host_root=True, new_reads_stdin=True, required_use_one_of=True, sub_slotting=True, ), ), ebd_env_options=eapi4._ebd_env_options, ) eapi6 = EAPI.register( magic="6", parent=eapi5, phases=eapi5.phases, default_phases=eapi5.default_phases, mandatory_keys=eapi5.mandatory_keys, dep_keys=eapi5.dep_keys, metadata_keys=eapi5.metadata_keys, eclass_keys=eapi5.eclass_keys | frozenset(["HTML_DOCS", "PATCHES"]), tracked_attributes=eapi5.tracked_attributes | frozenset(["user_patches"]), archive_exts=eapi5.archive_exts | frozenset([".txz"]), optionals=_combine_dicts( eapi5.options, dict( global_failglob=True, nonfatal_die=True, unpack_absolute_paths=True, unpack_case_insensitive=True, user_patches=True, bash_compat="4.2", ), ), ebd_env_options=eapi5._ebd_env_options, ) eapi7 = EAPI.register( magic="7", parent=eapi6, phases=eapi6.phases, default_phases=eapi6.default_phases, mandatory_keys=eapi6.mandatory_keys, dep_keys=eapi6.dep_keys | frozenset(["BDEPEND"]), metadata_keys=eapi6.metadata_keys, eclass_keys=eapi6.eclass_keys, tracked_attributes=eapi6.tracked_attributes, archive_exts=eapi6.archive_exts, optionals=_combine_dicts( eapi6.options, dict( has_profile_data_dirs=True, has_portdir=False, has_desttree=False, profile_pkg_provided=False, query_host_root=False, query_deps=True, has_sysroot=True, has_env_unset=True, trailing_slash="", ), ), ebd_env_options=eapi6._ebd_env_options, ) eapi8 = EAPI.register( magic="8", parent=eapi7, phases=eapi7.phases, default_phases=eapi7.default_phases, mandatory_keys=eapi7.mandatory_keys, dep_keys=eapi7.dep_keys | frozenset(["IDEPEND"]), metadata_keys=eapi7.metadata_keys, eclass_keys=eapi7.eclass_keys, tracked_attributes=eapi7.tracked_attributes, archive_exts=eapi7.archive_exts - frozenset( [ ".rar", ".RAR", ".lha", ".LHa", ".LHA", ".lzh", ".7z", ".7Z", ] ), optionals=_combine_dicts( eapi7.options, dict( accumulate_properties_restrict=True, bash_compat="5.0", dosym_relative=True, src_uri_unrestrict=True, update_regex=re.compile(r"^[^.]"), ), ), ebd_env_options=eapi7._ebd_env_options, )