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