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