JAL-4059 JAL-4395 Updated closure-compiler. Added local_eclipse.properties for buildship
[jalview.git] / build.gradle
index 22cb8db..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,11 +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")
-  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 = []
@@ -574,6 +587,8 @@ ext {
   jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
   jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
 
+  closureCompilerJar = "${jalviewDir}/${jalviewjs_closure_compiler}"
+    
   eclipseWorkspace = null
   eclipseBinary = string("")
   eclipseVersion = string("")
@@ -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}'")
+        }
       }
     }
 
@@ -3247,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
@@ -3301,8 +3332,8 @@ 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) }
 }
 
 
@@ -3437,41 +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 {
-  def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
+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}"
+  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 jalviewjsTransferUnzipSwingJs
-  dependsOn jalviewjsTransferUnzipLib
+  }
+  into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
 }
 
 
@@ -3500,7 +3527,6 @@ task jalviewjsCreateJ2sSettings(type: WriteProperties) {
 
   if (! IN_ECLIPSE) {
     inputs.properties(jalviewjsJ2sProps)
-    outputs.file(jalviewjsJ2sAltSettingsFileName)
   }
 }
 
@@ -3512,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 ?
   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
@@ -3559,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
 }
@@ -3579,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
 }
@@ -3613,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) {
@@ -3623,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
   }
@@ -3762,9 +3835,127 @@ ECLIPSE 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 jalviewjsTransferSiteMergeSiteJsDir (type: Copy) {
+  dependsOn jalviewjsTranspile
+
+  def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
+  def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
+  into outputDir
+  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"
+
+  // 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
+    }
+  )
+
+  inputs.files jalviewjsTranspile
+}
+
+task jalviewjsTransferSiteMergeLibDir (type: Copy) {
+  dependsOn jalviewjsTransferUnzipLib
+
+  def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
+
+  // This takes the outputs of jalviewjsTransferUnzipLib
+  from jalviewjsTransferUnzipLib
+  into outputDir
+
+  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()
+    }
+  }
+
+  duplicatesStrategy "INCLUDE"
+}
+
+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 jalviewjsTransferSiteMergeDirs {
+  dependsOn jalviewjsTransferSiteMergeLibDir
+  dependsOn jalviewjsTransferSiteMergeSwingJsDir
+  dependsOn jalviewjsTransferSiteMergeSiteJsDir
 }
 
 
@@ -3782,6 +3973,7 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin
 
   def coreTop = file(prefixFile)
   def coreBottom = file(suffixFile)
+  def missingFiles = []
   coreFile.getParentFile().mkdirs()
   coreFile.createNewFile()
   coreFile.write( coreTop.getText("UTF-8") )
@@ -3795,6 +3987,7 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin
       msg = "...file '"+f.getPath()+"' does not exist, skipping"
       println(msg)
       logOutFile.append(msg+"\n")
+      missingFiles += f
     }
   }
   coreFile.append( coreBottom.getText("UTF-8") )
@@ -3806,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", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
-    maxHeapSize = "2g"
+    args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
+    maxHeapSize = "4g"
 
     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
     println(msg)
@@ -3837,21 +4030,25 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin
     }
   }
   msg = "--"
+  if (missingFiles.size() > 0) {
+    msg += "\n!!! These files were listed but missing:\n"
+    missingFiles.each { file -> msg += "!!!  " + file.getPath() + "\n" }
+    msg = "--"
+  }
   println(msg)
   logOutFile.append(msg+"\n")
 }
 
 
-task jalviewjsBuildAllCores {
+task jalviewjsBuildCore {
   group "JalviewJS"
   description "Build the core js lib closures listed in the classlists dir"
-  dependsOn jalviewjsTranspile
-  dependsOn jalviewjsTransferUnzipSwingJs
+  dependsOn jalviewjsTransferSiteMergeDirs
 
-  def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
-  def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
-  def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
-  def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
+  def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
+  def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
+  def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
+  def jsDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_js_subdir}"
   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
   def prefixFile = "${jsDir}/core/coretop2.js"
   def suffixFile = "${jsDir}/core/corebottom2.js"
@@ -3870,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 = []
@@ -3912,65 +4108,18 @@ task jalviewjsBuildAllCores {
     outputs.file(zjsfile)
   }
   
-  // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
-  def stevesoftClasslistName = "_stevesoft"
-  def stevesoftClasslist = [
-    'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
-    'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
-    'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
-    'name': stevesoftClasslistName
-  ]
-  jalviewjsCoreClasslists += stevesoftClasslist
-  inputs.files(stevesoftClasslist['list'])
-  outputs.file(stevesoftClasslist['jsfile'])
-  outputs.file(stevesoftClasslist['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)
 }
 
 
@@ -4000,7 +4149,8 @@ def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inpu
 
 
 task jalviewjsPublishCoreTemplates {
-  dependsOn jalviewjsBuildAllCores
+  dependsOn jalviewjsBuildCore
+
   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
   def inputFile = file(inputFileName)
   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
@@ -4023,29 +4173,43 @@ 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
 }
 
 
 // this Copy version of TransferSiteJs will delete anything else in the target dir
+task jalviewjsCopyTransferSiteMergeDir(type: Copy) {
+  dependsOn jalviewjsTransferSiteMergeDirs
+
+  from "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
+  into "${jalviewDir}/${jalviewjsSiteDir}"
+}
+
+
+// this Copy version of TransferSiteJs will delete anything else in the target dir
 task jalviewjsCopyTransferSiteJs(type: Copy) {
   dependsOn jalviewjsTranspile
+
   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
   into "${jalviewDir}/${jalviewjsSiteDir}"
 }
@@ -4057,7 +4221,7 @@ task jalviewjsSyncTransferSiteJs(type: Sync) {
   include "**/*.*"
   into "${jalviewDir}/${jalviewjsSiteDir}"
   preserve {
-    include "**"
+    include "**/*"
   }
 }
 
@@ -4076,7 +4240,7 @@ jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
 task jalviewjsPrepareSite {
   group "JalviewJS"
   description "Prepares the website folder including unzipping files and copying resources"
-  dependsOn jalviewjsSyncAllLibs
+  //dependsOn jalviewjsSyncAllLibs // now using jalviewjsCopyTransferSiteMergeDir
   dependsOn jalviewjsSyncResources
   dependsOn jalviewjsSyncSiteResources
   dependsOn jalviewjsSyncBuildProperties
@@ -4087,7 +4251,7 @@ task jalviewjsPrepareSite {
 task jalviewjsBuildSite {
   group "JalviewJS"
   description "Builds the whole website including transpiled code"
-  dependsOn jalviewjsCopyTransferSiteJs
+  dependsOn jalviewjsCopyTransferSiteMergeDir
   dependsOn jalviewjsPrepareSite
 }
 
@@ -4417,33 +4581,37 @@ task jalviewjsLaunchTest {
       execStdout = stdout
       execStderr = stderr
     }
+    // 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
       "--headless=new",
       "--disable-gpu",
-      "--timeout=${timeoutms}",
-      "--virtual-time-budget=${timeoutms}",
       "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
       "--profile-directory=${jalviewjs_chromium_profile_name}",
       "--allow-file-access-from-files",
       "--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
@@ -4452,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()
@@ -4467,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()
     }
 
   }