b0d0fbb2bd2629197a53c8c6d8c2539b6e5f3c62
[jalview.git] / build.gradle
1 /* Convention for properties.  Read from gradle.properties, use lower_case_underlines for property names.
2  * For properties set within build.gradle, use camelCaseNoSpace.
3  */
4 import org.apache.tools.ant.filters.ReplaceTokens
5 import org.gradle.internal.os.OperatingSystem
6 import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject
7 import org.gradle.api.internal.PropertiesTransformer
8 import org.gradle.util.ConfigureUtil
9 import org.gradle.plugins.ide.eclipse.model.Output
10 import org.gradle.plugins.ide.eclipse.model.Library
11 import java.security.MessageDigest
12 import groovy.transform.ExternalizeMethods
13 import groovy.util.XmlParser
14 import groovy.xml.XmlUtil
15 import groovy.json.JsonBuilder
16 import com.vladsch.flexmark.util.ast.Node
17 import com.vladsch.flexmark.html.HtmlRenderer
18 import com.vladsch.flexmark.parser.Parser
19 import com.vladsch.flexmark.util.data.MutableDataSet
20 import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
21 import com.vladsch.flexmark.ext.tables.TablesExtension
22 import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
23 import com.vladsch.flexmark.ext.autolink.AutolinkExtension
24 import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
25 import com.vladsch.flexmark.ext.toc.TocExtension
26 import com.google.common.hash.HashCode
27 import com.google.common.hash.Hashing
28 import com.google.common.io.Files
29 import org.jsoup.Jsoup
30
31 buildscript {
32   repositories {
33     mavenCentral()
34     mavenLocal()
35   }
36   dependencies {
37     classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
38     classpath "org.jsoup:jsoup:1.14.3"
39   }
40 }
41
42
43 plugins {
44   id 'java'
45   id 'application'
46   id 'eclipse'
47   id "com.diffplug.gradle.spotless" version "3.28.0"
48   id 'com.github.johnrengelman.shadow' version '4.0.3'
49   id 'com.install4j.gradle' version '9.0.6'
50   id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
51   id 'com.palantir.git-version' version '0.13.0' apply false
52 }
53
54 repositories {
55   jcenter()
56   mavenCentral()
57   mavenLocal()
58 }
59
60
61
62 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
63 def string(Object o) {
64   return o == null ? "" : o.toString()
65 }
66
67 def overrideProperties(String propsFileName, boolean output = false) {
68   if (propsFileName == null) {
69     return
70   }
71   def propsFile = file(propsFileName)
72   if (propsFile != null && propsFile.exists()) {
73     println("Using properties from file '${propsFileName}'")
74     try {
75       def p = new Properties()
76       def localPropsFIS = new FileInputStream(propsFile)
77       p.load(localPropsFIS)
78       localPropsFIS.close()
79       p.each {
80         key, val -> 
81           def oldval
82           if (project.hasProperty(key)) {
83             oldval = project.findProperty(key)
84             project.setProperty(key, val)
85             if (output) {
86               println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
87             }
88           } else {
89             ext.setProperty(key, val)
90             if (output) {
91               println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
92             }
93           }
94       }
95     } catch (Exception e) {
96       println("Exception reading local.properties")
97       e.printStackTrace()
98     }
99   }
100 }
101
102 ext {
103   jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
104   jalviewDirRelativePath = jalviewDir
105   date = new Date()
106
107   getdownChannelName = CHANNEL.toLowerCase()
108   // default to "default". Currently only has different cosmetics for "develop", "release", "default"
109   propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
110   // Import channel_properties
111   channelDir = string("${jalviewDir}/${channel_properties_dir}/${propertiesChannelName}")
112   channelGradleProperties = string("${channelDir}/channel_gradle.properties")
113   channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
114   overrideProperties(channelGradleProperties, false)
115   // local build environment properties
116   // can be "projectDir/local.properties"
117   overrideProperties("${projectDir}/local.properties", true)
118   // or "../projectDir_local.properties"
119   overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
120
121   ////  
122   // Import releaseProps from the RELEASE file
123   // or a file specified via JALVIEW_RELEASE_FILE if defined
124   // Expect jalview.version and target release branch in jalview.release        
125   releaseProps = new Properties();
126   def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
127   def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
128   try {
129     (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream { 
130      releaseProps.load(it)
131     }
132   } catch (Exception fileLoadError) {
133     throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
134   }
135   ////
136   // Set JALVIEW_VERSION if it is not already set
137   if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
138     JALVIEW_VERSION = releaseProps.get("jalview.version")
139   }
140   
141   // this property set when running Eclipse headlessly
142   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
143   // this property set by Eclipse
144   eclipseApplicationProperty = string("eclipse.application")
145   // CHECK IF RUNNING FROM WITHIN ECLIPSE
146   def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
147   IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
148   // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
149   if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
150     println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
151     IN_ECLIPSE = false
152   }
153   if (IN_ECLIPSE) {
154     println("WITHIN ECLIPSE IDE")
155   } else {
156     println("HEADLESS BUILD")
157   }
158   
159   J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
160   if (J2S_ENABLED) {
161     println("J2S ENABLED")
162   } 
163   /* *-/
164   System.properties.sort { it.key }.each {
165     key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
166   }
167   /-* *-/
168   if (false && IN_ECLIPSE) {
169     jalviewDir = jalviewDirAbsolutePath
170   }
171   */
172
173   // datestamp
174   buildDate = new Date().format("yyyyMMdd")
175
176   // essentials
177   bareSourceDir = string(source_dir)
178   sourceDir = string("${jalviewDir}/${bareSourceDir}")
179   resourceDir = string("${jalviewDir}/${resource_dir}")
180   bareTestSourceDir = string(test_source_dir)
181   testDir = string("${jalviewDir}/${bareTestSourceDir}")
182
183   classesDir = string("${jalviewDir}/${classes_dir}")
184
185   // clover
186   useClover = clover.equals("true")
187   cloverBuildDir = "${buildDir}/clover"
188   cloverInstrDir = file("${cloverBuildDir}/clover-instr")
189   cloverClassesDir = file("${cloverBuildDir}/clover-classes")
190   cloverReportDir = file("${buildDir}/reports/clover")
191   cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
192   cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
193   //cloverTestClassesDir = cloverClassesDir
194   cloverDb = string("${cloverBuildDir}/clover.db")
195
196   testSourceDir = useClover ? cloverTestInstrDir : testDir
197   testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
198
199   getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
200   getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
201   getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
202   getdownFullArchiveDir = null
203   getdownTextLines = []
204   getdownLaunchJvl = null
205   getdownVersionLaunchJvl = null
206   buildDist = true
207   buildProperties = null
208
209   // the following values might be overridden by the CHANNEL switch
210   getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
211   getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
212   getdownArchiveAppBase = getdown_archive_base
213   getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
214   getdownAppDistDir = getdown_app_dir_alt
215   getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
216   getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
217   reportRsyncCommand = false
218   jvlChannelName = CHANNEL.toLowerCase()
219   install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
220   install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
221   install4jDMGBackgroundImage = "${install4j_images_dir}/${install4j_dmg_background}"
222   install4jInstallerName = "${jalview_name} Non-Release Installer"
223   install4jExecutableName = install4j_executable_name
224   install4jExtraScheme = "jalviewx"
225   install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
226   install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
227   install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
228   install4jBackground = string("${install4j_images_dir}/${install4j_background}")
229   install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
230   install4jCheckSums = true
231
232   applicationName = "${jalview_name}"
233   switch (CHANNEL) {
234
235     case "BUILD":
236     // TODO: get bamboo build artifact URL for getdown artifacts
237     getdown_channel_base = bamboo_channelbase
238     getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
239     getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
240     jvlChannelName += "_${getdownChannelName}"
241     // automatically add the test group Not-bamboo for exclusion 
242     if ("".equals(testng_excluded_groups)) { 
243       testng_excluded_groups = "Not-bamboo"
244     }
245     install4jExtraScheme = "jalviewb"
246     break
247
248     case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
249     getdownAppDistDir = getdown_app_dir_release
250     getdownSetAppBaseProperty = true
251     reportRsyncCommand = true
252     install4jSuffix = ""
253     install4jInstallerName = "${jalview_name} Installer"
254     break
255
256     case "ARCHIVE":
257     getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
258     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
259     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
260     if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
261       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
262     } else {
263       package_dir = string("${ARCHIVEDIR}/${package_dir}")
264       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
265       buildDist = false
266     }
267     reportRsyncCommand = true
268     install4jExtraScheme = "jalviewa"
269     break
270
271     case "ARCHIVELOCAL":
272     getdownChannelName = string("archive/${JALVIEW_VERSION}")
273     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
274     getdownAppBase = file(getdownAppBaseDir).toURI().toString()
275     if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
276       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
277     } else {
278       package_dir = string("${ARCHIVEDIR}/${package_dir}")
279       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
280       buildDist = false
281     }
282     reportRsyncCommand = true
283     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
284     install4jSuffix = "Archive"
285     install4jExtraScheme = "jalviewa"
286     break
287
288     case "DEVELOP":
289     reportRsyncCommand = true
290     getdownSetAppBaseProperty = true
291     // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
292     JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
293     
294     install4jSuffix = "Develop"
295     install4jExtraScheme = "jalviewd"
296     install4jInstallerName = "${jalview_name} Develop Installer"
297     break
298
299     case "TEST-RELEASE":
300     reportRsyncCommand = true
301     getdownSetAppBaseProperty = true
302     // Don't ignore transpile errors for release build
303     if (jalviewjs_ignore_transpile_errors.equals("true")) {
304       jalviewjs_ignore_transpile_errors = "false"
305       println("Setting jalviewjs_ignore_transpile_errors to 'false'")
306     }
307     JALVIEW_VERSION = JALVIEW_VERSION+"-test"
308     install4jSuffix = "Test"
309     install4jExtraScheme = "jalviewt"
310     install4jInstallerName = "${jalview_name} Test Installer"
311     break
312
313     case ~/^SCRATCH(|-[-\w]*)$/:
314     getdownChannelName = CHANNEL
315     JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
316     
317     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
318     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
319     reportRsyncCommand = true
320     install4jSuffix = "Scratch"
321     break
322
323     case "TEST-LOCAL":
324     if (!file("${LOCALDIR}").exists()) {
325       throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
326     } else {
327       getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
328       getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
329     }
330     JALVIEW_VERSION = "TEST"
331     install4jSuffix = "Test-Local"
332     install4jExtraScheme = "jalviewt"
333     install4jInstallerName = "${jalview_name} Test Installer"
334     break
335
336     case [ "LOCAL", "JALVIEWJS" ]:
337     JALVIEW_VERSION = "TEST"
338     getdownAppBase = file(getdownAppBaseDir).toURI().toString()
339     getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
340     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
341     install4jExtraScheme = "jalviewl"
342     install4jCheckSums = false
343     break
344
345     default: // something wrong specified
346     throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
347     break
348
349   }
350   JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
351   hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
352   hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
353   // override getdownAppBase if requested
354   if (findProperty("getdown_appbase_override") != null) {
355     // revert to LOCAL if empty string
356     if (string(getdown_appbase_override) == "") {
357       getdownAppBase = file(getdownAppBaseDir).toURI().toString()
358       getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
359     } else if (string(getdown_appbase_override).startsWith("file://")) {
360       getdownAppBase = string(getdown_appbase_override)
361       getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
362     } else {
363       getdownAppBase = string(getdown_appbase_override)
364     }
365     println("Overriding getdown appbase with '${getdownAppBase}'")
366   }
367   // sanitise file name for jalview launcher file for this channel
368   jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
369   // install4j application and folder names
370   if (install4jSuffix == "") {
371     install4jBundleId = "${install4j_bundle_id}"
372     install4jWinApplicationId = install4j_release_win_application_id
373   } else {
374     applicationName = "${jalview_name} ${install4jSuffix}"
375     install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
376     // add int hash of install4jSuffix to the last part of the application_id
377     def id = install4j_release_win_application_id
378     def idsplitreverse = id.split("-").reverse()
379     idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
380     install4jWinApplicationId = idsplitreverse.reverse().join("-")
381   }
382   // sanitise folder and id names
383   // install4jApplicationFolder = e.g. "Jalview Build"
384   install4jApplicationFolder = applicationName
385                                     .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
386                                     .replaceAll("_+", "_") // collapse __
387   install4jInternalId = applicationName
388                                     .replaceAll(" ","_")
389                                     .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
390                                     .replaceAll("_+", "") // collapse __
391                                     //.replaceAll("_*-_*", "-") // collapse _-_
392   install4jUnixApplicationFolder = applicationName
393                                     .replaceAll(" ","_")
394                                     .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
395                                     .replaceAll("_+", "_") // collapse __
396                                     .replaceAll("_*-_*", "-") // collapse _-_
397                                     .toLowerCase()
398
399   getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
400   getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
401   //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
402   getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
403   getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
404   getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
405   getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
406   /* compile without modules -- using classpath libraries
407   modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
408   modules_runtimeClasspath = modules_compileClasspath
409   */
410
411   gitHash = "SOURCE"
412   gitBranch = "Source"
413   try {
414     apply plugin: "com.palantir.git-version"
415     def details = versionDetails()
416     gitHash = details.gitHash
417     gitBranch = details.branchName
418   } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
419     println("Not in a git repository. Using git values from RELEASE properties file.")
420     gitHash = releaseProps.getProperty("git.hash")
421     gitBranch = releaseProps.getProperty("git.branch")
422   } catch(java.lang.RuntimeException e1) {
423     throw new GradleException("Error with git-version plugin.  Directory '.git' exists but versionDetails() cannot be found.")
424   }
425
426   println("Using a ${CHANNEL} profile.")
427
428   additional_compiler_args = []
429   // configure classpath/args for j8/j11 compilation
430   if (JAVA_VERSION.equals("1.8")) {
431     JAVA_INTEGER_VERSION = string("8")
432     //libDir = j8libDir
433     libDir = j11libDir
434     libDistDir = j8libDir
435     compile_source_compatibility = 1.8
436     compile_target_compatibility = 1.8
437     // these are getdown.txt properties defined dependent on the JAVA_VERSION
438     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
439     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
440     // this property is assigned below and expanded to multiple lines in the getdown task
441     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
442     // this property is for the Java library used in eclipse
443     eclipseJavaRuntimeName = string("JavaSE-1.8")
444   } else if (JAVA_VERSION.equals("11")) {
445     JAVA_INTEGER_VERSION = string("11")
446     libDir = j11libDir
447     libDistDir = j11libDir
448     compile_source_compatibility = 11
449     compile_target_compatibility = 11
450     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
451     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
452     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
453     eclipseJavaRuntimeName = string("JavaSE-11")
454     /* compile without modules -- using classpath libraries
455     additional_compiler_args += [
456     '--module-path', modules_compileClasspath.asPath,
457     '--add-modules', j11modules
458     ]
459      */
460   } else if (JAVA_VERSION.equals("17")) {
461     JAVA_INTEGER_VERSION = string("17")
462     libDir = j17libDir
463     libDistDir = j17libDir
464     compile_source_compatibility = 17
465     compile_target_compatibility = 17
466     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
467     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
468     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
469     eclipseJavaRuntimeName = string("JavaSE-17")
470     /* compile without modules -- using classpath libraries
471     additional_compiler_args += [
472     '--module-path', modules_compileClasspath.asPath,
473     '--add-modules', j11modules
474     ]
475      */
476   } else {
477     throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
478   }
479
480
481   // for install4j
482   JAVA_MIN_VERSION = JAVA_VERSION
483   JAVA_MAX_VERSION = JAVA_VERSION
484   def jreInstallsDir = string(jre_installs_dir)
485   if (jreInstallsDir.startsWith("~/")) {
486     jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
487   }
488   macosJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-mac-x64/jre")
489   windowsJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-windows-x64/jre")
490   linuxJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-linux-x64/jre")
491   macosJavaVMTgz = string("${jreInstallsDir}/tgz/jre_${JAVA_INTEGER_VERSION}_mac_x64.tar.gz")
492   windowsJavaVMTgz = string("${jreInstallsDir}/tgz/jre_${JAVA_INTEGER_VERSION}_windows_x64.tar.gz")
493   linuxJavaVMTgz = string("${jreInstallsDir}/tgz/jre_${JAVA_INTEGER_VERSION}_linux_x64.tar.gz")
494   install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
495   install4jConfFileName = string("jalview-install4j-conf.install4j")
496   install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
497   install4jHomeDir = install4j_home_dir
498   if (install4jHomeDir.startsWith("~/")) {
499     install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
500   }
501
502   resourceBuildDir = string("${buildDir}/resources")
503   resourcesBuildDir = string("${resourceBuildDir}/resources_build")
504   helpBuildDir = string("${resourceBuildDir}/help_build")
505   docBuildDir = string("${resourceBuildDir}/doc_build")
506
507   if (buildProperties == null) {
508     buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
509   }
510   buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
511   helpParentDir = string("${jalviewDir}/${help_parent_dir}")
512   helpSourceDir = string("${helpParentDir}/${help_dir}")
513   helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
514
515
516   relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
517   jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
518   jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
519   if (IN_ECLIPSE) {
520     jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
521   } else {
522     jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
523   }
524   jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
525   jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
526   jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
527   jalviewjsJalviewCoreHtmlFile = string("")
528   jalviewjsJalviewCoreName = string(jalviewjs_core_name)
529   jalviewjsCoreClasslists = []
530   jalviewjsJalviewTemplateName = string(jalviewjs_name)
531   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
532   jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
533   jalviewjsJ2sProps = null
534   jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
535
536   eclipseWorkspace = null
537   eclipseBinary = string("")
538   eclipseVersion = string("")
539   eclipseDebug = false
540   // ENDEXT
541 }
542
543
544 sourceSets {
545   main {
546     java {
547       srcDirs sourceDir
548       outputDir = file(classesDir)
549     }
550
551     resources {
552       srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
553     }
554
555     compileClasspath = files(sourceSets.main.java.outputDir)
556     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
557
558     runtimeClasspath = compileClasspath
559     runtimeClasspath += files(sourceSets.main.resources.srcDirs)
560   }
561
562   clover {
563     java {
564       srcDirs cloverInstrDir
565       outputDir = cloverClassesDir
566     }
567
568     resources {
569       srcDirs = sourceSets.main.resources.srcDirs
570     }
571
572     compileClasspath = files( sourceSets.clover.java.outputDir )
573     //compileClasspath += files( testClassesDir )
574     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
575     compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
576     compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
577
578     runtimeClasspath = compileClasspath
579   }
580
581   test {
582     java {
583       srcDirs testSourceDir
584       outputDir = file(testClassesDir)
585     }
586
587     resources {
588       srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
589     }
590
591     compileClasspath = files( sourceSets.test.java.outputDir )
592     compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
593     compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
594
595     runtimeClasspath = compileClasspath
596     runtimeClasspath += files(sourceSets.test.resources.srcDirs)
597   }
598
599 }
600
601
602 // eclipse project and settings files creation, also used by buildship
603 eclipse {
604   project {
605     name = eclipse_project_name
606
607     natures 'org.eclipse.jdt.core.javanature',
608     'org.eclipse.jdt.groovy.core.groovyNature',
609     'org.eclipse.buildship.core.gradleprojectnature'
610
611     buildCommand 'org.eclipse.jdt.core.javabuilder'
612     buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
613   }
614
615   classpath {
616     //defaultOutputDir = sourceSets.main.java.outputDir
617     configurations.each{ c->
618       if (c.isCanBeResolved()) {
619         minusConfigurations += [c]
620       }
621     }
622
623     plusConfigurations = [ ]
624     file {
625
626       whenMerged { cp ->
627         def removeTheseToo = []
628         HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
629         cp.entries.each { entry ->
630           // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
631           // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
632           // we add the resources and help/help dirs in as libs afterwards (see below)
633           if (entry.kind == 'src') {
634             if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
635               removeTheseToo += entry
636             } else {
637               alreadyAddedSrcPath.putAt(entry.path, true)
638             }
639           }
640
641         }
642         cp.entries.removeAll(removeTheseToo)
643
644         //cp.entries += new Output("${eclipse_bin_dir}/main")
645         if (file(helpParentDir).isDirectory()) {
646           cp.entries += new Library(fileReference(helpParentDir))
647         }
648         if (file(resourceDir).isDirectory()) {
649           cp.entries += new Library(fileReference(resourceDir))
650         }
651
652         HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
653
654         sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
655           //don't want to add outputDir as eclipse is using its own output dir in bin/main
656           if (it.isDirectory() || ! it.exists()) {
657             // don't add dirs to classpath, especially if they don't exist
658             return false // groovy "continue" in .any closure
659           }
660           def itPath = it.toString()
661           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
662             // make relative path
663             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
664           }
665           if (alreadyAddedLibPath.get(itPath)) {
666             //println("Not adding duplicate entry "+itPath)
667           } else {
668             //println("Adding entry "+itPath)
669             cp.entries += new Library(fileReference(itPath))
670             alreadyAddedLibPath.put(itPath, true)
671           }
672         }
673
674         sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
675           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
676           if (it.isDirectory() || ! it.exists()) {
677             // don't add dirs to classpath
678             return false // groovy "continue" in .any closure
679           }
680
681           def itPath = it.toString()
682           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
683             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
684           }
685           if (alreadyAddedLibPath.get(itPath)) {
686             // don't duplicate
687           } else {
688             def lib = new Library(fileReference(itPath))
689             lib.entryAttributes["test"] = "true"
690             cp.entries += lib
691             alreadyAddedLibPath.put(itPath, true)
692           }
693         }
694
695       } // whenMerged
696
697     } // file
698
699     containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
700
701   } // classpath
702
703   jdt {
704     // for the IDE, use java 11 compatibility
705     sourceCompatibility = compile_source_compatibility
706     targetCompatibility = compile_target_compatibility
707     javaRuntimeName = eclipseJavaRuntimeName
708
709     // add in jalview project specific properties/preferences into eclipse core preferences
710     file {
711       withProperties { props ->
712         def jalview_prefs = new Properties()
713         def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
714         jalview_prefs.load(ins)
715         ins.close()
716         jalview_prefs.forEach { t, v ->
717           if (props.getAt(t) == null) {
718             props.putAt(t, v)
719           }
720         }
721         // codestyle file -- overrides previous formatter prefs
722         def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
723         if (csFile.exists()) {
724           XmlParser parser = new XmlParser()
725           def profiles = parser.parse(csFile)
726           def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
727           if (profile != null) {
728             profile.'setting'.each { s ->
729               def id = s.'@id'
730               def value = s.'@value'
731               if (id != null && value != null) {
732                 props.putAt(id, value)
733               }
734             }
735           }
736         }
737       }
738     }
739
740   } // jdt
741
742   if (IN_ECLIPSE) {
743     // Don't want these to be activated if in headless build
744     synchronizationTasks "eclipseSynchronizationTask"
745     //autoBuildTasks "eclipseAutoBuildTask"
746
747   }
748 }
749
750
751 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
752 // Class to allow updating arbitrary properties files
753 class PropertiesFile extends PropertiesPersistableConfigurationObject {
754   public PropertiesFile(PropertiesTransformer t) { super(t); }
755   @Override protected void load(Properties properties) { }
756   @Override protected void store(Properties properties) { }
757   @Override protected String getDefaultResourceName() { return ""; }
758   // This is necessary, because PropertiesPersistableConfigurationObject fails
759   // if no default properties file exists.
760   @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
761 }
762
763 // Task to update arbitrary properties files (set outputFile)
764 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
765   private final PropertiesFileContentMerger file;
766   public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
767   protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
768   protected void configure(PropertiesFile props) {
769     file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
770   }
771   public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
772 }
773
774 task eclipseUIPreferences(type: PropertiesFileTask) {
775   description = "Generate Eclipse additional settings"
776   def filename = "org.eclipse.jdt.ui.prefs"
777   outputFile = "$projectDir/.settings/${filename}" as File
778   file {
779     withProperties {
780       it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
781     }
782   }
783 }
784
785 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
786   description = "Generate Eclipse additional settings"
787   def filename = "org.eclipse.jdt.groovy.core.prefs"
788   outputFile = "$projectDir/.settings/${filename}" as File
789   file {
790     withProperties {
791       it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
792     }
793   }
794 }
795
796 task eclipseAllPreferences {
797   dependsOn eclipseJdt
798   dependsOn eclipseUIPreferences
799   dependsOn eclipseGroovyCorePreferences
800 }
801
802 eclipseUIPreferences.mustRunAfter eclipseJdt
803 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
804
805 /* end of eclipse preferences hack */
806
807
808 // clover bits
809
810
811 task cleanClover {
812   doFirst {
813     delete cloverBuildDir
814     delete cloverReportDir
815   }
816 }
817
818
819 task cloverInstrJava(type: JavaExec) {
820   group = "Verification"
821   description = "Create clover instrumented source java files"
822
823   dependsOn cleanClover
824
825   inputs.files(sourceSets.main.allJava)
826   outputs.dir(cloverInstrDir)
827
828   //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
829   classpath = sourceSets.clover.compileClasspath
830   main = "com.atlassian.clover.CloverInstr"
831
832   def argsList = [
833     "--encoding",
834     "UTF-8",
835     "--initstring",
836     cloverDb,
837     "--destdir",
838     cloverInstrDir.getPath(),
839   ]
840   def srcFiles = sourceSets.main.allJava.files
841   argsList.addAll(
842     srcFiles.collect(
843       { file -> file.absolutePath }
844     )
845   )
846   args argsList.toArray()
847
848   doFirst {
849     delete cloverInstrDir
850     println("Clover: About to instrument "+srcFiles.size() +" files")
851   }
852 }
853
854
855 task cloverInstrTests(type: JavaExec) {
856   group = "Verification"
857   description = "Create clover instrumented source test files"
858
859   dependsOn cleanClover
860
861   inputs.files(testDir)
862   outputs.dir(cloverTestInstrDir)
863
864   classpath = sourceSets.clover.compileClasspath
865   main = "com.atlassian.clover.CloverInstr"
866
867   def argsList = [
868     "--encoding",
869     "UTF-8",
870     "--initstring",
871     cloverDb,
872     "--srcdir",
873     testDir,
874     "--destdir",
875     cloverTestInstrDir.getPath(),
876   ]
877   args argsList.toArray()
878
879   doFirst {
880     delete cloverTestInstrDir
881     println("Clover: About to instrument test files")
882   }
883 }
884
885
886 task cloverInstr {
887   group = "Verification"
888   description = "Create clover instrumented all source files"
889
890   dependsOn cloverInstrJava
891   dependsOn cloverInstrTests
892 }
893
894
895 cloverClasses.dependsOn cloverInstr
896
897
898 task cloverConsoleReport(type: JavaExec) {
899   group = "Verification"
900   description = "Creates clover console report"
901
902   onlyIf {
903     file(cloverDb).exists()
904   }
905
906   inputs.dir cloverClassesDir
907
908   classpath = sourceSets.clover.runtimeClasspath
909   main = "com.atlassian.clover.reporters.console.ConsoleReporter"
910
911   if (cloverreport_mem.length() > 0) {
912     maxHeapSize = cloverreport_mem
913   }
914   if (cloverreport_jvmargs.length() > 0) {
915     jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
916   }
917
918   def argsList = [
919     "--alwaysreport",
920     "--initstring",
921     cloverDb,
922     "--unittests"
923   ]
924
925   args argsList.toArray()
926 }
927
928
929 task cloverHtmlReport(type: JavaExec) {
930   group = "Verification"
931   description = "Creates clover HTML report"
932
933   onlyIf {
934     file(cloverDb).exists()
935   }
936
937   def cloverHtmlDir = cloverReportDir
938   inputs.dir cloverClassesDir
939   outputs.dir cloverHtmlDir
940
941   classpath = sourceSets.clover.runtimeClasspath
942   main = "com.atlassian.clover.reporters.html.HtmlReporter"
943
944   if (cloverreport_mem.length() > 0) {
945     maxHeapSize = cloverreport_mem
946   }
947   if (cloverreport_jvmargs.length() > 0) {
948     jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
949   }
950
951   def argsList = [
952     "--alwaysreport",
953     "--initstring",
954     cloverDb,
955     "--outputdir",
956     cloverHtmlDir
957   ]
958
959   if (cloverreport_html_options.length() > 0) {
960     argsList += cloverreport_html_options.split(" ")
961   }
962
963   args argsList.toArray()
964 }
965
966
967 task cloverXmlReport(type: JavaExec) {
968   group = "Verification"
969   description = "Creates clover XML report"
970
971   onlyIf {
972     file(cloverDb).exists()
973   }
974
975   def cloverXmlFile = "${cloverReportDir}/clover.xml"
976   inputs.dir cloverClassesDir
977   outputs.file cloverXmlFile
978
979   classpath = sourceSets.clover.runtimeClasspath
980   main = "com.atlassian.clover.reporters.xml.XMLReporter"
981
982   if (cloverreport_mem.length() > 0) {
983     maxHeapSize = cloverreport_mem
984   }
985   if (cloverreport_jvmargs.length() > 0) {
986     jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
987   }
988
989   def argsList = [
990     "--alwaysreport",
991     "--initstring",
992     cloverDb,
993     "--outfile",
994     cloverXmlFile
995   ]
996
997   if (cloverreport_xml_options.length() > 0) {
998     argsList += cloverreport_xml_options.split(" ")
999   }
1000
1001   args argsList.toArray()
1002 }
1003
1004
1005 task cloverReport {
1006   group = "Verification"
1007   description = "Creates clover reports"
1008
1009   dependsOn cloverXmlReport
1010   dependsOn cloverHtmlReport
1011 }
1012
1013
1014 compileCloverJava {
1015
1016   doFirst {
1017     sourceCompatibility = compile_source_compatibility
1018     targetCompatibility = compile_target_compatibility
1019     options.compilerArgs += additional_compiler_args
1020     print ("Setting target compatibility to "+targetCompatibility+"\n")
1021   }
1022   //classpath += configurations.cloverRuntime
1023 }
1024 // end clover bits
1025
1026
1027 compileJava {
1028   // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1029   sourceCompatibility = compile_source_compatibility
1030   targetCompatibility = compile_target_compatibility
1031   options.compilerArgs = additional_compiler_args
1032   options.encoding = "UTF-8"
1033   doFirst {
1034     print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1035   }
1036
1037 }
1038
1039
1040 compileTestJava {
1041   sourceCompatibility = compile_source_compatibility
1042   targetCompatibility = compile_target_compatibility
1043   options.compilerArgs = additional_compiler_args
1044   doFirst {
1045     print ("Setting target compatibility to "+targetCompatibility+"\n")
1046   }
1047 }
1048
1049
1050 clean {
1051   doFirst {
1052     delete sourceSets.main.java.outputDir
1053   }
1054 }
1055
1056
1057 cleanTest {
1058   dependsOn cleanClover
1059   doFirst {
1060     delete sourceSets.test.java.outputDir
1061   }
1062 }
1063
1064
1065 // format is a string like date.format("dd MMMM yyyy")
1066 def getDate(format) {
1067   return date.format(format)
1068 }
1069
1070
1071 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1072   MutableDataSet options = new MutableDataSet()
1073
1074   def extensions = new ArrayList<>()
1075   extensions.add(AnchorLinkExtension.create()) 
1076   extensions.add(AutolinkExtension.create())
1077   extensions.add(StrikethroughExtension.create())
1078   extensions.add(TaskListExtension.create())
1079   extensions.add(TablesExtension.create())
1080   extensions.add(TocExtension.create())
1081   
1082   options.set(Parser.EXTENSIONS, extensions)
1083
1084   // set GFM table parsing options
1085   options.set(TablesExtension.WITH_CAPTION, false)
1086   options.set(TablesExtension.COLUMN_SPANS, false)
1087   options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1088   options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1089   options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1090   options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1091   options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1092   // GFM anchor links
1093   options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1094   options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1095   options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1096   options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1097
1098   Parser parser = Parser.builder(options).build()
1099   HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1100
1101   mdFiles.each { mdFile ->
1102     // add table of contents
1103     def mdText = "[TOC]\n"+mdFile.text
1104
1105     // grab the first top-level title
1106     def title = null
1107     def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1108     def matcher = mdText =~ titleRegex
1109     if (matcher.size() > 0) {
1110       // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1111       title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1112     }
1113     // or use the filename if none found
1114     if (title == null) {
1115       title = mdFile.getName()
1116     }
1117
1118     Node document = parser.parse(mdText)
1119     String htmlBody = renderer.render(document)
1120     def htmlText = '''<html>
1121 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1122 <html xmlns="http://www.w3.org/1999/xhtml">
1123   <head>
1124     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1125     <meta http-equiv="Content-Style-Type" content="text/css" />
1126     <meta name="generator" content="flexmark" />
1127 '''
1128     htmlText += ((title != null) ? "  <title>${title}</title>" : '' )
1129     htmlText += '''
1130     <style type="text/css">code{white-space: pre;}</style>
1131 '''
1132     htmlText += ((cssFile != null) ? cssFile.text : '')
1133     htmlText += '''</head>
1134   <body>
1135 '''
1136     htmlText += htmlBody
1137     htmlText += '''
1138   </body>
1139 </html>
1140 '''
1141
1142     def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1143     def htmlFile = file(htmlFilePath)
1144     println("Creating ${htmlFilePath}")
1145     htmlFile.text = htmlText
1146   }
1147 }
1148
1149
1150 task copyDocs(type: Copy) {
1151   def inputDir = "${jalviewDir}/${doc_dir}"
1152   def outputDir = "${docBuildDir}/${doc_dir}"
1153   from(inputDir) {
1154     include('**/*.txt')
1155     include('**/*.md')
1156     include('**/*.html')
1157     include('**/*.xml')
1158     filter(ReplaceTokens,
1159       beginToken: '$$',
1160       endToken: '$$',
1161       tokens: [
1162         'Version-Rel': JALVIEW_VERSION,
1163         'Year-Rel': getDate("yyyy")
1164       ]
1165     )
1166   }
1167   from(inputDir) {
1168     exclude('**/*.txt')
1169     exclude('**/*.md')
1170     exclude('**/*.html')
1171     exclude('**/*.xml')
1172   }
1173   into outputDir
1174
1175   inputs.dir(inputDir)
1176   outputs.dir(outputDir)
1177 }
1178
1179
1180 task convertMdFiles {
1181   dependsOn copyDocs
1182   def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1183   def cssFile = file("${jalviewDir}/${flexmark_css}")
1184
1185   doLast {
1186     convertMdToHtml(mdFiles, cssFile)
1187   }
1188
1189   inputs.files(mdFiles)
1190   inputs.file(cssFile)
1191
1192   def htmlFiles = []
1193   mdFiles.each { mdFile ->
1194     def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1195     htmlFiles.add(file(htmlFilePath))
1196   }
1197   outputs.files(htmlFiles)
1198 }
1199
1200
1201 task copyHelp(type: Copy) {
1202   def inputDir = helpSourceDir
1203   def outputDir = "${helpBuildDir}/${help_dir}"
1204   from(inputDir) {
1205     include('**/*.txt')
1206     include('**/*.md')
1207     include('**/*.html')
1208     include('**/*.hs')
1209     include('**/*.xml')
1210     include('**/*.jhm')
1211     filter(ReplaceTokens,
1212       beginToken: '$$',
1213       endToken: '$$',
1214       tokens: [
1215         'Version-Rel': JALVIEW_VERSION,
1216         'Year-Rel': getDate("yyyy")
1217       ]
1218     )
1219   }
1220   from(inputDir) {
1221     exclude('**/*.txt')
1222     exclude('**/*.md')
1223     exclude('**/*.html')
1224     exclude('**/*.hs')
1225     exclude('**/*.xml')
1226     exclude('**/*.jhm')
1227   }
1228   into outputDir
1229
1230   inputs.dir(inputDir)
1231   outputs.files(helpFile)
1232   outputs.dir(outputDir)
1233 }
1234
1235
1236 task copyResources(type: Copy) {
1237   group = "build"
1238   description = "Copy (and make text substitutions in) the resources dir to the build area"
1239
1240   def inputDir = resourceDir
1241   def outputDir = resourcesBuildDir
1242   from(inputDir) {
1243     include('**/*.txt')
1244     include('**/*.md')
1245     include('**/*.html')
1246     include('**/*.xml')
1247     filter(ReplaceTokens,
1248       beginToken: '$$',
1249       endToken: '$$',
1250       tokens: [
1251         'Version-Rel': JALVIEW_VERSION,
1252         'Year-Rel': getDate("yyyy")
1253       ]
1254     )
1255   }
1256   from(inputDir) {
1257     exclude('**/*.txt')
1258     exclude('**/*.md')
1259     exclude('**/*.html')
1260     exclude('**/*.xml')
1261   }
1262   into outputDir
1263
1264   inputs.dir(inputDir)
1265   outputs.dir(outputDir)
1266 }
1267
1268 task copyChannelResources(type: Copy) {
1269   dependsOn copyResources
1270   group = "build"
1271   description = "Copy the channel resources dir to the build resources area"
1272
1273   def inputDir = "${channelDir}/${resource_dir}"
1274   def outputDir = resourcesBuildDir
1275   from inputDir
1276   into outputDir
1277
1278   inputs.dir(inputDir)
1279   outputs.dir(outputDir)
1280 }
1281
1282 task createBuildProperties(type: WriteProperties) {
1283   dependsOn copyResources
1284   group = "build"
1285   description = "Create the ${buildProperties} file"
1286   
1287   inputs.dir(sourceDir)
1288   inputs.dir(resourcesBuildDir)
1289   outputFile (buildProperties)
1290   // taking time specific comment out to allow better incremental builds
1291   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1292   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1293   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1294   property "VERSION", JALVIEW_VERSION
1295   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1296   if (getdownSetAppBaseProperty) {
1297     property "GETDOWNAPPBASE", getdownAppBase
1298     property "GETDOWNAPPDISTDIR", getdownAppDistDir
1299   }
1300   outputs.file(outputFile)
1301 }
1302
1303
1304 task buildIndices(type: JavaExec) {
1305   dependsOn copyHelp
1306   classpath = sourceSets.main.compileClasspath
1307   main = "com.sun.java.help.search.Indexer"
1308   workingDir = "${helpBuildDir}/${help_dir}"
1309   def argDir = "html"
1310   args = [ argDir ]
1311   inputs.dir("${workingDir}/${argDir}")
1312
1313   outputs.dir("${classesDir}/doc")
1314   outputs.dir("${classesDir}/help")
1315   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1316   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1317   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1318   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1319   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1320   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1321 }
1322
1323 task buildResources {
1324   dependsOn copyResources
1325   dependsOn copyChannelResources
1326   dependsOn createBuildProperties
1327 }
1328
1329 task prepare {
1330   dependsOn buildResources
1331   dependsOn copyDocs
1332   dependsOn copyHelp
1333   dependsOn convertMdFiles
1334   dependsOn buildIndices
1335 }
1336
1337
1338 compileJava.dependsOn prepare
1339 run.dependsOn compileJava
1340 //run.dependsOn prepare
1341
1342
1343 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
1344 test {
1345   dependsOn prepare
1346
1347   if (useClover) {
1348     dependsOn cloverClasses
1349    } else { //?
1350     dependsOn compileJava //?
1351   }
1352
1353   useTestNG() {
1354     includeGroups testng_groups
1355     excludeGroups testng_excluded_groups
1356     preserveOrder true
1357     useDefaultListeners=true
1358   }
1359
1360   maxHeapSize = "1024m"
1361
1362   workingDir = jalviewDir
1363   def testLaf = project.findProperty("test_laf")
1364   if (testLaf != null) {
1365     println("Setting Test LaF to '${testLaf}'")
1366     systemProperty "laf", testLaf
1367   }
1368   def testHiDPIScale = project.findProperty("test_HiDPIScale")
1369   if (testHiDPIScale != null) {
1370     println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1371     systemProperty "sun.java2d.uiScale", testHiDPIScale
1372   }
1373   sourceCompatibility = compile_source_compatibility
1374   targetCompatibility = compile_target_compatibility
1375   jvmArgs += additional_compiler_args
1376
1377   doFirst {
1378     if (useClover) {
1379       println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1380     }
1381   }
1382 }
1383
1384
1385 task compileLinkCheck(type: JavaCompile) {
1386   options.fork = true
1387   classpath = files("${jalviewDir}/${utils_dir}")
1388   destinationDir = file("${jalviewDir}/${utils_dir}")
1389   source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
1390
1391   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1392   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1393   outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
1394   outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
1395 }
1396
1397
1398 task linkCheck(type: JavaExec) {
1399   dependsOn prepare
1400   dependsOn compileLinkCheck
1401
1402   def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
1403   classpath = files("${jalviewDir}/${utils_dir}")
1404   main = "HelpLinksChecker"
1405   workingDir = jalviewDir
1406   args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
1407
1408   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
1409   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1410     outFOS,
1411     System.out)
1412   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1413     outFOS,
1414     System.err)
1415
1416   inputs.dir(helpBuildDir)
1417   outputs.file(helpLinksCheckerOutFile)
1418 }
1419
1420
1421 // import the pubhtmlhelp target
1422 ant.properties.basedir = "${jalviewDir}"
1423 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
1424 ant.importBuild "${utils_dir}/publishHelp.xml"
1425
1426
1427 task cleanPackageDir(type: Delete) {
1428   doFirst {
1429     delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
1430   }
1431 }
1432
1433
1434 jar {
1435   dependsOn prepare
1436   dependsOn linkCheck
1437
1438   manifest {
1439     attributes "Main-Class": main_class,
1440     "Permissions": "all-permissions",
1441     "Application-Name": applicationName,
1442     "Codebase": application_codebase,
1443     "Implementation-Version": JALVIEW_VERSION
1444   }
1445
1446   def outputDir = "${jalviewDir}/${package_dir}"
1447   destinationDirectory = file(outputDir)
1448   archiveFileName = rootProject.name+".jar"
1449   duplicatesStrategy "EXCLUDE"
1450
1451
1452   exclude "cache*/**"
1453   exclude "*.jar"
1454   exclude "*.jar.*"
1455   exclude "**/*.jar"
1456   exclude "**/*.jar.*"
1457
1458   inputs.dir(sourceSets.main.java.outputDir)
1459   sourceSets.main.resources.srcDirs.each{ dir ->
1460     inputs.dir(dir)
1461   }
1462   outputs.file("${outputDir}/${archiveFileName}")
1463 }
1464
1465
1466 task copyJars(type: Copy) {
1467   from fileTree(dir: classesDir, include: "**/*.jar").files
1468   into "${jalviewDir}/${package_dir}"
1469 }
1470
1471
1472 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
1473 task syncJars(type: Sync) {
1474   dependsOn jar
1475   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
1476   into "${jalviewDir}/${package_dir}"
1477   preserve {
1478     include jar.archiveFileName.getOrNull()
1479   }
1480 }
1481
1482
1483 task makeDist {
1484   group = "build"
1485   description = "Put all required libraries in dist"
1486   // order of "cleanPackageDir", "copyJars", "jar" important!
1487   jar.mustRunAfter cleanPackageDir
1488   syncJars.mustRunAfter cleanPackageDir
1489   dependsOn cleanPackageDir
1490   dependsOn syncJars
1491   dependsOn jar
1492   outputs.dir("${jalviewDir}/${package_dir}")
1493 }
1494
1495
1496 task cleanDist {
1497   dependsOn cleanPackageDir
1498   dependsOn cleanTest
1499   dependsOn clean
1500 }
1501
1502
1503 shadowJar {
1504   group = "distribution"
1505   description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
1506   if (buildDist) {
1507     dependsOn makeDist
1508   }
1509   from ("${jalviewDir}/${libDistDir}") {
1510     include("*.jar")
1511   }
1512   manifest {
1513     attributes "Implementation-Version": JALVIEW_VERSION,
1514     "Application-Name": applicationName
1515   }
1516
1517   duplicatesStrategy "INCLUDE"
1518
1519   mainClassName = shadow_jar_main_class
1520   mergeServiceFiles()
1521   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
1522   minimize()
1523 }
1524
1525
1526 task getdownWebsite() {
1527   group = "distribution"
1528   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
1529   if (buildDist) {
1530     dependsOn makeDist
1531   }
1532
1533   def getdownWebsiteResourceFilenames = []
1534   def getdownResourceDir = getdownResourceDir
1535   def getdownResourceFilenames = []
1536
1537   doFirst {
1538     // clean the getdown website and files dir before creating getdown folders
1539     delete getdownAppBaseDir
1540     delete getdownFilesDir
1541
1542     copy {
1543       from buildProperties
1544       rename(file(buildProperties).getName(), getdown_build_properties)
1545       into getdownAppDir
1546     }
1547     getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
1548
1549     copy {
1550       from channelPropsFile
1551       into getdownAppBaseDir
1552     }
1553     getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
1554
1555     // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
1556     def props = project.properties.sort { it.key }
1557     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
1558       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
1559     }
1560     if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
1561       props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
1562     }
1563     if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
1564       props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
1565     }
1566     if (getdownImagesDir != null && file(getdownImagesDir).exists()) {
1567       props.put("getdown_txt_ui.background_image", "${getdownImagesDir}/${getdown_background_image}")
1568       props.put("getdown_txt_ui.instant_background_image", "${getdownImagesDir}/${getdown_instant_background_image}")
1569       props.put("getdown_txt_ui.error_background", "${getdownImagesDir}/${getdown_error_background}")
1570       props.put("getdown_txt_ui.progress_image", "${getdownImagesDir}/${getdown_progress_image}")
1571       props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
1572       props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
1573     }
1574
1575     props.put("getdown_txt_title", jalview_name)
1576     props.put("getdown_txt_ui.name", applicationName)
1577
1578     // start with appbase
1579     getdownTextLines += "appbase = ${getdownAppBase}"
1580     props.each{ prop, val ->
1581       if (prop.startsWith("getdown_txt_") && val != null) {
1582         if (prop.startsWith("getdown_txt_multi_")) {
1583           def key = prop.substring(18)
1584           val.split(",").each{ v ->
1585             def line = "${key} = ${v}"
1586             getdownTextLines += line
1587           }
1588         } else {
1589           // file values rationalised
1590           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1591             def r = null
1592             if (val.indexOf('/') == 0) {
1593               // absolute path
1594               r = file(val)
1595             } else if (val.indexOf('/') > 0) {
1596               // relative path (relative to jalviewDir)
1597               r = file( "${jalviewDir}/${val}" )
1598             }
1599             if (r.exists()) {
1600               val = "${getdown_resource_dir}/" + r.getName()
1601               getdownWebsiteResourceFilenames += val
1602               getdownResourceFilenames += r.getPath()
1603             }
1604           }
1605           if (! prop.startsWith("getdown_txt_resource")) {
1606             def line = prop.substring(12) + " = ${val}"
1607             getdownTextLines += line
1608           }
1609         }
1610       }
1611     }
1612
1613     getdownWebsiteResourceFilenames.each{ filename ->
1614       getdownTextLines += "resource = ${filename}"
1615     }
1616     getdownResourceFilenames.each{ filename ->
1617       copy {
1618         from filename
1619         into getdownResourceDir
1620       }
1621     }
1622     
1623     def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
1624     getdownWrapperScripts.each{ script ->
1625       def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
1626       if (s.exists()) {
1627         copy {
1628           from s
1629           into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
1630         }
1631         getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}"
1632       }
1633     }
1634
1635     def codeFiles = []
1636     fileTree(file(package_dir)).each{ f ->
1637       if (f.isDirectory()) {
1638         def files = fileTree(dir: f, include: ["*"]).getFiles()
1639         codeFiles += files
1640       } else if (f.exists()) {
1641         codeFiles += f
1642       }
1643     }
1644     def jalviewJar = jar.archiveFileName.getOrNull()
1645     // put jalview.jar first for CLASSPATH and .properties files reasons
1646     codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
1647       def name = f.getName()
1648       def line = "code = ${getdownAppDistDir}/${name}"
1649       getdownTextLines += line
1650       copy {
1651         from f.getPath()
1652         into getdownAppDir
1653       }
1654     }
1655
1656     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
1657     /*
1658     if (JAVA_VERSION.equals("11")) {
1659     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
1660     j11libFiles.sort().each{f ->
1661     def name = f.getName()
1662     def line = "code = ${getdown_j11lib_dir}/${name}"
1663     getdownTextLines += line
1664     copy {
1665     from f.getPath()
1666     into getdownJ11libDir
1667     }
1668     }
1669     }
1670      */
1671
1672     // 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.
1673     //getdownTextLines += "class = " + file(getdownLauncher).getName()
1674     getdownTextLines += "resource = ${getdown_launcher_new}"
1675     getdownTextLines += "class = ${main_class}"
1676     // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
1677     if (getdownSetAppBaseProperty) {
1678       getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
1679       getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
1680     }
1681
1682     def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
1683     getdownTxt.write(getdownTextLines.join("\n"))
1684
1685     getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
1686     def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
1687     launchJvl.write("appbase=${getdownAppBase}")
1688
1689     // files going into the getdown website dir: getdown-launcher.jar
1690     copy {
1691       from getdownLauncher
1692       rename(file(getdownLauncher).getName(), getdown_launcher_new)
1693       into getdownAppBaseDir
1694     }
1695
1696     // files going into the getdown website dir: getdown-launcher(-local).jar
1697     copy {
1698       from getdownLauncher
1699       if (file(getdownLauncher).getName() != getdown_launcher) {
1700         rename(file(getdownLauncher).getName(), getdown_launcher)
1701       }
1702       into getdownAppBaseDir
1703     }
1704
1705     // files going into the getdown website dir: ./install dir and files
1706     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
1707       copy {
1708         from getdownTxt
1709         from getdownLauncher
1710         from "${getdownAppDir}/${getdown_build_properties}"
1711         if (file(getdownLauncher).getName() != getdown_launcher) {
1712           rename(file(getdownLauncher).getName(), getdown_launcher)
1713         }
1714         into getdownInstallDir
1715       }
1716
1717       // and make a copy in the getdown files dir (these are not downloaded by getdown)
1718       copy {
1719         from getdownInstallDir
1720         into getdownFilesInstallDir
1721       }
1722     }
1723
1724     // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
1725     copy {
1726       from getdownTxt
1727       from launchJvl
1728       from getdownLauncher
1729       from "${getdownAppBaseDir}/${getdown_build_properties}"
1730       from "${getdownAppBaseDir}/${channel_props}"
1731       if (file(getdownLauncher).getName() != getdown_launcher) {
1732         rename(file(getdownLauncher).getName(), getdown_launcher)
1733       }
1734       into getdownFilesDir
1735     }
1736
1737     // and ./resource (not all downloaded by getdown)
1738     copy {
1739       from getdownResourceDir
1740       into "${getdownFilesDir}/${getdown_resource_dir}"
1741     }
1742   }
1743
1744   if (buildDist) {
1745     inputs.dir("${jalviewDir}/${package_dir}")
1746   }
1747   outputs.dir(getdownAppBaseDir)
1748   outputs.dir(getdownFilesDir)
1749 }
1750
1751
1752 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
1753 task getdownDigestDir(type: JavaExec) {
1754   group "Help"
1755   description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
1756
1757   def digestDirPropertyName = "DIGESTDIR"
1758   doFirst {
1759     classpath = files(getdownLauncher)
1760     def digestDir = findProperty(digestDirPropertyName)
1761     if (digestDir == null) {
1762       throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
1763     }
1764     args digestDir
1765   }
1766   main = "com.threerings.getdown.tools.Digester"
1767 }
1768
1769
1770 task getdownDigest(type: JavaExec) {
1771   group = "distribution"
1772   description = "Digest the getdown website folder"
1773   dependsOn getdownWebsite
1774   doFirst {
1775     classpath = files(getdownLauncher)
1776   }
1777   main = "com.threerings.getdown.tools.Digester"
1778   args getdownAppBaseDir
1779   inputs.dir(getdownAppBaseDir)
1780   outputs.file("${getdownAppBaseDir}/digest2.txt")
1781 }
1782
1783
1784 task getdown() {
1785   group = "distribution"
1786   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1787   dependsOn getdownDigest
1788   doLast {
1789     if (reportRsyncCommand) {
1790       def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
1791       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1792       println "LIKELY RSYNC COMMAND:"
1793       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1794       if (RUNRSYNC == "true") {
1795         exec {
1796           commandLine "mkdir", "-p", toDir
1797         }
1798         exec {
1799           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1800         }
1801       }
1802     }
1803   }
1804 }
1805
1806
1807 task getdownArchiveBuild() {
1808   group = "distribution"
1809   description = "Put files in the archive dir to go on the website"
1810
1811   dependsOn getdownWebsite
1812
1813   def v = "v${JALVIEW_VERSION_UNDERSCORES}"
1814   def vDir = "${getdownArchiveDir}/${v}"
1815   getdownFullArchiveDir = "${vDir}/getdown"
1816   getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
1817
1818   def vAltDir = "alt_${v}"
1819   def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
1820
1821   doFirst {
1822     // cleanup old "old" dir
1823     delete getdownArchiveDir
1824
1825     def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
1826     getdownArchiveTxt.getParentFile().mkdirs()
1827     def getdownArchiveTextLines = []
1828     def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
1829
1830     // the libdir
1831     copy {
1832       from "${getdownAppBaseDir}/${getdownAppDistDir}"
1833       into "${getdownFullArchiveDir}/${vAltDir}"
1834     }
1835
1836     getdownTextLines.each { line ->
1837       line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
1838       line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
1839       line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
1840       line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
1841       line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
1842       line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
1843       // remove the existing resource = resource/ or bin/ lines
1844       if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
1845         getdownArchiveTextLines += line
1846       }
1847     }
1848
1849     // the resource dir -- add these files as resource lines in getdown.txt
1850     copy {
1851       from "${archiveImagesDir}"
1852       into "${getdownFullArchiveDir}/${getdown_resource_dir}"
1853       eachFile { file ->
1854         getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
1855       }
1856     }
1857
1858     getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
1859
1860     def vLaunchJvl = file(getdownVersionLaunchJvl)
1861     vLaunchJvl.getParentFile().mkdirs()
1862     vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
1863     def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
1864     def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
1865     // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
1866     //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
1867     java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
1868
1869     // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
1870     copy {
1871       from getdownLauncher
1872       from "${getdownAppBaseDir}/${getdownLaunchJvl}"
1873       from "${getdownAppBaseDir}/${getdown_launcher_new}"
1874       from "${getdownAppBaseDir}/${channel_props}"
1875       if (file(getdownLauncher).getName() != getdown_launcher) {
1876         rename(file(getdownLauncher).getName(), getdown_launcher)
1877       }
1878       into getdownFullArchiveDir
1879     }
1880
1881   }
1882 }
1883
1884 task getdownArchiveDigest(type: JavaExec) {
1885   group = "distribution"
1886   description = "Digest the getdown archive folder"
1887
1888   dependsOn getdownArchiveBuild
1889
1890   doFirst {
1891     classpath = files(getdownLauncher)
1892     args getdownFullArchiveDir
1893   }
1894   main = "com.threerings.getdown.tools.Digester"
1895   inputs.dir(getdownFullArchiveDir)
1896   outputs.file("${getdownFullArchiveDir}/digest2.txt")
1897 }
1898
1899 task getdownArchive() {
1900   group = "distribution"
1901   description = "Build the website archive dir with getdown digest"
1902
1903   dependsOn getdownArchiveBuild
1904   dependsOn getdownArchiveDigest
1905 }
1906
1907 tasks.withType(JavaCompile) {
1908         options.encoding = 'UTF-8'
1909 }
1910
1911
1912 clean {
1913   doFirst {
1914     delete getdownAppBaseDir
1915     delete getdownFilesDir
1916     delete getdownArchiveDir
1917   }
1918 }
1919
1920
1921 install4j {
1922   if (file(install4jHomeDir).exists()) {
1923     // good to go!
1924   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
1925     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1926   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
1927     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
1928   }
1929   installDir(file(install4jHomeDir))
1930
1931   mediaTypes = Arrays.asList(install4j_media_types.split(","))
1932 }
1933
1934
1935 task copyInstall4jTemplate {
1936   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
1937   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
1938   inputs.file(install4jTemplateFile)
1939   inputs.file(install4jFileAssociationsFile)
1940   inputs.property("CHANNEL", { CHANNEL })
1941   outputs.file(install4jConfFile)
1942
1943   doLast {
1944     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
1945
1946     // turn off code signing if no OSX_KEYPASS
1947     if (OSX_KEYPASS == "") {
1948       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
1949         codeSigning.'@macEnabled' = "false"
1950       }
1951       install4jConfigXml.'**'.windows.each { windows ->
1952         windows.'@runPostProcessor' = "false"
1953       }
1954     }
1955
1956     // disable install screen for OSX dmg (for 2.11.2.0)
1957     install4jConfigXml.'**'.macosArchive.each { macosArchive -> 
1958       macosArchive.attributes().remove('executeSetupApp')
1959       macosArchive.attributes().remove('setupAppId')
1960     }
1961
1962     // turn off checksum creation for LOCAL channel
1963     def e = install4jConfigXml.application[0]
1964     e.'@createChecksums' = string(install4jCheckSums)
1965
1966     // put file association actions where placeholder action is
1967     def install4jFileAssociationsText = install4jFileAssociationsFile.text
1968     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
1969     install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
1970       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
1971         def parent = a.parent()
1972         parent.remove(a)
1973         fileAssociationActions.each { faa ->
1974             parent.append(faa)
1975         }
1976         // don't need to continue in .any loop once replacements have been made
1977         return true
1978       }
1979     }
1980
1981     // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
1982     // NB we're deleting the /other/ one!
1983     // Also remove the examples subdir from non-release versions
1984     def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
1985     // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
1986     if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
1987       customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
1988     } else {
1989       // remove the examples subdir from Full File Set
1990       def files = install4jConfigXml.files[0]
1991       def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
1992       def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
1993       def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
1994       def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
1995       dirEntry.parent().remove(dirEntry)
1996     }
1997     install4jConfigXml.'**'.action.any { a ->
1998       if (a.'@customizedId' == customizedIdToDelete) {
1999         def parent = a.parent()
2000         parent.remove(a)
2001         return true
2002       }
2003     }
2004
2005     // write install4j file
2006     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2007   }
2008 }
2009
2010
2011 clean {
2012   doFirst {
2013     delete install4jConfFile
2014   }
2015 }
2016
2017 task cleanInstallersDataFiles {
2018   def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2019   def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2020   def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2021   doFirst {
2022     delete installersOutputTxt
2023     delete installersSha256
2024     delete hugoDataJsonFile
2025   }
2026 }
2027
2028 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2029   group = "distribution"
2030   description = "Create the install4j installers"
2031   dependsOn getdown
2032   dependsOn copyInstall4jTemplate
2033   dependsOn cleanInstallersDataFiles
2034
2035   projectFile = install4jConfFile
2036
2037   // create an md5 for the input files to use as version for install4j conf file
2038   def digest = MessageDigest.getInstance("MD5")
2039   digest.update(
2040     (file("${install4jDir}/${install4j_template}").text + 
2041     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2042     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2043   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2044   if (filesMd5.length() >= 8) {
2045     filesMd5 = filesMd5.substring(0,8)
2046   }
2047   def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2048
2049   variables = [
2050     'JALVIEW_NAME': jalview_name,
2051     'JALVIEW_APPLICATION_NAME': applicationName,
2052     'JALVIEW_DIR': "../..",
2053     'OSX_KEYSTORE': OSX_KEYSTORE,
2054     'OSX_APPLEID': OSX_APPLEID,
2055     'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2056     'JSIGN_SH': JSIGN_SH,
2057     'JRE_DIR': getdown_app_dir_java,
2058     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2059     'JALVIEW_VERSION': JALVIEW_VERSION,
2060     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2061     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2062     'JAVA_VERSION': JAVA_VERSION,
2063     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2064     'VERSION': JALVIEW_VERSION,
2065     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
2066     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
2067     'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
2068     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
2069     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
2070     'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
2071     'COPYRIGHT_MESSAGE': install4j_copyright_message,
2072     'BUNDLE_ID': install4jBundleId,
2073     'INTERNAL_ID': install4jInternalId,
2074     'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2075     'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2076     'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
2077     'WRAPPER_LINK': getdownWrapperLink,
2078     'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2079     'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2080     'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2081     'INSTALLER_NAME': install4jInstallerName,
2082     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2083     'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2084     'GETDOWN_FILES_DIR': getdown_files_dir,
2085     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2086     'GETDOWN_DIST_DIR': getdownAppDistDir,
2087     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2088     'GETDOWN_INSTALL_DIR': getdown_install_dir,
2089     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2090     'BUILD_DIR': install4jBuildDir,
2091     'APPLICATION_CATEGORIES': install4j_application_categories,
2092     'APPLICATION_FOLDER': install4jApplicationFolder,
2093     'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2094     'EXECUTABLE_NAME': install4jExecutableName,
2095     'EXTRA_SCHEME': install4jExtraScheme,
2096     'MAC_ICONS_FILE': install4jMacIconsFile,
2097     'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2098     'PNG_ICON_FILE': install4jPngIconFile,
2099     'BACKGROUND': install4jBackground,
2100
2101   ]
2102
2103   //println("INSTALL4J VARIABLES:")
2104   //variables.each{k,v->println("${k}=${v}")}
2105
2106   destination = "${jalviewDir}/${install4jBuildDir}"
2107   buildSelected = true
2108
2109   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2110     faster = true
2111     disableSigning = true
2112     disableNotarization = true
2113   }
2114
2115   if (OSX_KEYPASS) {
2116     macKeystorePassword = OSX_KEYPASS
2117   } 
2118   
2119   if (OSX_ALTOOLPASS) {
2120     appleIdPassword = OSX_ALTOOLPASS
2121     disableNotarization = false
2122   } else {
2123     disableNotarization = true
2124   }
2125
2126   doFirst {
2127     println("Using projectFile "+projectFile)
2128     if (!disableNotarization) { println("Will notarize OSX App DMG") }
2129   }
2130   //verbose=true
2131
2132   inputs.dir(getdownAppBaseDir)
2133   inputs.file(install4jConfFile)
2134   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2135   inputs.dir(macosJavaVMDir)
2136   inputs.dir(windowsJavaVMDir)
2137   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2138 }
2139
2140 def getDataHash(File myFile) {
2141   HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2142   return myFile.exists()
2143   ? [
2144       "file" : myFile.getName(),
2145       "filesize" : myFile.length(),
2146       "sha256" : hash.toString()
2147     ]
2148   : null
2149 }
2150
2151 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2152   def hash = [
2153     "channel" : getdownChannelName,
2154     "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2155     "git-commit" : "${gitHash} [${gitBranch}]",
2156     "version" : JALVIEW_VERSION
2157   ]
2158   // install4j installer files
2159   if (installersOutputTxt.exists()) {
2160     def idHash = [:]
2161     installersOutputTxt.readLines().each { def line ->
2162       if (line.startsWith("#")) {
2163         return;
2164       }
2165       line.replaceAll("\n","")
2166       def vals = line.split("\t")
2167       def filename = vals[3]
2168       def filesize = file(filename).length()
2169       filename = filename.replaceAll(/^.*\//, "")
2170       hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2171       idHash."${filename}" = vals[0]
2172     }
2173     if (install4jCheckSums && installersSha256.exists()) {
2174       installersSha256.readLines().each { def line ->
2175         if (line.startsWith("#")) {
2176           return;
2177         }
2178         line.replaceAll("\n","")
2179         def vals = line.split(/\s+\*?/)
2180         def filename = vals[1]
2181         def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
2182       }
2183     }
2184   }
2185
2186   [
2187     "JAR": shadowJar.archiveFile, // executable JAR
2188     "JVL": getdownVersionLaunchJvl, // version JVL
2189     "SOURCE": sourceDist.archiveFile // source TGZ
2190   ].each { key, value ->
2191     def file = file(value)
2192     if (file.exists()) {
2193       def fileHash = getDataHash(file)
2194       if (fileHash != null) {
2195         hash."${key}" = fileHash;
2196       }
2197     }
2198   }
2199   return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
2200 }
2201
2202 task staticMakeInstallersJsonFile {
2203   doFirst {
2204     def output = findProperty("i4j_output")
2205     def sha256 = findProperty("i4j_sha256")
2206     def json = findProperty("i4j_json")
2207     if (output == null || sha256 == null || json == null) {
2208       throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
2209     }
2210     writeDataJsonFile(file(output), file(sha256), file(json))
2211   }
2212 }
2213
2214 task installers {
2215   dependsOn installerFiles
2216 }
2217
2218
2219 spotless {
2220   java {
2221     eclipse().configFile(eclipse_codestyle_file)
2222   }
2223 }
2224
2225 task createSourceReleaseProperties(type: WriteProperties) {
2226   group = "distribution"
2227   description = "Create the source RELEASE properties file"
2228   
2229   def sourceTarBuildDir = "${buildDir}/sourceTar"
2230   def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
2231   outputFile (sourceReleasePropertiesFile)
2232
2233   doFirst {
2234     releaseProps.each{ key, val -> property key, val }
2235     property "git.branch", gitBranch
2236     property "git.hash", gitHash
2237   }
2238
2239   outputs.file(outputFile)
2240 }
2241
2242 task sourceDist(type: Tar) {
2243   group "distribution"
2244   description "Create a source .tar.gz file for distribution"
2245
2246   dependsOn createBuildProperties
2247   dependsOn convertMdFiles
2248   dependsOn eclipseAllPreferences
2249   dependsOn createSourceReleaseProperties
2250
2251
2252   def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
2253   archiveFileName = outputFileName
2254   
2255   compression Compression.GZIP
2256   
2257   into project.name
2258
2259   def EXCLUDE_FILES=[
2260     "build/*",
2261     "bin/*",
2262     "test-output/",
2263     "test-reports",
2264     "tests",
2265     "clover*/*",
2266     ".*",
2267     "benchmarking/*",
2268     "**/.*",
2269     "*.class",
2270     "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
2271     "*locales/**",
2272     "utils/InstallAnywhere",
2273     "**/*.log",
2274     "RELEASE",
2275   ] 
2276   def PROCESS_FILES=[
2277     "AUTHORS",
2278     "CITATION",
2279     "FEATURETODO",
2280     "JAVA-11-README",
2281     "FEATURETODO",
2282     "LICENSE",
2283     "**/README",
2284     "THIRDPARTYLIBS",
2285     "TESTNG",
2286     "build.gradle",
2287     "gradle.properties",
2288     "**/*.java",
2289     "**/*.html",
2290     "**/*.xml",
2291     "**/*.gradle",
2292     "**/*.groovy",
2293     "**/*.properties",
2294     "**/*.perl",
2295     "**/*.sh",
2296   ]
2297   def INCLUDE_FILES=[
2298     ".classpath",
2299     ".settings/org.eclipse.buildship.core.prefs",
2300     ".settings/org.eclipse.jdt.core.prefs"
2301   ]
2302
2303   from(jalviewDir) {
2304     exclude (EXCLUDE_FILES)
2305     include (PROCESS_FILES)
2306     filter(ReplaceTokens,
2307       beginToken: '$$',
2308       endToken: '$$',
2309       tokens: [
2310         'Version-Rel': JALVIEW_VERSION,
2311         'Year-Rel': getDate("yyyy")
2312       ]
2313     )
2314   }
2315   from(jalviewDir) {
2316     exclude (EXCLUDE_FILES)
2317     exclude (PROCESS_FILES)
2318     exclude ("appletlib")
2319     exclude ("**/*locales")
2320     exclude ("*locales/**")
2321     exclude ("utils/InstallAnywhere")
2322
2323     exclude (getdown_files_dir)
2324     // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
2325     //exclude (getdown_website_dir)
2326     //exclude (getdown_archive_dir)
2327
2328     // exluding these as not using jars as modules yet
2329     exclude ("${j11modDir}/**/*.jar")
2330   }
2331   from(jalviewDir) {
2332     include(INCLUDE_FILES)
2333   }
2334 //  from (jalviewDir) {
2335 //    // explicit includes for stuff that seemed to not get included
2336 //    include(fileTree("test/**/*."))
2337 //    exclude(EXCLUDE_FILES)
2338 //    exclude(PROCESS_FILES)
2339 //  }
2340
2341   from(file(buildProperties).getParent()) {
2342     include(file(buildProperties).getName())
2343     rename(file(buildProperties).getName(), "build_properties")
2344     filter({ line ->
2345       line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
2346     })
2347   }
2348
2349   def sourceTarBuildDir = "${buildDir}/sourceTar"
2350   from(sourceTarBuildDir) {
2351     // this includes the appended RELEASE properties file
2352   }
2353 }
2354
2355 task dataInstallersJson {
2356   group "website"
2357   description "Create the installers-VERSION.json data file for installer files created"
2358
2359   mustRunAfter installers
2360   mustRunAfter shadowJar
2361   mustRunAfter sourceDist
2362   mustRunAfter getdownArchive
2363
2364   def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2365   def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2366
2367   if (installersOutputTxt.exists()) {
2368     inputs.file(installersOutputTxt)
2369   }
2370   if (install4jCheckSums && installersSha256.exists()) {
2371     inputs.file(installersSha256)
2372   }
2373   [
2374     shadowJar.archiveFile, // executable JAR
2375     getdownVersionLaunchJvl, // version JVL
2376     sourceDist.archiveFile // source TGZ
2377   ].each { fileName ->
2378     if (file(fileName).exists()) {
2379       inputs.file(fileName)
2380     }
2381   }
2382
2383   outputs.file(hugoDataJsonFile)
2384
2385   doFirst {
2386     writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
2387   }
2388 }
2389
2390 def hugoTemplateSubstitutions(String input) {
2391   def output = input
2392   output = output.replaceAll("__DATE__", getDate("yyyy-MM-dd"))
2393   output = output.replaceAll("__CHANNEL__", propertiesChannelName)
2394   output = output.replaceAll("__APPLICATION_NAME__", applicationName)
2395   output = output.replaceAll("__GIT_HASH__", gitHash)
2396   output = output.replaceAll("__GIT_BRANCH__", gitBranch)
2397   output = output.replaceAll("__VERSION__", JALVIEW_VERSION)
2398   output = output.replaceAll("__JAVA_VERSION__", JAVA_VERSION)
2399   output = output.replaceAll("__VERSION_UNDERSCORES__", JALVIEW_VERSION_UNDERSCORES)
2400   return output
2401 }
2402
2403 task hugoTemplates {
2404   group "website"
2405   description "Create partially populated md pages for hugo website build"
2406
2407   def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
2408   def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
2409   def templateFiles = fileTree(dir: hugoTemplatesDir)
2410
2411   doFirst {
2412     // specific release template for version archive
2413     def summary = "${applicationName} ${JALVIEW_VERSION}"
2414     def changes = ""
2415     def oldDate = null
2416     if (CHANNEL == "RELEASE") {
2417       def releasesHtmlFile = file("${helpSourceDir}/${releases_html}")
2418       //HTMLPARSE
2419       def html = releasesHtmlFile.text
2420       def doc = Jsoup.parse(html)
2421       def table = doc.select("table").first()
2422       def headings = table.select("tr").first().select("th").collect { it.text() }
2423       def releaseRow = null
2424       def trs = table.select("tr")
2425       trs.any { tr ->
2426         def tds = tr.select("td")
2427         if (tds.size() == 0)
2428           return false
2429         def releaseTd = tds.first()
2430         if (releaseTd.text().startsWith("${JALVIEW_VERSION} ")) {
2431           releaseRow = tr
2432           return true
2433         }
2434       }
2435
2436       if (releaseRow != null && headings != null && headings.size() == 3) {
2437         def releaseTd = releaseRow.select("td").first()
2438         def spaceIndex = releaseTd.text().indexOf(" ")
2439         if (spaceIndex >= 0) {
2440           oldDate = new Date().parse("dd/MM/yyyy", releaseTd.text().substring(spaceIndex+1))
2441         }
2442         def releaseCells = releaseRow.select("td")
2443         if (releaseCells.size() == 3) {
2444           def title1 = headings[1]
2445           def title2 = headings[2]
2446
2447           def lastDotIndex = JALVIEW_VERSION.lastIndexOf(".")
2448           if (lastDotIndex > 0) {
2449             def patchRelease = JALVIEW_VERSION.substring(lastDotIndex+1) as Integer
2450             def patchReleaseString = null
2451             if (patchRelease == 0) {
2452                 patchReleaseString = "first minor"
2453             } else if (patchRelease == 1) {
2454                 patchReleaseString = "first patch"
2455             } else if (patchRelease == 2) {
2456                 patchReleaseString = "second patch"
2457             } else if (patchRelease == 3) {
2458                 patchReleaseString = "third patch"
2459             } else if (patchRelease == 4) {
2460                 patchReleaseString = "fourth patch"
2461             } else if (patchRelease == 5) {
2462                 patchReleaseString = "fifth patch"
2463             } else if (patchRelease == 6) {
2464                 patchReleaseString = "sixth patch"
2465             } else if (patchRelease == 7) {
2466                 patchReleaseString = "seventh patch"
2467             } else if (patchRelease > 13 && (patchRelease % 10 == 1)) {
2468                 patchReleaseString += "st"
2469             } else if (patchRelease > 13 && (patchRelease % 10 == 2)) {
2470                 patchReleaseString += "nd"
2471             } else if (patchRelease > 13 && (patchRelease % 10 == 3)) {
2472                 patchReleaseString += "rd"
2473             } else if (patchRelease != null) {
2474                 patchReleaseString += "th"
2475             }
2476             summary += (patchReleaseString != null) ? " is the ${patchReleaseString} release in the ${JALVIEW_VERSION.substring(0,lastDotIndex)} series." : ""
2477           }
2478
2479           if (headings[1] != null && headings[1].size() > 0) {
2480             def noheadings = true
2481             releaseCells[1].children().each { e ->
2482               if (e.tagName().toLowerCase() == "ul") {
2483                 e.select("li").each { li ->
2484                   def mdItem = "- "
2485                   li.childNodes().any {n ->
2486                     if (n.nodeName().equals("#comment")) {
2487                       mdItem += "${n} "
2488                       return true
2489                     }
2490                   }
2491                   mdItem += li.text()
2492                   if (noheadings) {
2493                     changes += "\n### ${headings[1]}\n\n"
2494                     noheadings = false
2495                   }
2496                   changes += "${mdItem}\n"
2497                 }
2498               } else if (e.tag() == "em") {
2499                 changes += "\n#### ${e.text()}\n\n"
2500                 noheadings = false
2501               }
2502             }
2503           }
2504
2505           if (headings[2] != null && headings[2].size() > 0) {
2506             changes += "\n### ${headings[2]}\n\n"
2507             releaseCells[2].children().each { e ->
2508               if (e.tagName().toLowerCase() == "ul") {
2509                 e.select("li").each { li ->
2510                   def mdItem = "- "
2511                   li.childNodes().any {n ->
2512                     if (n.nodeName().equals("#comment")) {
2513                       mdItem += "${n} "
2514                       return true
2515                     }
2516                   }
2517                   mdItem += li.text()
2518                   changes += "${mdItem}\n"
2519                 }
2520               } else if (e.tagName().toLowerCase() == "em") {
2521                 changes += "\n#### ${e.text()}\n\n"
2522               }
2523             }
2524           }
2525         }
2526       }
2527     }
2528
2529     templateFiles.each{ templateFile ->
2530       def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
2531       def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
2532       def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
2533
2534       def outPathName = string("${hugoBuildDir}/$newRelPathName")
2535       
2536       copy {
2537         from templateFile
2538         rename(templateFile.getName(), newFileName)
2539         into outPathName
2540       }
2541
2542       def newFile = file("${outPathName}/${newFileName}".toString())
2543       def content = newFile.text
2544       content = content.replaceAll("__SUMMARY__", summary)
2545       content = content.replaceAll("__CHANGES__", changes)
2546       if (oldDate != null) {
2547         content = content.replaceAll("__DATE__", oldDate.format("yyyy-MM-dd"))
2548       }
2549       newFile.text = hugoTemplateSubstitutions(content)
2550     }
2551   }
2552
2553   inputs.dir(hugoTemplatesDir)
2554   inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
2555   inputs.property("CHANNEL", { CHANNEL })
2556 }
2557
2558
2559 task helppages {
2560   dependsOn copyHelp
2561   dependsOn pubhtmlhelp
2562   
2563   inputs.dir("${helpBuildDir}/${help_dir}")
2564   outputs.dir("${buildDir}/distributions/${help_dir}")
2565 }
2566
2567
2568 task j2sSetHeadlessBuild {
2569   doFirst {
2570     IN_ECLIPSE = false
2571   }
2572 }
2573
2574
2575 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
2576   group "jalviewjs"
2577   description "Enable the alternative J2S Config file for headless build"
2578
2579   outputFile = jalviewjsJ2sSettingsFileName
2580   def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
2581   def j2sProps = new Properties()
2582   if (j2sPropsFile.exists()) {
2583     try {
2584       def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
2585       j2sProps.load(j2sPropsFileFIS)
2586       j2sPropsFileFIS.close()
2587
2588       j2sProps.each { prop, val ->
2589         property(prop, val)
2590       }
2591     } catch (Exception e) {
2592       println("Exception reading ${jalviewjsJ2sSettingsFileName}")
2593       e.printStackTrace()
2594     }
2595   }
2596   if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
2597     property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
2598   }
2599 }
2600
2601
2602 task jalviewjsSetEclipseWorkspace {
2603   def propKey = "jalviewjs_eclipse_workspace"
2604   def propVal = null
2605   if (project.hasProperty(propKey)) {
2606     propVal = project.getProperty(propKey)
2607     if (propVal.startsWith("~/")) {
2608       propVal = System.getProperty("user.home") + propVal.substring(1)
2609     }
2610   }
2611   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
2612   def propsFile = file(propsFileName)
2613   def eclipseWsDir = propVal
2614   def props = new Properties()
2615
2616   def writeProps = true
2617   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
2618     def ins = new FileInputStream(propsFileName)
2619     props.load(ins)
2620     ins.close()
2621     if (props.getProperty(propKey, null) != null) {
2622       eclipseWsDir = props.getProperty(propKey)
2623       writeProps = false
2624     }
2625   }
2626
2627   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
2628     def tempDir = File.createTempDir()
2629     eclipseWsDir = tempDir.getAbsolutePath()
2630     writeProps = true
2631   }
2632   eclipseWorkspace = file(eclipseWsDir)
2633
2634   doFirst {
2635     // do not run a headless transpile when we claim to be in Eclipse
2636     if (IN_ECLIPSE) {
2637       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2638       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2639     } else {
2640       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2641     }
2642
2643     if (writeProps) {
2644       props.setProperty(propKey, eclipseWsDir)
2645       propsFile.parentFile.mkdirs()
2646       def bytes = new ByteArrayOutputStream()
2647       props.store(bytes, null)
2648       def propertiesString = bytes.toString()
2649       propsFile.text = propertiesString
2650       print("NEW ")
2651     } else {
2652       print("EXISTING ")
2653     }
2654
2655     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
2656   }
2657
2658   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
2659   outputs.file(propsFileName)
2660   outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
2661 }
2662
2663
2664 task jalviewjsEclipsePaths {
2665   def eclipseProduct
2666
2667   def eclipseRoot = jalviewjs_eclipse_root
2668   if (eclipseRoot.startsWith("~/")) {
2669     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
2670   }
2671   if (OperatingSystem.current().isMacOsX()) {
2672     eclipseRoot += "/Eclipse.app"
2673     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
2674     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
2675   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
2676     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
2677       eclipseRoot += "/eclipse"
2678     }
2679     eclipseBinary = "${eclipseRoot}/eclipse.exe"
2680     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
2681   } else { // linux or unix
2682     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
2683       eclipseRoot += "/eclipse"
2684 println("eclipseDir exists")
2685     }
2686     eclipseBinary = "${eclipseRoot}/eclipse"
2687     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
2688   }
2689
2690   eclipseVersion = "4.13" // default
2691   def assumedVersion = true
2692   if (file(eclipseProduct).exists()) {
2693     def fis = new FileInputStream(eclipseProduct)
2694     def props = new Properties()
2695     props.load(fis)
2696     eclipseVersion = props.getProperty("version")
2697     fis.close()
2698     assumedVersion = false
2699   }
2700   
2701   def propKey = "eclipse_debug"
2702   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
2703
2704   doFirst {
2705     // do not run a headless transpile when we claim to be in Eclipse
2706     if (IN_ECLIPSE) {
2707       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2708       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2709     } else {
2710       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2711     }
2712
2713     if (!assumedVersion) {
2714       println("ECLIPSE VERSION=${eclipseVersion}")
2715     }
2716   }
2717 }
2718
2719
2720 task printProperties {
2721   group "Debug"
2722   description "Output to console all System.properties"
2723   doFirst {
2724     System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
2725   }
2726 }
2727
2728
2729 task eclipseSetup {
2730   dependsOn eclipseProject
2731   dependsOn eclipseClasspath
2732   dependsOn eclipseJdt
2733 }
2734
2735
2736 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
2737 task jalviewjsEclipseCopyDropins(type: Copy) {
2738   dependsOn jalviewjsEclipsePaths
2739
2740   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
2741   inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
2742   def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
2743
2744   from inputFiles
2745   into outputDir
2746 }
2747
2748
2749 // this eclipse -clean doesn't actually work
2750 task jalviewjsCleanEclipse(type: Exec) {
2751   dependsOn eclipseSetup
2752   dependsOn jalviewjsEclipsePaths
2753   dependsOn jalviewjsEclipseCopyDropins
2754
2755   executable(eclipseBinary)
2756   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
2757   if (eclipseDebug) {
2758     args += "-debug"
2759   }
2760   args += "-l"
2761
2762   def inputString = """exit
2763 y
2764 """
2765   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
2766   standardInput = inputByteStream
2767 }
2768
2769 /* not really working yet
2770 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
2771 */
2772
2773
2774 task jalviewjsTransferUnzipSwingJs {
2775   def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
2776
2777   doLast {
2778     copy {
2779       from zipTree(file_zip)
2780       into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2781     }
2782   }
2783
2784   inputs.file file_zip
2785   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2786 }
2787
2788
2789 task jalviewjsTransferUnzipLib {
2790   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
2791
2792   doLast {
2793     zipFiles.each { file_zip -> 
2794       copy {
2795         from zipTree(file_zip)
2796         into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2797       }
2798     }
2799   }
2800
2801   inputs.files zipFiles
2802   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2803 }
2804
2805
2806 task jalviewjsTransferUnzipAllLibs {
2807   dependsOn jalviewjsTransferUnzipSwingJs
2808   dependsOn jalviewjsTransferUnzipLib
2809 }
2810
2811
2812 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
2813   group "JalviewJS"
2814   description "Create the alternative j2s file from the j2s.* properties"
2815
2816   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
2817   def siteDirProperty = "j2s.site.directory"
2818   def setSiteDir = false
2819   jalviewjsJ2sProps.each { prop, val ->
2820     if (val != null) {
2821       if (prop == siteDirProperty) {
2822         if (!(val.startsWith('/') || val.startsWith("file://") )) {
2823           val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
2824         }
2825         setSiteDir = true
2826       }
2827       property(prop,val)
2828     }
2829     if (!setSiteDir) { // default site location, don't override specifically set property
2830       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
2831     }
2832   }
2833   outputFile = jalviewjsJ2sAltSettingsFileName
2834
2835   if (! IN_ECLIPSE) {
2836     inputs.properties(jalviewjsJ2sProps)
2837     outputs.file(jalviewjsJ2sAltSettingsFileName)
2838   }
2839 }
2840
2841
2842 task jalviewjsEclipseSetup {
2843   dependsOn jalviewjsEclipseCopyDropins
2844   dependsOn jalviewjsSetEclipseWorkspace
2845   dependsOn jalviewjsCreateJ2sSettings
2846 }
2847
2848
2849 task jalviewjsSyncAllLibs (type: Sync) {
2850   dependsOn jalviewjsTransferUnzipAllLibs
2851   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
2852   inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
2853   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2854
2855   from inputFiles
2856   into outputDir
2857   def outputFiles = []
2858   rename { filename ->
2859     outputFiles += "${outputDir}/${filename}"
2860     null
2861   }
2862   preserve {
2863     include "**"
2864   }
2865
2866   // should this be exclude really ?
2867   duplicatesStrategy "INCLUDE"
2868
2869   outputs.files outputFiles
2870   inputs.files inputFiles
2871 }
2872
2873
2874 task jalviewjsSyncResources (type: Sync) {
2875   dependsOn buildResources
2876
2877   def inputFiles = fileTree(dir: resourcesBuildDir)
2878   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
2879
2880   from inputFiles
2881   into outputDir
2882   def outputFiles = []
2883   rename { filename ->
2884     outputFiles += "${outputDir}/${filename}"
2885     null
2886   }
2887   preserve {
2888     include "**"
2889   }
2890   outputs.files outputFiles
2891   inputs.files inputFiles
2892 }
2893
2894
2895 task jalviewjsSyncSiteResources (type: Sync) {
2896   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
2897   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2898
2899   from inputFiles
2900   into outputDir
2901   def outputFiles = []
2902   rename { filename ->
2903     outputFiles += "${outputDir}/${filename}"
2904     null
2905   }
2906   preserve {
2907     include "**"
2908   }
2909   outputs.files outputFiles
2910   inputs.files inputFiles
2911 }
2912
2913
2914 task jalviewjsSyncBuildProperties (type: Sync) {
2915   dependsOn createBuildProperties
2916   def inputFiles = [file(buildProperties)]
2917   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
2918
2919   from inputFiles
2920   into outputDir
2921   def outputFiles = []
2922   rename { filename ->
2923     outputFiles += "${outputDir}/${filename}"
2924     null
2925   }
2926   preserve {
2927     include "**"
2928   }
2929   outputs.files outputFiles
2930   inputs.files inputFiles
2931 }
2932
2933
2934 task jalviewjsProjectImport(type: Exec) {
2935   dependsOn eclipseSetup
2936   dependsOn jalviewjsEclipsePaths
2937   dependsOn jalviewjsEclipseSetup
2938
2939   doFirst {
2940     // do not run a headless import when we claim to be in Eclipse
2941     if (IN_ECLIPSE) {
2942       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2943       throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2944     } else {
2945       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2946     }
2947   }
2948
2949   //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
2950   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
2951   executable(eclipseBinary)
2952   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
2953   if (eclipseDebug) {
2954     args += "-debug"
2955   }
2956   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
2957   if (!IN_ECLIPSE) {
2958     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
2959     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
2960   }
2961
2962   inputs.file("${jalviewDir}/.project")
2963   outputs.upToDateWhen { 
2964     file(projdir).exists()
2965   }
2966 }
2967
2968
2969 task jalviewjsTranspile(type: Exec) {
2970   dependsOn jalviewjsEclipseSetup 
2971   dependsOn jalviewjsProjectImport
2972   dependsOn jalviewjsEclipsePaths
2973   if (!IN_ECLIPSE) {
2974     dependsOn jalviewjsEnableAltFileProperty
2975   }
2976
2977   doFirst {
2978     // do not run a headless transpile when we claim to be in Eclipse
2979     if (IN_ECLIPSE) {
2980       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2981       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2982     } else {
2983       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2984     }
2985   }
2986
2987   executable(eclipseBinary)
2988   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
2989   if (eclipseDebug) {
2990     args += "-debug"
2991   }
2992   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
2993   if (!IN_ECLIPSE) {
2994     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
2995     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
2996   }
2997
2998   def stdout
2999   def stderr
3000   doFirst {
3001     stdout = new ByteArrayOutputStream()
3002     stderr = new ByteArrayOutputStream()
3003
3004     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3005     def logOutFile = file(logOutFileName)
3006     logOutFile.createNewFile()
3007     logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3008 BINARY: ${eclipseBinary}
3009 VERSION: ${eclipseVersion}
3010 WORKSPACE: ${eclipseWorkspace}
3011 DEBUG: ${eclipseDebug}
3012 ----
3013 """
3014     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3015     // combine stdout and stderr
3016     def logErrFOS = logOutFOS
3017
3018     if (jalviewjs_j2s_to_console.equals("true")) {
3019       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3020         new org.apache.tools.ant.util.TeeOutputStream(
3021           logOutFOS,
3022           stdout),
3023         System.out)
3024       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3025         new org.apache.tools.ant.util.TeeOutputStream(
3026           logErrFOS,
3027           stderr),
3028         System.err)
3029     } else {
3030       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3031         logOutFOS,
3032         stdout)
3033       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3034         logErrFOS,
3035         stderr)
3036     }
3037   }
3038
3039   doLast {
3040     if (stdout.toString().contains("Error processing ")) {
3041       // j2s did not complete transpile
3042       //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3043       if (jalviewjs_ignore_transpile_errors.equals("true")) {
3044         println("IGNORING TRANSPILE ERRORS")
3045         println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3046       } else {
3047         throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3048       }
3049     }
3050   }
3051
3052   inputs.dir("${jalviewDir}/${sourceDir}")
3053   outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3054   outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3055 }
3056
3057
3058 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3059
3060   def stdout = new ByteArrayOutputStream()
3061   def stderr = new ByteArrayOutputStream()
3062
3063   def coreFile = file(jsfile)
3064   def msg = ""
3065   msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3066   println(msg)
3067   logOutFile.createNewFile()
3068   logOutFile.append(msg+"\n")
3069
3070   def coreTop = file(prefixFile)
3071   def coreBottom = file(suffixFile)
3072   coreFile.getParentFile().mkdirs()
3073   coreFile.createNewFile()
3074   coreFile.write( coreTop.getText("UTF-8") )
3075   list.each {
3076     f ->
3077     if (f.exists()) {
3078       def t = f.getText("UTF-8")
3079       t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3080       coreFile.append( t )
3081     } else {
3082       msg = "...file '"+f.getPath()+"' does not exist, skipping"
3083       println(msg)
3084       logOutFile.append(msg+"\n")
3085     }
3086   }
3087   coreFile.append( coreBottom.getText("UTF-8") )
3088
3089   msg = "Generating ${zjsfile}"
3090   println(msg)
3091   logOutFile.append(msg+"\n")
3092   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3093   def logErrFOS = logOutFOS
3094
3095   javaexec {
3096     classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3097     main = "com.google.javascript.jscomp.CommandLineRunner"
3098     jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3099     args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3100     maxHeapSize = "2g"
3101
3102     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3103     println(msg)
3104     logOutFile.append(msg+"\n")
3105
3106     if (logOutConsole) {
3107       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3108         new org.apache.tools.ant.util.TeeOutputStream(
3109           logOutFOS,
3110           stdout),
3111         standardOutput)
3112         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3113           new org.apache.tools.ant.util.TeeOutputStream(
3114             logErrFOS,
3115             stderr),
3116           System.err)
3117     } else {
3118       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3119         logOutFOS,
3120         stdout)
3121         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3122           logErrFOS,
3123           stderr)
3124     }
3125   }
3126   msg = "--"
3127   println(msg)
3128   logOutFile.append(msg+"\n")
3129 }
3130
3131
3132 task jalviewjsBuildAllCores {
3133   group "JalviewJS"
3134   description "Build the core js lib closures listed in the classlists dir"
3135   dependsOn jalviewjsTranspile
3136   dependsOn jalviewjsTransferUnzipSwingJs
3137
3138   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3139   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3140   def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3141   def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3142   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3143   def prefixFile = "${jsDir}/core/coretop2.js"
3144   def suffixFile = "${jsDir}/core/corebottom2.js"
3145
3146   inputs.file prefixFile
3147   inputs.file suffixFile
3148
3149   def classlistFiles = []
3150   // add the classlists found int the jalviewjs_classlists_dir
3151   fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3152     file ->
3153     def name = file.getName() - ".txt"
3154     classlistFiles += [
3155       'file': file,
3156       'name': name
3157     ]
3158   }
3159
3160   // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3161   //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3162   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3163
3164   jalviewjsCoreClasslists = []
3165
3166   classlistFiles.each {
3167     hash ->
3168
3169     def file = hash['file']
3170     if (! file.exists()) {
3171       //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3172       return false // this is a "continue" in groovy .each closure
3173     }
3174     def name = hash['name']
3175     if (name == null) {
3176       name = file.getName() - ".txt"
3177     }
3178
3179     def filelist = []
3180     file.eachLine {
3181       line ->
3182         filelist += line
3183     }
3184     def list = fileTree(dir: j2sDir, includes: filelist)
3185
3186     def jsfile = "${outputDir}/core${name}.js"
3187     def zjsfile = "${outputDir}/core${name}.z.js"
3188
3189     jalviewjsCoreClasslists += [
3190       'jsfile': jsfile,
3191       'zjsfile': zjsfile,
3192       'list': list,
3193       'name': name
3194     ]
3195
3196     inputs.file(file)
3197     inputs.files(list)
3198     outputs.file(jsfile)
3199     outputs.file(zjsfile)
3200   }
3201   
3202   // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3203   def stevesoftClasslistName = "_stevesoft"
3204   def stevesoftClasslist = [
3205     'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3206     'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3207     'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3208     'name': stevesoftClasslistName
3209   ]
3210   jalviewjsCoreClasslists += stevesoftClasslist
3211   inputs.files(stevesoftClasslist['list'])
3212   outputs.file(stevesoftClasslist['jsfile'])
3213   outputs.file(stevesoftClasslist['zjsfile'])
3214
3215   // _all core
3216   def allClasslistName = "_all"
3217   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3218   allJsFiles += fileTree(
3219     dir: libJ2sDir,
3220     include: "**/*.js",
3221     excludes: [
3222       // these exlusions are files that the closure-compiler produces errors for. Should fix them
3223       "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3224       "**/org/jmol/export/JSExporter.js"
3225     ]
3226   )
3227   allJsFiles += fileTree(
3228     dir: swingJ2sDir,
3229     include: "**/*.js",
3230     excludes: [
3231       // these exlusions are files that the closure-compiler produces errors for. Should fix them
3232       "**/sun/misc/Unsafe.js",
3233       "**/swingjs/jquery/jquery-editable-select.js",
3234       "**/swingjs/jquery/j2sComboBox.js",
3235       "**/sun/misc/FloatingDecimal.js"
3236     ]
3237   )
3238   def allClasslist = [
3239     'jsfile': "${outputDir}/core${allClasslistName}.js",
3240     'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3241     'list': allJsFiles,
3242     'name': allClasslistName
3243   ]
3244   // not including this version of "all" core at the moment
3245   //jalviewjsCoreClasslists += allClasslist
3246   inputs.files(allClasslist['list'])
3247   outputs.file(allClasslist['jsfile'])
3248   outputs.file(allClasslist['zjsfile'])
3249
3250   doFirst {
3251     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
3252     logOutFile.getParentFile().mkdirs()
3253     logOutFile.createNewFile()
3254     logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
3255
3256     jalviewjsCoreClasslists.each {
3257       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
3258     }
3259   }
3260
3261 }
3262
3263
3264 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
3265   copy {
3266     from inputFile
3267     into file(outputFile).getParentFile()
3268     rename { filename ->
3269       if (filename.equals(inputFile.getName())) {
3270         return file(outputFile).getName()
3271       }
3272       return null
3273     }
3274     filter(ReplaceTokens,
3275       beginToken: '_',
3276       endToken: '_',
3277       tokens: [
3278         'MAIN': '"'+main_class+'"',
3279         'CODE': "null",
3280         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
3281         'COREKEY': jalviewjs_core_key,
3282         'CORENAME': coreName
3283       ]
3284     )
3285   }
3286 }
3287
3288
3289 task jalviewjsPublishCoreTemplates {
3290   dependsOn jalviewjsBuildAllCores
3291   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
3292   def inputFile = file(inputFileName)
3293   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3294
3295   def outputFiles = []
3296   jalviewjsCoreClasslists.each { cl ->
3297     def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
3298     cl['outputfile'] = outputFile
3299     outputFiles += outputFile
3300   }
3301
3302   doFirst {
3303     jalviewjsCoreClasslists.each { cl ->
3304       jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
3305     }
3306   }
3307   inputs.file(inputFile)
3308   outputs.files(outputFiles)
3309 }
3310
3311
3312 task jalviewjsSyncCore (type: Sync) {
3313   dependsOn jalviewjsBuildAllCores
3314   dependsOn jalviewjsPublishCoreTemplates
3315   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
3316   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3317
3318   from inputFiles
3319   into outputDir
3320   def outputFiles = []
3321   rename { filename ->
3322     outputFiles += "${outputDir}/${filename}"
3323     null
3324   }
3325   preserve {
3326     include "**"
3327   }
3328   outputs.files outputFiles
3329   inputs.files inputFiles
3330 }
3331
3332
3333 // this Copy version of TransferSiteJs will delete anything else in the target dir
3334 task jalviewjsCopyTransferSiteJs(type: Copy) {
3335   dependsOn jalviewjsTranspile
3336   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3337   into "${jalviewDir}/${jalviewjsSiteDir}"
3338 }
3339
3340
3341 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
3342 task jalviewjsSyncTransferSiteJs(type: Sync) {
3343   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3344   include "**/*.*"
3345   into "${jalviewDir}/${jalviewjsSiteDir}"
3346   preserve {
3347     include "**"
3348   }
3349 }
3350
3351
3352 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
3353 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
3354 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
3355 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
3356
3357 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
3358 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
3359 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
3360 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
3361
3362
3363 task jalviewjsPrepareSite {
3364   group "JalviewJS"
3365   description "Prepares the website folder including unzipping files and copying resources"
3366   dependsOn jalviewjsSyncAllLibs
3367   dependsOn jalviewjsSyncResources
3368   dependsOn jalviewjsSyncSiteResources
3369   dependsOn jalviewjsSyncBuildProperties
3370   dependsOn jalviewjsSyncCore
3371 }
3372
3373
3374 task jalviewjsBuildSite {
3375   group "JalviewJS"
3376   description "Builds the whole website including transpiled code"
3377   dependsOn jalviewjsCopyTransferSiteJs
3378   dependsOn jalviewjsPrepareSite
3379 }
3380
3381
3382 task cleanJalviewjsTransferSite {
3383   doFirst {
3384     delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3385     delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3386     delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3387     delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3388   }
3389 }
3390
3391
3392 task cleanJalviewjsSite {
3393   dependsOn cleanJalviewjsTransferSite
3394   doFirst {
3395     delete "${jalviewDir}/${jalviewjsSiteDir}"
3396   }
3397 }
3398
3399
3400 task jalviewjsSiteTar(type: Tar) {
3401   group "JalviewJS"
3402   description "Creates a tar.gz file for the website"
3403   dependsOn jalviewjsBuildSite
3404   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
3405   archiveFileName = outputFilename
3406
3407   compression Compression.GZIP
3408
3409   from "${jalviewDir}/${jalviewjsSiteDir}"
3410   into jalviewjs_site_dir // this is inside the tar file
3411
3412   inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
3413 }
3414
3415
3416 task jalviewjsServer {
3417   group "JalviewJS"
3418   def filename = "jalviewjsTest.html"
3419   description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
3420   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
3421   doLast {
3422
3423     def factory
3424     try {
3425       def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
3426       factory = f.newInstance()
3427     } catch (ClassNotFoundException e) {
3428       throw new GradleException("Unable to create SimpleHttpFileServerFactory")
3429     }
3430     def port = Integer.valueOf(jalviewjs_server_port)
3431     def start = port
3432     def running = false
3433     def url
3434     def jalviewjsServer
3435     while(port < start+1000 && !running) {
3436       try {
3437         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
3438         jalviewjsServer = factory.start(doc_root, port)
3439         running = true
3440         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
3441         println("SERVER STARTED with document root ${doc_root}.")
3442         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
3443         println("For debug: "+url+"?j2sdebug")
3444         println("For verbose: "+url+"?j2sverbose")
3445       } catch (Exception e) {
3446         port++;
3447       }
3448     }
3449     def htmlText = """
3450       <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
3451       <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
3452       <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
3453       """
3454     jalviewjsCoreClasslists.each { cl ->
3455       def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
3456       htmlText += """
3457       <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
3458       """
3459       println("For core ${cl.name}: "+urlcore)
3460     }
3461
3462     file(htmlFile).text = htmlText
3463   }
3464
3465   outputs.file(htmlFile)
3466   outputs.upToDateWhen({false})
3467 }
3468
3469
3470 task cleanJalviewjsAll {
3471   group "JalviewJS"
3472   description "Delete all configuration and build artifacts to do with JalviewJS build"
3473   dependsOn cleanJalviewjsSite
3474   dependsOn jalviewjsEclipsePaths
3475   
3476   doFirst {
3477     delete "${jalviewDir}/${jalviewjsBuildDir}"
3478     delete "${jalviewDir}/${eclipse_bin_dir}"
3479     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
3480       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
3481     }
3482     delete jalviewjsJ2sAltSettingsFileName
3483   }
3484
3485   outputs.upToDateWhen( { false } )
3486 }
3487
3488
3489 task jalviewjsIDE_checkJ2sPlugin {
3490   group "00 JalviewJS in Eclipse"
3491   description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
3492
3493   doFirst {
3494     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
3495     def j2sPluginFile = file(j2sPlugin)
3496     def eclipseHome = System.properties["eclipse.home.location"]
3497     if (eclipseHome == null || ! IN_ECLIPSE) {
3498       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
3499     }
3500     def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
3501     def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
3502     if (altPluginsDir != null && file(altPluginsDir).exists()) {
3503       eclipseJ2sPluginDirs += altPluginsDir
3504     }
3505     def foundPlugin = false
3506     def j2sPluginFileName = j2sPluginFile.getName()
3507     def eclipseJ2sPlugin
3508     def eclipseJ2sPluginFile
3509     eclipseJ2sPluginDirs.any { dir ->
3510       eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
3511       eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
3512       if (eclipseJ2sPluginFile.exists()) {
3513         foundPlugin = true
3514         return true
3515       }
3516     }
3517     if (!foundPlugin) {
3518       def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
3519       System.err.println(msg)
3520       throw new StopExecutionException(msg)
3521     }
3522
3523     def digest = MessageDigest.getInstance("MD5")
3524
3525     digest.update(j2sPluginFile.text.bytes)
3526     def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
3527
3528     digest.update(eclipseJ2sPluginFile.text.bytes)
3529     def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
3530      
3531     if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
3532       def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
3533       System.err.println(msg)
3534       throw new StopExecutionException(msg)
3535     } else {
3536       def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
3537       println(msg)
3538     }
3539   }
3540 }
3541
3542 task jalviewjsIDE_copyJ2sPlugin {
3543   group "00 JalviewJS in Eclipse"
3544   description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
3545
3546   doFirst {
3547     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
3548     def j2sPluginFile = file(j2sPlugin)
3549     def eclipseHome = System.properties["eclipse.home.location"]
3550     if (eclipseHome == null || ! IN_ECLIPSE) {
3551       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
3552     }
3553     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
3554     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
3555     def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
3556     System.err.println(msg)
3557     copy {
3558       from j2sPlugin
3559       eclipseJ2sPluginFile.getParentFile().mkdirs()
3560       into eclipseJ2sPluginFile.getParent()
3561     }
3562   }
3563 }
3564
3565
3566 task jalviewjsIDE_j2sFile {
3567   group "00 JalviewJS in Eclipse"
3568   description "Creates the .j2s file"
3569   dependsOn jalviewjsCreateJ2sSettings
3570 }
3571
3572
3573 task jalviewjsIDE_SyncCore {
3574   group "00 JalviewJS in Eclipse"
3575   description "Build the core js lib closures listed in the classlists dir and publish core html from template"
3576   dependsOn jalviewjsSyncCore
3577 }
3578
3579
3580 task jalviewjsIDE_SyncSiteAll {
3581   dependsOn jalviewjsSyncAllLibs
3582   dependsOn jalviewjsSyncResources
3583   dependsOn jalviewjsSyncSiteResources
3584   dependsOn jalviewjsSyncBuildProperties
3585 }
3586
3587
3588 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
3589
3590
3591 task jalviewjsIDE_PrepareSite {
3592   group "00 JalviewJS in Eclipse"
3593   description "Sync libs and resources to site dir, but not closure cores"
3594
3595   dependsOn jalviewjsIDE_SyncSiteAll
3596   //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
3597 }
3598
3599
3600 task jalviewjsIDE_AssembleSite {
3601   group "00 JalviewJS in Eclipse"
3602   description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
3603   dependsOn jalviewjsPrepareSite
3604 }
3605
3606
3607 task jalviewjsIDE_SiteClean {
3608   group "00 JalviewJS in Eclipse"
3609   description "Deletes the Eclipse transpiled site"
3610   dependsOn cleanJalviewjsSite
3611 }
3612
3613
3614 task jalviewjsIDE_Server {
3615   group "00 JalviewJS in Eclipse"
3616   description "Starts a webserver on localhost to test the website"
3617   dependsOn jalviewjsServer
3618 }
3619
3620
3621 // buildship runs this at import or gradle refresh
3622 task eclipseSynchronizationTask {
3623   //dependsOn eclipseSetup
3624   dependsOn createBuildProperties
3625   if (J2S_ENABLED) {
3626     dependsOn jalviewjsIDE_j2sFile
3627     dependsOn jalviewjsIDE_checkJ2sPlugin
3628     dependsOn jalviewjsIDE_PrepareSite
3629   }
3630 }
3631
3632
3633 // buildship runs this at build time or project refresh
3634 task eclipseAutoBuildTask {
3635   //dependsOn jalviewjsIDE_checkJ2sPlugin
3636   //dependsOn jalviewjsIDE_PrepareSite
3637 }
3638
3639
3640 task jalviewjs {
3641   group "JalviewJS"
3642   description "Build the site"
3643   dependsOn jalviewjsBuildSite
3644 }