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