#!/bin/sh
#
# Copyright Ericsson AB 2014. All Rights Reserved.
#
version="1.0.1"
force=
lib_path=
orig_dir=
sdir=
idir="/broken/path/here"
cleanup=no
install_docs=yes
invalid_src="does not seem to be a valid OTP source tree"
not_built="Source in has not been built"
doc_not_built="Documentation has not been built. Either build the
documentation and re-run 'otp_patch_apply', or re-run 'otp_patch_apply'
with the '-n' switch."
print_usage()
{
cat <<EOF
otp_patch_apply -s <Dir> -i <Dir> [-l <Dir>] [-c] [-f] [-h] [-n] [-v] \\
<App1> [... <AppN>]
-s <Dir> -- OTP source directory that contain build results.
-i <Dir> -- OTP installation directory to patch.
-l <Dir> -- Alternative OTP source library directory path(s) containing
build results of OTP applications. Multiple paths should be
colon separated.
-c -- Cleanup (remove) old versions of applications patched
in the installation.
-f -- Force patch of application(s) even though dependencies are
not fullfilled.
-h -- Print this help then exit.
-n -- Do not install documentation.
-v -- Print version then exit.
<AppX> -- Application to patch.
Environment Variable:
ERL_LIBS -- Alternative OTP source library directory path(s) containing
build results of OTP applications. Multiple paths should be
colon separated.
NOTE:
* Complete build environment is required while running otp_patch_apply.
* Before applying a patch you need to build all of OTP in the source
directory.
* All source directories identified by -s and -l should contain build
results of OTP applications.
Version: $version
EOF
}
error()
{
echo "ERROR:" "$@" 1>&2
exit 1
}
usage_error()
{
echo "ERROR:" "$@" 1>&2
echo "" 1>&2
print_usage 1>&2
exit 1
}
usage()
{
print_usage
exit 0
}
alt_lib_path()
{
app=$1
save_ifs=$IFS
IFS=:
cd "$orig_dir" || error "Cannot change directory to $orig_dir"
for lib in $lib_path; do
# Want absolute path
case "$lib" in
/*)
;;
*)
cd "$lib" || error "Cannot change directory to $lib"
lib=`pwd`
cd "$orig_dir" || error "Cannot change directory to $orig_dir"
esac
if [ -d "$lib/$app" ]; then
echo "$lib/$app"
IFS=$save_ifs
return 0
fi
done
IFS=$save_ifs
return 1
}
prog_in_mod_path()
{
chk_path="/bin:$PATH"
PROG=$1
save_ifs=$IFS
IFS=:
if [ "X$TARGET" = "Xwin32" ]; then
for p in $chk_path; do
if [ -f "$p/$PROG.exe" ]; then
IFS=$save_ifs
echo "$p/$PROG.exe"
return 0
fi
done
else
for p in $chk_path; do
if [ -x "$p/$PROG" ]; then
IFS=$save_ifs
echo "$p/$PROG"
return 0
fi
done
fi
IFS=$save_ifs
return 1
}
find_prog()
{
prog_in_mod_path "$1"
if [ $? -ne 0 ]; then
echo "$1"
fi
return 0
}
# Parse arguments
while [ $# -gt 0 ]; do
case "$1" in
"-s")
shift
if [ ! $# -gt 0 ]; then
usage_error "Missing OTP source directory"
fi
sdir="$1";;
"-i")
shift
if [ ! $# -gt 0 ]; then
usage_error "Missing OTP install directory"
fi
idir="$1";;
"-l")
shift
if [ ! $# -gt 0 ]; then
usage_error "Missing OTP library directory"
fi
if [ "x$lib_path" = "x" ]; then
lib_path="$1"
else
lib_path="$lib_path:$1"
fi;;
"-f")
force="-force";;
"-c")
cleanup=yes;;
"-h")
usage;;
"-n")
install_docs=no;;
"-v")
echo "otp_patch_apply version $version"
exit 0;;
*)
app="$1"
applications="$applications $app";;
esac
shift
done
# Check that we got mandatory arguments
test "x$sdir" != "x" || usage_error "Missing OTP source directory"
test "x$idir" != "x" || usage_error "Missing OTP install directory"
test "x$applications" != "x" || usage_error "Missing applications"
orig_dir=`pwd`
# Check that the source directory seems sane
cd "$sdir" 2>/dev/null || error "Cannot change directory to $sdir"
# Want absolute path
case "$sdir" in
/*) ;;
*) sdir=`pwd`;;
esac
export ERL_TOP="$sdir"
test -f "$sdir/otp_build" || error "$ERL_TOP" $invalid_src
test -f "$sdir/OTP_VERSION" || error "$ERL_TOP" $invalid_src
test -f "$sdir/otp_versions.table" || error "$ERL_TOP" $invalid_src
test -f "$sdir/erts/autoconf/config.guess" || error "$ERL_TOP" $invalid_src
test -f "$sdir/make/verify_runtime_dependencies" || error "$ERL_TOP" $invalid_src
test -x "$sdir/bootstrap/bin/erl" || error $not_built
test -x "$sdir/bootstrap/bin/erlc" || error $not_built
test -x "$sdir/bootstrap/bin/escript" || error $not_built
test -f "$sdir/make/otp_built" || error $not_built
if [ $install_docs = yes ]; then
test -f "$sdir/make/otp_doc_built" || usage_error $doc_not_built
fi
otp_rel=`sed 's|\([0-9]*\).*|\1|' < $ERL_TOP/OTP_VERSION` || \
error "Failed to read $ERL_TOP/OTP_VERSION"
case "$otp_rel" in
1[7-9]|[2-9][0-9]) ;; # ok; release 17-99
*) error "Invalid OTP release: $otp_rel";;
esac
export PATH="$ERL_TOP/bootstrap/bin:$PATH"
erlc="$ERL_TOP/bootstrap/bin/erlc"
erl="$ERL_TOP/bootstrap/bin/erl"
erl_otp_rel=`$erl -noshell -noinput -eval "io:format(\"~s~n\", [erlang:system_info(otp_release)]), erlang:halt(0)"` || \
error "Failed to execute: $erl"
test "$otp_rel" = "$erl_otp_rel" || error "Inconsistent source: $sdir"
app_dirs=
for app in $applications; do
case "$app" in
"erts")
dir="$ERL_TOP/erts";;
*)
dir="$ERL_TOP/lib/$app";;
esac
if [ ! -d "$dir" ]; then
dir=`alt_lib_path "$app"`
if [ $? -ne 0 ]; then
error "Application missing in source: $app"
fi
fi
app_dirs="$app_dirs $dir"
done
cd "$orig_dir" 2>/dev/null || error "Cannot change directory to $orig_dir"
# Check that the install directory seems sane
cd "$idir" 2>/dev/null || error "Cannot change directory to $idir"
# Want absolute path
case "$idir" in
/*) ;;
*) idir=`pwd`;;
esac
test -d "$idir/releases/$otp_rel" || \
error "No OTP-$otp_rel installation present in $idir"
cd "$ERL_TOP" 2>/dev/null || error "Cannot change directory to $ERL_TOP"
# Some tools we use
rm=`find_prog rm`
rmdir=`find_prog rmdir`
cp=`find_prog cp`
mv=`find_prog mv`
mkdir=`find_prog mkdir`
# Setup build stuff
if [ "x$TARGET" = "x" ]; then
TARGET=`$ERL_TOP/erts/autoconf/config.guess`
fi
BUILDSYS=$TARGET
if [ -z "$MAKE" ]; then
case $TARGET in
win32)
MAKE=make;;
*)
prog_in_mod_path gmake >/dev/null
if [ $? -eq 0 ]; then
MAKE=gmake
else
MAKE=make
fi;;
esac
fi
if [ X`$MAKE is_cross_configured` = Xyes ]; then
TARGET=`$MAKE target_configured`
elif [ "x$OVERRIDE_TARGET" != "x" -a "x$OVERRIDE_TARGET" != "xwin32" ]; then
TARGET=$OVERRIDE_TARGET
fi
# Check for cleanup
inst_app_vers="$idir/releases/$otp_rel/installed_application_versions"
rm_app_vers=
if [ $cleanup = yes ]; then
$mv "$inst_app_vers" "${inst_app_vers}.save" || \
error "Failed to save $inst_app_vers"
for app in $applications; do
tmp=`grep "$app-*" "${inst_app_vers}.save"`
rm_app_vers="$rm_app_vers $tmp"
done
$cp "${inst_app_vers}.save" "$inst_app_vers"
for rm_app_ver in $rm_app_vers; do
$cp "$inst_app_vers" "${inst_app_vers}.tmp"
grep -v $rm_app_ver "${inst_app_vers}.tmp" > "$inst_app_vers"
done
$rm -f "${inst_app_vers}.tmp"
fi
# Verify runtime dependencies
$ERL_TOP/make/verify_runtime_dependencies -release "$otp_rel" \
-source "$ERL_TOP" -target "$idir" $force $applications || {
test ! -f "${inst_app_vers}.save" || \
$mv "${inst_app_vers}.save" "$inst_app_vers"
exit 1
}
# Update OTP_VERSION in installation
otp_version=`cat "$idir/releases/$otp_rel/OTP_VERSION"` || {
test ! -f "${inst_app_vers}.save" || \
$mv "${inst_app_vers}.save" "$inst_app_vers"
error "Not able to read $idir/releases/$otp_rel/OTP_VERSION"
}
{
echo "$otp_version" | sed "s|^\([^\*]*\)\**|\1\*\*|g" > \
"$idir/releases/$otp_rel/OTP_VERSION"
} 2>/dev/null || {
test ! -f "${inst_app_vers}.save" || \
$mv "${inst_app_vers}.save" "$inst_app_vers"
error "Not able to update $idir/releases/$otp_rel/OTP_VERSION"
}
# Do actual cleanup
if [ "x$rm_app_vers" != "x" ]; then
for app_ver in $rm_app_vers; do
case x"$app_ver" in
x)
;;
xerts-*)
$rm -rf "$idir/$app_ver" ;;
x*)
$rm -rf "$idir/lib/$app_ver" ;;
esac
done
$rm -f "${inst_app_vers}.save"
fi
# Install application from built source
for app_dir in $app_dirs; do
(cd "$app_dir" && \
$MAKE MAKE="$MAKE" TARGET=$TARGET RELEASE_ROOT="$idir" \
RELEASE_PATH="$idir" TESTROOT="$idir" release) || exit 1
done
if [ $install_docs = yes ]; then
# Documentation have been built and should be installed
for app_dir in $app_dirs; do
(cd "$app_dir" && \
$MAKE MAKE="$MAKE" RELEASE_ROOT="$idir" RELEASE_PATH="$idir" \
TESTROOT="$idir" release_docs) || exit 1
done
(cd "$sdir/system/doc/top" && $MAKE clean)
(cd "$sdir/system/doc/top" && \
$MAKE MAKE="$MAKE" RELEASE_ROOT="$idir" RELEASE_PATH="$idir" \
TESTROOT="$idir" release_docs) || exit 1
echo ""
echo "*"
echo "* NOTE! In order to update pre-formatted man pages you"
echo "* need to run the 'Install' script located in:"
echo "* $idir"
echo "*"
fi
# If erts, kernel, stdlib or sasl is included, find versions
for app in $applications; do
case "$app" in
erts)
erts_vsn=`grep '^VSN' erts/vsn.mk | sed "s|^VSN.*=[^0-9]*\([0-9].*\)$|\1|g"`
update_rel=true;;
kernel)
kernel_vsn=`sed "s|^KERNEL_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/kernel/vsn.mk`
update_rel=true;;
stdlib)
stdlib_vsn=`sed "s|^STDLIB_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/stdlib/vsn.mk`
update_rel=true;;
sasl)
sasl_vsn=`sed "s|^SASL_VSN[^=]*=[^0-9]*\([0-9].*\)$|\1|g" lib/sasl/vsn.mk`
update_rel=true;;
*)
;;
esac
done
# and find the old versions for those not included
if [ "X$update_rel" != "X" ]; then
if [ "X$erts_vsn" = "X" ]; then
erts_vsns=`ls -d "$idir"/erts-* | sed "s|$idir/erts-\([0-9\.].*\)|\1|g"`
erts_vsn=`echo "$erts_vsns" | sort -t '.' -g | tail -n 1`
fi
if [ "X$kernel_vsn" = "X" ]; then
kernel_vsns=`ls -d "$idir"/lib/kernel-* | sed "s|$idir/lib/kernel-\([0-9\.].*\)|\1|g"`
kernel_vsn=`echo "$kernel_vsns" | sort -t '.' -g | tail -n 1`
fi
if [ "X$stdlib_vsn" = "X" ]; then
stdlib_vsns=`ls -d "$idir"/lib/stdlib-* | sed "s|$idir/lib/stdlib-\([0-9\.].*\)|\1|g"`
stdlib_vsn=`echo "$stdlib_vsns" | sort -t '.' -g | tail -n 1`
fi
if [ "X$sasl_vsn" = "X" ]; then
sasl_vsns=`ls -d "$idir"/lib/sasl-* | sed "s|$idir/lib/sasl-\([0-9\.].*\)|\1|g"`
sasl_vsn=`echo "$sasl_vsns" | sort -t '.' -g | tail -n 1`
fi
# Generate .rel, .script and .boot - to tmp dir
start_clean="{release, {\"Erlang/OTP\",\"$otp_rel\"}, {erts, \"$erts_vsn\"}, [{kernel,\"$kernel_vsn\"}, {stdlib,\"$stdlib_vsn\"}]}."
start_sasl="{release, {\"Erlang/OTP\",\"$otp_rel\"}, {erts, \"$erts_vsn\"}, [{kernel,\"$kernel_vsn\"}, {stdlib,\"$stdlib_vsn\"}, {sasl,\"$sasl_vsn\"}]}."
tmp_dir="$idir/tmp";
if [ ! -d "$tmp_dir" ]; then
$mkdir "$tmp_dir"
fi
echo "$start_sasl" > "$tmp_dir/start_sasl.rel"
echo "$start_clean" > "$tmp_dir/start_clean.rel"
echo "$start_clean" > "$tmp_dir/no_dot_erlang.rel"
$erlc -I"$idir"/lib/*/ebin -o"$tmp_dir" "$tmp_dir/start_sasl.rel" || exit 1
$erlc -I"$idir"/lib/*/ebin -o"$tmp_dir" +no_warn_sasl "$tmp_dir/start_clean.rel" || exit 1
$erlc -I"$idir"/lib/*/ebin -o"$tmp_dir" +no_warn_sasl +no_dot_erlang "$tmp_dir/no_dot_erlang.rel" || exit 1
# Generate RELEASES file
"$erl" -noinput +B -eval "release_handler:create_RELEASES(\"%ERL_ROOT%\", \"$tmp_dir\", \"$tmp_dir/start_sasl.rel\", []), halt()" || exit 1
# If all good so far, move generated files into target area
$mv "$tmp_dir/RELEASES" "$idir/releases/RELEASES.src"
$mv "$tmp_dir"/* "$idir/releases/$otp_rel"
$rmdir "$tmp_dir"
# Remove old start scripts (forces a new run of Install)
$rm -f "$idir"/releases/RELEASES
$rm -f "$idir"/bin/*.script
$rm -f "$idir"/bin/*.boot
$rm -f "$idir"/bin/erl
echo ""
echo "*"
echo "* NOTE! In order to get a runnable OTP system again you"
echo "* need to run the 'Install' script located in:"
echo "* $idir"
echo "*"
fi