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