JAL-3438 Eclipse code style formatting using spotless plugin using util/eclipse/Jalvi...
[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 import java.security.MessageDigest
6
7 import groovy.transform.ExternalizeMethods
8
9 buildscript {
10   dependencies {
11     classpath 'org.openclover:clover:4.3.1'
12   }
13 }
14
15 plugins {
16   id 'java'
17   id 'application'
18   id 'eclipse'
19   id 'com.github.johnrengelman.shadow' version '4.0.3'
20   id 'com.install4j.gradle' version '7.0.9'
21   id 'com.diffplug.gradle.spotless' version '3.27.1'
22   id 'com.dorongold.task-tree' version '1.4' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
23 }
24
25 repositories {
26   jcenter()
27   mavenCentral()
28   mavenLocal()
29   flatDir {
30     dirs gradlePluginsDir
31   }
32 }
33
34 dependencies {
35   compile 'org.apache.commons:commons-compress:1.18'
36 }
37
38
39 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
40 def string(Object o) {
41   return o.toString()
42 }
43
44
45 ext {
46   jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
47   jalviewDirRelativePath = jalviewDir
48
49   // local build environment properties
50   def localProps = "${jalviewDirAbsolutePath}/local.properties"
51   if (file(localProps).exists()) {
52     try {
53       def p = new Properties()
54       def localPropsFIS = new FileInputStream(localProps)
55       p.load(localPropsFIS)
56       localPropsFIS.close()
57       p.each {
58         key, val -> 
59           def over = getProperty(key) != null
60           setProperty(key, val)
61           if (over) {
62             println("Overriding property '${key}' with local.properties value '${val}'")
63           }
64       }
65     } catch (Exception e) {
66       System.out.println("Exception reading local.properties")
67     }
68   }
69
70   // this property set when running Eclipse headlessly
71   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
72   // this property set by Eclipse
73   eclipseApplicationProperty = string("eclipse.application")
74   // CHECK IF RUNNING FROM WITHIN ECLIPSE
75   def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
76   IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
77   // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
78   if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
79     println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
80     IN_ECLIPSE = false
81   }
82   if (IN_ECLIPSE) {
83     println("WITHIN ECLIPSE IDE")
84   } else {
85     println("HEADLESS BUILD")
86   }
87   
88   J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
89   if (J2S_ENABLED) {
90     println("J2S ENABLED")
91   }
92   
93   /* *-/
94   System.properties.sort { it.key }.each {
95     key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
96   }
97   /-* *-/
98   if (false && IN_ECLIPSE) {
99     jalviewDir = jalviewDirAbsolutePath
100   }
101   */
102
103   // essentials
104   bareSourceDir = string(source_dir)
105   sourceDir = string("${jalviewDir}/${bareSourceDir}")
106   resourceDir = string("${jalviewDir}/${resource_dir}")
107   bareTestSourceDir = string(test_source_dir)
108   testSourceDir = string("${jalviewDir}/${bareTestSourceDir}")
109
110   // clover
111   cloverInstrDir = file("${buildDir}/${cloverSourcesInstrDir}")
112   classesDir = string("${jalviewDir}/${classes_dir}")
113   if (clover.equals("true")) {
114     use_clover = true
115     classesDir = string("${buildDir}/${cloverClassesDir}")
116   } else {
117     use_clover = false
118     classesDir = string("${jalviewDir}/${classes_dir}")
119   }
120
121   classes = classesDir
122
123   getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
124   getdownDir = string("")
125   reportRsyncCmd = false
126   buildDist = true
127   buildProperties = string("${resourceDir}/${build_properties_file}")
128   getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
129   switch (CHANNEL) {
130
131     case "BUILD":
132     // TODO: get bamboo build artifact URL for getdown artifacts
133     getdown_channel_base = bamboo_channelbase
134     getdown_channel_name = string("${bamboo_planKey}/${JAVA_VERSION}")
135     getdown_app_base = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
136     getdown_app_dir = getdown_app_dir_alt
137     buildProperties = string("${resourceDir}/${build_properties_file}")
138     break
139
140     case "RELEASE":
141     getdown_channel_name = CHANNEL.toLowerCase()
142     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
143     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
144     getdown_app_dir = getdown_app_dir_release
145     buildProperties = string("${resourceDir}/${build_properties_file}")
146     reportRsyncCommand = true
147     // Don't ignore transpile errors for release build
148     if (jalviewjs_ignore_transpile_errors.equals("true")) {
149       jalviewjs_ignore_transpile_errors = "false"
150       println("Setting jalviewjs_ignore_transpile_errors to 'false'")
151     }
152     break
153
154     case "ARCHIVE":
155     getdown_channel_name = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
156     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
157     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
158     getdown_app_dir = getdown_app_dir_alt
159     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
160       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
161       exit
162     } else {
163       packageDir = string("${ARCHIVEDIR}/${packageDir}")
164       buildProperties = string("${ARCHIVEDIR}/${resource_dir}/${build_properties_file}")
165       buildDist = false
166     }
167     reportRsyncCommand = true
168     break
169
170     case "ARCHIVELOCAL":
171     getdown_channel_name = string("archive/${JALVIEW_VERSION}")
172     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
173     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
174     getdown_app_dir = getdown_app_dir_alt
175     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
176       print "Must provide an ARCHIVEDIR value to produce an archive distribution"
177       exit
178     } else {
179       packageDir = string("${ARCHIVEDIR}/${packageDir}")
180       buildProperties = string("${ARCHIVEDIR}/${resource_dir}/${build_properties_file}")
181       buildDist = false
182     }
183     reportRsyncCommand = true
184     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
185     break
186
187     case "DEVELOP":
188     getdown_channel_name = CHANNEL.toLowerCase()
189     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
190     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
191     getdown_app_dir = getdown_app_dir_alt
192     buildProperties = string("${resourceDir}/${build_properties_file}")
193     reportRsyncCommand = true
194     break
195
196     case "TEST-RELEASE":
197     getdown_channel_name = CHANNEL.toLowerCase()
198     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
199     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
200     getdown_app_dir = getdown_app_dir_alt
201     buildProperties = string("${resourceDir}/${build_properties_file}")
202     reportRsyncCommand = true
203     // Don't ignore transpile errors for release build
204     if (jalviewjs_ignore_transpile_errors.equals("true")) {
205       jalviewjs_ignore_transpile_errors = "false"
206       println("Setting jalviewjs_ignore_transpile_errors to 'false'")
207     }
208     break
209
210     case ~/^SCRATCH(|-[-\w]*)$/:
211     getdown_channel_name = CHANNEL
212     getdownDir = string("${getdown_channel_name}/${JAVA_VERSION}")
213     getdown_app_base = string("${getdown_channel_base}/${getdownDir}")
214     getdown_app_dir = getdown_app_dir_alt
215     buildProperties = string("${resourceDir}/${build_properties_file}")
216     reportRsyncCommand = true
217     break
218
219     case "LOCAL":
220     getdown_app_base = file(getdownWebsiteDir).toURI().toString()
221     getdown_app_dir = getdown_app_dir_alt
222     buildProperties = string("${resourceDir}/${build_properties_file}")
223     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
224     break
225
226     default: // something wrong specified
227     print("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
228     exit
229     break
230
231   }
232
233   getdownAppDir = string("${getdownWebsiteDir}/${getdown_app_dir}")
234   //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
235   getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
236   getdownInstallDir = string("${getdownWebsiteDir}/${getdown_install_dir}")
237   getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
238   getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
239   /* compile without modules -- using classpath libraries
240   modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
241   modules_runtimeClasspath = modules_compileClasspath
242   */
243   gitHash = string("")
244   gitBranch = string("")
245
246   println("Using a ${CHANNEL} profile.")
247
248   additional_compiler_args = []
249   // configure classpath/args for j8/j11 compilation
250   if (JAVA_VERSION.equals("1.8")) {
251     JAVA_INTEGER_VERSION = string("8")
252     //libDir = j8libDir
253     libDir = j11libDir
254     libDistDir = j8libDir
255     compile_source_compatibility = 1.8
256     compile_target_compatibility = 1.8
257     // these are getdown.txt properties defined dependent on the JAVA_VERSION
258     getdown_alt_java_min_version = getdown_alt_java8_min_version
259     getdown_alt_java_max_version = getdown_alt_java8_max_version
260     // this property is assigned below and expanded to multiple lines in the getdown task
261     getdown_alt_multi_java_location = getdown_alt_java8_txt_multi_java_location
262     // this property is for the Java library used in eclipse
263     eclipse_java_runtime_name = string("JavaSE-1.8")
264   } else if (JAVA_VERSION.equals("11")) {
265     JAVA_INTEGER_VERSION = string("11")
266     libDir = j11libDir
267     libDistDir = j11libDir
268     compile_source_compatibility = 11
269     compile_target_compatibility = 11
270     getdown_alt_java_min_version = getdown_alt_java11_min_version
271     getdown_alt_java_max_version = getdown_alt_java11_max_version
272     getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
273     eclipse_java_runtime_name = string("JavaSE-11")
274     /* compile without modules -- using classpath libraries
275     additional_compiler_args += [
276     '--module-path', modules_compileClasspath.asPath,
277     '--add-modules', j11modules
278     ]
279      */
280   } else if (JAVA_VERSION.equals("12") || JAVA_VERSION.equals("13")) {
281     JAVA_INTEGER_VERSION = JAVA_VERSION
282     libDir = j11libDir
283     libDistDir = j11libDir
284     compile_source_compatibility = JAVA_VERSION
285     compile_target_compatibility = JAVA_VERSION
286     getdown_alt_java_min_version = getdown_alt_java11_min_version
287     getdown_alt_java_max_version = getdown_alt_java11_max_version
288     getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
289     eclipse_java_runtime_name = string("JavaSE-11")
290     /* compile without modules -- using classpath libraries
291     additional_compiler_args += [
292     '--module-path', modules_compileClasspath.asPath,
293     '--add-modules', j11modules
294     ]
295      */
296   } else {
297     throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
298   }
299
300
301   // for install4j
302   macosJavaVMDir = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/getdown/macos-jre${JAVA_VERSION}/jre")
303   macosJavaVMTgz = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre${JAVA_VERSION}.tar.gz")
304   windowsJavaVMDir = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/getdown/windows-jre${JAVA_VERSION}/jre")
305   windowsJavaVMTgz = string("${System.env.HOME}/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre${JAVA_VERSION}.tar.gz")
306   install4jDir = string("${jalviewDir}/${install4jResourceDir}")
307   install4jConfFileName = string("jalview-installers-java${JAVA_VERSION}.install4j")
308   install4jConfFile = string("${install4jDir}/${install4jConfFileName}")
309
310
311   buildingHTML = string("${jalviewDir}/${docDir}/building.html")
312   helpFile = string("${classesDir}/${help_dir}/help.jhm")
313   helpParentDir = string("${jalviewDir}/${help_parent_dir}")
314   helpDir = string("${help_dir}")
315   helpSourceDir = string("${helpParentDir}/${helpDir}")
316
317
318   relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
319   jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
320   jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
321   if (IN_ECLIPSE) {
322     jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
323   } else {
324     jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
325   }
326   jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
327   jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
328   jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
329   jalviewjsJalviewCoreHtmlFile = string("")
330   jalviewjsJalviewCoreName = string(jalviewjs_core_name)
331   jalviewjsCoreClasslists = []
332   jalviewjsJalviewTemplateName = string(jalviewjs_name)
333   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
334   jalviewjsJ2sProps = null
335
336   eclipseWorkspace = null
337   eclipseBinary = string("")
338   eclipseVersion = string("")
339   eclipseDebug = false
340   // ENDEXT
341 }
342
343
344 sourceSets {
345   main {
346     java {
347       srcDirs sourceDir
348       outputDir = file(classesDir)
349     }
350
351     resources {
352       srcDirs resourceDir
353     }
354
355     jar.destinationDir = file("${jalviewDir}/${packageDir}")
356
357     compileClasspath = files(sourceSets.main.java.outputDir)
358     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
359
360     runtimeClasspath = compileClasspath
361   }
362
363   clover {
364     java {
365       srcDirs = [ cloverInstrDir ]
366       outputDir = file("${buildDir}/${cloverClassesDir}")
367     }
368
369     resources {
370       srcDirs = sourceSets.main.resources.srcDirs
371     }
372     compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
373     compileClasspath += files(sourceSets.main.java.outputDir)
374     compileClasspath += sourceSets.main.compileClasspath
375     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"])
376     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
377
378     runtimeClasspath = compileClasspath
379   }
380
381   test {
382     java {
383       srcDirs testSourceDir
384       outputDir = file("${jalviewDir}/${testOutputDir}")
385     }
386
387     resources {
388       srcDirs = sourceSets.main.resources.srcDirs
389     }
390
391     compileClasspath = files( sourceSets.test.java.outputDir )
392
393     if (use_clover) {
394       compileClasspath += sourceSets.clover.compileClasspath
395     } else {
396       compileClasspath += files(sourceSets.main.java.outputDir)
397     }
398
399     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
400     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"])
401     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"])
402
403     runtimeClasspath = compileClasspath
404   }
405 }
406
407
408 // clover bits
409 dependencies {
410   if (use_clover) {
411     cloverCompile 'org.openclover:clover:4.3.1'
412     testCompile 'org.openclover:clover:4.3.1'
413   }
414 }
415
416
417 configurations {
418   cloverRuntime
419   cloverRuntime.extendsFrom cloverCompile
420 }
421
422 eclipse {
423   project {
424     name = eclipse_project_name
425
426     natures 'org.eclipse.jdt.core.javanature',
427     'org.eclipse.jdt.groovy.core.groovyNature',
428     'org.eclipse.buildship.core.gradleprojectnature'
429
430     buildCommand 'org.eclipse.jdt.core.javabuilder'
431     buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
432   }
433
434   classpath {
435     //defaultOutputDir = sourceSets.main.java.outputDir
436     def removeThese = []
437     configurations.each{
438       if (it.isCanBeResolved()) {
439         removeThese += it
440       }
441     }
442
443     minusConfigurations += removeThese
444     plusConfigurations = [ ]
445     file {
446
447       whenMerged { cp ->
448         def removeTheseToo = []
449         HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
450         cp.entries.each { entry ->
451           // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
452           // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
453           // we add the resources and help/help dirs in as libs afterwards (see below)
454           if (entry.kind == 'src') {
455             if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
456               removeTheseToo += entry
457             } else {
458               alreadyAddedSrcPath.putAt(entry.path, true)
459             }
460           }
461
462         }
463         cp.entries.removeAll(removeTheseToo)
464
465         cp.entries += new Output("${eclipse_bin_dir}/main")
466         if (file(helpSourceDir).isDirectory()) {
467           cp.entries += new Library(fileReference(helpSourceDir))
468         }
469         if (file(resourceDir).isDirectory()) {
470           cp.entries += new Library(fileReference(resourceDir))
471         }
472
473         HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
474
475         sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.each {
476           //don't want to add outputDir as eclipse is using its own output dir in bin/main
477           if (it.isDirectory() || ! it.exists()) {
478             // don't add dirs to classpath, especially if they don't exist
479             return false // groovy "continue" in .any closure
480           }
481           def itPath = it.toString()
482           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
483             // make relative path
484             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
485           }
486           if (alreadyAddedLibPath.get(itPath)) {
487             //println("Not adding duplicate entry "+itPath)
488           } else {
489             //println("Adding entry "+itPath)
490             cp.entries += new Library(fileReference(itPath))
491             alreadyAddedLibPath.put(itPath, true)
492           }
493         }
494
495         //fileTree(dir: "$jalviewDir/$utilsDir", include: ["test*/*.jar"]).each {
496         sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
497           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
498           if (it.isDirectory() || ! it.exists()) {
499             // don't add dirs to classpath
500             return false // groovy "continue" in .any closure
501           }
502
503           def itPath = it.toString()
504           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
505             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
506           }
507           if (alreadyAddedLibPath.get(itPath)) {
508             // don't duplicate
509           } else {
510             def lib = new Library(fileReference(itPath))
511             lib.entryAttributes["test"] = "true"
512             cp.entries += lib
513             alreadyAddedLibPath.put(itPath, true)
514           }
515         }
516
517       } // whenMerged
518
519     } // file
520
521     containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
522
523   } // classpath
524
525   jdt {
526     // for the IDE, use java 11 compatibility
527     sourceCompatibility = compile_source_compatibility
528     targetCompatibility = compile_target_compatibility
529     javaRuntimeName = eclipse_java_runtime_name
530
531     // add in jalview project specific properties/preferences into eclipse core preferences
532     // and also the codestyle XML file
533     file {
534       withProperties { props ->
535         def jalview_prefs = new Properties()
536         def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
537         jalview_prefs.load(ins)
538         ins.close()
539         jalview_prefs.forEach { t, v ->
540           if (props.getAt(t) == null) {
541             props.putAt(t, v)
542           }
543         }
544         // codestyle file -- overrides previous formatter prefs
545         def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
546         if (csFile.exists()) {
547           XmlParser parser = new XmlParser()
548           def profiles = parser.parse(csFile)
549           def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
550           if (profile != null) {
551             profile.'setting'.each { s ->
552               def id = s.'@id'
553               def value = s.'@value'
554               if (id != null && value != null) {
555                 props.putAt(id, value)
556               }
557             }
558           }
559         }
560       }
561     }
562
563   } // jdt
564
565   if (IN_ECLIPSE) {
566     // Don't want these to be activated if in headless build
567     synchronizationTasks "eclipseSynchronizationTask"
568     //autoBuildTasks "eclipseAutoBuildTask"
569
570   }
571 }
572
573
574 task cloverInstr() {
575   // only instrument source, we build test classes as normal
576   inputs.files files (sourceSets.main.allJava) // , fileTree(dir: testSourceDir, include: ["**/*.java"]))
577   outputs.dir cloverInstrDir
578
579   doFirst {
580     delete cloverInstrDir
581     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
582     "-d", "${buildDir}/${cloverSourcesInstrDir}"]
583     argsList.addAll(inputs.files.files.collect({ file ->
584       file.absolutePath
585     }))
586     String[] args = argsList.toArray()
587     println("About to instrument "+args.length +" files")
588     com.atlassian.clover.CloverInstr.mainImpl(args)
589   }
590 }
591
592
593 task cloverReport {
594   group = "Verification"
595     description = "Createst the Clover report"
596     inputs.dir "${buildDir}/clover"
597     outputs.dir "${reportsDir}/clover"
598     onlyIf {
599       file("${buildDir}/clover/clover.db").exists()
600     }
601   doFirst {
602     def argsList = ["--initstring", "${buildDir}/clover/clover.db",
603     "-o", "${reportsDir}/clover"]
604     String[] args = argsList.toArray()
605     com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
606
607     // and generate ${reportsDir}/clover/clover.xml
608     args = ["--initstring", "${buildDir}/clover/clover.db",
609     "-o", "${reportsDir}/clover/clover.xml"].toArray()
610     com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
611   }
612 }
613 // end clover bits
614
615 compileJava {
616
617   doFirst {
618     sourceCompatibility = compile_source_compatibility
619     targetCompatibility = compile_target_compatibility
620     options.compilerArgs = additional_compiler_args
621     print ("Setting target compatibility to "+targetCompatibility+"\n")
622   }
623
624 }
625
626
627 compileTestJava {
628   if (use_clover) {
629     dependsOn compileCloverJava
630     classpath += configurations.cloverRuntime
631   } else {
632     classpath += sourceSets.main.runtimeClasspath
633   }
634   doFirst {
635     sourceCompatibility = compile_source_compatibility
636     targetCompatibility = compile_target_compatibility
637     options.compilerArgs = additional_compiler_args
638     print ("Setting target compatibility to "+targetCompatibility+"\n")
639   }
640 }
641
642
643 compileCloverJava {
644
645   doFirst {
646     sourceCompatibility = compile_source_compatibility
647     targetCompatibility = compile_target_compatibility
648     options.compilerArgs += additional_compiler_args
649     print ("Setting target compatibility to "+targetCompatibility+"\n")
650   }
651   classpath += configurations.cloverRuntime
652 }
653
654
655 clean {
656   doFirst {
657     delete sourceSets.main.java.outputDir
658   }
659 }
660
661
662 cleanTest {
663   doFirst {
664     delete sourceSets.test.java.outputDir
665     delete cloverInstrDir
666   }
667 }
668
669
670 // format is a string like date.format("dd MMMM yyyy")
671 def getDate(format) {
672   def date = new Date()
673   return date.format(format)
674 }
675
676
677 task setGitVals {
678   def hashStdOut = new ByteArrayOutputStream()
679   exec {
680     commandLine "git", "rev-parse", "--short", "HEAD"
681     standardOutput = hashStdOut
682     ignoreExitValue true
683   }
684
685   def branchStdOut = new ByteArrayOutputStream()
686   exec {
687     commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
688     standardOutput = branchStdOut
689     ignoreExitValue true
690   }
691
692   gitHash = hashStdOut.toString().trim()
693   gitBranch = branchStdOut.toString().trim()
694
695   outputs.upToDateWhen { false }
696 }
697
698
699 task createBuildProperties(type: WriteProperties) {
700   group = "build"
701   description = "Create the ${buildProperties} file"
702   dependsOn setGitVals
703   inputs.dir(sourceDir)
704   inputs.dir(resourceDir)
705   file(buildProperties).getParentFile().mkdirs()
706   outputFile (buildProperties)
707   // taking time specific comment out to allow better incremental builds
708   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
709   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
710   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
711   property "VERSION", JALVIEW_VERSION
712   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
713   outputs.file(outputFile)
714 }
715
716
717 clean {
718   doFirst {
719     delete buildProperties
720   }
721 }
722
723
724 task cleanBuildingHTML(type: Delete) {
725   doFirst {
726     delete buildingHTML
727   }
728 }
729
730
731 task convertBuildingMD(type: Exec) {
732   dependsOn cleanBuildingHTML
733   def buildingMD = "${jalviewDir}/${docDir}/building.md"
734   def css = "${jalviewDir}/${docDir}/github.css"
735
736   def pandoc = null
737   pandoc_exec.split(",").each {
738     if (file(it.trim()).exists()) {
739       pandoc = it.trim()
740       return true
741     }
742   }
743
744   def hostname = "hostname".execute().text.trim()
745   if ((pandoc == null || ! file(pandoc).exists()) && hostname.equals("jv-bamboo")) {
746     pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
747   }
748
749   doFirst {
750     if (pandoc != null && file(pandoc).exists()) {
751         commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
752     } else {
753         println("Cannot find pandoc. Skipping convert building.md to HTML")
754         throw new StopExecutionException("Cannot find pandoc. Skipping convert building.md to HTML")
755     }
756   }
757
758   ignoreExitValue true
759
760   inputs.file(buildingMD)
761   inputs.file(css)
762   outputs.file(buildingHTML)
763 }
764
765
766 clean {
767   doFirst {
768     delete buildingHTML
769   }
770 }
771
772
773 task syncDocs(type: Sync) {
774   dependsOn convertBuildingMD
775   def syncDir = "${classesDir}/${docDir}"
776   from fileTree("${jalviewDir}/${docDir}")
777   into syncDir
778
779 }
780
781
782 task copyHelp(type: Copy) {
783   def inputDir = helpSourceDir
784   def outputDir = "${classesDir}/${helpDir}"
785   from(inputDir) {
786     exclude '**/*.gif'
787     exclude '**/*.jpg'
788     exclude '**/*.png'
789     filter(ReplaceTokens,
790       beginToken: '$$',
791       endToken: '$$',
792       tokens: [
793         'Version-Rel': JALVIEW_VERSION,
794         'Year-Rel': getDate("yyyy")
795       ]
796     )
797   }
798   from(inputDir) {
799     include '**/*.gif'
800     include '**/*.jpg'
801     include '**/*.png'
802   }
803   into outputDir
804
805   inputs.dir(inputDir)
806   outputs.files(helpFile)
807   outputs.dir(outputDir)
808 }
809
810
811 task syncLib(type: Sync) {
812   def syncDir = "${classesDir}/${libDistDir}"
813   from fileTree("${jalviewDir}/${libDistDir}")
814   into syncDir
815 }
816
817
818 task syncResources(type: Sync) {
819   dependsOn createBuildProperties
820   from resourceDir
821   include "**/*.*"
822   into "${classesDir}"
823   preserve {
824     include "**"
825   }
826 }
827
828
829 task prepare {
830   dependsOn syncResources
831   dependsOn syncDocs
832   dependsOn copyHelp
833 }
834
835
836 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
837 test {
838   dependsOn prepare
839   dependsOn compileJava
840   if (use_clover) {
841     dependsOn cloverInstr
842   }
843
844   if (use_clover) {
845     print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
846   }
847
848   useTestNG() {
849     includeGroups testngGroups
850     preserveOrder true
851     useDefaultListeners=true
852   }
853
854   workingDir = jalviewDir
855   //systemProperties 'clover.jar' System.properties.clover.jar
856   sourceCompatibility = compile_source_compatibility
857   targetCompatibility = compile_target_compatibility
858   jvmArgs += additional_compiler_args
859
860 }
861
862
863 task buildIndices(type: JavaExec) {
864   dependsOn copyHelp
865   classpath = sourceSets.main.compileClasspath
866   main = "com.sun.java.help.search.Indexer"
867   workingDir = "${classesDir}/${helpDir}"
868   def argDir = "html"
869   args = [ argDir ]
870   inputs.dir("${workingDir}/${argDir}")
871
872   outputs.dir("${classesDir}/doc")
873   outputs.dir("${classesDir}/help")
874   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
875   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
876   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
877   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
878   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
879   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
880 }
881
882
883 task compileLinkCheck(type: JavaCompile) {
884   options.fork = true
885   classpath = files("${jalviewDir}/${utilsDir}")
886   destinationDir = file("${jalviewDir}/${utilsDir}")
887   source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
888
889   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
890   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
891   outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class")
892   outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class")
893 }
894
895
896 task linkCheck(type: JavaExec) {
897   dependsOn prepare, compileLinkCheck
898
899   def helpLinksCheckerOutFile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.out")
900   classpath = files("${jalviewDir}/${utilsDir}")
901   main = "HelpLinksChecker"
902   workingDir = jalviewDir
903   def help = "${classesDir}/${helpDir}"
904   args = [ "${classesDir}/${helpDir}", "-nointernet" ]
905
906   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
907   def errFOS = outFOS
908   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
909     outFOS,
910     standardOutput)
911   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
912     outFOS,
913     errorOutput)
914
915   inputs.dir("${classesDir}/${helpDir}")
916   outputs.file(helpLinksCheckerOutFile)
917 }
918
919 // import the pubhtmlhelp target
920 ant.properties.basedir = "${jalviewDir}"
921 ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${helpDir}"
922 ant.importBuild "${utilsDir}/publishHelp.xml"
923
924
925 task cleanPackageDir(type: Delete) {
926   doFirst {
927     delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar")
928   }
929 }
930
931
932 jar {
933   dependsOn linkCheck
934   dependsOn buildIndices
935   dependsOn createBuildProperties
936
937   manifest {
938     attributes "Main-Class": mainClass,
939     "Permissions": "all-permissions",
940     "Application-Name": "Jalview Desktop",
941     "Codebase": application_codebase
942   }
943
944   destinationDir = file("${jalviewDir}/${packageDir}")
945   archiveName = rootProject.name+".jar"
946
947   exclude "cache*/**"
948   exclude "*.jar"
949   exclude "*.jar.*"
950   exclude "**/*.jar"
951   exclude "**/*.jar.*"
952
953   inputs.dir(classesDir)
954   outputs.file("${jalviewDir}/${packageDir}/${archiveName}")
955 }
956
957
958 task copyJars(type: Copy) {
959   from fileTree(dir: classesDir, include: "**/*.jar").files
960   into "${jalviewDir}/${packageDir}"
961 }
962
963
964 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
965 task syncJars(type: Sync) {
966   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
967   into "${jalviewDir}/${packageDir}"
968   preserve {
969     include jar.archiveName
970   }
971 }
972
973
974 task makeDist {
975   group = "build"
976   description = "Put all required libraries in dist"
977   // order of "cleanPackageDir", "copyJars", "jar" important!
978   jar.mustRunAfter cleanPackageDir
979   syncJars.mustRunAfter cleanPackageDir
980   dependsOn cleanPackageDir
981   dependsOn syncJars
982   dependsOn jar
983   outputs.dir("${jalviewDir}/${packageDir}")
984 }
985
986
987 task cleanDist {
988   dependsOn cleanPackageDir
989   dependsOn cleanTest
990   dependsOn clean
991 }
992
993 shadowJar {
994   group = "distribution"
995   if (buildDist) {
996     dependsOn makeDist
997   }
998   from ("${jalviewDir}/${libDistDir}") {
999     include("*.jar")
1000   }
1001   manifest {
1002     attributes 'Implementation-Version': JALVIEW_VERSION
1003   }
1004   mainClassName = shadowJarMainClass
1005   mergeServiceFiles()
1006   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
1007   minimize()
1008 }
1009
1010
1011 task getdownWebsite() {
1012   group = "distribution"
1013   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
1014   if (buildDist) {
1015     dependsOn makeDist
1016   }
1017
1018   def getdownWebsiteResourceFilenames = []
1019   def getdownTextString = ""
1020   def getdownResourceDir = getdownResourceDir
1021   def getdownAppDir = getdownAppDir
1022   def getdownResourceFilenames = []
1023
1024   doFirst {
1025     // clean the getdown website and files dir before creating getdown folders
1026     delete getdownWebsiteDir
1027     delete getdownFilesDir
1028
1029     copy {
1030       from buildProperties
1031       rename(build_properties_file, getdown_build_properties)
1032       into getdownAppDir
1033     }
1034     getdownWebsiteResourceFilenames += "${getdown_app_dir}/${getdown_build_properties}"
1035
1036     // go through properties looking for getdown_txt_...
1037     def props = project.properties.sort { it.key }
1038     if (getdown_alt_java_min_version.length() > 0) {
1039       props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
1040     }
1041     if (getdown_alt_java_max_version.length() > 0) {
1042       props.put("getdown_txt_java_max_version", getdown_alt_java_max_version)
1043     }
1044     props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
1045
1046     props.put("getdown_txt_appbase", getdown_app_base)
1047     props.each{ prop, val ->
1048       if (prop.startsWith("getdown_txt_") && val != null) {
1049         if (prop.startsWith("getdown_txt_multi_")) {
1050           def key = prop.substring(18)
1051           val.split(",").each{ v ->
1052             def line = "${key} = ${v}\n"
1053             getdownTextString += line
1054           }
1055         } else {
1056           // file values rationalised
1057           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1058             def r = null
1059             if (val.indexOf('/') == 0) {
1060               // absolute path
1061               r = file(val)
1062             } else if (val.indexOf('/') > 0) {
1063               // relative path (relative to jalviewDir)
1064               r = file( "${jalviewDir}/${val}" )
1065             }
1066             if (r.exists()) {
1067               val = "${getdown_resource_dir}/" + r.getName()
1068               getdownWebsiteResourceFilenames += val
1069               getdownResourceFilenames += r.getPath()
1070             }
1071           }
1072           if (! prop.startsWith("getdown_txt_resource")) {
1073             def line = prop.substring(12) + " = ${val}\n"
1074             getdownTextString += line
1075           }
1076         }
1077       }
1078     }
1079
1080     getdownWebsiteResourceFilenames.each{ filename ->
1081       getdownTextString += "resource = ${filename}\n"
1082     }
1083     getdownResourceFilenames.each{ filename ->
1084       copy {
1085         from filename
1086         into getdownResourceDir
1087       }
1088     }
1089
1090     def codeFiles = []
1091     fileTree(file(packageDir)).each{ f ->
1092       if (f.isDirectory()) {
1093         def files = fileTree(dir: f, include: ["*"]).getFiles()
1094         codeFiles += files
1095       } else if (f.exists()) {
1096         codeFiles += f
1097       }
1098     }
1099     codeFiles.sort().each{f ->
1100       def name = f.getName()
1101       def line = "code = ${getdown_app_dir}/${name}\n"
1102       getdownTextString += line
1103       copy {
1104         from f.getPath()
1105         into getdownAppDir
1106       }
1107     }
1108
1109     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
1110     /*
1111     if (JAVA_VERSION.equals("11")) {
1112     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
1113     j11libFiles.sort().each{f ->
1114     def name = f.getName()
1115     def line = "code = ${getdown_j11lib_dir}/${name}\n"
1116     getdownTextString += line
1117     copy {
1118     from f.getPath()
1119     into getdownJ11libDir
1120     }
1121     }
1122     }
1123      */
1124
1125     // 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.
1126     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
1127     getdownTextString += "resource = ${getdown_launcher_new}\n"
1128     getdownTextString += "class = ${mainClass}\n"
1129
1130     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
1131     getdown_txt.write(getdownTextString)
1132
1133     def launch_jvl = file("${getdownWebsiteDir}/${getdown_launch_jvl}")
1134     launch_jvl.write("appbase="+props.get("getdown_txt_appbase"))
1135
1136     copy {
1137       from getdownLauncher
1138       rename(file(getdownLauncher).getName(), getdown_launcher_new)
1139       into getdownWebsiteDir
1140     }
1141
1142     copy {
1143       from getdownLauncher
1144       if (file(getdownLauncher).getName() != getdown_launcher) {
1145         rename(file(getdownLauncher).getName(), getdown_launcher)
1146       }
1147       into getdownWebsiteDir
1148     }
1149
1150     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
1151       copy {
1152         from getdown_txt
1153         from getdownLauncher
1154         from "${getdownWebsiteDir}/${getdown_build_properties}"
1155         if (file(getdownLauncher).getName() != getdown_launcher) {
1156           rename(file(getdownLauncher).getName(), getdown_launcher)
1157         }
1158         into getdownInstallDir
1159       }
1160
1161       copy {
1162         from getdownInstallDir
1163         into getdownFilesInstallDir
1164       }
1165     }
1166
1167     copy {
1168       from getdown_txt
1169       from launch_jvl
1170       from getdownLauncher
1171       from "${getdownWebsiteDir}/${getdown_build_properties}"
1172       if (file(getdownLauncher).getName() != getdown_launcher) {
1173         rename(file(getdownLauncher).getName(), getdown_launcher)
1174       }
1175       into getdownFilesDir
1176     }
1177
1178     copy {
1179       from getdownResourceDir
1180       into "${getdownFilesDir}/${getdown_resource_dir}"
1181     }
1182   }
1183
1184   if (buildDist) {
1185     inputs.dir("${jalviewDir}/${packageDir}")
1186   }
1187   outputs.dir(getdownWebsiteDir)
1188   outputs.dir(getdownFilesDir)
1189 }
1190
1191
1192 task getdownDigest(type: JavaExec) {
1193   group = "distribution"
1194   description = "Digest the getdown website folder"
1195   dependsOn getdownWebsite
1196   doFirst {
1197     classpath = files("${getdownWebsiteDir}/${getdown_launcher}")
1198   }
1199   main = "com.threerings.getdown.tools.Digester"
1200   args getdownWebsiteDir
1201   inputs.dir(getdownWebsiteDir)
1202   outputs.file("${getdownWebsiteDir}/digest2.txt")
1203 }
1204
1205
1206 task getdown() {
1207   group = "distribution"
1208   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1209   dependsOn getdownDigest
1210   doLast {
1211     if (reportRsyncCommand) {
1212       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
1213       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1214       println "LIKELY RSYNC COMMAND:"
1215       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1216       if (RUNRSYNC == "true") {
1217         exec {
1218           commandLine "mkdir", "-p", toDir
1219         }
1220         exec {
1221           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1222         }
1223       }
1224     }
1225   }
1226 }
1227
1228
1229 clean {
1230   doFirst {
1231     delete getdownWebsiteDir
1232     delete getdownFilesDir
1233   }
1234 }
1235
1236
1237 install4j {
1238   def install4jHomeDir = "/opt/install4j"
1239   def hostname = "hostname".execute().text.trim()
1240   if (hostname.equals("jv-bamboo")) {
1241     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1242   } else if (OperatingSystem.current().isMacOsX()) {
1243     install4jHomeDir = '/Applications/install4j.app/Contents/Resources/app'
1244     if (! file(install4jHomeDir).exists()) {
1245       install4jHomeDir = System.getProperty("user.home")+install4jHomeDir
1246     }
1247   } else if (OperatingSystem.current().isLinux()) {
1248     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1249   }
1250   installDir = file(install4jHomeDir)
1251   mediaTypes = Arrays.asList(install4jMediaTypes.split(","))
1252   if (install4jFaster.equals("true")) {
1253     faster = true
1254   }
1255 }
1256
1257
1258 task copyInstall4jTemplate(type: Copy) {
1259   from (install4jDir) {
1260     include install4jTemplate
1261     rename (install4jTemplate, install4jConfFileName)
1262     filter(ReplaceTokens,
1263       beginToken: '',
1264       endToken: '',
1265       tokens: [
1266         '9999999999': JAVA_VERSION
1267       ]
1268     )
1269     filter(ReplaceTokens,
1270       beginToken: '$$',
1271       endToken: '$$',
1272       tokens: [
1273         'JAVA_VERSION': JAVA_VERSION,
1274         'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1275         'VERSION': JALVIEW_VERSION,
1276         'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1277         'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1278         'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1279         'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1280         'INSTALL4JINFOPLISTFILEASSOCIATIONS': install4jInfoPlistFileAssociations,
1281         'COPYRIGHT_MESSAGE': install4jCopyrightMessage,
1282         'MACOS_BUNDLE_ID': install4jMacOSBundleId,
1283         'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1284         'GETDOWN_DIST_DIR': getdown_app_dir,
1285         'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1286         'GETDOWN_INSTALL_DIR': getdown_install_dir
1287       ]
1288     )
1289     if (OSX_KEYPASS == "") {
1290       filter(ReplaceTokens,
1291         beginToken: 'codeSigning macEnabled="',
1292         endToken: '"',
1293         tokens: [
1294           'true': 'codeSigning macEnabled="false"'
1295         ]
1296       )
1297       filter(ReplaceTokens,
1298         beginToken: 'runPostProcessor="true" ',
1299         endToken: 'Processor',
1300         tokens: [
1301           'post': 'runPostProcessor="false" postProcessor'
1302         ]
1303       )
1304     }
1305   }
1306   into install4jDir
1307   outputs.files(install4jConfFile)
1308
1309   doLast {
1310     // include file associations in installer
1311     def installerFileAssociationsXml = file("${install4jDir}/${install4jInstallerFileAssociations}").text
1312     ant.replaceregexp(
1313       byline: false,
1314       flags: "s",
1315       match: '<action name="EXTENSIONS_REPLACED_BY_GRADLE".*?</action>',
1316       replace: installerFileAssociationsXml,
1317       file: install4jConfFile
1318     )
1319     /*
1320     // include uninstaller applescript app files in dmg
1321     def installerDMGUninstallerXml = file("$install4jDir/$install4jDMGUninstallerAppFiles").text
1322     ant.replaceregexp(
1323     byline: false,
1324     flags: "s",
1325     match: '<file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file=.*?>',
1326     replace: installerDMGUninstallerXml,
1327     file: install4jConfFile
1328     )
1329      */
1330   }
1331 }
1332
1333
1334 clean {
1335   doFirst {
1336     delete install4jConfFile
1337   }
1338 }
1339
1340
1341 task installers(type: com.install4j.gradle.Install4jTask) {
1342   group = "distribution"
1343   description = "Create the install4j installers"
1344   dependsOn getdown
1345   dependsOn copyInstall4jTemplate
1346   projectFile = file(install4jConfFile)
1347   variables = [majorVersion: version.substring(2, 11), build: 001, OSX_KEYSTORE: OSX_KEYSTORE, JSIGN_SH: JSIGN_SH]
1348   destination = "${jalviewDir}/${install4jBuildDir}/${JAVA_VERSION}"
1349   buildSelected = true
1350
1351   if (OSX_KEYPASS) {
1352     macKeystorePassword=OSX_KEYPASS
1353   }
1354
1355   doFirst {
1356     println("Using projectFile "+projectFile)
1357   }
1358
1359   inputs.dir(getdownWebsiteDir)
1360   inputs.file(install4jConfFile)
1361   inputs.dir(macosJavaVMDir)
1362   inputs.dir(windowsJavaVMDir)
1363   outputs.dir("${jalviewDir}/${install4jBuildDir}/${JAVA_VERSION}")
1364 }
1365
1366
1367 spotless {
1368   java {
1369     licenseHeaderFile "${jalviewDir}/${source_license_file}"
1370     removeUnusedImports()
1371     importOrder 'jalview', 'java', 'javax', 'org', 'com', 'sun', ''
1372     eclipse().configFile "${jalviewDir}/${eclipse_codestyle_file}"
1373   }
1374 }
1375
1376 task sourceDist (type: Tar) {
1377   group 'Distribution'
1378   description 'Creates a source files compressed tar file'
1379   dependsOn 'spotlessApply'
1380   
1381   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
1382   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
1383   // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
1384   try {
1385     archiveFileName = outputFileName
1386   } catch (Exception e) {
1387     archiveName = outputFileName
1388   }
1389   
1390   compression Compression.GZIP
1391   
1392   into project.name
1393
1394   def EXCLUDE_FILES=["build/*","bin/*","test-output/","test-reports","tests","clover*/*"
1395   ,".*"
1396   ,"benchmarking/*"
1397   ,"**/.*"
1398   ,"*.class"
1399   ,"**/*.class","${j11modDir}/**/*.jar","appletlib","**/*locales"
1400   ,"*locales/**",
1401   ,"utils/InstallAnywhere"] 
1402   def PROCESS_FILES=[   "AUTHORS",
1403   "CITATION",
1404   "FEATURETODO",
1405   "JAVA-11-README",
1406   "FEATURETODO",
1407   "LICENSE",
1408   "**/README",
1409   "RELEASE",
1410   "THIRDPARTYLIBS","TESTNG",
1411   "build.gradle",
1412   "gradle.properties",
1413   "**/*.java",
1414   "**/*.html",
1415   "**/*.xml",
1416   "**/*.gradle",
1417   "**/*.groovy",
1418   "**/*.properties",
1419   "**/*.perl",
1420   "**/*.sh"]
1421
1422   from(jalviewDir) {
1423     exclude (EXCLUDE_FILES)
1424     include (PROCESS_FILES)
1425     filter(ReplaceTokens,
1426       beginToken: '$$',
1427       endToken: '$$',
1428       tokens: [
1429         'Version-Rel': JALVIEW_VERSION,
1430         'Year-Rel': getDate("yyyy")
1431       ]
1432     )
1433   }
1434   from(jalviewDir) {
1435     exclude (EXCLUDE_FILES)
1436     exclude (PROCESS_FILES)
1437     exclude ("appletlib")
1438     exclude ("**/*locales")
1439     exclude ("*locales/**")
1440     exclude ("utils/InstallAnywhere")
1441
1442     exclude (getdown_files_dir)
1443     exclude (getdown_website_dir)
1444
1445     // exluding these as not using jars as modules yet
1446     exclude ("${j11modDir}/**/*.jar")
1447   }
1448   //  from (jalviewDir) {
1449   //    // explicit includes for stuff that seemed to not get included
1450   //    include(fileTree("test/**/*."))
1451   //    exclude(EXCLUDE_FILES)
1452   //    exclude(PROCESS_FILES)
1453   //  }
1454 }
1455
1456
1457 task helppages  {
1458   dependsOn copyHelp
1459   dependsOn pubhtmlhelp
1460   
1461   inputs.dir("${classesDir}/${helpDir}")
1462   outputs.dir("${buildDir}/distributions/${helpDir}")
1463 }
1464
1465
1466 task j2sSetHeadlessBuild {
1467   doFirst {
1468     IN_ECLIPSE = false
1469   }
1470 }
1471
1472
1473 task jalviewjsSetEclipseWorkspace {
1474   def propKey = "jalviewjs_eclipse_workspace"
1475   def propVal = null
1476   if (project.hasProperty(propKey)) {
1477     propVal = project.getProperty(propKey)
1478     if (propVal.startsWith("~/")) {
1479       propVal = System.getProperty("user.home") + propVal.substring(1)
1480     }
1481   }
1482   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
1483   def propsFile = file(propsFileName)
1484   def eclipseWsDir = propVal
1485   def props = new Properties()
1486
1487   def writeProps = true
1488   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
1489     def ins = new FileInputStream(propsFileName)
1490     props.load(ins)
1491     ins.close()
1492     if (props.getProperty(propKey, null) != null) {
1493       eclipseWsDir = props.getProperty(propKey)
1494       writeProps = false
1495     }
1496   }
1497
1498   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
1499     def tempDir = File.createTempDir()
1500     eclipseWsDir = tempDir.getAbsolutePath()
1501     writeProps = true
1502   }
1503   eclipseWorkspace = file(eclipseWsDir)
1504
1505   doFirst {
1506     // do not run a headless transpile when we claim to be in Eclipse
1507     if (IN_ECLIPSE) {
1508       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1509       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1510     } else {
1511       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1512     }
1513
1514     if (writeProps) {
1515       props.setProperty(propKey, eclipseWsDir)
1516       propsFile.parentFile.mkdirs()
1517       def bytes = new ByteArrayOutputStream()
1518       props.store(bytes, null)
1519       def propertiesString = bytes.toString()
1520       propsFile.text = propertiesString
1521       print("NEW ")
1522     } else {
1523       print("EXISTING ")
1524     }
1525
1526     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
1527   }
1528
1529   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
1530   outputs.file(propsFileName)
1531   outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
1532 }
1533
1534
1535 task jalviewjsEclipsePaths {
1536   def eclipseProduct
1537
1538   def eclipseRoot = jalviewjs_eclipse_root
1539   if (eclipseRoot.startsWith("~/")) {
1540     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
1541   }
1542   if (OperatingSystem.current().isMacOsX()) {
1543     eclipseRoot += "/Eclipse.app"
1544     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
1545     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
1546   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
1547     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1548       eclipseRoot += "/eclipse.exe"
1549     }
1550     eclipseBinary = "${eclipseRoot}/eclipse"
1551     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1552   } else { // linux or unix
1553     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1554       eclipseRoot += "/eclipse"
1555     }
1556     eclipseBinary = "${eclipseRoot}/eclipse"
1557     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1558   }
1559
1560   eclipseVersion = "4.13" // default
1561   def assumedVersion = true
1562   if (file(eclipseProduct).exists()) {
1563     def fis = new FileInputStream(eclipseProduct)
1564     def props = new Properties()
1565     props.load(fis)
1566     eclipseVersion = props.getProperty("version")
1567     fis.close()
1568     assumedVersion = false
1569   }
1570   
1571   def propKey = "eclipse_debug"
1572   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
1573
1574   doFirst {
1575     // do not run a headless transpile when we claim to be in Eclipse
1576     if (IN_ECLIPSE) {
1577       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1578       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1579     } else {
1580       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1581     }
1582
1583     if (!assumedVersion) {
1584       println("ECLIPSE VERSION=${eclipseVersion}")
1585     }
1586   }
1587 }
1588
1589
1590 task eclipseSetup {
1591   dependsOn eclipseProject
1592   dependsOn eclipseClasspath
1593   dependsOn eclipseJdt
1594 }
1595
1596
1597 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
1598 task jalviewjsEclipseCopyDropins(type: Copy) {
1599   dependsOn jalviewjsEclipsePaths
1600
1601   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
1602   inputFiles += file("${jalviewDir}/${jalviewjs_j2s_plugin}")
1603   def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
1604
1605   from inputFiles
1606   into outputDir
1607 }
1608
1609
1610 // this eclipse -clean doesn't actually work
1611 task jalviewjsCleanEclipse(type: Exec) {
1612   dependsOn eclipseSetup
1613   dependsOn jalviewjsEclipsePaths
1614   dependsOn jalviewjsEclipseCopyDropins
1615
1616   executable(eclipseBinary)
1617   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
1618   if (eclipseDebug) {
1619     args += "-debug"
1620   }
1621   args += "-l"
1622
1623   def inputString = """exit
1624 y
1625 """
1626   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
1627   standardInput = inputByteStream
1628 }
1629
1630 /* not really working yet
1631 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
1632 */
1633
1634
1635 task jalviewjsTransferUnzipSwingJs {
1636   def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
1637
1638   doLast {
1639     copy {
1640       from zipTree(file_zip)
1641       into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
1642     }
1643   }
1644
1645   inputs.file file_zip
1646   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
1647 }
1648
1649
1650 task jalviewjsTransferUnzipLib {
1651   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
1652
1653   doLast {
1654     zipFiles.each { file_zip -> 
1655       copy {
1656         from zipTree(file_zip)
1657         into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
1658       }
1659     }
1660   }
1661
1662   inputs.files zipFiles
1663   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
1664 }
1665
1666
1667 task jalviewjsTransferUnzipAllLibs {
1668   dependsOn jalviewjsTransferUnzipSwingJs
1669   dependsOn jalviewjsTransferUnzipLib
1670 }
1671
1672
1673 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
1674   group "JalviewJS"
1675   description "Create the .j2s file from the j2s.* properties"
1676
1677   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
1678   def siteDirProperty = "j2s.site.directory"
1679   def setSiteDir = false
1680   jalviewjsJ2sProps.each { prop, val ->
1681     if (val != null) {
1682       if (prop == siteDirProperty) {
1683         if (!(val.startsWith('/') || val.startsWith("file://") )) {
1684           val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
1685         }
1686         setSiteDir = true
1687       }
1688       property(prop,val)
1689     }
1690     if (!setSiteDir) { // default site location, don't override specifically set property
1691       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
1692     }
1693   }
1694   outputFile = jalviewjsJ2sSettingsFileName
1695
1696   if (! IN_ECLIPSE) {
1697     inputs.properties(jalviewjsJ2sProps)
1698     outputs.file(jalviewjsJ2sSettingsFileName)
1699   }
1700 }
1701
1702
1703 task jalviewjsEclipseSetup {
1704   dependsOn jalviewjsEclipseCopyDropins
1705   dependsOn jalviewjsSetEclipseWorkspace
1706   dependsOn jalviewjsCreateJ2sSettings
1707 }
1708
1709
1710 task jalviewjsSyncAllLibs (type: Sync) {
1711   dependsOn jalviewjsTransferUnzipAllLibs
1712   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
1713   inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
1714   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
1715
1716   from inputFiles
1717   into outputDir
1718   def outputFiles = []
1719   rename { filename ->
1720     outputFiles += "${outputDir}/${filename}"
1721     null
1722   }
1723   preserve {
1724     include "**"
1725   }
1726   outputs.files outputFiles
1727   inputs.files inputFiles
1728 }
1729
1730
1731 task jalviewjsSyncResources (type: Sync) {
1732   def inputFiles = fileTree(dir: resourceDir)
1733   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
1734
1735   from inputFiles
1736   into outputDir
1737   def outputFiles = []
1738   rename { filename ->
1739     outputFiles += "${outputDir}/${filename}"
1740     null
1741   }
1742   preserve {
1743     include "**"
1744   }
1745   outputs.files outputFiles
1746   inputs.files inputFiles
1747 }
1748
1749
1750 task jalviewjsSyncSiteResources (type: Sync) {
1751   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
1752   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
1753
1754   from inputFiles
1755   into outputDir
1756   def outputFiles = []
1757   rename { filename ->
1758     outputFiles += "${outputDir}/${filename}"
1759     null
1760   }
1761   preserve {
1762     include "**"
1763   }
1764   outputs.files outputFiles
1765   inputs.files inputFiles
1766 }
1767
1768
1769 task jalviewjsSyncBuildProperties (type: Sync) {
1770   dependsOn createBuildProperties
1771   def inputFiles = [file(buildProperties)]
1772   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
1773
1774   from inputFiles
1775   into outputDir
1776   def outputFiles = []
1777   rename { filename ->
1778     outputFiles += "${outputDir}/${filename}"
1779     null
1780   }
1781   preserve {
1782     include "**"
1783   }
1784   outputs.files outputFiles
1785   inputs.files inputFiles
1786 }
1787
1788
1789 task jalviewjsProjectImport(type: Exec) {
1790   dependsOn eclipseSetup
1791   dependsOn jalviewjsEclipsePaths
1792   dependsOn jalviewjsEclipseSetup
1793
1794   doFirst {
1795     // do not run a headless import when we claim to be in Eclipse
1796     if (IN_ECLIPSE) {
1797       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1798       throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1799     } else {
1800       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1801     }
1802   }
1803
1804   //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
1805   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
1806   executable(eclipseBinary)
1807   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
1808   if (eclipseDebug) {
1809     args += "-debug"
1810   }
1811   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
1812   if (!IN_ECLIPSE) {
1813     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
1814   }
1815
1816   inputs.file("${jalviewDir}/.project")
1817   outputs.upToDateWhen { 
1818     file(projdir).exists()
1819   }
1820 }
1821
1822
1823 task jalviewjsTranspile(type: Exec) {
1824   dependsOn jalviewjsEclipseSetup 
1825   dependsOn jalviewjsProjectImport
1826   dependsOn jalviewjsEclipsePaths
1827
1828   doFirst {
1829     // do not run a headless transpile when we claim to be in Eclipse
1830     if (IN_ECLIPSE) {
1831       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1832       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1833     } else {
1834       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1835     }
1836   }
1837
1838   executable(eclipseBinary)
1839   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
1840   if (eclipseDebug) {
1841     args += "-debug"
1842   }
1843   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
1844   if (!IN_ECLIPSE) {
1845     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
1846   }
1847
1848   def stdout
1849   def stderr
1850   doFirst {
1851     stdout = new ByteArrayOutputStream()
1852     stderr = new ByteArrayOutputStream()
1853
1854     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
1855     def logOutFile = file(logOutFileName)
1856     logOutFile.createNewFile()
1857     logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
1858 BINARY: ${eclipseBinary}
1859 VERSION: ${eclipseVersion}
1860 WORKSPACE: ${eclipseWorkspace}
1861 DEBUG: ${eclipseDebug}
1862 ----
1863 """
1864     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
1865     // combine stdout and stderr
1866     def logErrFOS = logOutFOS
1867
1868     if (jalviewjs_j2s_to_console.equals("true")) {
1869       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1870         new org.apache.tools.ant.util.TeeOutputStream(
1871           logOutFOS,
1872           stdout),
1873         standardOutput)
1874       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1875         new org.apache.tools.ant.util.TeeOutputStream(
1876           logErrFOS,
1877           stderr),
1878         errorOutput)
1879     } else {
1880       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1881         logOutFOS,
1882         stdout)
1883       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1884         logErrFOS,
1885         stderr)
1886     }
1887   }
1888
1889   doLast {
1890     if (stdout.toString().contains("Error processing ")) {
1891       // j2s did not complete transpile
1892       //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
1893       if (jalviewjs_ignore_transpile_errors.equals("true")) {
1894         println("IGNORING TRANSPILE ERRORS")
1895         println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
1896       } else {
1897         throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
1898       }
1899     }
1900   }
1901
1902   inputs.dir("${jalviewDir}/${sourceDir}")
1903   outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
1904   outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
1905 }
1906
1907
1908 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
1909
1910   def stdout = new ByteArrayOutputStream()
1911   def stderr = new ByteArrayOutputStream()
1912
1913   def coreFile = file(jsfile)
1914   def msg = ""
1915   msg = "Creating core for ${name}...\nGenerating ${jsfile}"
1916   println(msg)
1917   logOutFile.createNewFile()
1918   logOutFile.append(msg+"\n")
1919
1920   def coreTop = file(prefixFile)
1921   def coreBottom = file(suffixFile)
1922   coreFile.getParentFile().mkdirs()
1923   coreFile.createNewFile()
1924   coreFile.write( coreTop.text )
1925   list.each {
1926     f ->
1927     if (f.exists()) {
1928       def t = f.text
1929       t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
1930       coreFile.append( t )
1931     } else {
1932       msg = "...file '"+f.getPath()+"' does not exist, skipping"
1933       println(msg)
1934       logOutFile.append(msg+"\n")
1935     }
1936   }
1937   coreFile.append( coreBottom.text )
1938
1939   msg = "Generating ${zjsfile}"
1940   println(msg)
1941   logOutFile.append(msg+"\n")
1942   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
1943   def logErrFOS = logOutFOS
1944
1945   javaexec {
1946     classpath = files(["${jalviewDir}/tools/closure_compiler.jar"])
1947     args = [ "--js", jsfile, "--js_output_file", zjsfile ]
1948     maxHeapSize = "2g"
1949
1950     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
1951     println(msg)
1952     logOutFile.append(msg+"\n")
1953
1954     if (logOutConsole) {
1955       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1956         new org.apache.tools.ant.util.TeeOutputStream(
1957           logOutFOS,
1958           stdout),
1959         standardOutput)
1960         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1961           new org.apache.tools.ant.util.TeeOutputStream(
1962             logErrFOS,
1963             stderr),
1964           errorOutput)
1965     } else {
1966       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1967         logOutFOS,
1968         stdout)
1969         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1970           logErrFOS,
1971           stderr)
1972     }
1973   }
1974   msg = "--"
1975   println(msg)
1976   logOutFile.append(msg+"\n")
1977 }
1978
1979
1980 task jalviewjsBuildAllCores {
1981   group "JalviewJS"
1982   description "Build the core js lib closures listed in the classlists dir"
1983   dependsOn jalviewjsTranspile
1984   dependsOn jalviewjsTransferUnzipSwingJs
1985
1986   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
1987   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
1988   def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
1989   def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
1990   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
1991   def prefixFile = "${jsDir}/core/coretop2.js"
1992   def suffixFile = "${jsDir}/core/corebottom2.js"
1993
1994   inputs.file prefixFile
1995   inputs.file suffixFile
1996
1997   def classlistFiles = []
1998   // add the classlists found int the jalviewjs_classlists_dir
1999   fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
2000     file ->
2001     def name = file.getName() - ".txt"
2002     classlistFiles += [
2003       'file': file,
2004       'name': name
2005     ]
2006   }
2007
2008   // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
2009   //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
2010   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
2011
2012   jalviewjsCoreClasslists = []
2013
2014   classlistFiles.each {
2015     hash ->
2016
2017     def file = hash['file']
2018     if (! file.exists()) {
2019       //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
2020       return false // this is a "continue" in groovy .each closure
2021     }
2022     def name = hash['name']
2023     if (name == null) {
2024       name = file.getName() - ".txt"
2025     }
2026
2027     def filelist = []
2028     file.eachLine {
2029       line ->
2030         filelist += line
2031     }
2032     def list = fileTree(dir: j2sDir, includes: filelist)
2033
2034     def jsfile = "${outputDir}/core${name}.js"
2035     def zjsfile = "${outputDir}/core${name}.z.js"
2036
2037     jalviewjsCoreClasslists += [
2038       'jsfile': jsfile,
2039       'zjsfile': zjsfile,
2040       'list': list,
2041       'name': name
2042     ]
2043
2044     inputs.file(file)
2045     inputs.files(list)
2046     outputs.file(jsfile)
2047     outputs.file(zjsfile)
2048   }
2049   
2050   // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
2051   def stevesoftClasslistName = "_stevesoft"
2052   def stevesoftClasslist = [
2053     'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
2054     'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
2055     'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
2056     'name': stevesoftClasslistName
2057   ]
2058   jalviewjsCoreClasslists += stevesoftClasslist
2059   inputs.files(stevesoftClasslist['list'])
2060   outputs.file(stevesoftClasslist['jsfile'])
2061   outputs.file(stevesoftClasslist['zjsfile'])
2062
2063   // _all core
2064   def allClasslistName = "_all"
2065   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
2066   allJsFiles += fileTree(
2067     dir: libJ2sDir,
2068     include: "**/*.js",
2069     excludes: [
2070       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2071       "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
2072       "**/org/jmol/export/JSExporter.js"
2073     ]
2074   )
2075   allJsFiles += fileTree(
2076     dir: swingJ2sDir,
2077     include: "**/*.js",
2078     excludes: [
2079       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2080       "**/sun/misc/Unsafe.js",
2081       "**/swingjs/jquery/jquery-editable-select.js",
2082       "**/swingjs/jquery/j2sComboBox.js",
2083       "**/sun/misc/FloatingDecimal.js"
2084     ]
2085   )
2086   def allClasslist = [
2087     'jsfile': "${outputDir}/core${allClasslistName}.js",
2088     'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
2089     'list': allJsFiles,
2090     'name': allClasslistName
2091   ]
2092   // not including this version of "all" core at the moment
2093   //jalviewjsCoreClasslists += allClasslist
2094   inputs.files(allClasslist['list'])
2095   outputs.file(allClasslist['jsfile'])
2096   outputs.file(allClasslist['zjsfile'])
2097
2098   doFirst {
2099     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
2100     logOutFile.getParentFile().mkdirs()
2101     logOutFile.createNewFile()
2102     logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
2103
2104     jalviewjsCoreClasslists.each {
2105       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
2106     }
2107   }
2108
2109 }
2110
2111
2112 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
2113   copy {
2114     from inputFile
2115     into file(outputFile).getParentFile()
2116     rename { filename ->
2117       if (filename.equals(inputFile.getName())) {
2118         return file(outputFile).getName()
2119       }
2120       return null
2121     }
2122     filter(ReplaceTokens,
2123       beginToken: '_',
2124       endToken: '_',
2125       tokens: [
2126         'MAIN': '"'+mainClass+'"',
2127         'CODE': "null",
2128         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
2129         'COREKEY': jalviewjs_core_key,
2130         'CORENAME': coreName
2131       ]
2132     )
2133   }
2134 }
2135
2136
2137 task jalviewjsPublishCoreTemplates {
2138   dependsOn jalviewjsBuildAllCores
2139   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
2140   def inputFile = file(inputFileName)
2141   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2142
2143   def outputFiles = []
2144   jalviewjsCoreClasslists.each { cl ->
2145     def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
2146     cl['outputfile'] = outputFile
2147     outputFiles += outputFile
2148   }
2149
2150   doFirst {
2151     jalviewjsCoreClasslists.each { cl ->
2152       jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
2153     }
2154   }
2155   inputs.file(inputFile)
2156   outputs.files(outputFiles)
2157 }
2158
2159
2160 task jalviewjsSyncCore (type: Sync) {
2161   dependsOn jalviewjsBuildAllCores
2162   dependsOn jalviewjsPublishCoreTemplates
2163   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
2164   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2165
2166   from inputFiles
2167   into outputDir
2168   def outputFiles = []
2169   rename { filename ->
2170     outputFiles += "${outputDir}/${filename}"
2171     null
2172   }
2173   preserve {
2174     include "**"
2175   }
2176   outputs.files outputFiles
2177   inputs.files inputFiles
2178 }
2179
2180
2181 // this Copy version of TransferSiteJs will delete anything else in the target dir
2182 task jalviewjsCopyTransferSiteJs(type: Copy) {
2183   dependsOn jalviewjsTranspile
2184   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2185   into "${jalviewDir}/${jalviewjsSiteDir}"
2186 }
2187
2188
2189 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
2190 task jalviewjsSyncTransferSiteJs(type: Sync) {
2191   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2192   include "**/*.*"
2193   into "${jalviewDir}/${jalviewjsSiteDir}"
2194   preserve {
2195     include "**"
2196   }
2197 }
2198
2199
2200 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
2201 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
2202 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
2203 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
2204
2205 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
2206 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
2207 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
2208 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
2209
2210
2211 task jalviewjsPrepareSite {
2212   group "JalviewJS"
2213   description "Prepares the website folder including unzipping files and copying resources"
2214   dependsOn jalviewjsSyncAllLibs
2215   dependsOn jalviewjsSyncResources
2216   dependsOn jalviewjsSyncSiteResources
2217   dependsOn jalviewjsSyncBuildProperties
2218   dependsOn jalviewjsSyncCore
2219 }
2220
2221
2222 task jalviewjsBuildSite {
2223   group "JalviewJS"
2224   description "Builds the whole website including transpiled code"
2225   dependsOn jalviewjsCopyTransferSiteJs
2226   dependsOn jalviewjsPrepareSite
2227 }
2228
2229
2230 task cleanJalviewjsTransferSite {
2231   doFirst {
2232     delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2233     delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2234     delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2235     delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2236   }
2237 }
2238
2239
2240 task cleanJalviewjsSite {
2241   dependsOn cleanJalviewjsTransferSite
2242   doFirst {
2243     delete "${jalviewDir}/${jalviewjsSiteDir}"
2244   }
2245 }
2246
2247
2248 task jalviewjsSiteTar(type: Tar) {
2249   group "JalviewJS"
2250   description "Creates a tar.gz file for the website"
2251   dependsOn jalviewjsBuildSite
2252   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
2253   try {
2254     archiveFileName = outputFilename
2255   } catch (Exception e) {
2256     archiveName = outputFilename
2257   }
2258
2259   compression Compression.GZIP
2260
2261   from "${jalviewDir}/${jalviewjsSiteDir}"
2262   into jalviewjs_site_dir // this is inside the tar file
2263
2264   inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
2265 }
2266
2267
2268 task jalviewjsServer {
2269   group "JalviewJS"
2270   def filename = "jalviewjsTest.html"
2271   description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
2272   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
2273   doLast {
2274
2275     SimpleHttpFileServerFactory factory = new SimpleHttpFileServerFactory()
2276     def port = Integer.valueOf(jalviewjs_server_port)
2277     def start = port
2278     def running = false
2279     def url
2280     def jalviewjsServer
2281     while(port < start+1000 && !running) {
2282       try {
2283         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
2284         jalviewjsServer = factory.start(doc_root, port)
2285         running = true
2286         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
2287         println("SERVER STARTED with document root ${doc_root}.")
2288         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
2289         println("For debug: "+url+"?j2sdebug")
2290         println("For verbose: "+url+"?j2sverbose")
2291       } catch (Exception e) {
2292         port++;
2293       }
2294     }
2295     def htmlText = """
2296       <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
2297       <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
2298       <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
2299       """
2300     jalviewjsCoreClasslists.each { cl ->
2301       def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
2302       htmlText += """
2303       <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
2304       """
2305       println("For core ${cl.name}: "+urlcore)
2306     }
2307
2308     file(htmlFile).text = htmlText
2309   }
2310
2311   outputs.file(htmlFile)
2312   outputs.upToDateWhen({false})
2313 }
2314
2315
2316 task cleanJalviewjsAll {
2317   group "JalviewJS"
2318   description "Delete all configuration and build artifacts to do with JalviewJS build"
2319   dependsOn cleanJalviewjsSite
2320   dependsOn jalviewjsEclipsePaths
2321   
2322   doFirst {
2323     delete "${jalviewDir}/${jalviewjsBuildDir}"
2324     delete "${jalviewDir}/${eclipse_bin_dir}"
2325     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
2326       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
2327     }
2328     delete "${jalviewDir}/${jalviewjs_j2s_settings}"
2329   }
2330
2331   outputs.upToDateWhen( { false } )
2332 }
2333
2334
2335 task jalviewjsIDE_checkJ2sPlugin {
2336   group "00 JalviewJS in Eclipse"
2337   description "Compare the swingjs/net.sf.j2s.core.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
2338
2339   doFirst {
2340     def j2sPlugin = string("${jalviewDir}/${jalviewjs_j2s_plugin}")
2341     def j2sPluginFile = file(j2sPlugin)
2342     def copyPlugin = jalviewjs_eclipseIDE_auto_copy_j2s_plugin == "true"
2343     def eclipseHome = System.properties["eclipse.home.location"]
2344     def doCopy = false
2345     if (eclipseHome == null || ! IN_ECLIPSE) {
2346       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
2347     }
2348     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
2349     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
2350     if (!eclipseJ2sPluginFile.exists()) {
2351       def msg = "Eclipse J2S Plugin is not installed (could not find '${eclipseJ2sPlugin}')"
2352       System.err.println(msg)
2353       if (! copyPlugin) {
2354         throw new GradleException(msg)
2355       }
2356       doCopy = true
2357     }
2358
2359     def digest = MessageDigest.getInstance("MD5")
2360
2361     digest.update(j2sPluginFile.text.bytes)
2362     def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
2363
2364     digest.update(eclipseJ2sPluginFile.text.bytes)
2365     def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
2366      
2367     if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
2368       def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
2369       System.err.println(msg)
2370       if (! copyPlugin) {
2371         throw new StopExecutionException(msg)
2372       }
2373       doCopy = true
2374     }
2375
2376     if (doCopy) {
2377       def msg = "WARNING! Auto-copying this commit's j2s plugin version '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n         May require an Eclipse restart"
2378       println(msg)
2379       copy {
2380         from j2sPlugin
2381         eclipseJ2sPluginFile.getParentFile().mkdirs()
2382         into eclipseJ2sPluginFile.getParent()
2383       }
2384     } else {
2385       def msg = "Eclipse J2S Plugin is the same as '${j2sPlugin}' (this is good)"
2386       println(msg)
2387     }
2388   }
2389 }
2390
2391
2392 task jalviewjsIDE_j2sFile {
2393   group "00 JalviewJS in Eclipse"
2394   description "Creates the .j2s file"
2395   dependsOn jalviewjsCreateJ2sSettings
2396 }
2397
2398
2399 task jalviewjsIDE_SyncCore {
2400   group "00 JalviewJS in Eclipse"
2401   description "Build the core js lib closures listed in the classlists dir and publish core html from template"
2402   dependsOn jalviewjsSyncCore
2403 }
2404
2405
2406 task jalviewjsIDE_SyncSiteAll {
2407   dependsOn jalviewjsSyncAllLibs
2408   dependsOn jalviewjsSyncResources
2409   dependsOn jalviewjsSyncSiteResources
2410   dependsOn jalviewjsSyncBuildProperties
2411 }
2412
2413
2414 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
2415
2416
2417 task jalviewjsIDE_PrepareSite {
2418   group "00 JalviewJS in Eclipse"
2419   description "Sync libs and resources to site dir, but not closure cores"
2420
2421   dependsOn jalviewjsIDE_SyncSiteAll
2422   dependsOn cleanJalviewjsTransferSite
2423 }
2424
2425
2426 task jalviewjsIDE_AssembleSite {
2427   group "00 JalviewJS in Eclipse"
2428   description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
2429   dependsOn jalviewjsPrepareSite
2430 }
2431
2432
2433 task jalviewjsIDE_SiteClean {
2434   group "00 JalviewJS in Eclipse"
2435   description "Deletes the Eclipse transpiled site"
2436   dependsOn cleanJalviewjsSite
2437 }
2438
2439
2440 task jalviewjsIDE_Server {
2441   group "00 JalviewJS in Eclipse"
2442   description "Starts a webserver on localhost to test the website"
2443   dependsOn jalviewjsServer
2444 }
2445
2446
2447 // buildship runs this at import or gradle refresh
2448 task eclipseSynchronizationTask {
2449   //dependsOn eclipseSetup
2450   dependsOn createBuildProperties
2451   if (J2S_ENABLED) {
2452     dependsOn jalviewjsIDE_j2sFile
2453     dependsOn jalviewjsIDE_checkJ2sPlugin
2454     dependsOn jalviewjsIDE_PrepareSite
2455   }
2456 }
2457
2458
2459 // buildship runs this at build time or project refresh
2460 task eclipseAutoBuildTask {
2461   //dependsOn jalviewjsIDE_checkJ2sPlugin
2462   //dependsOn jalviewjsIDE_PrepareSite
2463 }
2464
2465
2466
2467
2468
2469
2470
2471
2472 task jalviewjs {
2473   group "JalviewJS"
2474   description "Build the site"
2475   dependsOn jalviewjsBuildSite
2476 }