JAL-3521 Updated debian build.gradle in utils/debian/debian_build.gradle and patch...
authorBen Soares <b.soares@dundee.ac.uk>
Fri, 2 Jun 2023 10:10:03 +0000 (11:10 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Fri, 2 Jun 2023 10:10:03 +0000 (11:10 +0100)
utils/debian/build_gradle.patch
utils/debian/debian_build.gradle

index 1527c79..4d3762e 100644 (file)
@@ -1,6 +1,8 @@
---- a/build.gradle     2021-09-21 09:52:04.653972716 +0100
-+++ b/build.gradle     2021-09-21 09:52:18.117985307 +0100
-@@ -2,56 +2,12 @@
+diff --git a/build.gradle b/build.gradle
+index ca599a85a..ce7f13634 100644
+--- a/build.gradle
++++ b/build.gradle
+@@ -2,66 +2,12 @@
   * For properties set within build.gradle, use camelCaseNoSpace.
   */
  import org.apache.tools.ant.filters.ReplaceTokens
 -import org.gradle.plugins.ide.eclipse.model.Output
 -import org.gradle.plugins.ide.eclipse.model.Library
 -import java.security.MessageDigest
+-import java.util.regex.Matcher
 -import groovy.transform.ExternalizeMethods
 -import groovy.util.XmlParser
 -import groovy.xml.XmlUtil
+-import groovy.json.JsonBuilder
 -import com.vladsch.flexmark.util.ast.Node
 -import com.vladsch.flexmark.html.HtmlRenderer
 -import com.vladsch.flexmark.parser.Parser
 -import com.vladsch.flexmark.ext.autolink.AutolinkExtension
 -import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
 -import com.vladsch.flexmark.ext.toc.TocExtension
+-import com.google.common.hash.HashCode
+-import com.google.common.hash.Hashing
+-import com.google.common.io.Files
+-import org.jsoup.Jsoup
+-import org.jsoup.nodes.Element
 -
 -buildscript {
 -  repositories {
@@ -32,6 +41,8 @@
 -  }
 -  dependencies {
 -    classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
+-    classpath "org.jsoup:jsoup:1.14.3"
+-    classpath "com.eowise:gradle-imagemagick:0.5.1"
 -  }
 -}
 -
@@ -42,9 +53,9 @@
 -  id 'eclipse'
 -  id "com.diffplug.gradle.spotless" version "3.28.0"
 -  id 'com.github.johnrengelman.shadow' version '4.0.3'
--  id 'com.install4j.gradle' version '8.0.10'
--  id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
--  id 'com.palantir.git-version' version '0.12.3'
+-  id 'com.install4j.gradle' version '10.0.3'
+-  id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
+-  id 'com.palantir.git-version' version '0.13.0' apply false
 -}
 -
 -repositories {
  }
  
 -
