Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Nov 2015 15:25:13 +0000 (UTC)
From:      John Marino <marino@FreeBSD.org>
To:        ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org
Subject:   svn commit: r401709 - in head/Mk: . Scripts
Message-ID:  <201511151525.tAFFPDYm091886@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marino
Date: Sun Nov 15 15:25:12 2015
New Revision: 401709
URL: https://svnweb.freebsd.org/changeset/ports/401709

Log:
  Enhance "make makepatch" to address two major deficiencies
  
  This update to the "makepatch" target adds the following enhancements:
  
  1) Conserves comments
     If the existing patch has comments, they will be transferred to the
     regenerated patch.
  
  2) Supports multiple patches per file
     If the patch file contains concatenated patches, the makepatch
     target will keep these patches together.  It may change the order
     of the patches the first time, but every time after the multi-patch
     will be assembled in the same order.
  
  Behavioral changes:
  
  A) The "old" patches are not overwritten, but rather archived at:
     ${WRKDIR}/makepatch-tmp/archived-patches
  
  B) Any patch that was not replaced or renamed is deleted by makepatch
     (but it is archived first, see paragraph above)
  
  C) There regeneration messages for the user will show them which patches
     are using "legacy" names formats.
  
  D) Makepatch will do a great job at "cleaning" git patches; it removes
     lines starting with "diff" and "index" in the comments section.
  
  Notes:
  
  E) Should a source file be modified by multiple patches (e.g. two separate
     multi-patches), a composite patch will be generated.  In the above
     example of two multi-patches, one would get the full patch and the
     other no longer patch the source file.
  
  Approved by:		portmgr (mat)
  Differential Revision:	D4136

Added:
  head/Mk/Scripts/smart_makepatch.sh   (contents, props changed)
Modified:
  head/Mk/bsd.port.mk

