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