JAL-3210 Bringing in improvements to build.gradle
[jalview.git] / build.gradle
1 import org.apache.tools.ant.filters.ReplaceTokens
2 import org.gradle.internal.os.OperatingSystem
3 import org.gradle.plugins.ide.eclipse.model.Output
4 import org.gradle.plugins.ide.eclipse.model.Library
5 import java.security.MessageDigest
6 import groovy.transform.ExternalizeMethods
7 import groovy.util.XmlParser
8 import groovy.xml.XmlUtil
9
10 buildscript {
11   dependencies {
12     classpath 'org.openclover:clover:4.3.1'
13   }
14 }
15
16 plugins {
17   id 'java'
18   id 'application'
19   id 'eclipse'
20   id 'com.github.johnrengelman.shadow' version '4.0.3'
21   id 'com.install4j.gradle' version '8.0.2'
22   id 'com.dorongold.task-tree' version '1.4' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
23 }
24
25 repositories {
26   jcenter()
27   mavenCentral()
28   mavenLocal()
29   flatDir {
30     dirs gradlePluginsDir
31   }
32 }
33
34 dependencies {
35   compile 'org.apache.commons:commons-compress:1.18'
36 }
37
38
39 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
40 def string(Object o) {
41   return o.toString()
42 }
43
44
45 ext {
46   jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
47   jalviewDirRelativePath = jalviewDir
48
49   // local build environment properties
50   def localProps = "${jalviewDirAbsolutePath}/local.properties"
51   if (file(localProps).exists()) {
52     try {
53       def p = new Properties()
54       def localPropsFIS = new FileInputStream(localProps)
55       p.load(localPropsFIS)
56       localPropsFIS.close()
57       p.each {
58         key, val -> 
59           def over = getProperty(key) != null
60           setProperty(key, val)
61           if (over) {
62             println("Overriding property '${key}' with local.properties value '${val}'")
63           }
64       }
65     } catch (Exception e) {
66       System.out.println("Exception reading local.properties")
67     }
68   }
69
70   // this property set when running Eclipse headlessly
71   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
72   // this property set by Eclipse
73   eclipseApplicationProperty = string("eclipse.application")
74   // CHECK IF RUNNING FROM WITHIN ECLIPSE
75   def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
76   IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
77   // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
78   if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
79     println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
80     IN_ECLIPSE = false
81   }
82   if (IN_ECLIPSE) {
83     println("WITHIN ECLIPSE IDE")
84   } else {
85     println("HEADLESS BUILD")
86   }
87
88   J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
89   if (J2S_ENABLED) {
90     println("J2S ENABLED")
91   }
92
93   /* *-/
94   System.properties.sort { it.key }.each {
95     key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
96   }
97   /-* *-/
98   if (false && IN_ECLIPSE) {
99     jalviewDir = jalviewDirAbsolutePath
100   }
101   */
102
103   // essentials
104   bareSourceDir = string(source_dir)
105   sourceDir = string("${jalviewDir}/${bareSourceDir}")
106   resourceDir = string("${jalviewDir}/${resource_dir}")
107   bareTestSourceDir = string(test_source_dir)
108   testSourceDir = string("${jalviewDir}/${bareTestSourceDir}")
109
110   // clover
111   cloverInstrDir = file("${buildDir}/${cloverSourcesInstrDir}")
112   classesDir = string("${jalviewDir}/${classes_dir}")
113   if (clover.equals("true")) {
114     use_clover = true
115     classesDir = string("${buildDir}/${cloverClassesDir}")
116   } else {
117     use_clover = false
118     classesDir = string("${jalviewDir}/${classes_dir}")
119   }
120
121   classes = classesDir
122
123   getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
124   getdownDir = string("")
125   reportRsyncCmd = false
126   buildDist = true
127   buildProperties = build_properties_file
128   getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
129   switch (CHANNEL) {
130
131     case "BUILD":
132     // TODO: get bamboo build artifact URL for getdown artifacts
133     getdown_channel_base = bamboo_channelbase
134     getdown_channel_name = string("${bamboo_planKey}/${JAVA_VERSION}")
135     getdown_app_base = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
136     getdown_app_dir = getdown_app_dir_alt
137     buildProperties = string("${classesDir}/${build_properties_file}")
138     break
139
140     case "RELEASE":
141     getdown_channel_name = CHANNEL.toLowerCase()
142     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
143     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
144     getdown_app_dir = getdown_app_dir_release
145     buildProperties = string("${classesDir}/${build_properties_file}")
146     reportRsyncCommand = true
147     break
148
149     case "ARCHIVE":
150     getdown_channel_name = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
151     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
152     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
153     getdown_app_dir = getdown_app_dir_alt
154     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
155       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
156       exit
157     } else {
158       packageDir = string("${ARCHIVEDIR}/${packageDir}")
159       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
160       buildDist = false
161     }
162     reportRsyncCommand = true
163     break
164
165     case "ARCHIVELOCAL":
166     getdown_channel_name = string("archive/${JALVIEW_VERSION}")
167     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
168     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
169     getdown_app_dir = getdown_app_dir_alt
170     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
171       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
172       exit
173     } else {
174       packageDir = string("${ARCHIVEDIR}/${packageDir}")
175       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
176       buildDist = false
177     }
178     reportRsyncCommand = true
179     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
180     break
181
182     case "DEVELOP":
183     getdown_channel_name = CHANNEL.toLowerCase()
184     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
185     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
186     getdown_app_dir = getdown_app_dir_alt
187     buildProperties = string("${classesDir}/${build_properties_file}")
188     reportRsyncCommand = true
189     break
190
191     case "TEST-RELEASE":
192     getdown_channel_name = CHANNEL.toLowerCase()
193     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
194     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
195     getdown_app_dir = getdown_app_dir_alt
196     buildProperties = string("${classesDir}/${build_properties_file}")
197     reportRsyncCommand = true
198     break
199
200     case ~/^SCRATCH(|-[-\w]*)$/:
201     getdown_channel_name = CHANNEL
202     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
203     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
204     getdown_app_dir = getdown_app_dir_alt
205     buildProperties = string("${classesDir}/${build_properties_file}")
206     reportRsyncCommand = true
207     break
208
209     case "LOCAL":
210     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
211     getdown_app_dir = getdown_app_dir_alt
212     buildProperties = string("${classesDir}/${build_properties_file}")
213     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
214     break
215
216     default: // something wrong specified
217     print("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
218     exit
219     break
220
221   }
222
223   getdownAppDir = string("${getdownWebsiteDir}/${getdown_app_dir}")
224   //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
225   getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
226   getdownInstallDir = string("${getdownWebsiteDir}/${getdown_install_dir}")
227   getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
228   getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
229   /* compile without modules -- using classpath libraries
230   modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
231   modules_runtimeClasspath = modules_compileClasspath
232   */
233   gitHash = string("")
234   gitBranch = string("")
235
236   println("Using a ${CHANNEL} profile.")
237
238   additional_compiler_args = []
239   // configure classpath/args for j8/j11 compilation
240   if (JAVA_VERSION.equals("1.8")) {
241     JAVA_INTEGER_VERSION = string("8")
242     //libDir = j8libDir
243     libDir = j11libDir
244     libDistDir = j8libDir
245     compile_source_compatibility = 1.8
246     compile_target_compatibility = 1.8
247     // these are getdown.txt properties defined dependent on the JAVA_VERSION
248     getdown_alt_java_min_version = getdown_alt_java8_min_version
249     getdown_alt_java_max_version = getdown_alt_java8_max_version
250     // this property is assigned below and expanded to multiple lines in the getdown task
251     getdown_alt_multi_java_location = getdown_alt_java8_txt_multi_java_location
252     // this property is for the Java library used in eclipse
253     eclipse_java_runtime_name = string("JavaSE-1.8")
254   } else if (JAVA_VERSION.equals("11")) {
255     JAVA_INTEGER_VERSION = string("11")
256     libDir = j11libDir
257     libDistDir = j11libDir
258     compile_source_compatibility = 11
259     compile_target_compatibility = 11
260     getdown_alt_java_min_version = getdown_alt_java11_min_version
261     getdown_alt_java_max_version = getdown_alt_java11_max_version
262     getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
263     eclipse_java_runtime_name = string("JavaSE-11")
264     /* compile without modules -- using classpath libraries
265     additional_compiler_args += [
266     '--module-path', modules_compileClasspath.asPath,
267     '--add-modules', j11modules
268     ]
269      */
270   } else if (JAVA_VERSION.equals("12") || JAVA_VERSION.equals("13")) {
271     JAVA_INTEGER_VERSION = JAVA_VERSION
272     libDir = j11libDir
273     libDistDir = j11libDir
274     compile_source_compatibility = JAVA_VERSION
275     compile_target_compatibility = JAVA_VERSION
276     getdown_alt_java_min_version = getdown_alt_java11_min_version
277     getdown_alt_java_max_version = getdown_alt_java11_max_version
278     getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
279     eclipse_java_runtime_name = string("JavaSE-11")
280     /* compile without modules -- using classpath libraries
281     additional_compiler_args += [
282     '--module-path', modules_compileClasspath.asPath,
283     '--add-modules', j11modules
284     ]
285      */
286   } else {
287     throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
288   }
289
290
291   // for install4j
292   JAVA_MIN_VERSION = JAVA_VERSION
293   JAVA_MAX_VERSION = JAVA_VERSION
294   macosJavaVMDir = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/getdown/macos-jre${JAVA_VERSION}/jre")
295   macosJavaVMTgz = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre${JAVA_VERSION}.tar.gz")
296   windowsJavaVMDir = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/getdown/windows-jre${JAVA_VERSION}/jre")
297   windowsJavaVMTgz = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre${JAVA_VERSION}.tar.gz")
298   install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
299   install4jConfFileName = string("jalview-install4j-conf.install4j")
300   install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
301   install4jHomeDir = install4j_home_dir
302   if (install4jHomeDir.startsWith("~/")) {
303     install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
304   }
305
306
307
308   buildingHTML = string("${jalviewDir}/${docDir}/building.html")
309   helpFile = string("${classesDir}/${help_dir}/help.jhm")
310   helpParentDir = string("${jalviewDir}/${help_parent_dir}")
311   helpSourceDir = string("${helpParentDir}/${help_dir}")
312
313
314   relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
315   jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
316   jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
317   if (IN_ECLIPSE) {
318     jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
319   } else {
320     jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
321   }
322   jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
323   jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
324   jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
325   jalviewjsJalviewCoreHtmlFile = string("")
326   jalviewjsJalviewCoreName = string(jalviewjs_core_name)
327   jalviewjsCoreClasslists = []
328   jalviewjsJalviewTemplateName = string(jalviewjs_name)
329   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
330   jalviewjsJ2sProps = null
331
332   eclipseWorkspace = null
333   eclipseBinary = string("")
334   eclipseVersion = string("")
335   eclipseDebug = false
336   // ENDEXT
337 }
338
339
340 sourceSets {
341   main {
342     java {
343       srcDirs sourceDir
344       outputDir = file(classesDir)
345     }
346
347     resources {
348       srcDirs resourceDir
349       srcDirs += helpParentDir
350     }
351
352     jar.destinationDir = file("${jalviewDir}/${packageDir}")
353
354     compileClasspath = files(sourceSets.main.java.outputDir)
355     //compileClasspath += files(sourceSets.main.resources.srcDirs)
356     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
357
358     runtimeClasspath = compileClasspath
359   }
360
361   clover {
362     java {
363       srcDirs = [ cloverInstrDir ]
364       outputDir = file("${buildDir}/${cloverClassesDir}")
365     }
366
367     resources {
368       srcDirs = sourceSets.main.resources.srcDirs
369     }
370     compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
371     compileClasspath += files(sourceSets.main.java.outputDir)
372     compileClasspath += sourceSets.main.compileClasspath
373     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"])
374     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
375
376     runtimeClasspath = compileClasspath
377   }
378
379   test {
380     java {
381       srcDirs testSourceDir
382       outputDir = file("${jalviewDir}/${testOutputDir}")
383     }
384
385     resources {
386       srcDirs = sourceSets.main.resources.srcDirs
387     }
388
389     compileClasspath = files( sourceSets.test.java.outputDir )
390
391     if (use_clover) {
392       compileClasspath += sourceSets.clover.compileClasspath
393     } else {
394       compileClasspath += files(sourceSets.main.java.outputDir)
395     }
396
397     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
398     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"])
399     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"])
400
401     runtimeClasspath = compileClasspath
402   }
403 }
404
405
406 // clover bits
407 dependencies {
408   if (use_clover) {
409     cloverCompile 'org.openclover:clover:4.3.1'
410     testCompile 'org.openclover:clover:4.3.1'
411   }
412 }
413
414
415 configurations {
416   cloverRuntime
417   cloverRuntime.extendsFrom cloverCompile
418 }
419
420 eclipse {
421   project {
422     name = eclipse_project_name
423
424     natures 'org.eclipse.jdt.core.javanature',
425     'org.eclipse.jdt.groovy.core.groovyNature',
426     'org.eclipse.buildship.core.gradleprojectnature'
427
428     buildCommand 'org.eclipse.jdt.core.javabuilder'
429     buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
430   }
431
432   classpath {
433     //defaultOutputDir = sourceSets.main.java.outputDir
434     def removeThese = []
435     configurations.each{
436       if (it.isCanBeResolved()) {
437         removeThese += it
438       }
439     }
440
441     minusConfigurations += removeThese
442     plusConfigurations = [ ]
443     file {
444
445       whenMerged { cp ->
446         def removeTheseToo = []
447         HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
448         cp.entries.each { entry ->
449           // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
450           // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
451           // we add the resources and help/help dirs in as libs afterwards (see below)
452           if (entry.kind == 'src') {
453             if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
454               removeTheseToo += entry
455             } else {
456               alreadyAddedSrcPath.putAt(entry.path, true)
457             }
458           }
459
460         }
461         cp.entries.removeAll(removeTheseToo)
462
463         //cp.entries += new Output("${eclipse_bin_dir}/main")
464         if (file(helpParentDir).isDirectory()) {
465           cp.entries += new Library(fileReference(helpParentDir))
466         }
467         if (file(resourceDir).isDirectory()) {
468           cp.entries += new Library(fileReference(resourceDir))
469         }
470
471         HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
472
473         sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
474           //don't want to add outputDir as eclipse is using its own output dir in bin/main
475           if (it.isDirectory() || ! it.exists()) {
476             // don't add dirs to classpath, especially if they don't exist
477             return false // groovy "continue" in .any closure
478           }
479           def itPath = it.toString()
480           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
481             // make relative path
482             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
483           }
484           if (alreadyAddedLibPath.get(itPath)) {
485             //println("Not adding duplicate entry "+itPath)
486           } else {
487             //println("Adding entry "+itPath)
488             cp.entries += new Library(fileReference(itPath))
489             alreadyAddedLibPath.put(itPath, true)
490           }
491         }
492
493         sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
494           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
495           if (it.isDirectory() || ! it.exists()) {
496             // don't add dirs to classpath
497             return false // groovy "continue" in .any closure
498           }
499
500           def itPath = it.toString()
501           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
502             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
503           }
504           if (alreadyAddedLibPath.get(itPath)) {
505             // don't duplicate
506           } else {
507             def lib = new Library(fileReference(itPath))
508             lib.entryAttributes["test"] = "true"
509             cp.entries += lib
510             alreadyAddedLibPath.put(itPath, true)
511           }
512         }
513
514       } // whenMerged
515
516     } // file
517
518     containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
519
520   } // classpath
521
522   jdt {
523     // for the IDE, use java 11 compatibility
524     sourceCompatibility = compile_source_compatibility
525     targetCompatibility = compile_target_compatibility
526     javaRuntimeName = eclipse_java_runtime_name
527
528     // add in jalview project specific properties/preferences into eclipse core preferences
529     file {
530       withProperties { props ->
531         def jalview_prefs = new Properties()
532         def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
533         jalview_prefs.load(ins)
534         ins.close()
535         jalview_prefs.forEach { t, v ->
536           if (props.getAt(t) == null) {
537             props.putAt(t, v)
538           }
539         }
540       }
541     }
542
543   } // jdt
544
545   if (IN_ECLIPSE) {
546     // Don't want these to be activated if in headless build
547     synchronizationTasks "eclipseSynchronizationTask"
548     //autoBuildTasks "eclipseAutoBuildTask"
549
550   }
551 }
552
553
554 task cloverInstr() {
555   // only instrument source, we build test classes as normal
556   inputs.files files (sourceSets.main.allJava) // , fileTree(dir: testSourceDir, include: ["**/*.java"]))
557   outputs.dir cloverInstrDir
558
559   doFirst {
560     delete cloverInstrDir
561     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
562     "-d", "${buildDir}/${cloverSourcesInstrDir}"]
563     argsList.addAll(inputs.files.files.collect({ file ->
564       file.absolutePath
565     }))
566     String[] args = argsList.toArray()
567     println("About to instrument "+args.length +" files")
568     com.atlassian.clover.CloverInstr.mainImpl(args)
569   }
570 }
571
572
573 task cloverReport {
574   group = "Verification"
575     description = "Createst the Clover report"
576     inputs.dir "${buildDir}/clover"
577     outputs.dir "${reportsDir}/clover"
578     onlyIf {
579       file("${buildDir}/clover/clover.db").exists()
580     }
581   doFirst {
582     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
583     "-o", "${reportsDir}/clover"]
584     String[] args = argsList.toArray()
585     com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
586
587     // and generate ${reportsDir}/clover/clover.xml
588     args = ["--initstring", "${buildDir}/clover/clover.db",
589     "-o", "${reportsDir}/clover/clover.xml"].toArray()
590     com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
591   }
592 }
593 // end clover bits
594
595
596 compileJava {
597
598   doFirst {
599     sourceCompatibility = compile_source_compatibility
600     targetCompatibility = compile_target_compatibility
601     options.compilerArgs = additional_compiler_args
602     print ("Setting target compatibility to "+targetCompatibility+"\n")
603   }
604
605 }
606
607
608 compileTestJava {
609   if (use_clover) {
610     dependsOn compileCloverJava
611     classpath += configurations.cloverRuntime
612   } else {
613     classpath += sourceSets.main.runtimeClasspath
614   }
615   doFirst {
616     sourceCompatibility = compile_source_compatibility
617     targetCompatibility = compile_target_compatibility
618     options.compilerArgs = additional_compiler_args
619     print ("Setting target compatibility to "+targetCompatibility+"\n")
620   }
621 }
622
623
624 compileCloverJava {
625
626   doFirst {
627     sourceCompatibility = compile_source_compatibility
628     targetCompatibility = compile_target_compatibility
629     options.compilerArgs += additional_compiler_args
630     print ("Setting target compatibility to "+targetCompatibility+"\n")
631   }
632   classpath += configurations.cloverRuntime
633 }
634
635
636 clean {
637   doFirst {
638     delete sourceSets.main.java.outputDir
639   }
640 }
641
642
643 cleanTest {
644   doFirst {
645     delete sourceSets.test.java.outputDir
646     delete cloverInstrDir
647   }
648 }
649
650
651 // format is a string like date.format("dd MMMM yyyy")
652 def getDate(format) {
653   def date = new Date()
654   return date.format(format)
655 }
656
657
658 task setGitVals {
659   def hashStdOut = new ByteArrayOutputStream()
660   exec {
661     commandLine "git", "rev-parse", "--short", "HEAD"
662     standardOutput = hashStdOut
663     ignoreExitValue true
664   }
665
666   def branchStdOut = new ByteArrayOutputStream()
667   exec {
668     commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
669     standardOutput = branchStdOut
670     ignoreExitValue true
671   }
672
673   gitHash = hashStdOut.toString().trim()
674   gitBranch = branchStdOut.toString().trim()
675
676   outputs.upToDateWhen { false }
677 }
678
679
680 task createBuildProperties(type: WriteProperties) {
681   dependsOn setGitVals
682   inputs.dir(sourceDir)
683   inputs.dir(resourceDir)
684   file(buildProperties).getParentFile().mkdirs()
685   outputFile (buildProperties)
686   // taking time specific comment out to allow better incremental builds
687   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
688   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
689   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
690   property "VERSION", JALVIEW_VERSION
691   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
692   outputs.file(outputFile)
693 }
694
695
696 task cleanBuildingHTML(type: Delete) {
697   doFirst {
698     delete buildingHTML
699   }
700 }
701
702
703 task convertBuildingMD(type: Exec) {
704   dependsOn cleanBuildingHTML
705   def buildingMD = "${jalviewDir}/${docDir}/building.md"
706   def css = "${jalviewDir}/${docDir}/github.css"
707
708   def pandoc = null
709   pandoc_exec.split(",").each {
710     if (file(it.trim()).exists()) {
711       pandoc = it.trim()
712       return true
713     }
714   }
715
716   def hostname = "hostname".execute().text.trim()
717   if ((pandoc == null || ! file(pandoc).exists()) && hostname.equals("jv-bamboo")) {
718     pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
719   }
720
721   doFirst {
722     if (pandoc != null && file(pandoc).exists()) {
723         commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
724     } else {
725         println("Cannot find pandoc. Skipping convert building.md to HTML")
726         throw new StopExecutionException("Cannot find pandoc. Skipping convert building.md to HTML")
727     }
728   }
729
730   ignoreExitValue true
731
732   inputs.file(buildingMD)
733   inputs.file(css)
734   outputs.file(buildingHTML)
735 }
736
737
738 clean {
739   doFirst {
740     delete buildingHTML
741   }
742 }
743
744
745 task syncDocs(type: Sync) {
746   dependsOn convertBuildingMD
747   def syncDir = "${classesDir}/${docDir}"
748   from fileTree("${jalviewDir}/${docDir}")
749   into syncDir
750
751 }
752
753
754 task copyHelp(type: Copy) {
755   def inputDir = helpSourceDir
756   def outputDir = "${classesDir}/${help_dir}"
757   from(inputDir) {
758     exclude '**/*.gif'
759     exclude '**/*.jpg'
760     exclude '**/*.png'
761     filter(ReplaceTokens,
762       beginToken: '$$',
763       endToken: '$$',
764       tokens: [
765         'Version-Rel': JALVIEW_VERSION,
766         'Year-Rel': getDate("yyyy")
767       ]
768     )
769   }
770   from(inputDir) {
771     include '**/*.gif'
772     include '**/*.jpg'
773     include '**/*.png'
774   }
775   into outputDir
776
777   inputs.dir(inputDir)
778   outputs.files(helpFile)
779   outputs.dir(outputDir)
780 }
781
782
783 task syncLib(type: Sync) {
784   def syncDir = "${classesDir}/${libDistDir}"
785   from fileTree("${jalviewDir}/${libDistDir}")
786   into syncDir
787 }
788
789
790 task syncResources(type: Sync) {
791   from resourceDir
792   include "**/*.*"
793   into "${classesDir}"
794   preserve {
795     include "**"
796   }
797 }
798
799
800 task prepare {
801   dependsOn syncResources
802   dependsOn syncDocs
803   dependsOn copyHelp
804 }
805
806
807 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
808 test {
809   dependsOn prepare
810   dependsOn compileJava
811   if (use_clover) {
812     dependsOn cloverInstr
813   }
814
815   if (use_clover) {
816     print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
817   }
818
819   useTestNG() {
820     includeGroups testngGroups
821     preserveOrder true
822     useDefaultListeners=true
823   }
824
825   workingDir = jalviewDir
826   //systemProperties 'clover.jar' System.properties.clover.jar
827   sourceCompatibility = compile_source_compatibility
828   targetCompatibility = compile_target_compatibility
829   jvmArgs += additional_compiler_args
830
831 }
832
833
834 task buildIndices(type: JavaExec) {
835   dependsOn copyHelp
836   classpath = sourceSets.main.compileClasspath
837   main = "com.sun.java.help.search.Indexer"
838   workingDir = "${classesDir}/${help_dir}"
839   def argDir = "html"
840   args = [ argDir ]
841   inputs.dir("${workingDir}/${argDir}")
842
843   outputs.dir("${classesDir}/doc")
844   outputs.dir("${classesDir}/help")
845   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
846   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
847   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
848   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
849   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
850   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
851 }
852
853
854 task compileLinkCheck(type: JavaCompile) {
855   options.fork = true
856   classpath = files("${jalviewDir}/${utilsDir}")
857   destinationDir = file("${jalviewDir}/${utilsDir}")
858   source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
859
860   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
861   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
862   outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class")
863   outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class")
864 }
865
866
867 task linkCheck(type: JavaExec) {
868   dependsOn prepare, compileLinkCheck
869
870   def helpLinksCheckerOutFile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.out")
871   classpath = files("${jalviewDir}/${utilsDir}")
872   main = "HelpLinksChecker"
873   workingDir = jalviewDir
874   args = [ "${classesDir}/${help_dir}", "-nointernet" ]
875
876   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
877   def errFOS = outFOS
878   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
879     outFOS,
880     standardOutput)
881   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
882     outFOS,
883     errorOutput)
884
885   inputs.dir("${classesDir}/${help_dir}")
886   outputs.file(helpLinksCheckerOutFile)
887 }
888
889 // import the pubhtmlhelp target
890 ant.properties.basedir = "${jalviewDir}"
891 ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${help_dir}"
892 ant.importBuild "${utilsDir}/publishHelp.xml"
893
894
895 task cleanPackageDir(type: Delete) {
896   doFirst {
897     delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar")
898   }
899 }
900
901 jar {
902   dependsOn linkCheck
903   dependsOn buildIndices
904   dependsOn createBuildProperties
905
906   manifest {
907     attributes "Main-Class": mainClass,
908     "Permissions": "all-permissions",
909     "Application-Name": "Jalview Desktop",
910     "Codebase": application_codebase
911   }
912
913   destinationDir = file("${jalviewDir}/${packageDir}")
914   archiveName = rootProject.name+".jar"
915
916   exclude "cache*/**"
917   exclude "*.jar"
918   exclude "*.jar.*"
919   exclude "**/*.jar"
920   exclude "**/*.jar.*"
921
922   inputs.dir(classesDir)
923   outputs.file("${jalviewDir}/${packageDir}/${archiveName}")
924 }
925
926
927 task copyJars(type: Copy) {
928   from fileTree(dir: classesDir, include: "**/*.jar").files
929   into "${jalviewDir}/${packageDir}"
930 }
931
932
933 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
934 task syncJars(type: Sync) {
935   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
936   into "${jalviewDir}/${packageDir}"
937   preserve {
938     include jar.archiveName
939   }
940 }
941
942
943 task makeDist {
944   group = "build"
945   description = "Put all required libraries in dist"
946   // order of "cleanPackageDir", "copyJars", "jar" important!
947   jar.mustRunAfter cleanPackageDir
948   syncJars.mustRunAfter cleanPackageDir
949   dependsOn cleanPackageDir
950   dependsOn syncJars
951   dependsOn jar
952   outputs.dir("${jalviewDir}/${packageDir}")
953 }
954
955
956 task cleanDist {
957   dependsOn cleanPackageDir
958   dependsOn cleanTest
959   dependsOn clean
960 }
961
962 shadowJar {
963   group = "distribution"
964   if (buildDist) {
965     dependsOn makeDist
966   }
967   from ("${jalviewDir}/${libDistDir}") {
968     include("*.jar")
969   }
970   manifest {
971     attributes 'Implementation-Version': JALVIEW_VERSION
972   }
973   mainClassName = shadowJarMainClass
974   mergeServiceFiles()
975   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
976   minimize()
977 }
978
979
980 task getdownWebsite() {
981   group = "distribution"
982   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
983   if (buildDist) {
984     dependsOn makeDist
985   }
986
987   def getdownWebsiteResourceFilenames = []
988   def getdownTextString = ""
989   def getdownResourceDir = getdownResourceDir
990   def getdownAppDir = getdownAppDir
991   def getdownResourceFilenames = []
992
993   doFirst {
994     // clean the getdown website and files dir before creating getdown folders
995     delete getdownWebsiteDir
996     delete getdownFilesDir
997
998     copy {
999       from buildProperties
1000       rename(build_properties_file, getdown_build_properties)
1001       into getdownAppDir
1002     }
1003     getdownWebsiteResourceFilenames += "${getdown_app_dir}/${getdown_build_properties}"
1004
1005     // go through properties looking for getdown_txt_...
1006     def props = project.properties.sort { it.key }
1007     if (getdown_alt_java_min_version.length() > 0) {
1008       props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
1009     }
1010     if (getdown_alt_java_max_version.length() > 0) {
1011       props.put("getdown_txt_java_max_version", getdown_alt_java_max_version)
1012     }
1013     props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
1014
1015     props.put("getdown_txt_appbase", getdown_app_base)
1016     props.each{ prop, val ->
1017       if (prop.startsWith("getdown_txt_") && val != null) {
1018         if (prop.startsWith("getdown_txt_multi_")) {
1019           def key = prop.substring(18)
1020           val.split(",").each{ v ->
1021             def line = "${key} = ${v}\n"
1022             getdownTextString += line
1023           }
1024         } else {
1025           // file values rationalised
1026           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1027             def r = null
1028             if (val.indexOf('/') == 0) {
1029               // absolute path
1030               r = file(val)
1031             } else if (val.indexOf('/') > 0) {
1032               // relative path (relative to jalviewDir)
1033               r = file( "${jalviewDir}/${val}" )
1034             }
1035             if (r.exists()) {
1036               val = "${getdown_resource_dir}/" + r.getName()
1037               getdownWebsiteResourceFilenames += val
1038               getdownResourceFilenames += r.getPath()
1039             }
1040           }
1041           if (! prop.startsWith("getdown_txt_resource")) {
1042             def line = prop.substring(12) + " = ${val}\n"
1043             getdownTextString += line
1044           }
1045         }
1046       }
1047     }
1048
1049     getdownWebsiteResourceFilenames.each{ filename ->
1050       getdownTextString += "resource = ${filename}\n"
1051     }
1052     getdownResourceFilenames.each{ filename ->
1053       copy {
1054         from filename
1055         into getdownResourceDir
1056       }
1057     }
1058
1059     def codeFiles = []
1060     fileTree(file(packageDir)).each{ f ->
1061       if (f.isDirectory()) {
1062         def files = fileTree(dir: f, include: ["*"]).getFiles()
1063         codeFiles += files
1064       } else if (f.exists()) {
1065         codeFiles += f
1066       }
1067     }
1068     codeFiles.sort().each{f ->
1069       def name = f.getName()
1070       def line = "code = ${getdown_app_dir}/${name}\n"
1071       getdownTextString += line
1072       copy {
1073         from f.getPath()
1074         into getdownAppDir
1075       }
1076     }
1077
1078     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
1079     /*
1080     if (JAVA_VERSION.equals("11")) {
1081     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
1082     j11libFiles.sort().each{f ->
1083     def name = f.getName()
1084     def line = "code = ${getdown_j11lib_dir}/${name}\n"
1085     getdownTextString += line
1086     copy {
1087     from f.getPath()
1088     into getdownJ11libDir
1089     }
1090     }
1091     }
1092      */
1093
1094     // 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.
1095     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
1096     getdownTextString += "resource = ${getdown_launcher_new}\n"
1097     getdownTextString += "class = ${mainClass}\n"
1098
1099     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
1100     getdown_txt.write(getdownTextString)
1101
1102     def launch_jvl = file("${getdownWebsiteDir}/${getdown_launch_jvl}")
1103     launch_jvl.write("appbase="+props.get("getdown_txt_appbase"))
1104
1105     copy {
1106       from getdownLauncher
1107       rename(file(getdownLauncher).getName(), getdown_launcher_new)
1108       into getdownWebsiteDir
1109     }
1110
1111     copy {
1112       from getdownLauncher
1113       if (file(getdownLauncher).getName() != getdown_launcher) {
1114         rename(file(getdownLauncher).getName(), getdown_launcher)
1115       }
1116       into getdownWebsiteDir
1117     }
1118
1119     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
1120       copy {
1121         from getdown_txt
1122         from getdownLauncher
1123         from "${getdownWebsiteDir}/${getdown_build_properties}"
1124         if (file(getdownLauncher).getName() != getdown_launcher) {
1125           rename(file(getdownLauncher).getName(), getdown_launcher)
1126         }
1127         into getdownInstallDir
1128       }
1129
1130       copy {
1131         from getdownInstallDir
1132         into getdownFilesInstallDir
1133       }
1134     }
1135
1136     copy {
1137       from getdown_txt
1138       from launch_jvl
1139       from getdownLauncher
1140       from "${getdownWebsiteDir}/${getdown_build_properties}"
1141       if (file(getdownLauncher).getName() != getdown_launcher) {
1142         rename(file(getdownLauncher).getName(), getdown_launcher)
1143       }
1144       into getdownFilesDir
1145     }
1146
1147     copy {
1148       from getdownResourceDir
1149       into "${getdownFilesDir}/${getdown_resource_dir}"
1150     }
1151   }
1152
1153   if (buildDist) {
1154     inputs.dir("${jalviewDir}/${packageDir}")
1155   }
1156   outputs.dir(getdownWebsiteDir)
1157   outputs.dir(getdownFilesDir)
1158 }
1159
1160
1161 task getdownDigest(type: JavaExec) {
1162   group = "distribution"
1163   description = "Digest the getdown website folder"
1164   dependsOn getdownWebsite
1165   doFirst {
1166     classpath = files("${getdownWebsiteDir}/${getdown_launcher}")
1167   }
1168   main = "com.threerings.getdown.tools.Digester"
1169   args getdownWebsiteDir
1170   inputs.dir(getdownWebsiteDir)
1171   outputs.file("${getdownWebsiteDir}/digest2.txt")
1172 }
1173
1174
1175 task getdown() {
1176   group = "distribution"
1177   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1178   dependsOn getdownDigest
1179   doLast {
1180     if (reportRsyncCommand) {
1181       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
1182       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1183       println "LIKELY RSYNC COMMAND:"
1184       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1185       if (RUNRSYNC == "true") {
1186         exec {
1187           commandLine "mkdir", "-p", toDir
1188         }
1189         exec {
1190           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1191         }
1192       }
1193     }
1194   }
1195 }
1196
1197
1198 clean {
1199   doFirst {
1200     delete getdownWebsiteDir
1201     delete getdownFilesDir
1202   }
1203 }
1204
1205
1206 install4j {
1207   if (file(install4jHomeDir).exists()) {
1208     // good to go!
1209   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
1210     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1211   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
1212     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
1213   }
1214   installDir(file(install4jHomeDir))
1215
1216   mediaTypes = Arrays.asList(install4j_media_types.split(","))
1217 }
1218
1219
1220 task copyInstall4jTemplate {
1221   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
1222   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
1223   inputs.file(install4jTemplateFile)
1224   inputs.file(install4jFileAssociationsFile)
1225   outputs.file(install4jConfFile)
1226
1227   doLast {
1228     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
1229
1230     // turn off code signing if no OSX_KEYPASS
1231     if (OSX_KEYPASS == "") {
1232       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
1233         codeSigning.'@macEnabled' = "false"
1234       }
1235       install4jConfigXml.'**'.windows.each { windows ->
1236         windows.'@runPostProcessor' = "false"
1237       }
1238     }
1239
1240     // put file association actions where placeholder action is
1241     def install4jFileAssociationsText = install4jFileAssociationsFile.text
1242     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
1243     install4jConfigXml.'**'.action.any { a ->
1244       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
1245         def parent = a.parent()
1246         parent.remove(a)
1247         fileAssociationActions.each { faa ->
1248             parent.append(faa)
1249         }
1250         // don't need to continue in .any loop once replacements have been made
1251         return true
1252       }
1253     }
1254
1255     // write install4j file
1256     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
1257   }
1258 }
1259
1260
1261 clean {
1262   doFirst {
1263     delete install4jConfFile
1264   }
1265 }
1266
1267
1268 task installers(type: com.install4j.gradle.Install4jTask) {
1269   group = "distribution"
1270   description = "Create the install4j installers"
1271   dependsOn setGitVals
1272   dependsOn getdown
1273   dependsOn copyInstall4jTemplate
1274
1275   projectFile = install4jConfFile
1276
1277   // create an md5 for the input files to use as version for install4j conf file
1278   def digest = MessageDigest.getInstance("MD5")
1279   digest.update(
1280     (file("${install4jDir}/${install4j_template}").text + 
1281     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
1282     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
1283   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
1284   if (filesMd5.length() >= 8) {
1285     filesMd5 = filesMd5.substring(0,8)
1286   }
1287   def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
1288
1289   variables = [
1290     'OSX_KEYSTORE': OSX_KEYSTORE,
1291     'JSIGN_SH': JSIGN_SH,
1292     'JRE_DIR': getdown_app_dir_java,
1293     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
1294     'JALVIEW_VERSION': JALVIEW_VERSION,
1295     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
1296     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
1297     'JAVA_VERSION': JAVA_VERSION,
1298     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1299     'VERSION': JALVIEW_VERSION,
1300     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1301     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1302     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1303     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1304     'COPYRIGHT_MESSAGE': install4j_copyright_message,
1305     'MACOS_BUNDLE_ID': install4j_macOS_bundle_id,
1306     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1307     'GETDOWN_DIST_DIR': getdown_app_dir,
1308     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1309     'GETDOWN_INSTALL_DIR': getdown_install_dir,
1310     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
1311   ]
1312
1313   destination = "${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}"
1314   buildSelected = true
1315
1316   if (install4j_faster.equals("true") || CHANNEL.startsWith("DEVELOP") || CHANNEL.startsWith("LOCAL")) {
1317     // this doesn't seem to work
1318     faster = true
1319     disableSigning = true
1320   }
1321
1322   if (OSX_KEYPASS) {
1323     macKeystorePassword = OSX_KEYPASS
1324   }
1325
1326   doFirst {
1327     println("Using projectFile "+projectFile)
1328   }
1329
1330   inputs.dir(getdownWebsiteDir)
1331   inputs.file(install4jConfFile)
1332   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
1333   inputs.dir(macosJavaVMDir)
1334   inputs.dir(windowsJavaVMDir)
1335   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
1336 }
1337
1338
1339 task sourceDist(type: Tar) {
1340   
1341   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
1342   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
1343   // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
1344   try {
1345     archiveFileName = outputFileName
1346   } catch (Exception e) {
1347     archiveName = outputFileName
1348   }
1349   
1350   compression Compression.GZIP
1351   
1352   into project.name
1353
1354   def EXCLUDE_FILES=["build/*","bin/*","test-output/","test-reports","tests","clover*/*"
1355   ,".*"
1356   ,"benchmarking/*"
1357   ,"**/.*"
1358   ,"*.class"
1359   ,"**/*.class","${j11modDir}/**/*.jar","appletlib","**/*locales"
1360   ,"*locales/**",
1361   ,"utils/InstallAnywhere"] 
1362   def PROCESS_FILES=[   "AUTHORS",
1363   "CITATION",
1364   "FEATURETODO",
1365   "JAVA-11-README",
1366   "FEATURETODO",
1367   "LICENSE",
1368   "**/README",
1369   "RELEASE",
1370   "THIRDPARTYLIBS","TESTNG",
1371   "build.gradle",
1372   "gradle.properties",
1373   "**/*.java",
1374   "**/*.html",
1375   "**/*.xml",
1376   "**/*.gradle",
1377   "**/*.groovy",
1378   "**/*.properties",
1379   "**/*.perl",
1380   "**/*.sh"]
1381
1382   from(jalviewDir) {
1383     exclude (EXCLUDE_FILES)
1384     include (PROCESS_FILES)
1385     filter(ReplaceTokens,
1386       beginToken: '$$',
1387       endToken: '$$',
1388       tokens: [
1389         'Version-Rel': JALVIEW_VERSION,
1390         'Year-Rel': getDate("yyyy")
1391       ]
1392     )
1393   }
1394   from(jalviewDir) {
1395     exclude (EXCLUDE_FILES)
1396     exclude (PROCESS_FILES)
1397     exclude ("appletlib")
1398     exclude ("**/*locales")
1399     exclude ("*locales/**")
1400     exclude ("utils/InstallAnywhere")
1401
1402     exclude (getdown_files_dir)
1403     exclude (getdown_website_dir)
1404
1405     // exluding these as not using jars as modules yet
1406     exclude ("${j11modDir}/**/*.jar")
1407   }
1408   //  from (jalviewDir) {
1409   //    // explicit includes for stuff that seemed to not get included
1410   //    include(fileTree("test/**/*."))
1411   //    exclude(EXCLUDE_FILES)
1412   //    exclude(PROCESS_FILES)
1413   //  }
1414 }
1415
1416
1417 task helppages {
1418   dependsOn copyHelp
1419   dependsOn pubhtmlhelp
1420   
1421   inputs.dir("${classesDir}/${help_dir}")
1422   outputs.dir("${buildDir}/distributions/${help_dir}")
1423 }
1424
1425
1426
1427
1428 // JALVIEWJS STUFF HERE
1429
1430 task j2sSetHeadlessBuild {
1431   doFirst {
1432     IN_ECLIPSE = false
1433   }
1434 }
1435
1436
1437 task jalviewjsSetEclipseWorkspace {
1438   def propKey = "jalviewjs_eclipse_workspace"
1439   def propVal = null
1440   if (project.hasProperty(propKey)) {
1441     propVal = project.getProperty(propKey)
1442     if (propVal.startsWith("~/")) {
1443       propVal = System.getProperty("user.home") + propVal.substring(1)
1444     }
1445   }
1446   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
1447   def propsFile = file(propsFileName)
1448   def eclipseWsDir = propVal
1449   def props = new Properties()
1450
1451   def writeProps = true
1452   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
1453     def ins = new FileInputStream(propsFileName)
1454     props.load(ins)
1455     ins.close()
1456     if (props.getProperty(propKey, null) != null) {
1457       eclipseWsDir = props.getProperty(propKey)
1458       writeProps = false
1459     }
1460   }
1461
1462   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
1463     def tempDir = File.createTempDir()
1464     eclipseWsDir = tempDir.getAbsolutePath()
1465     writeProps = true
1466   }
1467   eclipseWorkspace = file(eclipseWsDir)
1468
1469   doFirst {
1470     // do not run a headless transpile when we claim to be in Eclipse
1471     if (IN_ECLIPSE) {
1472       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1473       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1474     } else {
1475       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1476     }
1477
1478     if (writeProps) {
1479       props.setProperty(propKey, eclipseWsDir)
1480       propsFile.parentFile.mkdirs()
1481       def bytes = new ByteArrayOutputStream()
1482       props.store(bytes, null)
1483       def propertiesString = bytes.toString()
1484       propsFile.text = propertiesString
1485       print("NEW ")
1486     } else {
1487       print("EXISTING ")
1488     }
1489
1490     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
1491   }
1492
1493   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
1494   outputs.file(propsFileName)
1495   outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
1496 }
1497
1498
1499 task jalviewjsEclipsePaths {
1500   def eclipseProduct
1501
1502   def eclipseRoot = jalviewjs_eclipse_root
1503   if (eclipseRoot.startsWith("~/")) {
1504     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
1505   }
1506   if (OperatingSystem.current().isMacOsX()) {
1507     eclipseRoot += "/Eclipse.app"
1508     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
1509     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
1510   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
1511     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1512       eclipseRoot += "/eclipse.exe"
1513     }
1514     eclipseBinary = "${eclipseRoot}/eclipse"
1515     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1516   } else { // linux or unix
1517     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1518       eclipseRoot += "/eclipse"
1519     }
1520     eclipseBinary = "${eclipseRoot}/eclipse"
1521     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1522   }
1523
1524   eclipseVersion = "4.13" // default
1525   def assumedVersion = true
1526   if (file(eclipseProduct).exists()) {
1527     def fis = new FileInputStream(eclipseProduct)
1528     def props = new Properties()
1529     props.load(fis)
1530     eclipseVersion = props.getProperty("version")
1531     fis.close()
1532     assumedVersion = false
1533   }
1534   
1535   def propKey = "eclipse_debug"
1536   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
1537
1538   doFirst {
1539     // do not run a headless transpile when we claim to be in Eclipse
1540     if (IN_ECLIPSE) {
1541       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1542       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1543     } else {
1544       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1545     }
1546
1547     if (!assumedVersion) {
1548       println("ECLIPSE VERSION=${eclipseVersion}")
1549     }
1550   }
1551 }
1552
1553
1554 task eclipseSetup {
1555   dependsOn eclipseProject
1556   dependsOn eclipseClasspath
1557   dependsOn eclipseJdt
1558 }
1559
1560
1561 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
1562 task jalviewjsEclipseCopyDropins(type: Copy) {
1563   dependsOn jalviewjsEclipsePaths
1564
1565   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
1566   inputFiles += file("${jalviewDir}/${jalviewjs_j2s_plugin}")
1567   def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
1568
1569   from inputFiles
1570   into outputDir
1571 }
1572
1573
1574 // this eclipse -clean doesn't actually work
1575 task jalviewjsCleanEclipse(type: Exec) {
1576   dependsOn eclipseSetup
1577   dependsOn jalviewjsEclipsePaths
1578   dependsOn jalviewjsEclipseCopyDropins
1579
1580   executable(eclipseBinary)
1581   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
1582   if (eclipseDebug) {
1583     args += "-debug"
1584   }
1585   args += "-l"
1586
1587   def inputString = """exit
1588 y
1589 """
1590   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
1591   standardInput = inputByteStream
1592 }
1593
1594 /* not really working yet
1595 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
1596 */
1597
1598
1599 task jalviewjsTransferUnzipSwingJs {
1600   def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
1601
1602   doLast {
1603     copy {
1604       from zipTree(file_zip)
1605       into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
1606     }
1607   }
1608
1609   inputs.file file_zip
1610   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
1611 }
1612
1613
1614 task jalviewjsTransferUnzipLib {
1615   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
1616
1617   doLast {
1618     zipFiles.each { file_zip -> 
1619       copy {
1620         from zipTree(file_zip)
1621         into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
1622       }
1623     }
1624   }
1625
1626   inputs.files zipFiles
1627   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
1628 }
1629
1630
1631 task jalviewjsTransferUnzipAllLibs {
1632   dependsOn jalviewjsTransferUnzipSwingJs
1633   dependsOn jalviewjsTransferUnzipLib
1634 }
1635
1636
1637 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
1638   group "JalviewJS"
1639   description "Create the .j2s file from the j2s.* properties"
1640
1641   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
1642   def siteDirProperty = "j2s.site.directory"
1643   def setSiteDir = false
1644   jalviewjsJ2sProps.each { prop, val ->
1645     if (val != null) {
1646       if (prop == siteDirProperty) {
1647         if (!(val.startsWith('/') || val.startsWith("file://") )) {
1648           val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
1649         }
1650         setSiteDir = true
1651       }
1652       property(prop,val)
1653     }
1654     if (!setSiteDir) { // default site location, don't override specifically set property
1655       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
1656     }
1657   }
1658   outputFile = jalviewjsJ2sSettingsFileName
1659
1660   if (! IN_ECLIPSE) {
1661     inputs.properties(jalviewjsJ2sProps)
1662     outputs.file(jalviewjsJ2sSettingsFileName)
1663   }
1664 }
1665
1666
1667 task jalviewjsEclipseSetup {
1668   dependsOn jalviewjsEclipseCopyDropins
1669   dependsOn jalviewjsSetEclipseWorkspace
1670   dependsOn jalviewjsCreateJ2sSettings
1671 }
1672
1673
1674 task jalviewjsSyncAllLibs (type: Sync) {
1675   dependsOn jalviewjsTransferUnzipAllLibs
1676   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
1677   inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
1678   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
1679
1680   from inputFiles
1681   into outputDir
1682   def outputFiles = []
1683   rename { filename ->
1684     outputFiles += "${outputDir}/${filename}"
1685     null
1686   }
1687   preserve {
1688     include "**"
1689   }
1690   outputs.files outputFiles
1691   inputs.files inputFiles
1692 }
1693
1694
1695 task jalviewjsSyncResources (type: Sync) {
1696   def inputFiles = fileTree(dir: resourceDir)
1697   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
1698
1699   from inputFiles
1700   into outputDir
1701   def outputFiles = []
1702   rename { filename ->
1703     outputFiles += "${outputDir}/${filename}"
1704     null
1705   }
1706   preserve {
1707     include "**"
1708   }
1709   outputs.files outputFiles
1710   inputs.files inputFiles
1711 }
1712
1713
1714 task jalviewjsSyncSiteResources (type: Sync) {
1715   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
1716   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
1717
1718   from inputFiles
1719   into outputDir
1720   def outputFiles = []
1721   rename { filename ->
1722     outputFiles += "${outputDir}/${filename}"
1723     null
1724   }
1725   preserve {
1726     include "**"
1727   }
1728   outputs.files outputFiles
1729   inputs.files inputFiles
1730 }
1731
1732
1733 task jalviewjsSyncBuildProperties (type: Sync) {
1734   dependsOn createBuildProperties
1735   def inputFiles = [file(buildProperties)]
1736   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
1737
1738   from inputFiles
1739   into outputDir
1740   def outputFiles = []
1741   rename { filename ->
1742     outputFiles += "${outputDir}/${filename}"
1743     null
1744   }
1745   preserve {
1746     include "**"
1747   }
1748   outputs.files outputFiles
1749   inputs.files inputFiles
1750 }
1751
1752
1753 task jalviewjsProjectImport(type: Exec) {
1754   dependsOn eclipseSetup
1755   dependsOn jalviewjsEclipsePaths
1756   dependsOn jalviewjsEclipseSetup
1757
1758   doFirst {
1759     // do not run a headless import when we claim to be in Eclipse
1760     if (IN_ECLIPSE) {
1761       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1762       throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1763     } else {
1764       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1765     }
1766   }
1767
1768   //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
1769   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
1770   executable(eclipseBinary)
1771   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
1772   if (eclipseDebug) {
1773     args += "-debug"
1774   }
1775   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
1776   if (!IN_ECLIPSE) {
1777     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
1778   }
1779
1780   inputs.file("${jalviewDir}/.project")
1781   outputs.upToDateWhen { 
1782     file(projdir).exists()
1783   }
1784 }
1785
1786
1787 task jalviewjsTranspile(type: Exec) {
1788   dependsOn jalviewjsEclipseSetup 
1789   dependsOn jalviewjsProjectImport
1790   dependsOn jalviewjsEclipsePaths
1791
1792   doFirst {
1793     // do not run a headless transpile when we claim to be in Eclipse
1794     if (IN_ECLIPSE) {
1795       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1796       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1797     } else {
1798       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1799     }
1800   }
1801
1802   executable(eclipseBinary)
1803   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
1804   if (eclipseDebug) {
1805     args += "-debug"
1806   }
1807   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
1808   if (!IN_ECLIPSE) {
1809     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
1810   }
1811
1812   def stdout
1813   def stderr
1814   doFirst {
1815     stdout = new ByteArrayOutputStream()
1816     stderr = new ByteArrayOutputStream()
1817
1818     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
1819     def logOutFile = file(logOutFileName)
1820     logOutFile.createNewFile()
1821     logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
1822 BINARY: ${eclipseBinary}
1823 VERSION: ${eclipseVersion}
1824 WORKSPACE: ${eclipseWorkspace}
1825 DEBUG: ${eclipseDebug}
1826 ----
1827 """
1828     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
1829     // combine stdout and stderr
1830     def logErrFOS = logOutFOS
1831
1832     if (jalviewjs_j2s_to_console.equals("true")) {
1833       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1834         new org.apache.tools.ant.util.TeeOutputStream(
1835           logOutFOS,
1836           stdout),
1837         standardOutput)
1838       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1839         new org.apache.tools.ant.util.TeeOutputStream(
1840           logErrFOS,
1841           stderr),
1842         errorOutput)
1843     } else {
1844       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1845         logOutFOS,
1846         stdout)
1847       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1848         logErrFOS,
1849         stderr)
1850     }
1851   }
1852
1853   doLast {
1854     if (stdout.toString().contains("Error processing ")) {
1855       // j2s did not complete transpile
1856       //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
1857       throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
1858     }
1859   }
1860
1861   inputs.dir("${jalviewDir}/${sourceDir}")
1862   outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
1863   outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
1864 }
1865
1866
1867 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
1868
1869   def stdout = new ByteArrayOutputStream()
1870   def stderr = new ByteArrayOutputStream()
1871
1872   def coreFile = file(jsfile)
1873   def msg = ""
1874   msg = "Creating core for ${name}...\nGenerating ${jsfile}"
1875   println(msg)
1876   logOutFile.createNewFile()
1877   logOutFile.append(msg+"\n")
1878
1879   def coreTop = file(prefixFile)
1880   def coreBottom = file(suffixFile)
1881   coreFile.getParentFile().mkdirs()
1882   coreFile.createNewFile()
1883   coreFile.write( coreTop.text )
1884   list.each {
1885     f ->
1886     if (f.exists()) {
1887       def t = f.text
1888       t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
1889       coreFile.append( t )
1890     } else {
1891       msg = "...file '"+f.getPath()+"' does not exist, skipping"
1892       println(msg)
1893       logOutFile.append(msg+"\n")
1894     }
1895   }
1896   coreFile.append( coreBottom.text )
1897
1898   msg = "Generating ${zjsfile}"
1899   println(msg)
1900   logOutFile.append(msg+"\n")
1901   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
1902   def logErrFOS = logOutFOS
1903
1904   javaexec {
1905     classpath = files(["${jalviewDir}/tools/closure_compiler.jar"])
1906     args = [ "--js", jsfile, "--js_output_file", zjsfile ]
1907     maxHeapSize = "2g"
1908
1909     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
1910     println(msg)
1911     logOutFile.append(msg+"\n")
1912
1913     if (logOutConsole) {
1914       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1915         new org.apache.tools.ant.util.TeeOutputStream(
1916           logOutFOS,
1917           stdout),
1918         standardOutput)
1919         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1920           new org.apache.tools.ant.util.TeeOutputStream(
1921             logErrFOS,
1922             stderr),
1923           errorOutput)
1924     } else {
1925       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1926         logOutFOS,
1927         stdout)
1928         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1929           logErrFOS,
1930           stderr)
1931     }
1932   }
1933   msg = "--"
1934   println(msg)
1935   logOutFile.append(msg+"\n")
1936 }
1937
1938
1939 task jalviewjsBuildAllCores {
1940   group "JalviewJS"
1941   description "Build the core js lib closures listed in the classlists dir"
1942   dependsOn jalviewjsTranspile
1943   dependsOn jalviewjsTransferUnzipSwingJs
1944
1945   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
1946   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
1947   def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
1948   def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
1949   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
1950   def prefixFile = "${jsDir}/core/coretop2.js"
1951   def suffixFile = "${jsDir}/core/corebottom2.js"
1952
1953   inputs.file prefixFile
1954   inputs.file suffixFile
1955
1956   def classlistFiles = []
1957   // add the classlists found int the jalviewjs_classlists_dir
1958   fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
1959     file ->
1960     def name = file.getName() - ".txt"
1961     classlistFiles += [
1962       'file': file,
1963       'name': name
1964     ]
1965   }
1966
1967   // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
1968   //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
1969   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
1970
1971   jalviewjsCoreClasslists = []
1972
1973   classlistFiles.each {
1974     hash ->
1975
1976     def file = hash['file']
1977     if (! file.exists()) {
1978       //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
1979       return false // this is a "continue" in groovy .each closure
1980     }
1981     def name = hash['name']
1982     if (name == null) {
1983       name = file.getName() - ".txt"
1984     }
1985
1986     def filelist = []
1987     file.eachLine {
1988       line ->
1989         filelist += line
1990     }
1991     def list = fileTree(dir: j2sDir, includes: filelist)
1992
1993     def jsfile = "${outputDir}/core${name}.js"
1994     def zjsfile = "${outputDir}/core${name}.z.js"
1995
1996     jalviewjsCoreClasslists += [
1997       'jsfile': jsfile,
1998       'zjsfile': zjsfile,
1999       'list': list,
2000       'name': name
2001     ]
2002
2003     inputs.file(file)
2004     inputs.files(list)
2005     outputs.file(jsfile)
2006     outputs.file(zjsfile)
2007   }
2008   
2009   // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
2010   def stevesoftClasslistName = "_stevesoft"
2011   def stevesoftClasslist = [
2012     'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
2013     'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
2014     'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
2015     'name': stevesoftClasslistName
2016   ]
2017   jalviewjsCoreClasslists += stevesoftClasslist
2018   inputs.files(stevesoftClasslist['list'])
2019   outputs.file(stevesoftClasslist['jsfile'])
2020   outputs.file(stevesoftClasslist['zjsfile'])
2021
2022   // _all core
2023   def allClasslistName = "_all"
2024   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
2025   allJsFiles += fileTree(
2026     dir: libJ2sDir,
2027     include: "**/*.js",
2028     excludes: [
2029       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2030       "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
2031       "**/org/jmol/export/JSExporter.js"
2032     ]
2033   )
2034   allJsFiles += fileTree(
2035     dir: swingJ2sDir,
2036     include: "**/*.js",
2037     excludes: [
2038       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2039       "**/sun/misc/Unsafe.js",
2040       "**/swingjs/jquery/jquery-editable-select.js",
2041       "**/swingjs/jquery/j2sComboBox.js",
2042       "**/sun/misc/FloatingDecimal.js"
2043     ]
2044   )
2045   def allClasslist = [
2046     'jsfile': "${outputDir}/core${allClasslistName}.js",
2047     'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
2048     'list': allJsFiles,
2049     'name': allClasslistName
2050   ]
2051   // not including this version of "all" core at the moment
2052   //jalviewjsCoreClasslists += allClasslist
2053   inputs.files(allClasslist['list'])
2054   outputs.file(allClasslist['jsfile'])
2055   outputs.file(allClasslist['zjsfile'])
2056
2057   doFirst {
2058     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
2059     logOutFile.getParentFile().mkdirs()
2060     logOutFile.createNewFile()
2061     logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
2062
2063     jalviewjsCoreClasslists.each {
2064       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
2065     }
2066   }
2067
2068 }
2069
2070
2071 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
2072   copy {
2073     from inputFile
2074     into file(outputFile).getParentFile()
2075     rename { filename ->
2076       if (filename.equals(inputFile.getName())) {
2077         return file(outputFile).getName()
2078       }
2079       return null
2080     }
2081     filter(ReplaceTokens,
2082       beginToken: '_',
2083       endToken: '_',
2084       tokens: [
2085         'MAIN': '"'+mainClass+'"',
2086         'CODE': "null",
2087         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
2088         'COREKEY': jalviewjs_core_key,
2089         'CORENAME': coreName
2090       ]
2091     )
2092   }
2093 }
2094
2095
2096 task jalviewjsPublishCoreTemplates {
2097   dependsOn jalviewjsBuildAllCores
2098   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
2099   def inputFile = file(inputFileName)
2100   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2101
2102   def outputFiles = []
2103   jalviewjsCoreClasslists.each { cl ->
2104     def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
2105     cl['outputfile'] = outputFile
2106     outputFiles += outputFile
2107   }
2108
2109   doFirst {
2110     jalviewjsCoreClasslists.each { cl ->
2111       jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
2112     }
2113   }
2114   inputs.file(inputFile)
2115   outputs.files(outputFiles)
2116 }
2117
2118
2119 task jalviewjsSyncCore (type: Sync) {
2120   dependsOn jalviewjsBuildAllCores
2121   dependsOn jalviewjsPublishCoreTemplates
2122   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
2123   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2124
2125   from inputFiles
2126   into outputDir
2127   def outputFiles = []
2128   rename { filename ->
2129     outputFiles += "${outputDir}/${filename}"
2130     null
2131   }
2132   preserve {
2133     include "**"
2134   }
2135   outputs.files outputFiles
2136   inputs.files inputFiles
2137 }
2138
2139
2140 // this Copy version of TransferSiteJs will delete anything else in the target dir
2141 task jalviewjsCopyTransferSiteJs(type: Copy) {
2142   dependsOn jalviewjsTranspile
2143   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2144   into "${jalviewDir}/${jalviewjsSiteDir}"
2145 }
2146
2147
2148 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
2149 task jalviewjsSyncTransferSiteJs(type: Sync) {
2150   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2151   include "**/*.*"
2152   into "${jalviewDir}/${jalviewjsSiteDir}"
2153   preserve {
2154     include "**"
2155   }
2156 }
2157
2158
2159 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
2160 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
2161 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
2162 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
2163
2164 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
2165 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
2166 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
2167 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
2168
2169
2170 task jalviewjsPrepareSite {
2171   group "JalviewJS"
2172   description "Prepares the website folder including unzipping files and copying resources"
2173   dependsOn jalviewjsSyncAllLibs
2174   dependsOn jalviewjsSyncResources
2175   dependsOn jalviewjsSyncSiteResources
2176   dependsOn jalviewjsSyncBuildProperties
2177   dependsOn jalviewjsSyncCore
2178 }
2179
2180
2181 task jalviewjsBuildSite {
2182   group "JalviewJS"
2183   description "Builds the whole website including transpiled code"
2184   dependsOn jalviewjsCopyTransferSiteJs
2185   dependsOn jalviewjsPrepareSite
2186 }
2187
2188
2189 task cleanJalviewjsTransferSite {
2190   doFirst {
2191     delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2192     delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2193     delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2194     delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2195   }
2196 }
2197
2198
2199 task cleanJalviewjsSite {
2200   dependsOn cleanJalviewjsTransferSite
2201   doFirst {
2202     delete "${jalviewDir}/${jalviewjsSiteDir}"
2203   }
2204 }
2205
2206
2207 task jalviewjsSiteTar(type: Tar) {
2208   group "JalviewJS"
2209   description "Creates a tar.gz file for the website"
2210   dependsOn jalviewjsBuildSite
2211   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
2212   try {
2213     archiveFileName = outputFilename
2214   } catch (Exception e) {
2215     archiveName = outputFilename
2216   }
2217
2218   compression Compression.GZIP
2219
2220   from "${jalviewDir}/${jalviewjsSiteDir}"
2221   into jalviewjs_site_dir // this is inside the tar file
2222
2223   inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
2224 }
2225
2226
2227 task jalviewjsServer {
2228   group "JalviewJS"
2229   def filename = "jalviewjsTest.html"
2230   description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
2231   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
2232   doLast {
2233
2234     SimpleHttpFileServerFactory factory = new SimpleHttpFileServerFactory()
2235     def port = Integer.valueOf(jalviewjs_server_port)
2236     def start = port
2237     def running = false
2238     def url
2239     def jalviewjsServer
2240     while(port < start+1000 && !running) {
2241       try {
2242         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
2243         jalviewjsServer = factory.start(doc_root, port)
2244         running = true
2245         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
2246         println("SERVER STARTED with document root ${doc_root}.")
2247         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
2248         println("For debug: "+url+"?j2sdebug")
2249         println("For verbose: "+url+"?j2sverbose")
2250       } catch (Exception e) {
2251         port++;
2252       }
2253     }
2254     def htmlText = """
2255       <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
2256       <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
2257       <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
2258       """
2259     jalviewjsCoreClasslists.each { cl ->
2260       def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
2261       htmlText += """
2262       <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
2263       """
2264       println("For core ${cl.name}: "+urlcore)
2265     }
2266
2267     file(htmlFile).text = htmlText
2268   }
2269
2270   outputs.file(htmlFile)
2271   outputs.upToDateWhen({false})
2272 }
2273
2274
2275 task cleanJalviewjsAll {
2276   group "JalviewJS"
2277   description "Delete all configuration and build artifacts to do with JalviewJS build"
2278   dependsOn cleanJalviewjsSite
2279   dependsOn jalviewjsEclipsePaths
2280   
2281   doFirst {
2282     delete "${jalviewDir}/${jalviewjsBuildDir}"
2283     delete "${jalviewDir}/${eclipse_bin_dir}"
2284     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
2285       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
2286     }
2287     delete "${jalviewDir}/${jalviewjs_j2s_settings}"
2288   }
2289
2290   outputs.upToDateWhen( { false } )
2291 }
2292
2293
2294 task jalviewjsIDE_checkJ2sPlugin {
2295   group "00 JalviewJS in Eclipse"
2296   description "Compare the swingjs/net.sf.j2s.core.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
2297
2298   doFirst {
2299     def j2sPlugin = string("${jalviewDir}/${jalviewjs_j2s_plugin}")
2300     def j2sPluginFile = file(j2sPlugin)
2301     def copyPlugin = jalviewjs_eclipseIDE_auto_copy_j2s_plugin == "true"
2302     def eclipseHome = System.properties["eclipse.home.location"]
2303     def doCopy = false
2304     if (eclipseHome == null || ! IN_ECLIPSE) {
2305       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
2306     }
2307     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
2308     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
2309     if (!eclipseJ2sPluginFile.exists()) {
2310       def msg = "Eclipse J2S Plugin is not installed (could not find '${eclipseJ2sPlugin}')"
2311       System.err.println(msg)
2312       if (! copyPlugin) {
2313         throw new GradleException(msg)
2314       }
2315       doCopy = true
2316     }
2317
2318     def digest = MessageDigest.getInstance("MD5")
2319
2320     digest.update(j2sPluginFile.text.bytes)
2321     def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
2322
2323     digest.update(eclipseJ2sPluginFile.text.bytes)
2324     def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
2325      
2326     if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
2327       def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
2328       System.err.println(msg)
2329       if (! copyPlugin) {
2330         throw new StopExecutionException(msg)
2331       }
2332       doCopy = true
2333     }
2334
2335     if (doCopy) {
2336       def msg = "WARNING! Auto-copying this commit's j2s plugin version '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n         May require an Eclipse restart"
2337       println(msg)
2338       copy {
2339         from j2sPlugin
2340         eclipseJ2sPluginFile.getParentFile().mkdirs()
2341         into eclipseJ2sPluginFile.getParent()
2342       }
2343     } else {
2344       def msg = "Eclipse J2S Plugin is the same as '${j2sPlugin}' (this is good)"
2345       println(msg)
2346     }
2347   }
2348 }
2349
2350
2351 task jalviewjsIDE_j2sFile {
2352   group "00 JalviewJS in Eclipse"
2353   description "Creates the .j2s file"
2354   dependsOn jalviewjsCreateJ2sSettings
2355 }
2356
2357
2358 task jalviewjsIDE_SyncCore {
2359   group "00 JalviewJS in Eclipse"
2360   description "Build the core js lib closures listed in the classlists dir and publish core html from template"
2361   dependsOn jalviewjsSyncCore
2362 }
2363
2364
2365 task jalviewjsIDE_SyncSiteAll {
2366   dependsOn jalviewjsSyncAllLibs
2367   dependsOn jalviewjsSyncResources
2368   dependsOn jalviewjsSyncSiteResources
2369   dependsOn jalviewjsSyncBuildProperties
2370 }
2371
2372
2373 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
2374
2375
2376 task jalviewjsIDE_PrepareSite {
2377   group "00 JalviewJS in Eclipse"
2378   description "Sync libs and resources to site dir, but not closure cores"
2379
2380   dependsOn jalviewjsIDE_SyncSiteAll
2381   dependsOn cleanJalviewjsTransferSite
2382 }
2383
2384
2385 task jalviewjsIDE_AssembleSite {
2386   group "00 JalviewJS in Eclipse"
2387   description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
2388   dependsOn jalviewjsPrepareSite
2389 }
2390
2391
2392 task jalviewjsIDE_SiteClean {
2393   group "00 JalviewJS in Eclipse"
2394   description "Deletes the Eclipse transpiled site"
2395   dependsOn cleanJalviewjsSite
2396 }
2397
2398
2399 task jalviewjsIDE_Server {
2400   group "00 JalviewJS in Eclipse"
2401   description "Starts a webserver on localhost to test the website"
2402   dependsOn jalviewjsServer
2403 }
2404
2405
2406 // buildship runs this at import or gradle refresh
2407 task eclipseSynchronizationTask {
2408   //dependsOn eclipseSetup
2409   dependsOn createBuildProperties
2410   if (J2S_ENABLED) {
2411     dependsOn jalviewjsIDE_j2sFile
2412     dependsOn jalviewjsIDE_checkJ2sPlugin
2413     dependsOn jalviewjsIDE_PrepareSite
2414   }
2415 }
2416
2417
2418 // buildship runs this at build time or project refresh
2419 task eclipseAutoBuildTask {
2420   //dependsOn jalviewjsIDE_checkJ2sPlugin
2421   //dependsOn jalviewjsIDE_PrepareSite
2422 }
2423
2424
2425
2426
2427
2428
2429
2430
2431 task jalviewjs {
2432   group "JalviewJS"
2433   description "Build the site"
2434   dependsOn jalviewjsBuildSite
2435 }