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