Admin/lib/Tools/makedist_bundle
author wenzelm
Fri, 04 May 2018 16:22:09 +0200
changeset 68080 17f79ae49401
parent 67994 11be98cefe92
child 68375 5714e8806060
permissions -rwxr-xr-x
set view title dynamically;

#!/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"