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