1441e7dde18fb634f603bf81e023a164cee22ec7
[jalview.git] / build.gradle
1 import org.apache.tools.ant.filters.ReplaceTokens
2 //import org.apache.tools.ant.filters.ReplaceRegexp
3 import org.gradle.internal.os.OperatingSystem
4 import org.gradle.plugins.ide.eclipse.model.*
5
6
7 import groovy.transform.ExternalizeMethods
8
9 buildscript {
10   dependencies {
11     classpath 'org.openclover:clover:4.3.1'
12     classpath 'org.apache.commons:commons-compress:1.18'
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 '7.0.9'
22 }
23
24 repositories {
25   jcenter()
26   mavenCentral()
27   mavenLocal()
28   flatDir {
29     dirs gradlePluginsDir
30   }
31 }
32
33 mainClassName = launcherClass
34 def cloverInstrDir = file("$buildDir/$cloverSourcesInstrDir")
35 def classes = "$jalviewDir/$classesDir"
36
37 if (clover.equals("true")) {
38   use_clover = true
39   classes = "$buildDir/$cloverClassesDir"
40 } else {
41   use_clover = false
42   classes = "$jalviewDir/$classesDir"
43 }
44
45 // configure classpath/args for j8/j11 compilation
46
47 def jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
48 def libDir
49 def libDistDir
50 def compile_source_compatibility
51 def compile_target_compatibility
52
53 ext {
54   // where the getdown channel will be built.
55   // TODO: consider allowing this expression to  be overridden by -P arg
56   getdownWebsiteDir = jalviewDir + '/' + getdown_website_dir + '/' + JAVA_VERSION
57   getdownAppDir = getdownWebsiteDir + '/' + getdown_app_dir
58   getdownJ11libDir = getdownWebsiteDir + '/' + getdown_j11lib_dir
59   getdownResourceDir = getdownWebsiteDir + '/' + getdown_resource_dir
60   getdownLauncher = jalviewDir + '/' + getdown_launcher
61   getdownFilesDir = jalviewDir + '/' + getdown_files_dir + '/' + JAVA_VERSION + '/'
62   getdown_app_base = getdown_channel_base+"/"+getdown_channel_name+"/"+JAVA_VERSION+"/"
63   modules_compileClasspath = fileTree(dir: "$jalviewDir/$j11modDir", include: ["*.jar"])
64   modules_runtimeClasspath = modules_compileClasspath
65   gitHash = ""
66   gitBranch = ""
67 }
68
69 def JAVA_INTEGER_VERSION
70 def additional_compiler_args = []
71 // these are getdown.txt properties defined dependent on the JAVA_VERSION
72 def getdown_alt_java_min_version
73 // this property is assigned below and expanded to multiple lines in the getdown task
74 def getdown_alt_multi_java_location
75 // this property is for the Java library used in eclipse
76 def eclipse_java_runtime_name
77 if (JAVA_VERSION.equals("1.8")) {
78   JAVA_INTEGER_VERSION = "8"
79   //libDir = j8libDir
80   libDir = j11libDir
81   libDistDir = j8libDir
82   compile_source_compatibility = 1.8
83   compile_target_compatibility = 1.8
84   getdown_alt_java_min_version = getdown_alt_java8_min_version
85   getdown_alt_multi_java_location = getdown_alt_java8_txt_multi_java_location
86   eclipse_java_runtime_name = "JavaSE-1.8"
87 } else if (JAVA_VERSION.equals("11")) {
88   JAVA_INTEGER_VERSION = "11"
89   libDir = j11libDir
90   libDistDir = j11libDir
91   compile_source_compatibility = 11
92   compile_target_compatibility = 11
93   getdown_alt_java_min_version = getdown_alt_java11_min_version
94   getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
95   eclipse_java_runtime_name = "JavaSE-11"
96   additional_compiler_args += [
97     '--module-path', ext.modules_compileClasspath.asPath,
98     '--add-modules', j11modules
99   ]
100 } else {
101   throw new GradleException("JAVA_VERSION=$JAVA_VERSION not currently supported by Jalview")
102 }
103
104 sourceSets {
105
106   main {
107     java {
108       srcDirs "$jalviewDir/$sourceDir"
109       outputDir = file("$classes")
110     }
111
112     resources {
113       srcDirs "$jalviewDir/$resourceDir"
114       srcDirs "$jalviewDir/$libDistDir"
115     }
116
117     jar.destinationDir = file("$jalviewDir/$packageDir")
118
119     compileClasspath = files(sourceSets.main.java.outputDir)
120     compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
121
122     runtimeClasspath = compileClasspath
123   }
124   clover {
125     java {
126       srcDirs = [ cloverInstrDir ]
127       outputDir = file("${buildDir}/${cloverClassesDir}")
128     }
129
130     resources {
131       srcDirs = sourceSets.main.resources.srcDirs
132     }
133     compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
134     compileClasspath += files(sourceSets.main.java.outputDir)
135     compileClasspath += sourceSets.main.compileClasspath
136     compileClasspath += fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"])
137     compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
138
139     runtimeClasspath = compileClasspath
140   }
141
142   test {
143     java {
144       srcDirs "$jalviewDir/$testSourceDir"
145       outputDir = file("$jalviewDir/$testOutputDir")
146     }
147
148     resources {
149       srcDirs = sourceSets.main.resources.srcDirs
150     }
151
152     compileClasspath = files( sourceSets.test.java.outputDir )
153
154     if (use_clover) {
155       compileClasspath += sourceSets.clover.compileClasspath
156     } else {
157       compileClasspath += files(sourceSets.main.java.outputDir)
158     }
159     //compileClasspath += sourceSets.main.compileClasspath
160     //compileClasspath += files( sourceSets.main.resources.srcDirs)
161     compileClasspath += fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"])
162     compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
163
164     runtimeClasspath = compileClasspath
165   }
166 }
167
168 // clover bits
169 dependencies {
170   if (use_clover) {
171     cloverCompile 'org.openclover:clover:4.3.1'
172     testCompile 'org.openclover:clover:4.3.1'
173   }
174 }
175
176 configurations {
177   cloverRuntime
178   cloverRuntime.extendsFrom cloverCompile
179 }
180
181 eclipse {
182   project {
183     name = "Jalview with gradle build"
184
185     natures 'org.eclipse.jdt.core.javanature',
186         'org.eclipse.jdt.groovy.core.groovyNature',
187         'org.eclipse.buildship.core.gradleprojectnature'
188
189     buildCommand 'org.eclipse.jdt.core.javabuilder'
190     buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
191   }
192
193   classpath {
194     //defaultOutputDir = sourceSets.main.java.outputDir
195     def removeThese = []
196     configurations.each{ if (it.isCanBeResolved()) {
197         removeThese += it
198       }
199     }
200
201     minusConfigurations += removeThese
202     plusConfigurations = [ ]
203     file {
204
205       whenMerged { cp ->
206         def removeTheseToo = []
207         HashMap<String, Boolean> addedSrcPath = new HashMap<>();
208         cp.entries.each { entry ->
209           if (entry.kind == 'src') {
210             if (addedSrcPath.getAt(entry.path) || !(entry.path == "src" || entry.path == "test")) {
211               removeTheseToo += entry
212             } else {
213               addedSrcPath.putAt(entry.path, true)
214             }
215           }
216         }
217         cp.entries.removeAll(removeTheseToo)
218         
219         cp.entries += new Output("bin/main")
220         cp.entries += new Library(fileReference(helpParentDir))
221         cp.entries += new Library(fileReference(resourceDir))
222         
223         HashMap<String, Boolean> addedLibPath = new HashMap<>();
224         def allPaths = sourceSets.test.compileClasspath + sourceSets.main.compileClasspath
225         sourceSets.main.compileClasspath.each{
226           //if ((it.isDirectory() || ! it.exists()) && ! (it.equals(sourceSets.main.java.outputDir))) {
227           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
228           if (it.isDirectory() || ! it.exists()) {
229             // don't add dirs to classpath
230             return
231           }
232           def itPath = it.toString()
233           if (itPath.startsWith(jalviewDirAbsolutePath+"/")) {
234             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
235           }
236           if (addedLibPath.get(itPath)) {
237             //println("Not adding duplicate entry "+itPath)
238           } else {
239             //println("Adding entry "+itPath)
240             cp.entries += new Library(fileReference(itPath))
241             addedLibPath.put(itPath, true)
242           }
243         }
244
245         sourceSets.test.compileClasspath.each{
246           //if ((it.isDirectory() || ! it.exists()) && ! (it.equals(sourceSets.main.java.outputDir))) {
247           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
248           if (it.isDirectory() || ! it.exists()) {
249             // don't add dirs to classpath
250             return false // groovy "break" in .each loop
251           }
252           def itPath = it.toString()
253           if (itPath.startsWith(jalviewDirAbsolutePath+"/")) {
254             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
255           }
256           if (addedLibPath.get(itPath)) {
257             // don't duplicate
258           } else {
259             def lib = new Library(fileReference(itPath))
260             // this doesn't work... yet.  Adding test=true attribute using withXml below
261             //def attrs = new Node(null, 'attributes', ["test":"true"])
262             //lib.appendNode(attrs) //
263             cp.entries += lib
264             addedLibPath.put(itPath, true)
265           }
266         }
267       }  
268
269       // withXml changes ignored by buildship, these add the "test=true" attribute
270       withXml {
271         def node = it.asNode()
272         
273         def srcTestAttributes
274         node.children().each{ cpe ->
275           def attributes = cpe.attributes()
276           if (attributes.get("kind") == "src" && attributes.get("path") == "test") {
277             srcTestAttributes = cpe.find { a -> a.name() == "attributes" }
278             return
279           }
280         }
281         def addTestAttribute = true
282         srcTestAttributes.each{a ->
283           if (a.name() == "attribute" && a.attributes().getAt("name") == "test") {
284             addTestAttribute = false
285           }
286         }
287         if (addTestAttribute) {
288           srcTestAttributes.append(new Node(null, "attribute", [name:"test", value:"true"]))
289         }
290
291         node.children().each{ cpe ->
292           def attributes = cpe.attributes()
293           if (attributes.get("kind") == "lib" && attributes.get("path").startsWith("utils/")) {
294             cpe.appendNode('attributes')
295                 .appendNode('attribute', [name:"test", value:"true"])
296           }
297         }
298       } // withXML
299     } // file
300
301     containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
302   } // classpath
303
304   jdt {
305     // for the IDE, use java 11 compatibility
306     sourceCompatibility = JAVA_VERSION
307     targetCompatibility = JAVA_VERSION
308     javaRuntimeName = eclipse_java_runtime_name
309
310     file {
311       withProperties { props ->
312         def jalview_prefs = new Properties()
313         def ins = new FileInputStream(jalviewDirAbsolutePath+"/"+eclipse_extra_jdt_prefs_file)
314         jalview_prefs.load(ins)
315         ins.close()
316         jalview_prefs.forEach { t, v ->
317           if (props.getAt(t) == null) {
318             props.putAt(t, v)
319           }
320         }
321       }
322     }
323   }
324   
325   //synchronizationTasks eclipseClasspath
326   //autoBuildTasks eclipseClasspath
327
328
329 task cloverInstr() {
330   // only instrument source, we build test classes as normal
331   inputs.files files (sourceSets.main.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"]))
332   outputs.dir cloverInstrDir
333
334   doFirst {
335     delete cloverInstrDir
336     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
337       "-d", "${buildDir}/${cloverSourcesInstrDir}"]
338     argsList.addAll(inputs.files.files.collect({ file ->
339       file.absolutePath
340     }))
341     String[] args = argsList.toArray()
342     println("About to instrument "+args.length +" files")
343     com.atlassian.clover.CloverInstr.mainImpl(args)
344   }
345 }
346   
347
348 task cloverReport {
349   group = "Verification"
350   description = "Createst the Clover report"
351   inputs.dir "${buildDir}/clover"
352   outputs.dir "${reportsDir}/clover"
353   onlyIf {
354     file("${buildDir}/clover/clover.db").exists()
355   }
356   doFirst {
357     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
358       "-o", "${reportsDir}/clover"]
359     String[] args = argsList.toArray()
360     com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
361
362     // and generate ${reportsDir}/clover/clover.xml
363     args = ["--initstring", "${buildDir}/clover/clover.db",
364       "-o", "${reportsDir}/clover/clover.xml"].toArray()
365     com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
366   }
367 }
368
369 // end clover bits
370
371
372 compileJava {
373
374   doFirst {
375     sourceCompatibility = compile_source_compatibility
376     targetCompatibility = compile_target_compatibility
377     options.compilerArgs = additional_compiler_args
378     print ("Setting target compatibility to "+targetCompatibility+"\n")
379   }
380
381 }
382
383 compileTestJava {
384   if (use_clover) {
385     dependsOn compileCloverJava
386     classpath += configurations.cloverRuntime
387   } else {
388     classpath += sourceSets.main.runtimeClasspath
389   }
390   doFirst {
391     sourceCompatibility = compile_source_compatibility
392     targetCompatibility = compile_target_compatibility
393     options.compilerArgs = additional_compiler_args
394     print ("Setting target compatibility to "+targetCompatibility+"\n")
395   }
396 }
397
398
399 compileCloverJava {
400
401   doFirst {
402     sourceCompatibility = compile_source_compatibility
403     targetCompatibility = compile_target_compatibility
404     options.compilerArgs += additional_compiler_args
405     print ("Setting target compatibility to "+targetCompatibility+"\n")
406   }
407   classpath += configurations.cloverRuntime
408 }
409
410 clean {
411   delete sourceSets.main.java.outputDir
412 }
413
414 cleanTest {
415   delete sourceSets.test.java.outputDir
416   delete cloverInstrDir
417 }
418
419 def getDate(format) {
420   def date = new Date()
421   //return date.format("dd MMMM yyyy")
422   return date.format(format)
423 }
424
425 task setGitVals {
426   def hashStdOut = new ByteArrayOutputStream()
427   exec {
428     commandLine "git", "rev-parse", "--short", "HEAD"
429     standardOutput = hashStdOut
430     ignoreExitValue true
431   }
432
433   def branchStdOut = new ByteArrayOutputStream()
434   exec {
435     commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
436     standardOutput = branchStdOut
437     ignoreExitValue true
438   }
439
440   project.ext.gitHash = hashStdOut.toString().trim()
441   project.ext.gitBranch = branchStdOut.toString().trim()
442
443   outputs.upToDateWhen { false }
444 }
445
446 task createBuildProperties(type: WriteProperties) {
447   dependsOn setGitVals
448   inputs.dir("$jalviewDir/$sourceDir")
449   inputs.dir("$classes")
450   inputs.dir("$jalviewDir/$resourceDir")
451   outputFile "$classes/$buildPropertiesFile"
452   // taking time specific comment out to allow better incremental builds
453   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
454   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
455   property "BUILD_DATE", getDate("dd MMMM yyyy")
456   property "VERSION", JALVIEW_VERSION
457   property "INSTALLATION", INSTALLATION+" git-commit:"+project.ext.gitHash+" ["+project.ext.gitBranch+"]"
458   outputs.file(outputFile)
459 }
460
461 def buildingHTML = "$jalviewDir/$docDir/building.html"
462 task deleteBuildingHTML(type: Delete) {
463   delete buildingHTML
464 }
465
466 task convertBuildingMD(type: Exec) {
467   dependsOn deleteBuildingHTML
468   def buildingMD = "$jalviewDir/$docDir/building.md"
469   def css = "$jalviewDir/$docDir/github.css"
470
471   def pandoc = pandoc_exec
472   def hostname = "hostname".execute().text.trim()
473   if (! file(pandoc).exists() && hostname.equals("jv-bamboo")) {
474     pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
475   }
476
477   if (file(pandoc).exists()) {
478     commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
479   } else {
480     commandLine "true"
481   }
482
483   ignoreExitValue true
484
485   inputs.file(buildingMD)
486   inputs.file(css)
487   outputs.file(buildingHTML)
488 }
489 clean {
490   delete buildingHTML
491 }
492
493 task syncDocs(type: Sync) {
494   dependsOn convertBuildingMD
495   def syncDir = "$classes/$docDir"
496   from fileTree("$jalviewDir/$docDir")
497   into syncDir
498
499 }
500
501 def helpFile = "$classes/$helpDir/help.jhm"
502
503 task copyHelp(type: Copy) {
504   def inputDir = "$jalviewDir/$helpParentDir/$helpDir"
505   def outputDir = "$classes/$helpDir"
506   from(inputDir) {
507     exclude '**/*.gif'
508     exclude '**/*.jpg'
509     exclude '**/*.png'
510     filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION])
511   }
512   from(inputDir) {
513     include '**/*.gif'
514     include '**/*.jpg'
515     include '**/*.png'
516   }
517   into outputDir
518
519   inputs.dir(inputDir)
520   outputs.files(helpFile)
521   outputs.dir(outputDir)
522 }
523
524 task syncLib(type: Sync) {
525   def syncDir = "$classes/$libDistDir"
526   from fileTree("$jalviewDir/$libDistDir")
527   into syncDir
528 }
529
530 task syncResources(type: Sync) {
531   from "$jalviewDir/$resourceDir"
532   include "**/*.*"
533   exclude "install4j"
534   into "$classes"
535   preserve {
536     include "**"
537   }
538 }
539
540 task prepare {
541   dependsOn syncResources
542   dependsOn syncDocs
543   dependsOn copyHelp
544 }
545
546
547 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
548 test {
549   dependsOn prepare
550   dependsOn compileJava
551   if (use_clover) {
552     dependsOn cloverInstr
553   }
554
555   print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
556
557   useTestNG() {
558     includeGroups testngGroups
559     preserveOrder true
560     useDefaultListeners=true
561   }
562
563   workingDir = jalviewDir
564   //systemProperties 'clover.jar' System.properties.clover.jar
565   sourceCompatibility = compile_source_compatibility
566   targetCompatibility = compile_target_compatibility
567   jvmArgs += additional_compiler_args
568   print ("Setting target compatibility to "+targetCompatibility+"\n")
569 }
570
571 task buildIndices(type: JavaExec) {
572   dependsOn copyHelp
573   classpath = sourceSets.main.compileClasspath
574   main = "com.sun.java.help.search.Indexer"
575   workingDir = "$classes/$helpDir"
576   def argDir = "html"
577   args = [ argDir ]
578   inputs.dir("$workingDir/$argDir")
579
580   outputs.dir("$classes/doc")
581   outputs.dir("$classes/help")
582   outputs.file("$workingDir/JavaHelpSearch/DOCS")
583   outputs.file("$workingDir/JavaHelpSearch/DOCS.TAB")
584   outputs.file("$workingDir/JavaHelpSearch/OFFSETS")
585   outputs.file("$workingDir/JavaHelpSearch/POSITIONS")
586   outputs.file("$workingDir/JavaHelpSearch/SCHEMA")
587   outputs.file("$workingDir/JavaHelpSearch/TMAP")
588 }
589
590 task compileLinkCheck(type: JavaCompile) {
591   options.fork = true
592   classpath = files("$jalviewDir/$utilsDir")
593   destinationDir = file("$jalviewDir/$utilsDir")
594   source = fileTree(dir: "$jalviewDir/$utilsDir", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
595
596   inputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.java")
597   inputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.java")
598   outputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.class")
599   outputs.file("$jalviewDir/$utilsDir/BufferedLineReader.class")
600 }
601
602 def helplinkscheckeroutputfile = file("$jalviewDir/$utilsDir/HelpLinksChecker.out")
603 task linkCheck(type: JavaExec) {
604   dependsOn prepare, compileLinkCheck
605   classpath = files("$jalviewDir/$utilsDir")
606   main = "HelpLinksChecker"
607   workingDir = jalviewDir
608   def help = "$classes/$helpDir"
609   args = [ "$classes/$helpDir", "-nointernet" ]
610
611   doFirst {
612     helplinkscheckeroutputfile.createNewFile()
613     standardOutput new FileOutputStream(helplinkscheckeroutputfile, false)
614   }
615
616   outputs.file(helplinkscheckeroutputfile)
617 }
618
619 task cleanPackageDir(type: Delete) {
620   delete fileTree("$jalviewDir/$packageDir").include("*.jar")
621 }
622
623 jar {
624   dependsOn linkCheck
625   dependsOn buildIndices
626   dependsOn createBuildProperties
627
628   manifest {
629     attributes "Main-Class": mainClass,
630     "Permissions": "all-permissions",
631     "Application-Name": "Jalview Desktop",
632     "Codebase": application_codebase
633   }
634
635   destinationDir = file("$jalviewDir/$packageDir")
636   archiveName = rootProject.name+".jar"
637
638   exclude "cache*/**"
639   exclude "*.jar"
640   exclude "*.jar.*"
641   exclude "**/*.jar"
642   exclude "**/*.jar.*"
643
644   inputs.dir("$classes")
645   outputs.file("$jalviewDir/$packageDir/$archiveName")
646 }
647
648 task copyJars(type: Copy) {
649   from fileTree("$classes").include("**/*.jar").include("*.jar").files
650   into "$jalviewDir/$packageDir"
651 }
652
653 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
654 task syncJars(type: Sync) {
655   from fileTree("$jalviewDir/$libDistDir").include("**/*.jar").include("*.jar").files
656   into "$jalviewDir/$packageDir"
657   preserve {
658     include jar.archiveName
659   }
660 }
661
662 task makeDist {
663   group = "build"
664   description = "Put all required libraries in dist"
665   // order of "cleanPackageDir", "copyJars", "jar" important!
666   jar.mustRunAfter cleanPackageDir
667   syncJars.mustRunAfter cleanPackageDir
668   dependsOn cleanPackageDir
669   dependsOn syncJars
670   dependsOn jar
671   outputs.dir("$jalviewDir/$packageDir")
672 }
673
674 task cleanDist {
675   dependsOn cleanPackageDir
676   dependsOn cleanTest
677   dependsOn clean
678 }
679
680 shadowJar {
681   group = "distribution"
682   dependsOn makeDist
683   from ("$jalviewDir/$libDistDir") {
684     include("*.jar")
685   }
686   mainClassName = shadowJarMainClass
687   mergeServiceFiles()
688   classifier = "all-"+JAVA_VERSION
689   minimize()
690 }
691
692 task getdownWebsite() {
693   group = "distribution"
694   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
695   dependsOn makeDist
696   def getdownWebsiteResourceFilenames = []
697   def getdownTextString = ""
698   def getdownResourceDir = project.ext.getdownResourceDir
699   def getdownAppDir = project.ext.getdownAppDir
700   def getdownResourceFilenames = []
701   doFirst {
702     // go through properties looking for getdown_txt_...
703     def props = project.properties.sort { it.key }
704     props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
705     props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
706
707     if (getdown_local == "true") {
708       getdown_app_base = file(getdownWebsiteDir).toURI().toString()
709     }
710     props.put("getdown_txt_appbase", getdown_app_base)
711     props.each{ prop, val ->
712       if (prop.startsWith("getdown_txt_") && val != null) {
713         if (prop.startsWith("getdown_txt_multi_")) {
714           def key = prop.substring(18)
715           val.split(",").each{ v ->
716             def line = key + " = " + v + "\n"
717             getdownTextString += line
718           }
719         } else {
720           // file values rationalised
721           if (val.indexOf('/') > -1) {
722             def r = null
723             if (val.indexOf('/') == 0) {
724               // absolute path
725               r = file(val)
726             } else if (val.indexOf('/') > 0) {
727               // relative path (relative to jalviewDir)
728               r = file( jalviewDir + '/' + val )
729             }
730             if (r.exists()) {
731               val = getdown_resource_dir + '/' + r.getName()
732               getdownWebsiteResourceFilenames += val
733               getdownResourceFilenames += r.getPath()
734             }
735           }
736           def line = prop.substring(12) + " = " + val + "\n"
737           getdownTextString += line
738         }
739       }
740     }
741
742     getdownWebsiteResourceFilenames.each{ filename ->
743       getdownTextString += "resource = "+filename+"\n"
744     }
745     getdownResourceFilenames.each{ filename ->
746       copy {
747         from filename
748         into project.ext.getdownResourceDir
749       }
750     }
751
752     def codeFiles = []
753     makeDist.outputs.files.each{ f ->
754       if (f.isDirectory()) {
755         def files = fileTree(dir: f, include: ["*"]).getFiles()
756         codeFiles += files
757       } else if (f.exists()) {
758         codeFiles += f
759       }
760     }
761     codeFiles.sort().each{f ->
762       def line = "code = " + getdown_app_dir + '/' + f.getName() + "\n"
763       getdownTextString += line
764       copy {
765         from f.getPath()
766         into project.ext.getdownAppDir
767       }
768     }
769
770     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
771     /*
772      if (JAVA_VERSION.equals("11")) {
773      def j11libFiles = fileTree(dir: "$jalviewDir/$j11libDir", include: ["*.jar"]).getFiles()
774      j11libFiles.sort().each{f ->
775      def line = "code = " + getdown_j11lib_dir + '/' + f.getName() + "\n"
776      getdownTextString += line
777      copy {
778      from f.getPath()
779      into project.ext.getdownJ11libDir
780      }
781      }
782      }
783      */
784
785     // 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.
786     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
787     getdownTextString += "resource = " + file(getdownLauncher).getName() + "\n"
788     getdownTextString += "class = " + mainClass + "\n"
789
790     def getdown_txt = file(project.ext.getdownWebsiteDir + "/getdown.txt")
791     getdown_txt.write(getdownTextString)
792
793     copy {
794       from getdown_txt
795       into project.ext.getdownFilesDir
796     }
797
798     copy {
799       from getdownLauncher
800       into project.ext.getdownFilesDir
801     }
802
803     copy {
804       from getdownLauncher
805       into project.ext.getdownWebsiteDir
806     }
807
808     copy {
809       from jalviewDir + '/' + project.getProperty('getdown_txt_ui.background_image')
810       from jalviewDir + '/' + project.getProperty('getdown_txt_ui.error_background')
811       from jalviewDir + '/' + project.getProperty('getdown_txt_ui.progress_image')
812       from jalviewDir + '/' + project.getProperty('getdown_txt_ui.icon')
813       from jalviewDir + '/' + project.getProperty('getdown_txt_ui.mac_dock_icon')
814       into project.ext.getdownFilesDir + '/' + getdown_resource_dir
815     }
816   }
817
818   inputs.dir(jalviewDir + '/' + packageDir)
819   outputs.dir(project.ext.getdownWebsiteDir)
820   outputs.dir(project.ext.getdownFilesDir)
821 }
822
823 task getdownDigest(type: JavaExec) {
824   group = "distribution"
825   description = "Digest the getdown website folder"
826   dependsOn getdownWebsite
827   classpath = files(jalviewDir + '/' + getdown_core, jalviewDir+'/'+getdown_launcher)
828   main = "com.threerings.getdown.tools.Digester"
829   args project.ext.getdownWebsiteDir
830   inputs.dir(project.ext.getdownWebsiteDir)
831   outputs.file(project.ext.getdownWebsiteDir + '/' + "digest2.txt")
832 }
833
834 task getdown() {
835   group = "distribution"
836   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
837   dependsOn getdownDigest
838 }
839
840 clean {
841   delete project.ext.getdownWebsiteDir
842   delete project.ext.getdownFilesDir
843 }
844
845 install4j {
846   def install4jHomeDir = "/opt/install4j"
847   def hostname = "hostname".execute().text.trim()
848   if (hostname.equals("jv-bamboo")) {
849     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
850   } else if (OperatingSystem.current().isMacOsX()) {
851     install4jHomeDir = '/Applications/install4j.app/Contents/Resources/app'
852     if (! file(install4jHomeDir).exists()) {
853       install4jHomeDir = System.getProperty("user.home")+install4jHomeDir
854     }
855   } else if (OperatingSystem.current().isLinux()) {
856     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
857   }
858   installDir = file(install4jHomeDir)
859   mediaTypes = Arrays.asList(install4jMediaTypes.split(","))
860   if (install4jFaster.equals("true")) {
861     faster = true
862   }
863 }
864
865 def install4jConf
866 def macosJavaVMDir
867 def macosJavaVMTgz
868 def windowsJavaVMDir
869 def windowsJavaVMTgz
870 def install4jDir = "$jalviewDir/$install4jResourceDir"
871 def install4jConfFile = "jalview-installers-java"+JAVA_VERSION+".install4j"
872 install4jConf = "$install4jDir/$install4jConfFile"
873
874 task copyInstall4jTemplate(type: Copy) {
875   macosJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/macos-jre"+JAVA_VERSION+"/jre"
876   macosJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre"+JAVA_VERSION+".tar.gz"
877   windowsJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/windows-jre"+JAVA_VERSION+"/jre"
878   windowsJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre"+JAVA_VERSION+".tar.gz"
879   from (install4jDir) {
880     include install4jTemplate
881     rename (install4jTemplate, install4jConfFile)
882     filter(ReplaceTokens, beginToken: '', endToken: '', tokens: ['9999999999': JAVA_VERSION])
883     filter(ReplaceTokens, beginToken: '$$', endToken: '$$',
884       tokens: [
885         'JAVA_VERSION': JAVA_VERSION,
886         'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
887         'VERSION': JALVIEW_VERSION,
888         'MACOS_JAVA_VM_DIR': macosJavaVMDir,
889         'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
890         'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
891         'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
892         'INSTALL4JINFOPLISTFILEASSOCIATIONS': install4jInfoPlistFileAssociations,
893         'COPYRIGHT_MESSAGE': install4jCopyrightMessage,
894         'MACOS_BUNDLE_ID': install4jMacOSBundleId
895       ]
896     )
897     if (OSX_KEYPASS=="") {
898       filter(ReplaceTokens, beginToken: 'codeSigning macEnabled="', endToken: '"', tokens: ['true':'codeSigning macEnabled="false"'])
899       filter(ReplaceTokens, beginToken: 'runPostProcessor="true" ',endToken: 'Processor', tokens: ['post':'runPostProcessor="false" postProcessor'])
900     }
901   }
902   into install4jDir
903   outputs.files(install4jConf)
904
905   doLast {
906     // include file associations in installer
907     def installerFileAssociationsXml = file("$install4jDir/$install4jInstallerFileAssociations").text
908     ant.replaceregexp(
909       byline: false,
910       flags: "s",
911       match: '<action name="EXTENSIONS_REPLACED_BY_GRADLE".*?</action>',
912       replace: installerFileAssociationsXml,
913       file: install4jConf
914     )
915     /*
916     // include uninstaller applescript app files in dmg
917     def installerDMGUninstallerXml = file("$install4jDir/$install4jDMGUninstallerAppFiles").text
918     ant.replaceregexp(
919       byline: false,
920       flags: "s",
921       match: '<file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file=.*?>',
922       replace: installerDMGUninstallerXml,
923       file: install4jConf
924     )
925     */
926   }
927 }
928
929 task installers(type: com.install4j.gradle.Install4jTask) {
930   group = "distribution"
931   description = "Create the install4j installers"
932   dependsOn getdown
933   dependsOn copyInstall4jTemplate
934   projectFile = file(install4jConf)
935   println("Using projectFile "+projectFile)
936   variables = [majorVersion: version.substring(2, 11), build: 001, OSX_KEYSTORE: OSX_KEYSTORE, JSIGN_SH: JSIGN_SH]
937   destination = "$jalviewDir/$install4jBuildDir/$JAVA_VERSION"
938   buildSelected = true
939
940   if (OSX_KEYPASS) {
941     macKeystorePassword=OSX_KEYPASS
942     
943   }
944   
945   inputs.dir(project.ext.getdownWebsiteDir)
946   inputs.file(install4jConf)
947   inputs.dir(macosJavaVMDir)
948   inputs.dir(windowsJavaVMDir)
949   outputs.dir("$jalviewDir/$install4jBuildDir/$JAVA_VERSION")
950 }
951
952 clean {
953   delete install4jConf
954 }