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