aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2018-03-23 10:31:27 -0700
committerZac Medico <zmedico@gentoo.org>2018-03-27 23:28:17 -0700
commit3416876c0ee7301ab7434e3d9928ab7629a784ef (patch)
treeb6b01183f75c3da565e505ccdec25d991dfcb4f7
parentEbuildPhase: add PackagePhase class for PKG_INSTALL_MASK (diff)
downloadportage-3416876c0ee7301ab7434e3d9928ab7629a784ef.tar.gz
portage-3416876c0ee7301ab7434e3d9928ab7629a784ef.tar.bz2
portage-3416876c0ee7301ab7434e3d9928ab7629a784ef.zip
{,PKG_}INSTALL_MASK: python implementation
The InstallMask.match code comes from the dblink _is_install_masked method from portage-mgorny: https://github.com/mgorny/portage/commit/f5ac3af3216d209618a2f232b4bf720bc8b520ad
-rwxr-xr-xbin/misc-functions.sh73
-rw-r--r--pym/_emerge/PackagePhase.py13
-rw-r--r--pym/portage/dbapi/vartree.py22
-rw-r--r--pym/portage/util/install_mask.py123
4 files changed, 152 insertions, 79 deletions
diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 3c01280c7..26f589915 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -323,72 +323,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"
@@ -512,13 +446,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 ed8256ee0..859f254be 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()
+ self._pkg_install_mask = InstallMask(f.read())
except EnvironmentError:
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..378d42dc0 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',
@@ -3845,11 +3846,22 @@ class dblink(object):
# 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 EnvironmentError:
+ 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..506e63c1f
--- /dev/null
+++ b/pym/portage/util/install_mask.py
@@ -0,0 +1,123 @@
+# 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 sys
+
+from portage import os, _unicode_decode
+from portage.exception import (
+ OperationNotPermitted, PermissionDenied, FileNotFound)
+from portage.util import normalize_path
+
+if sys.hexversion >= 0x3000000:
+ _unicode = str
+else:
+ _unicode = unicode
+
+
+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(_unicode(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):
+ try:
+ parent = _unicode_decode(parent, errors='strict')
+ except UnicodeDecodeError:
+ continue
+ dir_stack.append(parent)
+ for fname in files:
+ try:
+ fname = _unicode_decode(fname, errors='strict')
+ except UnicodeDecodeError:
+ continue
+ 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