JAL-4059 JAL-4395 Updated closure-compiler. Added local_eclipse.properties for buildship
[jalview.git] / build.gradle
index fb1a83b..df1f5cd 100644 (file)
@@ -14,6 +14,7 @@ import java.util.concurrent.Executors
 import java.util.concurrent.Future
 import java.util.concurrent.ScheduledExecutorService
 import java.util.concurrent.TimeUnit
+import java.nio.file.Path
 import groovy.transform.ExternalizeMethods
 import groovy.util.XmlParser
 import groovy.xml.XmlUtil
@@ -71,39 +72,48 @@ def string(Object o) {
   return o == null ? "" : o.toString()
 }
 
-def overrideProperties(String propsFileName, boolean output = false) {
-  if (propsFileName == null) {
-    return
-  }
+def Properties readPropertiesFile(String propsFileName) {
+  def p = null
   def propsFile = file(propsFileName)
   if (propsFile != null && propsFile.exists()) {
     println("Using properties from file '${propsFileName}'")
     try {
-      def p = new Properties()
+      p = new Properties()
       def localPropsFIS = new FileInputStream(propsFile)
       p.load(localPropsFIS)
       localPropsFIS.close()
-      p.each {
-        key, val -> 
-          def oldval
-          if (project.hasProperty(key)) {
-            oldval = project.findProperty(key)
-            project.setProperty(key, val)
-            if (output) {
-              println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
-            }
-          } else {
-            ext.setProperty(key, val)
-            if (output) {
-              println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
-            }
-          }
-      }
     } catch (Exception e) {
-      println("Exception reading local.properties")
+      println("Exception reading properties file '${propsFileName}'")
       e.printStackTrace()
     }
   }
+  return p
+}
+
+def overrideProperties(String propsFileName, boolean output = false) {
+  if (propsFileName == null) {
+    return
+  }
+  def propsFile = file(propsFileName)
+  if (propsFile != null && propsFile.exists()) {
+    println("Using properties from file '${propsFileName}'")
+    def p = readPropertiesFile(propsFileName)
+    p.each { key, val -> 
+      def oldval
+      if (project.hasProperty(key)) {
+        oldval = project.findProperty(key)
+        project.setProperty(key, val)
+        if (output) {
+          println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
+        }
+      } else {
+        ext.setProperty(key, val)
+        if (output) {
+          println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
+        }
+      }
+    }
+  }
 }
 
 ext {
@@ -122,12 +132,14 @@ ext {
   channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
   channelGradleProperties = string("${channelDir}/channel_gradle.properties")
   channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
+  localProperties = "local.properties"
+  localEclipseProperties = "local_eclipse.properties"
   overrideProperties(channelGradleProperties, false)
   // local build environment properties
   // can be "projectDir/local.properties"
-  overrideProperties("${projectDir}/local.properties", true)
+  overrideProperties("${projectDir}/${localProperties}", true)
   // or "../projectDir_local.properties"
-  overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
+  overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_${localProperties}", true)
 
   ////  
   // Import releaseProps from the RELEASE file
@@ -559,12 +571,12 @@ ext {
   if (IN_ECLIPSE) {
     jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
   } else {
-    jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
+    jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/sitejs")
   }
-  jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
-  jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
-  jalviewjsTransferSiteMergeDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_merge")
-  jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
+  jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/lib")
+  jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/swingjs")
+  jalviewjsTransferSiteMergeDir = string("${jalviewjsBuildDir}/merge/${jalviewjs_site_dir}")
+  jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/core")
   jalviewjsJalviewCoreHtmlFile = string("")
   jalviewjsJalviewCoreName = string(jalviewjs_core_name)
   jalviewjsCoreClasslists = []
@@ -575,9 +587,12 @@ ext {
   jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
   jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
 
+  closureCompilerJar = "${jalviewDir}/${jalviewjs_closure_compiler}"
+    
   eclipseWorkspace = null
   eclipseBinary = string("")
   eclipseVersion = string("")
+  eclipseProductVersion = string("")
   eclipseDebug = false
 
   jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
@@ -780,6 +795,20 @@ eclipse {
             }
           }
         }
+        // local eclipse settings
+        def localEclipsePropertiesFile = file("${jalviewDirAbsolutePath}/${localEclipseProperties}")
+        if (localEclipsePropertiesFile.exists()) {
+          def eclipse_prefs = new Properties()
+          def ins2 = new FileInputStream(localEclipsePropertiesFile)
+          println("Loading Eclipse Preferences from '${localEclipsePropertiesFile}'")
+          eclipse_prefs.load(ins2)
+          ins2.close()
+          eclipse_prefs.forEach { t, v ->
+            props.putAt(t, v)
+          }
+        } else {
+          println("No local Eclipse Preferences file '${localEclipsePropertiesFile}'")
+        }
       }
     }
 
@@ -2151,6 +2180,16 @@ task cleanDist {
 }
 
 
+task launcherJar(type: Jar) {
+  manifest {
+      attributes (
+        "Main-Class": shadow_jar_main_class,
+        "Implementation-Version": JALVIEW_VERSION,
+        "Application-Name": applicationName
+      )
+  }
+}
+
 shadowJar {
   group = "distribution"
   description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
@@ -2164,6 +2203,10 @@ shadowJar {
   from groovyJars
   from otherJars
 
+  manifest {
+    // shadowJar manifest must inheritFrom another Jar task.  Can't set attributes here.
+    inheritFrom(project.tasks.launcherJar.manifest)
+  }
   // we need to include the groovy-swing Include-Package for it to run in the shadowJar
   doFirst {
     def jarFileManifests = []
@@ -2173,9 +2216,7 @@ shadowJar {
         jarFileManifests += mf
       }
     }
-
     manifest {
-      attributes "Implementation-Version": JALVIEW_VERSION, "Application-Name": applicationName
       from (jarFileManifests) {
         eachEntry { details ->
           if (!details.key.equals("Import-Package")) {
@@ -2188,6 +2229,7 @@ shadowJar {
 
   duplicatesStrategy "INCLUDE"
 
+  // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
   mainClassName = shadow_jar_main_class
   mergeServiceFiles()
   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
@@ -2241,9 +2283,9 @@ task getdownImages() {
   dependsOn getdownImagesProcess
 }
 
-task getdownWebsite() {
+task getdownWebsiteBuild() {
   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"
+  description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer. No digest is created."
 
   dependsOn getdownImages
   if (buildDist) {
@@ -2355,7 +2397,7 @@ task getdownWebsite() {
           from s
           into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
         }
-        getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}"
+        getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
       }
     }
 
@@ -2497,7 +2539,9 @@ task getdownDigestDir(type: JavaExec) {
 task getdownDigest(type: JavaExec) {
   group = "distribution"
   description = "Digest the getdown website folder"
-  dependsOn getdownWebsite
+
+  dependsOn getdownWebsiteBuild
+
   doFirst {
     classpath = files(getdownLauncher)
   }
@@ -2530,12 +2574,19 @@ task getdown() {
   }
 }
 
+task getdownWebsite {
+  group = "distribution"
+  description = "A task to create the whole getdown channel website dir including digest file"
+
+  dependsOn getdownWebsiteBuild
+  dependsOn getdownDigest
+}
 
 task getdownArchiveBuild() {
   group = "distribution"
   description = "Put files in the archive dir to go on the website"
 
-  dependsOn getdownWebsite
+  dependsOn getdownWebsiteBuild
 
   def v = "v${JALVIEW_VERSION_UNDERSCORES}"
   def vDir = "${getdownArchiveDir}/${v}"
@@ -3225,12 +3276,14 @@ task jalviewjsEnableAltFileProperty(type: WriteProperties) {
 task jalviewjsSetEclipseWorkspace {
   def propKey = "jalviewjs_eclipse_workspace"
   def propVal = null
+  // see if jalviewjs_eclipse_workspace is set by a property
   if (project.hasProperty(propKey)) {
     propVal = project.getProperty(propKey)
     if (propVal.startsWith("~/")) {
       propVal = System.getProperty("user.home") + propVal.substring(1)
     }
   }
+  // else look for an existing build/jalviewjs/eclipse_workspace_location file
   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
   def propsFile = file(propsFileName)
   def eclipseWsDir = propVal
@@ -3279,13 +3332,14 @@ task jalviewjsSetEclipseWorkspace {
   }
 
   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
-  outputs.file(propsFileName)
-  outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
+  //outputs.file(propsFileName) // don't want this to be deleted because of falsely "stale" task
+  outputs.upToDateWhen { eclipseWorkspace.exists() && (propsFile.exists() || !writeProps) }
 }
 
 
 task jalviewjsEclipsePaths {
-  def eclipseProduct
+  def eclipseProductFile
+  def eclipseSetupLog
 
   def eclipseRoot = jalviewjs_eclipse_root
   if (eclipseRoot.startsWith("~/")) {
@@ -3294,32 +3348,49 @@ task jalviewjsEclipsePaths {
   if (OperatingSystem.current().isMacOsX()) {
     eclipseRoot += "/Eclipse.app"
     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
-    eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
+    eclipseProductFile = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
+    eclipseSetupLog = "${eclipseRoot}/Contents/Eclipse/configuration/org.eclipse.oomph.setup/setup.log"
   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
       eclipseRoot += "/eclipse"
     }
     eclipseBinary = "${eclipseRoot}/eclipse.exe"
-    eclipseProduct = "${eclipseRoot}/.eclipseproduct"
+    eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
+    eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
   } else { // linux or unix
     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
       eclipseRoot += "/eclipse"
-println("eclipseDir exists")
     }
     eclipseBinary = "${eclipseRoot}/eclipse"
-    eclipseProduct = "${eclipseRoot}/.eclipseproduct"
+    eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
+    eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
   }
 
-  eclipseVersion = "4.13" // default
+  eclipseVersion = "unknown" // default
   def assumedVersion = true
-  if (file(eclipseProduct).exists()) {
-    def fis = new FileInputStream(eclipseProduct)
+  if (file(eclipseProductFile).exists()) {
+    def fis = new FileInputStream(eclipseProductFile)
     def props = new Properties()
     props.load(fis)
     eclipseVersion = props.getProperty("version")
     fis.close()
     assumedVersion = false
   }
+  if (file(eclipseSetupLog).exists()) {
+    def productRegex = /(?m)^\[[^\]]+\]\s+Product\s+(org\.eclipse.\S*)/
+    int lineCount = 0
+    file(eclipseSetupLog).eachLine { String line ->
+      def matcher = line =~ productRegex
+      if (matcher.size() > 0) {
+        eclipseProductVersion = matcher[0][1]
+        return true
+      }
+      if (lineCount >= 100) {
+        return true
+      }
+      lineCount++
+    }
+  }
   
   def propKey = "eclipse_debug"
   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
@@ -3335,6 +3406,9 @@ println("eclipseDir exists")
 
     if (!assumedVersion) {
       println("ECLIPSE VERSION=${eclipseVersion}")
+      if (eclipseProductVersion.length() != 0) {
+        println("ECLIPSE PRODUCT=${eclipseProductVersion}")
+      }
     }
   }
 }
@@ -3394,55 +3468,37 @@ jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
 */
 
 
-task jalviewjsTransferUnzipSwingJs {
-  def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
+task jalviewjsTransferUnzipSwingJs(type: Copy) {
+  def swingJsZipFile = "${jalviewDir}/${jalviewjs_swingjs_zip}"
+  from zipTree( "${jalviewDir}/${jalviewjs_swingjs_zip}" )
+  into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
 
-  doLast {
-    copy {
-      from zipTree(file_zip)
-      into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
-    }
-  }
-
-  inputs.file file_zip
-  outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
+  inputs.file swingJsZipFile
 }
 
 
-task jalviewjsTransferUnzipLib {
+task jalviewjsTransferUnzipLib(type: Copy) {
   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip").sort()
 
-  doLast {
-    zipFiles.each { file_zip -> 
-      copy {
-        from zipTree(file_zip)
-        into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
-
-        // The following replace() is needed due to a mismatch in Jmol calls to
-        // colorPtToFFRGB$javajs_util_T3d when only colorPtToFFRGB$javajs_util_T3 is defined
-        // in the SwingJS.zip (github or the one distributed with JSmol)
-        if (file_zip.getName().equals("Jmol-SwingJS.zip")) {
-          filter { line ->
-            def l = ""
-            while(!line.equals(l)) {
-              line = line.replace('colorPtToFFRGB$javajs_util_T3d', 'colorPtToFFRGB$javajs_util_T3')
-              l = line
-            }
-            return line
-          }
+  zipFiles.each { file_zip ->
+    from zipTree(file_zip)
+
+    // The following replace() is needed due to a mismatch in Jmol calls to
+    // colorPtToFFRGB$javajs_util_T3d when only colorPtToFFRGB$javajs_util_T3 is defined
+    // in the SwingJS.zip (github or the one distributed with JSmol)
+    if (file_zip.getName().startsWith("Jmol-SwingJS")) {
+      filter { line ->
+        def l = ""
+        while(!line.equals(l)) {
+          line = line.replace('colorPtToFFRGB$javajs_util_T3d', 'colorPtToFFRGB$javajs_util_T3')
+          l = line
         }
+        return line
       }
     }
-  }
-
-  inputs.files zipFiles
-  outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
-}
 
-
-task jalviewjsTransferUnzipAllLibs {
-  dependsOn jalviewjsTransferUnzipLib
-  dependsOn jalviewjsTransferUnzipSwingJs
+  }
+  into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
 }
 
 
@@ -3471,7 +3527,6 @@ task jalviewjsCreateJ2sSettings(type: WriteProperties) {
 
   if (! IN_ECLIPSE) {
     inputs.properties(jalviewjsJ2sProps)
-    outputs.file(jalviewjsJ2sAltSettingsFileName)
   }
 }
 
@@ -3483,46 +3538,81 @@ task jalviewjsEclipseSetup {
 }
 
 
-task jalviewjsSyncAllLibs (type: Sync) {
-  dependsOn jalviewjsTransferUnzipAllLibs
-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
-  inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
+task jalviewjsSyncLibs (type: Sync) {
+  dependsOn jalviewjsTransferUnzipLib
+  dependsOn jalviewjsTransferUnzipSwingJs
+
+  def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteLibDir}")
+  def inputFiles = fileTree(dir: inputDir)
+  def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
+
+  from inputFiles
+  into outputDir
+  def outputFiles = []
+  inputFiles.each{ file ->
+    def rfile = inputDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputDir}/${rfile}")
+    outputFiles += "${ofile}"
+  }
+  preserve {
+    include "**/*"
+  }
+
+  duplicatesStrategy "EXCLUDE"
+
+  outputs.files outputFiles
+  inputs.files inputFiles
+}
+
+task jalviewjsSyncSwingJS (type: Sync) {
+  dependsOn jalviewjsTransferUnzipSwingJs
+  mustRunAfter jalviewjsSyncLibs
+
+  def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
+  def inputFiles = fileTree(dir: inputDir)
   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
 
   from inputFiles
   into outputDir
   def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
+  inputFiles.each{ file ->
+    def rfile = inputDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputDir}/${rfile}")
+    outputFiles += "${ofile}"
   }
   preserve {
-    include "**"
+    include "**/*"
   }
 
-  // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
   duplicatesStrategy "INCLUDE"
 
   outputs.files outputFiles
   inputs.files inputFiles
 }
 
+task jalviewjsSyncAllLibs {
+  dependsOn jalviewjsSyncLibs
+  dependsOn jalviewjsSyncSwingJS
+}
+
 
 task jalviewjsSyncResources (type: Sync) {
   dependsOn buildResources
 
-  def inputFiles = fileTree(dir: resourcesBuildDir)
+  def inputDir = file(resourcesBuildDir)
+  def inputFiles = fileTree(dir: inputDir)
   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
 
   from inputFiles
   into outputDir
   def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
+  inputFiles.each{ file ->
+    def rfile = inputDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputDir}/${rfile}")
+    outputFiles += "${ofile}"
   }
   preserve {
-    include "**"
+    include "**/*"
   }
   outputs.files outputFiles
   inputs.files inputFiles
@@ -3530,19 +3620,22 @@ task jalviewjsSyncResources (type: Sync) {
 
 
 task jalviewjsSyncSiteResources (type: Sync) {
-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
+  def inputDir = file("${jalviewDir}/${jalviewjs_site_resource_dir}")
+  def inputFiles = fileTree(dir: inputDir)
   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
 
   from inputFiles
   into outputDir
   def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
+  inputFiles.each{ file ->
+    def rfile = inputDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputDir}/${rfile}")
+    outputFiles += "${ofile}"
   }
   preserve {
-    include "**"
+    include "**/*"
   }
+
   outputs.files outputFiles
   inputs.files inputFiles
 }
@@ -3550,19 +3643,24 @@ task jalviewjsSyncSiteResources (type: Sync) {
 
 task jalviewjsSyncBuildProperties (type: Sync) {
   dependsOn createBuildProperties
-  def inputFiles = [file(buildProperties)]
+
+  def f = file(buildProperties)
+  def inputDir = f.getParentFile()
+  def inputFiles = [f]
   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
 
   from inputFiles
   into outputDir
   def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
+  inputFiles.each{ file ->
+    def rfile = inputDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputDir}/${rfile}")
+    outputFiles += "${ofile}"
   }
   preserve {
-    include "**"
+    include "**/*"
   }
+
   outputs.files outputFiles
   inputs.files inputFiles
 }
@@ -3584,7 +3682,8 @@ task jalviewjsProjectImport(type: Exec) {
   }
 
   //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"
+  def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/${eclipse_project_name}"
+
   executable(eclipseBinary)
   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
   if (eclipseDebug) {
@@ -3594,19 +3693,22 @@ task jalviewjsProjectImport(type: Exec) {
   if (!IN_ECLIPSE) {
     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
+    inputs.file("${jalviewjsJ2sAltSettingsFileName}")
   }
 
-  inputs.file("${jalviewDir}/.project")
-  outputs.upToDateWhen { 
-    file(projdir).exists()
-  }
+  outputs.upToDateWhen( {
+    if (IN_ECLIPSE) {
+      return true
+    }
+    def projDirExists = file(projdir).exists()
+    return projDirExists
+  } )
 }
 
-
+// jalviewjs_eclipse_workspace_location_file
 task jalviewjsTranspile(type: Exec) {
-  dependsOn jalviewjsEclipseSetup 
   dependsOn jalviewjsProjectImport
-  dependsOn jalviewjsEclipsePaths
+
   if (!IN_ECLIPSE) {
     dependsOn jalviewjsEnableAltFileProperty
   }
@@ -3641,11 +3743,12 @@ task jalviewjsTranspile(type: Exec) {
     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
     def logOutFile = file(logOutFileName)
     logOutFile.createNewFile()
-    logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
-BINARY: ${eclipseBinary}
-VERSION: ${eclipseVersion}
-WORKSPACE: ${eclipseWorkspace}
-DEBUG: ${eclipseDebug}
+    def info = """ROOT: ${jalviewjs_eclipse_root}
+ECLIPSE BINARY: ${eclipseBinary}
+ECLIPSE VERSION: ${eclipseVersion}
+ECLIPSE PRODUCT: ${eclipseProductVersion}
+ECLIPSE WORKSPACE: ${eclipseWorkspace}
+ECLIPSE DEBUG: ${eclipseDebug}
 ----
 """
     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
@@ -3671,12 +3774,58 @@ DEBUG: ${eclipseDebug}
         logErrFOS,
         stderr)
     }
+    standardOutput.write(string(info).getBytes("UTF-8"))
   }
 
   doLast {
-    if (stdout.toString().contains("Error processing ")) {
+    def transpileError = false
+    def j2sIsActive = false
+    def j2sBuildStarting = false
+    def compilingLines = 0
+    def j2sBuildingJavascript = false
+    def j2sBuildingJavascriptRegex = /(?m)^J2S building JavaScript for (\d+) files/
+    def numFiles = 0
+    def transpilingLines = 0
+    stdout.toString().eachLine { String line ->
+      if (line.startsWith("J2S isActive true")) {
+        j2sIsActive = true
+      }
+      if (line.startsWith("J2S buildStarting")) {
+        j2sBuildStarting = true
+      }
+      if (line =~ / Compiling /) {
+        compilingLines++
+      }
+      if (!j2sBuildingJavascript) {
+        def matcher = line =~ j2sBuildingJavascriptRegex
+        if (matcher.size() > 0) {
+          numFiles = Integer.valueOf(matcher[0][1])
+          j2sBuildingJavascript = true
+        }
+      }
+      if (line.startsWith("J2S transpiling ")) {
+        transpilingLines++
+      }
+      if (line.contains("Error processing ")) {
+        transpileError = true
+      }
+    }
+    
+    println("J2S IS ACTIVE=${j2sIsActive}")
+    println("J2S BUILD STARTING=${j2sBuildStarting}")
+    println("J2S BUILDING JAVASCRIPT=${j2sBuildingJavascript}")
+    println("NUM FILES=${numFiles}")
+    println("COMPILING LINES=${compilingLines}")
+    println("TRANSPILING LINES=${transpilingLines}")
+    println("TRANSPILE ERROR=${transpileError}")
+    
+    if (!j2sIsActive
+        || transpileError
+        || (j2sBuildStarting && transpilingLines == 0)
+        || (transpilingLines < compilingLines)
+        || (transpilingLines != numFiles)
+        ) {
       // j2s did not complete transpile
-      //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
       if (jalviewjs_ignore_transpile_errors.equals("true")) {
         println("IGNORING TRANSPILE ERRORS")
         println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
@@ -3686,74 +3835,127 @@ DEBUG: ${eclipseDebug}
     }
   }
 
+  if (IN_ECLIPSE) {
+    inputs.file(jalviewjsJ2sSettingsFileName)
+  } else {
+    inputs.file(jalviewjsJ2sAltSettingsFileName)
+  }
   inputs.dir("${jalviewDir}/${sourceDir}")
-  outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
-  outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
+
+  def inputJavaDir = file("${jalviewDir}/${sourceDir}")
+  def inputJavaFiles = fileTree(dir: inputJavaDir)
+  def outputJsDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
+  def outputJsFiles = []
+  inputJavaFiles.each{ file ->
+    def rfile = inputJavaDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputJsDir}/${rfile}")
+    def ofilenamejs = ofile.getPath()
+    if (ofilenamejs.endsWith(".java")) {
+      ofilenamejs = ofilenamejs.substring(0,ofilenamejs.length()-4)+"js"
+    }
+    outputJsFiles += "${ofilenamejs}"
+  }
+
+  outputs.files outputJsFiles
+  outputs.file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}")
 }
 
 
-task jalviewjsTranserSiteMergeLibDirs (type: Sync) {
-  dependsOn jalviewjsTransferUnzipAllLibs
-  dependsOn jalviewjsTransferUnzipSwingJs
+task jalviewjsTransferSiteMergeSiteJsDir (type: Copy) {
   dependsOn jalviewjsTranspile
 
-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
-  // merge swingjs lib last
-  inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
-
+  def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
-
-  from inputFiles
   into outputDir
-  def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
+  from {
+    def inputFiles = fileTree(dir: inputDir)
+    return inputFiles
   }
 
+  includeEmptyDirs = false
   exclude "**/*.html"
   exclude "**/*.htm"
 
   // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
   duplicatesStrategy "INCLUDE"
 
-  outputs.files outputFiles
-  inputs.files inputFiles
-}
+  // SiteJs files should take priority and write over existing files if different
+  // so we define the output files
+  outputs.upToDateWhen(
+    {
+      def transpiledFiles = jalviewjsTransferSiteMergeSiteJsDir.getOutputs().getFiles()
+      def inputFiles = fileTree(dir: inputDir)
+      if (inputFiles.size() < transpiledFiles.size()) {
+        return false
+      }
+      def retVal = ! inputFiles.any { file ->
+        def rfile = inputDir.toPath().relativize(file.toPath())
+        def ofile = new File("${outputDir}/${rfile}")
+        if (!ofile.exists() || ofile.lastModified() < file.lastModified()) {
+          return true // this is NOTted to false
+        }
+      }
 
+      return retVal
+    }
+  )
 
-task jalviewjsTranserSiteMergeSwingDir (type: Sync) {
-  dependsOn jalviewjsTransferUnzipAllLibs
-  dependsOn jalviewjsTransferUnzipSwingJs
-  dependsOn jalviewjsTranspile
+  inputs.files jalviewjsTranspile
+}
 
-  // merge jalview files very last
-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteJsDir}")
+task jalviewjsTransferSiteMergeLibDir (type: Copy) {
+  dependsOn jalviewjsTransferUnzipLib
 
   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
 
-  from inputFiles
+  // This takes the outputs of jalviewjsTransferUnzipLib
+  from jalviewjsTransferUnzipLib
   into outputDir
-  def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
-  }
-  preserve {
-    include "**"
+
+  includeEmptyDirs = false
+  exclude "**/*.html"
+  exclude "**/*.htm"
+
+  // don't overwrite files in the destination
+  // Note, this closure gets run at run stage not config stage
+  eachFile {
+    if (it.getRelativePath().getFile(file(outputDir)).exists()) {
+      it.exclude()
+    }
   }
 
-  // should this be exclude really ? No, jalview dir should be transferred last (and overwrite)
   duplicatesStrategy "INCLUDE"
+}
 
-  outputs.files outputFiles
-  inputs.files inputFiles
+task jalviewjsTransferSiteMergeSwingJsDir (type: Copy) {
+  dependsOn jalviewjsTransferUnzipSwingJs
+
+  def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
+
+  // This takes the outputs of jalviewjsTransferUnzipSwingJs
+  from jalviewjsTransferUnzipSwingJs
+  into outputDir
+
+  includeEmptyDirs = false
+  exclude "**/*.html"
+  exclude "**/*.htm"
+
+  // DO overwrite files in the destination
+  
+  // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
+  duplicatesStrategy "INCLUDE"
 }
 
+// we run after SiteJs and exclude overwriting files
+jalviewjsTransferSiteMergeLibDir.mustRunAfter jalviewjsTransferSiteMergeSiteJsDir
+jalviewjsTransferSiteMergeLibDir.mustRunAfter jalviewjsTransferSiteMergeSwingJsDir
+// we run this last, overwriting files from sitejs and lib, to ensure a consistent SwingJS
+jalviewjsTransferSiteMergeSwingJsDir.mustRunAfter jalviewjsTransferSiteMergeSiteJsDir
 
-task jalviewjsTranserSiteMergeDirs {
-  dependsOn jalviewjsTranserSiteMergeLibDirs
-  dependsOn jalviewjsTranserSiteMergeSwingDir
+task jalviewjsTransferSiteMergeDirs {
+  dependsOn jalviewjsTransferSiteMergeLibDir
+  dependsOn jalviewjsTransferSiteMergeSwingJsDir
+  dependsOn jalviewjsTransferSiteMergeSiteJsDir
 }
 
 
@@ -3797,11 +3999,11 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin
   def logErrFOS = logOutFOS
 
   javaexec {
-    classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
+    classpath = files([closureCompilerJar])
     main = "com.google.javascript.jscomp.CommandLineRunner"
     jvmArgs = [ "-Dfile.encoding=UTF-8" ]
     args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
-    maxHeapSize = "2g"
+    maxHeapSize = "4g"
 
     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
     println(msg)
@@ -3838,10 +4040,10 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin
 }
 
 
-task jalviewjsBuildAllCores {
+task jalviewjsBuildCore {
   group "JalviewJS"
   description "Build the core js lib closures listed in the classlists dir"
-  dependsOn jalviewjsTranserSiteMergeDirs
+  dependsOn jalviewjsTransferSiteMergeDirs
 
   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
@@ -3865,8 +4067,7 @@ task jalviewjsBuildAllCores {
     ]
   }
 
-  // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
-  //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
+  // _jalview core
   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
 
   jalviewjsCoreClasslists = []
@@ -3907,52 +4108,18 @@ task jalviewjsBuildAllCores {
     outputs.file(zjsfile)
   }
   
-  // _all core
-  def allClasslistName = "_all"
-  def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
-  allJsFiles += fileTree(
-    dir: libJ2sDir,
-    include: "**/*.js",
-    excludes: [
-      // these exlusions are files that the closure-compiler produces errors for. Should fix them
-      "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
-      "**/org/jmol/export/JSExporter.js"
-    ]
-  )
-  allJsFiles += fileTree(
-    dir: swingJ2sDir,
-    include: "**/*.js",
-    excludes: [
-      // these exlusions are files that the closure-compiler produces errors for. Should fix them
-      "**/sun/misc/Unsafe.js",
-      "**/swingjs/jquery/jquery-editable-select.js",
-      "**/swingjs/jquery/j2sComboBox.js",
-      "**/sun/misc/FloatingDecimal.js"
-    ]
-  )
-  def allClasslist = [
-    'jsfile': "${outputDir}/core${allClasslistName}.js",
-    'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
-    'list': allJsFiles,
-    'name': allClasslistName
-  ]
-  // not including this version of "all" core at the moment
-  //jalviewjsCoreClasslists += allClasslist
-  inputs.files(allClasslist['list'])
-  outputs.file(allClasslist['jsfile'])
-  outputs.file(allClasslist['zjsfile'])
-
   doFirst {
     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
     logOutFile.getParentFile().mkdirs()
     logOutFile.createNewFile()
-    logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
+    logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildCore\n----\n")
 
     jalviewjsCoreClasslists.each {
       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
     }
   }
 
+  inputs.file(closureCompilerJar)
 }
 
 
@@ -3982,7 +4149,7 @@ def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inpu
 
 
 task jalviewjsPublishCoreTemplates {
-  dependsOn jalviewjsBuildAllCores
+  dependsOn jalviewjsBuildCore
 
   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
   def inputFile = file(inputFileName)
@@ -4006,22 +4173,25 @@ task jalviewjsPublishCoreTemplates {
 
 
 task jalviewjsSyncCore (type: Sync) {
-  dependsOn jalviewjsBuildAllCores
+  dependsOn jalviewjsBuildCore
   dependsOn jalviewjsPublishCoreTemplates
 
-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
+  def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
+  def inputFiles = fileTree(dir: inputDir)
   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
 
   from inputFiles
   into outputDir
   def outputFiles = []
-  rename { filename ->
-    outputFiles += "${outputDir}/${filename}"
-    null
+  inputFiles.each{ file ->
+    def rfile = inputDir.toPath().relativize(file.toPath())
+    def ofile = new File("${outputDir}/${rfile}")
+    outputFiles += "${ofile}"
   }
   preserve {
-    include "**"
+    include "**/*"
   }
+
   outputs.files outputFiles
   inputs.files inputFiles
 }
@@ -4029,7 +4199,7 @@ task jalviewjsSyncCore (type: Sync) {
 
 // this Copy version of TransferSiteJs will delete anything else in the target dir
 task jalviewjsCopyTransferSiteMergeDir(type: Copy) {
-  dependsOn jalviewjsTranserSiteMergeDirs
+  dependsOn jalviewjsTransferSiteMergeDirs
 
   from "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
   into "${jalviewDir}/${jalviewjsSiteDir}"
@@ -4051,7 +4221,7 @@ task jalviewjsSyncTransferSiteJs(type: Sync) {
   include "**/*.*"
   into "${jalviewDir}/${jalviewjsSiteDir}"
   preserve {
-    include "**"
+    include "**/*"
   }
 }
 
@@ -4411,9 +4581,10 @@ task jalviewjsLaunchTest {
       execStdout = stdout
       execStderr = stderr
     }
-    // macOS not running properly with timeout arguments
-    def execArgs = macOS ? [] : [
+    // macOS seems okay now with timeout arguments
+    def execArgs = [
       "--virtual-time-budget=${timeoutms}",
+      "--timeout=${timeoutms}",
     ]
     execArgs += [
       "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
@@ -4425,21 +4596,22 @@ task jalviewjsLaunchTest {
       "--enable-logging=stderr",
       "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
     ]
-    
-    if (true || macOS) {
-      ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
-      Future f1 = executor.submit(
-        () -> {
-          exec {
-            standardOutput = execStdout
-            errorOutput = execStderr
-            executable(chromiumBinary)
-            args(execArgs)
-            println "COMMAND: '"+commandLine.join(" ")+"'"
-          }
-          executor.shutdownNow()
+
+    java.lang.Runnable runChrome = () -> {
+        exec {
+          standardOutput = execStdout
+          errorOutput = execStderr
+          executable(chromiumBinary)
+          args(execArgs)
+          println "COMMAND: '"+commandLine.join(" ")+"'"
         }
-      )
+      }
+
+    if (macOS) {
+    // we create our own timeout executor as --timeout doesn't work on macOS
+      ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
+
+      Future f1 = executor.submit( runChrome )
 
       def noChangeBytes = 0
       def noChangeIterations = 0
@@ -4448,10 +4620,10 @@ task jalviewjsLaunchTest {
           String stderrString = stderr.toString()
           // shutdown the task if we have a success string
           if (stderrString.contains(jalviewjs_desktop_init_string)) {
-            f1.cancel()
-            Thread.sleep(1000)
+            f1.cancel(true)
+            Thread.sleep(100)
             executor.shutdownNow()
-          }
+          } else
           // if no change in stderr for 10s then also end
           if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
             executor.shutdownNow()
@@ -4463,17 +4635,21 @@ task jalviewjsLaunchTest {
             noChangeIterations = 0
           }
         },
-        1, 1, TimeUnit.SECONDS)
+        200, 200, TimeUnit.MILLISECONDS)
 
       executor.schedule(new Runnable(){
         public void run(){
-          f1.cancel()
+          f1.cancel(true)
           executor.shutdownNow()
         }
       }, timeoutms, TimeUnit.MILLISECONDS)
 
-      executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
+      executor.awaitTermination(timeoutms+200, TimeUnit.MILLISECONDS)
+      f1.cancel(true)
       executor.shutdownNow()
+    } else {
+      // just run chrome and rely on --virtual-time-budget and --timeout
+      runChrome.run()
     }
 
   }