+-
  // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
  def string(Object o) {
    return o == null ? "" : o.toString()
-@@ -92,23 +48,15 @@
+@@ -102,34 +48,20 @@ def overrideProperties(String propsFileName, boolean output = false) {
    }
  }
  
 +project.ext {
    jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
    jalviewDirRelativePath = jalviewDir
+-  date = new Date()
  
 -  getdownChannelName = CHANNEL.toLowerCase()
 -  // default to "default". Currently only has different cosmetics for "develop", "release", "default"
 -  propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
+-  channelDirName = propertiesChannelName
 -  // Import channel_properties
+-  if (getdownChannelName.startsWith("develop-")) {
+-    channelDirName = "develop-SUFFIX"
+-  }
+-  channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
 +  propertiesChannelName = "release"
-   channelDir = string("${jalviewDir}/${channel_properties_dir}/${propertiesChannelName}")
++  channelDir = string("${jalviewDir}/${channel_properties_dir}/${propertiesChannelName}")
    channelGradleProperties = string("${channelDir}/channel_gradle.properties")
+-  channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
    overrideProperties(channelGradleProperties, false)
 -  // local build environment properties
 -  // can be "projectDir/local.properties"
    ////  
    // Import releaseProps from the RELEASE file
    // or a file specified via JALVIEW_RELEASE_FILE if defined
-@@ -128,41 +76,6 @@
+   // Expect jalview.version and target release branch in jalview.release        
+-  releaseProps = new Properties();
++  def releaseProps = new Properties();
+   def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
+   def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
+   try {
+@@ -144,42 +76,6 @@ ext {
    if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
      JALVIEW_VERSION = releaseProps.get("jalview.version")
    }
+-  println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
 -  
 -  // this property set when running Eclipse headlessly
 -  j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
  
    // essentials
    bareSourceDir = string(source_dir)
-@@ -173,218 +86,18 @@
+@@ -190,273 +86,18 @@ ext {
  
    classesDir = string("${jalviewDir}/${classes_dir}")
  
 -  cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
 -  //cloverTestClassesDir = cloverClassesDir
 -  cloverDb = string("${cloverBuildDir}/clover.db")
--
++  useClover = false
 -  testSourceDir = useClover ? cloverTestInstrDir : testDir
 -  testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
--
--  getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
++  resourceClassesDir = classesDir
+-  channelSuffix = ""
+-  backgroundImageText = BACKGROUNDIMAGETEXT
+-  getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
+-  getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
+-  getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
+-  getdownFullArchiveDir = null
+-  getdownTextLines = []
+-  getdownLaunchJvl = null
+-  getdownVersionLaunchJvl = null
 -  buildDist = true
 -  buildProperties = null
--
++  testSourceDir = testDir
++  testClassesDir = "${jalviewDir}/${test_output_dir}"
 -  // the following values might be overridden by the CHANNEL switch
 -  getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
 -  getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+-  getdownArchiveAppBase = getdown_archive_base
 -  getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
 -  getdownAppDistDir = getdown_app_dir_alt
 -  getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
--  getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
+-  getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown")
++  buildProperties = string("${classesDir}/${build_properties_file}")
+   getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
 -  reportRsyncCommand = false
 -  jvlChannelName = CHANNEL.toLowerCase()
 -  install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
 -  install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
--  install4jDMGBackgroundImage = "${install4j_images_dir}/${install4j_dmg_background}"
+-  install4jDMGBackgroundImageDir = "${install4j_images_dir}"
+-  install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j"
+-  install4jDMGBackgroundImageFile = "${install4j_dmg_background}"
 -  install4jInstallerName = "${jalview_name} Non-Release Installer"
 -  install4jExecutableName = install4j_executable_name
 -  install4jExtraScheme = "jalviewx"
 -  install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
 -  install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
 -  install4jBackground = string("${install4j_images_dir}/${install4j_background}")
+-  install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
+-  install4jCheckSums = true
+-
+-  applicationName = "${jalview_name}"
 -  switch (CHANNEL) {
 -
 -    case "BUILD":
 -      testng_excluded_groups = "Not-bamboo"
 -    }
 -    install4jExtraScheme = "jalviewb"
+-    backgroundImageText = true
 -    break
-+  useClover = false
+-
 -    case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
 -    getdownAppDistDir = getdown_app_dir_release
+-    getdownSetAppBaseProperty = true
 -    reportRsyncCommand = true
 -    install4jSuffix = ""
 -    install4jInstallerName = "${jalview_name} Installer"
 -    case "ARCHIVELOCAL":
 -    getdownChannelName = string("archive/${JALVIEW_VERSION}")
 -    getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
--    getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+-    getdownAppBase = file(getdownAppBaseDir).toURI().toString()
 -    if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
--      throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
+-      throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']")
 -    } else {
 -      package_dir = string("${ARCHIVEDIR}/${package_dir}")
 -      buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
 -    install4jExtraScheme = "jalviewa"
 -    break
 -
+-    case ~/^DEVELOP-([\.\-\w]*)$/:
+-    def suffix = Matcher.lastMatcher[0][1]
+-    reportRsyncCommand = true
+-    getdownSetAppBaseProperty = true
+-    JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}"
+-    install4jSuffix = "Develop ${suffix}"
+-    install4jExtraScheme = "jalviewd"
+-    install4jInstallerName = "${jalview_name} Develop ${suffix} Installer"
+-    getdownChannelName = string("develop-${suffix}")
+-    getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}")
+-    getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
+-    getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+-    getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+-    channelSuffix = string(suffix)
+-    backgroundImageText = true
+-    break
+-
 -    case "DEVELOP":
 -    reportRsyncCommand = true
 -    getdownSetAppBaseProperty = true
 -    install4jSuffix = "Develop"
 -    install4jExtraScheme = "jalviewd"
 -    install4jInstallerName = "${jalview_name} Develop Installer"
+-    backgroundImageText = true
 -    break
 -
 -    case "TEST-RELEASE":
 -    reportRsyncCommand = true
+-    getdownSetAppBaseProperty = true
 -    // Don't ignore transpile errors for release build
 -    if (jalviewjs_ignore_transpile_errors.equals("true")) {
 -      jalviewjs_ignore_transpile_errors = "false"
 -    install4jSuffix = "Test"
 -    install4jExtraScheme = "jalviewt"
 -    install4jInstallerName = "${jalview_name} Test Installer"
+-    backgroundImageText = true
 -    break
 -
 -    case ~/^SCRATCH(|-[-\w]*)$/:
 -    install4jSuffix = "Test-Local"
 -    install4jExtraScheme = "jalviewt"
 -    install4jInstallerName = "${jalview_name} Test Installer"
+-    backgroundImageText = true
 -    break
 -
 -    case [ "LOCAL", "JALVIEWJS" ]:
 -    JALVIEW_VERSION = "TEST"
--    getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+-    getdownAppBase = file(getdownAppBaseDir).toURI().toString()
+-    getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
 -    getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
 -    install4jExtraScheme = "jalviewl"
+-    install4jCheckSums = false
 -    break
 -
 -    default: // something wrong specified
 -    break
 -
 -  }
+-  JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
+-  hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
+-  hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
 -  // override getdownAppBase if requested
 -  if (findProperty("getdown_appbase_override") != null) {
 -    // revert to LOCAL if empty string
 -    if (string(getdown_appbase_override) == "") {
--      getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+-      getdownAppBase = file(getdownAppBaseDir).toURI().toString()
 -      getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
 -    } else if (string(getdown_appbase_override).startsWith("file://")) {
 -      getdownAppBase = string(getdown_appbase_override)
 -  jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
 -  // install4j application and folder names
 -  if (install4jSuffix == "") {
--    install4jApplicationName = "${jalview_name}"
 -    install4jBundleId = "${install4j_bundle_id}"
 -    install4jWinApplicationId = install4j_release_win_application_id
 -  } else {
--    install4jApplicationName = "${jalview_name} ${install4jSuffix}"
+-    applicationName = "${jalview_name} ${install4jSuffix}"
 -    install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
 -    // add int hash of install4jSuffix to the last part of the application_id
 -    def id = install4j_release_win_application_id
 -  }
 -  // sanitise folder and id names
 -  // install4jApplicationFolder = e.g. "Jalview Build"
--  install4jApplicationFolder = install4jApplicationName
+-  install4jApplicationFolder = applicationName
 -                                    .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
 -                                    .replaceAll("_+", "_") // collapse __
--  install4jInternalId = install4jApplicationName
+-  install4jInternalId = applicationName
 -                                    .replaceAll(" ","_")
 -                                    .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
 -                                    .replaceAll("_+", "") // collapse __
 -                                    //.replaceAll("_*-_*", "-") // collapse _-_
--  install4jUnixApplicationFolder = install4jApplicationName
+-  install4jUnixApplicationFolder = applicationName
 -                                    .replaceAll(" ","_")
 -                                    .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
 -                                    .replaceAll("_+", "_") // collapse __
 -                                    .toLowerCase()
 -
 -  getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
--  getdownAppDir = string("${getdownWebsiteDir}/${getdownAppDistDir}")
--  //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
--  getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
--  getdownInstallDir = string("${getdownWebsiteDir}/${getdown_install_dir}")
+-  getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
+-  //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
+-  getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
+-  getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
 -  getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
 -  getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
 -  /* compile without modules -- using classpath libraries
 -  modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
 -  modules_runtimeClasspath = modules_compileClasspath
 -  */
--  def details = versionDetails()
--  gitHash = details.gitHash
--  gitBranch = details.branchName
-+  resourceClassesDir = classesDir
-+
-+  testSourceDir = testDir
-+  testClassesDir = "${jalviewDir}/${test_output_dir}"
+-
+-  gitHash = "SOURCE"
+-  gitBranch = "Source"
+-  try {
+-    apply plugin: "com.palantir.git-version"
+-    def details = versionDetails()
+-    gitHash = details.gitHash
+-    gitBranch = details.branchName
+-  } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
+-    println("Not in a git repository. Using git values from RELEASE properties file.")
+-    gitHash = releaseProps.getProperty("git.hash")
+-    gitBranch = releaseProps.getProperty("git.branch")
+-  } catch(java.lang.RuntimeException e1) {
+-    throw new GradleException("Error with git-version plugin.  Directory '.git' exists but versionDetails() cannot be found.")
+-  }
  
-+  buildProperties = string("${classesDir}/${build_properties_file}")
-+  getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
-+
 +  install4jApplicationName = "${jalview_name}"
 +  
    println("Using a ${CHANNEL} profile.")
  
    additional_compiler_args = []
-@@ -396,71 +109,16 @@
+@@ -468,65 +109,16 @@ ext {
      libDistDir = j8libDir
      compile_source_compatibility = 1.8
      compile_target_compatibility = 1.8
 -    '--add-modules', j11modules
 -    ]
 -     */
--  } else if (JAVA_VERSION.equals("12") || JAVA_VERSION.equals("13")) {
--    JAVA_INTEGER_VERSION = JAVA_VERSION
--    libDir = j11libDir
--    libDistDir = j11libDir
--    compile_source_compatibility = JAVA_VERSION
--    compile_target_compatibility = JAVA_VERSION
+-  } else if (JAVA_VERSION.equals("17")) {
+-    JAVA_INTEGER_VERSION = string("17")
+-    libDir = j17libDir
+-    libDistDir = j17libDir
+-    compile_source_compatibility = 17
+-    compile_target_compatibility = 17
 -    getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
 -    getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
 -    getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
--    eclipseJavaRuntimeName = string("JavaSE-11")
+-    eclipseJavaRuntimeName = string("JavaSE-17")
 -    /* compile without modules -- using classpath libraries
 -    additional_compiler_args += [
 -    '--module-path', modules_compileClasspath.asPath,
 -  // for install4j
 -  JAVA_MIN_VERSION = JAVA_VERSION
 -  JAVA_MAX_VERSION = JAVA_VERSION
--  def jreInstallsDir = string(jre_installs_dir)
+-  jreInstallsDir = string(jre_installs_dir)
 -  if (jreInstallsDir.startsWith("~/")) {
 -    jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
 -  }
--  macosJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-mac-x64/jre")
--  macosJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-mac-x64.tar.gz")
--  windowsJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-windows-x64/jre")
--  windowsJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-windows-x64.tar.gz")
--  linuxJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-linux-x64/jre")
--  linuxJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-linux-x64.tar.gz")
 -  install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
 -  install4jConfFileName = string("jalview-install4j-conf.install4j")
 -  install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
    resourceBuildDir = string("${buildDir}/resources")
    resourcesBuildDir = string("${resourceBuildDir}/resources_build")
    helpBuildDir = string("${resourceBuildDir}/help_build")
-@@ -474,31 +132,6 @@
+@@ -540,39 +132,6 @@ ext {
    helpSourceDir = string("${helpParentDir}/${help_dir}")
    helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
  
+-  convertBinary = null
+-  convertBinaryExpectedLocation = imagemagick_convert
+-  if (convertBinaryExpectedLocation.startsWith("~/")) {
+-    convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1)
+-  }
+-  if (file(convertBinaryExpectedLocation).exists()) {
+-    convertBinary = convertBinaryExpectedLocation
+-  }
 -
 -  relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
 -  jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
    // ENDEXT
  }
  
-@@ -517,27 +150,12 @@
+@@ -591,27 +150,12 @@ sourceSets {
      compileClasspath = files(sourceSets.main.java.outputDir)
      compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
  
    }
  
    test {
-@@ -557,453 +175,41 @@
+@@ -631,453 +175,41 @@ sourceSets {
      runtimeClasspath = compileClasspath
      runtimeClasspath += files(sourceSets.test.resources.srcDirs)
    }
 -  if (cloverreport_jvmargs.length() > 0) {
 -    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
 -  }
--
 -  def argsList = [
 -    "--alwaysreport",
 -    "--initstring",
 -
 -  args argsList.toArray()
 -}
-+    compileClasspath = files( sourceSets.test.java.outputDir )
-+    compileClasspath += sourceSets.main.compileClasspath
-+    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**   REMOVE_THIS_GAP  /*.jar"])
+-
+-
 -task cloverReport {
 -  group = "Verification"
 -  description = "Creates clover reports"
 -
 -
 -compileCloverJava {
--
++    compileClasspath = files( sourceSets.test.java.outputDir )
++    compileClasspath += sourceSets.main.compileClasspath
++    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**   REMOVE_THIS_GAP  /*.jar"])
 -  doFirst {
 -    sourceCompatibility = compile_source_compatibility
 -    targetCompatibility = compile_target_compatibility
 -  // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
    sourceCompatibility = compile_source_compatibility
    targetCompatibility = compile_target_compatibility
-   options.compilerArgs = additional_compiler_args
+-  options.compilerArgs += additional_compiler_args
 -  options.encoding = "UTF-8"
++  options.compilerArgs = additional_compiler_args
    doFirst {
      print ("Setting target compatibility to "+compile_target_compatibility+"\n")
    }
  compileTestJava {
 -  sourceCompatibility = compile_source_compatibility
 -  targetCompatibility = compile_target_compatibility
--  options.compilerArgs = additional_compiler_args
+-  options.compilerArgs += additional_compiler_args
    doFirst {
 +    sourceCompatibility = compile_source_compatibility
 +    targetCompatibility = compile_target_compatibility
      print ("Setting target compatibility to "+targetCompatibility+"\n")
    }
  }
-@@ -1017,7 +223,6 @@
+@@ -1091,7 +223,6 @@ clean {
  
  
  cleanTest {
    doFirst {
      delete sourceSets.test.java.outputDir
    }
-@@ -1031,85 +236,6 @@
+@@ -1100,89 +231,11 @@ cleanTest {
+ // format is a string like date.format("dd MMMM yyyy")
+ def getDate(format) {
++  def date = new Date()
+   return date.format(format)
  }
  
  
  task copyDocs(type: Copy) {
    def inputDir = "${jalviewDir}/${doc_dir}"
    def outputDir = "${docBuildDir}/${doc_dir}"
-@@ -1140,27 +266,6 @@
+@@ -1213,235 +266,6 @@ task copyDocs(type: Copy) {
  }
  
  
 -}
 -
 -
+-def hugoTemplateSubstitutions(String input, Map extras=null) {
+-  def replacements = [
+-    DATE: getDate("yyyy-MM-dd"),
+-    CHANNEL: propertiesChannelName,
+-    APPLICATION_NAME: applicationName,
+-    GIT_HASH: gitHash,
+-    GIT_BRANCH: gitBranch,
+-    VERSION: JALVIEW_VERSION,
+-    JAVA_VERSION: JAVA_VERSION,
+-    VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
+-    DRAFT: "false",
+-    JVL_HEADER: ""
+-  ]
+-  def output = input
+-  if (extras != null) {
+-    extras.each{ k, v ->
+-      output = output.replaceAll("__${k}__", ((v == null)?"":v))
+-    }
+-  }
+-  replacements.each{ k, v ->
+-    output = output.replaceAll("__${k}__", ((v == null)?"":v))
+-  }
+-  return output
+-}
+-
+-def mdFileComponents(File mdFile, def dateOnly=false) {
+-  def map = [:]
+-  def content = ""
+-  if (mdFile.exists()) {
+-    def inFrontMatter = false
+-    def firstLine = true
+-    mdFile.eachLine { line ->
+-      if (line.matches("---")) {
+-        def prev = inFrontMatter
+-        inFrontMatter = firstLine
+-        if (inFrontMatter != prev)
+-          return false
+-      }
+-      if (inFrontMatter) {
+-        def m = null
+-        if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
+-          map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
+-        } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
+-          map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
+-        } else if (m = line =~ /^channel:\s*(\S+)/) {
+-          map["channel"] = m[0][1]
+-        } else if (m = line =~ /^version:\s*(\S+)/) {
+-          map["version"] = m[0][1]
+-        } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
+-          map[ m[0][1] ] = m[0][2]
+-        }
+-        if (dateOnly && map["date"] != null) {
+-          return false
+-        }
+-      } else {
+-        if (dateOnly)
+-          return false
+-        content += line+"\n"
+-      }
+-      firstLine = false
+-    }
+-  }
+-  return dateOnly ? map["date"] : [map, content]
+-}
+-
+-task hugoTemplates {
+-  group "website"
+-  description "Create partially populated md pages for hugo website build"
+-
+-  def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
+-  def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
+-  def templateFiles = fileTree(dir: hugoTemplatesDir)
+-  def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
+-  def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
+-  def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
+-  def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
+-
+-  doFirst {
+-    // specific release template for version archive
+-    def changes = ""
+-    def whatsnew = null
+-    def givenDate = null
+-    def givenChannel = null
+-    def givenVersion = null
+-    if (CHANNEL == "RELEASE") {
+-      def (map, content) = mdFileComponents(releaseMdFile)
+-      givenDate = map.date
+-      givenChannel = map.channel
+-      givenVersion = map.version
+-      changes = content
+-      if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
+-        throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
+-      }
+-
+-      if (whatsnewMdFile.exists())
+-        whatsnew = whatsnewMdFile.text
+-    }
+-
+-    def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
+-    def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
+-
+-    def changesHugo = null
+-    if (changes != null) {
+-      changesHugo = '<div class="release_notes">\n\n'
+-      def inSection = false
+-      changes.eachLine { line ->
+-        def m = null
+-        if (m = line =~ /^##([^#].*)$/) {
+-          if (inSection) {
+-            changesHugo += "</div>\n\n"
+-          }
+-          def section = m[0][1].trim()
+-          section = section.toLowerCase()
+-          section = section.replaceAll(/ +/, "_")
+-          section = section.replaceAll(/[^a-z0-9_\-]/, "")
+-          changesHugo += "<div class=\"${section}\">\n\n"
+-          inSection = true
+-        } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
+-          def comment = m[0][2].trim()
+-          if (comment != "") {
+-            comment = comment.replaceAll('"', "&quot;")
+-            def issuekeys = []
+-            comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
+-            def newline = m[0][1]
+-            if (comment.trim() != "")
+-              newline += "{{<comment>}}${comment}{{</comment>}}  "
+-            newline += m[0][3].trim()
+-            if (issuekeys.size() > 0)
+-              newline += "  {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
+-            if (m[0][4] != null)
+-              newline += m[0][4]
+-            line = newline
+-          }
+-        }
+-        changesHugo += line+"\n"
+-      }
+-      if (inSection) {
+-        changesHugo += "\n</div>\n\n"
+-      }
+-      changesHugo += '</div>'
+-    }
+-
+-    templateFiles.each{ templateFile ->
+-      def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
+-      def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
+-      def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
+-
+-      def outPathName = string("${hugoBuildDir}/$newRelPathName")
+-
+-      copy {
+-        from templateFile
+-        rename(templateFile.getName(), newFileName)
+-        into outPathName
+-      }
+-
+-      def newFile = file("${outPathName}/${newFileName}".toString())
+-      def content = newFile.text
+-      newFile.text = hugoTemplateSubstitutions(content,
+-        [
+-          WHATSNEW: whatsnew,
+-          CHANGES: changesHugo,
+-          DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
+-          DRAFT: givenDate == null ? "true" : "false",
+-          JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
+-          JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
+-        ]
+-      )
+-    }
+-
+-  }
+-
+-  inputs.file(oldJvlFile)
+-  inputs.dir(hugoTemplatesDir)
+-  inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
+-  inputs.property("CHANNEL", { CHANNEL })
+-}
+-
+-def getMdDate(File mdFile) {
+-  return mdFileComponents(mdFile, true)
+-}
+-
+-def getMdSections(String content) {
+-  def sections = [:]
+-  def sectionContent = ""
+-  def sectionName = null
+-  content.eachLine { line ->
+-    def m = null
+-    if (m = line =~ /^##([^#].*)$/) {
+-      if (sectionName != null) {
+-        sections[sectionName] = sectionContent
+-        sectionName = null
+-        sectionContent = ""
+-      }
+-      sectionName = m[0][1].trim()
+-      sectionName = sectionName.toLowerCase()
+-      sectionName = sectionName.replaceAll(/ +/, "_")
+-      sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
+-    } else if (sectionName != null) {
+-      sectionContent += line+"\n"
+-    }
+-  }
+-  if (sectionContent != null) {
+-    sections[sectionName] = sectionContent
+-  }
+-  return sections
+-}
+-
+-
  task copyHelp(type: Copy) {
    def inputDir = helpSourceDir
    def outputDir = "${helpBuildDir}/${help_dir}"
-@@ -1242,24 +347,15 @@
+@@ -1476,7 +300,6 @@ task copyHelp(type: Copy) {
+   outputs.dir(outputDir)
+ }
+-
+ task releasesTemplates {
+   group "help"
+   description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
+@@ -1491,6 +314,7 @@ task releasesTemplates {
+   def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
+   doFirst {
++    def JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
+     def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
+     def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
+@@ -1505,7 +329,7 @@ task releasesTemplates {
+     def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
+     def releaseFilesDates = releaseFiles.collectEntries {
+-      [(it): getMdDate(it)]
++      [(it): getDate("")]
+     }
+     releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
+@@ -1513,96 +337,13 @@ task releasesTemplates {
+     def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
+     def versionTemplate = m[0][1]
+-    MutableDataSet options = new MutableDataSet()
+-
+-    def extensions = new ArrayList<>()
+-    options.set(Parser.EXTENSIONS, extensions)
+-    options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
+-
+-    Parser parser = Parser.builder(options).build()
+-    HtmlRenderer renderer = HtmlRenderer.builder(options).build()
+-
+-    def actualVersions = releaseFiles.collect { rf ->
+-      def (rfMap, rfContent) = mdFileComponents(rf)
+-      return rfMap.version
+-    }
+     def versionsHtml = ""
+     def linkedVersions = []
+-    releaseFiles.reverse().each { rFile ->
+-      def (rMap, rContent) = mdFileComponents(rFile)
+-
+-      def versionLink = ""
+-      def partialVersion = ""
+-      def firstPart = true
+-      rMap.version.split("\\.").each { part ->
+-        def displayPart = ( firstPart ? "" : "." ) + part
+-        partialVersion += displayPart
+-        if (
+-            linkedVersions.contains(partialVersion)
+-            || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
+-            ) {
+-          versionLink += displayPart
+-        } else {
+-          versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
+-          linkedVersions += partialVersion
+-        }
+-        firstPart = false
+-      }
+-      def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
+-
+-      def lm = null
+-      def rContentProcessed = ""
+-      rContent.eachLine { line ->
+-        if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
+-          line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
+-      } else if (lm = line =~ /^###([^#]+.*)$/) {
+-          line = "_${lm[0][1].trim()}_"
+-        }
+-        rContentProcessed += line + "\n"
+-      }
+-
+-      def rContentSections = getMdSections(rContentProcessed)
+-      def rVersion = versionTemplate
+-      if (rVersion != "") {
+-        def rNewFeatures = rContentSections["new_features"]
+-        def rIssuesResolved = rContentSections["issues_resolved"]
+-        Node newFeaturesNode = parser.parse(rNewFeatures)
+-        String newFeaturesHtml = renderer.render(newFeaturesNode)
+-        Node issuesResolvedNode = parser.parse(rIssuesResolved)
+-        String issuesResolvedHtml = renderer.render(issuesResolvedNode)
+-        rVersion = hugoTemplateSubstitutions(rVersion,
+-          [
+-            VERSION: rMap.version,
+-            VERSION_LINK: versionLink,
+-            DISPLAY_DATE: displayDate,
+-            NEW_FEATURES: newFeaturesHtml,
+-            ISSUES_RESOLVED: issuesResolvedHtml
+-          ]
+-        )
+-        versionsHtml += rVersion
+-      }
+-    }
+     releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
+-    releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
+     releasesHtmlFile.text = releasesTemplate
+-    if (whatsnewMdFile.exists()) {
+-      def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
+-      def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
+-      Node whatsnewNode = parser.parse(whatsnewMd)
+-      String whatsnewHtml = renderer.render(whatsnewNode)
+-      whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
+-      whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
+-        [
+-            VERSION: JALVIEW_VERSION,
+-          DISPLAY_DATE: wnDisplayDate
+-        ]
+-      )
+-    } else if (gradle.taskGraph.hasTask(":linkCheck")) {
+-      whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
+-    }
+-
++    whatsnewHtmlFile.text = "Debian build " + getDate("yyyy-MM-dd HH:mm:ss")
+   }
+   inputs.file(releasesTemplateFile)
+@@ -1613,7 +354,6 @@ task releasesTemplates {
+   outputs.file(whatsnewHtmlFile)
+ }
+-
+ task copyResources(type: Copy) {
+   group = "build"
+   description = "Copy (and make text substitutions in) the resources dir to the build area"
+@@ -1653,44 +393,22 @@ task copyChannelResources(type: Copy) {
+   def inputDir = "${channelDir}/${resource_dir}"
+   def outputDir = resourcesBuildDir
+-  from(inputDir) {
+-    include(channel_props)
+-    filter(ReplaceTokens,
+-      beginToken: '__',
+-      endToken: '__',
+-      tokens: [
+-        'SUFFIX': channelSuffix
+-      ]
+-    )
+-  }
+-  from(inputDir) {
+-    exclude(channel_props)
+-  }
++  from inputDir
+   into outputDir
+   inputs.dir(inputDir)
    outputs.dir(outputDir)
  }
  
 -  property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
 -  property "VERSION", JALVIEW_VERSION
 -  property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
+-  property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
 -  if (getdownSetAppBaseProperty) {
 -    property "GETDOWNAPPBASE", getdownAppBase
 -    property "GETDOWNAPPDISTDIR", getdownAppDistDir
    outputs.file(outputFile)
  }
  
-@@ -1293,7 +389,6 @@
-   dependsOn buildResources
+@@ -1725,123 +443,31 @@ task prepare {
    dependsOn copyDocs
    dependsOn copyHelp
+   dependsOn releasesTemplates
 -  dependsOn convertMdFiles
    dependsOn buildIndices
  }
  
-@@ -1306,12 +401,7 @@
- //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
+ compileJava.dependsOn prepare
+ run.dependsOn compileJava
+-compileTestJava.dependsOn compileJava
+-
++//run.dependsOn prepare
++//testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
  test {
-   dependsOn prepare
+-  group = "Verification"
+-  description = "Runs all testTaskN tasks)"
 -
 -  if (useClover) {
 -    dependsOn cloverClasses
--   } else { //?
--    dependsOn compileJava //?
+-  } else { //?
+-    dependsOn testClasses
+-  }
+-
+-  // not running tests in this task
+-  exclude "**/*"
+-}
+-/* testTask0 is the main test task */
+-task testTask0(type: Test) {
+-  group = "Verification"
+-  description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
+-  useTestNG() {
+-    includeGroups testng_groups.split(",")
+-    excludeGroups testng_excluded_groups.split(",")
+-    tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
+-    preserveOrder true
+-    useDefaultListeners=true
+-  }
+-}
+-
+-/* separated tests */
+-task testTask1(type: Test) {
+-  group = "Verification"
+-  description = "Tests that need to be isolated from the main test run"
+-  useTestNG() {
+-    includeGroups name
+-    excludeGroups testng_excluded_groups.split(",")
+-    preserveOrder true
+-    useDefaultListeners=true
 -  }
+-}
++  dependsOn prepare
 +  dependsOn compileJava //?
  
+-/* insert more testTaskNs here -- change N to next digit or other string */
+-/*
+-task testTaskN(type: Test) {
+-  group = "Verification"
+-  description = "Tests that need to be isolated from the main test run"
    useTestNG() {
-     includeGroups testng_groups
-@@ -1323,6 +413,7 @@
+-    includeGroups name
+-    excludeGroups testng_excluded_groups.split(",")
++    includeGroups testng_groups
++    excludeGroups testng_excluded_groups
+     preserveOrder true
+     useDefaultListeners=true
+   }
+-}
+-*/
+-
+-/*
+- * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
+- * to summarise test results from all Test tasks
+- */
+-/* START of test tasks results summary */
+-import groovy.time.TimeCategory
+-import org.gradle.api.tasks.testing.logging.TestExceptionFormat
+-import org.gradle.api.tasks.testing.logging.TestLogEvent
+-rootProject.ext.testsResults = [] // Container for tests summaries
+-
+-tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
+-
+-  // from original test task
+-  if (useClover) {
+-    dependsOn cloverClasses
+-  } else { //?
+-    dependsOn testClasses //?
+-  }
+-
+-  // run main tests first
+-  if (!testTask.name.equals("testTask0"))
+-    testTask.mustRunAfter "testTask0"
+-
+-  testTask.testLogging { logging ->
+-    events TestLogEvent.FAILED
+-//      TestLogEvent.SKIPPED,
+-//      TestLogEvent.STANDARD_OUT,
+-//      TestLogEvent.STANDARD_ERROR
+-
+-    exceptionFormat TestExceptionFormat.FULL
+-    showExceptions true
+-    showCauses true
+-    showStackTraces true
+-
+-    info.events = [ TestLogEvent.FAILED ]
+-  }
+-
+-
+-
+-  ignoreFailures = true // Always try to run all tests for all modules
+-
+-  afterSuite { desc, result ->
+-    if (desc.parent)
+-      return // Only summarize results for whole modules
+-
+-    def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
+-
+-    rootProject.ext.testsResults.add(resultsInfo)
+-  }
+-  // from original test task
    maxHeapSize = "1024m"
  
    workingDir = jalviewDir
    def testLaf = project.findProperty("test_laf")
    if (testLaf != null) {
      println("Setting Test LaF to '${testLaf}'")
-@@ -1338,9 +429,6 @@
+@@ -1857,143 +483,8 @@ tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { te
    jvmArgs += additional_compiler_args
  
    doFirst {
+-    // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
+-    // testTasks that include the tests, and exclude all from the others.
+-    // get --test argument
+-    filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
+-    // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
+-    // commandLineIncludePatterns not matching anything.  Instead we are doing setFailOnNoMatchingTests(false) below
+-
+-
 -    if (useClover) {
 -      println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
 -    }
+-  }
+-
+-
+-  /* don't fail on no matching tests (so --tests will run across all testTasks) */
+-  testTask.filter.setFailOnNoMatchingTests(false)
+-
+-  /* ensure the "test" task dependsOn all the testTasks */
+-  test.dependsOn testTask
+-}
+-
+-gradle.buildFinished {
+-    def allResults = rootProject.ext.testsResults
+-
+-    if (!allResults.isEmpty()) {
+-        printResults allResults
+-        allResults.each {r ->
+-          if (r[2].resultType == TestResult.ResultType.FAILURE)
+-            throw new GradleException("Failed tests!")
+-        }
+-    }
+-}
+-
+-private static String colString(styler, col, colour, text) {
+-  return col?"${styler[colour](text)}":text
+-}
+-
+-private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
+-  def colour = 'black'
+-  def text = rt
+-  def nocol = false
+-  if (rc == 0) {
+-    text = "-----"
+-    nocol = true
+-  } else {
+-    switch(rt) {
+-      case TestResult.ResultType.SUCCESS:
+-        colour = 'green'
+-        break;
+-      case TestResult.ResultType.FAILURE:
+-        colour = 'red'
+-        break;
+-      default:
+-        nocol = true
+-        break;
+-    }
    }
+-  StringBuilder sb = new StringBuilder()
+-  sb.append("${pn}")
+-  if (tn != null)
+-    sb.append(":${tn}")
+-  sb.append(" results: ")
+-  sb.append(colString(s, col && !nocol, colour, text))
+-  sb.append(" (")
+-  sb.append("${rc} tests, ")
+-  sb.append(colString(s, col && rs > 0, 'green', rs))
+-  sb.append(" successes, ")
+-  sb.append(colString(s, col && rf > 0, 'red', rf))
+-  sb.append(" failures, ")
+-  sb.append("${rsk} skipped) in ${t}")
+-  return sb.toString()
+-}
+-
+-private static void printResults(allResults) {
+-
+-    // styler from https://stackoverflow.com/a/56139852
+-    def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
+-
+-    def maxLength = 0
+-    def failedTests = false
+-    def summaryLines = []
+-    def totalcount = 0
+-    def totalsuccess = 0
+-    def totalfail = 0
+-    def totalskip = 0
+-    def totaltime = TimeCategory.getSeconds(0)
+-    // sort on project name then task name
+-    allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
+-      def projectName = it[0]
+-      def taskName = it[1]
+-      def result = it[2]
+-      def time = it[3]
+-      def report = it[4]
+-      def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
+-      def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
+-      def reportLine = "Report file: ${report}"
+-      def ls = summaryPlain.length()
+-      def lr = reportLine.length()
+-      def m = [ls, lr].max()
+-      if (m > maxLength)
+-        maxLength = m
+-      def info = [ls, summaryCol, reportLine]
+-      summaryLines.add(info)
+-      failedTests |= result.resultType == TestResult.ResultType.FAILURE
+-      totalcount += result.testCount
+-      totalsuccess += result.successfulTestCount
+-      totalfail += result.failedTestCount
+-      totalskip += result.skippedTestCount
+-      totaltime += time
+-    }
+-    def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
+-    def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
+-    def tls = totalSummaryPlain.length()
+-    if (tls > maxLength)
+-      maxLength = tls
+-    def info = [tls, totalSummaryCol, null]
+-    summaryLines.add(info)
+-
+-    def allSummaries = []
+-    for(sInfo : summaryLines) {
+-      def ls = sInfo[0]
+-      def summary = sInfo[1]
+-      def report = sInfo[2]
+-
+-      StringBuilder sb = new StringBuilder()
+-      sb.append("│" + summary + " " * (maxLength - ls) + "│")
+-      if (report != null) {
+-        sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
+-      }
+-      allSummaries += sb.toString()
+-    }
+-
+-    println "┌${"${"─" * maxLength}"}┐"
+-    println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
+-    println "└${"${"─" * maxLength}"}┘"
  }
