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