Merge branch 'improvement/JAL-3449_add_install4j_installer_getdown_versions_to_jalvie...
authorJim Procter <jprocter@issues.jalview.org>
Wed, 26 Feb 2020 14:52:19 +0000 (14:52 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 26 Feb 2020 14:52:19 +0000 (14:52 +0000)
1  2 
build.gradle
doc/building.md
gradle.properties

diff --combined build.gradle
@@@ -1,15 -1,15 +1,15 @@@
  import org.apache.tools.ant.filters.ReplaceTokens
- //import org.apache.tools.ant.filters.ReplaceRegexp
  import org.gradle.internal.os.OperatingSystem
- import org.gradle.plugins.ide.eclipse.model.*
+ import org.gradle.plugins.ide.eclipse.model.Output
+ import org.gradle.plugins.ide.eclipse.model.Library
+ import java.security.MessageDigest
  import groovy.transform.ExternalizeMethods
+ import groovy.util.XmlParser
+ import groovy.xml.XmlUtil
  
  buildscript {
    dependencies {
      classpath 'org.openclover:clover:4.4.1'
-     classpath 'org.apache.commons:commons-compress:1.18'
    }
  }
  
@@@ -18,7 -18,8 +18,8 @@@ plugins 
    id 'application'
    id 'eclipse'
    id 'com.github.johnrengelman.shadow' version '4.0.3'
-   id 'com.install4j.gradle' version '7.0.9'
+   id 'com.install4j.gradle' version '8.0.2'
+   id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
  }
  
  repositories {
    }
  }
  
- mainClassName = launcherClass
- def cloverInstrDir = file("$buildDir/$cloverSourcesInstrDir")
- def classes = "$jalviewDir/$classesDir"
- if (clover.equals("true")) {
-   use_clover = true
-   classes = "$buildDir/$cloverClassesDir"
- } else {
-   use_clover = false
-   classes = "$jalviewDir/$classesDir"
+ dependencies {
  }
  
- // configure classpath/args for j8/j11 compilation
  
- def jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
- def libDir
- def libDistDir
- def compile_source_compatibility
- def compile_target_compatibility
+ // 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()
+ }
  
  ext {
-   getdownWebsiteDir = jalviewDir + '/' + getdown_website_dir + '/' + JAVA_VERSION
-   getdownDir = ""
-   reportRsyncCmd = false
+   jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
+   jalviewDirRelativePath = jalviewDir
+   // local build environment properties
+   def localProps = "${jalviewDirAbsolutePath}/local.properties"
+   if (file(localProps).exists()) {
+     try {
+       def p = new Properties()
+       def localPropsFIS = new FileInputStream(localProps)
+       p.load(localPropsFIS)
+       localPropsFIS.close()
+       p.each {
+         key, val -> 
+           def oldval = findProperty(key)
+           setProperty(key, val)
+           if (oldval != null) {
+             println("Overriding property '${key}' ('${oldval}') with local.properties value '${val}'")
+           } else {
+             println("Setting unknown property '${key}' with local.properties value '${val}'")
+           }
+       }
+     } catch (Exception e) {
+       System.out.println("Exception reading local.properties")
+     }
+   }
+   // this property set when running Eclipse headlessly
+   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
+   // this property set by Eclipse
+   eclipseApplicationProperty = string("eclipse.application")
+   // CHECK IF RUNNING FROM WITHIN ECLIPSE
+   def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
+   IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
+   // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
+   if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
+     println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
+     IN_ECLIPSE = false
+   }
+   if (IN_ECLIPSE) {
+     println("WITHIN ECLIPSE IDE")
+   } else {
+     println("HEADLESS BUILD")
+   }
+   /* *-/
+   System.properties.sort { it.key }.each {
+     key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
+   }
+   /-* *-/
+   if (false && IN_ECLIPSE) {
+     jalviewDir = jalviewDirAbsolutePath
+   }
+   */
+   // essentials
+   bareSourceDir = string(source_dir)
+   sourceDir = string("${jalviewDir}/${bareSourceDir}")
+   resourceDir = string("${jalviewDir}/${resource_dir}")
+   bareTestSourceDir = string(test_source_dir)
+   testSourceDir = string("${jalviewDir}/${bareTestSourceDir}")
+   // clover
+   cloverInstrDir = file("${buildDir}/${cloverSourcesInstrDir}")
+   classesDir = string("${jalviewDir}/${classes_dir}")
+   if (clover.equals("true")) {
+     use_clover = true
+     classesDir = string("${buildDir}/${cloverClassesDir}")
+   } else {
+     use_clover = false
+     classesDir = string("${jalviewDir}/${classes_dir}")
+   }
+   classes = classesDir
+   getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
    buildDist = true