+-/* END of test tasks results summary */
  
-@@ -1420,1752 +508,7 @@
+ task compileLinkCheck(type: JavaCompile) {
+@@ -2052,7 +543,7 @@ jar {
+   manifest {
+     attributes "Main-Class": main_class,
+     "Permissions": "all-permissions",
+-    "Application-Name": applicationName,
++    "Application-Name": install4jApplicationName,
+     "Codebase": application_codebase,
+     "Implementation-Version": JALVIEW_VERSION
+   }
+@@ -2060,8 +551,6 @@ jar {
+   def outputDir = "${jalviewDir}/${package_dir}"
+   destinationDirectory = file(outputDir)
+   archiveFileName = rootProject.name+".jar"
+-  duplicatesStrategy "EXCLUDE"
+-
+   exclude "cache*/**"
+   exclude "*.jar"
+@@ -2073,2137 +562,7 @@ jar {
    sourceSets.main.resources.srcDirs.each{ dir ->
      inputs.dir(dir)
    }
 -  }
 -  manifest {
 -    attributes "Implementation-Version": JALVIEW_VERSION,
--    "Application-Name": install4jApplicationName
+-    "Application-Name": applicationName
 -  }
+-
+-  duplicatesStrategy "INCLUDE"
+-
 -  mainClassName = shadow_jar_main_class
 -  mergeServiceFiles()
 -  classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
 -  minimize()
 -}
 -
+-task getdownImagesCopy() {
+-  inputs.dir getdownImagesDir
+-  outputs.dir getdownImagesBuildDir
+-
+-  doFirst {
+-    copy {
+-      from(getdownImagesDir) {
+-        include("*getdown*.png")
+-      }
+-      into getdownImagesBuildDir
+-    }
+-  }
+-}
+-
+-task getdownImagesProcess() {
+-  dependsOn getdownImagesCopy
+-
+-  doFirst {
+-    if (backgroundImageText) {
+-      if (convertBinary == null) {
+-        throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
+-      }
+-      if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
+-        throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
+-      }
+-      fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
+-        exec {
+-          executable convertBinary
+-          args = [
+-            file.getPath(),
+-            '-font', getdown_background_image_text_font,
+-            '-fill', getdown_background_image_text_colour,
+-            '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
+-            '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
+-            '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
+-            file.getPath()
+-          ]
+-        }
+-      }
+-    }
+-  }
+-}
+-
+-task getdownImages() {
+-  dependsOn getdownImagesProcess
+-}
 -
 -task getdownWebsite() {
 -  group = "distribution"
 -  description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
+-
+-  dependsOn getdownImages
 -  if (buildDist) {
 -    dependsOn makeDist
 -  }
 -
 -  def getdownWebsiteResourceFilenames = []
--  def getdownTextString = ""
 -  def getdownResourceDir = getdownResourceDir
 -  def getdownResourceFilenames = []
 -
 -  doFirst {
 -    // clean the getdown website and files dir before creating getdown folders
--    delete getdownWebsiteDir
+-    delete getdownAppBaseDir
 -    delete getdownFilesDir
 -
 -    copy {
 -    }
 -    getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
 -
--    // set some getdown_txt_ properties then go through all properties looking for getdown_txt_...
+-    copy {
+-      from channelPropsFile
+-      filter(ReplaceTokens,
+-        beginToken: '__',
+-        endToken: '__',
+-        tokens: [
+-          'SUFFIX': channelSuffix
+-        ]
+-      )
+-      into getdownAppBaseDir
+-    }
+-    getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
+-
+-    // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
 -    def props = project.properties.sort { it.key }
 -    if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
 -      props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
 -    if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
 -      props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
 -    }
--    if (getdownImagesDir != null && file(getdownImagesDir).exists()) {
--      props.put("getdown_txt_ui.background_image", "${getdownImagesDir}/${getdown_background_image}")
--      props.put("getdown_txt_ui.instant_background_image", "${getdownImagesDir}/${getdown_instant_background_image}")
--      props.put("getdown_txt_ui.error_background", "${getdownImagesDir}/${getdown_error_background}")
--      props.put("getdown_txt_ui.progress_image", "${getdownImagesDir}/${getdown_progress_image}")
+-    if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
+-      props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
+-      props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
+-      props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
+-      props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
 -      props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
 -      props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
 -    }
 -
 -    props.put("getdown_txt_title", jalview_name)
--    props.put("getdown_txt_ui.name", install4jApplicationName)
+-    props.put("getdown_txt_ui.name", applicationName)
 -
 -    // start with appbase
--    getdownTextString += "appbase = ${getdownAppBase}\n"
+-    getdownTextLines += "appbase = ${getdownAppBase}"
 -    props.each{ prop, val ->
 -      if (prop.startsWith("getdown_txt_") && val != null) {
 -        if (prop.startsWith("getdown_txt_multi_")) {
 -          def key = prop.substring(18)
 -          val.split(",").each{ v ->
--            def line = "${key} = ${v}\n"
--            getdownTextString += line
+-            def line = "${key} = ${v}"
+-            getdownTextLines += line
 -          }
 -        } else {
 -          // file values rationalised
 -            }
 -          }
 -          if (! prop.startsWith("getdown_txt_resource")) {
--            def line = prop.substring(12) + " = ${val}\n"
--            getdownTextString += line
+-            def line = prop.substring(12) + " = ${val}"
+-            getdownTextLines += line
 -          }
 -        }
 -      }
 -    }
 -
 -    getdownWebsiteResourceFilenames.each{ filename ->
--      getdownTextString += "resource = ${filename}\n"
+-      getdownTextLines += "resource = ${filename}"
 -    }
 -    getdownResourceFilenames.each{ filename ->
 -      copy {
 -      if (s.exists()) {
 -        copy {
 -          from s
--          into "${getdownWebsiteDir}/${getdown_wrapper_script_dir}"
+-          into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
 -        }
--        getdownTextString += "resource = ${getdown_wrapper_script_dir}/${script}\n"
+-        getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}"
 -      }
 -    }
 -
 -        codeFiles += f
 -      }
 -    }
