public inbox for gentoo-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-dev] [PATCH 1/2] phase-functions: Rework INSTALL_MASK to apply as a merge filter
@ 2018-03-18 10:50 Michał Górny
  2018-03-18 10:50 ` [gentoo-dev] [PATCH 2/2] portage.dbapi.vartree: Support exclusions in INSTALL_MASK Michał Górny
  0 siblings, 1 reply; 2+ messages in thread
From: Michał Górny @ 2018-03-18 10:50 UTC (permalink / raw
  To: gentoo-dev; +Cc: Michał Górny

Rework the INSTALL_MASK handler to filter installed files while merging
instead of removing them from the installation image.

The INSTALL_MASK handling is now moved to Python side of code, with bash
being only used to evaluate the value of INSTALL_MASK (with respect to
bashrc).

The evaluated value of INSTALL_MASK is used to test for collisions,
and afterwards to skip masked files from being installed. It is also
used in uninstall code to properly remove newly-masked files that were
installed previously.
---
 bin/misc-functions.sh        |  13 ++--
 bin/phase-functions.sh       |   3 +-
 pym/portage/dbapi/vartree.py | 138 +++++++++++++++++++++++++++++++------------
 3 files changed, 108 insertions(+), 46 deletions(-)

diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 7643af7b5..5e11eadb4 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -383,12 +383,13 @@ preinst_mask() {
 		fi
 	done
 
-	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
+	# Store the final value of INSTALL_MASK in build-info
+	local x
+	set -f
+	local IFS=$' \t\n\r'
+	x=$(echo ${INSTALL_MASK})
+	[[ -n $x ]] && echo "$x" > "${PORTAGE_BUILDDIR}"/build-info/INSTALL_MASK
+	set +f
 }
 
 preinst_sfperms() {
diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh
index 3aae3ef56..7f7f6fa11 100644
--- a/bin/phase-functions.sh
+++ b/bin/phase-functions.sh
@@ -666,7 +666,8 @@ __dyn_install() {
 		ASFLAGS CBUILD CC CFLAGS CHOST CTARGET CXX \
 		CXXFLAGS EXTRA_ECONF EXTRA_EINSTALL EXTRA_MAKE \
 		LDFLAGS LIBCFLAGS LIBCXXFLAGS QA_CONFIGURE_OPTIONS \
-		QA_DESKTOP_FILE QA_PREBUILT PROVIDES_EXCLUDE REQUIRES_EXCLUDE ; do
+		QA_DESKTOP_FILE QA_PREBUILT PROVIDES_EXCLUDE REQUIRES_EXCLUDE \
+		PKG_INSTALL_MASK ; do
 
 		x=$(echo -n ${!f})
 		[[ -n $x ]] && echo "$x" > $f
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index bed76d80f..12137a0a4 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1876,6 +1876,7 @@ class dblink(object):
 			for pos, e in errors:
 				writemsg(_("!!!   line %d: %s\n") % (pos, e), noiselevel=-1)
 		self.contentscache = pkgfiles
+
 		return pkgfiles
 
 	def _prune_plib_registry(self, unmerge=False,
@@ -1953,7 +1954,7 @@ class dblink(object):
 	@_slot_locked
 	def unmerge(self, pkgfiles=None, trimworld=None, cleanup=True,
 		ldpath_mtimes=None, others_in_slot=None, needed=None,
-		preserve_paths=None):
+		preserve_paths=None, install_mask=[]):
 		"""
 		Calls prerm
 		Unmerges a given package (CPV)
@@ -1978,6 +1979,10 @@ class dblink(object):
 			LinkageMap, since they are not registered in the
 			PreservedLibsRegistry yet.
 		@type preserve_paths: set
+		@param install_mask: List of INSTALL_MASK values for the install
+			enforcing cleanup. This is needed to let unmerge() clean old
+			files that now are filtered via INSTALL_MASK.
+		@type install_mask: list
 		@rtype: Integer
 		@return:
 		1. os.EX_OK if everything went well.
@@ -2121,7 +2126,7 @@ class dblink(object):
 
 			self.vartree.dbapi._fs_lock()
 			try:
-				self._unmerge_pkgfiles(pkgfiles, others_in_slot)
+				self._unmerge_pkgfiles(pkgfiles, others_in_slot, install_mask)
 			finally:
 				self.vartree.dbapi._fs_unlock()
 			self._clear_contents_cache()
@@ -2267,7 +2272,7 @@ class dblink(object):
 		self._display_merge("%s %s %s %s\n" % \
 			(zing, desc.ljust(8), file_type, file_name))
 
-	def _unmerge_pkgfiles(self, pkgfiles, others_in_slot):
+	def _unmerge_pkgfiles(self, pkgfiles, others_in_slot, install_mask):
 		"""
 
 		Unmerges the contents of a package from the liveFS
@@ -2277,6 +2282,8 @@ class dblink(object):
 		@type pkgfiles: Dictionary { filename: [ 'type', '?', 'md5sum' ] }
 		@param others_in_slot: all dblink instances in this slot, excluding self
 		@type others_in_slot: list
+		@param install_mask: List of values in INSTALL_MASK.
+		@type install_maks: list
 		@rtype: None
 		"""
 
@@ -2491,7 +2498,10 @@ class dblink(object):
 								(statobj.st_dev, statobj.st_ino),
 								[]).append(relative_path)
 
-					if is_owned:
+					# if the file was replaced by new version, keep it
+					# unless it is also INSTALL_MASK-ed in the new version
+					# (then it was not really installed and we want to clean it)
+					if is_owned and not self._is_install_masked(relative_path[1:], install_mask):
 						show_unmerge("---", unmerge_desc["replaced"], file_type, obj)
 						continue
 					elif relative_path in cfgfiledict:
@@ -3689,6 +3699,24 @@ class dblink(object):
 	def _emerge_log(self, msg):
 		emergelog(False, msg)
 
+	def _is_install_masked(self, relative_path, install_mask):
+		ret = False
+		for pattern in 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(relative_path, pattern[1:])
+						or fnmatch.fnmatch(relative_path, pattern[1:] + '/*')):
+					ret = True
+					break
+			# filename
+			else:
+				if fnmatch.fnmatch(os.path.basename(relative_path), pattern):
+					ret = True
+					break
+		return ret
+
 	def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
 		mydbapi=None, prev_mtimes=None, counter=None):
 		"""
@@ -3841,7 +3869,7 @@ 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.
@@ -3851,6 +3879,17 @@ class dblink(object):
 		phase.start()
 		phase.wait()
 
+		try:
+			print('!!!\nRead INSTALL_MASK from %s\n!!!' % os.path.join(inforoot, "INSTALL_MASK"))
+			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 = f.read().split()
+		except EnvironmentError as e:
+			install_mask = self.settings.get('INSTALL_MASK', '').split()
+
 		# We check for unicode encoding issues after src_install. However,
 		# the check must be repeated here for binary packages (it's
 		# inexpensive since we call os.walk() here anyway).
@@ -3922,6 +3961,10 @@ class dblink(object):
 
 					relative_path = fpath[srcroot_len:]
 
+					# filter on INSTALL_MASK
+					if self._is_install_masked(relative_path, install_mask):
+						continue
+
 					if line_ending_re.search(relative_path) is not None:
 						paths_with_newlines.append(relative_path)
 
@@ -4270,7 +4313,8 @@ class dblink(object):
 			else:
 				cfgfiledict["IGNORE"]=0
 
-			rval = self._merge_contents(srcroot, destroot, cfgfiledict)
+			rval = self._merge_contents(srcroot, destroot, cfgfiledict,
+					install_mask)
 			if rval != os.EX_OK:
 				return rval
 		finally:
@@ -4351,7 +4395,7 @@ class dblink(object):
 			dblnk.settings.backup_changes("REPLACED_BY_VERSION")
 			unmerge_rval = dblnk.unmerge(ldpath_mtimes=prev_mtimes,
 				others_in_slot=others_in_slot, needed=needed,
-				preserve_paths=preserve_paths)
+				preserve_paths=preserve_paths, install_mask=install_mask)
 			dblnk.settings.pop("REPLACED_BY_VERSION", None)
 
 			if unmerge_rval == os.EX_OK:
@@ -4523,7 +4567,7 @@ class dblink(object):
 
 		return backup_p
 
-	def _merge_contents(self, srcroot, destroot, cfgfiledict):
+	def _merge_contents(self, srcroot, destroot, cfgfiledict, install_mask):
 
 		cfgfiledict_orig = cfgfiledict.copy()
 
@@ -4550,7 +4594,8 @@ class dblink(object):
 		# we do a first merge; this will recurse through all files in our srcroot but also build up a
 		# "second hand" of symlinks to merge later
 		if self.mergeme(srcroot, destroot, outfile, secondhand,
-			self.settings["EPREFIX"].lstrip(os.sep), cfgfiledict, mymtime):
+				self.settings["EPREFIX"].lstrip(os.sep), cfgfiledict,
+				mymtime, install_mask):
 			return 1
 
 		# now, it's time for dealing our second hand; we'll loop until we can't merge anymore.	The rest are
@@ -4562,7 +4607,7 @@ class dblink(object):
 
 			thirdhand = []
 			if self.mergeme(srcroot, destroot, outfile, thirdhand,
-				secondhand, cfgfiledict, mymtime):
+					secondhand, cfgfiledict, mymtime, install_mask):
 				return 1
 
 			#swap hands
@@ -4576,7 +4621,7 @@ class dblink(object):
 		if len(secondhand):
 			# force merge of remaining symlinks (broken or circular; oh well)
 			if self.mergeme(srcroot, destroot, outfile, None,
-				secondhand, cfgfiledict, mymtime):
+					secondhand, cfgfiledict, mymtime, install_mask):
 				return 1
 
 		#restore umask
@@ -4597,7 +4642,8 @@ class dblink(object):
 
 		return os.EX_OK
 
-	def mergeme(self, srcroot, destroot, outfile, secondhand, stufftomerge, cfgfiledict, thismtime):
+	def mergeme(self, srcroot, destroot, outfile, secondhand, stufftomerge,
+			cfgfiledict, thismtime, install_mask):
 		"""
 
 		This function handles actual merging of the package contents to the livefs.
@@ -4651,6 +4697,7 @@ class dblink(object):
 		while mergelist:
 
 			relative_path = mergelist.pop()
+			instmasked = self._is_install_masked(relative_path, install_mask)
 			mysrc = join(srcroot, relative_path)
 			mydest = join(destroot, relative_path)
 			# myrealdest is mydest without the $ROOT prefix (makes a difference if ROOT!="/")
@@ -4737,7 +4784,7 @@ class dblink(object):
 				destmd5 = None
 
 			moveme = True
-			if protected:
+			if protected and not instmasked:
 				mydest, protected, moveme = self._protect(cfgfiledict,
 					protect_if_modified, mymd5, myto, mydest,
 					myrealdest, mydmode, destmd5, mydest_link)
@@ -4765,7 +4812,7 @@ class dblink(object):
 				# we can simply test for existence of this file to see if the target has been merged yet
 				myrealto = normalize_path(os.path.join(destroot, myabsto))
 				if mydmode is not None and stat.S_ISDIR(mydmode):
-					if not protected:
+					if not protected and not instmasked:
 						# we can't merge a symlink over a directory
 						newdest = self._new_backup_path(mydest)
 						msg = []
@@ -4785,26 +4832,32 @@ class dblink(object):
 					# it later.
 					secondhand.append(mysrc[len(srcroot):])
 					continue
-				# unlinking no longer necessary; "movefile" will overwrite symlinks atomically and correctly
-				if moveme:
-					zing = ">>>"
-					mymtime = movefile(mysrc, mydest, newmtime=thismtime,
-						sstat=mystat, mysettings=self.settings,
-						encoding=_encodings['merge'])
 
-				try:
-					self._merged_path(mydest, os.lstat(mydest))
-				except OSError:
-					pass
+				if instmasked:
+					zing = "###"
+					# pass mymtime through from initial stat
+				else:
+					# unlinking no longer necessary; "movefile" will overwrite symlinks atomically and correctly
+					if moveme:
+						zing = ">>>"
+						mymtime = movefile(mysrc, mydest, newmtime=thismtime,
+							sstat=mystat, mysettings=self.settings,
+							encoding=_encodings['merge'])
+
+					try:
+						self._merged_path(mydest, os.lstat(mydest))
+					except OSError:
+						pass
 
 				if mymtime != None:
-					# Use lexists, since if the target happens to be a broken
-					# symlink then that should trigger an independent warning.
-					if not (os.path.lexists(myrealto) or
-						os.path.lexists(join(srcroot, myabsto))):
-						self._eqawarn('preinst',
-							[_("QA Notice: Symbolic link /%s points to /%s which does not exist.")
-							% (relative_path, myabsto)])
+					if not instmasked:
+						# Use lexists, since if the target happens to be a broken
+						# symlink then that should trigger an independent warning.
+						if not (os.path.lexists(myrealto) or
+							os.path.lexists(join(srcroot, myabsto))):
+							self._eqawarn('preinst',
+								[_("QA Notice: Symbolic link /%s points to /%s which does not exist.")
+								% (relative_path, myabsto)])
 
 					showMessage("%s %s -> %s\n" % (zing, mydest, myto))
 					if sys.hexversion >= 0x3030000:
@@ -4819,7 +4872,9 @@ class dblink(object):
 					return 1
 			elif stat.S_ISDIR(mymode):
 				# we are merging a directory
-				if mydmode != None:
+				if instmasked:
+					showMessage("### %s/\n" % mydest)
+				elif mydmode != None:
 					# destination exists
 
 					if bsd_chflags:
@@ -4906,10 +4961,11 @@ class dblink(object):
 					os.chown(mydest, mystat[4], mystat[5])
 					showMessage(">>> %s/\n" % mydest)
 
-				try:
-					self._merged_path(mydest, os.lstat(mydest))
-				except OSError:
-					pass
+				if not instmasked:
+					try:
+						self._merged_path(mydest, os.lstat(mydest))
+					except OSError:
+						pass
 
 				outfile.write("dir "+myrealdest+"\n")
 				# recurse and merge this directory
@@ -4918,7 +4974,7 @@ class dblink(object):
 
 			elif stat.S_ISREG(mymode):
 				# we are merging a regular file
-				if not protected and \
+				if not protected and not instmasked and \
 					mydmode is not None and stat.S_ISDIR(mydmode):
 						# install of destination is blocked by an existing directory with the same name
 						newdest = self._new_backup_path(mydest)
@@ -4932,9 +4988,11 @@ class dblink(object):
 						self._eerror("preinst", msg)
 						mydest = newdest
 
+				if instmasked:
+					zing = "###"
 				# whether config protection or not, we merge the new file the
 				# same way.  Unless moveme=0 (blocking directory)
-				if moveme:
+				elif moveme:
 					# Create hardlinks only for source files that already exist
 					# as hardlinks (having identical st_dev and st_ino).
 					hardlink_key = (mystat.st_dev, mystat.st_ino)
@@ -4967,7 +5025,9 @@ class dblink(object):
 			else:
 				# we are merging a fifo or device node
 				zing = "!!!"
-				if mydmode is None:
+				if instmasked:
+					zing = "###"
+				elif mydmode is None:
 					# destination doesn't exist
 					if movefile(mysrc, mydest, newmtime=thismtime,
 						sstat=mystat, mysettings=self.settings,
-- 
2.16.2



^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [gentoo-dev] [PATCH 2/2] portage.dbapi.vartree: Support exclusions in INSTALL_MASK
  2018-03-18 10:50 [gentoo-dev] [PATCH 1/2] phase-functions: Rework INSTALL_MASK to apply as a merge filter Michał Górny
@ 2018-03-18 10:50 ` Michał Górny
  0 siblings, 0 replies; 2+ messages in thread
