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