--    codeFiles.sort().each{f ->
+-    def jalviewJar = jar.archiveFileName.getOrNull()
+-    // put jalview.jar first for CLASSPATH and .properties files reasons
+-    codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
 -      def name = f.getName()
--      def line = "code = ${getdownAppDistDir}/${name}\n"
--      getdownTextString += line
+-      def line = "code = ${getdownAppDistDir}/${name}"
+-      getdownTextLines += line
 -      copy {
 -        from f.getPath()
 -        into getdownAppDir
 -    def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
 -    j11libFiles.sort().each{f ->
 -    def name = f.getName()
--    def line = "code = ${getdown_j11lib_dir}/${name}\n"
--    getdownTextString += line
+-    def line = "code = ${getdown_j11lib_dir}/${name}"
+-    getdownTextLines += line
 -    copy {
 -    from f.getPath()
 -    into getdownJ11libDir
 -     */
 -
 -    // getdown-launcher.jar should not be in main application class path so the main application can move it when updated.  Listed as a resource so it gets updated.
--    //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
--    getdownTextString += "resource = ${getdown_launcher_new}\n"
--    getdownTextString += "class = ${main_class}\n"
+-    //getdownTextLines += "class = " + file(getdownLauncher).getName()
+-    getdownTextLines += "resource = ${getdown_launcher_new}"
+-    getdownTextLines += "class = ${main_class}"
 -    // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
 -    if (getdownSetAppBaseProperty) {
--      getdownTextString += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}\n"
--      getdownTextString += "jvmarg = -Dgetdownappbase=${getdownAppBase}\n"
+-      getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
+-      getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
 -    }
 -
--    def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
--    getdown_txt.write(getdownTextString)
+-    def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
+-    getdownTxt.write(getdownTextLines.join("\n"))
 -
--    def getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
--    def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
+-    getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
+-    def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
 -    launchJvl.write("appbase=${getdownAppBase}")
 -
 -    // files going into the getdown website dir: getdown-launcher.jar
 -    copy {
 -      from getdownLauncher
 -      rename(file(getdownLauncher).getName(), getdown_launcher_new)
--      into getdownWebsiteDir
+-      into getdownAppBaseDir
 -    }
 -
 -    // files going into the getdown website dir: getdown-launcher(-local).jar
 -      if (file(getdownLauncher).getName() != getdown_launcher) {
 -        rename(file(getdownLauncher).getName(), getdown_launcher)
 -      }
--      into getdownWebsiteDir
+-      into getdownAppBaseDir
 -    }
 -
 -    // files going into the getdown website dir: ./install dir and files
 -    if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
 -      copy {
--        from getdown_txt
+-        from getdownTxt
 -        from getdownLauncher
 -        from "${getdownAppDir}/${getdown_build_properties}"
 -        if (file(getdownLauncher).getName() != getdown_launcher) {
 -
 -    // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
 -    copy {
--      from getdown_txt
+-      from getdownTxt
 -      from launchJvl
 -      from getdownLauncher
--      from "${getdownWebsiteDir}/${getdown_build_properties}"
+-      from "${getdownAppBaseDir}/${getdown_build_properties}"
+-      from "${getdownAppBaseDir}/${channel_props}"
 -      if (file(getdownLauncher).getName() != getdown_launcher) {
 -        rename(file(getdownLauncher).getName(), getdown_launcher)
 -      }
 -      into getdownFilesDir
 -    }
 -
--    // and ./resources (not all downloaded by getdown)
+-    // and ./resource (not all downloaded by getdown)
 -    copy {
 -      from getdownResourceDir
 -      into "${getdownFilesDir}/${getdown_resource_dir}"
 -  if (buildDist) {
 -    inputs.dir("${jalviewDir}/${package_dir}")
 -  }
--  outputs.dir(getdownWebsiteDir)
+-  outputs.dir(getdownAppBaseDir)
 -  outputs.dir(getdownFilesDir)
 -}
 -
 -    classpath = files(getdownLauncher)
 -  }
 -  main = "com.threerings.getdown.tools.Digester"
--  args getdownWebsiteDir
--  inputs.dir(getdownWebsiteDir)
--  outputs.file("${getdownWebsiteDir}/digest2.txt")
+-  args getdownAppBaseDir
+-  inputs.dir(getdownAppBaseDir)
+-  outputs.file("${getdownAppBaseDir}/digest2.txt")
 -}
 -
 -
 -  dependsOn getdownDigest
 -  doLast {
 -    if (reportRsyncCommand) {
--      def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
+-      def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
 -      def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
 -      println "LIKELY RSYNC COMMAND:"
 -      println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
 -    }
 -  }
 -}
+-
+-task getdownArchiveBuild() {
+-  group = "distribution"
+-  description = "Put files in the archive dir to go on the website"
+-
+-  dependsOn getdownWebsite
+-
+-  def v = "v${JALVIEW_VERSION_UNDERSCORES}"
+-  def vDir = "${getdownArchiveDir}/${v}"
+-  getdownFullArchiveDir = "${vDir}/getdown"
+-  getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
+-
+-  def vAltDir = "alt_${v}"
+-  def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
+-
+-  doFirst {
+-    // cleanup old "old" dir
+-    delete getdownArchiveDir
+-
+-    def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
+-    getdownArchiveTxt.getParentFile().mkdirs()
+-    def getdownArchiveTextLines = []
+-    def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
+-
+-    // the libdir
+-    copy {
+-      from "${getdownAppBaseDir}/${getdownAppDistDir}"
+-      into "${getdownFullArchiveDir}/${vAltDir}"
+-    }
+-
+-    getdownTextLines.each { line ->
+-      line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
+-      line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
+-      line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
+-      line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
+-      line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
+-      line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
+-      // remove the existing resource = resource/ or bin/ lines
+-      if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
+-        getdownArchiveTextLines += line
+-      }
+-    }
+-
+-    // the resource dir -- add these files as resource lines in getdown.txt
+-    copy {
+-      from "${archiveImagesDir}"
+-      into "${getdownFullArchiveDir}/${getdown_resource_dir}"
+-      eachFile { file ->
+-        getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
+-      }
+-    }
+-
+-    getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
+-
+-    def vLaunchJvl = file(getdownVersionLaunchJvl)
+-    vLaunchJvl.getParentFile().mkdirs()
+-    vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
+-    def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
+-    def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
+-    // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
+-    //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
+-    java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
+-
+-    // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
+-    copy {
+-      from getdownLauncher
+-      from "${getdownAppBaseDir}/${getdownLaunchJvl}"
+-      from "${getdownAppBaseDir}/${getdown_launcher_new}"
+-      from "${getdownAppBaseDir}/${channel_props}"
+-      if (file(getdownLauncher).getName() != getdown_launcher) {
+-        rename(file(getdownLauncher).getName(), getdown_launcher)
+-      }
+-      into getdownFullArchiveDir
+-    }
+-
+-  }
+-}
+-
+-task getdownArchiveDigest(type: JavaExec) {
+-  group = "distribution"
+-  description = "Digest the getdown archive folder"
+-
+-  dependsOn getdownArchiveBuild
+-
+-  doFirst {
+-    classpath = files(getdownLauncher)
+-    args getdownFullArchiveDir
+-  }
+-  main = "com.threerings.getdown.tools.Digester"
+-  inputs.dir(getdownFullArchiveDir)
+-  outputs.file("${getdownFullArchiveDir}/digest2.txt")
+-}
 -
