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