Path: blob/main/Mk/Scripts/smart_makepatch.sh
16461 views
#!/bin/sh1# MAINTAINER: [email protected]23# This script regenerates patches. It conserves existing comments and4# file names, even if the file name does not meet any current or5# previous convention. It will keep multiple patches in the same file6# rather than splitting them into individual files.7#8# If a generated patch was not present before, it will create a file9# name where forward slashes are replaced with an underscore and10# underscores are appended by another underscore.11#12# Limitations:13# 1) If a file is modified by multiple patches, it will be regenerated14# as a single patch. That means if two multi-patch files modified15# the same source file, when regenerated, the source file's patch16# will only appear in one of patch file.17# 2) It's possible that trailing garbage at the end of a patch in a18# multipatch file might corrupt the comment (or be interpreted as19# a comment) of the following patch. (garbage in, garbage out)20#21# Reminder22# Don't forget to disable post-patch targets before regenerating patches23# if those targets modify source files (e.g. with sed). You may also24# want to disable EXTRA_PATCHES as well if that is being used.2526set -o pipefail2728[ -n "${DEBUG_MK_SCRIPTS}" -o -n "${DEBUG_MK_SCRIPTS_SMART_MAKEPATCH}" ] && set -x2930if [ -z "${PATCHDIR}" -o -z "${PATCH_WRKSRC}" -o -z "${WRKDIR}" ]; then31echo "WRKDIR, PATCHDIR, and PATCH_WRKSRC required in environment." >&232exit 133fi3435WORKAREA=${WRKDIR}/.makepatch-tmp36PATCHMAP=${WORKAREA}/pregen.map37COMMENTS=${WORKAREA}/comments38REGENNED=${WORKAREA}/regenerated39DESTDIR=${WORKAREA}/staged40SAVEDIR=${WORKAREA}/archived-patches4142case "${STRIP_COMPONENTS}" in43[123456789]) ;;441[0123456789]) ;;45*) STRIP_COMPONENTS=046esac4748strip_path() {49local raw_name=$150if [ "${STRIP_COMPONENTS}" = "0" ]; then51echo ${raw_name}52else53echo ${raw_name} | awk -v sc=${STRIP_COMPONENTS} -F "/" \54'{ for (x = sc + 1; x <= NF; x++) {55slash = (x>sc+1) ? "/" : "";56printf ("%s%s", slash, $x);57}}'58fi59}6061std_patch_filename() {62local sans_cwd63local raw_name64sans_cwd=$(echo $1 | sed 's|^\.\/||')65raw_name=$(strip_path ${sans_cwd})66echo "patch-$(echo ${raw_name} | sed -e 's|_|&&|g; s|/|_|g')"67}6869patchdir_files_list() {70if [ -d "${PATCHDIR}" ]; then71(cd ${PATCHDIR} && \72find -s . -type f -name "patch-*" -maxdepth 1 \732>/dev/null | sed -e 's,^\./,,; /\.orig$/d'74)75fi;76}7778valid_name() {79local current_patch_name=$180local result=$381local first_target82local testres83local lps84first_target=$(echo $2 | sed 's|^\.\/||')85for lps in __ - + ; do86testres=patch-$(echo ${first_target} | sed -e "s|/|${lps}|g")87if [ "${testres}" = "${current_patch_name}" ]; then88result=${testres}89break90fi91done92echo ${result}93}9495map_existing_patches() {96mkdir -p ${WORKAREA}97: > ${PATCHMAP}98local target99local future_name100local std_target101local P102local t103for P in ${old_patch_list}; do104target=$(cd ${PATCHDIR} && \105grep "^+++ " ${P} | awk '{print $2}'106)107# For single patches, we honor previous separators, but use108# a standard patch name if the current patch name does not109# conform. However, if two or more patches are contained in110# single file, then we do *NOT* rename the file111future_name=112for t in ${target}; do113if [ -n "${future_name}" ]; then114future_name=${P}115break;116fi117std_target=$(std_patch_filename ${t})118future_name=$(valid_name ${P} ${t} ${std_target})119done120for t in ${target}; do121std_target=$(std_patch_filename ${t})122echo "${future_name} ${std_target}" >> ${PATCHMAP}123done124done125}126127extract_comment_from_patch() {128local existing_patch=${PATCHDIR}/$1129local contains130local rawname131local fname132local num133contains=$(grep "^+++ " ${existing_patch} | awk '{x++; print x}')134for num in ${contains}; do135rawname=$(grep "^+++ " ${existing_patch} | \136awk -v num=${num} '{x++; if (x==num) print $2}')137fname=$(std_patch_filename $rawname)138awk -v num=${num} '139BEGIN { done=0; x=0; hunk=0; looking=(num==1) }140{141if (!done) {142if ($1 == "@@") {143split ($2,a,",");144split ($3,b,",");145hca = a[2];146hcb = a[3];147hunk = 1;148} else if (hunk) {149first=substr($1,1,1);150if (first == "-") { hca-- }151else if (first == "+") { hcb-- }152else {hca--; hcb--}153if (hca == 0 && hcb == 0) {hunk = 0}154}155if ($1 == "---") {156x++;157if (x == num) { done = 1 }158if (x + 1 == num) { looking = 1 }159} else if (!hunk && looking) {160if ($1!="diff" && $1!="index" && $1!="+++") {161print $0162}163}164}165}' ${existing_patch} > ${COMMENTS}/${fname}166done167}168169extract_comments() {170mkdir -p ${COMMENTS}171rm -f ${COMMENTS}/*172local P173for P in ${old_patch_list}; do174extract_comment_from_patch ${P}175done176}177178regenerate_patches() {179mkdir -p ${REGENNED}180rm -f ${REGENNED}/*181[ ! -d "${PATCH_WRKSRC}" ] && return182183local F184local NEW185local OUT186local ORIG187local new_list188new_list=$(cd "${PATCH_WRKSRC}" && \189find -s . -type f -name '*.orig' 2>/dev/null)190(cd "${PATCH_WRKSRC}" && for F in ${new_list}; do191ORIG=${F#./}192NEW=${ORIG%.orig}193cmp -s ${ORIG} ${NEW} && continue194OUT=${REGENNED}/$(std_patch_filename ${NEW})195TZ=UTC diff -audp ${ORIG} ${NEW} | sed \196-e '/^---/s|\.[0-9]* +0000$| UTC|' \197-e '/^+++/s|\([[:blank:]][-0-9:.+]*\)*$||' \198> ${OUT} || true199done)200}201202get_patch_name() {203awk -v name=$1 '204{ if ($2 == name)205{206if (!done) { print $1 };207done = 1;208}209}210END { if (!done) print name }' ${PATCHMAP}211}212213stage_patches() {214mkdir -p ${DESTDIR}215rm -f ${DESTDIR}/*216local P217local name218local patch_list219patch_list=$(cd ${REGENNED} && find -s . -name "patch-*" 2>/dev/null)220for P in ${patch_list}; do221P=${P#./}222name=$(get_patch_name ${P})223[ -e ${COMMENTS}/${P} ] && cat ${COMMENTS}/${P} \224>> ${DESTDIR}/${name}225if [ "${P}" = "${name}" ]; then226echo "Generated ${P}"227else228echo "Generated ${P} >> ${name} (legacy)"229fi230cat ${REGENNED}/${P} >> ${DESTDIR}/${name}231done232}233234compare_common_patches() {235[ -z "${old_patch_list}" ] && return236local archive_patch_list237local P238local ppatch239local ppatch_stripped240local cpatch241local cpatch_stripped242for P in ${old_patch_list}; do243if [ -e ${DESTDIR}/${P} ]; then244ppatch=${PATCHDIR}/${P}245cpatch=${DESTDIR}/${P}246ppatch_stripped=$(mktemp -t portpatch)247cpatch_stripped=$(mktemp -t portpatch)248sed -E -e '/^--- .+ UTC$/d; s/^(@@ [^@]* @@).*/\1/' \249${ppatch} > ${ppatch_stripped}250sed -E -e '/^--- .+ UTC$/d; s/^(@@ [^@]* @@).*/\1/' \251${cpatch} > ${cpatch_stripped}252# Don't replace patches with only metadata changes253if ! cmp -s ${ppatch_stripped} ${cpatch_stripped}; then254archive_patch_list="${archive_patch_list} ${P}"255else256echo "${P} only contains metadata changes; not replacing"257rm ${cpatch}258fi259rm ${ppatch_stripped}260rm ${cpatch_stripped}261fi262done263old_patch_list=${archive_patch_list}264}265266conserve_old_patches() {267mkdir -p ${SAVEDIR}268rm -f ${SAVEDIR}/*269[ -z "${old_patch_list}" ] && return270271local P272for P in ${old_patch_list}; do273mv ${PATCHDIR}/${P} ${SAVEDIR}/${P}274done275echo "The previous patches have been placed here:"276echo ${SAVEDIR}277}278279install_regenerated_patches() {280local testdir281testdir=$(find ${DESTDIR} -empty)282if [ -z "${testdir}" ]; then283mkdir -p ${PATCHDIR}284find ${DESTDIR} -type f -exec mv {} ${PATCHDIR}/ \;285fi286}287288old_patch_list=$(patchdir_files_list)289290map_existing_patches291extract_comments292regenerate_patches293stage_patches294compare_common_patches295conserve_old_patches296install_regenerated_patches297298299