+-task getdownArchive() {
+-  group = "distribution"
+-  description = "Build the website archive dir with getdown digest"
+-
+-  dependsOn getdownArchiveBuild
+-  dependsOn getdownArchiveDigest
+-}
 -
 -tasks.withType(JavaCompile) {
 -      options.encoding = 'UTF-8'
 -
 -clean {
 -  doFirst {
--    delete getdownWebsiteDir
+-    delete getdownAppBaseDir
 -    delete getdownFilesDir
+-    delete getdownArchiveDir
 -  }
 -}
 -
 -      }
 -    }
 -
+-    // disable install screen for OSX dmg (for 2.11.2.0)
+-    install4jConfigXml.'**'.macosArchive.each { macosArchive -> 
+-      macosArchive.attributes().remove('executeSetupApp')
+-      macosArchive.attributes().remove('setupAppId')
+-    }
+-
 -    // turn off checksum creation for LOCAL channel
 -    def e = install4jConfigXml.application[0]
--    if (CHANNEL == "LOCAL") {
--      e.'@createChecksums' = "false"
--    } else {
--      e.'@createChecksums' = "true"
--    }
+-    e.'@createChecksums' = string(install4jCheckSums)
 -
 -    // put file association actions where placeholder action is
 -    def install4jFileAssociationsText = install4jFileAssociationsFile.text
 -  }
 -}
 -
