aboutsummaryrefslogtreecommitdiffstats
path: root/Mk/Scripts
diff options
context:
space:
mode:
authormarino <marino@FreeBSD.org>2015-11-15 23:25:12 +0800
committermarino <marino@FreeBSD.org>2015-11-15 23:25:12 +0800
commit1a35d9723fe52e36cde5793d610dc8636be06d3f (patch)
treea33bf429b5d1b17137be56e35ce8fbdb41d738b3 /Mk/Scripts
parent112934f5eab626f5928a42dbb5616e691e42b262 (diff)
downloadfreebsd-ports-graphics-1a35d9723fe52e36cde5793d610dc8636be06d3f.tar.gz
freebsd-ports-graphics-1a35d9723fe52e36cde5793d610dc8636be06d3f.tar.zst
freebsd-ports-graphics-1a35d9723fe52e36cde5793d610dc8636be06d3f.zip
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
Diffstat (limited to 'Mk/Scripts')
-rw-r--r--Mk/Scripts/smart_makepatch.sh251
1 files changed, 251 insertions, 0 deletions
diff --git a/Mk/Scripts/smart_makepatch.sh b/Mk/Scripts/smart_makepatch.sh
new file mode 100644
index 00000000000..1ef87b0c49a
--- /dev/null
+++ b/Mk/Scripts/smart_makepatch.sh
@@ -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