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