+-task cleanInstallersDataFiles {
+-  def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
+-  def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
+-  def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
+-  doFirst {
+-    delete installersOutputTxt
+-    delete installersSha256
+-    delete hugoDataJsonFile
+-  }
+-}
+-
+-task install4jDMGBackgroundImageCopy {
+-  inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
+-  outputs.dir "${install4jDMGBackgroundImageBuildDir}"
+-  doFirst {
+-    copy {
+-      from(install4jDMGBackgroundImageDir) {
+-        include(install4jDMGBackgroundImageFile)
+-      }
+-      into install4jDMGBackgroundImageBuildDir
+-    }
+-  }
+-}
+-
+-task install4jDMGBackgroundImageProcess {
+-  dependsOn install4jDMGBackgroundImageCopy
+-
+-  doFirst {
+-    if (backgroundImageText) {
+-      if (convertBinary == null) {
+-        throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
+-      }
+-      if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
+-        throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
+-      }
+-      fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
+-        exec {
+-          executable convertBinary
+-          args = [
+-            file.getPath(),
+-            '-font', install4j_background_image_text_font,
+-            '-fill', install4j_background_image_text_colour,
+-            '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
+-            '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
+-            '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
+-            file.getPath()
+-          ]
+-        }
+-      }
+-    }
+-  }
+-}
+-
+-task install4jDMGBackgroundImage {
+-  dependsOn install4jDMGBackgroundImageProcess
+-}
 -