-   buildProperties = buildPropertiesFile
-   getdownLauncher = jalviewDir + '/' + getdown_lib_dir + '/' + getdown_launcher
+   // the following values might be overridden by the CHANNEL switch
+   getdownChannelName = CHANNEL.toLowerCase()
+   getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+   getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+   getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
+   getdownAppDistDir = getdown_app_dir_alt
+   buildProperties = string("${classesDir}/${build_properties_file}")
+   reportRsyncCmd = false
+   jvlChannelName = CHANNEL.toLowerCase()
    switch (CHANNEL) {
  
      case "BUILD":
      // TODO: get bamboo build artifact URL for getdown artifacts
      getdown_channel_base = bamboo_channelbase
-     getdown_channel_name = bamboo_planKey + '/'+JAVA_VERSION
-     getdown_app_base = bamboo_channelbase + '/'+ bamboo_planKey + bamboo_getdown_channel_suffix + '/'+JAVA_VERSION
-     getdown_app_dir = getdown_app_dir_alt
-     buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+     getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
+     getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
+     jvlChannelName += "_${getdownChannelName}"
      break
  
      case "RELEASE":
-     getdown_channel_name = CHANNEL.toLowerCase()
-     getdownDir = getdown_channel_name + "/" + JAVA_VERSION
-     getdown_app_base = getdown_channel_base + "/" + getdownDir
-     getdown_app_dir = getdown_app_dir_release
-     buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+     getdownAppDistDir = getdown_app_dir_release
      reportRsyncCommand = true
      break
  
      case "ARCHIVE":
-     getdown_channel_name = CHANNEL.toLowerCase()+"/"+JALVIEW_VERSION
-     getdownDir = getdown_channel_name + "/" + JAVA_VERSION
-     getdown_app_base = getdown_channel_base + "/" + getdownDir
-     getdown_app_dir = getdown_app_dir_alt
-     if (!file(ARCHIVEDIR+"/"+packageDir).exists()) {
-       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
-       exit
+     getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
+     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
+       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
      } else {
-       packageDir = ARCHIVEDIR + "/" + packageDir
-       buildProperties = ARCHIVEDIR +"/" + classesDir + "/" + buildPropertiesFile
+       packageDir = string("${ARCHIVEDIR}/${packageDir}")
+       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
        buildDist = false
      }
      reportRsyncCommand = true
      break
  
      case "ARCHIVELOCAL":
-     getdown_channel_name = "archive" + "/" + JALVIEW_VERSION
-     getdownDir = getdown_channel_name + "/" + JAVA_VERSION
-     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
-     getdown_app_dir = getdown_app_dir_alt
-     if (!file(ARCHIVEDIR+"/"+packageDir).exists()) {
-       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
-       exit
+     getdownChannelName = string("archive/${JALVIEW_VERSION}")
+     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+     getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
+       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
      } else {
-       packageDir = ARCHIVEDIR + "/" + packageDir
-       buildProperties = ARCHIVEDIR +"/" + classesDir + "/" + buildPropertiesFile
+       packageDir = string("${ARCHIVEDIR}/${packageDir}")
+       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
        buildDist = false
      }
      reportRsyncCommand = true
-     getdownLauncher = jalviewDir + '/' + getdown_lib_dir + '/' + getdown_launcher_local
+     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
      break
  
      case "DEVELOP":
-     getdown_channel_name = CHANNEL.toLowerCase()
-     getdownDir = getdown_channel_name + "/" + JAVA_VERSION
-     getdown_app_base = getdown_channel_base + "/" + getdownDir
-     getdown_app_dir = getdown_app_dir_alt
-     buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
      reportRsyncCommand = true
      break
  
      case "TEST-RELEASE":
-     getdown_channel_name = CHANNEL.toLowerCase()
-     getdownDir = getdown_channel_name + "/" + JAVA_VERSION
-     getdown_app_base = getdown_channel_base + "/" + getdownDir
-     getdown_app_dir = getdown_app_dir_alt
-     buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
      reportRsyncCommand = true
      break
  
      case ~/^SCRATCH(|-[-\w]*)$/:
-     getdown_channel_name = CHANNEL
-     getdownDir = getdown_channel_name + "/" + JAVA_VERSION
-     getdown_app_base = getdown_channel_base + "/" + getdownDir
-     getdown_app_dir = getdown_app_dir_alt
-     buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+     getdownChannelName = CHANNEL
+     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
      reportRsyncCommand = true
      break
  
+     case "TEST-LOCAL":
+     if (!file("${LOCALDIR}").exists()) {
+       throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
+     } else {
+       getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
+       getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
+     }
+     break
      case "LOCAL":
-     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
-     getdown_app_dir = getdown_app_dir_alt
-     buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
-     getdownLauncher = jalviewDir + '/' + getdown_lib_dir + '/' + getdown_launcher_local
+     getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
      break
  
      default: // something wrong specified
-     print("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
-     exit
+     throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
      break
  
    }
-   println("Using a "+CHANNEL+" profile. appbase="+getdown_app_base)
-   getdownAppDir = getdownWebsiteDir + '/' + getdown_app_dir
-   //getdownJ11libDir = getdownWebsiteDir + '/' + getdown_j11lib_dir
-   getdownResourceDir = getdownWebsiteDir + '/' + getdown_resource_dir
-   getdownInstallDir = getdownWebsiteDir + '/' + getdown_install_dir
-   getdownFilesDir = jalviewDir + '/' + getdown_files_dir + '/' + JAVA_VERSION + '/'
-   getdownFilesInstallDir = getdownFilesDir+"/"+getdown_install_dir
+   // override getdownAppBase if requested
+   if (findProperty("getdown_appbase_override") != null) {
+     getdownAppBase = string(getProperty("getdown_appbase_override"))
+     println("Overriding getdown appbase with '${getdownAppBase}'")
+   }
+   // sanitise file name for jalview launcher file for this channel
+   jvlChannelName = jvlChannelName.replaceAll(/[^\w\-]/,"_")
+   getdownAppDir = string("${getdownWebsiteDir}/${getdownAppDistDir}")
+   //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
+   getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
+   getdownInstallDir = string("${getdownWebsiteDir}/${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_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
    modules_runtimeClasspath = modules_compileClasspath
    */
-   gitHash = ""
-   gitBranch = ""
- }
+   gitHash = string("")
+   gitBranch = string("")
+   println("Using a ${CHANNEL} profile.")
+   additional_compiler_args = []
+   // configure classpath/args for j8/j11 compilation
+   if (JAVA_VERSION.equals("1.8")) {
+     JAVA_INTEGER_VERSION = string("8")
+     //libDir = j8libDir
+     libDir = j11libDir
+     libDistDir = j8libDir
+     compile_source_compatibility = 1.8
+     compile_target_compatibility = 1.8
+     // these are getdown.txt properties defined dependent on the JAVA_VERSION
+     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
+     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
+     // this property is assigned below and expanded to multiple lines in the getdown task
+     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
+     // this property is for the Java library used in eclipse
+     eclipseJavaRuntimeName = string("JavaSE-1.8")
+   } else if (JAVA_VERSION.equals("11")) {
+     JAVA_INTEGER_VERSION = string("11")
+     libDir = j11libDir
+     libDistDir = j11libDir
+     compile_source_compatibility = 11
+     compile_target_compatibility = 11
+     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")
+     /* compile without modules -- using classpath libraries
+     additional_compiler_args += [
+     '--module-path', modules_compileClasspath.asPath,
+     '--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
+     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")
+     /* compile without modules -- using classpath libraries
+     additional_compiler_args += [
+     '--module-path', modules_compileClasspath.asPath,
+     '--add-modules', j11modules
+     ]
+      */
+   } else {
+     throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
+   }
  
- def JAVA_INTEGER_VERSION
- def additional_compiler_args = []
- // these are getdown.txt properties defined dependent on the JAVA_VERSION
- def getdown_alt_java_min_version
- def getdown_alt_java_max_version
- // this property is assigned below and expanded to multiple lines in the getdown task
- def getdown_alt_multi_java_location
- // this property is for the Java library used in eclipse
- def eclipse_java_runtime_name
- if (JAVA_VERSION.equals("1.8")) {
-   JAVA_INTEGER_VERSION = "8"
-   //libDir = j8libDir
-   libDir = j11libDir
-   libDistDir = j8libDir
-   compile_source_compatibility = 1.8
-   compile_target_compatibility = 1.8
-   getdown_alt_java_min_version = getdown_alt_java8_min_version
-   getdown_alt_java_max_version = getdown_alt_java8_max_version
-   getdown_alt_multi_java_location = getdown_alt_java8_txt_multi_java_location
-   eclipse_java_runtime_name = "JavaSE-1.8"
- } else if (JAVA_VERSION.equals("11")) {
-   JAVA_INTEGER_VERSION = "11"
-   libDir = j11libDir
-   libDistDir = j11libDir
-   compile_source_compatibility = 11
-   compile_target_compatibility = 11
-   getdown_alt_java_min_version = getdown_alt_java11_min_version
-   getdown_alt_java_max_version = getdown_alt_java11_max_version
-   getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
-   eclipse_java_runtime_name = "JavaSE-11"
-   /* compile without modules -- using classpath libraries
-   additional_compiler_args += [
-   '--module-path', ext.modules_compileClasspath.asPath,
-   '--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
-   getdown_alt_java_min_version = getdown_alt_java11_min_version
-   getdown_alt_java_max_version = getdown_alt_java11_max_version
-   getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
-   eclipse_java_runtime_name = "JavaSE-11"
-   /* compile without modules -- using classpath libraries
-   additional_compiler_args += [
-   '--module-path', ext.modules_compileClasspath.asPath,
-   '--add-modules', j11modules
-   ]
-   */
- } else {
-   throw new GradleException("JAVA_VERSION=$JAVA_VERSION not currently supported by Jalview")
+   // for install4j
+   JAVA_MIN_VERSION = JAVA_VERSION
+   JAVA_MAX_VERSION = JAVA_VERSION
+   def 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}")
+   install4jHomeDir = install4j_home_dir
+   if (install4jHomeDir.startsWith("~/")) {
+     install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
+   }
+   buildingHTML = string("${jalviewDir}/${docDir}/building.html")
+   helpFile = string("${classesDir}/${help_dir}/help.jhm")
+   helpParentDir = string("${jalviewDir}/${help_parent_dir}")
+   helpSourceDir = string("${helpParentDir}/${help_dir}")
+   relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
+   jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
+   jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
+   if (IN_ECLIPSE) {
+     jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
+   } else {
+     jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
+   }
+   jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
+   jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
+   jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
+   jalviewjsJalviewCoreHtmlFile = string("")
+   jalviewjsJalviewCoreName = string(jalviewjs_core_name)
+   jalviewjsCoreClasslists = []
+   jalviewjsJalviewTemplateName = string(jalviewjs_name)
+   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
+   jalviewjsJ2sProps = null
+   eclipseWorkspace = null
+   eclipseBinary = string("")
+   eclipseVersion = string("")
+   eclipseDebug = false
+   // ENDEXT
  }
  
