File: //lib/kcare/libcare-patch-make
#!/bin/sh -e
usage() {
cat<<'EOF'
Makes `kpatch'es for the makesystem in the current directory.
Usage: libcare-patch-make [-h|--help] [-u|--update || -c|--clean]
[-p|--prepatch-ext=.EXT] \
[-s|--srcdir=SRCDIR] \
[-d|--destdir=DESTDIRVAR] \
PATCH1 PATCH2 ...
Run from inside the directory with `make'ble software. Makesystem must support
install with specified DESTDIR.
-c --clean do a clean build, execute `make clean` first
-u --update only update existing patches without rebuild. useful when
working on patch utils.
-p --prepatch-ext file extension used for patches applied on prebuild stage
-s --srcdir path to source folder of the project to be patched
-d --destdir specify variable makefile system uses to specify destination
directory for the installation
EOF
exit ${1-0}
}
xrealpath() {
if command -v realpath >/dev/null; then
realpath $1;
else
readlink -f -- "$1"
fi
}
prepare_env() {
export KPATCH_PATH=$(xrealpath $(dirname $0))
if test ! -x "$KPATCH_PATH/kpatch_gensrc"; then
echo "kpatch tools are missing" >&2
exit 1
fi
LPMAKEFILE=""
test -f lpmakefile && LPMAKEFILE="-f lpmakefile"
LPMAKE_TEMP_DIR="${LPMAKE_PATCHED_DIR-$PWD/.lpmaketmp}"
LPMAKE_ORIGINAL_DIR="${LPMAKE_ORIGINAL_DIR-$PWD/lpmake}"
LPMAKE_PATCHED_DIR="${LPMAKE_PATCHED_DIR-$LPMAKE_TEMP_DIR/patched}"
LPMAKE_PREPATCHED_DIR="${LPMAKE_PREPATCHED_DIR-$PWD/.lpmaketmp/prepatched}"
LPMAKE_PATCHROOT="${LPMAKE_PATCHROOT-$PWD/patchroot}"
export LPMAKE_ORIGINAL_DIR LPMAKE_PATCHED_DIR LPMAKE_PATCHROOT
mkdir -p "$LPMAKE_ORIGINAL_DIR" "$LPMAKE_PATCHED_DIR" "$LPMAKE_PATCHROOT"
export IS_LIBCARE_CC=y
export CC="${LPMAKE_TEMP_DIR}/gcc"
export CXX="${LPMAKE_TEMP_DIR}/g++"
export KPCCREAL=$(which gcc)
export KPCXXREAL=$(which g++)
unset MAKELEVEL
unset MAKEFLAGS
red=$(tput setaf 1 || :)
green=$(tput setaf 2 || :)
reset=$(tput sgr0 || :)
}
restore_origs() {
# Apply backups in reverse order
find $srcdir -regex '.+\.[0-9]+\.lpmakeorig' | sort -rV \
| sed -r 's/^(.+)\.[0-9]+\.lpmakeorig$/\0 \1/' \
| xargs -r -n2 mv
}
trap "restore_origs" 0
clean() {
if test -n "$do_clean"; then
local oldpwd="$(pwd)"
if test -n "$srcdir"; then
cd "$srcdir"
fi
make $LPMAKEFILE clean
rm -rf "$LPMAKE_ORIGINAL_DIR" "$LPMAKE_PATCHED_DIR"
if test -n "$KPATCH_LTO_REPLAY_PLUGIN"; then
echo "${red}clean lto files${reset}"
rm -rf "$KPATCH_ASM_DIR" patchroot *cgraph *.ltrans* *.wpa* *.res *.i *.lto_wrapper*
fi
if test -n "$srcdir"; then
cd "$oldpwd"
fi
fi
}
build_original() {
export KPATCH_STAGE=original
export KPCC_DBGFILTER_ARGS="$KPCC_DBGFILTER_ARGS"
echo "${green}BUILDING ORIGINAL CODE${reset}"
make $LPMAKEFILE
echo "${green}INSTALLING ORIGINAL OBJECTS INTO $LPMAKE_ORIGINAL_DIR${reset}"
make $LPMAKEFILE install \
"$destdir=$LPMAKE_ORIGINAL_DIR"
}
build_prepatched() {
export KPATCH_STAGE=prepatched
export KPCC_DBGFILTER_ARGS="$KPCC_DBGFILTER_ARGS"
i=0
for patch; do
test -z "${patch#*$prepatch_ext}" || continue
echo "${red}applying prebuild patch $patch...${reset}"
patch -b -z .${i}.lpmakeorig -p1 < $patch
i=$((i+1))
done
if test $i -eq 0 -a -z "$KPATCH_EMIT_DEBUG_FRAME" \
&& ! make -np $LPMAKEFILE | grep -q KPATCH_EMIT_DEBUG_FRAME; then
# No prebuild patches and KPATCH_EMIT_DEBUG_FRAME is not set.
# We can skip prepatch stage.
return
fi
echo "${green}BUILDING PREPATCHED CODE${reset}"
make -B $LPMAKEFILE
echo "${green}INSTALLING PREPATCHED OBJECTS INTO $LPMAKE_PREPATCHED_DIR${reset}"
make $LPMAKEFILE install "$destdir=$LPMAKE_PREPATCHED_DIR"
}
build_patched() {
export KPATCH_STAGE=patched
export KPCC_APPEND_ARGS="-Wl,-q"
if test -n "$KPATCH_LTO_REPLAY_PLUGIN"; then
echo "${green}copy lto files to $KPATCH_ASM_DIR${reset}"
find -iname \*.wpa.000i.cgraph -exec cp --parents {} "$KPATCH_ASM_DIR/$PWD/" \; || echo
find -iname \*.res -exec cp --parents {} "$KPATCH_ASM_DIR/$PWD/" \; || echo
find "$KP_PROJECT_DIR" -iname '*ltrans*[so]' -delete || echo
fi
local i=0
for patch; do
shift
test -n "${patch#*$prepatch_ext}" || continue
set -- "$@" "$patch"
i=$((i+1))
done
for patch; do
echo "${red}applying $patch...${reset}"
patch -b -z .${i}.lpmakeorig -p1 < $patch
i=$((i+1))
done
echo "${green}BUILDING PATCHED CODE${reset}"
make $LPMAKEFILE
echo "${green}INSTALLING PATCHED OBJECTS INTO $LPMAKE_PATCHED_DIR${reset}"
make $LPMAKEFILE install \
"$destdir=$LPMAKE_PATCHED_DIR"
}
build_objects() {
restore_origs
mkdir -p "$(dirname $CC)" && ln -sf "${KPATCH_PATH}/libcare-cc" "$CC"
mkdir -p "$(dirname $CXX)" && ln -sf "${KPATCH_PATH}/libcare-cc" "$CXX"
local oldpwd="$(pwd)"
if test -n "$srcdir"; then
cd "$srcdir"
fi
build_original
build_prepatched "$@"
build_patched "$@"
if test -n "$srcdir"; then
cd "$oldpwd"
fi
}
build_kpatches() {
mkdir -p "${LPMAKE_PATCHROOT}"
echo "${green}MAKING PATCHES${reset}"
for execfile in $(find "$LPMAKE_ORIGINAL_DIR" -perm /0111 -type f); do
origexec="$execfile"
filename="${origexec##*$LPMAKE_ORIGINAL_DIR/}"
patchedexec="$LPMAKE_PATCHED_DIR/$filename"
prepatchedexec="$LPMAKE_PREPATCHED_DIR/$filename"
buildid=$(eu-readelf -n "$origexec" | sed -n '/Build ID:/ { s/.* //; p }')
if ! eu-readelf -S "$patchedexec" | grep -q '.kpatch'; then
continue
fi
test -n "$buildid" || continue
chmod u+w "${origexec}" "${patchedexec}"
elfmake_args="$origexec $patchedexec ${patchedexec}.kpatch"
if test -f "$prepatchedexec"; then
elfmake_args="-w $prepatchedexec $elfmake_args"
fi
echo "$KPATCH_PATH/${KPATCH_ELFMAKE:-kpatch_dlmake}" $elfmake_args
"$KPATCH_PATH/${KPATCH_ELFMAKE:-kpatch_dlmake}" $elfmake_args > "${patchedexec}.kpatch.txt" 2>&1
cp "${patchedexec}.kpatch" "${LPMAKE_PATCHROOT}"/${buildid}.kpatch
echo "patch for ${origexec} is in ${LPMAKE_PATCHROOT}/${buildid}.kpatch"
done
}
main() {
PROG_NAME=$(basename $0)
TEMP=$(getopt -o s:ucd:p: --long srcdir:update,clean,destdir:,prepatch-ext: -n ${PROG_NAME} -- "$@" || usage 1)
eval set -- "$TEMP"
destdir="DESTDIR"
while true; do
case $1 in
-s|--srcdir)
shift
srcdir="$1"
shift
;;
-u|--update)
shift
only_update=1
;;
-c|--clean)
shift
do_clean=1
;;
-d|--destdir)
shift
destdir=$1
shift
;;
-p|--prepatch-ext)
shift
prepatch_ext=$1
shift
;;
--)
shift; break;
;;
esac
done
prepare_env
if test -z "$only_update"; then
clean
build_objects "$@"
fi
build_kpatches
}
main "$@"