--task installers(type: com.install4j.gradle.Install4jTask) {
+-task installerFiles(type: com.install4j.gradle.Install4jTask) {
 -  group = "distribution"
 -  description = "Create the install4j installers"
 -  dependsOn getdown
 -  dependsOn copyInstall4jTemplate
+-  dependsOn cleanInstallersDataFiles
+-  dependsOn install4jDMGBackgroundImage
 -
 -  projectFile = install4jConfFile
 -
 -    filesMd5 = filesMd5.substring(0,8)
 -  }
 -  def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
--  // make install4jBuildDir relative to jalviewDir
--  def install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
 -
 -  variables = [
 -    'JALVIEW_NAME': jalview_name,
--    'JALVIEW_APPLICATION_NAME': install4jApplicationName,
+-    'JALVIEW_APPLICATION_NAME': applicationName,
 -    'JALVIEW_DIR': "../..",
 -    'OSX_KEYSTORE': OSX_KEYSTORE,
 -    'OSX_APPLEID': OSX_APPLEID,
 -    'JAVA_VERSION': JAVA_VERSION,
 -    'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
 -    'VERSION': JALVIEW_VERSION,
--    'MACOS_JAVA_VM_DIR': macosJavaVMDir,
--    'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
--    'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
--    'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
--    'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
--    'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
 -    'COPYRIGHT_MESSAGE': install4j_copyright_message,
 -    'BUNDLE_ID': install4jBundleId,
 -    'INTERNAL_ID': install4jInternalId,
 -    'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
 -    'MACOS_DMG_DS_STORE': install4jDMGDSStore,
--    'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
+-    'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
 -    'WRAPPER_LINK': getdownWrapperLink,
 -    'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
 -    'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
+-    'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
 -    'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
 -    'INSTALLER_NAME': install4jInstallerName,
 -    'INSTALL4J_UTILS_DIR': install4j_utils_dir,
--    'GETDOWN_WEBSITE_DIR': getdown_website_dir,
+-    'GETDOWN_CHANNEL_DIR': getdownChannelDir,
 -    'GETDOWN_FILES_DIR': getdown_files_dir,
 -    'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
 -    'GETDOWN_DIST_DIR': getdownAppDistDir,
 -    'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
 -    'PNG_ICON_FILE': install4jPngIconFile,
 -    'BACKGROUND': install4jBackground,
+-  ]
 -
+-  def varNameMap = [
+-    'mac': 'MACOS',
+-    'windows': 'WINDOWS',
+-    'linux': 'LINUX'
 -  ]
+-  
+-  // these are the bundled OS/architecture VMs needed by install4j
+-  def osArch = [
+-    [ "mac", "x64" ],
+-    [ "mac", "aarch64" ],
+-    [ "windows", "x64" ],
+-    [ "linux", "x64" ],
+-    [ "linux", "aarch64" ]
+-  ]
+-  osArch.forEach { os, arch ->
+-    variables[ sprintf("%s_%s_JAVA_VM_DIR", varNameMap[os], arch.toUpperCase(Locale.ROOT)) ] = sprintf("%s/jre-%s-%s-%s/jre", jreInstallsDir, JAVA_INTEGER_VERSION, os, arch)
+-    // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
+-    // otherwise running `gradle installers` generates a non-useful error:
+-    // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
+-    variables[ sprintf("%s_%s_JAVA_VM_TGZ", varNameMap[os], arch.toUpperCase(Locale.ROOT)) ] = sprintf("%s/tgz/jre_%s_%s_%s.tar.gz", jreInstallsDir, JAVA_INTEGER_VERSION, os, arch)
+-  }
 -
 -  //println("INSTALL4J VARIABLES:")
 -  //variables.each{k,v->println("${k}=${v}")}
 -  }
 -  //verbose=true
 -
--  inputs.dir(getdownWebsiteDir)
+-  inputs.dir(getdownAppBaseDir)
 -  inputs.file(install4jConfFile)
 -  inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
--  inputs.dir(macosJavaVMDir)
--  inputs.dir(windowsJavaVMDir)
 -  outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
 -}
 -
