JAL-4012 what’s new and release notes (in the new styleee!) for JAL-4004, JAL-3989...
[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 '1.5' // 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 task releasesTemplates {
1411   group "help"
1412   description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1413
1414   def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1415   def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1416   def releasesHtmlFile = file("${helpSourceDir}/${releases_html}")
1417   def whatsnewHtmlFile = file("${helpSourceDir}/${whatsnew_html}")
1418   def releasesMdDir = "${jalviewDir}/${releases_dir}"
1419   def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1420
1421   doFirst {
1422     def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1423     def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1424
1425     if (CHANNEL == "RELEASE") {
1426       if (!releaseMdFile.exists()) {
1427         throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1428       }
1429       if (!whatsnewMdFile.exists()) {
1430         throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1431       }
1432     }
1433
1434     def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1435     def releaseFilesDates = releaseFiles.collectEntries {
1436       [(it): getMdDate(it)]
1437     }
1438     releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1439
1440     def releasesTemplate = releasesTemplateFile.text
1441     def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1442     def versionTemplate = m[0][1]
1443
1444     MutableDataSet options = new MutableDataSet()
1445
1446     def extensions = new ArrayList<>()
1447     options.set(Parser.EXTENSIONS, extensions)
1448     options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1449
1450     Parser parser = Parser.builder(options).build()
1451     HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1452
1453     def actualVersions = releaseFiles.collect { rf ->
1454       def (rfMap, rfContent) = mdFileComponents(rf)
1455       return rfMap.version
1456     }
1457     def versionsHtml = ""
1458     def linkedVersions = []
1459     releaseFiles.reverse().each { rFile ->
1460       def (rMap, rContent) = mdFileComponents(rFile)
1461
1462       def versionLink = ""
1463       def partialVersion = ""
1464       def firstPart = true
1465       rMap.version.split("\\.").each { part ->
1466         def displayPart = ( firstPart ? "" : "." ) + part
1467         partialVersion += displayPart
1468         if (
1469             linkedVersions.contains(partialVersion)
1470             || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1471             ) {
1472           versionLink += displayPart
1473         } else {
1474           versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1475           linkedVersions += partialVersion
1476         }
1477         firstPart = false
1478       }
1479       def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1480
1481         def lm = null
1482         def rContentProcessed = ""
1483         rContent.eachLine { line ->
1484           if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1485             line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1486         } else if (lm = line =~ /^###([^#]+.*)$/) {
1487             line = "_${lm[0][1].trim()}_"
1488           }
1489           rContentProcessed += line + "\n"
1490         }
1491
1492       def rContentSections = getMdSections(rContentProcessed)
1493       def rVersion = versionTemplate
1494       if (rVersion != "") {
1495         def rNewFeatures = rContentSections["new_features"]
1496         def rIssuesResolved = rContentSections["issues_resolved"]
1497         Node newFeaturesNode = parser.parse(rNewFeatures)
1498         String newFeaturesHtml = renderer.render(newFeaturesNode)
1499         Node issuesResolvedNode = parser.parse(rIssuesResolved)
1500         String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1501         rVersion = hugoTemplateSubstitutions(rVersion,
1502           [
1503             VERSION: rMap.version,
1504             VERSION_LINK: versionLink,
1505             DISPLAY_DATE: displayDate,
1506             NEW_FEATURES: newFeaturesHtml,
1507             ISSUES_RESOLVED: issuesResolvedHtml
1508           ]
1509         )
1510         versionsHtml += rVersion
1511       }
1512     }
1513
1514     releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1515     releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1516     releasesHtmlFile.text = releasesTemplate
1517
1518     if (whatsnewMdFile.exists()) {
1519       def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1520       def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1521       Node whatsnewNode = parser.parse(whatsnewMd)
1522       String whatsnewHtml = renderer.render(whatsnewNode)
1523       whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1524       whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1525         [
1526           DISPLAY_DATE: wnDisplayDate
1527         ]
1528       )
1529     }
1530
1531   }
1532
1533   inputs.file(releasesTemplateFile)
1534   inputs.file(whatsnewTemplateFile)
1535   inputs.dir(releasesMdDir)
1536   inputs.dir(whatsnewMdDir)
1537   outputs.file(releasesHtmlFile)
1538   outputs.file(whatsnewHtmlFile)
1539 }
1540
1541
1542 task copyHelp(type: Copy) {
1543   dependsOn releasesTemplates
1544
1545   def inputDir = helpSourceDir
1546   def outputDir = "${helpBuildDir}/${help_dir}"
1547   from(inputDir) {
1548     include('**/*.txt')
1549     include('**/*.md')
1550     include('**/*.html')
1551     include('**/*.hs')
1552     include('**/*.xml')
1553     include('**/*.jhm')
1554     filter(ReplaceTokens,
1555       beginToken: '$$',
1556       endToken: '$$',
1557       tokens: [
1558         'Version-Rel': JALVIEW_VERSION,
1559         'Year-Rel': getDate("yyyy")
1560       ]
1561     )
1562   }
1563   from(inputDir) {
1564     exclude('**/*.txt')
1565     exclude('**/*.md')
1566     exclude('**/*.html')
1567     exclude('**/*.hs')
1568     exclude('**/*.xml')
1569     exclude('**/*.jhm')
1570   }
1571   into outputDir
1572
1573   inputs.dir(inputDir)
1574   outputs.files(helpFile)
1575   outputs.dir(outputDir)
1576 }
1577
1578
1579 task copyResources(type: Copy) {
1580   group = "build"
1581   description = "Copy (and make text substitutions in) the resources dir to the build area"
1582
1583   def inputDir = resourceDir
1584   def outputDir = resourcesBuildDir
1585   from(inputDir) {
1586     include('**/*.txt')
1587     include('**/*.md')
1588     include('**/*.html')
1589     include('**/*.xml')
1590     filter(ReplaceTokens,
1591       beginToken: '$$',
1592       endToken: '$$',
1593       tokens: [
1594         'Version-Rel': JALVIEW_VERSION,
1595         'Year-Rel': getDate("yyyy")
1596       ]
1597     )
1598   }
1599   from(inputDir) {
1600     exclude('**/*.txt')
1601     exclude('**/*.md')
1602     exclude('**/*.html')
1603     exclude('**/*.xml')
1604   }
1605   into outputDir
1606
1607   inputs.dir(inputDir)
1608   outputs.dir(outputDir)
1609 }
1610
1611 task copyChannelResources(type: Copy) {
1612   dependsOn copyResources
1613   group = "build"
1614   description = "Copy the channel resources dir to the build resources area"
1615
1616   def inputDir = "${channelDir}/${resource_dir}"
1617   def outputDir = resourcesBuildDir
1618   from inputDir
1619   into outputDir
1620
1621   inputs.dir(inputDir)
1622   outputs.dir(outputDir)
1623 }
1624
1625 task createBuildProperties(type: WriteProperties) {
1626   dependsOn copyResources
1627   group = "build"
1628   description = "Create the ${buildProperties} file"
1629   
1630   inputs.dir(sourceDir)
1631   inputs.dir(resourcesBuildDir)
1632   outputFile (buildProperties)
1633   // taking time specific comment out to allow better incremental builds
1634   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1635   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1636   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1637   property "VERSION", JALVIEW_VERSION
1638   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1639   if (getdownSetAppBaseProperty) {
1640     property "GETDOWNAPPBASE", getdownAppBase
1641     property "GETDOWNAPPDISTDIR", getdownAppDistDir
1642   }
1643   outputs.file(outputFile)
1644 }
1645
1646
1647 task buildIndices(type: JavaExec) {
1648   dependsOn copyHelp
1649   classpath = sourceSets.main.compileClasspath
1650   main = "com.sun.java.help.search.Indexer"
1651   workingDir = "${helpBuildDir}/${help_dir}"
1652   def argDir = "html"
1653   args = [ argDir ]
1654   inputs.dir("${workingDir}/${argDir}")
1655
1656   outputs.dir("${classesDir}/doc")
1657   outputs.dir("${classesDir}/help")
1658   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1659   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1660   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1661   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1662   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1663   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1664 }
1665
1666 task buildResources {
1667   dependsOn copyResources
1668   dependsOn copyChannelResources
1669   dependsOn createBuildProperties
1670 }
1671
1672 task prepare {
1673   dependsOn buildResources
1674   dependsOn copyDocs
1675   dependsOn copyHelp
1676   dependsOn convertMdFiles
1677   dependsOn buildIndices
1678 }
1679
1680
1681 compileJava.dependsOn prepare
1682 run.dependsOn compileJava
1683 //run.dependsOn prepare
1684
1685
1686 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
1687 test {
1688   dependsOn prepare
1689
1690   if (useClover) {
1691     dependsOn cloverClasses
1692    } else { //?
1693     dependsOn compileJava //?
1694   }
1695
1696   useTestNG() {
1697     includeGroups testng_groups
1698     excludeGroups testng_excluded_groups
1699     preserveOrder true
1700     useDefaultListeners=true
1701   }
1702
1703   maxHeapSize = "1024m"
1704
1705   workingDir = jalviewDir
1706   def testLaf = project.findProperty("test_laf")
1707   if (testLaf != null) {
1708     println("Setting Test LaF to '${testLaf}'")
1709     systemProperty "laf", testLaf
1710   }
1711   def testHiDPIScale = project.findProperty("test_HiDPIScale")
1712   if (testHiDPIScale != null) {
1713     println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1714     systemProperty "sun.java2d.uiScale", testHiDPIScale
1715   }
1716   sourceCompatibility = compile_source_compatibility
1717   targetCompatibility = compile_target_compatibility
1718   jvmArgs += additional_compiler_args
1719
1720   doFirst {
1721     if (useClover) {
1722       println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1723     }
1724   }
1725 }
1726
1727
1728 task compileLinkCheck(type: JavaCompile) {
1729   options.fork = true
1730   classpath = files("${jalviewDir}/${utils_dir}")
1731   destinationDir = file("${jalviewDir}/${utils_dir}")
1732   source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
1733
1734   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1735   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1736   outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
1737   outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
1738 }
1739
1740
1741 task linkCheck(type: JavaExec) {
1742   dependsOn prepare
1743   dependsOn compileLinkCheck
1744
1745   def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
1746   classpath = files("${jalviewDir}/${utils_dir}")
1747   main = "HelpLinksChecker"
1748   workingDir = jalviewDir
1749   args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
1750
1751   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
1752   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1753     outFOS,
1754     System.out)
1755   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1756     outFOS,
1757     System.err)
1758
1759   inputs.dir(helpBuildDir)
1760   outputs.file(helpLinksCheckerOutFile)
1761 }
1762
1763
1764 // import the pubhtmlhelp target
1765 ant.properties.basedir = "${jalviewDir}"
1766 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
1767 ant.importBuild "${utils_dir}/publishHelp.xml"
1768
1769
1770 task cleanPackageDir(type: Delete) {
1771   doFirst {
1772     delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
1773   }
1774 }
1775
1776
1777 jar {
1778   dependsOn prepare
1779   dependsOn linkCheck
1780
1781   manifest {
1782     attributes "Main-Class": main_class,
1783     "Permissions": "all-permissions",
1784     "Application-Name": applicationName,
1785     "Codebase": application_codebase,
1786     "Implementation-Version": JALVIEW_VERSION
1787   }
1788
1789   def outputDir = "${jalviewDir}/${package_dir}"
1790   destinationDirectory = file(outputDir)
1791   archiveFileName = rootProject.name+".jar"
1792   duplicatesStrategy "EXCLUDE"
1793
1794
1795   exclude "cache*/**"
1796   exclude "*.jar"
1797   exclude "*.jar.*"
1798   exclude "**/*.jar"
1799   exclude "**/*.jar.*"
1800
1801   inputs.dir(sourceSets.main.java.outputDir)
1802   sourceSets.main.resources.srcDirs.each{ dir ->
1803     inputs.dir(dir)
1804   }
1805   outputs.file("${outputDir}/${archiveFileName}")
1806 }
1807
1808
1809 task copyJars(type: Copy) {
1810   from fileTree(dir: classesDir, include: "**/*.jar").files
1811   into "${jalviewDir}/${package_dir}"
1812 }
1813
1814
1815 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
1816 task syncJars(type: Sync) {
1817   dependsOn jar
1818   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
1819   into "${jalviewDir}/${package_dir}"
1820   preserve {
1821     include jar.archiveFileName.getOrNull()
1822   }
1823 }
1824
1825
1826 task makeDist {
1827   group = "build"
1828   description = "Put all required libraries in dist"
1829   // order of "cleanPackageDir", "copyJars", "jar" important!
1830   jar.mustRunAfter cleanPackageDir
1831   syncJars.mustRunAfter cleanPackageDir
1832   dependsOn cleanPackageDir
1833   dependsOn syncJars
1834   dependsOn jar
1835   outputs.dir("${jalviewDir}/${package_dir}")
1836 }
1837
1838
1839 task cleanDist {
1840   dependsOn cleanPackageDir
1841   dependsOn cleanTest
1842   dependsOn clean
1843 }
1844
1845
1846 shadowJar {
1847   group = "distribution"
1848   description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
1849   if (buildDist) {
1850     dependsOn makeDist
1851   }
1852   from ("${jalviewDir}/${libDistDir}") {
1853     include("*.jar")
1854   }
1855   manifest {
1856     attributes "Implementation-Version": JALVIEW_VERSION,
1857     "Application-Name": applicationName
1858   }
1859
1860   duplicatesStrategy "INCLUDE"
1861
1862   mainClassName = shadow_jar_main_class
1863   mergeServiceFiles()
1864   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
1865   minimize()
1866 }
1867
1868
1869 task getdownWebsite() {
1870   group = "distribution"
1871   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
1872   if (buildDist) {
1873     dependsOn makeDist
1874   }
1875
1876   def getdownWebsiteResourceFilenames = []
1877   def getdownResourceDir = getdownResourceDir
1878   def getdownResourceFilenames = []
1879
1880   doFirst {
1881     // clean the getdown website and files dir before creating getdown folders
1882     delete getdownAppBaseDir
1883     delete getdownFilesDir
1884
1885     copy {
1886       from buildProperties
1887       rename(file(buildProperties).getName(), getdown_build_properties)
1888       into getdownAppDir
1889     }
1890     getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
1891
1892     copy {
1893       from channelPropsFile
1894       into getdownAppBaseDir
1895     }
1896     getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
1897
1898     // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
1899     def props = project.properties.sort { it.key }
1900     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
1901       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
1902     }
1903     if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
1904       props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
1905     }
1906     if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
1907       props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
1908     }
1909     if (getdownImagesDir != null && file(getdownImagesDir).exists()) {
1910       props.put("getdown_txt_ui.background_image", "${getdownImagesDir}/${getdown_background_image}")
1911       props.put("getdown_txt_ui.instant_background_image", "${getdownImagesDir}/${getdown_instant_background_image}")
1912       props.put("getdown_txt_ui.error_background", "${getdownImagesDir}/${getdown_error_background}")
1913       props.put("getdown_txt_ui.progress_image", "${getdownImagesDir}/${getdown_progress_image}")
1914       props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
1915       props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
1916     }
1917
1918     props.put("getdown_txt_title", jalview_name)
1919     props.put("getdown_txt_ui.name", applicationName)
1920
1921     // start with appbase
1922     getdownTextLines += "appbase = ${getdownAppBase}"
1923     props.each{ prop, val ->
1924       if (prop.startsWith("getdown_txt_") && val != null) {
1925         if (prop.startsWith("getdown_txt_multi_")) {
1926           def key = prop.substring(18)
1927           val.split(",").each{ v ->
1928             def line = "${key} = ${v}"
1929             getdownTextLines += line
1930           }
1931         } else {
1932           // file values rationalised
1933           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1934             def r = null
1935             if (val.indexOf('/') == 0) {
1936               // absolute path
1937               r = file(val)
1938             } else if (val.indexOf('/') > 0) {
1939               // relative path (relative to jalviewDir)
1940               r = file( "${jalviewDir}/${val}" )
1941             }
1942             if (r.exists()) {
1943               val = "${getdown_resource_dir}/" + r.getName()
1944               getdownWebsiteResourceFilenames += val
1945               getdownResourceFilenames += r.getPath()
1946             }
1947           }
1948           if (! prop.startsWith("getdown_txt_resource")) {
1949             def line = prop.substring(12) + " = ${val}"
1950             getdownTextLines += line
1951           }
1952         }
1953       }
1954     }
1955
1956     getdownWebsiteResourceFilenames.each{ filename ->
1957       getdownTextLines += "resource = ${filename}"
1958     }
1959     getdownResourceFilenames.each{ filename ->
1960       copy {
1961         from filename
1962         into getdownResourceDir
1963       }
1964     }
1965     
1966     def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
1967     getdownWrapperScripts.each{ script ->
1968       def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
1969       if (s.exists()) {
1970         copy {
1971           from s
1972           into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
1973         }
1974         getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}"
1975       }
1976     }
1977
1978     def codeFiles = []
1979     fileTree(file(package_dir)).each{ f ->
1980       if (f.isDirectory()) {
1981         def files = fileTree(dir: f, include: ["*"]).getFiles()
1982         codeFiles += files
1983       } else if (f.exists()) {
1984         codeFiles += f
1985       }
1986     }
1987     def jalviewJar = jar.archiveFileName.getOrNull()
1988     // put jalview.jar first for CLASSPATH and .properties files reasons
1989     codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
1990       def name = f.getName()
1991       def line = "code = ${getdownAppDistDir}/${name}"
1992       getdownTextLines += line
1993       copy {
1994         from f.getPath()
1995         into getdownAppDir
1996       }
1997     }
1998
1999     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2000     /*
2001     if (JAVA_VERSION.equals("11")) {
2002     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2003     j11libFiles.sort().each{f ->
2004     def name = f.getName()
2005     def line = "code = ${getdown_j11lib_dir}/${name}"
2006     getdownTextLines += line
2007     copy {
2008     from f.getPath()
2009     into getdownJ11libDir
2010     }
2011     }
2012     }
2013      */
2014
2015     // 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.
2016     //getdownTextLines += "class = " + file(getdownLauncher).getName()
2017     getdownTextLines += "resource = ${getdown_launcher_new}"
2018     getdownTextLines += "class = ${main_class}"
2019     // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2020     if (getdownSetAppBaseProperty) {
2021       getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2022       getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2023     }
2024
2025     def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2026     getdownTxt.write(getdownTextLines.join("\n"))
2027
2028     getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2029     def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2030     launchJvl.write("appbase=${getdownAppBase}")
2031
2032     // files going into the getdown website dir: getdown-launcher.jar
2033     copy {
2034       from getdownLauncher
2035       rename(file(getdownLauncher).getName(), getdown_launcher_new)
2036       into getdownAppBaseDir
2037     }
2038
2039     // files going into the getdown website dir: getdown-launcher(-local).jar
2040     copy {
2041       from getdownLauncher
2042       if (file(getdownLauncher).getName() != getdown_launcher) {
2043         rename(file(getdownLauncher).getName(), getdown_launcher)
2044       }
2045       into getdownAppBaseDir
2046     }
2047
2048     // files going into the getdown website dir: ./install dir and files
2049     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2050       copy {
2051         from getdownTxt
2052         from getdownLauncher
2053         from "${getdownAppDir}/${getdown_build_properties}"
2054         if (file(getdownLauncher).getName() != getdown_launcher) {
2055           rename(file(getdownLauncher).getName(), getdown_launcher)
2056         }
2057         into getdownInstallDir
2058       }
2059
2060       // and make a copy in the getdown files dir (these are not downloaded by getdown)
2061       copy {
2062         from getdownInstallDir
2063         into getdownFilesInstallDir
2064       }
2065     }
2066
2067     // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2068     copy {
2069       from getdownTxt
2070       from launchJvl
2071       from getdownLauncher
2072       from "${getdownAppBaseDir}/${getdown_build_properties}"
2073       from "${getdownAppBaseDir}/${channel_props}"
2074       if (file(getdownLauncher).getName() != getdown_launcher) {
2075         rename(file(getdownLauncher).getName(), getdown_launcher)
2076       }
2077       into getdownFilesDir
2078     }
2079
2080     // and ./resource (not all downloaded by getdown)
2081     copy {
2082       from getdownResourceDir
2083       into "${getdownFilesDir}/${getdown_resource_dir}"
2084     }
2085   }
2086
2087   if (buildDist) {
2088     inputs.dir("${jalviewDir}/${package_dir}")
2089   }
2090   outputs.dir(getdownAppBaseDir)
2091   outputs.dir(getdownFilesDir)
2092 }
2093
2094
2095 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2096 task getdownDigestDir(type: JavaExec) {
2097   group "Help"
2098   description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2099
2100   def digestDirPropertyName = "DIGESTDIR"
2101   doFirst {
2102     classpath = files(getdownLauncher)
2103     def digestDir = findProperty(digestDirPropertyName)
2104     if (digestDir == null) {
2105       throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2106     }
2107     args digestDir
2108   }
2109   main = "com.threerings.getdown.tools.Digester"
2110 }
2111
2112
2113 task getdownDigest(type: JavaExec) {
2114   group = "distribution"
2115   description = "Digest the getdown website folder"
2116   dependsOn getdownWebsite
2117   doFirst {
2118     classpath = files(getdownLauncher)
2119   }
2120   main = "com.threerings.getdown.tools.Digester"
2121   args getdownAppBaseDir
2122   inputs.dir(getdownAppBaseDir)
2123   outputs.file("${getdownAppBaseDir}/digest2.txt")
2124 }
2125
2126
2127 task getdown() {
2128   group = "distribution"
2129   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2130   dependsOn getdownDigest
2131   doLast {
2132     if (reportRsyncCommand) {
2133       def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2134       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2135       println "LIKELY RSYNC COMMAND:"
2136       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2137       if (RUNRSYNC == "true") {
2138         exec {
2139           commandLine "mkdir", "-p", toDir
2140         }
2141         exec {
2142           commandLine "rsync", "-avh", "--delete", fromDir, toDir
2143         }
2144       }
2145     }
2146   }
2147 }
2148
2149
2150 task getdownArchiveBuild() {
2151   group = "distribution"
2152   description = "Put files in the archive dir to go on the website"
2153
2154   dependsOn getdownWebsite
2155
2156   def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2157   def vDir = "${getdownArchiveDir}/${v}"
2158   getdownFullArchiveDir = "${vDir}/getdown"
2159   getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2160
2161   def vAltDir = "alt_${v}"
2162   def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2163
2164   doFirst {
2165     // cleanup old "old" dir
2166     delete getdownArchiveDir
2167
2168     def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2169     getdownArchiveTxt.getParentFile().mkdirs()
2170     def getdownArchiveTextLines = []
2171     def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2172
2173     // the libdir
2174     copy {
2175       from "${getdownAppBaseDir}/${getdownAppDistDir}"
2176       into "${getdownFullArchiveDir}/${vAltDir}"
2177     }
2178
2179     getdownTextLines.each { line ->
2180       line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2181       line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2182       line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2183       line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2184       line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2185       line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2186       // remove the existing resource = resource/ or bin/ lines
2187       if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2188         getdownArchiveTextLines += line
2189       }
2190     }
2191
2192     // the resource dir -- add these files as resource lines in getdown.txt
2193     copy {
2194       from "${archiveImagesDir}"
2195       into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2196       eachFile { file ->
2197         getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2198       }
2199     }
2200
2201     getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2202
2203     def vLaunchJvl = file(getdownVersionLaunchJvl)
2204     vLaunchJvl.getParentFile().mkdirs()
2205     vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2206     def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2207     def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2208     // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2209     //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2210     java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2211
2212     // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2213     copy {
2214       from getdownLauncher
2215       from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2216       from "${getdownAppBaseDir}/${getdown_launcher_new}"
2217       from "${getdownAppBaseDir}/${channel_props}"
2218       if (file(getdownLauncher).getName() != getdown_launcher) {
2219         rename(file(getdownLauncher).getName(), getdown_launcher)
2220       }
2221       into getdownFullArchiveDir
2222     }
2223
2224   }
2225 }
2226
2227 task getdownArchiveDigest(type: JavaExec) {
2228   group = "distribution"
2229   description = "Digest the getdown archive folder"
2230
2231   dependsOn getdownArchiveBuild
2232
2233   doFirst {
2234     classpath = files(getdownLauncher)
2235     args getdownFullArchiveDir
2236   }
2237   main = "com.threerings.getdown.tools.Digester"
2238   inputs.dir(getdownFullArchiveDir)
2239   outputs.file("${getdownFullArchiveDir}/digest2.txt")
2240 }
2241
2242 task getdownArchive() {
2243   group = "distribution"
2244   description = "Build the website archive dir with getdown digest"
2245
2246   dependsOn getdownArchiveBuild
2247   dependsOn getdownArchiveDigest
2248 }
2249
2250 tasks.withType(JavaCompile) {
2251         options.encoding = 'UTF-8'
2252 }
2253
2254
2255 clean {
2256   doFirst {
2257     delete getdownAppBaseDir
2258     delete getdownFilesDir
2259     delete getdownArchiveDir
2260   }
2261 }
2262
2263
2264 install4j {
2265   if (file(install4jHomeDir).exists()) {
2266     // good to go!
2267   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2268     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2269   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2270     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2271   }
2272   installDir(file(install4jHomeDir))
2273
2274   mediaTypes = Arrays.asList(install4j_media_types.split(","))
2275 }
2276
2277
2278 task copyInstall4jTemplate {
2279   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2280   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2281   inputs.file(install4jTemplateFile)
2282   inputs.file(install4jFileAssociationsFile)
2283   inputs.property("CHANNEL", { CHANNEL })
2284   outputs.file(install4jConfFile)
2285
2286   doLast {
2287     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2288
2289     // turn off code signing if no OSX_KEYPASS
2290     if (OSX_KEYPASS == "") {
2291       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2292         codeSigning.'@macEnabled' = "false"
2293       }
2294       install4jConfigXml.'**'.windows.each { windows ->
2295         windows.'@runPostProcessor' = "false"
2296       }
2297     }
2298
2299     // disable install screen for OSX dmg (for 2.11.2.0)
2300     install4jConfigXml.'**'.macosArchive.each { macosArchive -> 
2301       macosArchive.attributes().remove('executeSetupApp')
2302       macosArchive.attributes().remove('setupAppId')
2303     }
2304
2305     // turn off checksum creation for LOCAL channel
2306     def e = install4jConfigXml.application[0]
2307     e.'@createChecksums' = string(install4jCheckSums)
2308
2309     // put file association actions where placeholder action is
2310     def install4jFileAssociationsText = install4jFileAssociationsFile.text
2311     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2312     install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2313       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2314         def parent = a.parent()
2315         parent.remove(a)
2316         fileAssociationActions.each { faa ->
2317             parent.append(faa)
2318         }
2319         // don't need to continue in .any loop once replacements have been made
2320         return true
2321       }
2322     }
2323
2324     // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2325     // NB we're deleting the /other/ one!
2326     // Also remove the examples subdir from non-release versions
2327     def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2328     // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2329     if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2330       customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2331     } else {
2332       // remove the examples subdir from Full File Set
2333       def files = install4jConfigXml.files[0]
2334       def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2335       def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2336       def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2337       def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2338       dirEntry.parent().remove(dirEntry)
2339     }
2340     install4jConfigXml.'**'.action.any { a ->
2341       if (a.'@customizedId' == customizedIdToDelete) {
2342         def parent = a.parent()
2343         parent.remove(a)
2344         return true
2345       }
2346     }
2347
2348     // write install4j file
2349     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2350   }
2351 }
2352
2353
2354 clean {
2355   doFirst {
2356     delete install4jConfFile
2357   }
2358 }
2359
2360 task cleanInstallersDataFiles {
2361   def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2362   def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2363   def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2364   doFirst {
2365     delete installersOutputTxt
2366     delete installersSha256
2367     delete hugoDataJsonFile
2368   }
2369 }
2370
2371 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2372   group = "distribution"
2373   description = "Create the install4j installers"
2374   dependsOn getdown
2375   dependsOn copyInstall4jTemplate
2376   dependsOn cleanInstallersDataFiles
2377
2378   projectFile = install4jConfFile
2379
2380   // create an md5 for the input files to use as version for install4j conf file
2381   def digest = MessageDigest.getInstance("MD5")
2382   digest.update(
2383     (file("${install4jDir}/${install4j_template}").text + 
2384     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2385     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2386   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2387   if (filesMd5.length() >= 8) {
2388     filesMd5 = filesMd5.substring(0,8)
2389   }
2390   def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2391
2392   variables = [
2393     'JALVIEW_NAME': jalview_name,
2394     'JALVIEW_APPLICATION_NAME': applicationName,
2395     'JALVIEW_DIR': "../..",
2396     'OSX_KEYSTORE': OSX_KEYSTORE,
2397     'OSX_APPLEID': OSX_APPLEID,
2398     'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2399     'JSIGN_SH': JSIGN_SH,
2400     'JRE_DIR': getdown_app_dir_java,
2401     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2402     'JALVIEW_VERSION': JALVIEW_VERSION,
2403     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2404     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2405     'JAVA_VERSION': JAVA_VERSION,
2406     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2407     'VERSION': JALVIEW_VERSION,
2408     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
2409     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
2410     'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
2411     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
2412     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
2413     'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
2414     'COPYRIGHT_MESSAGE': install4j_copyright_message,
2415     'BUNDLE_ID': install4jBundleId,
2416     'INTERNAL_ID': install4jInternalId,
2417     'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2418     'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2419     'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
2420     'WRAPPER_LINK': getdownWrapperLink,
2421     'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2422     'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2423     'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2424     'INSTALLER_NAME': install4jInstallerName,
2425     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2426     'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2427     'GETDOWN_FILES_DIR': getdown_files_dir,
2428     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2429     'GETDOWN_DIST_DIR': getdownAppDistDir,
2430     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2431     'GETDOWN_INSTALL_DIR': getdown_install_dir,
2432     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2433     'BUILD_DIR': install4jBuildDir,
2434     'APPLICATION_CATEGORIES': install4j_application_categories,
2435     'APPLICATION_FOLDER': install4jApplicationFolder,
2436     'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2437     'EXECUTABLE_NAME': install4jExecutableName,
2438     'EXTRA_SCHEME': install4jExtraScheme,
2439     'MAC_ICONS_FILE': install4jMacIconsFile,
2440     'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2441     'PNG_ICON_FILE': install4jPngIconFile,
2442     'BACKGROUND': install4jBackground,
2443
2444   ]
2445
2446   //println("INSTALL4J VARIABLES:")
2447   //variables.each{k,v->println("${k}=${v}")}
2448
2449   destination = "${jalviewDir}/${install4jBuildDir}"
2450   buildSelected = true
2451
2452   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2453     faster = true
2454     disableSigning = true
2455     disableNotarization = true
2456   }
2457
2458   if (OSX_KEYPASS) {
2459     macKeystorePassword = OSX_KEYPASS
2460   } 
2461   
2462   if (OSX_ALTOOLPASS) {
2463     appleIdPassword = OSX_ALTOOLPASS
2464     disableNotarization = false
2465   } else {
2466     disableNotarization = true
2467   }
2468
2469   doFirst {
2470     println("Using projectFile "+projectFile)
2471     if (!disableNotarization) { println("Will notarize OSX App DMG") }
2472   }
2473   //verbose=true
2474
2475   inputs.dir(getdownAppBaseDir)
2476   inputs.file(install4jConfFile)
2477   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2478   inputs.dir(macosJavaVMDir)
2479   inputs.dir(windowsJavaVMDir)
2480   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2481 }
2482
2483 def getDataHash(File myFile) {
2484   HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2485   return myFile.exists()
2486   ? [
2487       "file" : myFile.getName(),
2488       "filesize" : myFile.length(),
2489       "sha256" : hash.toString()
2490     ]
2491   : null
2492 }
2493
2494 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2495   def hash = [
2496     "channel" : getdownChannelName,
2497     "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2498     "git-commit" : "${gitHash} [${gitBranch}]",
2499     "version" : JALVIEW_VERSION
2500   ]
2501   // install4j installer files
2502   if (installersOutputTxt.exists()) {
2503     def idHash = [:]
2504     installersOutputTxt.readLines().each { def line ->
2505       if (line.startsWith("#")) {
2506         return;
2507       }
2508       line.replaceAll("\n","")
2509       def vals = line.split("\t")
2510       def filename = vals[3]
2511       def filesize = file(filename).length()
2512       filename = filename.replaceAll(/^.*\//, "")
2513       hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2514       idHash."${filename}" = vals[0]
2515     }
2516     if (install4jCheckSums && installersSha256.exists()) {
2517       installersSha256.readLines().each { def line ->
2518         if (line.startsWith("#")) {
2519           return;
2520         }
2521         line.replaceAll("\n","")
2522         def vals = line.split(/\s+\*?/)
2523         def filename = vals[1]
2524         def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
2525       }
2526     }
2527   }
2528
2529   [
2530     "JAR": shadowJar.archiveFile, // executable JAR
2531     "JVL": getdownVersionLaunchJvl, // version JVL
2532     "SOURCE": sourceDist.archiveFile // source TGZ
2533   ].each { key, value ->
2534     def file = file(value)
2535     if (file.exists()) {
2536       def fileHash = getDataHash(file)
2537       if (fileHash != null) {
2538         hash."${key}" = fileHash;
2539       }
2540     }
2541   }
2542   return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
2543 }
2544
2545 task staticMakeInstallersJsonFile {
2546   doFirst {
2547     def output = findProperty("i4j_output")
2548     def sha256 = findProperty("i4j_sha256")
2549     def json = findProperty("i4j_json")
2550     if (output == null || sha256 == null || json == null) {
2551       throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
2552     }
2553     writeDataJsonFile(file(output), file(sha256), file(json))
2554   }
2555 }
2556
2557 task installers {
2558   dependsOn installerFiles
2559 }
2560
2561
2562 spotless {
2563   java {
2564     eclipse().configFile(eclipse_codestyle_file)
2565   }
2566 }
2567
2568 task createSourceReleaseProperties(type: WriteProperties) {
2569   group = "distribution"
2570   description = "Create the source RELEASE properties file"
2571   
2572   def sourceTarBuildDir = "${buildDir}/sourceTar"
2573   def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
2574   outputFile (sourceReleasePropertiesFile)
2575
2576   doFirst {
2577     releaseProps.each{ key, val -> property key, val }
2578     property "git.branch", gitBranch
2579     property "git.hash", gitHash
2580   }
2581
2582   outputs.file(outputFile)
2583 }
2584
2585 task sourceDist(type: Tar) {
2586   group "distribution"
2587   description "Create a source .tar.gz file for distribution"
2588
2589   dependsOn createBuildProperties
2590   dependsOn convertMdFiles
2591   dependsOn eclipseAllPreferences
2592   dependsOn createSourceReleaseProperties
2593
2594
2595   def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
2596   archiveFileName = outputFileName
2597   
2598   compression Compression.GZIP
2599   
2600   into project.name
2601
2602   def EXCLUDE_FILES=[
2603     "build/*",
2604     "bin/*",
2605     "test-output/",
2606     "test-reports",
2607     "tests",
2608     "clover*/*",
2609     ".*",
2610     "benchmarking/*",
2611     "**/.*",
2612     "*.class",
2613     "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
2614     "*locales/**",
2615     "utils/InstallAnywhere",
2616     "**/*.log",
2617     "RELEASE",
2618   ] 
2619   def PROCESS_FILES=[
2620     "AUTHORS",
2621     "CITATION",
2622     "FEATURETODO",
2623     "JAVA-11-README",
2624     "FEATURETODO",
2625     "LICENSE",
2626     "**/README",
2627     "THIRDPARTYLIBS",
2628     "TESTNG",
2629     "build.gradle",
2630     "gradle.properties",
2631     "**/*.java",
2632     "**/*.html",
2633     "**/*.xml",
2634     "**/*.gradle",
2635     "**/*.groovy",
2636     "**/*.properties",
2637     "**/*.perl",
2638     "**/*.sh",
2639   ]
2640   def INCLUDE_FILES=[
2641     ".classpath",
2642     ".settings/org.eclipse.buildship.core.prefs",
2643     ".settings/org.eclipse.jdt.core.prefs"
2644   ]
2645
2646   from(jalviewDir) {
2647     exclude (EXCLUDE_FILES)
2648     include (PROCESS_FILES)
2649     filter(ReplaceTokens,
2650       beginToken: '$$',
2651       endToken: '$$',
2652       tokens: [
2653         'Version-Rel': JALVIEW_VERSION,
2654         'Year-Rel': getDate("yyyy")
2655       ]
2656     )
2657   }
2658   from(jalviewDir) {
2659     exclude (EXCLUDE_FILES)
2660     exclude (PROCESS_FILES)
2661     exclude ("appletlib")
2662     exclude ("**/*locales")
2663     exclude ("*locales/**")
2664     exclude ("utils/InstallAnywhere")
2665
2666     exclude (getdown_files_dir)
2667     // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
2668     //exclude (getdown_website_dir)
2669     //exclude (getdown_archive_dir)
2670
2671     // exluding these as not using jars as modules yet
2672     exclude ("${j11modDir}/**/*.jar")
2673   }
2674   from(jalviewDir) {
2675     include(INCLUDE_FILES)
2676   }
2677 //  from (jalviewDir) {
2678 //    // explicit includes for stuff that seemed to not get included
2679 //    include(fileTree("test/**/*."))
2680 //    exclude(EXCLUDE_FILES)
2681 //    exclude(PROCESS_FILES)
2682 //  }
2683
2684   from(file(buildProperties).getParent()) {
2685     include(file(buildProperties).getName())
2686     rename(file(buildProperties).getName(), "build_properties")
2687     filter({ line ->
2688       line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
2689     })
2690   }
2691
2692   def sourceTarBuildDir = "${buildDir}/sourceTar"
2693   from(sourceTarBuildDir) {
2694     // this includes the appended RELEASE properties file
2695   }
2696 }
2697
2698 task dataInstallersJson {
2699   group "website"
2700   description "Create the installers-VERSION.json data file for installer files created"
2701
2702   mustRunAfter installers
2703   mustRunAfter shadowJar
2704   mustRunAfter sourceDist
2705   mustRunAfter getdownArchive
2706
2707   def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2708   def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2709
2710   if (installersOutputTxt.exists()) {
2711     inputs.file(installersOutputTxt)
2712   }
2713   if (install4jCheckSums && installersSha256.exists()) {
2714     inputs.file(installersSha256)
2715   }
2716   [
2717     shadowJar.archiveFile, // executable JAR
2718     getdownVersionLaunchJvl, // version JVL
2719     sourceDist.archiveFile // source TGZ
2720   ].each { fileName ->
2721     if (file(fileName).exists()) {
2722       inputs.file(fileName)
2723     }
2724   }
2725
2726   outputs.file(hugoDataJsonFile)
2727
2728   doFirst {
2729     writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
2730   }
2731 }
2732
2733 task helppages {
2734   group "help"
2735   description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
2736
2737   dependsOn copyHelp
2738   dependsOn pubhtmlhelp
2739   
2740   inputs.dir("${helpBuildDir}/${help_dir}")
2741   outputs.dir("${buildDir}/distributions/${help_dir}")
2742 }
2743
2744
2745 task j2sSetHeadlessBuild {
2746   doFirst {
2747     IN_ECLIPSE = false
2748   }
2749 }
2750
2751
2752 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
2753   group "jalviewjs"
2754   description "Enable the alternative J2S Config file for headless build"
2755
2756   outputFile = jalviewjsJ2sSettingsFileName
2757   def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
2758   def j2sProps = new Properties()
2759   if (j2sPropsFile.exists()) {
2760     try {
2761       def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
2762       j2sProps.load(j2sPropsFileFIS)
2763       j2sPropsFileFIS.close()
2764
2765       j2sProps.each { prop, val ->
2766         property(prop, val)
2767       }
2768     } catch (Exception e) {
2769       println("Exception reading ${jalviewjsJ2sSettingsFileName}")
2770       e.printStackTrace()
2771     }
2772   }
2773   if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
2774     property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
2775   }
2776 }
2777
2778
2779 task jalviewjsSetEclipseWorkspace {
2780   def propKey = "jalviewjs_eclipse_workspace"
2781   def propVal = null
2782   if (project.hasProperty(propKey)) {
2783     propVal = project.getProperty(propKey)
2784     if (propVal.startsWith("~/")) {
2785       propVal = System.getProperty("user.home") + propVal.substring(1)
2786     }
2787   }
2788   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
2789   def propsFile = file(propsFileName)
2790   def eclipseWsDir = propVal
2791   def props = new Properties()
2792
2793   def writeProps = true
2794   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
2795     def ins = new FileInputStream(propsFileName)
2796     props.load(ins)
2797     ins.close()
2798     if (props.getProperty(propKey, null) != null) {
2799       eclipseWsDir = props.getProperty(propKey)
2800       writeProps = false
2801     }
2802   }
2803
2804   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
2805     def tempDir = File.createTempDir()
2806     eclipseWsDir = tempDir.getAbsolutePath()
2807     writeProps = true
2808   }
2809   eclipseWorkspace = file(eclipseWsDir)
2810
2811   doFirst {
2812     // do not run a headless transpile when we claim to be in Eclipse
2813     if (IN_ECLIPSE) {
2814       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2815       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2816     } else {
2817       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2818     }
2819
2820     if (writeProps) {
2821       props.setProperty(propKey, eclipseWsDir)
2822       propsFile.parentFile.mkdirs()
2823       def bytes = new ByteArrayOutputStream()
2824       props.store(bytes, null)
2825       def propertiesString = bytes.toString()
2826       propsFile.text = propertiesString
2827       print("NEW ")
2828     } else {
2829       print("EXISTING ")
2830     }
2831
2832     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
2833   }
2834
2835   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
2836   outputs.file(propsFileName)
2837   outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
2838 }
2839
2840
2841 task jalviewjsEclipsePaths {
2842   def eclipseProduct
2843
2844   def eclipseRoot = jalviewjs_eclipse_root
2845   if (eclipseRoot.startsWith("~/")) {
2846     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
2847   }
2848   if (OperatingSystem.current().isMacOsX()) {
2849     eclipseRoot += "/Eclipse.app"
2850     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
2851     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
2852   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
2853     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
2854       eclipseRoot += "/eclipse"
2855     }
2856     eclipseBinary = "${eclipseRoot}/eclipse.exe"
2857     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
2858   } else { // linux or unix
2859     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
2860       eclipseRoot += "/eclipse"
2861 println("eclipseDir exists")
2862     }
2863     eclipseBinary = "${eclipseRoot}/eclipse"
2864     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
2865   }
2866
2867   eclipseVersion = "4.13" // default
2868   def assumedVersion = true
2869   if (file(eclipseProduct).exists()) {
2870     def fis = new FileInputStream(eclipseProduct)
2871     def props = new Properties()
2872     props.load(fis)
2873     eclipseVersion = props.getProperty("version")
2874     fis.close()
2875     assumedVersion = false
2876   }
2877   
2878   def propKey = "eclipse_debug"
2879   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
2880
2881   doFirst {
2882     // do not run a headless transpile when we claim to be in Eclipse
2883     if (IN_ECLIPSE) {
2884       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2885       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2886     } else {
2887       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2888     }
2889
2890     if (!assumedVersion) {
2891       println("ECLIPSE VERSION=${eclipseVersion}")
2892     }
2893   }
2894 }
2895
2896
2897 task printProperties {
2898   group "Debug"
2899   description "Output to console all System.properties"
2900   doFirst {
2901     System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
2902   }
2903 }
2904
2905
2906 task eclipseSetup {
2907   dependsOn eclipseProject
2908   dependsOn eclipseClasspath
2909   dependsOn eclipseJdt
2910 }
2911
2912
2913 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
2914 task jalviewjsEclipseCopyDropins(type: Copy) {
2915   dependsOn jalviewjsEclipsePaths
2916
2917   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
2918   inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
2919   def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
2920
2921   from inputFiles
2922   into outputDir
2923 }
2924
2925
2926 // this eclipse -clean doesn't actually work
2927 task jalviewjsCleanEclipse(type: Exec) {
2928   dependsOn eclipseSetup
2929   dependsOn jalviewjsEclipsePaths
2930   dependsOn jalviewjsEclipseCopyDropins
2931
2932   executable(eclipseBinary)
2933   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
2934   if (eclipseDebug) {
2935     args += "-debug"
2936   }
2937   args += "-l"
2938
2939   def inputString = """exit
2940 y
2941 """
2942   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
2943   standardInput = inputByteStream
2944 }
2945
2946 /* not really working yet
2947 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
2948 */
2949
2950
2951 task jalviewjsTransferUnzipSwingJs {
2952   def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
2953
2954   doLast {
2955     copy {
2956       from zipTree(file_zip)
2957       into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2958     }
2959   }
2960
2961   inputs.file file_zip
2962   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2963 }
2964
2965
2966 task jalviewjsTransferUnzipLib {
2967   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
2968
2969   doLast {
2970     zipFiles.each { file_zip -> 
2971       copy {
2972         from zipTree(file_zip)
2973         into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2974       }
2975     }
2976   }
2977
2978   inputs.files zipFiles
2979   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2980 }
2981
2982
2983 task jalviewjsTransferUnzipAllLibs {
2984   dependsOn jalviewjsTransferUnzipSwingJs
2985   dependsOn jalviewjsTransferUnzipLib
2986 }
2987
2988
2989 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
2990   group "JalviewJS"
2991   description "Create the alternative j2s file from the j2s.* properties"
2992
2993   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
2994   def siteDirProperty = "j2s.site.directory"
2995   def setSiteDir = false
2996   jalviewjsJ2sProps.each { prop, val ->
2997     if (val != null) {
2998       if (prop == siteDirProperty) {
2999         if (!(val.startsWith('/') || val.startsWith("file://") )) {
3000           val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3001         }
3002         setSiteDir = true
3003       }
3004       property(prop,val)
3005     }
3006     if (!setSiteDir) { // default site location, don't override specifically set property
3007       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3008     }
3009   }
3010   outputFile = jalviewjsJ2sAltSettingsFileName
3011
3012   if (! IN_ECLIPSE) {
3013     inputs.properties(jalviewjsJ2sProps)
3014     outputs.file(jalviewjsJ2sAltSettingsFileName)
3015   }
3016 }
3017
3018
3019 task jalviewjsEclipseSetup {
3020   dependsOn jalviewjsEclipseCopyDropins
3021   dependsOn jalviewjsSetEclipseWorkspace
3022   dependsOn jalviewjsCreateJ2sSettings
3023 }
3024
3025
3026 task jalviewjsSyncAllLibs (type: Sync) {
3027   dependsOn jalviewjsTransferUnzipAllLibs
3028   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3029   inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3030   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3031
3032   from inputFiles
3033   into outputDir
3034   def outputFiles = []
3035   rename { filename ->
3036     outputFiles += "${outputDir}/${filename}"
3037     null
3038   }
3039   preserve {
3040     include "**"
3041   }
3042
3043   // should this be exclude really ?
3044   duplicatesStrategy "INCLUDE"
3045
3046   outputs.files outputFiles
3047   inputs.files inputFiles
3048 }
3049
3050
3051 task jalviewjsSyncResources (type: Sync) {
3052   dependsOn buildResources
3053
3054   def inputFiles = fileTree(dir: resourcesBuildDir)
3055   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3056
3057   from inputFiles
3058   into outputDir
3059   def outputFiles = []
3060   rename { filename ->
3061     outputFiles += "${outputDir}/${filename}"
3062     null
3063   }
3064   preserve {
3065     include "**"
3066   }
3067   outputs.files outputFiles
3068   inputs.files inputFiles
3069 }
3070
3071
3072 task jalviewjsSyncSiteResources (type: Sync) {
3073   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3074   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3075
3076   from inputFiles
3077   into outputDir
3078   def outputFiles = []
3079   rename { filename ->
3080     outputFiles += "${outputDir}/${filename}"
3081     null
3082   }
3083   preserve {
3084     include "**"
3085   }
3086   outputs.files outputFiles
3087   inputs.files inputFiles
3088 }
3089
3090
3091 task jalviewjsSyncBuildProperties (type: Sync) {
3092   dependsOn createBuildProperties
3093   def inputFiles = [file(buildProperties)]
3094   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3095
3096   from inputFiles
3097   into outputDir
3098   def outputFiles = []
3099   rename { filename ->
3100     outputFiles += "${outputDir}/${filename}"
3101     null
3102   }
3103   preserve {
3104     include "**"
3105   }
3106   outputs.files outputFiles
3107   inputs.files inputFiles
3108 }
3109
3110
3111 task jalviewjsProjectImport(type: Exec) {
3112   dependsOn eclipseSetup
3113   dependsOn jalviewjsEclipsePaths
3114   dependsOn jalviewjsEclipseSetup
3115
3116   doFirst {
3117     // do not run a headless import when we claim to be in Eclipse
3118     if (IN_ECLIPSE) {
3119       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3120       throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3121     } else {
3122       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3123     }
3124   }
3125
3126   //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3127   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3128   executable(eclipseBinary)
3129   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3130   if (eclipseDebug) {
3131     args += "-debug"
3132   }
3133   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3134   if (!IN_ECLIPSE) {
3135     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3136     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3137   }
3138
3139   inputs.file("${jalviewDir}/.project")
3140   outputs.upToDateWhen { 
3141     file(projdir).exists()
3142   }
3143 }
3144
3145
3146 task jalviewjsTranspile(type: Exec) {
3147   dependsOn jalviewjsEclipseSetup 
3148   dependsOn jalviewjsProjectImport
3149   dependsOn jalviewjsEclipsePaths
3150   if (!IN_ECLIPSE) {
3151     dependsOn jalviewjsEnableAltFileProperty
3152   }
3153
3154   doFirst {
3155     // do not run a headless transpile when we claim to be in Eclipse
3156     if (IN_ECLIPSE) {
3157       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3158       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3159     } else {
3160       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3161     }
3162   }
3163
3164   executable(eclipseBinary)
3165   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3166   if (eclipseDebug) {
3167     args += "-debug"
3168   }
3169   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3170   if (!IN_ECLIPSE) {
3171     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3172     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3173   }
3174
3175   def stdout
3176   def stderr
3177   doFirst {
3178     stdout = new ByteArrayOutputStream()
3179     stderr = new ByteArrayOutputStream()
3180
3181     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3182     def logOutFile = file(logOutFileName)
3183     logOutFile.createNewFile()
3184     logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3185 BINARY: ${eclipseBinary}
3186 VERSION: ${eclipseVersion}
3187 WORKSPACE: ${eclipseWorkspace}
3188 DEBUG: ${eclipseDebug}
3189 ----
3190 """
3191     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3192     // combine stdout and stderr
3193     def logErrFOS = logOutFOS
3194
3195     if (jalviewjs_j2s_to_console.equals("true")) {
3196       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3197         new org.apache.tools.ant.util.TeeOutputStream(
3198           logOutFOS,
3199           stdout),
3200         System.out)
3201       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3202         new org.apache.tools.ant.util.TeeOutputStream(
3203           logErrFOS,
3204           stderr),
3205         System.err)
3206     } else {
3207       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3208         logOutFOS,
3209         stdout)
3210       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3211         logErrFOS,
3212         stderr)
3213     }
3214   }
3215
3216   doLast {
3217     if (stdout.toString().contains("Error processing ")) {
3218       // j2s did not complete transpile
3219       //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3220       if (jalviewjs_ignore_transpile_errors.equals("true")) {
3221         println("IGNORING TRANSPILE ERRORS")
3222         println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3223       } else {
3224         throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3225       }
3226     }
3227   }
3228
3229   inputs.dir("${jalviewDir}/${sourceDir}")
3230   outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3231   outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3232 }
3233
3234
3235 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3236
3237   def stdout = new ByteArrayOutputStream()
3238   def stderr = new ByteArrayOutputStream()
3239
3240   def coreFile = file(jsfile)
3241   def msg = ""
3242   msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3243   println(msg)
3244   logOutFile.createNewFile()
3245   logOutFile.append(msg+"\n")
3246
3247   def coreTop = file(prefixFile)
3248   def coreBottom = file(suffixFile)
3249   coreFile.getParentFile().mkdirs()
3250   coreFile.createNewFile()
3251   coreFile.write( coreTop.getText("UTF-8") )
3252   list.each {
3253     f ->
3254     if (f.exists()) {
3255       def t = f.getText("UTF-8")
3256       t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3257       coreFile.append( t )
3258     } else {
3259       msg = "...file '"+f.getPath()+"' does not exist, skipping"
3260       println(msg)
3261       logOutFile.append(msg+"\n")
3262     }
3263   }
3264   coreFile.append( coreBottom.getText("UTF-8") )
3265
3266   msg = "Generating ${zjsfile}"
3267   println(msg)
3268   logOutFile.append(msg+"\n")
3269   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3270   def logErrFOS = logOutFOS
3271
3272   javaexec {
3273     classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3274     main = "com.google.javascript.jscomp.CommandLineRunner"
3275     jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3276     args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3277     maxHeapSize = "2g"
3278
3279     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3280     println(msg)
3281     logOutFile.append(msg+"\n")
3282
3283     if (logOutConsole) {
3284       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3285         new org.apache.tools.ant.util.TeeOutputStream(
3286           logOutFOS,
3287           stdout),
3288         standardOutput)
3289         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3290           new org.apache.tools.ant.util.TeeOutputStream(
3291             logErrFOS,
3292             stderr),
3293           System.err)
3294     } else {
3295       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3296         logOutFOS,
3297         stdout)
3298         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3299           logErrFOS,
3300           stderr)
3301     }
3302   }
3303   msg = "--"
3304   println(msg)
3305   logOutFile.append(msg+"\n")
3306 }
3307
3308
3309 task jalviewjsBuildAllCores {
3310   group "JalviewJS"
3311   description "Build the core js lib closures listed in the classlists dir"
3312   dependsOn jalviewjsTranspile
3313   dependsOn jalviewjsTransferUnzipSwingJs
3314
3315   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3316   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3317   def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3318   def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3319   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3320   def prefixFile = "${jsDir}/core/coretop2.js"
3321   def suffixFile = "${jsDir}/core/corebottom2.js"
3322
3323   inputs.file prefixFile
3324   inputs.file suffixFile
3325
3326   def classlistFiles = []
3327   // add the classlists found int the jalviewjs_classlists_dir
3328   fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3329     file ->
3330     def name = file.getName() - ".txt"
3331     classlistFiles += [
3332       'file': file,
3333       'name': name
3334     ]
3335   }
3336
3337   // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3338   //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3339   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3340
3341   jalviewjsCoreClasslists = []
3342
3343   classlistFiles.each {
3344     hash ->
3345
3346     def file = hash['file']
3347     if (! file.exists()) {
3348       //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3349       return false // this is a "continue" in groovy .each closure
3350     }
3351     def name = hash['name']
3352     if (name == null) {
3353       name = file.getName() - ".txt"
3354     }
3355
3356     def filelist = []
3357     file.eachLine {
3358       line ->
3359         filelist += line
3360     }
3361     def list = fileTree(dir: j2sDir, includes: filelist)
3362
3363     def jsfile = "${outputDir}/core${name}.js"
3364     def zjsfile = "${outputDir}/core${name}.z.js"
3365
3366     jalviewjsCoreClasslists += [
3367       'jsfile': jsfile,
3368       'zjsfile': zjsfile,
3369       'list': list,
3370       'name': name
3371     ]
3372
3373     inputs.file(file)
3374     inputs.files(list)
3375     outputs.file(jsfile)
3376     outputs.file(zjsfile)
3377   }
3378   
3379   // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3380   def stevesoftClasslistName = "_stevesoft"
3381   def stevesoftClasslist = [
3382     'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3383     'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3384     'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3385     'name': stevesoftClasslistName
3386   ]
3387   jalviewjsCoreClasslists += stevesoftClasslist
3388   inputs.files(stevesoftClasslist['list'])
3389   outputs.file(stevesoftClasslist['jsfile'])
3390   outputs.file(stevesoftClasslist['zjsfile'])
3391
3392   // _all core
3393   def allClasslistName = "_all"
3394   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3395   allJsFiles += fileTree(
3396     dir: libJ2sDir,
3397     include: "**/*.js",
3398     excludes: [
3399       // these exlusions are files that the closure-compiler produces errors for. Should fix them
3400       "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3401       "**/org/jmol/export/JSExporter.js"
3402     ]
3403   )
3404   allJsFiles += fileTree(
3405     dir: swingJ2sDir,
3406     include: "**/*.js",
3407     excludes: [
3408       // these exlusions are files that the closure-compiler produces errors for. Should fix them
3409       "**/sun/misc/Unsafe.js",
3410       "**/swingjs/jquery/jquery-editable-select.js",
3411       "**/swingjs/jquery/j2sComboBox.js",
3412       "**/sun/misc/FloatingDecimal.js"
3413     ]
3414   )
3415   def allClasslist = [
3416     'jsfile': "${outputDir}/core${allClasslistName}.js",
3417     'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3418     'list': allJsFiles,
3419     'name': allClasslistName
3420   ]
3421   // not including this version of "all" core at the moment
3422   //jalviewjsCoreClasslists += allClasslist
3423   inputs.files(allClasslist['list'])
3424   outputs.file(allClasslist['jsfile'])
3425   outputs.file(allClasslist['zjsfile'])
3426
3427   doFirst {
3428     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
3429     logOutFile.getParentFile().mkdirs()
3430     logOutFile.createNewFile()
3431     logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
3432
3433     jalviewjsCoreClasslists.each {
3434       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
3435     }
3436   }
3437
3438 }
3439
3440
3441 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
3442   copy {
3443     from inputFile
3444     into file(outputFile).getParentFile()
3445     rename { filename ->
3446       if (filename.equals(inputFile.getName())) {
3447         return file(outputFile).getName()
3448       }
3449       return null
3450     }
3451     filter(ReplaceTokens,
3452       beginToken: '_',
3453       endToken: '_',
3454       tokens: [
3455         'MAIN': '"'+main_class+'"',
3456         'CODE': "null",
3457         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
3458         'COREKEY': jalviewjs_core_key,
3459         'CORENAME': coreName
3460       ]
3461     )
3462   }
3463 }
3464
3465
3466 task jalviewjsPublishCoreTemplates {
3467   dependsOn jalviewjsBuildAllCores
3468   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
3469   def inputFile = file(inputFileName)
3470   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3471
3472   def outputFiles = []
3473   jalviewjsCoreClasslists.each { cl ->
3474     def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
3475     cl['outputfile'] = outputFile
3476     outputFiles += outputFile
3477   }
3478
3479   doFirst {
3480     jalviewjsCoreClasslists.each { cl ->
3481       jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
3482     }
3483   }
3484   inputs.file(inputFile)
3485   outputs.files(outputFiles)
3486 }
3487
3488
3489 task jalviewjsSyncCore (type: Sync) {
3490   dependsOn jalviewjsBuildAllCores
3491   dependsOn jalviewjsPublishCoreTemplates
3492   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
3493   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3494
3495   from inputFiles
3496   into outputDir
3497   def outputFiles = []
3498   rename { filename ->
3499     outputFiles += "${outputDir}/${filename}"
3500     null
3501   }
3502   preserve {
3503     include "**"
3504   }
3505   outputs.files outputFiles
3506   inputs.files inputFiles
3507 }
3508
3509
3510 // this Copy version of TransferSiteJs will delete anything else in the target dir
3511 task jalviewjsCopyTransferSiteJs(type: Copy) {
3512   dependsOn jalviewjsTranspile
3513   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3514   into "${jalviewDir}/${jalviewjsSiteDir}"
3515 }
3516
3517
3518 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
3519 task jalviewjsSyncTransferSiteJs(type: Sync) {
3520   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3521   include "**/*.*"
3522   into "${jalviewDir}/${jalviewjsSiteDir}"
3523   preserve {
3524     include "**"
3525   }
3526 }
3527
3528
3529 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
3530 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
3531 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
3532 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
3533
3534 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
3535 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
3536 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
3537 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
3538
3539
3540 task jalviewjsPrepareSite {
3541   group "JalviewJS"
3542   description "Prepares the website folder including unzipping files and copying resources"
3543   dependsOn jalviewjsSyncAllLibs
3544   dependsOn jalviewjsSyncResources
3545   dependsOn jalviewjsSyncSiteResources
3546   dependsOn jalviewjsSyncBuildProperties
3547   dependsOn jalviewjsSyncCore
3548 }
3549
3550
3551 task jalviewjsBuildSite {
3552   group "JalviewJS"
3553   description "Builds the whole website including transpiled code"
3554   dependsOn jalviewjsCopyTransferSiteJs
3555   dependsOn jalviewjsPrepareSite
3556 }
3557
3558
3559 task cleanJalviewjsTransferSite {
3560   doFirst {
3561     delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3562     delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3563     delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3564     delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3565   }
3566 }
3567
3568
3569 task cleanJalviewjsSite {
3570   dependsOn cleanJalviewjsTransferSite
3571   doFirst {
3572     delete "${jalviewDir}/${jalviewjsSiteDir}"
3573   }
3574 }
3575
3576
3577 task jalviewjsSiteTar(type: Tar) {
3578   group "JalviewJS"
3579   description "Creates a tar.gz file for the website"
3580   dependsOn jalviewjsBuildSite
3581   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
3582   archiveFileName = outputFilename
3583
3584   compression Compression.GZIP
3585
3586   from "${jalviewDir}/${jalviewjsSiteDir}"
3587   into jalviewjs_site_dir // this is inside the tar file
3588
3589   inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
3590 }
3591
3592
3593 task jalviewjsServer {
3594   group "JalviewJS"
3595   def filename = "jalviewjsTest.html"
3596   description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
3597   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
3598   doLast {
3599
3600     def factory
3601     try {
3602       def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
3603       factory = f.newInstance()
3604     } catch (ClassNotFoundException e) {
3605       throw new GradleException("Unable to create SimpleHttpFileServerFactory")
3606     }
3607     def port = Integer.valueOf(jalviewjs_server_port)
3608     def start = port
3609     def running = false
3610     def url
3611     def jalviewjsServer
3612     while(port < start+1000 && !running) {
3613       try {
3614         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
3615         jalviewjsServer = factory.start(doc_root, port)
3616         running = true
3617         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
3618         println("SERVER STARTED with document root ${doc_root}.")
3619         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
3620         println("For debug: "+url+"?j2sdebug")
3621         println("For verbose: "+url+"?j2sverbose")
3622       } catch (Exception e) {
3623         port++;
3624       }
3625     }
3626     def htmlText = """
3627       <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
3628       <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
3629       <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
3630       """
3631     jalviewjsCoreClasslists.each { cl ->
3632       def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
3633       htmlText += """
3634       <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
3635       """
3636       println("For core ${cl.name}: "+urlcore)
3637     }
3638
3639     file(htmlFile).text = htmlText
3640   }
3641
3642   outputs.file(htmlFile)
3643   outputs.upToDateWhen({false})
3644 }
3645
3646
3647 task cleanJalviewjsAll {
3648   group "JalviewJS"
3649   description "Delete all configuration and build artifacts to do with JalviewJS build"
3650   dependsOn cleanJalviewjsSite
3651   dependsOn jalviewjsEclipsePaths
3652   
3653   doFirst {
3654     delete "${jalviewDir}/${jalviewjsBuildDir}"
3655     delete "${jalviewDir}/${eclipse_bin_dir}"
3656     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
3657       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
3658     }
3659     delete jalviewjsJ2sAltSettingsFileName
3660   }
3661
3662   outputs.upToDateWhen( { false } )
3663 }
3664
3665
3666 task jalviewjsIDE_checkJ2sPlugin {
3667   group "00 JalviewJS in Eclipse"
3668   description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
3669
3670   doFirst {
3671     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
3672     def j2sPluginFile = file(j2sPlugin)
3673     def eclipseHome = System.properties["eclipse.home.location"]
3674     if (eclipseHome == null || ! IN_ECLIPSE) {
3675       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
3676     }
3677     def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
3678     def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
3679     if (altPluginsDir != null && file(altPluginsDir).exists()) {
3680       eclipseJ2sPluginDirs += altPluginsDir
3681     }
3682     def foundPlugin = false
3683     def j2sPluginFileName = j2sPluginFile.getName()
3684     def eclipseJ2sPlugin
3685     def eclipseJ2sPluginFile
3686     eclipseJ2sPluginDirs.any { dir ->
3687       eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
3688       eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
3689       if (eclipseJ2sPluginFile.exists()) {
3690         foundPlugin = true
3691         return true
3692       }
3693     }
3694     if (!foundPlugin) {
3695       def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
3696       System.err.println(msg)
3697       throw new StopExecutionException(msg)
3698     }
3699
3700     def digest = MessageDigest.getInstance("MD5")
3701
3702     digest.update(j2sPluginFile.text.bytes)
3703     def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
3704
3705     digest.update(eclipseJ2sPluginFile.text.bytes)
3706     def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
3707      
3708     if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
3709       def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
3710       System.err.println(msg)
3711       throw new StopExecutionException(msg)
3712     } else {
3713       def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
3714       println(msg)
3715     }
3716   }
3717 }
3718
3719 task jalviewjsIDE_copyJ2sPlugin {
3720   group "00 JalviewJS in Eclipse"
3721   description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
3722
3723   doFirst {
3724     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
3725     def j2sPluginFile = file(j2sPlugin)
3726     def eclipseHome = System.properties["eclipse.home.location"]
3727     if (eclipseHome == null || ! IN_ECLIPSE) {
3728       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
3729     }
3730     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
3731     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
3732     def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
3733     System.err.println(msg)
3734     copy {
3735       from j2sPlugin
3736       eclipseJ2sPluginFile.getParentFile().mkdirs()
3737       into eclipseJ2sPluginFile.getParent()
3738     }
3739   }
3740 }
3741
3742
3743 task jalviewjsIDE_j2sFile {
3744   group "00 JalviewJS in Eclipse"
3745   description "Creates the .j2s file"
3746   dependsOn jalviewjsCreateJ2sSettings
3747 }
3748
3749
3750 task jalviewjsIDE_SyncCore {
3751   group "00 JalviewJS in Eclipse"
3752   description "Build the core js lib closures listed in the classlists dir and publish core html from template"
3753   dependsOn jalviewjsSyncCore
3754 }
3755
3756
3757 task jalviewjsIDE_SyncSiteAll {
3758   dependsOn jalviewjsSyncAllLibs
3759   dependsOn jalviewjsSyncResources
3760   dependsOn jalviewjsSyncSiteResources
3761   dependsOn jalviewjsSyncBuildProperties
3762 }
3763
3764
3765 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
3766
3767
3768 task jalviewjsIDE_PrepareSite {
3769   group "00 JalviewJS in Eclipse"
3770   description "Sync libs and resources to site dir, but not closure cores"
3771
3772   dependsOn jalviewjsIDE_SyncSiteAll
3773   //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
3774 }
3775
3776
3777 task jalviewjsIDE_AssembleSite {
3778   group "00 JalviewJS in Eclipse"
3779   description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
3780   dependsOn jalviewjsPrepareSite
3781 }
3782
3783
3784 task jalviewjsIDE_SiteClean {
3785   group "00 JalviewJS in Eclipse"
3786   description "Deletes the Eclipse transpiled site"
3787   dependsOn cleanJalviewjsSite
3788 }
3789
3790
3791 task jalviewjsIDE_Server {
3792   group "00 JalviewJS in Eclipse"
3793   description "Starts a webserver on localhost to test the website"
3794   dependsOn jalviewjsServer
3795 }
3796
3797
3798 // buildship runs this at import or gradle refresh
3799 task eclipseSynchronizationTask {
3800   //dependsOn eclipseSetup
3801   dependsOn createBuildProperties
3802   if (J2S_ENABLED) {
3803     dependsOn jalviewjsIDE_j2sFile
3804     dependsOn jalviewjsIDE_checkJ2sPlugin
3805     dependsOn jalviewjsIDE_PrepareSite
3806   }
3807 }
3808
3809
3810 // buildship runs this at build time or project refresh
3811 task eclipseAutoBuildTask {
3812   //dependsOn jalviewjsIDE_checkJ2sPlugin
3813   //dependsOn jalviewjsIDE_PrepareSite
3814 }
3815
3816
3817 task jalviewjs {
3818   group "JalviewJS"
3819   description "Build the site"
3820   dependsOn jalviewjsBuildSite
3821 }