JAL-3577 cleaned up old DMG files. Added NON-RELEASE DMG files. Changed how Jalview...
[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 import groovy.transform.ExternalizeMethods
7 import groovy.util.XmlParser
8 import groovy.xml.XmlUtil
9
10
11 buildscript {
12   repositories {
13     mavenCentral()
14     mavenLocal()
15   }
16   dependencies {
17     classpath 'org.openclover:clover:4.4.1'
18   }
19 }
20
21
22 plugins {
23   id 'java'
24   id 'application'
25   id 'eclipse'
26   id 'com.github.johnrengelman.shadow' version '4.0.3'
27   id 'com.install4j.gradle' version '8.0.4'
28   id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
29 }
30
31 repositories {
32   jcenter()
33   mavenCentral()
34   mavenLocal()
35 }
36
37
38 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
39 def string(Object o) {
40   return o == null ? "" : o.toString()
41 }
42
43
44 ext {
45   jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
46   jalviewDirRelativePath = jalviewDir
47
48   // local build environment properties
49   // can be "projectDir/local.properties"
50   def localProps = "${projectDir}/local.properties"
51   def propsFile = null;
52   if (file(localProps).exists()) {
53     propsFile = localProps
54   }
55   // or "../projectDir_local.properties"
56   def dirLocalProps = projectDir.getParent() + "/" + projectDir.getName() + "_local.properties"
57   if (file(dirLocalProps).exists()) {
58     propsFile = dirLocalProps
59   }
60   if (propsFile != null) {
61     try {
62       def p = new Properties()
63       def localPropsFIS = new FileInputStream(propsFile)
64       p.load(localPropsFIS)
65       localPropsFIS.close()
66       p.each {
67         key, val -> 
68           def oldval = findProperty(key)
69           setProperty(key, val)
70           if (oldval != null) {
71             println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
72           } else {
73             println("Setting unknown property '${key}' with ${file(propsFile).getName()}s value '${val}'")
74           }
75       }
76     } catch (Exception e) {
77       System.out.println("Exception reading local.properties")
78     }
79   }
80
81   // this property set when running Eclipse headlessly
82   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
83   // this property set by Eclipse
84   eclipseApplicationProperty = string("eclipse.application")
85   // CHECK IF RUNNING FROM WITHIN ECLIPSE
86   def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
87   IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
88   // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
89   if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
90     println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
91     IN_ECLIPSE = false
92   }
93   if (IN_ECLIPSE) {
94     println("WITHIN ECLIPSE IDE")
95   } else {
96     println("HEADLESS BUILD")
97   }
98   /* *-/
99   System.properties.sort { it.key }.each {
100     key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
101   }
102   /-* *-/
103   if (false && IN_ECLIPSE) {
104     jalviewDir = jalviewDirAbsolutePath
105   }
106   */
107
108   // essentials
109   bareSourceDir = string(source_dir)
110   sourceDir = string("${jalviewDir}/${bareSourceDir}")
111   resourceDir = string("${jalviewDir}/${resource_dir}")
112   bareTestSourceDir = string(test_source_dir)
113   testSourceDir = string("${jalviewDir}/${bareTestSourceDir}")
114
115   // clover
116   cloverInstrDir = file("${buildDir}/${cloverSourcesInstrDir}")
117   cloverDb = string("${buildDir}/clover/clover.db")
118   classesDir = string("${jalviewDir}/${classes_dir}")
119   if (clover.equals("true")) {
120     use_clover = true
121     classesDir = string("${buildDir}/${cloverClassesDir}")
122   } else {
123     use_clover = false
124     classesDir = string("${jalviewDir}/${classes_dir}")
125   }
126
127   classes = classesDir
128
129   getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
130   buildDist = true
131
132   // the following values might be overridden by the CHANNEL switch
133   getdownChannelName = CHANNEL.toLowerCase()
134   getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
135   getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
136   getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
137   getdownAppDistDir = getdown_app_dir_alt
138   buildProperties = string("${classesDir}/${build_properties_file}")
139   reportRsyncCommand = false
140   jvlChannelName = CHANNEL.toLowerCase()
141   install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
142   install4jDSStore = "DS_Store-NON-RELEASE"
143   install4jDMGBackgroundImage = "jalview_dmg_background-NON-RELEASE.png"
144   install4jInstallerName = "${jalview_name} Installer"
145   install4jExecutableName = jalview_name.replaceAll("[^\\w]+", "_").toLowerCase()
146   switch (CHANNEL) {
147
148     case "BUILD":
149     // TODO: get bamboo build artifact URL for getdown artifacts
150     getdown_channel_base = bamboo_channelbase
151     getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
152     getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
153     jvlChannelName += "_${getdownChannelName}"
154     // automatically add the test group Not-bamboo for exclusion 
155     if ("".equals(testngExcludedGroups)) { 
156       testngExcludedGroups = "Not-bamboo"
157     }
158     break
159
160     case "RELEASE":
161     getdownAppDistDir = getdown_app_dir_release
162     reportRsyncCommand = true
163     install4jSuffix = ""
164     install4jDSStore = "DS_Store"
165     install4jDMGBackgroundImage = "jalview_dmg_background.png"
166     break
167
168     case "ARCHIVE":
169     getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
170     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
171     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
172     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
173       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
174     } else {
175       packageDir = string("${ARCHIVEDIR}/${packageDir}")
176       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
177       buildDist = false
178     }
179     reportRsyncCommand = true
180     break
181
182     case "ARCHIVELOCAL":
183     getdownChannelName = string("archive/${JALVIEW_VERSION}")
184     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
185     getdownAppBase = file(getdownWebsiteDir).toURI().toString()
186     if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
187       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
188     } else {
189       packageDir = string("${ARCHIVEDIR}/${packageDir}")
190       buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
191       buildDist = false
192     }
193     reportRsyncCommand = true
194     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
195     install4jSuffix = "Archive"
196     break
197
198     case "DEVELOP":
199     reportRsyncCommand = true
200     break
201
202     case "TEST-RELEASE":
203     reportRsyncCommand = true
204     install4jSuffix = "Test"
205     JALVIEW_VERSION = "TEST"
206     break
207
208     case ~/^SCRATCH(|-[-\w]*)$/:
209     getdownChannelName = CHANNEL
210     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
211     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
212     reportRsyncCommand = true
213     install4jSuffix = "Scratch"
214     break
215
216     case "TEST-LOCAL":
217     if (!file("${LOCALDIR}").exists()) {
218       throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
219     } else {
220       getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
221       getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
222     }
223     install4jSuffix = "Test-Local"
224     JALVIEW_VERSION = "TEST"
225     break
226
227     case "LOCAL":
228     getdownAppBase = file(getdownWebsiteDir).toURI().toString()
229     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
230     break
231
232     default: // something wrong specified
233     throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
234     break
235
236   }
237   // override getdownAppBase if requested
238   if (findProperty("getdown_appbase_override") != null) {
239     getdownAppBase = string(getProperty("getdown_appbase_override"))
240     println("Overriding getdown appbase with '${getdownAppBase}'")
241   }
242   // sanitise file name for jalview launcher file for this channel
243   jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
244   // install4j application and folder names
245   if (install4jSuffix == "") {
246     install4jApplicationName = "${jalview_name}"
247   } else {
248     install4jApplicationName = "${jalview_name} ${install4jSuffix}"
249     install4jInstallerName = "${jalview_name} Non-Release Installer"
250   }
251   // sanitise folder names
252   install4jApplicationFolder = install4jApplicationName
253                                     .replaceAll("[\"'~:\\\\]+", "_") // replace all awkward filename chars
254   install4jUnixApplicationFolder = install4jApplicationFolder
255                                     .toLowerCase()
256                                     .replaceAll(" ","_")
257                                     .replaceAll("[^\\w\\-\\.]+", "_") // replace other non [alphanumeric,_,-,.]
258                                     .replaceAll("_+", "_")
259                                     .replaceAll("_*-_*", "-")
260
261   getdownAppDir = string("${getdownWebsiteDir}/${getdownAppDistDir}")
262   //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
263   getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
264   getdownInstallDir = string("${getdownWebsiteDir}/${getdown_install_dir}")
265   getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
266   getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
267   /* compile without modules -- using classpath libraries
268   modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
269   modules_runtimeClasspath = modules_compileClasspath
270   */
271   gitHash = string("")
272   gitBranch = string("")
273
274   println("Using a ${CHANNEL} profile.")
275
276   additional_compiler_args = []
277   // configure classpath/args for j8/j11 compilation
278   if (JAVA_VERSION.equals("1.8")) {
279     JAVA_INTEGER_VERSION = string("8")
280     //libDir = j8libDir
281     libDir = j11libDir
282     libDistDir = j8libDir
283     compile_source_compatibility = 1.8
284     compile_target_compatibility = 1.8
285     // these are getdown.txt properties defined dependent on the JAVA_VERSION
286     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
287     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
288     // this property is assigned below and expanded to multiple lines in the getdown task
289     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
290     // this property is for the Java library used in eclipse
291     eclipseJavaRuntimeName = string("JavaSE-1.8")
292   } else if (JAVA_VERSION.equals("11")) {
293     JAVA_INTEGER_VERSION = string("11")
294     libDir = j11libDir
295     libDistDir = j11libDir
296     compile_source_compatibility = 11
297     compile_target_compatibility = 11
298     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
299     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
300     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
301     eclipseJavaRuntimeName = string("JavaSE-11")
302     /* compile without modules -- using classpath libraries
303     additional_compiler_args += [
304     '--module-path', modules_compileClasspath.asPath,
305     '--add-modules', j11modules
306     ]
307      */
308   } else if (JAVA_VERSION.equals("12") || JAVA_VERSION.equals("13")) {
309     JAVA_INTEGER_VERSION = JAVA_VERSION
310     libDir = j11libDir
311     libDistDir = j11libDir
312     compile_source_compatibility = JAVA_VERSION
313     compile_target_compatibility = JAVA_VERSION
314     getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
315     getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
316     getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
317     eclipseJavaRuntimeName = string("JavaSE-11")
318     /* compile without modules -- using classpath libraries
319     additional_compiler_args += [
320     '--module-path', modules_compileClasspath.asPath,
321     '--add-modules', j11modules
322     ]
323      */
324   } else {
325     throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
326   }
327
328
329   // for install4j
330   JAVA_MIN_VERSION = JAVA_VERSION
331   JAVA_MAX_VERSION = JAVA_VERSION
332   def jreInstallsDir = string(jre_installs_dir)
333   if (jreInstallsDir.startsWith("~/")) {
334     jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
335   }
336   macosJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-mac-x64/jre")
337   macosJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-mac-x64.tar.gz")
338   windowsJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-windows-x64/jre")
339   windowsJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-windows-x64.tar.gz")
340   linuxJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-linux-x64/jre")
341   linuxJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-linux-x64.tar.gz")
342   install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
343   install4jConfFileName = string("jalview-install4j-conf.install4j")
344   install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
345   install4jHomeDir = install4j_home_dir
346   if (install4jHomeDir.startsWith("~/")) {
347     install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
348   }
349
350
351
352   buildingHTML = string("${jalviewDir}/${docDir}/building.html")
353   helpFile = string("${classesDir}/${help_dir}/help.jhm")
354   helpParentDir = string("${jalviewDir}/${help_parent_dir}")
355   helpSourceDir = string("${helpParentDir}/${help_dir}")
356
357
358   relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
359   jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
360   jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
361   if (IN_ECLIPSE) {
362     jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
363   } else {
364     jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
365   }
366   jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
367   jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
368   jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
369   jalviewjsJalviewCoreHtmlFile = string("")
370   jalviewjsJalviewCoreName = string(jalviewjs_core_name)
371   jalviewjsCoreClasslists = []
372   jalviewjsJalviewTemplateName = string(jalviewjs_name)
373   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
374   jalviewjsJ2sProps = null
375
376   eclipseWorkspace = null
377   eclipseBinary = string("")
378   eclipseVersion = string("")
379   eclipseDebug = false
380   // ENDEXT
381 }
382
383
384 sourceSets {
385   main {
386     java {
387       srcDirs sourceDir
388       outputDir = file(classesDir)
389     }
390
391     resources {
392       srcDirs resourceDir
393       srcDirs += helpParentDir
394     }
395
396     jar.destinationDir = file("${jalviewDir}/${packageDir}")
397
398     compileClasspath = files(sourceSets.main.java.outputDir)
399     //compileClasspath += files(sourceSets.main.resources.srcDirs)
400     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
401
402     runtimeClasspath = compileClasspath
403   }
404
405   clover {
406     java {
407       srcDirs = [ cloverInstrDir ]
408       outputDir = file("${buildDir}/${cloverClassesDir}")
409     }
410
411     resources {
412       srcDirs = sourceSets.main.resources.srcDirs
413     }
414     compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
415     compileClasspath += files(sourceSets.main.java.outputDir)
416     compileClasspath += sourceSets.main.compileClasspath
417     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"])
418     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
419
420     runtimeClasspath = compileClasspath
421   }
422
423   test {
424     java {
425       srcDirs testSourceDir
426       outputDir = file("${jalviewDir}/${testOutputDir}")
427     }
428
429     resources {
430       srcDirs = sourceSets.main.resources.srcDirs
431     }
432
433     compileClasspath = files( sourceSets.test.java.outputDir )
434
435     if (use_clover) {
436       compileClasspath = sourceSets.clover.compileClasspath
437     } else {
438       compileClasspath += files(sourceSets.main.java.outputDir)
439     }
440
441     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
442     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"])
443     compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"])
444
445     runtimeClasspath = compileClasspath
446   }
447 }
448
449
450 // clover bits
451 dependencies {
452   if (use_clover) {
453     cloverCompile 'org.openclover:clover:4.4.1'
454     testCompile 'org.openclover:clover:4.4.1'
455   }
456 }
457
458 configurations {
459   cloverRuntime
460   cloverRuntime.extendsFrom cloverCompile
461 }
462
463
464 // eclipse project and settings files creation, also used by buildship
465 eclipse {
466   project {
467     name = eclipse_project_name
468
469     natures 'org.eclipse.jdt.core.javanature',
470     'org.eclipse.jdt.groovy.core.groovyNature',
471     'org.eclipse.buildship.core.gradleprojectnature'
472
473     buildCommand 'org.eclipse.jdt.core.javabuilder'
474     buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
475   }
476
477   classpath {
478     //defaultOutputDir = sourceSets.main.java.outputDir
479     def removeThese = []
480     configurations.each{
481       if (it.isCanBeResolved()) {
482         removeThese += it
483       }
484     }
485
486     minusConfigurations += removeThese
487     plusConfigurations = [ ]
488     file {
489
490       whenMerged { cp ->
491         def removeTheseToo = []
492         HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
493         cp.entries.each { entry ->
494           // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
495           // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
496           // we add the resources and help/help dirs in as libs afterwards (see below)
497           if (entry.kind == 'src') {
498             if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
499               removeTheseToo += entry
500             } else {
501               alreadyAddedSrcPath.putAt(entry.path, true)
502             }
503           }
504
505         }
506         cp.entries.removeAll(removeTheseToo)
507
508         //cp.entries += new Output("${eclipse_bin_dir}/main")
509         if (file(helpParentDir).isDirectory()) {
510           cp.entries += new Library(fileReference(helpParentDir))
511         }
512         if (file(resourceDir).isDirectory()) {
513           cp.entries += new Library(fileReference(resourceDir))
514         }
515
516         HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
517
518         sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
519           //don't want to add outputDir as eclipse is using its own output dir in bin/main
520           if (it.isDirectory() || ! it.exists()) {
521             // don't add dirs to classpath, especially if they don't exist
522             return false // groovy "continue" in .any closure
523           }
524           def itPath = it.toString()
525           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
526             // make relative path
527             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
528           }
529           if (alreadyAddedLibPath.get(itPath)) {
530             //println("Not adding duplicate entry "+itPath)
531           } else {
532             //println("Adding entry "+itPath)
533             cp.entries += new Library(fileReference(itPath))
534             alreadyAddedLibPath.put(itPath, true)
535           }
536         }
537
538         sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
539           //no longer want to add outputDir as eclipse is using its own output dir in bin/main
540           if (it.isDirectory() || ! it.exists()) {
541             // don't add dirs to classpath
542             return false // groovy "continue" in .any closure
543           }
544
545           def itPath = it.toString()
546           if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
547             itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
548           }
549           if (alreadyAddedLibPath.get(itPath)) {
550             // don't duplicate
551           } else {
552             def lib = new Library(fileReference(itPath))
553             lib.entryAttributes["test"] = "true"
554             cp.entries += lib
555             alreadyAddedLibPath.put(itPath, true)
556           }
557         }
558
559       } // whenMerged
560
561     } // file
562
563     containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
564
565   } // classpath
566
567   jdt {
568     // for the IDE, use java 11 compatibility
569     sourceCompatibility = compile_source_compatibility
570     targetCompatibility = compile_target_compatibility
571     javaRuntimeName = eclipseJavaRuntimeName
572
573     // add in jalview project specific properties/preferences into eclipse core preferences
574     file {
575       withProperties { props ->
576         def jalview_prefs = new Properties()
577         def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
578         jalview_prefs.load(ins)
579         ins.close()
580         jalview_prefs.forEach { t, v ->
581           if (props.getAt(t) == null) {
582             props.putAt(t, v)
583           }
584         }
585       }
586     }
587
588   } // jdt
589
590   if (IN_ECLIPSE) {
591     // Don't want these to be activated if in headless build
592     synchronizationTasks "eclipseSynchronizationTask"
593     autoBuildTasks "eclipseAutoBuildTask"
594
595   }
596 }
597
598
599 task cloverInstr {
600   // only instrument source, we build test classes as normal
601   inputs.files files (sourceSets.main.allJava,sourceSets.test.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"]))
602   outputs.dir cloverInstrDir
603
604   doFirst {
605     delete cloverInstrDir
606     def argsList = [
607       "--initstring",
608       cloverDb,
609       "-d",
610       cloverInstrDir.getPath(),
611     ]
612     argsList.addAll(
613       inputs.files.files.collect(
614         { file -> file.absolutePath }
615       )
616     )
617     String[] args = argsList.toArray()
618     println("About to instrument "+args.length +" files")
619     com.atlassian.clover.CloverInstr.mainImpl(args)
620   }
621 }
622
623
624 cloverClasses.dependsOn cloverInstr
625
626
627 task cloverReport {
628   group = "Verification"
629   description = "Creates the Clover report"
630   inputs.dir "${buildDir}/clover"
631   outputs.dir "${reportsDir}/clover"
632   onlyIf {
633     file(cloverDb).exists()
634   }
635   doFirst {
636     def argsList = [
637       "--initstring",
638       cloverDb,
639       "-o",
640       "${reportsDir}/clover"
641     ]
642     String[] args = argsList.toArray()
643     com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
644
645     // and generate ${reportsDir}/clover/clover.xml
646     args = [
647       "--initstring",
648       cloverDb,
649       "-o",
650       "${reportsDir}/clover/clover.xml"
651     ].toArray()
652     com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
653   }
654 }
655
656
657 compileCloverJava {
658
659   doFirst {
660     sourceCompatibility = compile_source_compatibility
661     targetCompatibility = compile_target_compatibility
662     options.compilerArgs += additional_compiler_args
663     print ("Setting target compatibility to "+targetCompatibility+"\n")
664   }
665   classpath += configurations.cloverRuntime
666 }
667
668
669 task cleanClover {
670   doFirst {
671     delete cloverInstrDir
672     delete cloverDb
673   }
674 }
675 // end clover bits
676
677
678 compileJava {
679
680   doFirst {
681     sourceCompatibility = compile_source_compatibility
682     targetCompatibility = compile_target_compatibility
683     options.compilerArgs = additional_compiler_args
684     print ("Setting target compatibility to "+targetCompatibility+"\n")
685   }
686
687 }
688
689
690 compileTestJava {
691   if (use_clover) {
692     dependsOn compileCloverJava
693     classpath += configurations.cloverRuntime
694   } else {
695     classpath += sourceSets.main.runtimeClasspath
696   }
697   doFirst {
698     sourceCompatibility = compile_source_compatibility
699     targetCompatibility = compile_target_compatibility
700     options.compilerArgs = additional_compiler_args
701     print ("Setting target compatibility to "+targetCompatibility+"\n")
702   }
703 }
704
705
706 clean {
707   doFirst {
708     delete sourceSets.main.java.outputDir
709   }
710 }
711
712
713 cleanTest {
714   dependsOn cleanClover
715   doFirst {
716     delete sourceSets.test.java.outputDir
717   }
718 }
719
720
721 // format is a string like date.format("dd MMMM yyyy")
722 def getDate(format) {
723   def date = new Date()
724   return date.format(format)
725 }
726
727
728 task setGitVals {
729   def hashStdOut = new ByteArrayOutputStream()
730   exec {
731     commandLine "git", "rev-parse", "--short", "HEAD"
732     standardOutput = hashStdOut
733     ignoreExitValue true
734   }
735
736   def branchStdOut = new ByteArrayOutputStream()
737   exec {
738     commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
739     standardOutput = branchStdOut
740     ignoreExitValue true
741   }
742
743   gitHash = hashStdOut.toString().trim()
744   gitBranch = branchStdOut.toString().trim()
745
746   outputs.upToDateWhen { false }
747 }
748
749
750 task createBuildProperties(type: WriteProperties) {
751   dependsOn setGitVals
752   inputs.dir(sourceDir)
753   inputs.dir(resourceDir)
754   file(buildProperties).getParentFile().mkdirs()
755   outputFile (buildProperties)
756   // taking time specific comment out to allow better incremental builds
757   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
758   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
759   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
760   property "VERSION", JALVIEW_VERSION
761   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
762   outputs.file(outputFile)
763 }
764
765
766 task cleanBuildingHTML(type: Delete) {
767   doFirst {
768     delete buildingHTML
769   }
770 }
771
772
773 task convertBuildingMD(type: Exec) {
774   dependsOn cleanBuildingHTML
775   def buildingMD = "${jalviewDir}/${docDir}/building.md"
776   def css = "${jalviewDir}/${docDir}/github.css"
777
778   def pandoc = null
779   pandoc_exec.split(",").each {
780     if (file(it.trim()).exists()) {
781       pandoc = it.trim()
782       return true
783     }
784   }
785
786   def hostname = "hostname".execute().text.trim()
787   def buildtoolsPandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
788   if ((pandoc == null || ! file(pandoc).exists()) && file(buildtoolsPandoc).exists()) {
789     pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
790   }
791
792   doFirst {
793     if (pandoc != null && file(pandoc).exists()) {
794         commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
795     } else {
796         println("Cannot find pandoc. Skipping convert building.md to HTML")
797         throw new StopExecutionException("Cannot find pandoc. Skipping convert building.md to HTML")
798     }
799   }
800
801   ignoreExitValue true
802
803   inputs.file(buildingMD)
804   inputs.file(css)
805   outputs.file(buildingHTML)
806 }
807
808
809 clean {
810   doFirst {
811     delete buildingHTML
812   }
813 }
814
815
816 task syncDocs(type: Sync) {
817   dependsOn convertBuildingMD
818   def syncDir = "${classesDir}/${docDir}"
819   from fileTree("${jalviewDir}/${docDir}")
820   into syncDir
821
822 }
823
824
825 task copyHelp(type: Copy) {
826   def inputDir = helpSourceDir
827   def outputDir = "${classesDir}/${help_dir}"
828   from(inputDir) {
829     exclude '**/*.gif'
830     exclude '**/*.jpg'
831     exclude '**/*.png'
832     filter(ReplaceTokens,
833       beginToken: '$$',
834       endToken: '$$',
835       tokens: [
836         'Version-Rel': JALVIEW_VERSION,
837         'Year-Rel': getDate("yyyy")
838       ]
839     )
840   }
841   from(inputDir) {
842     include '**/*.gif'
843     include '**/*.jpg'
844     include '**/*.png'
845   }
846   into outputDir
847
848   inputs.dir(inputDir)
849   outputs.files(helpFile)
850   outputs.dir(outputDir)
851 }
852
853
854 task syncLib(type: Sync) {
855   def syncDir = "${classesDir}/${libDistDir}"
856   from fileTree("${jalviewDir}/${libDistDir}")
857   into syncDir
858 }
859
860
861 task syncResources(type: Sync) {
862   from resourceDir
863   include "**/*.*"
864   into "${classesDir}"
865   preserve {
866     include "**"
867   }
868 }
869
870
871 task prepare {
872   dependsOn syncResources
873   dependsOn syncDocs
874   dependsOn copyHelp
875 }
876
877
878 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
879 test {
880   dependsOn prepare
881   dependsOn compileJava
882   if (use_clover) {
883     dependsOn cloverInstr
884   }
885
886   if (use_clover) {
887     print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
888   }
889
890   useTestNG() {
891     includeGroups testngGroups
892     excludeGroups testngExcludedGroups
893     preserveOrder true
894     useDefaultListeners=true
895   }
896
897   maxHeapSize = "1024m"
898
899   workingDir = jalviewDir
900   //systemProperties 'clover.jar' System.properties.clover.jar
901   sourceCompatibility = compile_source_compatibility
902   targetCompatibility = compile_target_compatibility
903   jvmArgs += additional_compiler_args
904
905 }
906
907
908 task buildIndices(type: JavaExec) {
909   dependsOn copyHelp
910   classpath = sourceSets.main.compileClasspath
911   main = "com.sun.java.help.search.Indexer"
912   workingDir = "${classesDir}/${help_dir}"
913   def argDir = "html"
914   args = [ argDir ]
915   inputs.dir("${workingDir}/${argDir}")
916
917   outputs.dir("${classesDir}/doc")
918   outputs.dir("${classesDir}/help")
919   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
920   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
921   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
922   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
923   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
924   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
925 }
926
927
928 task compileLinkCheck(type: JavaCompile) {
929   options.fork = true
930   classpath = files("${jalviewDir}/${utilsDir}")
931   destinationDir = file("${jalviewDir}/${utilsDir}")
932   source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
933
934   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
935   inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
936   outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class")
937   outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class")
938 }
939
940
941 task linkCheck(type: JavaExec) {
942   dependsOn prepare, compileLinkCheck
943
944   def helpLinksCheckerOutFile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.out")
945   classpath = files("${jalviewDir}/${utilsDir}")
946   main = "HelpLinksChecker"
947   workingDir = jalviewDir
948   args = [ "${classesDir}/${help_dir}", "-nointernet" ]
949
950   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
951   def errFOS = outFOS
952   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
953     outFOS,
954     standardOutput)
955   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
956     outFOS,
957     errorOutput)
958
959   inputs.dir("${classesDir}/${help_dir}")
960   outputs.file(helpLinksCheckerOutFile)
961 }
962
963 // import the pubhtmlhelp target
964 ant.properties.basedir = "${jalviewDir}"
965 ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${help_dir}"
966 ant.importBuild "${utilsDir}/publishHelp.xml"
967
968
969 task cleanPackageDir(type: Delete) {
970   doFirst {
971     delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar")
972   }
973 }
974
975 jar {
976   dependsOn linkCheck
977   dependsOn buildIndices
978   dependsOn createBuildProperties
979
980   manifest {
981     attributes "Main-Class": mainClass,
982     "Permissions": "all-permissions",
983     "Application-Name": "Jalview Desktop",
984     "Codebase": application_codebase
985   }
986
987   destinationDir = file("${jalviewDir}/${packageDir}")
988   archiveName = rootProject.name+".jar"
989
990   exclude "cache*/**"
991   exclude "*.jar"
992   exclude "*.jar.*"
993   exclude "**/*.jar"
994   exclude "**/*.jar.*"
995
996   inputs.dir(classesDir)
997   outputs.file("${jalviewDir}/${packageDir}/${archiveName}")
998 }
999
1000
1001 task copyJars(type: Copy) {
1002   from fileTree(dir: classesDir, include: "**/*.jar").files
1003   into "${jalviewDir}/${packageDir}"
1004 }
1005
1006
1007 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
1008 task syncJars(type: Sync) {
1009   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
1010   into "${jalviewDir}/${packageDir}"
1011   preserve {
1012     include jar.archiveName
1013   }
1014 }
1015
1016
1017 task makeDist {
1018   group = "build"
1019   description = "Put all required libraries in dist"
1020   // order of "cleanPackageDir", "copyJars", "jar" important!
1021   jar.mustRunAfter cleanPackageDir
1022   syncJars.mustRunAfter cleanPackageDir
1023   dependsOn cleanPackageDir
1024   dependsOn syncJars
1025   dependsOn jar
1026   outputs.dir("${jalviewDir}/${packageDir}")
1027 }
1028
1029
1030 task cleanDist {
1031   dependsOn cleanPackageDir
1032   dependsOn cleanTest
1033   dependsOn clean
1034 }
1035
1036 shadowJar {
1037   group = "distribution"
1038   if (buildDist) {
1039     dependsOn makeDist
1040   }
1041   from ("${jalviewDir}/${libDistDir}") {
1042     include("*.jar")
1043   }
1044   manifest {
1045     attributes 'Implementation-Version': JALVIEW_VERSION
1046   }
1047   mainClassName = shadowJarMainClass
1048   mergeServiceFiles()
1049   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
1050   minimize()
1051 }
1052
1053
1054 task getdownWebsite() {
1055   group = "distribution"
1056   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
1057   if (buildDist) {
1058     dependsOn makeDist
1059   }
1060
1061   def getdownWebsiteResourceFilenames = []
1062   def getdownTextString = ""
1063   def getdownResourceDir = getdownResourceDir
1064   def getdownResourceFilenames = []
1065
1066   doFirst {
1067     // clean the getdown website and files dir before creating getdown folders
1068     delete getdownWebsiteDir
1069     delete getdownFilesDir
1070
1071     copy {
1072       from buildProperties
1073       rename(build_properties_file, getdown_build_properties)
1074       into getdownAppDir
1075     }
1076     getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
1077
1078     // set some getdown_txt_ properties then go through all properties looking for getdown_txt_...
1079     def props = project.properties.sort { it.key }
1080     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
1081       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
1082     }
1083     if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
1084       props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
1085     }
1086     if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
1087       props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
1088     }
1089
1090     props.put("getdown_txt_title", jalview_name)
1091     props.put("getdown_txt_ui.name", install4jApplicationName)
1092
1093     // start with appbase
1094     getdownTextString += "appbase = ${getdownAppBase}\n"
1095     props.each{ prop, val ->
1096       if (prop.startsWith("getdown_txt_") && val != null) {
1097         if (prop.startsWith("getdown_txt_multi_")) {
1098           def key = prop.substring(18)
1099           val.split(",").each{ v ->
1100             def line = "${key} = ${v}\n"
1101             getdownTextString += line
1102           }
1103         } else {
1104           // file values rationalised
1105           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1106             def r = null
1107             if (val.indexOf('/') == 0) {
1108               // absolute path
1109               r = file(val)
1110             } else if (val.indexOf('/') > 0) {
1111               // relative path (relative to jalviewDir)
1112               r = file( "${jalviewDir}/${val}" )
1113             }
1114             if (r.exists()) {
1115               val = "${getdown_resource_dir}/" + r.getName()
1116               getdownWebsiteResourceFilenames += val
1117               getdownResourceFilenames += r.getPath()
1118             }
1119           }
1120           if (! prop.startsWith("getdown_txt_resource")) {
1121             def line = prop.substring(12) + " = ${val}\n"
1122             getdownTextString += line
1123           }
1124         }
1125       }
1126     }
1127
1128     getdownWebsiteResourceFilenames.each{ filename ->
1129       getdownTextString += "resource = ${filename}\n"
1130     }
1131     getdownResourceFilenames.each{ filename ->
1132       copy {
1133         from filename
1134         into getdownResourceDir
1135       }
1136     }
1137
1138     def codeFiles = []
1139     fileTree(file(packageDir)).each{ f ->
1140       if (f.isDirectory()) {
1141         def files = fileTree(dir: f, include: ["*"]).getFiles()
1142         codeFiles += files
1143       } else if (f.exists()) {
1144         codeFiles += f
1145       }
1146     }
1147     codeFiles.sort().each{f ->
1148       def name = f.getName()
1149       def line = "code = ${getdownAppDistDir}/${name}\n"
1150       getdownTextString += line
1151       copy {
1152         from f.getPath()
1153         into getdownAppDir
1154       }
1155     }
1156
1157     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
1158     /*
1159     if (JAVA_VERSION.equals("11")) {
1160     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
1161     j11libFiles.sort().each{f ->
1162     def name = f.getName()
1163     def line = "code = ${getdown_j11lib_dir}/${name}\n"
1164     getdownTextString += line
1165     copy {
1166     from f.getPath()
1167     into getdownJ11libDir
1168     }
1169     }
1170     }
1171      */
1172
1173     // 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.
1174     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
1175     getdownTextString += "resource = ${getdown_launcher_new}\n"
1176     getdownTextString += "class = ${mainClass}\n"
1177
1178     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
1179     getdown_txt.write(getdownTextString)
1180
1181     def getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
1182     def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
1183     launchJvl.write("appbase=${getdownAppBase}")
1184
1185     copy {
1186       from getdownLauncher
1187       rename(file(getdownLauncher).getName(), getdown_launcher_new)
1188       into getdownWebsiteDir
1189     }
1190
1191     copy {
1192       from getdownLauncher
1193       if (file(getdownLauncher).getName() != getdown_launcher) {
1194         rename(file(getdownLauncher).getName(), getdown_launcher)
1195       }
1196       into getdownWebsiteDir
1197     }
1198
1199     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
1200       copy {
1201         from getdown_txt
1202         from getdownLauncher
1203         from "${getdownWebsiteDir}/${getdown_build_properties}"
1204         if (file(getdownLauncher).getName() != getdown_launcher) {
1205           rename(file(getdownLauncher).getName(), getdown_launcher)
1206         }
1207         into getdownInstallDir
1208       }
1209
1210       copy {
1211         from getdownInstallDir
1212         into getdownFilesInstallDir
1213       }
1214     }
1215
1216     copy {
1217       from getdown_txt
1218       from launchJvl
1219       from getdownLauncher
1220       from "${getdownWebsiteDir}/${getdown_build_properties}"
1221       if (file(getdownLauncher).getName() != getdown_launcher) {
1222         rename(file(getdownLauncher).getName(), getdown_launcher)
1223       }
1224       into getdownFilesDir
1225     }
1226
1227     copy {
1228       from getdownResourceDir
1229       into "${getdownFilesDir}/${getdown_resource_dir}"
1230     }
1231   }
1232
1233   if (buildDist) {
1234     inputs.dir("${jalviewDir}/${packageDir}")
1235   }
1236   outputs.dir(getdownWebsiteDir)
1237   outputs.dir(getdownFilesDir)
1238 }
1239
1240
1241 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
1242 task getdownDigestDir(type: JavaExec) {
1243   def digestDirPropertyName = "DIGESTDIR"
1244   description = "Digest a local dir (-P${digestDirPropertyName}=...) for getdown"
1245   doFirst {
1246     classpath = files(getdownLauncher)
1247     def digestDir = findProperty(digestDirPropertyName)
1248     if (digestDir == null) {
1249       throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
1250     }
1251     args digestDir
1252   }
1253   main = "com.threerings.getdown.tools.Digester"
1254 }
1255
1256
1257 task getdownDigest(type: JavaExec) {
1258   group = "distribution"
1259   description = "Digest the getdown website folder"
1260   dependsOn getdownWebsite
1261   doFirst {
1262     classpath = files(getdownLauncher)
1263   }
1264   main = "com.threerings.getdown.tools.Digester"
1265   args getdownWebsiteDir
1266   inputs.dir(getdownWebsiteDir)
1267   outputs.file("${getdownWebsiteDir}/digest2.txt")
1268 }
1269
1270
1271 task getdown() {
1272   group = "distribution"
1273   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1274   dependsOn getdownDigest
1275   doLast {
1276     if (reportRsyncCommand) {
1277       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
1278       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1279       println "LIKELY RSYNC COMMAND:"
1280       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1281       if (RUNRSYNC == "true") {
1282         exec {
1283           commandLine "mkdir", "-p", toDir
1284         }
1285         exec {
1286           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1287         }
1288       }
1289     }
1290   }
1291 }
1292
1293
1294 clean {
1295   doFirst {
1296     delete getdownWebsiteDir
1297     delete getdownFilesDir
1298   }
1299 }
1300
1301
1302 install4j {
1303   if (file(install4jHomeDir).exists()) {
1304     // good to go!
1305   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
1306     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1307   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
1308     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
1309   }
1310   installDir(file(install4jHomeDir))
1311
1312   mediaTypes = Arrays.asList(install4j_media_types.split(","))
1313 }
1314
1315
1316 task copyInstall4jTemplate {
1317   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
1318   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
1319   inputs.file(install4jTemplateFile)
1320   inputs.file(install4jFileAssociationsFile)
1321   outputs.file(install4jConfFile)
1322
1323   doLast {
1324     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
1325
1326     // turn off code signing if no OSX_KEYPASS
1327     if (OSX_KEYPASS == "") {
1328       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
1329         codeSigning.'@macEnabled' = "false"
1330       }
1331       install4jConfigXml.'**'.windows.each { windows ->
1332         windows.'@runPostProcessor' = "false"
1333       }
1334     }
1335
1336     // turn off checksum creation for LOCAL channel
1337     def e = install4jConfigXml.application[0]
1338     if (CHANNEL == "LOCAL") {
1339       e.'@createChecksums' = "false"
1340     } else {
1341       e.'@createChecksums' = "true"
1342     }
1343
1344     // put file association actions where placeholder action is
1345     def install4jFileAssociationsText = install4jFileAssociationsFile.text
1346     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
1347     install4jConfigXml.'**'.action.any { a ->
1348       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
1349         def parent = a.parent()
1350         parent.remove(a)
1351         fileAssociationActions.each { faa ->
1352             parent.append(faa)
1353         }
1354         // don't need to continue in .any loop once replacements have been made
1355         return true
1356       }
1357     }
1358
1359     // write install4j file
1360     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
1361   }
1362 }
1363
1364
1365 clean {
1366   doFirst {
1367     delete install4jConfFile
1368   }
1369 }
1370
1371
1372 task installers(type: com.install4j.gradle.Install4jTask) {
1373   group = "distribution"
1374   description = "Create the install4j installers"
1375   dependsOn setGitVals
1376   dependsOn getdown
1377   dependsOn copyInstall4jTemplate
1378
1379   projectFile = install4jConfFile
1380
1381   // create an md5 for the input files to use as version for install4j conf file
1382   def digest = MessageDigest.getInstance("MD5")
1383   digest.update(
1384     (file("${install4jDir}/${install4j_template}").text + 
1385     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
1386     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
1387   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
1388   if (filesMd5.length() >= 8) {
1389     filesMd5 = filesMd5.substring(0,8)
1390   }
1391   def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
1392   // make install4jBuildDir relative to jalviewDir
1393   def install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
1394
1395   variables = [
1396     'JALVIEW_NAME': install4jApplicationName,
1397     'JALVIEW_DIR': "../..",
1398     'OSX_KEYSTORE': OSX_KEYSTORE,
1399     'JSIGN_SH': JSIGN_SH,
1400     'JRE_DIR': getdown_app_dir_java,
1401     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
1402     'JALVIEW_VERSION': JALVIEW_VERSION,
1403     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
1404     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
1405     'JAVA_VERSION': JAVA_VERSION,
1406     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1407     'VERSION': JALVIEW_VERSION,
1408     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1409     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1410     'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
1411     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1412     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1413     'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
1414     'COPYRIGHT_MESSAGE': install4j_copyright_message,
1415     'MACOS_BUNDLE_ID': install4j_macOS_bundle_id,
1416     'MACOS_DS_STORE': install4jDSStore,
1417     'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
1418     'INSTALLER_NAME': install4jInstallerName,
1419     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
1420     'GETDOWN_WEBSITE_DIR': getdown_website_dir,
1421     'GETDOWN_FILES_DIR': getdown_files_dir,
1422     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1423     'GETDOWN_DIST_DIR': getdownAppDistDir,
1424     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1425     'GETDOWN_INSTALL_DIR': getdown_install_dir,
1426     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
1427     'BUILD_DIR': install4jBuildDir,
1428     'UNIX_DESKTOP_ADDITIONS': install4j_unix_desktop_additions,
1429     'APPLICATION_FOLDER': install4jApplicationFolder,
1430     'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
1431     'EXECUTABLE_NAME': install4jExecutableName,
1432   ]
1433
1434   destination = "${jalviewDir}/${install4jBuildDir}"
1435   buildSelected = true
1436
1437   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
1438     faster = true
1439     disableSigning = true
1440   }
1441
1442   if (OSX_KEYPASS) {
1443     macKeystorePassword = OSX_KEYPASS
1444   }
1445
1446   doFirst {
1447     println("Using projectFile "+projectFile)
1448   }
1449
1450   inputs.dir(getdownWebsiteDir)
1451   inputs.file(install4jConfFile)
1452   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
1453   inputs.dir(macosJavaVMDir)
1454   inputs.dir(windowsJavaVMDir)
1455   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
1456 }
1457
1458
1459 task sourceDist(type: Tar) {
1460   
1461   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
1462   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
1463   // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
1464   try {
1465     archiveFileName = outputFileName
1466   } catch (Exception e) {
1467     archiveName = outputFileName
1468   }
1469   
1470   compression Compression.GZIP
1471   
1472   into project.name
1473
1474   def EXCLUDE_FILES=[
1475     "build/*",
1476     "bin/*",
1477     "test-output/",
1478     "test-reports",
1479     "tests",
1480     "clover*/*",
1481     ".*",
1482     "benchmarking/*",
1483     "**/.*",
1484     "*.class",
1485     "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
1486     "*locales/**",
1487     "utils/InstallAnywhere",
1488     "**/*.log",
1489   ] 
1490   def PROCESS_FILES=[
1491     "AUTHORS",
1492     "CITATION",
1493     "FEATURETODO",
1494     "JAVA-11-README",
1495     "FEATURETODO",
1496     "LICENSE",
1497     "**/README",
1498     "RELEASE",
1499     "THIRDPARTYLIBS",
1500     "TESTNG",
1501     "build.gradle",
1502     "gradle.properties",
1503     "**/*.java",
1504     "**/*.html",
1505     "**/*.xml",
1506     "**/*.gradle",
1507     "**/*.groovy",
1508     "**/*.properties",
1509     "**/*.perl",
1510     "**/*.sh",
1511   ]
1512   def INCLUDE_FILES=[
1513     ".settings/org.eclipse.jdt.core.jalview.prefs",
1514   ]
1515
1516   from(jalviewDir) {
1517     exclude (EXCLUDE_FILES)
1518     include (PROCESS_FILES)
1519     filter(ReplaceTokens,
1520       beginToken: '$$',
1521       endToken: '$$',
1522       tokens: [
1523         'Version-Rel': JALVIEW_VERSION,
1524         'Year-Rel': getDate("yyyy")
1525       ]
1526     )
1527   }
1528   from(jalviewDir) {
1529     exclude (EXCLUDE_FILES)
1530     exclude (PROCESS_FILES)
1531     exclude ("appletlib")
1532     exclude ("**/*locales")
1533     exclude ("*locales/**")
1534     exclude ("utils/InstallAnywhere")
1535
1536     exclude (getdown_files_dir)
1537     exclude (getdown_website_dir)
1538
1539     // exluding these as not using jars as modules yet
1540     exclude ("$j11modDir/**/*.jar")
1541   }
1542   from(jalviewDir) {
1543     include(INCLUDE_FILES)
1544   }
1545 //  from (jalviewDir) {
1546 //    // explicit includes for stuff that seemed to not get included
1547 //    include(fileTree("test/**/*."))
1548 //    exclude(EXCLUDE_FILES)
1549 //    exclude(PROCESS_FILES)
1550 //  }
1551 }
1552
1553
1554 task helppages {
1555   dependsOn copyHelp
1556   dependsOn pubhtmlhelp
1557   
1558   inputs.dir("${classesDir}/${help_dir}")
1559   outputs.dir("${buildDir}/distributions/${help_dir}")
1560 }
1561
1562 // LARGE AMOUNT OF JALVIEWJS STUFF DELETED HERE
1563 task eclipseAutoBuildTask {}
1564 task eclipseSynchronizationTask {}