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