- sourceSets {
  
+ sourceSets {
    main {
      java {
-       srcDirs "$jalviewDir/$sourceDir"
-       outputDir = file("$classes")
+       srcDirs sourceDir
+       outputDir = file(classesDir)
      }
  
      resources {
-       srcDirs "$jalviewDir/$resourceDir"
+       srcDirs resourceDir
+       srcDirs += helpParentDir
      }
  
-     jar.destinationDir = file("$jalviewDir/$packageDir")
+     jar.destinationDir = file("${jalviewDir}/${packageDir}")
  
      compileClasspath = files(sourceSets.main.java.outputDir)
-     compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
+     //compileClasspath += files(sourceSets.main.resources.srcDirs)
+     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
  
      runtimeClasspath = compileClasspath
    }
    clover {
      java {
        srcDirs = [ cloverInstrDir ]
      compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
      compileClasspath += files(sourceSets.main.java.outputDir)
      compileClasspath += sourceSets.main.compileClasspath
-     compileClasspath += fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"])
-     compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
+     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"])
+     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
  
      runtimeClasspath = compileClasspath
    }
  
    test {
      java {
-       srcDirs "$jalviewDir/$testSourceDir"
-       outputDir = file("$jalviewDir/$testOutputDir")
+       srcDirs testSourceDir
+       outputDir = file("${jalviewDir}/${testOutputDir}")
      }
  
      resources {
        compileClasspath += files(sourceSets.main.java.outputDir)
      }
  
-     compileClasspath += fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"])
-     compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
+     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
+     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"])
+     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"])
  
      runtimeClasspath = compileClasspath
    }
  }
  
  // clover bits
  dependencies {
    if (use_clover) {
    }
  }
  
  configurations {
    cloverRuntime
    cloverRuntime.extendsFrom cloverCompile
  
  eclipse {
    project {
-     name = "Jalview with gradle build"
+     name = eclipse_project_name
  
      natures 'org.eclipse.jdt.core.javanature',
      'org.eclipse.jdt.groovy.core.groovyNature',
    classpath {
      //defaultOutputDir = sourceSets.main.java.outputDir
      def removeThese = []
-     configurations.each{ if (it.isCanBeResolved()) {
-       removeThese += it
+     configurations.each{
+       if (it.isCanBeResolved()) {
+         removeThese += it
+       }
      }
-   }
  
-   minusConfigurations += removeThese
-   plusConfigurations = [ ]
-   file {
+     minusConfigurations += removeThese
+     plusConfigurations = [ ]
+     file {
  
-     whenMerged { cp ->
-       def removeTheseToo = []
-       HashMap<String, Boolean> addedSrcPath = new HashMap<>();
-       cp.entries.each { entry ->
-         if (entry.kind == 'src') {
-           if (addedSrcPath.getAt(entry.path) || !(entry.path == "src" || entry.path == "test")) {
-             removeTheseToo += entry
-           } else {
-             addedSrcPath.putAt(entry.path, true)
+       whenMerged { cp ->
+         def removeTheseToo = []
+         HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
+         cp.entries.each { entry ->
+           // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
+           // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
+           // we add the resources and help/help dirs in as libs afterwards (see below)
+           if (entry.kind == 'src') {
+             if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
+               removeTheseToo += entry
+             } else {
+               alreadyAddedSrcPath.putAt(entry.path, true)
+             }
            }
-         }
-       }
-       cp.entries.removeAll(removeTheseToo)
-       print ("CP="+cp.inspect())
-       cp.entries += new Output("bin/main")
-       cp.entries += new Library(fileReference(helpParentDir))
-       cp.entries += new Library(fileReference(resourceDir))
  
-       HashMap<String, Boolean> addedLibPath = new HashMap<>();
-       // changing from sourcesets.main.classpath to specific Java version lib
-       //sourceSets.main.compileClasspath.each{
-       fileTree("$jalviewDir/$libDistDir").include("**/*.jar").include("*.jar").each {
-         //don't want to add outputDir as eclipse is using its own output dir in bin/main
-         if (it.isDirectory() || ! it.exists()) {
-           // don't add dirs to classpath
-           return
-         }
-         def itPath = it.toString()
-         if (itPath.startsWith(jalviewDirAbsolutePath+"/")) {
-           itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
-         }
-         if (addedLibPath.get(itPath)) {
-           //println("Not adding duplicate entry "+itPath)
-         } else {
-           //println("Adding entry "+itPath)
-           cp.entries += new Library(fileReference(itPath))
-           addedLibPath.put(itPath, true)
          }
-       }
+         cp.entries.removeAll(removeTheseToo)
  
-       // changing from sourcesets.main.classpath to specific Java version lib
-       //sourceSets.test.compileClasspath.each{
-       fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"]).each {
-         //if ((it.isDirectory() || ! it.exists()) && ! (it.equals(sourceSets.main.java.outputDir))) {
-         //no longer want to add outputDir as eclipse is using its own output dir in bin/main
-         if (it.isDirectory() || ! it.exists()) {
-           // don't add dirs to classpath
-           return false // groovy "break" in .each loop
-         }
-         def itPath = it.toString()
-         if (itPath.startsWith(jalviewDirAbsolutePath+"/")) {
-           itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
-         }
-         if (addedLibPath.get(itPath)) {
-           // don't duplicate
-         } else {
-           def lib = new Library(fileReference(itPath))
-           // this doesn't work... yet.  Adding test=true attribute using withXml below
-           //def attrs = new Node(null, 'attributes', ["test":"true"])
-           //lib.appendNode(attrs) //
-           cp.entries += lib
-           addedLibPath.put(itPath, true)
+         //cp.entries += new Output("${eclipse_bin_dir}/main")
+         if (file(helpParentDir).isDirectory()) {
+           cp.entries += new Library(fileReference(helpParentDir))
          }
+         if (file(resourceDir).isDirectory()) {
+           cp.entries += new Library(fileReference(resourceDir))
          }
-       }
  
-       // withXml changes ignored by buildship, these add the "test=true" attribute
-       withXml {
-         def node = it.asNode()
+         HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
  
-         def srcTestAttributes
-         node.children().each{ cpe ->
-           def attributes = cpe.attributes()
-           if (attributes.get("kind") == "src" && attributes.get("path") == "test") {
-             srcTestAttributes = cpe.find { a -> a.name() == "attributes" }
-             return
+         sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
+           //don't want to add outputDir as eclipse is using its own output dir in bin/main
+           if (it.isDirectory() || ! it.exists()) {
+             // don't add dirs to classpath, especially if they don't exist
+             return false // groovy "continue" in .any closure
            }
-         }
-         def addTestAttribute = true
-         srcTestAttributes.each{a ->
-           if (a.name() == "attribute" && a.attributes().getAt("name") == "test") {
-             addTestAttribute = false
+           def itPath = it.toString()
+           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
+             // make relative path
+             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
+           }
+           if (alreadyAddedLibPath.get(itPath)) {
+             //println("Not adding duplicate entry "+itPath)
+           } else {
+             //println("Adding entry "+itPath)
+             cp.entries += new Library(fileReference(itPath))
+             alreadyAddedLibPath.put(itPath, true)
            }
-         }
-         if (addTestAttribute) {
-           srcTestAttributes.append(new Node(null, "attribute", [name:"test", value:"true"]))
          }
  
-         node.children().each{ cpe ->
-           def attributes = cpe.attributes()
-           if (attributes.get("kind") == "lib" && attributes.get("path").startsWith("utils/")) {
-             cpe.appendNode('attributes')
-             .appendNode('attribute', [name:"test", value:"true"])
+         sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
+           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
+           if (it.isDirectory() || ! it.exists()) {
+             // don't add dirs to classpath
+             return false // groovy "continue" in .any closure
+           }
+           def itPath = it.toString()
+           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
+             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
+           }
+           if (alreadyAddedLibPath.get(itPath)) {
+             // don't duplicate
+           } else {
+             def lib = new Library(fileReference(itPath))
+             lib.entryAttributes["test"] = "true"
+             cp.entries += lib
+             alreadyAddedLibPath.put(itPath, true)
            }
          }
-       } // withXML
+       } // whenMerged
      } // file
  
      containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
    } // classpath
  
    jdt {
      // for the IDE, use java 11 compatibility
      sourceCompatibility = compile_source_compatibility
      targetCompatibility = compile_target_compatibility
-     javaRuntimeName = eclipse_java_runtime_name
+     javaRuntimeName = eclipseJavaRuntimeName
  
+     // add in jalview project specific properties/preferences into eclipse core preferences
      file {
        withProperties { props ->
          def jalview_prefs = new Properties()
-         def ins = new FileInputStream(jalviewDirAbsolutePath+"/"+eclipse_extra_jdt_prefs_file)
+         def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
          jalview_prefs.load(ins)
          ins.close()
          jalview_prefs.forEach { t, v ->
          }
        }
      }
-   }
  
-   //synchronizationTasks eclipseClasspath
-   //autoBuildTasks eclipseClasspath
+   } // jdt
+   if (IN_ECLIPSE) {
+     // Don't want these to be activated if in headless build
+     synchronizationTasks "eclipseSynchronizationTask"
+     autoBuildTasks "eclipseAutoBuildTask"
+   }
  }
  
  task cloverInstr() {
    // only instrument source, we build test classes as normal
    inputs.files files (sourceSets.main.allJava,sourceSets.test.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"]))
  }
  
  
 +task cloverReportHTML (type: JavaExec) {
 +    inputs.dir "${buildDir}/clover"
 +    outputs.dir "${reportsDir}/clover"
 +
 +    classpath configurations.cloverRuntime
 +    maxHeapSize "${cloverReportJVMHeap}"
 +    jvmArgs += "${cloverReportJVMArgs}"
 +    main = "com.atlassian.clover.reporters.html.HtmlReporter"
 +    args  "--initstring", "${buildDir}/clover/clover.db", "-o", "${reportsDir}/clover"
 +    "${cloverReportHTMLOptions}".split(",").each {
 +      args+= it.trim()
 +      }
 +}
 +
 +task cloverReportXML (type: JavaExec) {
 +    inputs.dir "${buildDir}/clover"
 +    outputs.dir "${reportsDir}/clover"
 +    maxHeapSize "${cloverReportJVMHeap}"
 +    jvmArgs "${cloverReportJVMArgs}"
 +    classpath configurations.cloverRuntime
 +    main = "com.atlassian.clover.reporters.xml.XMLReporter"
 +    args  "--initstring", "${buildDir}/clover/clover.db", "-o", "${reportsDir}/clover/clover.xml"
 +    
 +    "${cloverReportXMLOptions}".split(",").each {
 +      args+= it.trim()
 +      }
 +}
  task cloverReport {
    group = "Verification"
 -    description = "Createst the Clover report"
 +    description = "Creates the Clover report"
      inputs.dir "${buildDir}/clover"
      outputs.dir "${reportsDir}/clover"
      onlyIf {
        file("${buildDir}/clover/clover.db").exists()
      }
 -  doFirst {
 -    def argsList = ["--initstring", "${buildDir}/clover/clover.db",
 -    "-o", "${reportsDir}/clover"]
 -    String[] args = argsList.toArray()
 -    com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
 -
 -    // and generate ${reportsDir}/clover/clover.xml
 -    args = ["--initstring", "${buildDir}/clover/clover.db",
 -    "-o", "${reportsDir}/clover/clover.xml"].toArray()
 -    com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
 -  }
 +    dependsOn cloverReportXML
 +    dependsOn cloverReportHTML
  }
  // end clover bits
  
  
@@@ -522,6 -603,7 +621,7 @@@ compileJava 
  
  }
  
  compileTestJava {
    if (use_clover) {
      dependsOn compileCloverJava
@@@ -549,21 -631,29 +649,29 @@@ compileCloverJava 
    classpath += configurations.cloverRuntime
  }
  
  clean {
-   delete sourceSets.main.java.outputDir
+   doFirst {
+     delete sourceSets.main.java.outputDir
+   }
  }
  
  cleanTest {
-   delete sourceSets.test.java.outputDir
-   delete cloverInstrDir
+   doFirst {
+     delete sourceSets.test.java.outputDir
+     delete cloverInstrDir
+   }
  }
  
  // format is a string like date.format("dd MMMM yyyy")
  def getDate(format) {
    def date = new Date()
    return date.format(format)
  }
  
  task setGitVals {
    def hashStdOut = new ByteArrayOutputStream()
    exec {
      ignoreExitValue true
    }
  
-   project.ext.gitHash = hashStdOut.toString().trim()
-   project.ext.gitBranch = branchStdOut.toString().trim()
+   gitHash = hashStdOut.toString().trim()
+   gitBranch = branchStdOut.toString().trim()
  
    outputs.upToDateWhen { false }
  }
  
  task createBuildProperties(type: WriteProperties) {
    dependsOn setGitVals
-   inputs.dir("$jalviewDir/$sourceDir")
-   inputs.dir("$classes")
-   inputs.dir("$jalviewDir/$resourceDir")
+   inputs.dir(sourceDir)
+   inputs.dir(resourceDir)
+   file(buildProperties).getParentFile().mkdirs()
    outputFile (buildProperties)
    // taking time specific comment out to allow better incremental builds
    comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
    //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
    property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
    property "VERSION", JALVIEW_VERSION
-   property "INSTALLATION", INSTALLATION+" git-commit:"+project.ext.gitHash+" ["+project.ext.gitBranch+"]"
+   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
    outputs.file(outputFile)
  }
  
- def buildingHTML = "$jalviewDir/$docDir/building.html"
- task deleteBuildingHTML(type: Delete) {
-   delete buildingHTML
+ task cleanBuildingHTML(type: Delete) {
+   doFirst {
+     delete buildingHTML
+   }
  }
  
  task convertBuildingMD(type: Exec) {
-   dependsOn deleteBuildingHTML
-   def buildingMD = "$jalviewDir/$docDir/building.md"
-   def css = "$jalviewDir/$docDir/github.css"
+   dependsOn cleanBuildingHTML
+   def buildingMD = "${jalviewDir}/${docDir}/building.md"
+   def css = "${jalviewDir}/${docDir}/github.css"
  
    def pandoc = null
    pandoc_exec.split(",").each {
    }
  
    def hostname = "hostname".execute().text.trim()
-   if ((pandoc == null || ! file(pandoc).exists()) && hostname.equals("jv-bamboo")) {
+   def buildtoolsPandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
+   if ((pandoc == null || ! file(pandoc).exists()) && file(buildtoolsPandoc).exists()) {
      pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
    }
  
          commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
      } else {
          println("Cannot find pandoc. Skipping convert building.md to HTML")
-         throw new StopExecutionException()
+         throw new StopExecutionException("Cannot find pandoc. Skipping convert building.md to HTML")
      }
    }
  
    inputs.file(css)
    outputs.file(buildingHTML)
  }
  clean {
-   delete buildingHTML
+   doFirst {
+     delete buildingHTML
+   }
  }
  
  task syncDocs(type: Sync) {
    dependsOn convertBuildingMD
-   def syncDir = "$classes/$docDir"
-   from fileTree("$jalviewDir/$docDir")
+   def syncDir = "${classesDir}/${docDir}"
+   from fileTree("${jalviewDir}/${docDir}")
    into syncDir
  
  }
  
- def helpFile = "$classes/$helpDir/help.jhm"
  
  task copyHelp(type: Copy) {
-   def inputDir = "$jalviewDir/$helpParentDir/$helpDir"
-   def outputDir = "$classes/$helpDir"
+   def inputDir = helpSourceDir
+   def outputDir = "${classesDir}/${help_dir}"
    from(inputDir) {
      exclude '**/*.gif'
-       exclude '**/*.jpg'
-       exclude '**/*.png'
-       filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION,'Year-Rel': getDate("yyyy")])
+     exclude '**/*.jpg'
+     exclude '**/*.png'
+     filter(ReplaceTokens,
+       beginToken: '$$',
+       endToken: '$$',
+       tokens: [
+         'Version-Rel': JALVIEW_VERSION,
+         'Year-Rel': getDate("yyyy")
+       ]
+     )
    }
    from(inputDir) {
      include '**/*.gif'
-       include '**/*.jpg'
-       include '**/*.png'
+     include '**/*.jpg'
+     include '**/*.png'
    }
    into outputDir
  
    outputs.dir(outputDir)
  }
  
  task syncLib(type: Sync) {
-   def syncDir = "$classes/$libDistDir"
-   from fileTree("$jalviewDir/$libDistDir")
+   def syncDir = "${classesDir}/${libDistDir}"
+   from fileTree("${jalviewDir}/${libDistDir}")
    into syncDir
  }
  
  task syncResources(type: Sync) {
-   from "$jalviewDir/$resourceDir"
+   from resourceDir
    include "**/*.*"
-   exclude "install4j"
-   into "$classes"
+   into "${classesDir}"
    preserve {
      include "**"
    }
  }
  
  task prepare {
    dependsOn syncResources
    dependsOn syncDocs
@@@ -719,65 -827,75 +845,75 @@@ test 
    sourceCompatibility = compile_source_compatibility
    targetCompatibility = compile_target_compatibility
    jvmArgs += additional_compiler_args
-   print ("Setting target compatibility to "+targetCompatibility+"\n")
  }
  
  task buildIndices(type: JavaExec) {
    dependsOn copyHelp
    classpath = sourceSets.main.compileClasspath
    main = "com.sun.java.help.search.Indexer"
-   workingDir = "$classes/$helpDir"
+   workingDir = "${classesDir}/${help_dir}"
    def argDir = "html"
    args = [ argDir ]
-   inputs.dir("$workingDir/$argDir")
-   outputs.dir("$classes/doc")
-   outputs.dir("$classes/help")
-   outputs.file("$workingDir/JavaHelpSearch/DOCS")
-   outputs.file("$workingDir/JavaHelpSearch/DOCS.TAB")
-   outputs.file("$workingDir/JavaHelpSearch/OFFSETS")
-   outputs.file("$workingDir/JavaHelpSearch/POSITIONS")
-   outputs.file("$workingDir/JavaHelpSearch/SCHEMA")
-   outputs.file("$workingDir/JavaHelpSearch/TMAP")
+   inputs.dir("${workingDir}/${argDir}")
+   outputs.dir("${classesDir}/doc")
+   outputs.dir("${classesDir}/help")
+   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
+   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
+   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
+   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
+   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
+   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
  }
  
  task compileLinkCheck(type: JavaCompile) {
    options.fork = true
-   classpath = files("$jalviewDir/$utilsDir")
-   destinationDir = file("$jalviewDir/$utilsDir")
-   source = fileTree(dir: "$jalviewDir/$utilsDir", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
-   inputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.java")
-   inputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.java")
-   outputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.class")
-   outputs.file("$jalviewDir/$utilsDir/BufferedLineReader.class")
+   classpath = files("${jalviewDir}/${utilsDir}")
+   destinationDir = file("${jalviewDir}/${utilsDir}")
+   source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
+   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
+   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
+   outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class")
+   outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class")
  }
  
- def helplinkscheckertouchfile = file("$jalviewDir/$utilsDir/HelpLinksChecker.touch")
  task linkCheck(type: JavaExec) {
    dependsOn prepare, compileLinkCheck
-   classpath = files("$jalviewDir/$utilsDir")
+   def helpLinksCheckerOutFile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.out")
+   classpath = files("${jalviewDir}/${utilsDir}")
    main = "HelpLinksChecker"
    workingDir = jalviewDir
-   def help = "$classes/$helpDir"
-   args = [ "$classes/$helpDir", "-nointernet" ]
-   doLast {
-     helplinkscheckertouchfile.createNewFile()
-   }
-   inputs.dir("$classes/$helpDir")
-   outputs.file(helplinkscheckertouchfile)
+   args = [ "${classesDir}/${help_dir}", "-nointernet" ]
+   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
+   def errFOS = outFOS
+   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+     outFOS,
+     standardOutput)
+   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+     outFOS,
+     errorOutput)
+   inputs.dir("${classesDir}/${help_dir}")
+   outputs.file(helpLinksCheckerOutFile)
  }
  
  // import the pubhtmlhelp target
- ant.properties.basedir = "$jalviewDir"
- ant.properties.helpBuildDir = jalviewDirAbsolutePath+"/$classes/$helpDir"
- ant.importBuild "$utilsDir/publishHelp.xml"
+ ant.properties.basedir = "${jalviewDir}"
+ ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${help_dir}"
+ ant.importBuild "${utilsDir}/publishHelp.xml"
  
  
  task cleanPackageDir(type: Delete) {
-   delete fileTree("$jalviewDir/$packageDir").include("*.jar")
+   doFirst {
+     delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar")
+   }
  }
  
  jar {
      "Codebase": application_codebase
    }
  
-   destinationDir = file("$jalviewDir/$packageDir")
+   destinationDir = file("${jalviewDir}/${packageDir}")
    archiveName = rootProject.name+".jar"
  
    exclude "cache*/**"
    exclude "**/*.jar"
    exclude "**/*.jar.*"
  
-   inputs.dir("$classes")
-   outputs.file("$jalviewDir/$packageDir/$archiveName")
+   inputs.dir(classesDir)
+   outputs.file("${jalviewDir}/${packageDir}/${archiveName}")
  }
  
  task copyJars(type: Copy) {
-   from fileTree("$classes").include("**/*.jar").include("*.jar").files
-   into "$jalviewDir/$packageDir"
+   from fileTree(dir: classesDir, include: "**/*.jar").files
+   into "${jalviewDir}/${packageDir}"
  }
  
  // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
  task syncJars(type: Sync) {
-   from fileTree("$jalviewDir/$libDistDir").include("**/*.jar").include("*.jar").files
-   into "$jalviewDir/$packageDir"
+   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
+   into "${jalviewDir}/${packageDir}"
    preserve {
      include jar.archiveName
    }
  }
  
  task makeDist {
    group = "build"
    description = "Put all required libraries in dist"
    dependsOn cleanPackageDir
    dependsOn syncJars
    dependsOn jar
-   outputs.dir("$jalviewDir/$packageDir")
+   outputs.dir("${jalviewDir}/${packageDir}")
  }
  
  task cleanDist {
    dependsOn cleanPackageDir
    dependsOn cleanTest
@@@ -842,7 -964,7 +982,7 @@@ shadowJar 
    if (buildDist) {
      dependsOn makeDist
    }
-   from ("$jalviewDir/$libDistDir") {
+   from ("${jalviewDir}/${libDistDir}") {
      include("*.jar")
    }
    manifest {
    minimize()
  }
  
  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 makeDist
    }
  
-       // clean the getdown website and files dir before creating getdown folders
-       delete project.ext.getdownWebsiteDir
-       delete project.ext.getdownFilesDir
    def getdownWebsiteResourceFilenames = []
    def getdownTextString = ""
-   def getdownResourceDir = project.ext.getdownResourceDir
-   def getdownAppDir = project.ext.getdownAppDir
+   def getdownResourceDir = getdownResourceDir
    def getdownResourceFilenames = []
  
    doFirst {
+     // clean the getdown website and files dir before creating getdown folders
+     delete getdownWebsiteDir
+     delete getdownFilesDir
      copy {
        from buildProperties
-       rename(buildPropertiesFile, getdown_build_properties)
-       into project.ext.getdownAppDir
+       rename(build_properties_file, getdown_build_properties)
+       into getdownAppDir
      }
-     getdownWebsiteResourceFilenames += getdown_app_dir+"/"+getdown_build_properties
+     getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
  
      // go through properties looking for getdown_txt_...
      def props = project.properties.sort { it.key }
-       if (getdown_alt_java_min_version.length() > 0) {
-               props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
-       }
-       if (getdown_alt_java_max_version.length() > 0) {
-               props.put("getdown_txt_java_max_version", getdown_alt_java_max_version)
-       }
-       props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
-     props.put("getdown_txt_appbase", getdown_app_base)
+     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
+       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
+     }
+     if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
+       props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
+     }
+     if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
+       props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
+     }
+     props.put("getdown_txt_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"
+             def line = "${key} = ${v}\n"
              getdownTextString += line
            }
          } else {
                r = file(val)
              } else if (val.indexOf('/') > 0) {
                // relative path (relative to jalviewDir)
-               r = file( jalviewDir + '/' + val )
+               r = file( "${jalviewDir}/${val}" )
              }
              if (r.exists()) {
-               val = getdown_resource_dir + '/' + r.getName()
+               val = "${getdown_resource_dir}/" + r.getName()
                getdownWebsiteResourceFilenames += val
                getdownResourceFilenames += r.getPath()
              }
            }
            if (! prop.startsWith("getdown_txt_resource")) {
-             def line = prop.substring(12) + " = " + val + "\n"
+             def line = prop.substring(12) + " = ${val}\n"
              getdownTextString += line
            }
          }
      }
  
      getdownWebsiteResourceFilenames.each{ filename ->
-       getdownTextString += "resource = "+filename+"\n"
+       getdownTextString += "resource = ${filename}\n"
      }
      getdownResourceFilenames.each{ filename ->
        copy {
          from filename
-         into project.ext.getdownResourceDir
+         into getdownResourceDir
        }
      }
  
        }
      }
      codeFiles.sort().each{f ->
-       def line = "code = " + getdown_app_dir + '/' + f.getName() + "\n"
+       def name = f.getName()
+       def line = "code = ${getdownAppDistDir}/${name}\n"
        getdownTextString += line
        copy {
          from f.getPath()
-         into project.ext.getdownAppDir
+         into getdownAppDir
        }
      }
  
      // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
      /*
      if (JAVA_VERSION.equals("11")) {
-     def j11libFiles = fileTree(dir: "$jalviewDir/$j11libDir", include: ["*.jar"]).getFiles()
+     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
      j11libFiles.sort().each{f ->
-     def line = "code = " + getdown_j11lib_dir + '/' + f.getName() + "\n"
+     def name = f.getName()
+     def line = "code = ${getdown_j11lib_dir}/${name}\n"
      getdownTextString += line
      copy {
      from f.getPath()
-     into project.ext.getdownJ11libDir
+     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 = " + mainClass + "\n"
+     getdownTextString += "resource = ${getdown_launcher_new}\n"
+     getdownTextString += "class = ${mainClass}\n"
  
-     def getdown_txt = file(project.ext.getdownWebsiteDir + "/getdown.txt")
+     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
      getdown_txt.write(getdownTextString)
  
-     def launch_jvl = file(project.ext.getdownWebsiteDir + "/" + getdown_launch_jvl)
-     launch_jvl.write("appbase="+props.get("getdown_txt_appbase"))
+     def getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
+     def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
+     launchJvl.write("appbase="+props.get("getdown_txt_appbase"))
  
      copy {
        from getdownLauncher
        rename(file(getdownLauncher).getName(), getdown_launcher_new)
-       into project.ext.getdownWebsiteDir
+       into getdownWebsiteDir
      }
  
      copy {
        if (file(getdownLauncher).getName() != getdown_launcher) {
          rename(file(getdownLauncher).getName(), getdown_launcher)
        }
-       into project.ext.getdownWebsiteDir
+       into getdownWebsiteDir
      }
  
      if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
        copy {
          from getdown_txt
          from getdownLauncher
-         from getdownWebsiteDir+"/"+getdown_build_properties
+         from "${getdownWebsiteDir}/${getdown_build_properties}"
          if (file(getdownLauncher).getName() != getdown_launcher) {
            rename(file(getdownLauncher).getName(), getdown_launcher)
          }
  
      copy {
        from getdown_txt
-       from launch_jvl
+       from launchJvl
        from getdownLauncher
-       from getdownWebsiteDir+"/"+getdown_build_properties
+       from "${getdownWebsiteDir}/${getdown_build_properties}"
        if (file(getdownLauncher).getName() != getdown_launcher) {
          rename(file(getdownLauncher).getName(), getdown_launcher)
        }
      }
  
      copy {
-         from getdownResourceDir
-       into project.ext.getdownFilesDir + '/' + getdown_resource_dir
+       from getdownResourceDir
+       into "${getdownFilesDir}/${getdown_resource_dir}"
      }
    }
  
    if (buildDist) {
-     inputs.dir(jalviewDir + '/' + packageDir)
+     inputs.dir("${jalviewDir}/${packageDir}")
+   }
+   outputs.dir(getdownWebsiteDir)
+   outputs.dir(getdownFilesDir)
+ }
+ // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
+ task getdownDigestDir(type: JavaExec) {
+   def digestDirPropertyName = "DIGESTDIR"
+   description = "Digest a local dir (-P${digestDirPropertyName}=...) for getdown"
+   doFirst {
+     classpath = files(getdownLauncher)
+     def digestDir = findProperty(digestDirPropertyName)
+     if (digestDir == null) {
+       throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
+     }
+     args digestDir
    }
-   outputs.dir(project.ext.getdownWebsiteDir)
-   outputs.dir(project.ext.getdownFilesDir)
+   main = "com.threerings.getdown.tools.Digester"
  }
  
  task getdownDigest(type: JavaExec) {
    group = "distribution"
    description = "Digest the getdown website folder"
    dependsOn getdownWebsite
    doFirst {
-     classpath = files(getdownWebsiteDir + '/' + getdown_launcher)
+     classpath = files(getdownLauncher)
    }
    main = "com.threerings.getdown.tools.Digester"
-   args project.ext.getdownWebsiteDir
-   inputs.dir(project.ext.getdownWebsiteDir)
-   outputs.file(project.ext.getdownWebsiteDir + '/' + "digest2.txt")
+   args getdownWebsiteDir
+   inputs.dir(getdownWebsiteDir)
+   outputs.file("${getdownWebsiteDir}/digest2.txt")
  }
  
  task getdown() {
    group = "distribution"
    description = "Create the minimal and full getdown app folder for installers and website and create digest file"
    dependsOn getdownDigest
    doLast {
      if (reportRsyncCommand) {
-       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith("/")?"":"/")
-       def toDir = getdown_rsync_dest + "/" + getdownDir + (getdownDir.endsWith("/")?"":"/")
+       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
+       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
        println "LIKELY RSYNC COMMAND:"
        println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
        if (RUNRSYNC == "true") {
    }
  }
  
  clean {
-   delete project.ext.getdownWebsiteDir
-   delete project.ext.getdownFilesDir
+   doFirst {
+     delete getdownWebsiteDir
+     delete getdownFilesDir
+   }
  }
  
  install4j {
-   def install4jHomeDir = "/opt/install4j"
-   def hostname = "hostname".execute().text.trim()
-   if (hostname.equals("jv-bamboo")) {
+   if (file(install4jHomeDir).exists()) {
+     // good to go!
+   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
      install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
-   } else if (OperatingSystem.current().isMacOsX()) {
-     install4jHomeDir = '/Applications/install4j.app/Contents/Resources/app'
-     if (! file(install4jHomeDir).exists()) {
-       install4jHomeDir = System.getProperty("user.home")+install4jHomeDir
-     }
-   } else if (OperatingSystem.current().isLinux()) {
-     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
-   }
-   installDir = file(install4jHomeDir)
-   mediaTypes = Arrays.asList(install4jMediaTypes.split(","))
-   if (install4jFaster.equals("true")) {
-     faster = true
+   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
+     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
    }
+   installDir(file(install4jHomeDir))
+   mediaTypes = Arrays.asList(install4j_media_types.split(","))
  }
  
- def install4jConf
- def macosJavaVMDir
- def macosJavaVMTgz
- def windowsJavaVMDir
- def windowsJavaVMTgz
- def install4jDir = "$jalviewDir/$install4jResourceDir"
- def install4jConfFile = "jalview-installers-java"+JAVA_VERSION+".install4j"
- install4jConf = "$install4jDir/$install4jConfFile"
- task copyInstall4jTemplate(type: Copy) {
-   macosJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/macos-jre"+JAVA_VERSION+"/jre"
-   macosJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre"+JAVA_VERSION+".tar.gz"
-   windowsJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/windows-jre"+JAVA_VERSION+"/jre"
-   windowsJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre"+JAVA_VERSION+".tar.gz"
-   from (install4jDir) {
-     include install4jTemplate
-     rename (install4jTemplate, install4jConfFile)
-     filter(ReplaceTokens, beginToken: '', endToken: '', tokens: ['9999999999': JAVA_VERSION])
-     filter(ReplaceTokens, beginToken: '$$', endToken: '$$',
-     tokens: [
-     'JAVA_VERSION': JAVA_VERSION,
-     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
-     'VERSION': JALVIEW_VERSION,
-     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
-     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
-     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
-     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
-     'INSTALL4JINFOPLISTFILEASSOCIATIONS': install4jInfoPlistFileAssociations,
-     'COPYRIGHT_MESSAGE': install4jCopyrightMessage,
-     'MACOS_BUNDLE_ID': install4jMacOSBundleId,
-     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
-     'GETDOWN_DIST_DIR': getdown_app_dir,
-     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
-     'GETDOWN_INSTALL_DIR': getdown_install_dir
-     ]
-     )
-     if (OSX_KEYPASS=="") {
-       filter(ReplaceTokens, beginToken: 'codeSigning macEnabled="', endToken: '"', tokens: ['true':'codeSigning macEnabled="false"'])
-       filter(ReplaceTokens, beginToken: 'runPostProcessor="true" ',endToken: 'Processor', tokens: ['post':'runPostProcessor="false" postProcessor'])
+ task copyInstall4jTemplate {
+   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
+   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
+   inputs.file(install4jTemplateFile)
+   inputs.file(install4jFileAssociationsFile)
+   outputs.file(install4jConfFile)
+   doLast {
+     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
+     // turn off code signing if no OSX_KEYPASS
+     if (OSX_KEYPASS == "") {
+       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
+         codeSigning.'@macEnabled' = "false"
+       }
+       install4jConfigXml.'**'.windows.each { windows ->
+         windows.'@runPostProcessor' = "false"
+       }
+     }
+     // turn off checksum creation for LOCAL channel
+     def e = install4jConfigXml.application[0]
+     if (CHANNEL == "LOCAL") {
+       e.'@createChecksums' = "false"
+     } else {
+       e.'@createChecksums' = "true"
      }
+     // put file association actions where placeholder action is
+     def install4jFileAssociationsText = install4jFileAssociationsFile.text
+     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
+     install4jConfigXml.'**'.action.any { a ->
+       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
+         def parent = a.parent()
+         parent.remove(a)
+         fileAssociationActions.each { faa ->
+             parent.append(faa)
+         }
+         // don't need to continue in .any loop once replacements have been made
+         return true
+       }
+     }
+     // write install4j file
+     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
    }
-   into install4jDir
-   outputs.files(install4jConf)
+ }
  
-   doLast {
-     // include file associations in installer
-     def installerFileAssociationsXml = file("$install4jDir/$install4jInstallerFileAssociations").text
-     ant.replaceregexp(
-       byline: false,
-       flags: "s",
-       match: '<action name="EXTENSIONS_REPLACED_BY_GRADLE".*?</action>',
-       replace: installerFileAssociationsXml,
-       file: install4jConf
-     )
-     /*
-     // include uninstaller applescript app files in dmg
-     def installerDMGUninstallerXml = file("$install4jDir/$install4jDMGUninstallerAppFiles").text
-     ant.replaceregexp(
-     byline: false,
-     flags: "s",
-     match: '<file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file=.*?>',
-     replace: installerDMGUninstallerXml,
-     file: install4jConf
-     )
-      */
+ clean {
+   doFirst {
+     delete install4jConfFile
    }
  }
  
  task installers(type: com.install4j.gradle.Install4jTask) {
    group = "distribution"
    description = "Create the install4j installers"
+   dependsOn setGitVals
    dependsOn getdown
    dependsOn copyInstall4jTemplate
-   projectFile = file(install4jConf)
-   println("Using projectFile "+projectFile)
-   variables = [majorVersion: version.substring(2, 11), build: 001, OSX_KEYSTORE: OSX_KEYSTORE, JSIGN_SH: JSIGN_SH]
-   destination = "$jalviewDir/$install4jBuildDir/$JAVA_VERSION"
+   projectFile = install4jConfFile
+   // create an md5 for the input files to use as version for install4j conf file
+   def digest = MessageDigest.getInstance("MD5")
+   digest.update(
+     (file("${install4jDir}/${install4j_template}").text + 
+     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
+     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
+   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
+   if (filesMd5.length() >= 8) {
+     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': getdown_txt_title,
+     'JALVIEW_DIR': "../..",
+     'OSX_KEYSTORE': OSX_KEYSTORE,
+     'JSIGN_SH': JSIGN_SH,
+     'JRE_DIR': getdown_app_dir_java,
+     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
+     'JALVIEW_VERSION': JALVIEW_VERSION,
+     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
+     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
+     '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,
+     'MACOS_BUNDLE_ID': install4j_macOS_bundle_id,
+     'INSTALLER_NAME': install4j_installer_name,
+     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
+     'GETDOWN_WEBSITE_DIR': getdown_website_dir,
+     'GETDOWN_FILES_DIR': getdown_files_dir,
+     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
+     'GETDOWN_DIST_DIR': getdownAppDistDir,
+     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
+     'GETDOWN_INSTALL_DIR': getdown_install_dir,
+     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
+     'BUILD_DIR': install4jBuildDir,
+   ]
+   destination = "${jalviewDir}/${install4jBuildDir}"
    buildSelected = true
  
+   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
+     faster = true
+     disableSigning = true
+   }
    if (OSX_KEYPASS) {
-     macKeystorePassword=OSX_KEYPASS
+     macKeystorePassword = OSX_KEYPASS
+   }
  
+   doFirst {
+     println("Using projectFile "+projectFile)
    }
  
-   inputs.dir(project.ext.getdownWebsiteDir)
-   inputs.file(install4jConf)
+   inputs.dir(getdownWebsiteDir)
+   inputs.file(install4jConfFile)
+   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
    inputs.dir(macosJavaVMDir)
    inputs.dir(windowsJavaVMDir)
-   outputs.dir("$jalviewDir/$install4jBuildDir/$JAVA_VERSION")
+   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
  }
  
- clean {
-   delete install4jConf
- }
  
- task sourceDist (type: Tar) {
+ task sourceDist(type: Tar) {
    
    def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
-   def outputFileName = project.name + "_" + VERSION_UNDERSCORES + ".tar.gz"
+   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
    // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
    try {
      archiveFileName = outputFileName
    from(jalviewDir) {
      exclude (EXCLUDE_FILES)
      include (PROCESS_FILES)
-     filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION,'Year-Rel': getDate("yyyy")])
+     filter(ReplaceTokens,
+       beginToken: '$$',
+       endToken: '$$',
+       tokens: [
+         'Version-Rel': JALVIEW_VERSION,
+         'Year-Rel': getDate("yyyy")
+       ]
+     )
    }
    from(jalviewDir) {
      exclude (EXCLUDE_FILES)
  //  }
  }
  
- task helppages  {
+ task helppages {
    dependsOn copyHelp
    dependsOn pubhtmlhelp
    
-   inputs.dir("$classes/$helpDir")
-   outputs.dir("$helpOutputDir")
+   inputs.dir("${classesDir}/${help_dir}")
+   outputs.dir("${buildDir}/distributions/${help_dir}")
  }
+ // LARGE AMOUNT OF JALVIEWJS STUFF DELETED HERE
+ task eclipseAutoBuildTask {}
+ task eclipseSynchronizationTask {}
diff --combined doc/building.md
@@@ -486,6 -486,11 +486,11 @@@ There are several values of `CHANNEL` t
      - `appbase` as `http://www.jalview.org/getdown/SCRATCH-NAME/JAVA_VERSION`
      - application subdir as `alt`
      - Getdown launcher cannot use a `file://` scheme appbase.
+ * `TEST-LOCAL`:  Like `SCRATCH` but with a specific `test-local` channel name and a local filesystem appbase.  This is meant for testing an over-the-air update on the local filesystem.  An extra property `LOCALDIR` must be given (e.g. `-PLOCALDIR=/home/user/tmp/test`)
+   It will set
+     - `appbase` as `file://${LOCALDIR}`
+     - application subdir as `alt`
+     - Getdown launcher can use a `file://` scheme appbase.
  * `TEST-RELEASE`:  Like `SCRATCH` but with a specific `test-release` channel name.  This won't become live until the actual getdown artefact is synced to the web server.  This is meant for testing an over-the-air update without interfering with the live `release` or `develop` channels.
    It will set
      - `appbase` as `http://www.jalview.org/getdown/test-release/JAVA_VERSION`
@@@ -541,35 -546,5 +546,35 @@@ perl -n -e 'm/^\s*<(\w+)[^>]*\bmediaFil
  in the `jalview` root folder.
  
  
 +## Enabling Code Coverage with OpenClover
 +
 +Bytecode instrumentation tasks are enabled by specifying 'true' (or just a non-whitespace non-numeric word) in the 'clover' property. This adds the 'openclover' plugin to the build script's classpath, making it possible to track code execution during test which can be viewed as an HTML report published at build/reports/clover/index.html.
 +
 +```gradle -Pclover=true test cloverReport```
 +
 +#### Troubleshooting report generation
 +
 +The build forks a new JVM process to run the clover report generation tools (both XML and HTML reports are generated by default). The following properties can be used to specify additional options or adjust JVM memory settings. Default values for these options are:
 +
 +##### JVM Memory settings - increase if out of memory errors are reported
 +
 +```cloverReportJVMHeap = 2g```
 +
 +##### -Dfile.encoding=UTF-8 is an essential parameters for report generation. Add additional ones separated by a space.
 +
 +```cloverReportJVMArgs = -Dfile.encoding=UTF-8```
 +
 +##### Add -v to debug velocity html generation errors, or -d to track more detailed issues with the coverage database
 +
 +```cloverReportHTMLOptions = ```
 +
 +##### -v for verbose, -d for debug level messages (as above)
 +
 +```cloverReportXMLOptions = ```
 +
 +
 +_Note_ do not forget to include the -Dfile.encoding=UTF-8 option: this is essential for some platforms in order for Clover to correctly parse some Jalview source files that contain characters that are UTF-8 encoded. 
 +
 +
  ---
  [Jalview Development Team](mailto:help@jalview.org)
diff --combined gradle.properties
@@@ -2,6 -2,13 +2,13 @@@ jalviewDir = 
  
  #JAVA_VERSION = 1.8
  JAVA_VERSION = 11
+ source_dir = src
+ #source_dir = utils/jalviewjs/test/src
+ test_source_dir = test
+ #test_source_dir = utils/jalviewjs/test/test
  JALVIEW_VERSION = DEVELOPMENT
  INSTALLATION = Source
  jalview_keystore = keys/.keystore
@@@ -18,32 -25,25 +25,29 @@@ testngGroups = Functiona
  
  j8libDir = j8lib
  j11libDir = j11lib
- resourceDir = resources
- helpParentDir = help
- helpDir = help
- helpOutputDir = build/distributions/help
+ resource_dir = resources
+ help_parent_dir = help
+ help_dir = help
  docDir = doc
- sourceDir = src
  schemaDir = schemas
- classesDir = classes
+ classes_dir = classes
  examplesDir = examples
  clover = false
  use_clover = false
 +cloverReportJVMHeap = 2g
 +cloverReportJVMArgs = -Dfile.encoding=UTF-8
 +cloverReportHTMLOptions = 
 +cloverReportXMLOptions =
  cloverClassesDir = clover-classes
  cloverSourcesInstrDir = sources-instr
  packageDir = dist
  ARCHIVEDIR =
  outputJar = jalview.jar
  
  testOutputDir = tests
  utilsDir = utils
  
- buildPropertiesFile = .build_properties
+ build_properties_file = .build_properties
  application_codebase = *.jalview.org
  mainClass = jalview.bin.Jalview
  shadowJarMainClass = jalview.bin.Launcher
@@@ -61,12 -61,14 +65,14 @@@ getdown_launcher = getdown-launcher.ja
  getdown_launcher_local = getdown-launcher-local.jar
  getdown_launcher_new = getdown-launcher-new.jar
  getdown_core = getdown/lib/getdown-core.jar
- getdown_launch_jvl = channel_launch.jvl
  getdown_build_properties = build_properties
- getdown_txt_title = Jalview
+ getdown_launch_jvl_name = channel_launch
  getdown_txt_allow_offline = true
  getdown_txt_max_concurrent_downloads = 10
- getdown_txt_jalview.jvmmempc = 90
+ # now got better defaults when not set
+ #getdown_txt_jalview.jvmmempc = 90
+ # now got better defaults when not set
+ #getdown_txt_jalview.jvmmemmax = 32G
  getdown_txt_multi_jvmarg = -Dgetdownappdir=%APPDIR%
  getdown_txt_strict_comments = true
  getdown_txt_title = Jalview
@@@ -89,52 -91,46 +95,46 @@@ getdown_txt_ui.progress_text = 00000
  getdown_txt_ui.status = 20, 380, 600, 58
  getdown_txt_ui.status_text = 000066
  #getdown_txt_ui.text_shadow = FFFFFF
- getdown_txt_ui.install_error = http://www.jalview.org/faq/getdownerror
+ getdown_txt_ui.install_error = https://www.jalview.org/faq/getdownerror
  getdown_txt_ui.mac_dock_icon = resources/images/jalview_logos.ico
  getdown_alt_java8_min_version  = 01080000
  getdown_alt_java8_max_version  = 01089999
  getdown_alt_java11_min_version = 11000000
  getdown_alt_java11_max_version =
- getdown_alt_java11_txt_multi_java_location = [windows-amd64] /getdown/jre/windows-jre11.jar,[linux-amd64] /getdown/jre/linux-jre11.tgz,[mac os x] /getdown/jre/macos-jre11.tgz
- getdown_alt_java8_txt_multi_java_location = [windows-amd64] /getdown/jre/windows-jre1.8.tgz,[linux-amd64] /getdown/jre/linux-jre1.8.tgz,[mac os x] /getdown/jre/macos-jre1.8.tgz
- JRE_installs = /Users/bsoares/Java/installs
- Windows_JRE8 = OpenJDK8U-jdk_x64_windows_hotspot_8u202b08/jdk8u202-b08
- Mac_JRE8 = OpenJDK8U-jre_x64_mac_hotspot_8u202b08/jdk8u202-b08-jre/Contents/Home
- Linux_JRE8 = OpenJDK8U-jre_x64_linux_hotspot_8u202b08/jdk8u202-b08-jre
- Windows_JRE11 = OpenJDK11-jre_x64_windows_hotspot_11_28/jdk-11+28-jre
- Mac_JRE11 = OpenJDK11-jre_x64_mac_hotspot_11_28/jdk-11+28-jre/Contents/Home
- Linux_JRE11 = OpenJDK11-jre_x64_linux_hotspot_11_28/jdk-11+28-jre
+ #getdown_alt_java11_txt_multi_java_location = [windows-amd64] /getdown/jre/windows-jre11.jar,[linux-amd64] /getdown/jre/linux-jre11.tgz,[mac os x] /getdown/jre/macos-jre11.tgz
+ #getdown_alt_java8_txt_multi_java_location = [windows-amd64] /getdown/jre/windows-jre1.8.tgz,[linux-amd64] /getdown/jre/linux-jre1.8.tgz,[mac os x] /getdown/jre/macos-jre1.8.tgz
+ jre_installs_dir = ~/buildtools/jre
  
  j8libDir = j8lib
  j11libDir = j11lib
  j11modDir = j11mod
  j11modules = com.sun.istack.runtime,com.sun.xml.bind,com.sun.xml.fastinfoset,com.sun.xml.streambuffer,com.sun.xml.txw2,com.sun.xml.ws.policy,java.activation,java.annotation,java.base,java.compiler,java.datatransfer,java.desktop,java.logging,java.management,java.management.rmi,java.naming,java.prefs,java.rmi,java.scripting,java.security.sasl,java.sql,java.xml,java.xml.bind,java.xml.soap,java.xml.ws,javax.jws,jdk.httpserver,jdk.jsobject,jdk.unsupported,jdk.xml.dom,org.jvnet.mimepull,org.jvnet.staxex,javax.servlet.api,java.ws.rs
  
- install4jCopyrightMessage = ...
- install4jMacOSBundleId = org.jalview.jalview-desktop
- install4jResourceDir = utils/install4j
- install4jTemplate = install4j_template.install4j
- install4jInfoPlistFileAssociations = file_associations_auto-Info_plist.xml
- install4jInstallerFileAssociations = file_associations_auto-install4j.xml
- install4jDMGUninstallerAppFiles = uninstall_old_jalview_files.xml
- install4jBuildDir = build/install4j
- install4jMediaTypes = windows,macosArchive,linuxRPM,linuxDeb,unixArchive,unixInstaller
- install4jFaster = false
+ install4j_home_dir = ~/buildtools/install4j8
+ install4j_copyright_message = ...
+ install4j_macOS_bundle_id = org.jalview.jalview-desktop
+ install4j_utils_dir = utils/install4j
+ install4j_template = install4j8_template.install4j
+ install4j_info_plist_file_associations = file_associations_auto-Info_plist.xml
+ install4j_installer_file_associations = file_associations_auto-install4j8.xml
+ #install4j_DMG_uninstaller_app_files = uninstall_old_jalview_files.xml
+ install4j_build_dir = build/install4j
+ install4j_media_types = windows,macosArchive,linuxRPM,linuxDeb,unixArchive,unixInstaller
+ install4j_faster = false
+ install4j_installer_name = Jalview Installer
  
  OSX_KEYSTORE =
  OSX_KEYPASS =
  JSIGN_SH = echo
  
- eclipse_extra_jdt_prefs_file = .settings/org.eclipse.jdt.core.jalview.prefs
  pandoc_exec = /usr/local/bin/pandoc,/usr/bin/pandoc
  dev = false
  
  CHANNEL=LOCAL
- getdown_channel_base = http://www.jalview.org/getdown
- getdown_channel_name = SCRATCH-DEFAULT
+ getdown_channel_base = https://www.jalview.org/getdown
  getdown_app_dir_release = release
  getdown_app_dir_alt = alt
+ getdown_app_dir_java = jre
  getdown_install_dir = install
  getdown_rsync_dest = /Volumes/jalview/docroot/getdown
  reportRsyncCommand =
@@@ -143,4 -139,72 +143,72 @@@ RUNRSYNC=fals
  bamboo_channelbase = https://builds.jalview.org/browse
  bamboo_planKey = 
  bamboo_getdown_channel_suffix = /latest/artifact/shared/getdown-channel
-  
+ eclipse_extra_jdt_prefs_file = .settings/org.eclipse.jdt.core.jalview.prefs
+ eclipse_project_name = jalview
+ eclipse_bin_dir = bin
+ eclipse_debug = false
+ # for developing in Eclipse as IDE, set this to automatically copy current swingjs/net.sf.j2s.core.jar to your dropins dir
+ jalviewjs_eclipseIDE_auto_copy_j2s_plugin = false
+ # Override this in a local.properties file
+ jalviewjs_eclipse_root = ~/buildtools/eclipse/jee-2019-09
+ jalviewjs_eclipse_dropins_dir = utils/jalviewjs/eclipse/dropins
+ jalviewjs_swingjs_zip = swingjs/SwingJS-site.zip
+ jalviewjs_j2s_plugin = swingjs/net.sf.j2s.core.jar
+ jalviewjs_libjs_dir = utils/jalviewjs/libjs
+ jalviewjs_site_resource_dir = utils/jalviewjs/site-resources
+ jalviewjs_classlists_dir = utils/jalviewjs/classlists
+ jalviewjs_classlist_jalview = utils/jalviewjs/_j2sclasslist.txt
+ jalviewjs_j2s_settings = .j2s
+ #jalviewjs_eclipse_workspace = ~/tmp/eclipse-workspace
+ # these 3 files/dirs found in build/jalviewjs
+ jalviewjs_eclipse_tmp_dropins_dir = eclipse_dropins
+ jalviewjs_eclipse_workspace_location_file = eclipse_workspace_location
+ jalviewjs_site_dir = site
+ # these dirs are subdirs of the site dir
+ jalviewjs_swingjs_subdir = swingjs
+ jalviewjs_j2s_subdir = swingjs/j2s
+ jalviewjs_js_subdir = swingjs/js
+ #jalviewjs_eclipseBuildArg = build
+ jalviewjs_eclipse_build_arg = cleanBuild
+ jalviewjs_server_port = 9001
+ jalviewjs_server_wait = 30
+ jalviewjs_server_resource = /jalview_bin_Jalview.html
+ jalviewjs_core_name = _jalview
+ jalviewjs_name = JalviewJS
+ jalviewjs_core_key = core
+ #jalviewjs_core_key = preloadCore
+ j2s.compiler.status = enable
+ #j2s.site.directory = null ## site defined from buildDir+'/jalviewjs/'+jalviewjs_site_dir
+ #j2s.log.methods.declared = j2s_methods_declared.log
+ #j2s.log.methods.called = j2s_methods_called.log
+ #a semicolon-separated list of package-level file paths to be excluded
+ j2s.excluded.paths = test;testng;util
+ #j2s.include.tests = true
+ #j2s.testing = null
+ #j2s.compiler.nonqualified.packages = null
+ #j2s.compiler.nonqualified.classes = null
+ #j2s.compiler.mode = debug
+ #a semicolon-separated list of package (foo.) or class (foo.bar) replacements to be made 
+ j2s.class.replacements = org.apache.log4j.->jalview.javascript.log4j.
+ j2s.template.html = utils/jalviewjs/template.html
+ j2s_coretemplate_html = utils/jalviewjs/coretemplate.html
+ #output file name for logging methods declared - delete the file to regenerate a listing 
+ #j2s.prop.j2s.log.methods.declared=swingjs/methodsDeclared_csv
+ #output file name for logging methods called - delete the file to regenerate a listing
+ #j2s.prop.j2s.log.methods_called=swingjs/methodsCalled_csv
+ #if set, every instance of methods called will be logged
+ #otherwise, only the first call to a method will be logged 
+ #output will be comma-separated: called method,caller class 
+ #j2s.prop.j2s.log.all.calls=true
+ jalviewjs_j2s_transpile_stdout = j2s-transpile.out
+ #jalviewjs_j2s_stderr = j2s-transpile.err # all going into out
+ jalviewjs_j2s_to_console = true
+ jalviewjs_j2s_closure_stdout = j2s-closure.out
+ testp=gradle.properties