JAL-3631 Separated jalview.sh and update.sh, and included in install4j installer...
authorBen Soares <b.soares@dundee.ac.uk>
Thu, 1 Aug 2024 14:30:15 +0000 (15:30 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Thu, 1 Aug 2024 14:30:15 +0000 (15:30 +0100)
build.gradle
gradle.properties
utils/getdown/bin/jalview.ps1
utils/getdown/bin/jalview.sh
utils/getdown/bin/update.bat [new file with mode: 0644]
utils/getdown/bin/update.ps1 [new file with mode: 0644]
utils/getdown/bin/update.sh [new file with mode: 0755]
utils/install4j/install4j10_template.install4j

index e0d7aed..87e4ced 100644 (file)
@@ -2388,7 +2388,14 @@ task getdownWebsiteBuild() {
       }
     }
     
-    def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
+    def getdownWrapperScripts = [
+      getdown_bash_wrapper_script,
+      getdown_powershell_wrapper_script,
+      getdown_batch_wrapper_script,
+      getdown_bash_update_script,
+      getdown_powershell_update_script,
+      getdown_batch_update_script
+    ]
     getdownWrapperScripts.each{ script ->
       def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
       if (s.exists()) {
@@ -3025,6 +3032,9 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
     'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
     'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
     'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
+    'BASH_UPDATE_SCRIPT': getdown_bash_update_script,
+    'POWERSHELL_UPDATE_SCRIPT': getdown_powershell_update_script,
+    'BATCH_UPDATE_SCRIPT': getdown_batch_update_script,
     'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
     'MACOSARCHIVE_X64_NAME': install4jmacOSArchiveX64Name,
     'MACOSARCHIVE_AARCH64_NAME': install4jmacOSArchiveAarch64Name,
index 0b722ef..23d745a 100644 (file)
@@ -166,6 +166,9 @@ getdown_wrapper_script_dir = bin
 getdown_bash_wrapper_script = jalview.sh
 getdown_powershell_wrapper_script = jalview.ps1
 getdown_batch_wrapper_script = jalview.bat
+getdown_bash_update_script = update.sh
+getdown_powershell_update_script = update.ps1
+getdown_batch_update_script = update.bat
 
 OSX_KEYSTORE =
 OSX_KEYPASS =
index d57b1de..edf0e49 100755 (executable)
@@ -16,41 +16,34 @@ if ( $IsWindows -eq $null ) {
   $myIsMacOS = $IsMacOS
 }
 
-# are we running an update
-[bool] $update = 0
-[bool] $updateuser = 0
-[bool] $updateinstallation = 0
 [bool] $headless = 0
-[bool] $help = 0
 [bool] $gui = 0
+[bool] $help = 0
 [bool] $debug = 0
-for ( $i = 0; $i -lt $args.count; $i++ ) {
-  if ( $args[$i] -eq "--updateuser" ) {
-    $update = 1
-    $updateuser = 1
-    $headless = 1
-  }
-  if ( $args[$i] -eq "--updateinstallation" ) {
-    $update = 1
-    $updateinstallation = 1
-    $headless = 1
-  }
-  if ( $args[$i] -eq "--help" -or $args[$i].StartsWith("--help-") -or $args[$i] -eq "--version" -or $args[$i] -eq "-h" ) {
+Switch -Regex ($args) {
+  "--help|--help-|--version|-h" {
     $help = 1
     $headless = 1
+    Continue
   }
-  if ( $args[$i] -eq "--gui" ) {
+  "--gui" {
     $gui = 1
+    Continue
+  }
+  "--headless" {
+    $headless = 1
+    Continue
   }
-  if ( $args[$i] -eq "--debug" ) {
+  "--debug" {
     $debug = 1
+    Continue
   }
 }
 if ( $help ) {
-  $gui = 0
-} elseif ( $update ) {
+  # --help takes precedence
   $gui = 0
 } elseif ( $gui ) {
+  # --gui takes precedence over --headless
   $headless = 0
 }
 
@@ -90,15 +83,16 @@ if ( $MyInvocation.MyCommand.Path -eq $null ) {
   throw "Script or link to script must have extension .ps1"
 }
 
+# args for the JVM
+$JVMARGS = @()
+
 $CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
 $SCRIPTPATH = Readlink-f -Link $CMDPATH
 $SCRIPT = $SCRIPTPATH
 $DIR = Split-Path -Path $SCRIPTPATH -Parent
-
 $APPDIR = If ( ( Split-Path -Path $DIR -Leaf ) -eq "bin" ) { Split-Path -Path $DIR -Parent } Else { $DIR }
 $JAVAEXE = If ( $myIsWindows ) { "java.exe" } Else { "java" }
 $JAVA = Join-Path -Path $APPDIR -ChildPath ( "jre/" + $( If ( $myIsMacOS ) { "Contents/Home/" } Else { "" } ) + "bin/${JAVAEXE}" )
-$JVMARGS = @()
 
 if ( $headless ) {
   # not setting java.awt.headless in java invocation of running jalview due to problem with Jmol
@@ -112,63 +106,35 @@ if ( $headless ) {
 }
 
 $GETDOWNTXT = Join-Path -Path $APPDIR -ChildPath "getdown.txt"
-$GETDOWNJAR = Join-Path -Path $APPDIR -ChildPath "getdown-launcher.jar"
 
-# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
-if ( -not ( Test-Path -Path "${JAVA}" ) ) {
-  Write-Host "Cannot find bundled ${JAVAEXE}. Using system ${JAVAEXE} and hoping for the best!"
-  $JAVA = $JAVAEXE
+# look for getdown.txt -- needed to create classpath
+if ( -not ( Test-Path -Path "${GETDOWNTXT}" ) ) {
+  throw "Cannot find ${GETDOWNTXT}"
 }
 
+# launching Jalview with jalview.bin.Launcher instead of getdown-launcher.jar
 $CLASS = "jalview.bin.Launcher"
-if ( $update ) {
-
-  # JUST RUNNING A GETDOWN INSTALLATION UPDATE, not launching Jalview
-  $CLASS = "com.threerings.getdown.launcher.GetdownApp"
-
-  $CLASSPATH = $GETDOWNJAR
-
-  # installation update
-  if ( $updateinstallation ) {
-    $JVMARGS += "-Dnouserdefaultappdir=true"
-  }
-
-  # tell getdown to update jalview without launching
-  $JVMARGS += "-Dsilent=true"
-  $JVMARGS += "-Dlauncher.script=${SCRIPT}"
-  $JVMARGS += "-Dlauncher.update=true"
-  $JVMARGS += "-Dappid=jalview"
-  $JVMARGS += "-Dinstaller.appdir=${APPDIR}"
-  # resetting ARGS to just these two values
-  $myArgs = @("${APPDIR}", "jalview")
-  # add these Just In Case although jalview shouldn't be launched
-  $myArgs += "--headless", "--quit", "--nojavaconsole", "--nosplash", "--nonews"
 
-} else {
-
-  # LAUNCHING JALVIEW without getdown-launcher
-  $CLASS = "jalview.bin.Launcher"
-  # look for getdown.txt -- needed to create classpath
-  if ( -not ( Test-Path -Path "${GETDOWNTXT}" ) ) {
-    throw "Cannot find ${GETDOWNTXT}"
-  }
-  # get CLASSPATH from the code= entries in getdown.txt
-  $CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
+# get CLASSPATH from the code= entries in getdown.txt
+$CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
 
-  # get console width
-  $CONSOLEWIDTH = $Host.UI.RawUI.WindowSize.Width
-  $JVMARGS += "-DCONSOLEWIDTH=${CONSOLEWIDTH}"
-  $JVMARGS += "-Dgetdownappdir=${APPDIR}"
-  $JVMARGS += "-Dinstaller.appdir=${APPDIR}"
-  $JVMARGS += "-Dlauncher.appdir=${APPDIR}"
-  $JVMARGS += "-Dlauncher.script=${SCRIPT}"
+# get console width
+$COLUMNS = $Host.UI.RawUI.WindowSize.Width
+$JVMARGS += "-DCONSOLEWIDTH=${COLUMNS}"
+$JVMARGS += "-Dgetdownappdir=${APPDIR}"
+$JVMARGS += "-Dinstaller.appdir=${APPDIR}"
+$JVMARGS += "-Dlauncher.script=${SCRIPT}"
 
+# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
+if ( -not ( Test-Path -Path "${JAVA}" ) ) {
+  Write-Host "Cannot find bundled ${JAVA}. Using system ${JAVAEXE} and hoping for the best!"
+  $JAVA = $JAVAEXE
 }
 
-# we should always have at least one JVMARGS
+# we should always have at least one JVMARGS so this is okay
 $myJvmArgsString = '"' + $($JVMARGS -join '" "') + '"'
 
-# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate command not string
+# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate a command not string
 if ( $myArgs.count -eq 0 ) {
   $COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${CLASSPATH}`" $CLASS"
 } else {
@@ -182,8 +148,3 @@ if ( $debug -or $help ) {
 
 Invoke-Expression -Command ${COMMAND}
 
-# move updated getdown-launcher.jar into place
-if ( $update )
-  Invoke-Expression -Command "& `"${JAVA}`" ${myJvmArgString} -cp `"${GDCOREPATH}`" jalview.bin.GetdownUpdate `"${APPDIR}`"
-fi
-
index 58a13d8..95e6fae 100755 (executable)
@@ -2,34 +2,6 @@
 
 declare -a ARGS=("${@}")
 
-# this whole next part is because there's no readlink -f in Darwin
-function readlinkf() {
-  FINDFILE="$1"
-  FILE="${FINDFILE}"
-  PREVFILE=""
-  C=0
-  MAX=100 # just in case we end up in a loop
-  FOUND=0
-  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
-    PREVFILE="${FILE}"
-    FILE="$(readlink "${FILE}")"
-    if [ -z "${FILE}" ]; then
-      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
-      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
-      FOUND=1
-    elif [ "${FILE#/}" = "${FILE}" ]; then
-      # FILE is not an absolute path link, we need to add the relative path to the previous dir
-      FILE="$(dirname "${PREVFILE}")/${FILE}"
-    fi
-    C=$((C+1))
-  done
-  if [ "${FOUND}" -ne 1 ]; then
-    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
-    exit 1
-  fi
-  echo "${FILE}"
-}
-
 ISMACOS=0
 if [ "$( uname -s )" = "Darwin" ]; then
   ISMACOS=1
@@ -40,9 +12,6 @@ HEADLESS=0
 GUI=0
 HELP=0
 DEBUG=0
-UPDATE=0
-UPDATEUSER=0
-UPDATEINSTALLATION=0
 for RAWARG in "${@}"; do
   ARG="${RAWARG%%=*}"
   case "${ARG}" in
@@ -59,29 +28,44 @@ for RAWARG in "${@}"; do
     --debug)
       DEBUG=1
       ;;
-    --updateinstallation)
-      UPDATE=1
-      UPDATEINSTALLATION=1
-      HEADLESS=1
-      ;;
-    --updateuser)
-      UPDATE=1
-      UPDATEUSER=1
-      HEADLESS=1
-      ;;
   esac
 done
 if [ "${HELP}" = 1 ]; then
   # --help takes precedence
   GUI=0
-elif [ "${UPDATE}" = 1 ]; then
-  # --update takes precedence over everything else, we don't run jalview!
-  GUI=0
 elif [ "${GUI}" = 1 ]; then
   # --gui takes precedence over --headless
   HEADLESS=0
 fi
 
+# this whole next part is because there's no readlink -f in Darwin
+function readlinkf() {
+  FINDFILE="$1"
+  FILE="${FINDFILE}"
+  PREVFILE=""
+  C=0
+  MAX=100 # just in case we end up in a loop
+  FOUND=0
+  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
+    PREVFILE="${FILE}"
+    FILE="$(readlink "${FILE}")"
+    if [ -z "${FILE}" ]; then
+      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
+      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
+      FOUND=1
+    elif [ "${FILE#/}" = "${FILE}" ]; then
+      # FILE is not an absolute path link, we need to add the relative path to the previous dir
+      FILE="$(dirname "${PREVFILE}")/${FILE}"
+    fi
+    C=$((C+1))
+  done
+  if [ "${FOUND}" -ne 1 ]; then
+    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
+    exit 1
+  fi
+  echo "${FILE}"
+}
+
 # args for the JVM
 declare -a JVMARGS=()
 
@@ -91,22 +75,18 @@ if [ "${ISMACOS}" = 1 ]; then
   SCRIPT="$(readlinkf "$0")"
   DIR="$(dirname "${SCRIPT}")"
   APPDIR="${DIR%/bin}"
-  JAVA="${APPDIR}/jre/Contents/Home/bin/java"
+  JAVABIN="${APPDIR}/jre/Contents/Home/bin"
+  JAVA="${JAVABIN}/java"
   if [ "${HEADLESS}" != 1 ]; then
    JVMARGS=( "${JVMARGS[@]}" "-Xdock:icon=${APPDIR}/resource/jalview_logo.png" )
   fi
-  # e.g. APPFOLDER=/Applications/Jalview.app
-  APPFOLDER="${APPDIR%%.app/*}.app"
-  VMOPTIONS="${APPFOLDER}/Contents/vmoptions.txt"
 else
 # NOT MACOS
   SCRIPT="$(readlink -f "$0")"
   DIR="$(dirname "${SCRIPT}")"
   APPDIR="${DIR%/bin}"
-  JAVA="${APPDIR}/jre/bin/java"
-  # e.g. APPFOLDER=/opt/jalview
-  APPFOLDER="${APPDIR}"
-  VMOPTIONS="${APPDIR}/jalviewg.vmoptions"
+  JAVABIN="${APPDIR}/jre/bin"
+  JAVA="${JAVABIN}/java"
 fi
 
 # headless java arguments
@@ -123,124 +103,46 @@ fi
 
 SYSJAVA=java
 GETDOWNTXT="${APPDIR}/getdown.txt"
-GETDOWNJAR="${APPDIR}/getdown-launcher.jar"
 CHANNELPROPS="${APPDIR}/channel.props"
-NAME="$( grep app_name= App/channel.props | cut -d= -f2 )"
-US_NAME="${NAME// /_}"
+NAME="$( grep app_name= "${CHANNELPROPS}" | cut -d= -f2 )"
 
 CLASSPATH=""
 # save an array of JAR paths in case we're in WSL (see later)
 declare -a JARPATHS=()
-declare -a APPJARPATHS=()
-GDCOREPATH="${APPDIR}/resource/getdown-core.jar"
 
-# get classpath from getdown.txt
-if [ -e "${GETDOWNTXT}" ]; then
-  # always check grep and sed regexes on macos -- they're not the same
-  for JAR in $(grep -e '^code[[:space:]]*=[[:space:]]*' "${GETDOWNTXT}" | while read -r line; do echo $line | sed -E -e 's/code[[:space:]]*=[[:space:]]*//;'; done);
-  do
-    [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH}:"
-    CLASSPATH="${CLASSPATH}${APPDIR}/${JAR}"
-    APPJARPATHS=( "${APPJARPATHS[@]}" "${APPDIR}/${JAR}" )
-    if [ "${JAR}" != "${JAR%getdown-core.jar}" -a \! -e "${GDCOREPATH}" ]; then
-      GDCOREPATH="${APPDIR}/${JAR}"
-    fi
-  done
-else
-  echo "Cannot find getdown.txt" >&2
+# look for getdown.txt -- needed to create classpath
+if [ \! -e "${GETDOWNTXT}" ]; then
+  echo "Cannot find ${GETDOWNTXT}" >&2
   exit 3
 fi
 
-CLASS="jalview.bin.Launcher" # set as default Just In Case
-if [ "${UPDATE}" = 1 ]; then
-
-  # GETDOWN UPDATE ONLY, not launching Jalview
-  CLASS="com.threerings.getdown.launcher.GetdownApp"
-  if [ -e "${GETDOWNJAR}" ]; then
-    CLASSPATH="${GETDOWNJAR}"
-    JARPATHS=( "${JARPATHS[@]}" "${GETDOWNJAR}" )
-  else
-    echo "Cannot find getdown-launcher.jar to run update" >&2
-    exit 13
-  fi
+# launching Jalview with jalview.bin.Launcher instead of getdown-launcher.jar
+CLASS="jalview.bin.Launcher"
 
-  # USER space update
-  if [ "${UPDATEUSER}" = 1 ]; then
-    if [ -e "${VMOPTIONS}" ]; then
-      LINENUM=0
-      while IFS= read -r line; do
-        # remove comments
-        line=${line%%#*}
-        # remove trailing whitespace
-        line="${line%"${line##*[![:space:]]}"}"
-        LINENUM=$((LINENUM+1))
-
-        # add settings for user appdir
-        if [ "${line}" != "${line#-Dsetuserappdirpath=}" ]; then # starts with -Dsetuserappdirpath=
-          JVMARGS=( "${JVMARGS[@]}" "${line}" )
-        fi
-        if [ "${line}" != "${line#-Dnouserdefaultappdir=}" ]; then # starts with -Dnouserdefaultappdir=
-          # don't perform user update if user updates are disabled
-          NOUSERUPDATEVALUE="$(echo "${line#-Dnouserdefaultappdir=}" | tr '[:upper:]' '[:lower:]')"
-          if [ "${NOUSERUPDATEVALUE}" = "true" ]; then
-            echo "Cannot perform manual user-space update when user-space updates are disabled." >&2
-            echo "See line ${LINENUM} (${line}) in file '${VMOPTIONS}'." >&2
-            exit 14
-          fi
-          JVMARGS=( "${JVMARGS[@]}" "${line}" )
-        fi
-      done < "${VMOPTIONS}"
-    fi
-    JVMARGS=( "${JVMARGS[@]}" "-Duserdefaultappdir=true" )
-    # resetting ARGS to just these two blank values so userappdir is determined
-    ARGS=( "" "jalview" )
-  fi
-
-  # INSTALLATION update
-  if [ "${UPDATEINSTALLATION}" = 1 ]; then
-    JVMARGS=( "${JVMARGS[@]}" "-Dnouserdefaultappdir=true" )
-    JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.appdir=${APPDIR}" )
-    # resetting ARGS to just these two values
-    ARGS=( "${APPDIR}" "jalview" )
-  fi
-
-  # both USER and INSTALLATION updates
-  JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.appdir=${APPDIR}" )
-
-  # tell getdown to update jalview withouth launching
-  JVMARGS=( "${JVMARGS[@]}" "-Dsilent=true" )
-
-  JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.update=true" )
-  JVMARGS=( "${JVMARGS[@]}" "-Dappid=jalview" )
-  JVMARGS=( "${JVMARGS[@]}" "-Dchannel.app_name=${NAME}" )
-  JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.application_folder=${US_NAME}" )
-
-  # add these Just In Case although jalview shouldn't be launched due to -Dsilent=true
-  ARGS=( "${ARGS[@]}" "--headless" "--quit" "--nojavaconsole" "--nosplash" "--nonews" )
-
-else
-
-  # LAUNCHING JALVIEW without getdown-launcher
-  CLASS="jalview.bin.Launcher"
-  JARPATHS="${APPJARPATHS[@]}"
-
-  COLUMNS=80
-  # get console width -- three ways to try, just in case (not needed for update)
-  if command -v tput 2>&1 >/dev/null; then
-    COLUMNS=$(tput cols) 2>/dev/null
-  elif command -v stty 2>&1 >/dev/null; then
-    COLUMNS=$(stty size | cut -d" " -f2) 2>/dev/null
-  elif command -v resize 2>&1 >/dev/null; then
-    COLUMNS=$(resize -u | grep COLUMNS= | sed -e 's/.*=//;s/;//') 2>/dev/null
-  fi
-
-  JVMARGS=( "${JVMARGS[@]}" "-DCONSOLEWIDTH=${COLUMNS}" )
-  JVMARGS=( "${JVMARGS[@]}" "-Dgetdownappdir=${APPDIR}" )
+# get classpath from the code= entries in getdown.txt
+# always check grep and sed regexes on macos -- they're not the same
+for JAR in $(grep -e '^code[[:space:]]*=[[:space:]]*' "${GETDOWNTXT}" | while read -r line; do echo $line | sed -E -e 's/code[[:space:]]*=[[:space:]]*//;'; done);
+do
+  [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH}:"
+  CLASSPATH="${CLASSPATH}${APPDIR}/${JAR}"
+  JARPATHS=( "${JARPATHS[@]}" "${APPDIR}/${JAR}" )
+done
 
+COLUMNS=80
+# get console width -- three ways to try, just in case (not needed for update)
+if command -v tput 2>&1 >/dev/null; then
+  COLUMNS=$(tput cols) 2>/dev/null
+elif command -v stty 2>&1 >/dev/null; then
+  COLUMNS=$(stty size | cut -d" " -f2) 2>/dev/null
+elif command -v resize 2>&1 >/dev/null; then
+  COLUMNS=$(resize -u | grep COLUMNS= | sed -e 's/.*=//;s/;//') 2>/dev/null
 fi
+JVMARGS=( "${JVMARGS[@]}" "-DCONSOLEWIDTH=${COLUMNS}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dgetdownappdir=${APPDIR}" )
 JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.script=${SCRIPT}" )
 JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.appdir=${APPDIR}" )
 
+JAVAEXT=""
 # WINDOWS ONLY in Cygwin or Windows Subsystem for Linux (WSL)
 # change paths for Cygwin or WSL
 if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoid
@@ -274,14 +176,19 @@ if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoi
         ARGS=( "${ARGS[@]}" "${ARG}" )
       fi
     done
-    JAVA="${JAVA}.exe"
-    SYSJAVA="java.exe"
+    JAVAEXT=".exe"
+    JAVA="${JAVA}${JAVAEXT}"
+    SYSJAVA="java${JAVAEXT}"
   fi
 fi
 
-# Is there a bundled Java?  If not just try one in the PATH (we need .exe in WSL, added above)
+# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
+if [ -e "${JAVABIN}/${NAME}${JAVAEXT}" ]; then
+  JAVA="${JAVABIN}/${NAME}${JAVAEXT}"
+fi
+# If not just try one in the PATH (we need .exe in WSL, added above)
 if [ \! -e "${JAVA}" ]; then
-  JAVA=$SYSJAVA
+  JAVA="${SYSJAVA}"
   echo "Cannot find bundled ${JAVA}, using system ${SYSJAVA} and hoping for the best!" >&2
 fi
 
@@ -297,6 +204,7 @@ function quotearray() {
   echo $QUOTEDVALS
 }
 
+# for the debug command display
 JVMARGSSTR=$(quotearray "${JVMARGS[@]}")
 ARGSSTR=$(quotearray "${ARGS[@]}")
 
@@ -305,11 +213,3 @@ if [ "${DEBUG}" = 1 ]; then
 fi
 
 "${JAVA}" "${JVMARGS[@]}" -cp "${CLASSPATH}" "${CLASS}" "${ARGS[@]}"
-
-# move updated getdown-launcher.jar into place
-if [ "${DEBUG}" = 1 ]; then
-  echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${GDCOREPATH}"\" jalview.bin.GetdownLauncherUpdate >&2
-fi
-if [ "${UPDATE}" = 1 ]; then
-  "${JAVA}" "${JVMARGS[@]}" -cp "${GDCOREPATH}" jalview.bin.GetdownLauncherUpdate
-fi
diff --git a/utils/getdown/bin/update.bat b/utils/getdown/bin/update.bat
new file mode 100644 (file)
index 0000000..c88225d
--- /dev/null
@@ -0,0 +1,50 @@
+@ECHO OFF
+
+REM This is the Jalview batch script wrapper to run the powershell script of the same name.
+REM There is nothing specific to Jalview.
+
+REM ******************************************************************************
+REM If you need to set a full path to the PowerShell executable please do so here:
+SET PWSHPATH=
+REM ******************************************************************************
+
+REM This is some DOS magic to substitute the extension in the full path of this batch script with .ps1
+SET SCRIPTPATH=%~dpn0.ps1
+
+REM PowerShell script isn't where it should be!
+IF NOT EXIST "%SCRIPTPATH%" (
+  ECHO Could not find PowerShell script "%SCRIPTPATH%".  Is %~nx0 in the right folder?
+  EXIT /B 1
+)
+
+REM Look for either pwsh.exe or powershell.exe if not set in PWSHPATH above.
+REM pwsh.exe is preferred as it is likely to be a newer version.
+SET PWSH=
+IF DEFINED PWSHPATH (
+  SET PWSH=%PWSHPATH%
+)
+FOR %%X IN (pwsh.exe powershell.exe) DO (
+  IF NOT DEFINED PWSH ( 
+    IF NOT "%%~$PATH:X" == "" (
+      REM Found a PowerShell executable in the PATH
+      SET PWSH="%%X"
+      GOTO end_looking
+    )
+  )
+)
+:end_looking
+
+IF NOT DEFINED PWSH (
+  REM No PowerShell executable found -- tell the user what to do.
+  ECHO No PowerShell found in %%PATH%%. If PowerShell is installed either
+  ECHO 1. add it to your PATH, or
+  ECHO 2. edit the PWSHPATH value at the top of this file:
+  ECHO    "%~dpnx0"
+  ECHO.
+  ECHO %~n0 on the command line requires PowerShell. To install PowerShell see
+  ECHO https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell
+  EXIT /B 2
+)
+
+REM Run the PowerShell script
+"%PWSH%" -NoProfile -ExecutionPolicy Bypass -Command "& '%SCRIPTPATH%' %*";
diff --git a/utils/getdown/bin/update.ps1 b/utils/getdown/bin/update.ps1
new file mode 100644 (file)
index 0000000..4914dd8
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/env pwsh
+
+# save args and first parameter
+$myArgs = $args.Clone()
+$myArg1 = $args[0]
+
+# setup for powershell version < 6.0
+[bool] $myIsWindows = 0
+[bool] $myIsMacOS = 0
+if ( $IsWindows -eq $null ) {
+  # for powershell version < 6.0 let's assume Windows
+  $myIsWindows = 1
+  $myIsMacOS = 0
+} else {
+  $myIsWindows = $IsWindows
+  $myIsMacOS = $IsMacOS
+}
+
+[bool] $headless = 0
+[bool] $gui = 0
+[bool] $help = 0
+[bool] $debug = 0
+Switch -Regex ($args) {
+  "--help|--help-|--version|-h" {
+    $help = 1
+    $headless = 1
+    Continue
+  }
+  "--gui" {
+    $gui = 1
+    Continue
+  }
+  "--headless" {
+    $headless = 1
+    Continue
+  }
+  "--debug" {
+    $debug = 1
+    Continue
+  }
+}
+if ( $help ) {
+  # --help takes precedence
+  $gui = 0
+} elseif ( $gui ) {
+  # --gui takes precedence over --headless
+  $headless = 0
+}
+
+# parent dir of this actual script (which should be the getdown appdir/bin). Follow all symlinks.  Like GNU readlink -f
+function Readlink-f {
+  Param($Link)
+  $Return = $null
+  $c = 0
+  $max = 100 # just in case we end up in a loop
+  [bool] $found = 0
+  $file = Get-Item -Path $Link
+  $prevfile = $null
+  While ( $c -lt $max -and "${file}" -ne "${prevfile}" -and -not $found ) {
+    $prevfile = $file
+    [string] $target = ( $file ).Target
+    If ( $target -eq $null -or ( $file ).LinkType -ne "SymbolicLink" ) {
+      $Return = $file
+      $found = 1
+    } Else {
+      If ( $( Split-Path -Path $target -IsAbsolute ) ) {
+        $file = Get-Item -Path $target
+      } Else {
+# symbolic link is relative: combine previous link parent dir with the link target and resolve
+        $file = Get-Item -Path ( Join-Path -Path ( Split-Path -Path $prevfile -Parent ) -ChildPath $target -Resolve )
+      }
+    }
+    $c++
+  }
+  if ( -not $found ) {
+    throw "Could not determine path to actual file $( Split-Path -Path $Link -Leaf )"
+  }
+  $Return
+}
+
+# Avert problem with unix version of powershell and tell user the reason (Windows must always have .ps1 extension)
+if ( $MyInvocation.MyCommand.Path -eq $null ) {
+  throw "Script or link to script must have extension .ps1"
+}
+
+$CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
+$SCRIPTPATH = Readlink-f -Link $CMDPATH
+$SCRIPT = $SCRIPTPATH
+$DIR = Split-Path -Path $SCRIPTPATH -Parent
+
+$APPDIR = If ( ( Split-Path -Path $DIR -Leaf ) -eq "bin" ) { Split-Path -Path $DIR -Parent } Else { $DIR }
+$JAVAEXE = If ( $myIsWindows ) { "java.exe" } Else { "java" }
+$JAVA = Join-Path -Path $APPDIR -ChildPath ( "jre/" + $( If ( $myIsMacOS ) { "Contents/Home/" } Else { "" } ) + "bin/${JAVAEXE}" )
+$JVMARGS = @()
+
+if ( $headless ) {
+  # not setting java.awt.headless in java invocation of running jalview due to problem with Jmol
+  if ( $help ) {
+    $JVMARGS += "-Djava.awt.headless=true"
+  }
+  # this suppresses the Java icon appearing in the macOS Dock
+  if ( $myIsMacOS ) {
+    $JVMARGS += "-Dapple.awt.UIElement=true"
+  }
+}
+
+$GETDOWNTXT = Join-Path -Path $APPDIR -ChildPath "getdown.txt"
+
+# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
+if ( -not ( Test-Path -Path "${JAVA}" ) ) {
+  Write-Host "Cannot find bundled ${JAVA}. Using system ${JAVAEXE} and hoping for the best!"
+  $JAVA = $JAVAEXE
+}
+
+# launching Jalview with jalview.bin.Launcher instead of getdown-launcher.jar
+$CLASS = "jalview.bin.Launcher"
+# look for getdown.txt -- needed to create classpath
+if ( -not ( Test-Path -Path "${GETDOWNTXT}" ) ) {
+  throw "Cannot find ${GETDOWNTXT}"
+}
+# get CLASSPATH from the code= entries in getdown.txt
+$CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
+
+# get console width
+$COLUMNS = $Host.UI.RawUI.WindowSize.Width
+$JVMARGS += "-DCONSOLEWIDTH=${COLUMNS}"
+$JVMARGS += "-Dgetdownappdir=${APPDIR}"
+$JVMARGS += "-Dinstaller.appdir=${APPDIR}"
+$JVMARGS += "-Dlauncher.appdir=${APPDIR}"
+$JVMARGS += "-Dlauncher.script=${SCRIPT}"
+
+# we should always have at least one JVMARGS so this is okay
+$myJvmArgsString = '"' + $($JVMARGS -join '" "') + '"'
+
+# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate a command not string
+if ( $myArgs.count -eq 0 ) {
+  $COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${CLASSPATH}`" $CLASS"
+} else {
+  $myArgsString = '"' + $($myArgs -join '" "') + '"'
+  $COMMAND = "& `"${JAVA}`" ${myJvmArgsString} -cp `"${CLASSPATH}`" $CLASS ${myArgsString}"
+}
+
+if ( $debug -or $help ) {
+  Write-Error -Message "Shell running: ${COMMAND}"
+}
+
+Invoke-Expression -Command ${COMMAND}
+
diff --git a/utils/getdown/bin/update.sh b/utils/getdown/bin/update.sh
new file mode 100755 (executable)
index 0000000..92c28c2
--- /dev/null
@@ -0,0 +1,232 @@
+#!/usr/bin/env bash
+
+declare -a ARGS=("${@}")
+
+# this whole next part is because there's no readlink -f in Darwin
+function readlinkf() {
+  FINDFILE="$1"
+  FILE="${FINDFILE}"
+  PREVFILE=""
+  C=0
+  MAX=100 # just in case we end up in a loop
+  FOUND=0
+  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
+    PREVFILE="${FILE}"
+    FILE="$(readlink "${FILE}")"
+    if [ -z "${FILE}" ]; then
+      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
+      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
+      FOUND=1
+    elif [ "${FILE#/}" = "${FILE}" ]; then
+      # FILE is not an absolute path link, we need to add the relative path to the previous dir
+      FILE="$(dirname "${PREVFILE}")/${FILE}"
+    fi
+    C=$((C+1))
+  done
+  if [ "${FOUND}" -ne 1 ]; then
+    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
+    exit 1
+  fi
+  echo "${FILE}"
+}
+
+ISMACOS=0
+if [ "$( uname -s )" = "Darwin" ]; then
+  ISMACOS=1
+fi
+
+DEBUG=0
+USERSPACE=0
+INSTALLATION=0
+for RAWARG in "${@}"; do
+  ARG="${RAWARG%%=*}"
+  case "${ARG}" in
+    --debug)
+      DEBUG=1
+      ;;
+    --installation)
+      INSTALLATION=1
+      ;;
+    --userspace)
+      USERSPACE=1
+      ;;
+    *)
+      echo "Unkown option '${ARG}'" >&2
+      exit 2
+  esac
+done
+
+# args for the JVM
+declare -a JVMARGS=()
+
+# set vars for being inside the macos App Bundle
+if [ "${ISMACOS}" = 1 ]; then
+# MACOS ONLY
+  SCRIPT="$(readlinkf "$0")"
+  DIR="$(dirname "${SCRIPT}")"
+  APPDIR="${DIR%/bin}"
+  JAVABIN="${APPDIR}/jre/Contents/Home/bin"
+  JAVA="${JAVABIN}/java"
+  # e.g. APPFOLDER=/Applications/Jalview.app
+  APPFOLDER="${APPDIR%%.app/*}.app"
+  VMOPTIONS="${APPFOLDER}/Contents/vmoptions.txt"
+else
+# NOT MACOS
+  SCRIPT="$(readlink -f "$0")"
+  DIR="$(dirname "${SCRIPT}")"
+  APPDIR="${DIR%/bin}"
+  JAVABIN="${APPDIR}/jre/bin"
+  JAVA="${JAVABIN}/java"
+  # e.g. APPFOLDER=/opt/jalview
+  APPFOLDER="${APPDIR}"
+  VMOPTIONS="${APPDIR}/jalviewg.vmoptions"
+fi
+
+# headless java arguments
+JVMARGS=( "${JVMARGS[@]}" "-Djava.awt.headless=true" )
+if [ "${ISMACOS}" = 1 ]; then
+  JVMARGS=( "${JVMARGS[@]}" "-Dapple.awt.UIElement=true" )
+fi
+
+SYSJAVA=java
+GETDOWNTXT="${APPDIR}/getdown.txt"
+GETDOWNLAUNCHERJAR="${APPDIR}/getdown-launcher.jar"
+GETDOWNCOREJAR="${APPDIR}/resource/getdown-core.jar"
+CHANNELPROPS="${APPDIR}/channel.props"
+NAME="$( grep app_name= "${CHANNELPROPS}" | cut -d= -f2 )"
+echo $NAME
+US_NAME="${NAME// /_}"
+
+GDL_CLASSPATH=""
+GDC_CLASSPATH=""
+# save an array of JAR paths in case we're in WSL (see later)
+declare -a GDL_JARPATHS=()
+declare -a GDC_JARPATHS=()
+declare -a GDL_ARGS=()
+
+# getdown-launcher classpath
+if [ -e "${GETDOWNLAUNCHERJAR}" ]; then
+  GDL_CLASSPATH="${GETDOWNLAUNCHERJAR}"
+  GDL_JARPATHS=( "${GDL_JARPATHS[@]}" "${GETDOWNLAUNCHERJAR}" )
+else
+  echo "Cannot find $( basename "${GETDOWNLAUNCHERJAR}" ) to run update" >&2
+  exit 3
+fi
+
+# getdown-core classpath
+if [ -e "${GETDOWNCOREJAR}" ]; then
+  GDC_CLASSPATH="${GETDOWNCOREJAR}"
+  GDC_JARPATHS=( "${GDC_JARPATHS[@]}" "${GETDOWNCOREJAR}" )
+else
+  echo "Cannot find $( basename "${GETDOWNCOREJAR}" ) to run update" >&2
+  exit 4
+fi
+
+# USER space update
+if [ "${USERSPACE}" = 1 ]; then
+  if [ -e "${VMOPTIONS}" ]; then
+    LINENUM=0
+    while IFS= read -r line; do
+      # remove comments
+      line=${line%%#*}
+      # remove trailing whitespace
+      line="${line%"${line##*[![:space:]]}"}"
+      LINENUM=$((LINENUM+1))
+
+      # add settings for user appdir
+      if [ "${line}" != "${line#-Dsetuserappdirpath=}" ]; then # starts with -Dsetuserappdirpath=
+        JVMARGS=( "${JVMARGS[@]}" "${line}" )
+      fi
+      if [ "${line}" != "${line#-Dnouserdefaultappdir=}" ]; then # starts with -Dnouserdefaultappdir=
+        # don't perform user update if user updates are disabled
+        NOUSERUPDATEVALUE="$(echo "${line#-Dnouserdefaultappdir=}" | tr '[:upper:]' '[:lower:]')"
+        if [ "${NOUSERUPDATEVALUE}" = "true" ]; then
+          echo "Cannot perform manual user-space update when user-space updates are disabled." >&2
+          echo "See line ${LINENUM} (${line}) in file '${VMOPTIONS}'." >&2
+          exit 5
+        fi
+        JVMARGS=( "${JVMARGS[@]}" "${line}" )
+      fi
+    done < "${VMOPTIONS}"
+  fi
+  JVMARGS=( "${JVMARGS[@]}" "-Duserdefaultappdir=true" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dnouserdefaultappdir=false" )
+  # setting the appdir arg[0] to a blank value so appDir is determined by EnvConfig.getUserAppdir()
+fi
+
+# INSTALLATION update
+if [ "${INSTALLATION}" = 1 ]; then
+  JVMARGS=( "${JVMARGS[@]}" "-Duserdefaultappdir=false" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dnouserdefaultappdir=true" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.appdir=${APPDIR}" )
+  JVMARGS=( "${JVMARGS[@]}" "-Dappdir=${APPDIR}" )
+  # set GDL_ARGS to these two values
+fi
+
+GDL_ARGS=( "" "" )
+
+# both USER and INSTALLATION updates
+JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.appdir=${APPDIR}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dinstaller.application_folder=${US_NAME}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.script=${SCRIPT}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dlauncher.update=true" )
+JVMARGS=( "${JVMARGS[@]}" "-Dchannel.app_name=${NAME}" )
+JVMARGS=( "${JVMARGS[@]}" "-Dappid=jalview" )
+
+# IMPORTANT! tell getdown to update jalview withouth launching
+JVMARGS=( "${JVMARGS[@]}" "-Dsilent=true" )
+
+# add these Just In Case although jalview shouldn't be launched due to -Dsilent=true
+GDL_ARGS=( "${GDL_ARGS[@]}" "--headless" "--quit" "--nojavaconsole" "--nosplash" "--nonews" )
+
+# WINDOWS ONLY in Cygwin or Windows Subsystem for Linux (WSL)
+# change paths for Cygwin or WSL
+if [ "${ISMACOS}" != 1 ]; then # older macos doesn't like uname -o, best to avoid
+  if [ "$(uname -o)" = "Cygwin" ]; then
+  # CYGWIN
+    GDL_CLASSPATH=$(cygpath -pw "${GDL_CLASSPATH}")
+  elif uname -r | grep -i microsoft | grep -i wsl >/dev/null; then
+  # WSL
+    GDL_CLASSPATH=""
+    for JARPATH in "${GDL_JARPATHS[@]}"; do
+      [ -n "${GDL_CLASSPATH}" ] && GDL_CLASSPATH="${GDL_CLASSPATH};"
+      GDL_CLASSPATH="${GDL_CLASSPATH}$(wslpath -aw "${JARPATH}")"
+    done
+    JAVA="${JAVA}.exe"
+    SYSJAVA="java.exe"
+  fi
+fi
+
+# Is there a bundled Java?  If not just try one in the PATH (we need .exe in WSL, added above)
+if [ \! -e "${JAVA}" ]; then
+  JAVA=$SYSJAVA
+  echo "Cannot find bundled ${JAVA}, using system ${SYSJAVA} and hoping for the best!" >&2
+fi
+
+# This is just needed for display purposes
+function quotearray() {
+  QUOTEDVALS=""
+  for VAL in "${@}"; do
+    if [ \! "$QUOTEDVALS" = "" ]; then
+      QUOTEDVALS="${QUOTEDVALS} "
+    fi
+    QUOTEDVALS="${QUOTEDVALS}\"${VAL}\""
+  done
+  echo $QUOTEDVALS
+}
+
+JVMARGSSTR=$(quotearray "${JVMARGS[@]}")
+ARGSSTR=$(quotearray "${GDL_ARGS[@]}")
+
+if [ "${DEBUG}" = 1 ]; then
+ echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${GDL_CLASSPATH}"\" com.threerings.getdown.launcher.GetdownApp ${ARGSSTR} >&2
+fi
+
+"${JAVA}" "${JVMARGS[@]}" -cp "${GDL_CLASSPATH}" com.threerings.getdown.launcher.GetdownApp "${GDL_ARGS[@]}"
+
+# move updated getdown-launcher.jar into place
+if [ "${DEBUG}" = 1 ]; then
+  echo Shell running: \""${JAVA}"\" ${JVMARGSSTR} -cp \""${GDC_CLASSPATH}"\" jalview.bin.GetdownLauncherUpdate >&2
+fi
+
+"${JAVA}" "${JVMARGS[@]}" -cp "${GDC_CLASSPATH}" jalview.bin.GetdownLauncherUpdate
index b8de2b6..6c8fe21 100644 (file)
       <variable name="MACOSARCHIVE_VOLUMEICON" />
       <variable name="WRAPPER_LINK" value="jalview" />
       <variable name="BASH_WRAPPER_SCRIPT" value="jalview.sh" />
+      <variable name="BATCH_WRAPPER_SCRIPT" value="jalview.bat" />
+      <variable name="POWERSHELL_WRAPPER_SCRIPT" value="jalview.ps1" />
+      <variable name="BASH_UPDATE_SCRIPT" value="update.sh" />
+      <variable name="BATCH_UPDATE_SCRIPT" value="update.bat" />
+      <variable name="POWERSHELL_UPDATE_SCRIPT" value="update.ps1" />
       <variable name="WRAPPER_SCRIPT_BIN_DIR" value="bin" />
       <variable name="MACOSARCHIVE_X64_NAME" value="Install Jalview (Intel)" />
       <variable name="MACOSARCHIVE_AARCH64_NAME" value="Install Jalview (Apple Silicon)" />
@@ -66,8 +71,6 @@
       <variable name="WINDOWS_ICONS_FILE" value="jalview_logo.ico" />
       <variable name="PNG_ICON_FILE" value="jalview_logo.png" />
       <variable name="BACKGROUND" value="utils/channels/release/images/jalview_logo_background_fade-640x480.png" />
-      <variable name="BATCH_WRAPPER_SCRIPT" value="jalview.bat" />
-      <variable name="POWERSHELL_WRAPPER_SCRIPT" value="jalview.ps1" />
       <variable name="WIZARD_WIDTH" value="640" description="Default/initial width of installer wizard window.  Linux media types adapt this.&#xA;NOT USED" />
       <variable name="WIZARD_HEIGHT" value="480" description="Default/initial width of installer wizard window.  Linux media types adapt this.&#xA;NOT USED" />
       <variable name="MACOSARCHIVE_X64_DMG_FILENAME" value="${compiler:APPLICATION_FOLDER}-${compiler:JALVIEW_VERSION}-${compiler:sys.platform}-x64-java_${compiler:JAVA_INTEGER_VERSION}" />
@@ -606,22 +609,22 @@ return null;
                 </serializedBean>
                 <condition>context.getBooleanVariable("makeSymbolicLink")</condition>
               </action>
-              <action name="USERSPACE: Set macWrapperLinkLocation (macOS)" id="2745" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+              <action name="USERSPACE: Set MacOSDir (macOS)" id="2745" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
                 <serializedBean>
                   <property name="script">
                     <object class="com.install4j.api.beans.ScriptProperty">
                       <property name="value" type="string">String javaHome = System.getProperty("java.home");
 String appName = ((String)context.getCompilerVariable("JALVIEW_APPLICATION_NAME")) + ".app";
 int i = javaHome.indexOf(appName);
-String wrapperLink = null;
+String MacOSDir = null;
 if (i &gt; -1) {
-    wrapperLink = javaHome.substring(0, i) + appName + File.separator + "Contents" + File.separator + "MacOS" + File.separator + ((String)context.getCompilerVariable("WRAPPER_LINK"));
+    MacOSDir = javaHome.substring(0, i) + appName + File.separator + "Contents" + File.separator + "MacOS";
 }
-return wrapperLink;
+return MacOSDir;
 </property>
                     </object>
                   </property>
-                  <property name="variableName" type="string">macWrapperLinkLocation</property>
+                  <property name="variableName" type="string">MacOSDir</property>
                 </serializedBean>
                 <condition>Util.isMacOS() &amp;&amp; !context.getBooleanVariable("isAdmin") // Admin on macOS will add path to /etc/paths.d in Create File action</condition>
               </action>
@@ -922,7 +925,7 @@ return console.askOkCancel(message, true);
      || Util.isUnixInstaller()
      || (
           ( Util.isMacOS() &amp;&amp; !Util.hasFullAdminRights() ) // Admin on macOS will add path to /etc/paths.d
-          &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+          &amp;&amp; context.getVariable("MacOSDir") != null
         )
     )</visibilityScript>
               </formComponent>
@@ -1080,7 +1083,7 @@ On ${installer:osName}, user updates will be installed under
      || Util.isUnixInstaller()
      || (
           ( Util.isMacOS() &amp;&amp; !Util.hasFullAdminRights() ) // Admin on macOS will add path to /etc/paths.d
-          &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+          &amp;&amp; context.getVariable("MacOSDir") != null
         )
     )
 </visibilityScript>
@@ -1583,11 +1586,51 @@ return context.getBooleanVariable("allowUserDefaultAppdirUpdates") &amp;&amp; co
                     </serializedBean>
                     <condition>context.getBooleanVariable("appendToPathAction")</condition>
                   </action>
-                  <action name="USERSPACE: Create macOS symbolic link to jalview in user's local bin" id="2920" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                  <action name="USERSPACE: Create macOS symbolic link to jalview.sh" id="2920" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:MacOSDir}">
+                    <serializedBean>
+                      <property name="file">
+                        <object class="java.io.File">
+                          <string>../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}</string>
+                        </object>
+                      </property>
+                      <property name="linkFile">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isMacOS() &amp;&amp;
+(
+  context.getBooleanVariable("makeSymbolicLinkAction")
+  &amp;&amp; context.getVariable("unixBinDir") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
+)</condition>
+                  </action>
+                  <action name="USERSPACE: Create macOS symbolic link to update.sh" id="3052" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:MacOSDir}">
                     <serializedBean>
                       <property name="file">
                         <object class="java.io.File">
-                          <string>${installer:macWrapperLinkLocation}</string>
+                          <string>../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}</string>
+                        </object>
+                      </property>
+                      <property name="linkFile">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/update_${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isMacOS() &amp;&amp;
+(
+  context.getBooleanVariable("makeSymbolicLinkAction")
+  &amp;&amp; context.getVariable("unixBinDir") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
+)</condition>
+                  </action>
+                  <action name="USERSPACE: Create macOS symbolic link to jalview in user's local bin" id="3048" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                    <serializedBean>
+                      <property name="file">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/${compiler:WRAPPER_LINK}</string>
                         </object>
                       </property>
                       <property name="linkFile">
@@ -1600,7 +1643,27 @@ return context.getBooleanVariable("allowUserDefaultAppdirUpdates") &amp;&amp; co
 (
   context.getBooleanVariable("makeSymbolicLinkAction")
   &amp;&amp; context.getVariable("unixBinDir") != null
-  &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
+)</condition>
+                  </action>
+                  <action name="USERSPACE: Create macOS symbolic link to jalview_update in user's local bin" id="3049" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixBinDir}">
+                    <serializedBean>
+                      <property name="file">
+                        <object class="java.io.File">
+                          <string>${installer:MacOSDir}/update_${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                      <property name="linkFile">
+                        <object class="java.io.File">
+                          <string>${installer:unixBinDir}/update_${compiler:WRAPPER_LINK}</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isMacOS() &amp;&amp;
+(
+  context.getBooleanVariable("makeSymbolicLinkAction")
+  &amp;&amp; context.getVariable("unixBinDir") != null
+  &amp;&amp; context.getVariable("MacOSDir") != null
 )</condition>
                   </action>
                 </beans>
@@ -1939,7 +2002,7 @@ return sb.toString();
               </group>
               <group name="Windows scripts" id="2949" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
                 <beans>
-                  <action name="Windows copy BAT file" id="2947" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
+                  <action name="Windows copy BAT jalview file" id="2947" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="destinationFile">
                         <object class="java.io.File">
@@ -1956,7 +2019,7 @@ return sb.toString();
                     </serializedBean>
                     <condition>Util.isWindows() &amp;&amp; !(((String)context.getCompilerVariable("WRAPPER_LINK")+".bat").equals((String)context.getCompilerVariable("BATCH_WRAPPER_SCRIPT")))</condition>
                   </action>
-                  <action name="Windows copy PS1 file" id="2948" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
+                  <action name="Windows copy PS1 jalview file" id="2948" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="destinationFile">
                         <object class="java.io.File">
@@ -1973,6 +2036,40 @@ return sb.toString();
                     </serializedBean>
                     <condition>Util.isWindows() &amp;&amp; !(((String)context.getCompilerVariable("WRAPPER_LINK")+".ps1").equals((String)context.getCompilerVariable("POWERSHELL_WRAPPER_SCRIPT")))</condition>
                   </action>
+                  <action name="Windows copy BAT update file" id="3055" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="destinationFile">
+                        <object class="java.io.File">
+                          <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/update_${compiler:WRAPPER_LINK}.bat</string>
+                        </object>
+                      </property>
+                      <property name="files" type="array" class="java.io.File" length="1">
+                        <element index="0">
+                          <object class="java.io.File">
+                            <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BATCH_UPDATE_SCRIPT}</string>
+                          </object>
+                        </element>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isWindows()</condition>
+                  </action>
+                  <action name="Windows copy PS1 update file" id="3056" beanClass="com.install4j.runtime.beans.actions.files.CopyFileAction" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="destinationFile">
+                        <object class="java.io.File">
+                          <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/update_${compiler:WRAPPER_LINK}.ps1</string>
+                        </object>
+                      </property>
+                      <property name="files" type="array" class="java.io.File" length="1">
+                        <element index="0">
+                          <object class="java.io.File">
+                            <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:POWERSHELL_UPDATE_SCRIPT}</string>
+                          </object>
+                        </element>
+                      </property>
+                    </serializedBean>
+                    <condition>Util.isWindows()</condition>
+                  </action>
                 </beans>
               </group>
               <action id="3020" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
@@ -2389,7 +2486,7 @@ return console.askYesNo(message, true);
 ( Util.isLinux()
   || Util.isUnixInstaller()
   || ( Util.isMacOS()
-       &amp;&amp; context.getVariable("macWrapperLinkLocation") != null
+       &amp;&amp; context.getVariable("MacOSDir") != null
      )
  )
  &amp;&amp; context.getVariable("unixBinDir") != null</visibilityScript>
@@ -2686,6 +2783,7 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/Jalview-File.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/Jalview-File.icns" />
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/jvl_file.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/jvl_file.icns" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}" />
+        <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/update_${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_APPLICATION_NAME}" target="java" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_NAME}" target="java" />
         <file name=".VolumeIcon.icns" file="${compiler:JALVIEW_DIR}/${compiler:MACOSARCHIVE_VOLUMEICON}" />
@@ -2708,6 +2806,7 @@ ${compiler:JALVIEW_APPLICATION_NAME} will now launch.</property>
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/Jalview-File.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/Jalview-File.icns" />
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/jvl_file.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/jvl_file.icns" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}" />
+        <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/update_${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_UPDATE_SCRIPT}" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_APPLICATION_NAME}" target="java" />
         <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/jre/Contents/Home/bin/${compiler:JALVIEW_NAME}" target="java" />
         <file name=".VolumeIcon.icns" file="${compiler:JALVIEW_DIR}/${compiler:MACOSARCHIVE_VOLUMEICON}" />