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