#!/usr/bin/env bash
#
# DESCRIPTION: re-package Isabelle distribution with add-on components
## diagnostics
PRG=$(basename "$0")
function usage()
{
echo
echo "Usage: isabelle $PRG ARCHIVE PLATFORM_FAMILY [REMOTE_MAC]"
echo
echo " Re-package Isabelle source distribution with add-on components and"
echo " post-hoc patches for platform family linux, windows, macos."
echo
echo " The optional remote Mac OS X system is used for dmg build."
echo
echo " Add-on components are that of the running Isabelle version!"
echo
exit 1
}
function fail()
{
echo "$1" >&2
exit 2
}
## arguments
[ "$#" -ne 2 -a "$#" -ne 3 ] && usage
ARCHIVE="$1"; shift
PLATFORM_FAMILY="$1"; shift
REMOTE_MAC="$1"; shift
[ -f "$ARCHIVE" ] || fail "Bad source archive: $ARCHIVE"
ARCHIVE_DIR="$(cd $(dirname "$ARCHIVE"); echo "$PWD")"
ISABELLE_NAME="$(basename "$ARCHIVE" .tar.gz)"
## main
#GNU tar (notably on Mac OS X)
type -p gnutar >/dev/null && function tar() { gnutar "$@"; }
TMP="/var/tmp/isabelle-makedist$$"
mkdir "$TMP" || fail "Cannot create directory $TMP"
ISABELLE_TARGET="$TMP/$ISABELLE_NAME"
tar -C "$TMP" -x -z -f "$ARCHIVE" || exit 2
# distribution classpath (based on educated guesses)
splitarray ":" "$ISABELLE_CLASSPATH"; CLASSPATH_ENTRIES=("${SPLITARRAY[@]}")
declare -a DISTRIBITION_CLASSPATH=()
for ENTRY in "${CLASSPATH_ENTRIES[@]}"
do
ENTRY=$(echo "$ENTRY" | perl -n -e "
if (m,$ISABELLE_HOME/(.*)\$,) { print qq{\$1}; }
elsif (m,$USER_HOME/.isabelle/contrib/(.*)\$,) { print qq{contrib/\$1}; }
elsif (m,/home/isabelle/contrib/(.*)\$,) { print qq{contrib/\$1}; }
else { print; };
print qq{\n};")
DISTRIBITION_CLASSPATH["${#DISTRIBITION_CLASSPATH[@]}"]="$ENTRY"
done
DISTRIBITION_CLASSPATH["${#DISTRIBITION_CLASSPATH[@]}"]="src/Tools/jEdit/dist/jedit.jar"
echo "classpath"
for ENTRY in "${DISTRIBITION_CLASSPATH[@]}"
do
echo " $ENTRY"
done
# bundled components
if [ ! -e "$ARCHIVE_DIR/contrib" ]; then
if [ ! -e "$ARCHIVE_DIR/../contrib" ]; then
mkdir -p "$ARCHIVE_DIR/contrib"
else
ln -s "../contrib" "$ARCHIVE_DIR/contrib"
fi
fi
echo "#bundled components" >> "$ISABELLE_TARGET/etc/components"
for CATALOG in main "$PLATFORM_FAMILY" bundled "bundled-$PLATFORM_FAMILY"
do
CATALOG_FILE="$ISABELLE_HOME/Admin/components/$CATALOG"
if [ -f "$CATALOG_FILE" ]
then
echo "catalog ${CATALOG}"
{
while { unset REPLY; read -r; test "$?" = 0 -o -n "$REPLY"; }
do
case "$REPLY" in
\#* | "") ;;
*)
COMPONENT="$REPLY"
COMPONENT_DIR="$ISABELLE_TARGET/contrib/$COMPONENT"
case "$COMPONENT" in
jedit_build*) ;;
*)
echo " component $COMPONENT"
CONTRIB="$ARCHIVE_DIR/contrib/${COMPONENT}.tar.gz"
if [ ! -f "$CONTRIB" ]; then
type -p curl > /dev/null || fail "Cannot download files: missing curl"
REMOTE="$ISABELLE_COMPONENT_REPOSITORY/${COMPONENT}.tar.gz"
echo " downloading $REMOTE"
curl --fail --silent "$REMOTE" > "$CONTRIB" || \
fail "Failed to download \"$REMOTE\""
perl -e "exit((stat('${CONTRIB}'))[7] == 0 ? 0 : 1);" && exit 2
fi
tar -C "$ISABELLE_TARGET/contrib" -x -z -f "$CONTRIB" || exit 2
if [ -f "$COMPONENT_DIR/etc/settings" -o -f "$COMPONENT_DIR/etc/components" ]
then
case "$COMPONENT" in
jdk-*)
mv "$ISABELLE_TARGET/contrib/$COMPONENT" "$ISABELLE_TARGET/contrib/jdk"
echo "contrib/jdk" >> "$ISABELLE_TARGET/etc/components"
;;
*)
echo "contrib/$COMPONENT" >> "$ISABELLE_TARGET/etc/components"
;;
esac
fi
;;
esac
;;
esac
done
} < "$CATALOG_FILE"
fi
done
# purge other platforms
function purge_target
{
(
cd "$ISABELLE_TARGET"
for DIR in $(eval find "$@" | sort)
do
echo "removing $DIR"
rm -rf "$DIR"
done
)
}
# purge jdk -- keep only jre
function purge_jdk
{
local DIR="contrib/jdk/$1"
(
cd "$ISABELLE_TARGET"
if [ -d "$DIR/jre" ]; then
for X in "$DIR"/*
do
case "$X" in
*/jre) ;;
*)
echo "removing $X"
rm -rf "$X"
;;
esac
done
else
fail "Bad JDK directory: \"$DIR\""
fi
)
}
# platform-specific setup (inside archive)
case "$PLATFORM_FAMILY" in
linux)
purge_target 'contrib -name "x86*-darwin" -o -name "x86*-cygwin" -o -name "x86*-windows"'
purge_jdk "x86_64-linux"
(
init_component "$JEDIT_HOME"
echo "# Java runtime options"
eval "declare -a JAVA_ARGS=($ISABELLE_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS)"
for ARG in "${JAVA_ARGS[@]}"
do
echo "$ARG"
done
echo "-Disabelle.jedit_server=${ISABELLE_NAME}"
) > "$ISABELLE_TARGET/${ISABELLE_NAME}.options"
LINUX_CLASSPATH=""
for ENTRY in "${DISTRIBITION_CLASSPATH[@]}"
do
if [ -z "$LINUX_CLASSPATH" ]; then
LINUX_CLASSPATH="\\\$ISABELLE_HOME/$ENTRY"
else
LINUX_CLASSPATH="$LINUX_CLASSPATH:\\\$ISABELLE_HOME/$ENTRY"
fi
done
cat "$ISABELLE_HOME/Admin/Linux/Isabelle.run" | \
perl -p > "$ISABELLE_TARGET/${ISABELLE_NAME}.run" -e "s,{CLASSPATH},$LINUX_CLASSPATH,;"
chmod +x "$ISABELLE_TARGET/${ISABELLE_NAME}.run"
mv "$ISABELLE_TARGET/contrib/linux_app" "$TMP/."
cp "$TMP/linux_app/Isabelle" "$ISABELLE_TARGET/$ISABELLE_NAME"
;;
macos)
purge_target 'contrib -name "x86*-linux" -o -name "x86*-cygwin" -o -name "x86*-windows"'
purge_jdk "x86_64-darwin/Contents/Home"
mv "$ISABELLE_TARGET/contrib/macos_app" "$TMP/."
perl -pi \
-e "s,lookAndFeel=.*,lookAndFeel=com.apple.laf.AquaLookAndFeel,g;" \
-e "s,delete-line.shortcut=.*,delete-line.shortcut=C+d,g;" \
-e "s,delete.shortcut2=.*,delete.shortcut2=A+d,g;" \
-e "s,plugin-blacklist.MacOSX.jar=true,plugin-blacklist.MacOSX.jar=,g;" \
"$ISABELLE_TARGET/src/Tools/jEdit/dist/properties/jEdit.props"
;;
windows)
purge_target 'contrib -name "x86*-linux" -o -name "x86*-darwin" -o -name "x86-cygwin"'
purge_jdk "x86_64-windows"
mv "$ISABELLE_TARGET/contrib/windows_app" "$TMP/."
perl -pi \
-e "s,lookAndFeel=.*,lookAndFeel=com.sun.java.swing.plaf.windows.WindowsLookAndFeel,g;" \
-e "s,foldPainter=.*,foldPainter=Square,g;" \
"$ISABELLE_TARGET/src/Tools/jEdit/dist/properties/jEdit.props"
(
init_component "$JEDIT_HOME"
echo -e "# Java runtime options\r"
eval "declare -a JAVA_ARGS=($ISABELLE_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS)"
for ARG in "${JAVA_ARGS[@]}"
do
echo -e "$ARG\r"
done
echo -e "-Disabelle.jedit_server=${ISABELLE_NAME}\r"
) > "$ISABELLE_TARGET/${ISABELLE_NAME}.l4j.ini"
(
cd "$TMP"
APP_TEMPLATE="$ISABELLE_HOME/Admin/Windows/launch4j"
(
for ENTRY in "${DISTRIBITION_CLASSPATH[@]}"
do
ENTRY=$(echo "$ENTRY" | perl -p -e 's,/,\\\\,g;')
echo " <cp>%EXEDIR%\\\\$ENTRY</cp>"
done
) > exe_classpath
EXE_CLASSPATH="$(cat exe_classpath)"
perl -p \
-e "s,{OUTFILE},$ISABELLE_TARGET/${ISABELLE_NAME}.exe,g;" \
-e "s,{ICON},$APP_TEMPLATE/isabelle_transparent.ico,g;" \
-e "s,{SPLASH},$APP_TEMPLATE/isabelle.bmp,g;" \
-e "s,{CLASSPATH},$EXE_CLASSPATH,g;" \
-e "s,{ISABELLE_NAME},$ISABELLE_NAME,g;" \
"$APP_TEMPLATE/isabelle.xml" > isabelle.xml
"windows_app/launch4j-${ISABELLE_PLATFORM_FAMILY}/launch4j" isabelle.xml
cp "$APP_TEMPLATE/manifest.xml" "$ISABELLE_TARGET/${ISABELLE_NAME}.exe.manifest"
)
(
cd "$ISABELLE_TARGET"
cp "$ISABELLE_HOME/Admin/Windows/Cygwin/Cygwin-Setup.bat" \
"$ISABELLE_HOME/Admin/Windows/Cygwin/Cygwin-Terminal.bat" .
for NAME in postinstall rebaseall
do
cp -a "$ISABELLE_HOME/Admin/Windows/Cygwin/isabelle/$NAME" \
"contrib/cygwin/isabelle/."
done
if [ "$ISABELLE_PLATFORM_FAMILY" = macos ]; then
find . -type f -not -name '*.exe' -not -name '*.dll' -perm +100 \
-print0 > "contrib/cygwin/isabelle/executables"
else
find . -type f -not -name '*.exe' -not -name '*.dll' -executable \
-print0 > "contrib/cygwin/isabelle/executables"
fi
find . -type l -exec echo "{}" ";" -exec readlink "{}" ";" \
> "contrib/cygwin/isabelle/symlinks"
find . -type l -exec rm "{}" ";"
touch "contrib/cygwin/isabelle/uninitialized"
)
;;
*)
;;
esac
# archive
BUNDLE_ARCHIVE="${ARCHIVE_DIR}/${ISABELLE_NAME}_${PLATFORM_FAMILY}.tar.gz"
echo "packaging $(basename "$BUNDLE_ARCHIVE")"
tar -C "$TMP" -c -z -f "$BUNDLE_ARCHIVE" "$ISABELLE_NAME" || exit 2
# platform-specific setup (outside archive)
case "$PLATFORM_FAMILY" in
linux)
echo "application for $PLATFORM_FAMILY"
ln -s "${ISABELLE_NAME}_linux.tar.gz" "${ARCHIVE_DIR}/${ISABELLE_NAME}_app.tar.gz"
;;
macos)
echo "application for $PLATFORM_FAMILY"
(
cd "$TMP"
APP_TEMPLATE="$ISABELLE_HOME/Admin/MacOS"
APP="dmg/${ISABELLE_NAME}.app"
mkdir -p "dmg/.background"
cp "$APP_TEMPLATE/dmg/background.png" "dmg/.background/"
cp "$APP_TEMPLATE/dmg/DS_Store" "dmg/.DS_Store"
ln -s /Applications "dmg/."
for NAME in Java MacOS PlugIns Resources
do
mkdir -p "$APP/Contents/$NAME"
done
(
init_component "$JEDIT_HOME"
cat "$APP_TEMPLATE/Info.plist-part1"
declare -a OPTIONS=()
eval "OPTIONS=($ISABELLE_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS)"
for OPT in "${OPTIONS[@]}"
do
echo "<string>$OPT</string>"
done
echo "<string>-Disabelle.jedit_server={ISABELLE_NAME}</string>"
echo "<string>-Dapple.awt.application.name={ISABELLE_NAME}</string>"
cat "$APP_TEMPLATE/Info.plist-part2"
) | perl -p -e "s,{ISABELLE_NAME},${ISABELLE_NAME},g;" > "$APP/Contents/Info.plist"
for ENTRY in "${DISTRIBITION_CLASSPATH[@]}"
do
ln -sf "../Resources/${ISABELLE_NAME}/$ENTRY" "$APP/Contents/Java"
done
cp -R "$APP_TEMPLATE/Resources/." "$APP/Contents/Resources/."
ln -sf "../Resources/${ISABELLE_NAME}/contrib/jdk/x86_64-darwin" \
"$APP/Contents/PlugIns/bundled.jdk"
cp macos_app/JavaAppLauncher "$APP/Contents/MacOS/." && \
chmod +x "$APP/Contents/MacOS/JavaAppLauncher"
mv "$ISABELLE_NAME" "$APP/Contents/Resources/."
ln -sf "../../Info.plist" "$APP/Contents/Resources/$ISABELLE_NAME/${ISABELLE_NAME}.plist"
ln -sf "Contents/Resources/$ISABELLE_NAME" "$APP/Isabelle"
rm -f "${ARCHIVE_DIR}/${ISABELLE_NAME}.dmg"
tar -C dmg -czf "${ARCHIVE_DIR}/${ISABELLE_NAME}_dmg.tar.gz" .
if [ -n "$REMOTE_MAC" ]
then
echo -n "$REMOTE_MAC: building dmg ..."
isabelle remote_dmg -V Isabelle "$REMOTE_MAC" \
"${ARCHIVE_DIR}/${ISABELLE_NAME}_dmg.tar.gz" "${ARCHIVE_DIR}/${ISABELLE_NAME}.dmg" &&
echo " done"
fi
)
;;
windows)
(
cd "$TMP"
rm -f "$TMP/${ISABELLE_NAME}.7z"
7z -y -bd a "$TMP/${ISABELLE_NAME}.7z" "$ISABELLE_NAME"
[ -f "$TMP/${ISABELLE_NAME}.7z" ] || exit 2
echo "application for $PLATFORM_FAMILY"
(
cat "windows_app/7zsd_All_x64.sfx"
cat "$ISABELLE_HOME/Admin/Windows/Installer/sfx.txt" | \
perl -p -e "s,{ISABELLE_NAME},${ISABELLE_NAME},g;"
cat "$TMP/${ISABELLE_NAME}.7z"
) > "${ARCHIVE_DIR}/${ISABELLE_NAME}.exe"
chmod +x "${ARCHIVE_DIR}/${ISABELLE_NAME}.exe"
)
;;
*)
;;
esac
# clean up
rm -rf "$TMP"