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