1*4882a593Smuzhiyun#!/usr/bin/env bash 2*4882a593Smuzhiyun# A little script I whipped up to make it easy to 3*4882a593Smuzhiyun# patch source trees and have sane error handling 4*4882a593Smuzhiyun# -Erik 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun# (c) 2002 Erik Andersen <andersen@codepoet.org> 7*4882a593Smuzhiyun# 8*4882a593Smuzhiyun# Parameters: 9*4882a593Smuzhiyun# - "-s", optional. Silent operation, don't print anything if there 10*4882a593Smuzhiyun# isn't any error. 11*4882a593Smuzhiyun# - the build directory, optional, default value is '.'. The place where are 12*4882a593Smuzhiyun# the package sources. 13*4882a593Smuzhiyun# - the patch directory, optional, default '../kernel-patches'. The place 14*4882a593Smuzhiyun# where are the scripts you want to apply. 15*4882a593Smuzhiyun# - other parameters are the patch name patterns, optional, default value is 16*4882a593Smuzhiyun# '*'. Pattern(s) describing the patch names you want to apply. 17*4882a593Smuzhiyun# 18*4882a593Smuzhiyun# The script will look recursively for patches from the patch directory. If a 19*4882a593Smuzhiyun# file named 'series' exists then the patches mentioned in it will be applied 20*4882a593Smuzhiyun# as plain patches, regardless of their file name. If no 'series' file exists, 21*4882a593Smuzhiyun# the script will look for file names matching pattern(s). If the name 22*4882a593Smuzhiyun# ends with '.tar.*', '.tbz2' or '.tgz', the file is considered as an archive 23*4882a593Smuzhiyun# and will be uncompressed into a directory named 24*4882a593Smuzhiyun# '.patches-name_of_the_archive-unpacked'. It's the turn of this directory to 25*4882a593Smuzhiyun# be scanned with '*' as pattern. Remember that scanning is recursive. Other 26*4882a593Smuzhiyun# files than series file and archives are considered as a patch. 27*4882a593Smuzhiyun# 28*4882a593Smuzhiyun# Once a patch is found, the script will try to apply it. If its name doesn't 29*4882a593Smuzhiyun# end with '.gz', '.bz', '.bz2', '.xz', '.zip', '.Z', '.diff*' or '.patch*', 30*4882a593Smuzhiyun# it will be skipped. If necessary, the patch will be uncompressed before being 31*4882a593Smuzhiyun# applied. The list of the patches applied is stored in '.applied_patches_list' 32*4882a593Smuzhiyun# file in the build directory. 33*4882a593Smuzhiyun 34*4882a593Smuzhiyunset -e 35*4882a593Smuzhiyun 36*4882a593Smuzhiyunsilent= 37*4882a593Smuzhiyunif [ "$1" = "-s" ] ; then 38*4882a593Smuzhiyun # add option to be used by the patch tool 39*4882a593Smuzhiyun silent=-s 40*4882a593Smuzhiyun shift 41*4882a593Smuzhiyunfi 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun# Set directories from arguments, or use defaults. 44*4882a593Smuzhiyunbuilddir=${1-.} 45*4882a593Smuzhiyunpatchdir=${2-../kernel-patches} 46*4882a593Smuzhiyunshift 2 47*4882a593Smuzhiyunpatchpattern=${@-*} 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun# use a well defined sorting order 50*4882a593Smuzhiyunexport LC_COLLATE=C 51*4882a593Smuzhiyun 52*4882a593Smuzhiyunif [ ! -d "${builddir}" ] ; then 53*4882a593Smuzhiyun echo "Aborting. '${builddir}' is not a directory." 54*4882a593Smuzhiyun exit 1 55*4882a593Smuzhiyunfi 56*4882a593Smuzhiyunif [ ! -d "${patchdir}" ] ; then 57*4882a593Smuzhiyun echo "Aborting. '${patchdir}' is not a directory." 58*4882a593Smuzhiyun exit 1 59*4882a593Smuzhiyunfi 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun# Remove any rejects present BEFORE patching - Because if there are 62*4882a593Smuzhiyun# any, even if patches are well applied, at the end it will complain 63*4882a593Smuzhiyun# about rejects in builddir. 64*4882a593Smuzhiyunfind ${builddir}/ '(' -name '*.rej' -o -name '.*.rej' ')' -print0 | \ 65*4882a593Smuzhiyun xargs -0 -r rm -f 66*4882a593Smuzhiyun 67*4882a593Smuzhiyunfunction apply_patch { 68*4882a593Smuzhiyun path="${1%%/}" 69*4882a593Smuzhiyun patch="${2}" 70*4882a593Smuzhiyun case "${path}" in 71*4882a593Smuzhiyun /*) ;; 72*4882a593Smuzhiyun *) path="$PWD/${path}";; 73*4882a593Smuzhiyun esac 74*4882a593Smuzhiyun if [ "$3" ]; then 75*4882a593Smuzhiyun type="series"; uncomp="cat" 76*4882a593Smuzhiyun else 77*4882a593Smuzhiyun case "$patch" in 78*4882a593Smuzhiyun *.gz) 79*4882a593Smuzhiyun type="gzip"; uncomp="gunzip -dc"; ;; 80*4882a593Smuzhiyun *.bz) 81*4882a593Smuzhiyun type="bzip"; uncomp="bunzip -dc"; ;; 82*4882a593Smuzhiyun *.bz2) 83*4882a593Smuzhiyun type="bzip2"; uncomp="bunzip2 -dc"; ;; 84*4882a593Smuzhiyun *.xz) 85*4882a593Smuzhiyun type="xz"; uncomp="unxz -dc"; ;; 86*4882a593Smuzhiyun *.zip) 87*4882a593Smuzhiyun type="zip"; uncomp="unzip -d"; ;; 88*4882a593Smuzhiyun *.Z) 89*4882a593Smuzhiyun type="compress"; uncomp="uncompress -c"; ;; 90*4882a593Smuzhiyun *.diff*) 91*4882a593Smuzhiyun type="diff"; uncomp="cat"; ;; 92*4882a593Smuzhiyun *.patch*) 93*4882a593Smuzhiyun type="patch"; uncomp="cat"; ;; 94*4882a593Smuzhiyun *) 95*4882a593Smuzhiyun echo "Unsupported file type for ${path}/${patch}, skipping"; 96*4882a593Smuzhiyun return 0 97*4882a593Smuzhiyun ;; 98*4882a593Smuzhiyun esac 99*4882a593Smuzhiyun fi 100*4882a593Smuzhiyun if [ -z "$silent" ] ; then 101*4882a593Smuzhiyun echo "" 102*4882a593Smuzhiyun echo "Applying $patch using ${type}: " 103*4882a593Smuzhiyun fi 104*4882a593Smuzhiyun if [ ! -e "${path}/$patch" ] ; then 105*4882a593Smuzhiyun echo "Error: missing patch file ${path}/$patch" 106*4882a593Smuzhiyun exit 1 107*4882a593Smuzhiyun fi 108*4882a593Smuzhiyun existing="$(grep -E "/${patch}\$" ${builddir}/.applied_patches_list || true)" 109*4882a593Smuzhiyun if [ -n "${existing}" ]; then 110*4882a593Smuzhiyun echo "Error: duplicate filename '${patch}'" 111*4882a593Smuzhiyun echo "Conflicting files are:" 112*4882a593Smuzhiyun echo " already applied: ${existing}" 113*4882a593Smuzhiyun echo " to be applied : ${path}/${patch}" 114*4882a593Smuzhiyun exit 1 115*4882a593Smuzhiyun fi 116*4882a593Smuzhiyun echo "${path}/${patch}" >> ${builddir}/.applied_patches_list 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun cd ${builddir} 119*4882a593Smuzhiyun if [ -n "$BR2_GEN_GIT" ]; then 120*4882a593Smuzhiyun if [ ! -d .git ]; then 121*4882a593Smuzhiyun git init 122*4882a593Smuzhiyun echo -e "*" >> .gitignore 123*4882a593Smuzhiyun git add -f .gitignore * 124*4882a593Smuzhiyun git commit --no-edit -m "init" 125*4882a593Smuzhiyun fi 126*4882a593Smuzhiyun fi 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun ${uncomp} "${path}/$patch" | patch -g0 -p1 -E --no-backup-if-mismatch -d "${builddir}" -t -N $silent 129*4882a593Smuzhiyun if [ $? != 0 ] ; then 130*4882a593Smuzhiyun echo "Patch failed! Please fix ${patch}!" 131*4882a593Smuzhiyun exit 1 132*4882a593Smuzhiyun fi 133*4882a593Smuzhiyun 134*4882a593Smuzhiyun if [ -n "$BR2_GEN_GIT" ]; then 135*4882a593Smuzhiyun # Remove backup files 136*4882a593Smuzhiyun find $builddir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \; 137*4882a593Smuzhiyun git am "${path}/${patch}" --exclude "*" || 138*4882a593Smuzhiyun git commit --allow-empty --no-edit -m "${patch}" 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun git add -f * 141*4882a593Smuzhiyun git commit --allow-empty --amend --no-edit 142*4882a593Smuzhiyun rm -rf .git/rebase-apply/ 143*4882a593Smuzhiyun 144*4882a593Smuzhiyun # Wait for auto gc 145*4882a593Smuzhiyun while [ -f .git/gc.pid ]; do sleep 1;done 146*4882a593Smuzhiyun fi 147*4882a593Smuzhiyun} 148*4882a593Smuzhiyun 149*4882a593Smuzhiyunfunction scan_patchdir { 150*4882a593Smuzhiyun local path=$1 151*4882a593Smuzhiyun shift 1 152*4882a593Smuzhiyun patches=${@-*} 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun # If there is a series file, use it instead of using ls sort order 155*4882a593Smuzhiyun # to apply patches. Skip line starting with a dash. 156*4882a593Smuzhiyun if [ -e "${path}/series" ] ; then 157*4882a593Smuzhiyun # The format of a series file accepts a second field that is 158*4882a593Smuzhiyun # used to specify the number of directory components to strip 159*4882a593Smuzhiyun # when applying the patch, in the form -pN (N an integer >= 0) 160*4882a593Smuzhiyun # We assume this field to always be -p1 whether it is present 161*4882a593Smuzhiyun # or missing. 162*4882a593Smuzhiyun series_patches="`grep -Ev "^#" ${path}/series | cut -d ' ' -f1 2> /dev/null`" 163*4882a593Smuzhiyun for i in $series_patches; do 164*4882a593Smuzhiyun apply_patch "$path" "$i" series 165*4882a593Smuzhiyun done 166*4882a593Smuzhiyun else 167*4882a593Smuzhiyun for i in `cd $path; ls -d $patches 2> /dev/null` ; do 168*4882a593Smuzhiyun if [ -d "${path}/$i" ] ; then 169*4882a593Smuzhiyun scan_patchdir "${path}/$i" 170*4882a593Smuzhiyun elif echo "$i" | grep -q -E "\.tar(\..*)?$|\.tbz2?$|\.tgz$" ; then 171*4882a593Smuzhiyun unpackedarchivedir="$builddir/.patches-$(basename $i)-unpacked" 172*4882a593Smuzhiyun rm -rf "$unpackedarchivedir" 2> /dev/null 173*4882a593Smuzhiyun mkdir "$unpackedarchivedir" 174*4882a593Smuzhiyun tar -C "$unpackedarchivedir" -xaf "${path}/$i" 175*4882a593Smuzhiyun scan_patchdir "$unpackedarchivedir" 176*4882a593Smuzhiyun else 177*4882a593Smuzhiyun apply_patch "$path" "$i" 178*4882a593Smuzhiyun fi 179*4882a593Smuzhiyun done 180*4882a593Smuzhiyun fi 181*4882a593Smuzhiyun} 182*4882a593Smuzhiyun 183*4882a593Smuzhiyuntouch ${builddir}/.applied_patches_list 184*4882a593Smuzhiyunscan_patchdir "$patchdir" "$patchpattern" 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun# Check for rejects... 187*4882a593Smuzhiyunif [ "`find $builddir/ '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ] ; then 188*4882a593Smuzhiyun echo "Aborting. Reject files found." 189*4882a593Smuzhiyun exit 1 190*4882a593Smuzhiyunfi 191