JAL-3111 sign the uninstaller script .. again
[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
822         // clean the getdown website and files dir before creating getdown folders
823         delete project.ext.getdownWebsiteDir
824         delete project.ext.getdownFilesDir
825
826   def getdownWebsiteResourceFilenames = []
827   def getdownTextString = ""
828   def getdownResourceDir = project.ext.getdownResourceDir
829   def getdownAppDir = project.ext.getdownAppDir
830   def getdownResourceFilenames = []
831
832   doFirst {
833     copy {
834       from buildProperties
835       rename(buildPropertiesFile, getdown_build_properties)
836       into project.ext.getdownWebsiteDir
837     }
838     getdownWebsiteResourceFilenames += getdown_build_properties
839
840     // go through properties looking for getdown_txt_...
841     def props = project.properties.sort { it.key }
842     props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
843     props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
844
845     props.put("getdown_txt_appbase", getdown_app_base)
846     props.each{ prop, val ->
847       if (prop.startsWith("getdown_txt_") && val != null) {
848         if (prop.startsWith("getdown_txt_multi_")) {
849           def key = prop.substring(18)
850           val.split(",").each{ v ->
851             def line = key + " = " + v + "\n"
852             getdownTextString += line
853           }
854         } else {
855           // file values rationalised
856           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
857             def r = null
858             if (val.indexOf('/') == 0) {
859               // absolute path
860               r = file(val)
861             } else if (val.indexOf('/') > 0) {
862               // relative path (relative to jalviewDir)
863               r = file( jalviewDir + '/' + val )
864             }
865             if (r.exists()) {
866               val = getdown_resource_dir + '/' + r.getName()
867               getdownWebsiteResourceFilenames += val
868               getdownResourceFilenames += r.getPath()
869             }
870           }
871           if (! prop.startsWith("getdown_txt_resource")) {
872             def line = prop.substring(12) + " = " + val + "\n"
873             getdownTextString += line
874           }
875         }
876       }
877     }
878
879     getdownWebsiteResourceFilenames.each{ filename ->
880       getdownTextString += "resource = "+filename+"\n"
881     }
882     getdownResourceFilenames.each{ filename ->
883       copy {
884         from filename
885         into project.ext.getdownResourceDir
886       }
887     }
888
889     def codeFiles = []
890     fileTree(file(packageDir)).each{ f ->
891       if (f.isDirectory()) {
892         def files = fileTree(dir: f, include: ["*"]).getFiles()
893         codeFiles += files
894       } else if (f.exists()) {
895         codeFiles += f
896       }
897     }
898     codeFiles.sort().each{f ->
899       def line = "code = " + getdown_app_dir + '/' + f.getName() + "\n"
900       getdownTextString += line
901       copy {
902         from f.getPath()
903         into project.ext.getdownAppDir
904       }
905     }
906
907     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
908     /*
909     if (JAVA_VERSION.equals("11")) {
910     def j11libFiles = fileTree(dir: "$jalviewDir/$j11libDir", include: ["*.jar"]).getFiles()
911     j11libFiles.sort().each{f ->
912     def line = "code = " + getdown_j11lib_dir + '/' + f.getName() + "\n"
913     getdownTextString += line
914     copy {
915     from f.getPath()
916     into project.ext.getdownJ11libDir
917     }
918     }
919     }
920      */
921
922     // 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.
923     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
924     getdownTextString += "resource = " + getdown_launcher_new + "\n"
925     getdownTextString += "class = " + mainClass + "\n"
926
927     def getdown_txt = file(project.ext.getdownWebsiteDir + "/getdown.txt")
928     getdown_txt.write(getdownTextString)
929
930     def launch_jvl = file(project.ext.getdownWebsiteDir + "/" + getdown_launch_jvl)
931     launch_jvl.write("appbase="+props.get("getdown_txt_appbase"))
932
933     copy {
934       from getdownLauncher
935       rename(file(getdownLauncher).getName(), getdown_launcher_new)
936       into project.ext.getdownWebsiteDir
937     }
938
939     copy {
940       from getdownLauncher
941       into project.ext.getdownWebsiteDir
942     }
943
944     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
945       copy {
946         from getdown_txt
947         from getdownLauncher
948         from getdownWebsiteDir+"/"+getdown_build_properties
949         into getdownInstallDir
950       }
951
952       copy {
953         from getdownInstallDir
954         into getdownFilesInstallDir
955       }
956     }
957
958     copy {
959       from getdown_txt
960       from launch_jvl
961       from getdownLauncher
962       from getdownWebsiteDir+"/"+getdown_build_properties
963       into getdownFilesDir
964     }
965
966     copy {
967           from getdownResourceDir
968       into project.ext.getdownFilesDir + '/' + getdown_resource_dir
969     }
970   }
971
972   if (buildDist) {
973     inputs.dir(jalviewDir + '/' + packageDir)
974   }
975   outputs.dir(project.ext.getdownWebsiteDir)
976   outputs.dir(project.ext.getdownFilesDir)
977 }
978
979 task getdownDigest(type: JavaExec) {
980   group = "distribution"
981   description = "Digest the getdown website folder"
982   dependsOn getdownWebsite
983   classpath = files(jalviewDir + '/' + getdown_core, jalviewDir+'/'+getdown_launcher)
984   main = "com.threerings.getdown.tools.Digester"
985   args project.ext.getdownWebsiteDir
986   inputs.dir(project.ext.getdownWebsiteDir)
987   outputs.file(project.ext.getdownWebsiteDir + '/' + "digest2.txt")
988 }
989
990 task getdown() {
991   group = "distribution"
992   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
993   dependsOn getdownDigest
994   doLast {
995     if (reportRsyncCommand) {
996       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith("/")?"":"/")
997       def toDir = getdown_rsync_dest + "/" + getdownDir + (getdownDir.endsWith("/")?"":"/")
998       println "LIKELY RSYNC COMMAND:"
999       println "rsync -avh --delete '$fromDir' '$toDir'"
1000       if (RUNRSYNC == "true") {
1001         exec {
1002           commandLine "mkdir", "-p", toDir
1003         }
1004         exec {
1005           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1006         }
1007       }
1008     }
1009   }
1010 }
1011
1012 clean {
1013   delete project.ext.getdownWebsiteDir
1014   delete project.ext.getdownFilesDir
1015 }
1016
1017 install4j {
1018   def install4jHomeDir = "/opt/install4j"
1019   def hostname = "hostname".execute().text.trim()
1020   if (hostname.equals("jv-bamboo")) {
1021     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1022   } else if (OperatingSystem.current().isMacOsX()) {
1023     install4jHomeDir = '/Applications/install4j.app/Contents/Resources/app'
1024     if (! file(install4jHomeDir).exists()) {
1025       install4jHomeDir = System.getProperty("user.home")+install4jHomeDir
1026     }
1027   } else if (OperatingSystem.current().isLinux()) {
1028     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1029   }
1030   installDir = file(install4jHomeDir)
1031   mediaTypes = Arrays.asList(install4jMediaTypes.split(","))
1032   if (install4jFaster.equals("true")) {
1033     faster = true
1034   }
1035 }
1036
1037 def install4jConf
1038 def macosJavaVMDir
1039 def macosJavaVMTgz
1040 def windowsJavaVMDir
1041 def windowsJavaVMTgz
1042 def install4jDir = "$jalviewDir/$install4jResourceDir"
1043 def install4jConfFile = "jalview-installers-java"+JAVA_VERSION+".install4j"
1044 install4jConf = "$install4jDir/$install4jConfFile"
1045
1046 task copyInstall4jTemplate(type: Copy) {
1047   macosJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/macos-jre"+JAVA_VERSION+"/jre"
1048   macosJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre"+JAVA_VERSION+".tar.gz"
1049   windowsJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/windows-jre"+JAVA_VERSION+"/jre"
1050   windowsJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre"+JAVA_VERSION+".tar.gz"
1051   from (install4jDir) {
1052     include install4jTemplate
1053     rename (install4jTemplate, install4jConfFile)
1054     filter(ReplaceTokens, beginToken: '', endToken: '', tokens: ['9999999999': JAVA_VERSION])
1055     filter(ReplaceTokens, beginToken: '$$', endToken: '$$',
1056     tokens: [
1057     'JAVA_VERSION': JAVA_VERSION,
1058     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1059     'VERSION': JALVIEW_VERSION,
1060     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1061     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1062     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1063     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1064     'INSTALL4JINFOPLISTFILEASSOCIATIONS': install4jInfoPlistFileAssociations,
1065     'COPYRIGHT_MESSAGE': install4jCopyrightMessage,
1066     'MACOS_BUNDLE_ID': install4jMacOSBundleId,
1067     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1068     'GETDOWN_DIST_DIR': getdown_app_dir,
1069     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1070     'GETDOWN_INSTALL_DIR': getdown_install_dir
1071     ]
1072     )
1073     if (OSX_KEYPASS=="") {
1074       filter(ReplaceTokens, beginToken: 'codeSigning macEnabled="', endToken: '"', tokens: ['true':'codeSigning macEnabled="false"'])
1075       filter(ReplaceTokens, beginToken: 'runPostProcessor="true" ',endToken: 'Processor', tokens: ['post':'runPostProcessor="false" postProcessor'])
1076     }
1077   }
1078   into install4jDir
1079   outputs.files(install4jConf)
1080
1081   doLast {
1082     // include file associations in installer
1083     def installerFileAssociationsXml = file("$install4jDir/$install4jInstallerFileAssociations").text
1084     ant.replaceregexp(
1085       byline: false,
1086       flags: "s",
1087       match: '<action name="EXTENSIONS_REPLACED_BY_GRADLE".*?</action>',
1088       replace: installerFileAssociationsXml,
1089       file: install4jConf
1090     )
1091     /*
1092     // include uninstaller applescript app files in dmg
1093     def installerDMGUninstallerXml = file("$install4jDir/$install4jDMGUninstallerAppFiles").text
1094     ant.replaceregexp(
1095     byline: false,
1096     flags: "s",
1097     match: '<file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file=.*?>',
1098     replace: installerDMGUninstallerXml,
1099     file: install4jConf
1100     )
1101      */
1102   }
1103 }
1104
1105 task installers(type: com.install4j.gradle.Install4jTask) {
1106   group = "distribution"
1107   description = "Create the install4j installers"
1108   dependsOn getdown
1109   dependsOn copyInstall4jTemplate
1110   projectFile = file(install4jConf)
1111   println("Using projectFile "+projectFile)
1112   variables = [majorVersion: version.substring(2, 11), build: 001, OSX_KEYSTORE: OSX_KEYSTORE, JSIGN_SH: JSIGN_SH]
1113   destination = "$jalviewDir/$install4jBuildDir/$JAVA_VERSION"
1114   buildSelected = true
1115
1116   if (OSX_KEYPASS) {
1117     macKeystorePassword=OSX_KEYPASS
1118
1119   }
1120
1121   inputs.dir(project.ext.getdownWebsiteDir)
1122   inputs.file(install4jConf)
1123   inputs.dir(macosJavaVMDir)
1124   inputs.dir(windowsJavaVMDir)
1125   outputs.dir("$jalviewDir/$install4jBuildDir/$JAVA_VERSION")
1126
1127 }
1128
1129 clean {
1130   delete install4jConf
1131 }
1132