The InstallMask.match code comes from the dblink _is_install_masked
method from portage-mgorny:

https://github.com/mgorny/portage/commit/f5ac3af3216d209618a2f232b4bf720bc8b520ad
---
 bin/misc-functions.sh            |  73 --------------------------
 pym/_emerge/PackagePhase.py      |  13 ++++-
 pym/portage/dbapi/vartree.py     |  24 ++++++---
 pym/portage/util/install_mask.py | 109 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 139 insertions(+), 80 deletions(-)
 create mode 100644 pym/portage/util/install_mask.py

diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 4fe6ead17..ea2557724 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -317,72 +317,6 @@ postinst_qa_check() {
        done < <(printf "%s\0" "${qa_checks[@]}" | LC_ALL=C sort -u -z)
 }
 
-install_mask() {
-       local root="$1"
-       shift
-       local install_mask="$*"
-
-       # We think of $install_mask as a space-separated list of
-       # globs. We don't want globbing in the "for" loop; that is, we
-       # want to keep the asterisks in the indivual entries.
-       local shopts=$-
-       set -o noglob
-       local no_inst
-       for no_inst in ${install_mask}; do
-               # Here, $no_inst is a single "entry" potentially
-               # containing a glob. From now on, we *do* want to
-               # expand it.
-               set +o noglob
-
-               # The standard case where $no_inst is something that
-               # the shell could expand on its own.
-               if [[ -e "${root}"/${no_inst} || -L "${root}"/${no_inst} ||
-                       "${root}"/${no_inst} != $(echo "${root}"/${no_inst}) ]] 
; then
-                       __quiet_mode || einfo "Removing ${no_inst}"
-                       rm -Rf "${root}"/${no_inst} >&/dev/null
-               fi
-
-               # We also want to allow the user to specify a "bare
-               # glob." For example, $no_inst="*.a" should prevent
-               # ALL files ending in ".a" from being installed,
-               # regardless of their location/depth. We achieve this
-               # by passing the pattern to `find`.
-               find "${root}" \( -path "${no_inst}" -or -name "${no_inst}" \) \
-                       -print0 2> /dev/null \
-               | LC_ALL=C sort -z \
-               | while read -r -d ''; do
-                       __quiet_mode || einfo "Removing /${REPLY#${root}}"
-                       rm -Rf "${REPLY}" >&/dev/null
-               done
-
-       done
-       # set everything back the way we found it
-       set +o noglob
-       set -${shopts}
-}
-
-preinst_mask() {
-       if [ -z "${D}" ]; then
-                eerror "${FUNCNAME}: D is unset"
-                return 1
-       fi
-
-       if ! ___eapi_has_prefix_variables; then
-               local ED=${D}
-       fi
-
-       # Make sure $PWD is not ${D} so that we don't leave gmon.out files
-       # in there in case any tools were built with -pg in CFLAGS.
-       cd "${T}"
-
-       install_mask "${ED}" "${INSTALL_MASK}"
-
-       # remove share dir if unnessesary
-       if has nodoc $FEATURES || has noman $FEATURES || has noinfo $FEATURES; 
then
-               rmdir "${ED%/}/usr/share" &> /dev/null
-       fi
-}
-
 preinst_sfperms() {
        if [ -z "${D}" ]; then
                 eerror "${FUNCNAME}: D is unset"
@@ -506,13 +440,6 @@ __dyn_package() {
        # in there in case any tools were built with -pg in CFLAGS.
        cd "${T}" || die
 
-       if [[ -n ${PKG_INSTALL_MASK} ]] ; then
-               # The caller makes ${D} refer to a temporary copy in this
-               # case, so that this does not mask files from the normal
-               # install image.
-               install_mask "${D%/}${EPREFIX}/" "${PKG_INSTALL_MASK}"
-       fi
-
        local tar_options=""
        [[ $PORTAGE_VERBOSE = 1 ]] && tar_options+=" -v"
        has xattr ${FEATURES} && [[ $(tar --help 2> /dev/null) == *--xattrs* ]] 
&& tar_options+=" --xattrs"
diff --git a/pym/_emerge/PackagePhase.py b/pym/_emerge/PackagePhase.py
index 083745059..35137532a 100644
--- a/pym/_emerge/PackagePhase.py
+++ b/pym/_emerge/PackagePhase.py
@@ -11,6 +11,8 @@ import portage
 from portage import os
 from portage import _encodings
 from portage import _unicode_encode
+from portage.util._async.AsyncFunction import AsyncFunction
+from portage.util.install_mask import install_mask_dir, InstallMask
 
 
 class PackagePhase(CompositeTask):
@@ -31,7 +33,7 @@ class PackagePhase(CompositeTask):
                                encoding=_encodings['fs'], errors='strict'),
                                mode='r', encoding=_encodings['repo.content'],
                                errors='replace') as f:
-                               self._pkg_install_mask = f.read().split()
+                               self._pkg_install_mask = InstallMask(f.read())
                except OSError:
                        self._pkg_install_mask = None
                if self._pkg_install_mask:
@@ -51,6 +53,15 @@ class PackagePhase(CompositeTask):
                if self._default_exit(proc) != os.EX_OK:
                        self.wait()
                else:
+                       self._start_task(AsyncFunction(
+                               target=install_mask_dir,
+                               args=(self._proot, self._pkg_install_mask)),
+                               self._pkg_install_mask_exit)
+
+       def _pkg_install_mask_exit(self, proc):
+               if self._default_exit(proc) != os.EX_OK:
+                       self.wait()
+               else:
                        self._start_package_phase()
 
        def _start_package_phase(self):
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index bed76d80f..bf7bd8c2a 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -32,6 +32,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
                'grabdict,normalize_path,new_protect_filename',
        'portage.util.digraph:digraph',
        'portage.util.env_update:env_update',
