815c6d2a27262feb6c1981445d472173613c8557
[jalview.git] / build.gradle
1 import org.apache.tools.ant.filters.ReplaceTokens
2 import org.gradle.internal.os.OperatingSystem
3 import org.gradle.plugins.ide.eclipse.model.Output
4 import org.gradle.plugins.ide.eclipse.model.Library
5
6 import groovy.transform.ExternalizeMethods
7
8 buildscript {
9   dependencies {
10     classpath 'org.openclover:clover:4.3.1'
11   }
12 }
13
14 plugins {
15   id 'java'
16   id 'application'
17   id 'eclipse'
18   id 'com.github.johnrengelman.shadow' version '4.0.3'
19   id 'com.install4j.gradle' version '7.0.9'
20 }
21
22 repositories {
23   jcenter()
24   mavenCentral()
25   mavenLocal()
26   flatDir {
27     dirs gradlePluginsDir
28   }
29 }
30
31 dependencies {
32   compile 'org.apache.commons:commons-compress:1.18'
33 }
34
35 mainClassName = launcherClass
36 def cloverInstrDir = file("$buildDir/$cloverSourcesInstrDir")
37 def classes = "$jalviewDir/$classesDir"
38
39 if (clover.equals("true")) {
40   use_clover = true
41   classes = "$buildDir/$cloverClassesDir"
42 } else {
43   use_clover = false
44   classes = "$jalviewDir/$classesDir"
45 }
46
47 // configure classpath/args for j8/j11 compilation
48
49 def jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
50 def libDir
51 def libDistDir
52 def compile_source_compatibility
53 def compile_target_compatibility
54
55 ext {
56   // local build environment properties
57   def localProps = "${jalviewDir}/local.properties"
58   if (file(localProps).exists()) {
59     def p = new Properties()
60     def localPropsFIS = new FileInputStream(localProps)
61     p.load(localPropsFIS)
62     localPropsFIS.close()
63     p.each {
64       key, val -> 
65       def over = project.properties.get(key) != null
66       project.properties.put(key, val)
67       if (over) {
68         println("Overriding property '${key}' with local.properties value")
69       }
70     }
71   }
72   getdownWebsiteDir = "${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}"
73   getdownDir = ""
74   reportRsyncCmd = false
75   buildDist = true
76   buildProperties = buildPropertiesFile
77   getdownLauncher = "${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}"
78   switch (CHANNEL) {
79
80     case "BUILD":
81     // TODO: get bamboo build artifact URL for getdown artifacts
82     getdown_channel_base = bamboo_channelbase
83     getdown_channel_name = "${bamboo_planKey}/${JAVA_VERSION}"
84     getdown_app_base = "${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}"
85     getdown_app_dir = getdown_app_dir_alt
86     buildProperties = "${jalviewDir}/${classesDir}/${buildPropertiesFile}"
87     break
88
89     case "RELEASE":
90     getdown_channel_name = CHANNEL.toLowerCase()
91     getdownDir = "${getdown_channel_name}/${JAVA_VERSION}"
92     getdown_app_base = "${getdown_channel_base}/${getdownDir}"
93     getdown_app_dir = getdown_app_dir_release
94     buildProperties = "${jalviewDir}/${classesDir}/${buildPropertiesFile}"
95     reportRsyncCommand = true
96     break
97
98     case "ARCHIVE":
99     getdown_channel_name = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
100     getdownDir = "${getdown_channel_name}/${JAVA_VERSION}"
101     getdown_app_base = "${getdown_channel_base}/${getdownDir}"
102     getdown_app_dir = getdown_app_dir_alt
103     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
104       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
105       exit
106     } else {
107       packageDir = "${ARCHIVEDIR}/${packageDir}"
108       buildProperties = "${ARCHIVEDIR}/${classesDir}/${buildPropertiesFile}"
109       buildDist = false
110     }
111     reportRsyncCommand = true
112     break
113
114     case "ARCHIVELOCAL":
115     getdown_channel_name = "archive/${JALVIEW_VERSION}"
116     getdownDir = "${getdown_channel_name}/${JAVA_VERSION}"
117     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
118     getdown_app_dir = getdown_app_dir_alt
119     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
120       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
121       exit
122     } else {
123       packageDir = "${ARCHIVEDIR}/${packageDir}"
124       buildProperties = "${ARCHIVEDIR}/${classesDir}/${buildPropertiesFile}"
125       buildDist = false
126     }
127     reportRsyncCommand = true
128     getdownLauncher = "${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}"
129     break
130
131     case "DEVELOP":
132     getdown_channel_name = CHANNEL.toLowerCase()
133     getdownDir = "${getdown_channel_name}/${JAVA_VERSION}"
134     getdown_app_base = "${getdown_channel_base}/${getdownDir}"
135     getdown_app_dir = getdown_app_dir_alt
136     buildProperties = "${jalviewDir}/${classesDir}/${buildPropertiesFile}"
137     reportRsyncCommand = true
138     break
139
140     case "TEST-RELEASE":
141     getdown_channel_name = CHANNEL.toLowerCase()
142     getdownDir = "${getdown_channel_name}/${JAVA_VERSION}"
143     getdown_app_base = "${getdown_channel_base}/${getdownDir}"
144     getdown_app_dir = getdown_app_dir_alt
145     buildProperties = "${jalviewDir}/${classesDir}/${buildPropertiesFile}"
146     reportRsyncCommand = true
147     break
148
149     case ~/^SCRATCH(|-[-\w]*)$/:
150     getdown_channel_name = CHANNEL
151     getdownDir = "${getdown_channel_name}/${JAVA_VERSION}"
152     getdown_app_base = "${getdown_channel_base}/${getdownDir}"
153     getdown_app_dir = getdown_app_dir_alt
154     buildProperties = "${jalviewDir}/${classesDir}/${buildPropertiesFile}"
155     reportRsyncCommand = true
156     break
157
158     case "LOCAL":
159     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
160     getdown_app_dir = getdown_app_dir_alt
161     buildProperties = "${jalviewDir}/${classesDir}/${buildPropertiesFile}"
162     getdownLauncher = "${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}"
163     break
164
165     default: // something wrong specified
166     print("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
167     exit
168     break
169
170   }
171
172   getdownAppDir = "${getdownWebsiteDir}/${getdown_app_dir}"
173   //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
174   getdownResourceDir = "${getdownWebsiteDir}/${getdown_resource_dir}"
175   getdownInstallDir = "${getdownWebsiteDir}/${getdown_install_dir}"
176   getdownFilesDir = "${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/"
177   getdownFilesInstallDir = "${getdownFilesDir}/${getdown_install_dir}"
178   /* compile without modules -- using classpath libraries
179   modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
180   modules_runtimeClasspath = modules_compileClasspath
181   */
182   gitHash = ""
183   gitBranch = ""
184
185   println("Using a ${CHANNEL} profile.")
186 }
187
188 def JAVA_INTEGER_VERSION
189 def additional_compiler_args = []
190 // these are getdown.txt properties defined dependent on the JAVA_VERSION
191 def getdown_alt_java_min_version
192 def getdown_alt_java_max_version
193 // this property is assigned below and expanded to multiple lines in the getdown task
194 def getdown_alt_multi_java_location
195 // this property is for the Java library used in eclipse
196 def eclipse_java_runtime_name
197 if (JAVA_VERSION.equals("1.8")) {
198   JAVA_INTEGER_VERSION = "8"
199   //libDir = j8libDir
200   libDir = j11libDir
201   libDistDir = j8libDir
202   compile_source_compatibility = 1.8
203   compile_target_compatibility = 1.8
204   getdown_alt_java_min_version = getdown_alt_java8_min_version
205   getdown_alt_java_max_version = getdown_alt_java8_max_version
206   getdown_alt_multi_java_location = getdown_alt_java8_txt_multi_java_location
207   eclipse_java_runtime_name = "JavaSE-1.8"
208 } else if (JAVA_VERSION.equals("11")) {
209   JAVA_INTEGER_VERSION = "11"
210   libDir = j11libDir
211   libDistDir = j11libDir
212   compile_source_compatibility = 11
213   compile_target_compatibility = 11
214   getdown_alt_java_min_version = getdown_alt_java11_min_version
215   getdown_alt_java_max_version = getdown_alt_java11_max_version
216   getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
217   eclipse_java_runtime_name = "JavaSE-11"
218   /* compile without modules -- using classpath libraries
219   additional_compiler_args += [
220   '--module-path', ext.modules_compileClasspath.asPath,
221   '--add-modules', j11modules
222   ]
223   */
224 } else if (JAVA_VERSION.equals("12") || JAVA_VERSION.equals("13")) {
225   JAVA_INTEGER_VERSION = JAVA_VERSION
226   libDir = j11libDir
227   libDistDir = j11libDir
228   compile_source_compatibility = JAVA_VERSION
229   compile_target_compatibility = JAVA_VERSION
230   getdown_alt_java_min_version = getdown_alt_java11_min_version
231   getdown_alt_java_max_version = getdown_alt_java11_max_version
232   getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
233   eclipse_java_runtime_name = "JavaSE-11"
234   /* compile without modules -- using classpath libraries
235   additional_compiler_args += [
236   '--module-path', ext.modules_compileClasspath.asPath,
237   '--add-modules', j11modules
238   ]
239   */
240 } else {
241   throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
242 }
243
244 sourceSets {
245
246   main {
247     java {
248       srcDirs "${jalviewDir}/${sourceDir}"
249       outputDir = file("${classes}")
250     }
251
252     resources {
253       srcDirs "${jalviewDir}/${resourceDir}"
254     }
255
256     jar.destinationDir = file("${jalviewDir}/${packageDir}")
257
258     compileClasspath = files(sourceSets.main.java.outputDir)
259     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
260
261     runtimeClasspath = compileClasspath
262   }
263
264   clover {
265     java {
266       srcDirs = [ cloverInstrDir ]
267       outputDir = file("${buildDir}/${cloverClassesDir}")
268     }
269
270     resources {
271       srcDirs = sourceSets.main.resources.srcDirs
272     }
273     compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
274     compileClasspath += files(sourceSets.main.java.outputDir)
275     compileClasspath += sourceSets.main.compileClasspath
276     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"])
277     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
278
279     runtimeClasspath = compileClasspath
280   }
281
282   test {
283     java {
284       srcDirs "${jalviewDir}/${testSourceDir}"
285       outputDir = file("${jalviewDir}/${testOutputDir}")
286     }
287
288     resources {
289       srcDirs = sourceSets.main.resources.srcDirs
290     }
291
292     compileClasspath = files( sourceSets.test.java.outputDir )
293
294     if (use_clover) {
295       compileClasspath += sourceSets.clover.compileClasspath
296     } else {
297       compileClasspath += files(sourceSets.main.java.outputDir)
298     }
299
300     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
301     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"])
302     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"])
303
304     runtimeClasspath = compileClasspath
305   }
306 }
307
308 // clover bits
309 dependencies {
310   if (use_clover) {
311     cloverCompile 'org.openclover:clover:4.3.1'
312     testCompile 'org.openclover:clover:4.3.1'
313   }
314 }
315
316 configurations {
317   cloverRuntime
318   cloverRuntime.extendsFrom cloverCompile
319 }
320
321 eclipse {
322   project {
323     name = eclipse_project_name
324
325     natures 'org.eclipse.jdt.core.javanature',
326     'org.eclipse.jdt.groovy.core.groovyNature',
327     'org.eclipse.buildship.core.gradleprojectnature'
328
329     buildCommand 'org.eclipse.jdt.core.javabuilder'
330     buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
331   }
332
333   classpath {
334     //defaultOutputDir = sourceSets.main.java.outputDir
335     def removeThese = []
336     configurations.each{
337       if (it.isCanBeResolved()) {
338         removeThese += it
339       }
340     }
341
342     minusConfigurations += removeThese
343     plusConfigurations = [ ]
344     file {
345
346       whenMerged { cp ->
347         def removeTheseToo = []
348         HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
349         cp.entries.each { entry ->
350           if (entry.kind == 'src') {
351             if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == sourceDir || entry.path == testSourceDir)) {
352               removeTheseToo += entry
353             } else {
354               alreadyAddedSrcPath.putAt(entry.path, true)
355             }
356           }
357         }
358         cp.entries.removeAll(removeTheseToo)
359
360         if (file("${jalviewDir}/${eclipse_bin_dir}/main").isDirectory()) {
361           cp.entries += new Output("${eclipse_bin_dir}/main")
362         }
363         if (file(helpParentDir).isDirectory()) {
364           cp.entries += new Library(fileReference(helpParentDir))
365         }
366         if (file(resourceDir).isDirectory()) {
367           cp.entries += new Library(fileReference(resourceDir))
368         }
369
370         HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
371
372         sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.each {
373           //don't want to add outputDir as eclipse is using its own output dir in bin/main
374           if (it.isDirectory() || ! it.exists()) {
375             // don't add dirs to classpath
376             return
377           }
378           def itPath = it.toString()
379           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
380             // make relative path
381             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
382           }
383           if (alreadyAddedLibPath.get(itPath)) {
384             //println("Not adding duplicate entry "+itPath)
385           } else {
386             //println("Adding entry "+itPath)
387             cp.entries += new Library(fileReference(itPath))
388             alreadyAddedLibPath.put(itPath, true)
389           }
390         }
391
392         //fileTree(dir: "$jalviewDir/$utilsDir", include: ["test*/*.jar"]).each {
393         sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.each {
394           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
395           if (it.isDirectory() || ! it.exists()) {
396             // don't add dirs to classpath
397             return false // groovy "break" in .each closure
398           }
399           def itPath = it.toString()
400           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
401             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
402           }
403           if (alreadyAddedLibPath.get(itPath)) {
404             // don't duplicate
405           } else {
406             def lib = new Library(fileReference(itPath))
407             lib.entryAttributes["test"] = "true"
408             cp.entries += lib
409             alreadyAddedLibPath.put(itPath, true)
410           }
411         }
412
413       } // whenMerged
414
415     } // file
416
417     containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
418
419   } // classpath
420
421   jdt {
422     // for the IDE, use java 11 compatibility
423     sourceCompatibility = compile_source_compatibility
424     targetCompatibility = compile_target_compatibility
425     javaRuntimeName = eclipse_java_runtime_name
426
427     // add in jalview project specific properties/preferences into eclipse core preferences
428     file {
429       withProperties { props ->
430         def jalview_prefs = new Properties()
431         def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
432         jalview_prefs.load(ins)
433         ins.close()
434         jalview_prefs.forEach { t, v ->
435           if (props.getAt(t) == null) {
436             props.putAt(t, v)
437           }
438         }
439       }
440     }
441
442   } // jdt
443
444 }
445
446 task cloverInstr() {
447   // only instrument source, we build test classes as normal
448   inputs.files files (sourceSets.main.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"]))
449   outputs.dir cloverInstrDir
450
451   doFirst {
452     delete cloverInstrDir
453     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
454     "-d", "${buildDir}/${cloverSourcesInstrDir}"]
455     argsList.addAll(inputs.files.files.collect({ file ->
456       file.absolutePath
457     }))
458     String[] args = argsList.toArray()
459     println("About to instrument "+args.length +" files")
460     com.atlassian.clover.CloverInstr.mainImpl(args)
461   }
462 }
463
464
465 task cloverReport {
466   group = "Verification"
467     description = "Createst the Clover report"
468     inputs.dir "${buildDir}/clover"
469     outputs.dir "${reportsDir}/clover"
470     onlyIf {
471       file("${buildDir}/clover/clover.db").exists()
472     }
473   doFirst {
474     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
475     "-o", "${reportsDir}/clover"]
476     String[] args = argsList.toArray()
477     com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
478
479     // and generate ${reportsDir}/clover/clover.xml
480     args = ["--initstring", "${buildDir}/clover/clover.db",
481     "-o", "${reportsDir}/clover/clover.xml"].toArray()
482     com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
483   }
484 }
485
486 // end clover bits
487
488
489 compileJava {
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 compileTestJava {
501   if (use_clover) {
502     dependsOn compileCloverJava
503     classpath += configurations.cloverRuntime
504   } else {
505     classpath += sourceSets.main.runtimeClasspath
506   }
507   doFirst {
508     sourceCompatibility = compile_source_compatibility
509     targetCompatibility = compile_target_compatibility
510     options.compilerArgs = additional_compiler_args
511     print ("Setting target compatibility to "+targetCompatibility+"\n")
512   }
513 }
514
515
516 compileCloverJava {
517
518   doFirst {
519     sourceCompatibility = compile_source_compatibility
520     targetCompatibility = compile_target_compatibility
521     options.compilerArgs += additional_compiler_args
522     print ("Setting target compatibility to "+targetCompatibility+"\n")
523   }
524   classpath += configurations.cloverRuntime
525 }
526
527 clean {
528   doFirst {
529     delete sourceSets.main.java.outputDir
530   }
531 }
532
533 cleanTest {
534   doFirst {
535     delete sourceSets.test.java.outputDir
536     delete cloverInstrDir
537   }
538 }
539
540 // format is a string like date.format("dd MMMM yyyy")
541 def getDate(format) {
542   def date = new Date()
543   return date.format(format)
544 }
545
546 task setGitVals {
547   def hashStdOut = new ByteArrayOutputStream()
548   exec {
549     commandLine "git", "rev-parse", "--short", "HEAD"
550     standardOutput = hashStdOut
551     ignoreExitValue true
552   }
553
554   def branchStdOut = new ByteArrayOutputStream()
555   exec {
556     commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
557     standardOutput = branchStdOut
558     ignoreExitValue true
559   }
560
561   project.ext.gitHash = hashStdOut.toString().trim()
562   project.ext.gitBranch = branchStdOut.toString().trim()
563
564   outputs.upToDateWhen { false }
565 }
566
567 task createBuildProperties(type: WriteProperties) {
568   dependsOn setGitVals
569   inputs.dir("${jalviewDir}/${sourceDir}")
570   inputs.dir("${classes}")
571   inputs.dir("${jalviewDir}/${resourceDir}")
572   outputFile (buildProperties)
573   // taking time specific comment out to allow better incremental builds
574   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
575   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
576   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
577   property "VERSION", JALVIEW_VERSION
578   property "INSTALLATION", INSTALLATION+" git-commit:"+project.ext.gitHash+" ["+project.ext.gitBranch+"]"
579   outputs.file(outputFile)
580 }
581
582 def buildingHTML = "${jalviewDir}/${docDir}/building.html"
583 task cleanBuildingHTML(type: Delete) {
584   doFirst {
585     delete buildingHTML
586   }
587 }
588
589 task convertBuildingMD(type: Exec) {
590   dependsOn cleanBuildingHTML
591   def buildingMD = "${jalviewDir}/${docDir}/building.md"
592   def css = "${jalviewDir}/${docDir}/github.css"
593
594   def pandoc = null
595   pandoc_exec.split(",").each {
596     if (file(it.trim()).exists()) {
597       pandoc = it.trim()
598       return true
599     }
600   }
601
602   def hostname = "hostname".execute().text.trim()
603   if ((pandoc == null || ! file(pandoc).exists()) && hostname.equals("jv-bamboo")) {
604     pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
605   }
606
607   doFirst {
608     if (pandoc != null && file(pandoc).exists()) {
609         commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
610     } else {
611         println("Cannot find pandoc. Skipping convert building.md to HTML")
612         throw new StopExecutionException()
613     }
614   }
615
616   ignoreExitValue true
617
618   inputs.file(buildingMD)
619   inputs.file(css)
620   outputs.file(buildingHTML)
621 }
622
623 clean {
624   doFirst {
625     delete buildingHTML
626   }
627 }
628
629 task syncDocs(type: Sync) {
630   dependsOn convertBuildingMD
631   def syncDir = "${classes}/${docDir}"
632   from fileTree("${jalviewDir}/${docDir}")
633   into syncDir
634
635 }
636
637 def helpFile = "${classes}/${helpDir}/help.jhm"
638
639 task copyHelp(type: Copy) {
640   def inputDir = "${jalviewDir}/${helpParentDir}/${helpDir}"
641   def outputDir = "${classes}/${helpDir}"
642   from(inputDir) {
643     exclude '**/*.gif'
644       exclude '**/*.jpg'
645       exclude '**/*.png'
646       filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION,'Year-Rel': getDate("yyyy")])
647   }
648   from(inputDir) {
649     include '**/*.gif'
650     include '**/*.jpg'
651     include '**/*.png'
652   }
653   into outputDir
654
655   inputs.dir(inputDir)
656   outputs.files(helpFile)
657   outputs.dir(outputDir)
658 }
659
660 task syncLib(type: Sync) {
661   def syncDir = "${classes}/${libDistDir}"
662   from fileTree("${jalviewDir}/${libDistDir}")
663   into syncDir
664 }
665
666 task syncResources(type: Sync) {
667   from "${jalviewDir}/${resourceDir}"
668   include "**/*.*"
669   into "${classes}"
670   preserve {
671     include "**"
672   }
673 }
674
675 task prepare {
676   dependsOn syncResources
677   dependsOn syncDocs
678   dependsOn copyHelp
679 }
680
681
682 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
683 test {
684   dependsOn prepare
685   dependsOn compileJava
686   if (use_clover) {
687     dependsOn cloverInstr
688   }
689
690   if (use_clover) {
691     print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
692   }
693
694   useTestNG() {
695     includeGroups testngGroups
696     preserveOrder true
697     useDefaultListeners=true
698   }
699
700   workingDir = jalviewDir
701   //systemProperties 'clover.jar' System.properties.clover.jar
702   sourceCompatibility = compile_source_compatibility
703   targetCompatibility = compile_target_compatibility
704   jvmArgs += additional_compiler_args
705
706 }
707
708 task buildIndices(type: JavaExec) {
709   dependsOn copyHelp
710   classpath = sourceSets.main.compileClasspath
711   main = "com.sun.java.help.search.Indexer"
712   workingDir = "${classes}/${helpDir}"
713   def argDir = "html"
714   args = [ argDir ]
715   inputs.dir("${workingDir}/${argDir}")
716
717   outputs.dir("${classes}/doc")
718   outputs.dir("${classes}/help")
719   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
720   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
721   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
722   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
723   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
724   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
725 }
726
727 task compileLinkCheck(type: JavaCompile) {
728   options.fork = true
729   classpath = files("${jalviewDir}/${utilsDir}")
730   destinationDir = file("${jalviewDir}/${utilsDir}")
731   source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
732
733   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
734   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
735   outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class")
736   outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class")
737 }
738
739 def helplinkscheckertouchfile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.touch")
740 task linkCheck(type: JavaExec) {
741   dependsOn prepare, compileLinkCheck
742   classpath = files("${jalviewDir}/${utilsDir}")
743   main = "HelpLinksChecker"
744   workingDir = jalviewDir
745   def help = "${classes}/${helpDir}"
746   args = [ "${classes}/${helpDir}", "-nointernet" ]
747
748   doLast {
749     helplinkscheckertouchfile.createNewFile()
750   }
751
752   inputs.dir("${classes}/${helpDir}")
753   outputs.file(helplinkscheckertouchfile)
754 }
755
756 // import the pubhtmlhelp target
757 ant.properties.basedir = "${jalviewDir}"
758 ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes}/${helpDir}"
759 ant.importBuild "${utilsDir}/publishHelp.xml"
760
761
762 task cleanPackageDir(type: Delete) {
763   doFirst {
764     delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar")
765   }
766 }
767
768 jar {
769   dependsOn linkCheck
770   dependsOn buildIndices
771   dependsOn createBuildProperties
772
773   manifest {
774     attributes "Main-Class": mainClass,
775     "Permissions": "all-permissions",
776     "Application-Name": "Jalview Desktop",
777     "Codebase": application_codebase
778   }
779
780   destinationDir = file("${jalviewDir}/${packageDir}")
781   archiveName = rootProject.name+".jar"
782
783   exclude "cache*/**"
784   exclude "*.jar"
785   exclude "*.jar.*"
786   exclude "**/*.jar"
787   exclude "**/*.jar.*"
788
789   inputs.dir("${classes}")
790   outputs.file("${jalviewDir}/${packageDir}/${archiveName}")
791 }
792
793 task copyJars(type: Copy) {
794   from fileTree(dir: "${classes}", include: "**/*.jar").files
795   into "${jalviewDir}/${packageDir}"
796 }
797
798 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
799 task syncJars(type: Sync) {
800   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
801   into "${jalviewDir}/${packageDir}"
802   preserve {
803     include jar.archiveName
804   }
805 }
806
807 task makeDist {
808   group = "build"
809   description = "Put all required libraries in dist"
810   // order of "cleanPackageDir", "copyJars", "jar" important!
811   jar.mustRunAfter cleanPackageDir
812   syncJars.mustRunAfter cleanPackageDir
813   dependsOn cleanPackageDir
814   dependsOn syncJars
815   dependsOn jar
816   outputs.dir("${jalviewDir}/${packageDir}")
817 }
818
819 task cleanDist {
820   dependsOn cleanPackageDir
821   dependsOn cleanTest
822   dependsOn clean
823 }
824
825 shadowJar {
826   group = "distribution"
827   if (buildDist) {
828     dependsOn makeDist
829   }
830   from ("${jalviewDir}/${libDistDir}") {
831     include("*.jar")
832   }
833   manifest {
834     attributes 'Implementation-Version': JALVIEW_VERSION
835   }
836   mainClassName = shadowJarMainClass
837   mergeServiceFiles()
838   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
839   minimize()
840 }
841
842 task getdownWebsite() {
843   group = "distribution"
844   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
845   if (buildDist) {
846     dependsOn makeDist
847   }
848
849   def getdownWebsiteResourceFilenames = []
850   def getdownTextString = ""
851   def getdownResourceDir = project.ext.getdownResourceDir
852   def getdownAppDir = project.ext.getdownAppDir
853   def getdownResourceFilenames = []
854
855   doFirst {
856     // clean the getdown website and files dir before creating getdown folders
857     delete project.ext.getdownWebsiteDir
858     delete project.ext.getdownFilesDir
859
860     copy {
861       from buildProperties
862       rename(buildPropertiesFile, getdown_build_properties)
863       into project.ext.getdownAppDir
864     }
865     getdownWebsiteResourceFilenames += "${getdown_app_dir}/${getdown_build_properties}"
866
867     // go through properties looking for getdown_txt_...
868     def props = project.properties.sort { it.key }
869     if (getdown_alt_java_min_version.length() > 0) {
870       props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
871     }
872     if (getdown_alt_java_max_version.length() > 0) {
873       props.put("getdown_txt_java_max_version", getdown_alt_java_max_version)
874     }
875     props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
876
877     props.put("getdown_txt_appbase", getdown_app_base)
878     props.each{ prop, val ->
879       if (prop.startsWith("getdown_txt_") && val != null) {
880         if (prop.startsWith("getdown_txt_multi_")) {
881           def key = prop.substring(18)
882           val.split(",").each{ v ->
883             def line = "${key} = ${v}\n"
884             getdownTextString += line
885           }
886         } else {
887           // file values rationalised
888           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
889             def r = null
890             if (val.indexOf('/') == 0) {
891               // absolute path
892               r = file(val)
893             } else if (val.indexOf('/') > 0) {
894               // relative path (relative to jalviewDir)
895               r = file( "${jalviewDir}/${val}" )
896             }
897             if (r.exists()) {
898               val = "${getdown_resource_dir}/" + r.getName()
899               getdownWebsiteResourceFilenames += val
900               getdownResourceFilenames += r.getPath()
901             }
902           }
903           if (! prop.startsWith("getdown_txt_resource")) {
904             def line = prop.substring(12) + " = ${val}\n"
905             getdownTextString += line
906           }
907         }
908       }
909     }
910
911     getdownWebsiteResourceFilenames.each{ filename ->
912       getdownTextString += "resource = ${filename}\n"
913     }
914     getdownResourceFilenames.each{ filename ->
915       copy {
916         from filename
917         into project.ext.getdownResourceDir
918       }
919     }
920
921     def codeFiles = []
922     fileTree(file(packageDir)).each{ f ->
923       if (f.isDirectory()) {
924         def files = fileTree(dir: f, include: ["*"]).getFiles()
925         codeFiles += files
926       } else if (f.exists()) {
927         codeFiles += f
928       }
929     }
930     codeFiles.sort().each{f ->
931       def name = f.getName()
932       def line = "code = ${getdown_app_dir}/${name}\n"
933       getdownTextString += line
934       copy {
935         from f.getPath()
936         into project.ext.getdownAppDir
937       }
938     }
939
940     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
941     /*
942     if (JAVA_VERSION.equals("11")) {
943     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
944     j11libFiles.sort().each{f ->
945     def name = f.getName()
946     def line = "code = ${getdown_j11lib_dir}/${name}\n"
947     getdownTextString += line
948     copy {
949     from f.getPath()
950     into project.ext.getdownJ11libDir
951     }
952     }
953     }
954      */
955
956     // 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.
957     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
958     getdownTextString += "resource = ${getdown_launcher_new}\n"
959     getdownTextString += "class = ${mainClass}\n"
960
961     def getdown_txt = file("${project.ext.getdownWebsiteDir}/getdown.txt")
962     getdown_txt.write(getdownTextString)
963
964     def launch_jvl = file("${project.ext.getdownWebsiteDir}/${getdown_launch_jvl}")
965     launch_jvl.write("appbase="+props.get("getdown_txt_appbase"))
966
967     copy {
968       from getdownLauncher
969       rename(file(getdownLauncher).getName(), getdown_launcher_new)
970       into project.ext.getdownWebsiteDir
971     }
972
973     copy {
974       from getdownLauncher
975       if (file(getdownLauncher).getName() != getdown_launcher) {
976         rename(file(getdownLauncher).getName(), getdown_launcher)
977       }
978       into project.ext.getdownWebsiteDir
979     }
980
981     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
982       copy {
983         from getdown_txt
984         from getdownLauncher
985         from "${getdownWebsiteDir}/${getdown_build_properties}"
986         if (file(getdownLauncher).getName() != getdown_launcher) {
987           rename(file(getdownLauncher).getName(), getdown_launcher)
988         }
989         into getdownInstallDir
990       }
991
992       copy {
993         from getdownInstallDir
994         into getdownFilesInstallDir
995       }
996     }
997
998     copy {
999       from getdown_txt
1000       from launch_jvl
1001       from getdownLauncher
1002       from "${getdownWebsiteDir}/${getdown_build_properties}"
1003       if (file(getdownLauncher).getName() != getdown_launcher) {
1004         rename(file(getdownLauncher).getName(), getdown_launcher)
1005       }
1006       into getdownFilesDir
1007     }
1008
1009     copy {
1010       from getdownResourceDir
1011       into "${project.ext.getdownFilesDir}/${getdown_resource_dir}"
1012     }
1013   }
1014
1015   if (buildDist) {
1016     inputs.dir("${jalviewDir}/${packageDir}")
1017   }
1018   outputs.dir(project.ext.getdownWebsiteDir)
1019   outputs.dir(project.ext.getdownFilesDir)
1020 }
1021
1022 task getdownDigest(type: JavaExec) {
1023   group = "distribution"
1024   description = "Digest the getdown website folder"
1025   dependsOn getdownWebsite
1026   doFirst {
1027     classpath = files("${getdownWebsiteDir}/${getdown_launcher}")
1028   }
1029   main = "com.threerings.getdown.tools.Digester"
1030   args project.ext.getdownWebsiteDir
1031   inputs.dir(project.ext.getdownWebsiteDir)
1032   outputs.file("${project.ext.getdownWebsiteDir}/digest2.txt")
1033 }
1034
1035 task getdown() {
1036   group = "distribution"
1037   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1038   dependsOn getdownDigest
1039   doLast {
1040     if (reportRsyncCommand) {
1041       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
1042       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1043       println "LIKELY RSYNC COMMAND:"
1044       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1045       if (RUNRSYNC == "true") {
1046         exec {
1047           commandLine "mkdir", "-p", toDir
1048         }
1049         exec {
1050           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1051         }
1052       }
1053     }
1054   }
1055 }
1056
1057 clean {
1058   doFirst {
1059     delete project.ext.getdownWebsiteDir
1060     delete project.ext.getdownFilesDir
1061   }
1062 }
1063
1064 install4j {
1065   def install4jHomeDir = "/opt/install4j"
1066   def hostname = "hostname".execute().text.trim()
1067   if (hostname.equals("jv-bamboo")) {
1068     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1069   } else if (OperatingSystem.current().isMacOsX()) {
1070     install4jHomeDir = '/Applications/install4j.app/Contents/Resources/app'
1071     if (! file(install4jHomeDir).exists()) {
1072       install4jHomeDir = System.getProperty("user.home")+install4jHomeDir
1073     }
1074   } else if (OperatingSystem.current().isLinux()) {
1075     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1076   }
1077   installDir = file(install4jHomeDir)
1078   mediaTypes = Arrays.asList(install4jMediaTypes.split(","))
1079   if (install4jFaster.equals("true")) {
1080     faster = true
1081   }
1082 }
1083
1084 def install4jConf
1085 def macosJavaVMDir
1086 def macosJavaVMTgz
1087 def windowsJavaVMDir
1088 def windowsJavaVMTgz
1089 def install4jDir = "${jalviewDir}/${install4jResourceDir}"
1090 def install4jConfFile = "jalview-installers-java${JAVA_VERSION}.install4j"
1091 install4jConf = "${install4jDir}/${install4jConfFile}"
1092
1093 task copyInstall4jTemplate(type: Copy) {
1094   macosJavaVMDir = "${System.env.HOME}/buildtools/jre/openjdk-java_vm/getdown/macos-jre${JAVA_VERSION}/jre"
1095   macosJavaVMTgz = "${System.env.HOME}/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre${JAVA_VERSION}.tar.gz"
1096   windowsJavaVMDir = "${System.env.HOME}/buildtools/jre/openjdk-java_vm/getdown/windows-jre${JAVA_VERSION}/jre"
1097   windowsJavaVMTgz = "${System.env.HOME}/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre${JAVA_VERSION}.tar.gz"
1098   from (install4jDir) {
1099     include install4jTemplate
1100     rename (install4jTemplate, install4jConfFile)
1101     filter(ReplaceTokens, beginToken: '', endToken: '', tokens: ['9999999999': JAVA_VERSION])
1102     filter(ReplaceTokens, beginToken: '$$', endToken: '$$',
1103     tokens: [
1104     'JAVA_VERSION': JAVA_VERSION,
1105     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1106     'VERSION': JALVIEW_VERSION,
1107     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1108     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1109     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1110     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1111     'INSTALL4JINFOPLISTFILEASSOCIATIONS': install4jInfoPlistFileAssociations,
1112     'COPYRIGHT_MESSAGE': install4jCopyrightMessage,
1113     'MACOS_BUNDLE_ID': install4jMacOSBundleId,
1114     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1115     'GETDOWN_DIST_DIR': getdown_app_dir,
1116     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1117     'GETDOWN_INSTALL_DIR': getdown_install_dir
1118     ]
1119     )
1120     if (OSX_KEYPASS=="") {
1121       filter(ReplaceTokens, beginToken: 'codeSigning macEnabled="', endToken: '"', tokens: ['true':'codeSigning macEnabled="false"'])
1122       filter(ReplaceTokens, beginToken: 'runPostProcessor="true" ',endToken: 'Processor', tokens: ['post':'runPostProcessor="false" postProcessor'])
1123     }
1124   }
1125   into install4jDir
1126   outputs.files(install4jConf)
1127
1128   doLast {
1129     // include file associations in installer
1130     def installerFileAssociationsXml = file("${install4jDir}/${install4jInstallerFileAssociations}").text
1131     ant.replaceregexp(
1132       byline: false,
1133       flags: "s",
1134       match: '<action name="EXTENSIONS_REPLACED_BY_GRADLE".*?</action>',
1135       replace: installerFileAssociationsXml,
1136       file: install4jConf
1137     )
1138     /*
1139     // include uninstaller applescript app files in dmg
1140     def installerDMGUninstallerXml = file("$install4jDir/$install4jDMGUninstallerAppFiles").text
1141     ant.replaceregexp(
1142     byline: false,
1143     flags: "s",
1144     match: '<file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file=.*?>',
1145     replace: installerDMGUninstallerXml,
1146     file: install4jConf
1147     )
1148      */
1149   }
1150 }
1151
1152 task installers(type: com.install4j.gradle.Install4jTask) {
1153   group = "distribution"
1154   description = "Create the install4j installers"
1155   dependsOn getdown
1156   dependsOn copyInstall4jTemplate
1157   projectFile = file(install4jConf)
1158   variables = [majorVersion: version.substring(2, 11), build: 001, OSX_KEYSTORE: OSX_KEYSTORE, JSIGN_SH: JSIGN_SH]
1159   destination = "${jalviewDir}/${install4jBuildDir}/${JAVA_VERSION}"
1160   buildSelected = true
1161
1162   if (OSX_KEYPASS) {
1163     macKeystorePassword=OSX_KEYPASS
1164   }
1165
1166   doFirst {
1167     println("Using projectFile "+projectFile)
1168   }
1169
1170   inputs.dir(project.ext.getdownWebsiteDir)
1171   inputs.file(install4jConf)
1172   inputs.dir(macosJavaVMDir)
1173   inputs.dir(windowsJavaVMDir)
1174   outputs.dir("${jalviewDir}/${install4jBuildDir}/${JAVA_VERSION}")
1175 }
1176
1177 clean {
1178   doFirst {
1179     delete install4jConf
1180   }
1181 }
1182
1183 task sourceDist (type: Tar) {
1184   
1185   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
1186   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
1187   // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
1188   try {
1189     archiveFileName = outputFileName
1190   } catch (Exception e) {
1191     archiveName = outputFileName
1192   }
1193   
1194   compression Compression.GZIP
1195   
1196   into project.name
1197
1198   def EXCLUDE_FILES=["build/*","bin/*","test-output/","test-reports","tests","clover*/*"
1199   ,".*"
1200   ,"benchmarking/*"
1201   ,"**/.*"
1202   ,"*.class"
1203   ,"**/*.class","${j11modDir}/**/*.jar","appletlib","**/*locales"
1204   ,"*locales/**",
1205   ,"utils/InstallAnywhere"] 
1206   def PROCESS_FILES=[   "AUTHORS",
1207   "CITATION",
1208   "FEATURETODO",
1209   "JAVA-11-README",
1210   "FEATURETODO",
1211   "LICENSE",
1212   "**/README",
1213   "RELEASE",
1214   "THIRDPARTYLIBS","TESTNG",
1215   "build.gradle",
1216   "gradle.properties",
1217   "**/*.java",
1218   "**/*.html",
1219   "**/*.xml",
1220   "**/*.gradle",
1221   "**/*.groovy",
1222   "**/*.properties",
1223   "**/*.perl",
1224   "**/*.sh"]
1225
1226   from(jalviewDir) {
1227     exclude (EXCLUDE_FILES)
1228     include (PROCESS_FILES)
1229     filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION,'Year-Rel': getDate("yyyy")])
1230   }
1231   from(jalviewDir) {
1232     exclude (EXCLUDE_FILES)
1233     exclude (PROCESS_FILES)
1234   exclude ("appletlib")
1235   exclude ("**/*locales")
1236   exclude ("*locales/**")
1237   exclude ("utils/InstallAnywhere")
1238
1239     exclude (getdown_files_dir)
1240   exclude (getdown_website_dir)
1241
1242   // exluding these as not using jars as modules yet
1243   exclude ("${j11modDir}/**/*.jar")
1244 }
1245 //  from (jalviewDir) {
1246 //    // explicit includes for stuff that seemed to not get included
1247 //    include(fileTree("test/**/*."))
1248 //    exclude(EXCLUDE_FILES)
1249 //    exclude(PROCESS_FILES)
1250 //  }
1251 }
1252
1253 task helppages  {
1254   dependsOn copyHelp
1255   dependsOn pubhtmlhelp
1256   
1257   inputs.dir("${classes}/${helpDir}")
1258   outputs.dir("${helpOutputDir}")
1259 }
1260
1261 def jalviewjsBuildDir
1262 def jalviewjsSiteDir
1263 def jalviewjsTransferSiteDir
1264 task jalviewjsSitePath {
1265   def relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
1266   jalviewjsBuildDir = "${relativeBuildDir}/jalviewjs"
1267   if (jalviewjs_site_dir.startsWith('/')) {
1268     jalviewjsSiteDir = jalviewjs_site_dir
1269   } else {
1270     jalviewjsSiteDir = "${jalviewjsBuildDir}/${jalviewjs_site_dir}"
1271   }
1272   jalviewjsTransferSiteDir = "${jalviewjsBuildDir}/tmp/site"
1273 }
1274
1275 def eclipseWorkspace
1276 task jalviewjsSetEclipseWorkspace {
1277   def propKey = "jalviewjs_eclipse_workspace"
1278   def propVal = null
1279   if (project.hasProperty(propKey)) {
1280     propVal = project.getProperty(propKey)
1281   }
1282   def propsFileName = "${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
1283   def eclipseWsDir = propVal
1284   def props = new Properties()
1285
1286   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && file(propsFileName).exists()) {
1287     def ins = new FileInputStream("${jalviewDirAbsolutePath}/${propsFileName}")
1288     props.load(ins)
1289     ins.close()
1290     if (props.getProperty(propKey, null) != null) {
1291       eclipseWsDir = props.getProperty(propKey)
1292     }
1293   }
1294
1295   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
1296     def tempDir = File.createTempDir()
1297     eclipseWsDir = tempDir.getAbsolutePath()
1298     props.setProperty(propKey, eclipseWsDir)
1299     def propsFile = file(propsFileName)
1300     propsFile.parentFile.mkdirs()
1301     propsFile.createNewFile() // doesn't affect existing file
1302     def outs = new FileOutputStream(propsFile, false)
1303     props.store(outs, null)
1304     outs.close()
1305   }
1306
1307   eclipseWorkspace = file(eclipseWsDir)
1308
1309   doFirst {
1310     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
1311   }
1312   inputs.property(propKey, eclipseWsDir)
1313   outputs.file(propsFileName)
1314 }
1315
1316
1317 def eclipseDropinsDir
1318 def utilsDropinsDir
1319 def eclipseBinary
1320 def eclipseVersion
1321 def eclipseDebug = false
1322 def eclipseVersionSuffix = ""
1323 task jalviewjsEclipsePaths {
1324   def eclipseProduct
1325
1326   def eclipseRoot = jalviewjs_eclipse_root
1327   if (eclipseRoot.startsWith("~")) {
1328     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
1329   }
1330   if (OperatingSystem.current().isMacOsX()) {
1331     eclipseRoot += "/Eclipse.app"
1332     eclipseDropinsDir = "${eclipseRoot}/Contents/Eclipse/dropins"
1333     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
1334     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
1335   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
1336     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1337       eclipseRoot += "/eclipse"
1338     }
1339     eclipseDropinsDir = "${eclipseRoot}/dropins"
1340     eclipseBinary = "${eclipseRoot}/eclipse"
1341     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1342   } else { // linux or unix
1343     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1344       eclipseRoot += "/eclipse"
1345     }
1346     eclipseDropinsDir = "${eclipseRoot}/dropins"
1347     eclipseBinary = "${eclipseRoot}/eclipse"
1348     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1349   }
1350
1351   eclipseVersion = "4.12" // default
1352   def assumedVersion = true
1353   if (file(eclipseProduct).exists()) {
1354     def fis = new FileInputStream(eclipseProduct)
1355     def props = new Properties()
1356     props.load(fis)
1357     eclipseVersion = props.getProperty("version")
1358     fis.close()
1359     assumedVersion = false
1360   }
1361   /*
1362   String[] v = eclipseVersion.split("\\.")
1363   def v0 = Integer.valueOf(v[0])
1364   def v1 = Integer.valueOf(v[1])
1365   if (v0 < 4 || ( v0 == 4 && v1 < 13 )) {
1366     eclipseVersionSuffix = "_4.12"
1367   } else {
1368     eclipseVersionSuffix = "_4.13"
1369   }
1370   utilsDropinsDir = "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}${eclipseVersionSuffix}"
1371   */
1372
1373   utilsDropinsDir = "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}"
1374   def propKey = "eclipse_debug"
1375   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
1376
1377   doFirst {
1378     if (!assumedVersion) {
1379       println("ECLIPSE_VERSION=${eclipseVersion}")
1380     }
1381   }
1382 }
1383
1384 task eclipseSetup {
1385   dependsOn eclipseProject
1386   dependsOn eclipseClasspath
1387   dependsOn eclipseJdt
1388 }
1389
1390 /* using the Copy task below
1391 task OLDjalviewjsEclipseCopyDropins {
1392   dependsOn jalviewjsEclipsePaths
1393   dependsOn jalviewjsCleanEclipse
1394   def inputFiles = fileTree(dir: utilsDropinsDir, include: "*.jar")
1395   inputFiles += file(jalviewjs_j2s_plugin)
1396   def outputDir = eclipseDropinsDir
1397
1398   inputs.files inputFiles
1399   inputFiles.each { file ->
1400     outputs.file("${outputDir}/${file.name}")
1401   }
1402
1403   doLast {
1404     inputFiles.each { file ->
1405       copy {
1406         from file
1407         into outputDir
1408       }
1409     }
1410   }
1411 }
1412 */
1413
1414 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
1415 task jalviewjsEclipseCopyDropins(type: Copy) {
1416   dependsOn jalviewjsEclipsePaths
1417
1418   from utilsDropinsDir
1419   into eclipseDropinsDir
1420
1421   doLast {
1422     copy {
1423       from jalviewjs_j2s_plugin
1424       into eclipseDropinsDir
1425     }
1426   }
1427 }
1428
1429 // this eclipse -clean doesn't actually work
1430 task jalviewjsCleanEclipse(type: Exec) {
1431   dependsOn eclipseSetup
1432   dependsOn jalviewjsEclipsePaths
1433   dependsOn jalviewjsEclipseCopyDropins
1434
1435   executable(eclipseBinary)
1436   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
1437   if (eclipseDebug) {
1438     args += "-debug"
1439   }
1440   args += "-l"
1441
1442   def inputString = """exit
1443 y
1444 """
1445   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
1446   standardInput = inputByteStream
1447
1448   doFirst {
1449     println("CLEAN ECLIPSE_DEBUG=${eclipseDebug}")
1450   }
1451 }
1452 /* not really working yet
1453 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
1454 */
1455
1456 task jalviewjsUnzipFiles {
1457   dependsOn jalviewjsSitePath
1458
1459   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
1460   zipFiles += "${jalviewDir}/${jalviewjs_swingjs_zip}"
1461
1462   doLast {
1463     zipFiles.each { file_zip -> 
1464       copy {
1465         from zipTree(file_zip)
1466         into jalviewjsSiteDir
1467       }
1468     }
1469   }
1470
1471   inputs.files zipFiles
1472   outputs.dir jalviewjsSiteDir
1473 }
1474
1475 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
1476   dependsOn jalviewjsSitePath
1477   outputFile ("${jalviewDir}/${jalviewjs_j2s_settings}")
1478   def j2s_props = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
1479   def siteDirProperty = "j2s.site.directory"
1480   def setSiteDir = false
1481   j2s_props.each { prop, val ->
1482     if (val != null) {
1483       if (prop == siteDirProperty) {
1484         if (!(val.startsWith('/') || val.startsWith("file://") )) {
1485           val = "${jalviewjsTransferSiteDir}/${val}"
1486         }
1487         setSiteDir = true
1488       }
1489       property(prop,val)
1490     }
1491     if (!setSiteDir) { // default site location, don't override specifically set property
1492       property(siteDirProperty,jalviewjsTransferSiteDir)
1493     }
1494   }
1495   inputs.properties(j2s_props)
1496   outputs.file(outputFile)
1497 }
1498
1499 task jalviewjsEclipseSetup {
1500   dependsOn jalviewjsEclipseCopyDropins
1501   dependsOn jalviewjsSetEclipseWorkspace
1502   dependsOn jalviewjsCreateJ2sSettings
1503 }
1504
1505 task jalviewjsCopyResources (type: Sync) {
1506   dependsOn jalviewjsSitePath
1507   def inputFiles = fileTree(dir: resourceDir)
1508   def outputDir = "${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
1509
1510   from inputFiles
1511   into outputDir
1512   def outputFiles = []
1513   rename { filename ->
1514     outputFiles += "${outputDir}/${filename}"
1515     null
1516   }
1517   preserve {
1518     include "**"
1519   }
1520   outputs.files outputFiles
1521   inputs.files inputFiles
1522 }
1523
1524 task jalviewjsCopySiteResources (type: Sync) {
1525   dependsOn jalviewjsSitePath
1526   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
1527   def outputDir = jalviewjsSiteDir
1528
1529   from inputFiles
1530   into outputDir
1531   def outputFiles = []
1532   rename { filename ->
1533     outputFiles += "${outputDir}/${filename}"
1534     null
1535   }
1536   preserve {
1537     include "**"
1538   }
1539   outputs.files outputFiles
1540   inputs.files inputFiles
1541 }
1542
1543 task jalviewjsProjectImport(type: Exec) {
1544   dependsOn eclipseSetup
1545   dependsOn jalviewjsEclipsePaths
1546   dependsOn jalviewjsEclipseSetup
1547
1548   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
1549   executable(eclipseBinary)
1550   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
1551   if (eclipseDebug) {
1552     args += "-debug"
1553   }
1554
1555   doFirst {
1556     println("IMPORT ECLIPSE_DEBUG=${eclipseDebug}")
1557   }
1558
1559   inputs.file("${jalviewDir}/.project")
1560   outputs.dir(projdir)
1561   outputs.upToDateWhen { file(projdir).exists() }
1562 }
1563
1564 task jalviewjsTranspile(type: Exec) {
1565   dependsOn jalviewjsEclipseSetup 
1566   dependsOn jalviewjsProjectImport
1567   dependsOn jalviewjsEclipsePaths
1568
1569   executable(eclipseBinary)
1570   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
1571   if (eclipseDebug) {
1572     args += "-debug"
1573   }
1574
1575   def stdout
1576   def stderr
1577   stdout = new ByteArrayOutputStream()
1578   stderr = new ByteArrayOutputStream()
1579
1580   def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_stdout}"
1581   def logOutFile = file(logOutFileName)
1582   logOutFile.createNewFile()
1583   logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
1584 BINARY: ${eclipseBinary}
1585 VERSION: ${eclipseVersion}
1586 WORKSPACE: ${eclipseWorkspace}
1587 DEBUG: ${eclipseDebug}
1588 ----
1589 """
1590   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
1591   //def logErrFileName = "${jalviewjsBuildDir}/${jalviewjs_j2s_stderr}"
1592   //def logErrFile = file(logFileName)
1593   //logErrFile.createNewFile()
1594   //def logErrFOS = new FileErrputStream(logErrFile, false)
1595   // combine stdout and stderr
1596   def logErrFOS = logOutFOS
1597
1598   if (jalviewjs_j2s_to_console.equals("true")) {
1599     standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1600       new org.apache.tools.ant.util.TeeOutputStream(
1601         logOutFOS,
1602         stdout),
1603       standardOutput)
1604       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1605         new org.apache.tools.ant.util.TeeOutputStream(
1606           logErrFOS,
1607           stderr),
1608         errorOutput)
1609   } else {
1610     standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1611       logOutFOS,
1612       stdout)
1613       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1614         logErrFOS,
1615         stderr)
1616   }
1617
1618   doLast {
1619     if (stdout.toString().contains("Error processing ")) {
1620       // j2s did not complete transpile
1621       throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewjsBuildDir}/${jalviewjs_j2s_stdout}'")
1622     }
1623   }
1624
1625   doFirst {
1626     println("TRANSPILE ECLIPSE_DEBUG=${eclipseDebug}")
1627   }
1628
1629   inputs.dir(sourceDir)
1630   outputs.dir("${eclipse_bin_dir}/main")
1631   outputs.dir(jalviewjsTransferSiteDir)
1632   outputs.upToDateWhen { file("${jalviewjsTransferSiteDir}${jalviewjs_server_resource}").exists() }
1633
1634 }
1635
1636 task jalviewjsCopyTransferSite(type: Copy) {
1637   dependsOn jalviewjsTranspile
1638   from jalviewjsTransferSiteDir
1639   into jalviewjsSiteDir
1640 }
1641
1642 jalviewjsUnzipFiles.mustRunAfter jalviewjsCopyTransferSite
1643 jalviewjsCopyResources.mustRunAfter jalviewjsCopyTransferSite
1644 jalviewjsCopySiteResources.mustRunAfter jalviewjsCopyTransferSite
1645
1646 task jalviewjsPrepareSite {
1647   group "JalviewJS"
1648   description "Prepares the website folder including unzipping files and copying resources"
1649   dependsOn jalviewjsSitePath
1650   dependsOn jalviewjsUnzipFiles
1651   dependsOn jalviewjsCopyResources
1652   dependsOn jalviewjsCopySiteResources
1653 }
1654
1655 task jalviewjsBuildSite {
1656   group "JalviewJS"
1657   description "Builds the whole website including transpiled code"
1658   dependsOn jalviewjsCopyTransferSite
1659   dependsOn jalviewjsPrepareSite
1660 }
1661
1662 task cleanJalviewjsSite {
1663   doFirst {
1664     delete jalviewjsTransferSiteDir
1665     delete jalviewjsSiteDir
1666   }
1667 }
1668
1669 task jalviewjsSiteTar(type: Tar) {
1670   group "JalviewJS"
1671   description "Creates a tar.gz file for the website"
1672   dependsOn jalviewjsBuildSite
1673   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
1674   try {
1675     archiveFileName = outputFilename
1676   } catch (Exception e) {
1677     archiveName = outputFilename
1678   }
1679
1680   compression Compression.GZIP
1681
1682   from jalviewjsSiteDir
1683   into jalviewjs_site_dir // this is inside the tar file
1684
1685   inputs.dir(jalviewjsSiteDir)
1686 }
1687
1688 task jalviewjsServer {
1689   group "JalviewJS"
1690   description "Starts a webserver on localhost to test the website"
1691   dependsOn jalviewjsSitePath
1692   def htmlFile = "${jalviewDirAbsolutePath}/jalviewjsTest.html"
1693   doLast {
1694
1695     SimpleHttpFileServerFactory factory = new SimpleHttpFileServerFactory()
1696     def port = Integer.valueOf(jalviewjs_server_port)
1697     def start = port
1698     def running = false
1699     def url
1700     while(port < start+1000 && !running) {
1701       try {
1702         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
1703         def jalviewjsServer = factory.start(doc_root, port)
1704         running = true
1705         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
1706         println("SERVER STARTED with document root ${doc_root}.")
1707         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
1708         println("For debug: "+url+"?j2sdebug")
1709
1710         file(htmlFile).text = """
1711         <p><a href="${url}">Jalview JS Test. &lt;${url}&gt;</a></p>
1712         <p><a href="${url}?j2sdebug">Jalview JS Test with debug. &lt;${url}?j2sdebug&lt;</a></p>
1713         """
1714
1715       } catch (Exception e) {
1716         port++;
1717       }
1718     }
1719
1720   }
1721
1722   outputs.file(htmlFile)
1723 }
1724
1725 task cleanJalviewjsAll {
1726   group "JalviewJS"
1727   description "Delete all configuration and build artifacts to do with JalviewJS build"
1728   dependsOn cleanJalviewjsSite
1729   dependsOn jalviewjsEclipsePaths
1730   
1731   doFirst {
1732     delete jalviewjsBuildDir
1733     delete "${jalviewDir}/${eclipse_bin_dir}"
1734     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
1735       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
1736     }
1737     delete "${jalviewDir}/${jalviewjs_j2s_settings}"
1738   }
1739 }
1740
1741 task jalviewjs {
1742   group "JalviewJS"
1743   description "Build the site"
1744   dependsOn jalviewjsBuildSite
1745 }
1746
1747
1748 task jalviewjsIDECopyTransferSite(type: Copy) {
1749   from jalviewjsTransferSiteDir
1750   into jalviewjsSiteDir
1751 }
1752
1753 task jalviewjsIDEj2s {
1754   group "JalviewJS in Eclipse"
1755   description "Creates the .j2s file"
1756   dependsOn jalviewjsCreateJ2sSettings
1757 }
1758
1759 task jalviewjsIDEBuildSite {
1760   group "JalviewJS in Eclipse"
1761   description "Copies the Eclipse transpiled site and unzips supporting zipfiles"
1762   dependsOn jalviewjsIDECopyTransferSite
1763   dependsOn jalviewjsPrepareSite
1764 }
1765
1766 task jalviewjsIDESiteClean {
1767   group "JalviewJS in Eclipse"
1768   description "Deletes the Eclipse transpiled site"
1769   dependsOn cleanJalviewjsSite
1770 }
1771
1772 task jalviewjsIDEServer {
1773   group "JalviewJS in Eclipse"
1774   description "Starts a webserver on localhost to test the website"
1775   dependsOn jalviewjsServer
1776 }
1777