From: Michał Górny @ 2018-03-18 10:50 UTC (permalink / raw
  To: gentoo-dev; +Cc: Michał Górny

Allow INSTALL_MASK patterns to start with '-' to indicate that
a specific match is to be excluded from being masked. In this case,
the last matching pattern determines whether the file is actually
filtered out or kept.
---
 pym/portage/dbapi/vartree.py | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 12137a0a4..360677824 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -3702,19 +3702,21 @@ class dblink(object):
 	def _is_install_masked(self, relative_path, install_mask):
 		ret = False
 		for pattern in install_mask:
+			# if pattern starts with -, possibly exclude this path
+			is_inclusive = not pattern.startswith('-')
+			if not is_inclusive:
+				pattern = pattern[1:]
 			# 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(relative_path, pattern[1:])
 						or fnmatch.fnmatch(relative_path, pattern[1:] + '/*')):
-					ret = True
-					break
+					ret = is_inclusive
 			# filename
 			else:
 				if fnmatch.fnmatch(os.path.basename(relative_path), pattern):
-					ret = True
-					break
+					ret = is_inclusive
 		return ret
 
 	def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
-- 
2.16.2



^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2018-03-18 10:51 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-18 10:50 [gentoo-dev] [PATCH 1/2] phase-functions: Rework INSTALL_MASK to apply as a merge filter Michał Górny
2018-03-18 10:50 ` [gentoo-dev] [PATCH 2/2] portage.dbapi.vartree: Support exclusions in INSTALL_MASK Michał Górny

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox