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