Added: head/Mk/Scripts/smart_makepatch.sh
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/Mk/Scripts/smart_makepatch.sh	Sun Nov 15 15:25:12 2015	(r401709)
@@ -0,0 +1,251 @@
+#!/bin/sh
+# MAINTAINER: portmgr@FreeBSD.org
+# $FreeBSD$
+
+# This script regenerates patches.  It conserves existing comments and
+# file names, even if the file name does not meet any current or
+# previous convention.  It will keep multiple patches in the same file
+# rather than splitting them into individual files.
+#
+# If a generated patch was not present before, it will create a file
+# name where forward slashes are replaced with an underscore and
+# underscores are appended by another underscore.
+#
+# Limitations:
+# 1) If a file is modified by multiple patches, it will be regenerated
+#    as a single patch.  That means if two multi-patch files modified
+#    the same source file, when regenerated, the source file's patch
+#    will only appear in one of patch file.
+# 2) It's possible that trailing garbage at the end of a patch in a
+#    multipatch file might corrupt the comment (or be interpreted as
+#    a comment) of the following patch.  (garbage in, garbage out)
+#
+# Reminder
+# Don't forget to disable post-patch targets before regenerating patches
+# if those targets modify source files (e.g. with sed).  You may also
+# want to disable EXTRA_PATCHES as well if that is being used.
+
+
+if [ -z "${PATCHDIR}" -o -z "${PATCH_WRKSRC}" -o -z "${WRKDIR}" ]; then
+	echo "WRKDIR, PATCHDIR, and PATCH_WRKSRC required in environment." >&2
+	exit 1
+fi
+
+WORKAREA=${WRKDIR}/.makepatch-tmp
+PATCHMAP=${WORKAREA}/pregen.map
+COMMENTS=${WORKAREA}/comments
+REGENNED=${WORKAREA}/regenerated
+DESTDIR=${WORKAREA}/staged
+SAVEDIR=${WORKAREA}/archived-patches
+
+case "${STRIP_COMPONENTS}" in
+	[123456789])	;;
+	1[0123456789])	;;
+	*)		STRIP_COMPONENTS=0
+esac
+
+strip_path() {
+	local raw_name=$1
+	if [ "${STRIP_COMPONENTS}" = "0" ]; then
+		echo ${raw_name}
+	else
+		echo ${raw_name} | awk -v sc=${STRIP_COMPONENTS} -F "/" \
+		'{ for (x = sc + 1; x <= NF; x++) { \
+			slash = (x>sc+1) ? "/" : ""; \
+			printf ("%s%s", slash, $x); \
+		   }}'
+	fi
+}
+
+std_patch_filename() {
+	local sans_cwd=$(echo $1 | sed 's|^\.\/||')
+	local raw_name=$(strip_path ${sans_cwd})
+	echo patch-$(echo ${raw_name} | sed -e 's|_|&&|g; s|/|_|g')
+}
+
+patchdir_files_list() {
+	if [ -d "${PATCHDIR}" ]; then
+		(cd ${PATCHDIR} && \
+			find * -type f -name "patch-*" -maxdepth 0 \
+			2>/dev/null | sed -e '/\.orig$/d'
+		)
+	fi;
+}
+
+valid_name() {
+	local current_patch_name=$1
+	local first_target=$(echo $2 | sed 's|^\.\/||')
+	local result=$3
+	local testres
+	local lps
+	for lps in __ - + ; do
+		testres=patch-$(echo ${first_target} | sed -e "s|/|${lps}|g")
+		if [ "${testres}" = "${current_patch_name}" ]; then
+			result=${testres}
+			break
+		fi
+	done
+	echo ${result}
+}
+
+map_existing_patches() {
+	mkdir -p ${WORKAREA}
+	: > ${PATCHMAP}
+	local target
+	local future_name
+	local std_target
+	local P
+	local t
+	for P in ${old_patch_list}; do
+		target=$(cd ${PATCHDIR} && \
+			grep "^+++ " ${P} | awk '{print $2}'
+		)
+		# For single patches, we honor previous separators, but use
+		# a standard patch name if the current patch name does not
+		# conform.  However, if two or more patches are contained in
+		# single file, then we do *NOT* rename the file
+		future_name=
+		for t in ${target}; do
+			if [ -n "${future_name}" ]; then
+				future_name=${P}
+				break;
+			fi
+			std_target=$(std_patch_filename ${t})
+			future_name=$(valid_name ${P} ${t} ${std_target})
+		done
+		for t in ${target}; do
+			std_target=$(std_patch_filename ${t})
+			echo "${future_name}	${std_target}" >> ${PATCHMAP}
+		done
+	done
+}
+
+extract_comment_from_patch() {
+	local existing_patch=${PATCHDIR}/$1
+	local contains=$(grep "^+++ " ${existing_patch} | awk '{x++; print x}')
+	local rawname
+	local fname
+	local num
+	for num in ${contains}; do
+		rawname=$(grep "^+++ " ${existing_patch} | \
+			awk -v num=${num} '{x++; if (x==num) print $2}')
+		fname=$(std_patch_filename $rawname)
+		awk -v num=${num} '\
+		BEGIN { done=0; x=0; hunk=0; looking=(num==1) } \
+		{ \
+		    if (!done) { \
+		        if ($1 == "@@") { \
+		            split ($3,a,","); \
+		            hc = a[2]; \
+		            hunk = 1;
+		        } else if (hunk) { \
+		            first=substr($1,1,1); \
+		            if (first == "-") { hc++ } else { hc-- } \
+		            if (hc == 0) {hunk = 0} \
+		        } \
+			if ($1 == "---") { \
+			   x++; \
+			   if (x == num) { done = 1 } \
+			   if (x + 1 == num) { looking = 1 } \
+			} else if (!hunk && looking) { \
+		            if ($1!="diff" && $1!="index" && $1!="+++") {\
+		                print $0 \
+		            } \
+		        } \
+		    } \
+		}' ${existing_patch} > ${COMMENTS}/${fname}
+	done
+}
+
+extract_comments() {
+	mkdir -p ${COMMENTS}
+	rm -f ${COMMENTS}/*
+	local P
+	for P in ${old_patch_list}; do
+		extract_comment_from_patch ${P}
+	done
+}
+
+regenerate_patches() {
+	mkdir -p ${REGENNED}
+	rm -f ${REGENNED}/*
+	[ ! -d "${PATCH_WRKSRC}" ] && return
+
+	local F
+	local NEW
+	local OUT
+	local ORIG
+	local new_list=
+	new_list=$(cd ${PATCH_WRKSRC} && \
+		find -s * -type f -name '*.orig' 2>/dev/null)
+	(cd ${PATCH_WRKSRC} && for F in ${new_list}; do
+		ORIG=${F#./}
+		NEW=${ORIG%.orig}
+		cmp -s ${ORIG} ${NEW} && continue
+		OUT=${REGENNED}/$(std_patch_filename ${NEW})
+		TZ=UTC diff -udp ${ORIG} ${NEW} | sed \
+			-e '/^---/s|\.[0-9]* +0000$| UTC|' \
+			-e '/^+++/s|\([[:blank:]][-0-9:.+]*\)*$||' \
+			> ${OUT} || true
+	done)
+}
+
+get_patch_name() {
+	awk -v name=$1 '\
+	{ if ($2 == name) \
+	  { \
+	      if (!done) { print $1 }; \
+	      done = 1; \
+	  } \
+	} \
+	END { if (!done) print name }' ${PATCHMAP}
+}
+
+stage_patches() {
+	mkdir -p ${DESTDIR}
+	rm -f ${DESTDIR}/*
+	local P
+	local name
+	local patch_list=$(cd ${REGENNED} && find * -name "patch-*" 2>/dev/null)
+	for P in ${patch_list}; do
+		name=$(get_patch_name ${P})
+		[ -e ${COMMENTS}/${P} ] && cat ${COMMENTS}/${P} \
+			>> ${DESTDIR}/${name}
+		if [ "${P}" = "${name}" ]; then
+			echo "Generated ${P}"
+		else
+			echo "Generated ${P} >> ${name} (legacy)"
+		fi
+		cat ${REGENNED}/${P} >> ${DESTDIR}/${name}
+	done
+}
+
+conserve_old_patches() {
+	mkdir -p ${SAVEDIR}
+	rm -f ${SAVEDIR}/*
+	[ -z "${old_patch_list}" ] && return
+
+	local P
+	for P in ${old_patch_list}; do
+		mv ${PATCHDIR}/${P} ${SAVEDIR}/${P}
+	done
+	echo "The previous patches have been placed here:"
+	echo ${SAVEDIR}
+}
+
+install_regenerated_patches() {
+	local testdir=$(find ${DESTDIR} -empty)
+	if [ -z "${testdir}" ]; then
+		mkdir -p ${PATCHDIR}
+		find ${DESTDIR} -type f -exec mv {} ${PATCHDIR}/ \;
+	fi
+}
+
+old_patch_list=$(patchdir_files_list)
+
+map_existing_patches
+extract_comments
+regenerate_patches
+stage_patches
+conserve_old_patches
+install_regenerated_patches

Modified: head/Mk/bsd.port.mk
==============================================================================
--- head/Mk/bsd.port.mk	Sun Nov 15 15:07:08 2015	(r401708)
+++ head/Mk/bsd.port.mk	Sun Nov 15 15:25:12 2015	(r401709)
@@ -1114,44 +1114,12 @@ STRIPBIN=	${STRIP_CMD}
 
 .else
 
-# Look for files named "*.orig" under ${PATCH_WRKSRC} and (re-)generate
-# ${PATCHDIR}/patch-* files from them.  By popular demand, we currently
-# use '_' (underscore) to replace path separators in patch file names.
-#
-# If a file name happens to contain character which is also a separator
-# replacement character, it will be doubled in the resulting patch name.
-#
-# To minimize gratuitous patch renames, newly generated patches will be
-# written under existing file names when they use any of the previously
-# common path separators ([-+_]) or legacy double underscore (__).
-
 .if !target(makepatch)
-PATCH_PATH_SEPARATOR=	_
 makepatch:
-	@${MKDIR} ${PATCHDIR}
-	@(cd ${PATCH_WRKSRC}; \
-		for f in `${FIND} -s . -type f -name '*.orig'`; do \
-			ORIG=$${f#./}; \
-			NEW=$${ORIG%.orig}; \
-			cmp -s $${ORIG} $${NEW} && continue; \
-			! for _lps in `${ECHO} _ - + | ${SED} -e \
-				's|${PATCH_PATH_SEPARATOR}|__|'`; do \
-					PATCH=`${ECHO} $${NEW} | ${SED} -e "s|/|$${_lps}|g"`; \
-					test -f "${PATCHDIR}/patch-$${PATCH}" && break; \
-			done || ${ECHO} $${_SEEN} | ${GREP} -q /$${PATCH} && { \
-				PATCH=`${ECHO} $${NEW} | ${SED} -e \
-					's|${PATCH_PATH_SEPARATOR}|&&|g' -e \
-					's|/|${PATCH_PATH_SEPARATOR}|g'`; \
-				_SEEN=$${_SEEN}/$${PATCH}; \
-			}; \
-			OUT=${PATCHDIR}/patch-$${PATCH}; \
-			${ECHO} ${DIFF} -udp $${ORIG} $${NEW} '>' $${OUT}; \
-			TZ=UTC ${DIFF} -udp $${ORIG} $${NEW} | ${SED} -e \
-				'/^---/s|\.[0-9]* +0000$$| UTC|' -e \
-				'/^+++/s|\([[:blank:]][-0-9:.+]*\)*$$||' \
-					> $${OUT} || ${TRUE}; \
-		done \
-	)
+	@${SETENV} WRKDIR=${WRKDIR} PATCHDIR=${PATCHDIR} \
+		PATCH_WRKSRC=${PATCH_WRKSRC} \
+		STRIP_COMPONENTS="${PATCH_STRIP:S/-p//}" \
+		${SH} ${SCRIPTSDIR}/smart_makepatch.sh
 .endif
 
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201511151525.tAFFPDYm091886>