+       'portage.util.install_mask:install_mask_dir,InstallMask',
        'portage.util.listdir:dircache,listdir',
        'portage.util.movefile:movefile',
        'portage.util.path:first_existing,iter_parents',
@@ -3841,15 +3842,26 @@ class dblink(object):
                                        max_dblnk = dblnk
                        self._installed_instance = max_dblnk
 
-               # Apply INSTALL_MASK before collision-protect, since it may
+               # Update INSTALL_MASK before collision-protect, since it may
                # be useful to avoid collisions in some scenarios.
                # We cannot detect if this is needed or not here as 
INSTALL_MASK can be
                # modified by bashrc files.
-               phase = MiscFunctionsProcess(background=False,
-                       commands=["preinst_mask"], phase="preinst",
-                       scheduler=self._scheduler, settings=self.settings)
-               phase.start()
-               phase.wait()
+               try:
+                       with io.open(_unicode_encode(os.path.join(inforoot, 
"INSTALL_MASK"),
+                               encoding=_encodings['fs'], errors='strict'),
+                               mode='r', encoding=_encodings['repo.content'],
+                               errors='replace') as f:
+                               install_mask = InstallMask(f.read())
+               except OSError:
+                       install_mask = None
+
+               if install_mask:
+                       install_mask_dir(self.settings["ED"], install_mask)
+                       if any(x in self.settings.features for x in ('nodoc', 
'noman', 'noinfo')):
+                               try:
+                                       
os.rmdir(os.path.join(self.settings["ED"], 'usr', 'share'))
+                               except OSError:
+                                       pass
 
                # We check for unicode encoding issues after src_install. 
However,
                # the check must be repeated here for binary packages (it's
diff --git a/pym/portage/util/install_mask.py b/pym/portage/util/install_mask.py
new file mode 100644
index 000000000..64fe0b21a
--- /dev/null
+++ b/pym/portage/util/install_mask.py
@@ -0,0 +1,109 @@
+# Copyright 2018 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__all__ = ['install_mask_dir', 'InstallMask']
+
+import errno
+import fnmatch
+import os
+
+from portage.exception import (
+       OperationNotPermitted, PermissionDenied, FileNotFound)
+from portage.util import normalize_path
+
+
+class InstallMask(object):
+       def __init__(self, install_mask):
+               """
+               @param install_mask: INSTALL_MASK value
+               @type install_mask: str
+               """
+               self._install_mask = install_mask.split()
+
+       def match(self, path):
+               """
+               @param path: file path relative to ${ED}
+               @type path: str
+               @rtype: bool
+               @return: True if path matches INSTALL_MASK, False otherwise
+               """
+               ret = False
+               for pattern in self._install_mask:
+                       # absolute path pattern
+                       if pattern.startswith('/'):
+                               # match either exact path or one of parent dirs
+                               # the latter is done via matching pattern/*
+                               if (fnmatch.fnmatch(path, pattern[1:])
+                                               or fnmatch.fnmatch(path, 
pattern[1:] + '/*')):
+                                       ret = True
+                                       break
+                       # filename
+                       else:
+                               if fnmatch.fnmatch(os.path.basename(path), 
pattern):
+                                       ret = True
+                                       break
+               return ret
+
+
+_exc_map = {
+       errno.ENOENT: FileNotFound,
+       errno.EPERM: OperationNotPermitted,
+       errno.EACCES: PermissionDenied,
+}
+
+
+def _raise_exc(e):
+       """
+       Wrap OSError with portage.exception wrapper exceptions, with
+       __cause__ chaining when python supports it.
+
+       @param e: os exception
+       @type e: OSError
+       @raise PortageException: portage.exception wrapper exception
+       """
+       wrapper_cls = _exc_map.get(e.errno)
+       if wrapper_cls is None:
+               raise
+       wrapper = wrapper_cls(str(e))
+       wrapper.__cause__ = e
+       raise wrapper
+
+
+def install_mask_dir(base_dir, install_mask, onerror=None):
+       """
+       Remove files and directories matched by INSTALL_MASK.
+
+       @param base_dir: directory path corresponding to ${ED}
+       @type base_dir: str
+       @param install_mask: INSTALL_MASK configuration
+       @type install_mask: InstallMask
+       """
+       onerror = onerror or _raise_exc
+       base_dir = normalize_path(base_dir)
+       base_dir_len = len(base_dir) + 1
+       dir_stack = []
+
+       # Remove masked files.
+       for parent, dirs, files in os.walk(base_dir, onerror=onerror):
+               dir_stack.append(parent)
+               for fname in files:
+                       abs_path = os.path.join(parent, fname)
+                       relative_path = abs_path[base_dir_len:]
+                       if install_mask.match(relative_path):
+                               try:
+                                       os.unlink(abs_path)
+                               except OSError as e:
+                                       onerror(e)
+
+       # Remove masked dirs (unless non-empty due to exclusions).
+       while True:
+               try:
+                       dir_path = dir_stack.pop()
+               except IndexError:
+                       break
+
+               if install_mask.match(dir_path[base_dir_len:]):
+                       try:
+                               os.rmdir(dir_path)
+                       except OSError:
+                               pass
-- 
2.13.6


Reply via email to