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