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