Index: head/Mk/Scripts/smart_makepatch.sh =================================================================== --- head/Mk/Scripts/smart_makepatch.sh +++ head/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 Index: head/Mk/bsd.port.mk =================================================================== --- head/Mk/bsd.port.mk +++ head/Mk/bsd.port.mk @@ -1114,44 +1114,12 @@ .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