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