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