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