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