+-def getDataHash(File myFile) {
+-  HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
+-  return myFile.exists()
+-  ? [
+-      "file" : myFile.getName(),
+-      "filesize" : myFile.length(),
+-      "sha256" : hash.toString()
+-    ]
+-  : null
+-}
+-
+-def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
+-  def hash = [
+-    "channel" : getdownChannelName,
+-    "date" : getDate("yyyy-MM-dd HH:mm:ss"),
+-    "git-commit" : "${gitHash} [${gitBranch}]",
+-    "version" : JALVIEW_VERSION
+-  ]
+-  // install4j installer files
+-  if (installersOutputTxt.exists()) {
+-    def idHash = [:]
+-    installersOutputTxt.readLines().each { def line ->
+-      if (line.startsWith("#")) {
+-        return;
+-      }
+-      line.replaceAll("\n","")
+-      def vals = line.split("\t")
+-      def filename = vals[3]
+-      def filesize = file(filename).length()
+-      filename = filename.replaceAll(/^.*\//, "")
+-      hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
+-      idHash."${filename}" = vals[0]
+-    }
+-    if (install4jCheckSums && installersSha256.exists()) {
+-      installersSha256.readLines().each { def line ->
+-        if (line.startsWith("#")) {
+-          return;
+-        }
+-        line.replaceAll("\n","")
+-        def vals = line.split(/\s+\*?/)
+-        def filename = vals[1]
+-        def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
+-      }
+-    }
+-  }
+-
+-  [
+-    "JAR": shadowJar.archiveFile, // executable JAR
+-    "JVL": getdownVersionLaunchJvl, // version JVL
+-    "SOURCE": sourceDist.archiveFile // source TGZ
+-  ].each { key, value ->
+-    def file = file(value)
+-    if (file.exists()) {
+-      def fileHash = getDataHash(file)
+-      if (fileHash != null) {
+-        hash."${key}" = fileHash;
+-      }
+-    }
+-  }
+-  return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
+-}
+-
+-task staticMakeInstallersJsonFile {
+-  doFirst {
+-    def output = findProperty("i4j_output")
+-    def sha256 = findProperty("i4j_sha256")
+-    def json = findProperty("i4j_json")
+-    if (output == null || sha256 == null || json == null) {
+-      throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
+-    }
+-    writeDataJsonFile(file(output), file(sha256), file(json))
+-  }
+-}
+-
+-task installers {
+-  dependsOn installerFiles
+-}
+-
 -
 -spotless {
 -  java {
 -  }
 -}
 -
+-task createSourceReleaseProperties(type: WriteProperties) {
+-  group = "distribution"
+-  description = "Create the source RELEASE properties file"
+-  
+-  def sourceTarBuildDir = "${buildDir}/sourceTar"
+-  def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
+-  outputFile (sourceReleasePropertiesFile)
+-
+-  doFirst {
+-    releaseProps.each{ key, val -> property key, val }
+-    property "git.branch", gitBranch
+-    property "git.hash", gitHash
+-  }
+-
+-  outputs.file(outputFile)
+-}
 -
 -task sourceDist(type: Tar) {
 -  group "distribution"
 -
 -  dependsOn createBuildProperties
 -  dependsOn convertMdFiles
+-  dependsOn eclipseAllPreferences
+-  dependsOn createSourceReleaseProperties
+-
 -
--  def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
--  def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
+-  def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
 -  archiveFileName = outputFileName
 -  
 -  compression Compression.GZIP
 -    "*locales/**",
 -    "utils/InstallAnywhere",
 -    "**/*.log",
+-    "RELEASE",
 -  ] 
 -  def PROCESS_FILES=[
 -    "AUTHORS",
 -    "FEATURETODO",
 -    "LICENSE",
 -    "**/README",
--    "RELEASE",
 -    "THIRDPARTYLIBS",
 -    "TESTNG",
 -    "build.gradle",
 -    "**/*.sh",
 -  ]
 -  def INCLUDE_FILES=[
--    ".settings/org.eclipse.jdt.core.jalview.prefs",
+-    ".classpath",
+-    ".settings/org.eclipse.buildship.core.prefs",
+-    ".settings/org.eclipse.jdt.core.prefs"
 -  ]
 -
 -  from(jalviewDir) {
 -    exclude ("utils/InstallAnywhere")
 -
 -    exclude (getdown_files_dir)
--    exclude (getdown_website_dir)
+-    // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
+-    //exclude (getdown_website_dir)
+-    //exclude (getdown_archive_dir)
 -
 -    // exluding these as not using jars as modules yet
 -    exclude ("${j11modDir}/**/*.jar")
 -    })
 -  }
 -
+-  def sourceTarBuildDir = "${buildDir}/sourceTar"
+-  from(sourceTarBuildDir) {
+-    // this includes the appended RELEASE properties file
+-  }
 -}
 -
+-task dataInstallersJson {
+-  group "website"
+-  description "Create the installers-VERSION.json data file for installer files created"
+-
+-  mustRunAfter installers
+-  mustRunAfter shadowJar
+-  mustRunAfter sourceDist
+-  mustRunAfter getdownArchive
+-
+-  def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
+-  def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
+-
+-  if (installersOutputTxt.exists()) {
+-    inputs.file(installersOutputTxt)
+-  }
+-  if (install4jCheckSums && installersSha256.exists()) {
+-    inputs.file(installersSha256)
+-  }
+-  [
+-    shadowJar.archiveFile, // executable JAR
+-    getdownVersionLaunchJvl, // version JVL
+-    sourceDist.archiveFile // source TGZ
+-  ].each { fileName ->
+-    if (file(fileName).exists()) {
+-      inputs.file(fileName)
+-    }
+-  }
+-
+-  outputs.file(hugoDataJsonFile)
+-
+-  doFirst {
+-    writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
+-  }
+-}
 -
 -task helppages {
+-  group "help"
+-  description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
+-
 -  dependsOn copyHelp
 -  dependsOn pubhtmlhelp
 -  
 -  preserve {
 -    include "**"
 -  }
+-
+-  // should this be exclude really ?
+-  duplicatesStrategy "INCLUDE"
+-
 -  outputs.files outputFiles
 -  inputs.files inputFiles
 -}
 -      println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
 -    }
 -  }
+-
 -  //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
 -  def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
 -  executable(eclipseBinary)
 -          new org.apache.tools.ant.util.TeeOutputStream(
 -            logErrFOS,
 -            stderr),
--          errorOutput)
+-          System.err)
 -    } else {
 -      standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
 -        logOutFOS,
 -task eclipseAutoBuildTask {
 -  //dependsOn jalviewjsIDE_checkJ2sPlugin
 -  //dependsOn jalviewjsIDE_PrepareSite
-+  outputs.file("${outputDir}/${archiveFileName}")
- }
+-}
+-
 -
 -task jalviewjs {
 -  group "JalviewJS"
 -  description "Build the site"
 -  dependsOn jalviewjsBuildSite
--}
++  outputs.file("${outputDir}/${archiveFileName}")
+ }
index a0e8cd9..ce7f136 100644 (file)
@@ -300,6 +300,59 @@ task copyHelp(type: Copy) {
   outputs.dir(outputDir)
 }
 
+task releasesTemplates {
+  group "help"
+  description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
+
+  dependsOn copyHelp
+
+  def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
+  def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
+  def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
+  def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
+  def releasesMdDir = "${jalviewDir}/${releases_dir}"
+  def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
+
+  doFirst {
+    def JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
+    def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
+    def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
+
+    if (CHANNEL == "RELEASE") {
+      if (!releaseMdFile.exists()) {
+        throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
+      }
+      if (!whatsnewMdFile.exists()) {
+        throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
+      }
+    }
+
+    def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
+    def releaseFilesDates = releaseFiles.collectEntries {
+      [(it): getDate("")]
+    }
+    releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
+
+    def releasesTemplate = releasesTemplateFile.text
+    def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
+    def versionTemplate = m[0][1]
+
+    def versionsHtml = ""
+    def linkedVersions = []
+
+    releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
+    releasesHtmlFile.text = releasesTemplate
+
+    whatsnewHtmlFile.text = "Debian build " + getDate("yyyy-MM-dd HH:mm:ss")
+  }
+
+  inputs.file(releasesTemplateFile)
+  inputs.file(whatsnewTemplateFile)
+  inputs.dir(releasesMdDir)
+  inputs.dir(whatsnewMdDir)
+  outputs.file(releasesHtmlFile)
+  outputs.file(whatsnewHtmlFile)
+}
 
 task copyResources(type: Copy) {
   group = "build"
@@ -389,6 +442,7 @@ task prepare {
   dependsOn buildResources
   dependsOn copyDocs
   dependsOn copyHelp
+  dependsOn releasesTemplates
   dependsOn buildIndices
 }
 
@@ -453,7 +507,7 @@ task linkCheck(type: JavaExec) {
   def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
   classpath = files("${jalviewDir}/${utils_dir}")
   main = "HelpLinksChecker"
-  workingDir = jalviewDir
+  workingDir = "${helpBuildDir}"
   args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
 
   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append