summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mk/Scripts/smart_makepatch.sh251
-rw-r--r--Mk/bsd.port.mk40
2 files changed, 255 insertions, 36 deletions
diff --git a/Mk/Scripts/smart_makepatch.sh b/Mk/Scripts/smart_makepatch.sh
new file mode 100644
index 000000000000..1ef87b0c49ac
--- /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
diff --git a/Mk/bsd.port.mk b/Mk/bsd.port.mk
index c27f12bec82e..1ed4f64b6ed9 100644
--- a/Mk/bsd.port.mk
+++ b/Mk/bsd.port.mk
@@ -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