JAL-4020 class formatting
[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   if (getdownSetAppBaseProperty) {
1274     property "GETDOWNAPPBASE", getdownAppBase
1275     property "GETDOWNAPPDISTDIR", getdownAppDistDir
1276   }
1277   outputs.file(outputFile)
1278 }
1279
1280
1281 task buildIndices(type: JavaExec) {
1282   dependsOn copyHelp
1283   classpath = sourceSets.main.compileClasspath
1284   main = "com.sun.java.help.search.Indexer"
1285   workingDir = "${helpBuildDir}/${help_dir}"
1286   def argDir = "html"
1287   args = [ argDir ]
1288   inputs.dir("${workingDir}/${argDir}")
1289
1290   outputs.dir("${classesDir}/doc")
1291   outputs.dir("${classesDir}/help")
1292   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1293   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1294   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1295   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1296   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1297   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1298 }
1299
1300 task buildResources {
1301   dependsOn copyResources
1302   dependsOn copyChannelResources
1303   dependsOn createBuildProperties
1304 }
1305
1306 task prepare {
1307   dependsOn buildResources
1308   dependsOn copyDocs
1309   dependsOn copyHelp
1310   dependsOn convertMdFiles
1311   dependsOn buildIndices
1312 }
1313
1314
1315 compileJava.dependsOn prepare
1316 run.dependsOn compileJava
1317 //run.dependsOn prepare
1318
1319
1320 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
1321 test {
1322   dependsOn prepare
1323
1324   if (useClover) {
1325     dependsOn cloverClasses
1326    } else { //?
1327     dependsOn compileJava //?
1328   }
1329
1330   useTestNG() {
1331     includeGroups testng_groups
1332     excludeGroups testng_excluded_groups
1333     preserveOrder true
1334     useDefaultListeners=true
1335   }
1336
1337   maxHeapSize = "1024m"
1338
1339   workingDir = jalviewDir
1340   def testLaf = project.findProperty("test_laf")
1341   if (testLaf != null) {
1342     println("Setting Test LaF to '${testLaf}'")
1343     systemProperty "laf", testLaf
1344   }
1345   def testHiDPIScale = project.findProperty("test_HiDPIScale")
1346   if (testHiDPIScale != null) {
1347     println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1348     systemProperty "sun.java2d.uiScale", testHiDPIScale
1349   }
1350   sourceCompatibility = compile_source_compatibility
1351   targetCompatibility = compile_target_compatibility
1352   jvmArgs += additional_compiler_args
1353
1354   doFirst {
1355     if (useClover) {
1356       println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1357     }
1358   }
1359 }
1360
1361
1362 task compileLinkCheck(type: JavaCompile) {
1363   options.fork = true
1364   classpath = files("${jalviewDir}/${utils_dir}")
1365   destinationDir = file("${jalviewDir}/${utils_dir}")
1366   source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
1367
1368   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1369   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1370   outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
1371   outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
1372 }
1373
1374
1375 task linkCheck(type: JavaExec) {
1376   dependsOn prepare
1377   dependsOn compileLinkCheck
1378
1379   def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
1380   classpath = files("${jalviewDir}/${utils_dir}")
1381   main = "HelpLinksChecker"
1382   workingDir = jalviewDir
1383   args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
1384
1385   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
1386   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1387     outFOS,
1388     System.out)
1389   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1390     outFOS,
1391     System.err)
1392
1393   inputs.dir(helpBuildDir)
1394   outputs.file(helpLinksCheckerOutFile)
1395 }
1396
1397
1398 // import the pubhtmlhelp target
1399 ant.properties.basedir = "${jalviewDir}"
1400 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
1401 ant.importBuild "${utils_dir}/publishHelp.xml"
1402
1403
1404 task cleanPackageDir(type: Delete) {
1405   doFirst {
1406     delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
1407   }
1408 }
1409
1410
1411 jar {
1412   dependsOn prepare
1413   dependsOn linkCheck
1414
1415   manifest {
1416     attributes "Main-Class": main_class,
1417     "Permissions": "all-permissions",
1418     "Application-Name": install4jApplicationName,
1419     "Codebase": application_codebase,
1420     "Implementation-Version": JALVIEW_VERSION
1421   }
1422
1423   def outputDir = "${jalviewDir}/${package_dir}"
1424   destinationDirectory = file(outputDir)
1425   archiveFileName = rootProject.name+".jar"
1426   duplicatesStrategy "EXCLUDE"
1427
1428
1429   exclude "cache*/**"
1430   exclude "*.jar"
1431   exclude "*.jar.*"
1432   exclude "**/*.jar"
1433   exclude "**/*.jar.*"
1434
1435   inputs.dir(sourceSets.main.java.outputDir)
1436   sourceSets.main.resources.srcDirs.each{ dir ->
1437     inputs.dir(dir)
1438   }
1439   outputs.file("${outputDir}/${archiveFileName}")
1440 }
1441
1442
1443 task copyJars(type: Copy) {
1444   from fileTree(dir: classesDir, include: "**/*.jar").files
1445   into "${jalviewDir}/${package_dir}"
1446 }
1447
1448
1449 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
1450 task syncJars(type: Sync) {
1451   dependsOn jar
1452   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
1453   into "${jalviewDir}/${package_dir}"
1454   preserve {
1455     include jar.archiveFileName.getOrNull()
1456   }
1457 }
1458
1459
1460 task makeDist {
1461   group = "build"
1462   description = "Put all required libraries in dist"
1463   // order of "cleanPackageDir", "copyJars", "jar" important!
1464   jar.mustRunAfter cleanPackageDir
1465   syncJars.mustRunAfter cleanPackageDir
1466   dependsOn cleanPackageDir
1467   dependsOn syncJars
1468   dependsOn jar
1469   outputs.dir("${jalviewDir}/${package_dir}")
1470 }
1471
1472
1473 task cleanDist {
1474   dependsOn cleanPackageDir
1475   dependsOn cleanTest
1476   dependsOn clean
1477 }
1478
1479
1480 shadowJar {
1481   group = "distribution"
1482   description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
1483   if (buildDist) {
1484     dependsOn makeDist
1485   }
1486   from ("${jalviewDir}/${libDistDir}") {
1487     include("*.jar")
1488   }
1489   manifest {
1490     attributes "Implementation-Version": JALVIEW_VERSION,
1491     "Application-Name": install4jApplicationName
1492   }
1493
1494   duplicatesStrategy "INCLUDE"
1495
1496   mainClassName = shadow_jar_main_class
1497   mergeServiceFiles()
1498   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
1499   minimize()
1500 }
1501
1502
1503 task getdownWebsite() {
1504   group = "distribution"
1505   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
1506   if (buildDist) {
1507     dependsOn makeDist
1508   }
1509
1510   def getdownWebsiteResourceFilenames = []
1511   def getdownTextString = ""
1512   def getdownResourceDir = getdownResourceDir
1513   def getdownResourceFilenames = []
1514
1515   doFirst {
1516     // clean the getdown website and files dir before creating getdown folders
1517     delete getdownWebsiteDir
1518     delete getdownFilesDir
1519
1520     copy {
1521       from buildProperties
1522       rename(file(buildProperties).getName(), getdown_build_properties)
1523       into getdownAppDir
1524     }
1525     getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
1526
1527     copy {
1528       from channelPropsFile
1529       into getdownWebsiteDir
1530     }
1531     getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
1532
1533     // set some getdown_txt_ properties then go through all properties looking for getdown_txt_...
1534     def props = project.properties.sort { it.key }
1535     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
1536       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
1537     }
1538     if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
1539       props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
1540     }
1541     if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
1542       props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
1543     }
1544     if (getdownImagesDir != null && file(getdownImagesDir).exists()) {
1545       props.put("getdown_txt_ui.background_image", "${getdownImagesDir}/${getdown_background_image}")
1546       props.put("getdown_txt_ui.instant_background_image", "${getdownImagesDir}/${getdown_instant_background_image}")
1547       props.put("getdown_txt_ui.error_background", "${getdownImagesDir}/${getdown_error_background}")
1548       props.put("getdown_txt_ui.progress_image", "${getdownImagesDir}/${getdown_progress_image}")
1549       props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
1550       props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
1551     }
1552
1553     props.put("getdown_txt_title", jalview_name)
1554     props.put("getdown_txt_ui.name", install4jApplicationName)
1555
1556     // start with appbase
1557     getdownTextString += "appbase = ${getdownAppBase}\n"
1558     props.each{ prop, val ->
1559       if (prop.startsWith("getdown_txt_") && val != null) {
1560         if (prop.startsWith("getdown_txt_multi_")) {
1561           def key = prop.substring(18)
1562           val.split(",").each{ v ->
1563             def line = "${key} = ${v}\n"
1564             getdownTextString += line
1565           }
1566         } else {
1567           // file values rationalised
1568           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1569             def r = null
1570             if (val.indexOf('/') == 0) {
1571               // absolute path
1572               r = file(val)
1573             } else if (val.indexOf('/') > 0) {
1574               // relative path (relative to jalviewDir)
1575               r = file( "${jalviewDir}/${val}" )
1576             }
1577             if (r.exists()) {
1578               val = "${getdown_resource_dir}/" + r.getName()
1579               getdownWebsiteResourceFilenames += val
1580               getdownResourceFilenames += r.getPath()
1581             }
1582           }
1583           if (! prop.startsWith("getdown_txt_resource")) {
1584             def line = prop.substring(12) + " = ${val}\n"
1585             getdownTextString += line
1586           }
1587         }
1588       }
1589     }
1590
1591     getdownWebsiteResourceFilenames.each{ filename ->
1592       getdownTextString += "resource = ${filename}\n"
1593     }
1594     getdownResourceFilenames.each{ filename ->
1595       copy {
1596         from filename
1597         into getdownResourceDir
1598       }
1599     }
1600     
1601     def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
1602     getdownWrapperScripts.each{ script ->
1603       def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
1604       if (s.exists()) {
1605         copy {
1606           from s
1607           into "${getdownWebsiteDir}/${getdown_wrapper_script_dir}"
1608         }
1609         getdownTextString += "resource = ${getdown_wrapper_script_dir}/${script}\n"
1610       }
1611     }
1612
1613     def codeFiles = []
1614     fileTree(file(package_dir)).each{ f ->
1615       if (f.isDirectory()) {
1616         def files = fileTree(dir: f, include: ["*"]).getFiles()
1617         codeFiles += files
1618       } else if (f.exists()) {
1619         codeFiles += f
1620       }
1621     }
1622     def jalviewJar = jar.archiveFileName.getOrNull()
1623     // put jalview.jar first for CLASSPATH and .properties files reasons
1624     codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
1625       def name = f.getName()
1626       def line = "code = ${getdownAppDistDir}/${name}\n"
1627       getdownTextString += line
1628       copy {
1629         from f.getPath()
1630         into getdownAppDir
1631       }
1632     }
1633
1634     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
1635     /*
1636     if (JAVA_VERSION.equals("11")) {
1637     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
1638     j11libFiles.sort().each{f ->
1639     def name = f.getName()
1640     def line = "code = ${getdown_j11lib_dir}/${name}\n"
1641     getdownTextString += line
1642     copy {
1643     from f.getPath()
1644     into getdownJ11libDir
1645     }
1646     }
1647     }
1648      */
1649
1650     // 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.
1651     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
1652     getdownTextString += "resource = ${getdown_launcher_new}\n"
1653     getdownTextString += "class = ${main_class}\n"
1654     // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
1655     if (getdownSetAppBaseProperty) {
1656       getdownTextString += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}\n"
1657       getdownTextString += "jvmarg = -Dgetdownappbase=${getdownAppBase}\n"
1658     }
1659
1660     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
1661     getdown_txt.write(getdownTextString)
1662
1663     def getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
1664     def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
1665     launchJvl.write("appbase=${getdownAppBase}")
1666
1667     // files going into the getdown website dir: getdown-launcher.jar
1668     copy {
1669       from getdownLauncher
1670       rename(file(getdownLauncher).getName(), getdown_launcher_new)
1671       into getdownWebsiteDir
1672     }
1673
1674     // files going into the getdown website dir: getdown-launcher(-local).jar
1675     copy {
1676       from getdownLauncher
1677       if (file(getdownLauncher).getName() != getdown_launcher) {
1678         rename(file(getdownLauncher).getName(), getdown_launcher)
1679       }
1680       into getdownWebsiteDir
1681     }
1682
1683     // files going into the getdown website dir: ./install dir and files
1684     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
1685       copy {
1686         from getdown_txt
1687         from getdownLauncher
1688         from "${getdownAppDir}/${getdown_build_properties}"
1689         if (file(getdownLauncher).getName() != getdown_launcher) {
1690           rename(file(getdownLauncher).getName(), getdown_launcher)
1691         }
1692         into getdownInstallDir
1693       }
1694
1695       // and make a copy in the getdown files dir (these are not downloaded by getdown)
1696       copy {
1697         from getdownInstallDir
1698         into getdownFilesInstallDir
1699       }
1700     }
1701
1702     // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
1703     copy {
1704       from getdown_txt
1705       from launchJvl
1706       from getdownLauncher
1707       from "${getdownWebsiteDir}/${getdown_build_properties}"
1708       from "${getdownWebsiteDir}/${channel_props}"
1709       if (file(getdownLauncher).getName() != getdown_launcher) {
1710         rename(file(getdownLauncher).getName(), getdown_launcher)
1711       }
1712       into getdownFilesDir
1713     }
1714
1715     // and ./resources (not all downloaded by getdown)
1716     copy {
1717       from getdownResourceDir
1718       into "${getdownFilesDir}/${getdown_resource_dir}"
1719     }
1720   }
1721
1722   if (buildDist) {
1723     inputs.dir("${jalviewDir}/${package_dir}")
1724   }
1725   outputs.dir(getdownWebsiteDir)
1726   outputs.dir(getdownFilesDir)
1727 }
1728
1729
1730 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
1731 task getdownDigestDir(type: JavaExec) {
1732   group "Help"
1733   description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
1734
1735   def digestDirPropertyName = "DIGESTDIR"
1736   doFirst {
1737     classpath = files(getdownLauncher)
1738     def digestDir = findProperty(digestDirPropertyName)
1739     if (digestDir == null) {
1740       throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
1741     }
1742     args digestDir
1743   }
1744   main = "com.threerings.getdown.tools.Digester"
1745 }
1746
1747
1748 task getdownDigest(type: JavaExec) {
1749   group = "distribution"
1750   description = "Digest the getdown website folder"
1751   dependsOn getdownWebsite
1752   doFirst {
1753     classpath = files(getdownLauncher)
1754   }
1755   main = "com.threerings.getdown.tools.Digester"
1756   args getdownWebsiteDir
1757   inputs.dir(getdownWebsiteDir)
1758   outputs.file("${getdownWebsiteDir}/digest2.txt")
1759 }
1760
1761
1762 task getdown() {
1763   group = "distribution"
1764   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1765   dependsOn getdownDigest
1766   doLast {
1767     if (reportRsyncCommand) {
1768       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
1769       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1770       println "LIKELY RSYNC COMMAND:"
1771       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1772       if (RUNRSYNC == "true") {
1773         exec {
1774           commandLine "mkdir", "-p", toDir
1775         }
1776         exec {
1777           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1778         }
1779       }
1780     }
1781   }
1782 }
1783
1784
1785 tasks.withType(JavaCompile) {
1786         options.encoding = 'UTF-8'
1787 }
1788
1789
1790 clean {
1791   doFirst {
1792     delete getdownWebsiteDir
1793     delete getdownFilesDir
1794   }
1795 }
1796
1797
1798 install4j {
1799   if (file(install4jHomeDir).exists()) {
1800     // good to go!
1801   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
1802     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1803   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
1804     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
1805   }
1806   installDir(file(install4jHomeDir))
1807
1808   mediaTypes = Arrays.asList(install4j_media_types.split(","))
1809 }
1810
1811
1812 task copyInstall4jTemplate {
1813   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
1814   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
1815   inputs.file(install4jTemplateFile)
1816   inputs.file(install4jFileAssociationsFile)
1817   inputs.property("CHANNEL", { CHANNEL })
1818   outputs.file(install4jConfFile)
1819
1820   doLast {
1821     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
1822
1823     // turn off code signing if no OSX_KEYPASS
1824     if (OSX_KEYPASS == "") {
1825       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
1826         codeSigning.'@macEnabled' = "false"
1827       }
1828       install4jConfigXml.'**'.windows.each { windows ->
1829         windows.'@runPostProcessor' = "false"
1830       }
1831     }
1832
1833     // disable install screen for OSX dmg (for 2.11.2.0)
1834     install4jConfigXml.'**'.macosArchive.each { macosArchive -> 
1835       macosArchive.attributes().remove('executeSetupApp')
1836       macosArchive.attributes().remove('setupAppId')
1837     }
1838
1839     // turn off checksum creation for LOCAL channel
1840     def e = install4jConfigXml.application[0]
1841     if (CHANNEL == "LOCAL") {
1842       e.'@createChecksums' = "false"
1843     } else {
1844       e.'@createChecksums' = "true"
1845     }
1846
1847     // put file association actions where placeholder action is
1848     def install4jFileAssociationsText = install4jFileAssociationsFile.text
1849     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
1850     install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
1851       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
1852         def parent = a.parent()
1853         parent.remove(a)
1854         fileAssociationActions.each { faa ->
1855             parent.append(faa)
1856         }
1857         // don't need to continue in .any loop once replacements have been made
1858         return true
1859       }
1860     }
1861
1862     // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
1863     // NB we're deleting the /other/ one!
1864     // Also remove the examples subdir from non-release versions
1865     def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
1866     // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
1867     if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
1868       customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
1869     } else {
1870       // remove the examples subdir from Full File Set
1871       def files = install4jConfigXml.files[0]
1872       def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
1873       def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
1874       def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
1875       def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
1876       dirEntry.parent().remove(dirEntry)
1877     }
1878     install4jConfigXml.'**'.action.any { a ->
1879       if (a.'@customizedId' == customizedIdToDelete) {
1880         def parent = a.parent()
1881         parent.remove(a)
1882         return true
1883       }
1884     }
1885
1886     // write install4j file
1887     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
1888   }
1889 }
1890
1891
1892 clean {
1893   doFirst {
1894     delete install4jConfFile
1895   }
1896 }
1897
1898
1899 task installers(type: com.install4j.gradle.Install4jTask) {
1900   group = "distribution"
1901   description = "Create the install4j installers"
1902   dependsOn getdown
1903   dependsOn copyInstall4jTemplate
1904
1905   projectFile = install4jConfFile
1906
1907   // create an md5 for the input files to use as version for install4j conf file
1908   def digest = MessageDigest.getInstance("MD5")
1909   digest.update(
1910     (file("${install4jDir}/${install4j_template}").text + 
1911     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
1912     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
1913   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
1914   if (filesMd5.length() >= 8) {
1915     filesMd5 = filesMd5.substring(0,8)
1916   }
1917   def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
1918   // make install4jBuildDir relative to jalviewDir
1919   def install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
1920
1921   variables = [
1922     'JALVIEW_NAME': jalview_name,
1923     'JALVIEW_APPLICATION_NAME': install4jApplicationName,
1924     'JALVIEW_DIR': "../..",
1925     'OSX_KEYSTORE': OSX_KEYSTORE,
1926     'OSX_APPLEID': OSX_APPLEID,
1927     'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
1928     'JSIGN_SH': JSIGN_SH,
1929     'JRE_DIR': getdown_app_dir_java,
1930     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
1931     'JALVIEW_VERSION': JALVIEW_VERSION,
1932     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
1933     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
1934     'JAVA_VERSION': JAVA_VERSION,
1935     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1936     'VERSION': JALVIEW_VERSION,
1937     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1938     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1939     'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
1940     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1941     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1942     'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
1943     'COPYRIGHT_MESSAGE': install4j_copyright_message,
1944     'BUNDLE_ID': install4jBundleId,
1945     'INTERNAL_ID': install4jInternalId,
1946     'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
1947     'MACOS_DMG_DS_STORE': install4jDMGDSStore,
1948     'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
1949     'WRAPPER_LINK': getdownWrapperLink,
1950     'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
1951     'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
1952     'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
1953     'INSTALLER_NAME': install4jInstallerName,
1954     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
1955     'GETDOWN_WEBSITE_DIR': getdown_website_dir,
1956     'GETDOWN_FILES_DIR': getdown_files_dir,
1957     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1958     'GETDOWN_DIST_DIR': getdownAppDistDir,
1959     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1960     'GETDOWN_INSTALL_DIR': getdown_install_dir,
1961     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
1962     'BUILD_DIR': install4jBuildDir,
1963     'APPLICATION_CATEGORIES': install4j_application_categories,
1964     'APPLICATION_FOLDER': install4jApplicationFolder,
1965     'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
1966     'EXECUTABLE_NAME': install4jExecutableName,
1967     'EXTRA_SCHEME': install4jExtraScheme,
1968     'MAC_ICONS_FILE': install4jMacIconsFile,
1969     'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
1970     'PNG_ICON_FILE': install4jPngIconFile,
1971     'BACKGROUND': install4jBackground,
1972
1973   ]
1974
1975   //println("INSTALL4J VARIABLES:")
1976   //variables.each{k,v->println("${k}=${v}")}
1977
1978   destination = "${jalviewDir}/${install4jBuildDir}"
1979   buildSelected = true
1980
1981   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
1982     faster = true
1983     disableSigning = true
1984     disableNotarization = true
1985   }
1986
1987   if (OSX_KEYPASS) {
1988     macKeystorePassword = OSX_KEYPASS
1989   } 
1990   
1991   if (OSX_ALTOOLPASS) {
1992     appleIdPassword = OSX_ALTOOLPASS
1993     disableNotarization = false
1994   } else {
1995     disableNotarization = true
1996   }
1997
1998   doFirst {
1999     println("Using projectFile "+projectFile)
2000     if (!disableNotarization) { println("Will notarize OSX App DMG") }
2001   }
2002   //verbose=true
2003
2004   inputs.dir(getdownWebsiteDir)
2005   inputs.file(install4jConfFile)
2006   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2007   inputs.dir(macosJavaVMDir)
2008   inputs.dir(windowsJavaVMDir)
2009   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2010 }
2011
2012
2013 spotless {
2014   java {
2015     eclipse().configFile(eclipse_codestyle_file)
2016   }
2017 }
2018
2019 task createSourceReleaseProperties(type: WriteProperties) {
2020   group = "distribution"
2021   description = "Create the source RELEASE properties file"
2022   
2023   def sourceTarBuildDir = "${buildDir}/sourceTar"
2024   def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
2025   outputFile (sourceReleasePropertiesFile)
2026
2027   doFirst {
2028     releaseProps.each{ key, val -> property key, val }
2029     property "git.branch", gitBranch
2030     property "git.hash", gitHash
2031   }
2032
2033   outputs.file(outputFile)
2034 }
2035
2036 task sourceDist(type: Tar) {
2037   group "distribution"
2038   description "Create a source .tar.gz file for distribution"
2039
2040   dependsOn createBuildProperties
2041   dependsOn convertMdFiles
2042   dependsOn eclipseAllPreferences
2043   dependsOn createSourceReleaseProperties
2044
2045
2046   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
2047   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
2048   archiveFileName = outputFileName
2049   
2050   compression Compression.GZIP
2051   
2052   into project.name
2053
2054   def EXCLUDE_FILES=[
2055     "build/*",
2056     "bin/*",
2057     "test-output/",
2058     "test-reports",
2059     "tests",
2060     "clover*/*",
2061     ".*",
2062     "benchmarking/*",
2063     "**/.*",
2064     "*.class",
2065     "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
2066     "*locales/**",
2067     "utils/InstallAnywhere",
2068     "**/*.log",
2069     "RELEASE",
2070   ] 
2071   def PROCESS_FILES=[
2072     "AUTHORS",
2073     "CITATION",
2074     "FEATURETODO",
2075     "JAVA-11-README",
2076     "FEATURETODO",
2077     "LICENSE",
2078     "**/README",
2079     "THIRDPARTYLIBS",
2080     "TESTNG",
2081     "build.gradle",
2082     "gradle.properties",
2083     "**/*.java",
2084     "**/*.html",
2085     "**/*.xml",
2086     "**/*.gradle",
2087     "**/*.groovy",
2088     "**/*.properties",
2089     "**/*.perl",
2090     "**/*.sh",
2091   ]
2092   def INCLUDE_FILES=[
2093     ".classpath",
2094     ".settings/org.eclipse.buildship.core.prefs",
2095     ".settings/org.eclipse.jdt.core.prefs"
2096   ]
2097
2098   from(jalviewDir) {
2099     exclude (EXCLUDE_FILES)
2100     include (PROCESS_FILES)
2101     filter(ReplaceTokens,
2102       beginToken: '$$',
2103       endToken: '$$',
2104       tokens: [
2105         'Version-Rel': JALVIEW_VERSION,
2106         'Year-Rel': getDate("yyyy")
2107       ]
2108     )
2109   }
2110   from(jalviewDir) {
2111     exclude (EXCLUDE_FILES)
2112     exclude (PROCESS_FILES)
2113     exclude ("appletlib")
2114     exclude ("**/*locales")
2115     exclude ("*locales/**")
2116     exclude ("utils/InstallAnywhere")
2117
2118     exclude (getdown_files_dir)
2119     exclude (getdown_website_dir)
2120
2121     // exluding these as not using jars as modules yet
2122     exclude ("${j11modDir}/**/*.jar")
2123   }
2124   from(jalviewDir) {
2125     include(INCLUDE_FILES)
2126   }
2127 //  from (jalviewDir) {
2128 //    // explicit includes for stuff that seemed to not get included
2129 //    include(fileTree("test/**/*."))
2130 //    exclude(EXCLUDE_FILES)
2131 //    exclude(PROCESS_FILES)
2132 //  }
2133
2134   from(file(buildProperties).getParent()) {
2135     include(file(buildProperties).getName())
2136     rename(file(buildProperties).getName(), "build_properties")
2137     filter({ line ->
2138       line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
2139     })
2140   }
2141
2142   def sourceTarBuildDir = "${buildDir}/sourceTar"
2143   from(sourceTarBuildDir) {
2144     // this includes the appended RELEASE properties file
2145   }
2146 }
2147
2148
2149 task helppages {
2150   dependsOn copyHelp
2151   dependsOn pubhtmlhelp
2152   
2153   inputs.dir("${helpBuildDir}/${help_dir}")
2154   outputs.dir("${buildDir}/distributions/${help_dir}")
2155 }
2156
2157
2158 task j2sSetHeadlessBuild {
2159   doFirst {
2160     IN_ECLIPSE = false
2161   }
2162 }
2163
2164
2165 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
2166   group "jalviewjs"
2167   description "Enable the alternative J2S Config file for headless build"
2168
2169   outputFile = jalviewjsJ2sSettingsFileName
2170   def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
2171   def j2sProps = new Properties()
2172   if (j2sPropsFile.exists()) {
2173     try {
2174       def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
2175       j2sProps.load(j2sPropsFileFIS)
2176       j2sPropsFileFIS.close()
2177
2178       j2sProps.each { prop, val ->
2179         property(prop, val)
2180       }
2181     } catch (Exception e) {
2182       println("Exception reading ${jalviewjsJ2sSettingsFileName}")
2183       e.printStackTrace()
2184     }
2185   }
2186   if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
2187     property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
2188   }
2189 }
2190
2191
2192 task jalviewjsSetEclipseWorkspace {
2193   def propKey = "jalviewjs_eclipse_workspace"
2194   def propVal = null
2195   if (project.hasProperty(propKey)) {
2196     propVal = project.getProperty(propKey)
2197     if (propVal.startsWith("~/")) {
2198       propVal = System.getProperty("user.home") + propVal.substring(1)
2199     }
2200   }
2201   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
2202   def propsFile = file(propsFileName)
2203   def eclipseWsDir = propVal
2204   def props = new Properties()
2205
2206   def writeProps = true
2207   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
2208     def ins = new FileInputStream(propsFileName)
2209     props.load(ins)
2210     ins.close()
2211     if (props.getProperty(propKey, null) != null) {
2212       eclipseWsDir = props.getProperty(propKey)
2213       writeProps = false
2214     }
2215   }
2216
2217   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
2218     def tempDir = File.createTempDir()
2219     eclipseWsDir = tempDir.getAbsolutePath()
2220     writeProps = true
2221   }
2222   eclipseWorkspace = file(eclipseWsDir)
2223
2224   doFirst {
2225     // do not run a headless transpile when we claim to be in Eclipse
2226     if (IN_ECLIPSE) {
2227       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2228       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2229     } else {
2230       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2231     }
2232
2233     if (writeProps) {
2234       props.setProperty(propKey, eclipseWsDir)
2235       propsFile.parentFile.mkdirs()
2236       def bytes = new ByteArrayOutputStream()
2237       props.store(bytes, null)
2238       def propertiesString = bytes.toString()
2239       propsFile.text = propertiesString
2240       print("NEW ")
2241     } else {
2242       print("EXISTING ")
2243     }
2244
2245     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
2246   }
2247
2248   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
2249   outputs.file(propsFileName)
2250   outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
2251 }
2252
2253
2254 task jalviewjsEclipsePaths {
2255   def eclipseProduct
2256
2257   def eclipseRoot = jalviewjs_eclipse_root
2258   if (eclipseRoot.startsWith("~/")) {
2259     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
2260   }
2261   if (OperatingSystem.current().isMacOsX()) {
2262     eclipseRoot += "/Eclipse.app"
2263     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
2264     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
2265   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
2266     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
2267       eclipseRoot += "/eclipse"
2268     }
2269     eclipseBinary = "${eclipseRoot}/eclipse.exe"
2270     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
2271   } else { // linux or unix
2272     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
2273       eclipseRoot += "/eclipse"
2274 println("eclipseDir exists")
2275     }
2276     eclipseBinary = "${eclipseRoot}/eclipse"
2277     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
2278   }
2279
2280   eclipseVersion = "4.13" // default
2281   def assumedVersion = true
2282   if (file(eclipseProduct).exists()) {
2283     def fis = new FileInputStream(eclipseProduct)
2284     def props = new Properties()
2285     props.load(fis)
2286     eclipseVersion = props.getProperty("version")
2287     fis.close()
2288     assumedVersion = false
2289   }
2290   
2291   def propKey = "eclipse_debug"
2292   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
2293
2294   doFirst {
2295     // do not run a headless transpile when we claim to be in Eclipse
2296     if (IN_ECLIPSE) {
2297       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2298       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2299     } else {
2300       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2301     }
2302
2303     if (!assumedVersion) {
2304       println("ECLIPSE VERSION=${eclipseVersion}")
2305     }
2306   }
2307 }
2308
2309
2310 task printProperties {
2311   group "Debug"
2312   description "Output to console all System.properties"
2313   doFirst {
2314     System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
2315   }
2316 }
2317
2318
2319 task eclipseSetup {
2320   dependsOn eclipseProject
2321   dependsOn eclipseClasspath
2322   dependsOn eclipseJdt
2323 }
2324
2325
2326 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
2327 task jalviewjsEclipseCopyDropins(type: Copy) {
2328   dependsOn jalviewjsEclipsePaths
2329
2330   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
2331   inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
2332   def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
2333
2334   from inputFiles
2335   into outputDir
2336 }
2337
2338
2339 // this eclipse -clean doesn't actually work
2340 task jalviewjsCleanEclipse(type: Exec) {
2341   dependsOn eclipseSetup
2342   dependsOn jalviewjsEclipsePaths
2343   dependsOn jalviewjsEclipseCopyDropins
2344
2345   executable(eclipseBinary)
2346   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
2347   if (eclipseDebug) {
2348     args += "-debug"
2349   }
2350   args += "-l"
2351
2352   def inputString = """exit
2353 y
2354 """
2355   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
2356   standardInput = inputByteStream
2357 }
2358
2359 /* not really working yet
2360 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
2361 */
2362
2363
2364 task jalviewjsTransferUnzipSwingJs {
2365   def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
2366
2367   doLast {
2368     copy {
2369       from zipTree(file_zip)
2370       into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2371     }
2372   }
2373
2374   inputs.file file_zip
2375   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2376 }
2377
2378
2379 task jalviewjsTransferUnzipLib {
2380   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
2381
2382   doLast {
2383     zipFiles.each { file_zip -> 
2384       copy {
2385         from zipTree(file_zip)
2386         into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2387       }
2388     }
2389   }
2390
2391   inputs.files zipFiles
2392   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2393 }
2394
2395
2396 task jalviewjsTransferUnzipAllLibs {
2397   dependsOn jalviewjsTransferUnzipSwingJs
2398   dependsOn jalviewjsTransferUnzipLib
2399 }
2400
2401
2402 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
2403   group "JalviewJS"
2404   description "Create the alternative j2s file from the j2s.* properties"
2405
2406   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
2407   def siteDirProperty = "j2s.site.directory"
2408   def setSiteDir = false
2409   jalviewjsJ2sProps.each { prop, val ->
2410     if (val != null) {
2411       if (prop == siteDirProperty) {
2412         if (!(val.startsWith('/') || val.startsWith("file://") )) {
2413           val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
2414         }
2415         setSiteDir = true
2416       }
2417       property(prop,val)
2418     }
2419     if (!setSiteDir) { // default site location, don't override specifically set property
2420       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
2421     }
2422   }
2423   outputFile = jalviewjsJ2sAltSettingsFileName
2424
2425   if (! IN_ECLIPSE) {
2426     inputs.properties(jalviewjsJ2sProps)
2427     outputs.file(jalviewjsJ2sAltSettingsFileName)
2428   }
2429 }
2430
2431
2432 task jalviewjsEclipseSetup {
2433   dependsOn jalviewjsEclipseCopyDropins
2434   dependsOn jalviewjsSetEclipseWorkspace
2435   dependsOn jalviewjsCreateJ2sSettings
2436 }
2437
2438
2439 task jalviewjsSyncAllLibs (type: Sync) {
2440   dependsOn jalviewjsTransferUnzipAllLibs
2441   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
2442   inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
2443   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2444
2445   from inputFiles
2446   into outputDir
2447   def outputFiles = []
2448   rename { filename ->
2449     outputFiles += "${outputDir}/${filename}"
2450     null
2451   }
2452   preserve {
2453     include "**"
2454   }
2455
2456   // should this be exclude really ?
2457   duplicatesStrategy "INCLUDE"
2458
2459   outputs.files outputFiles
2460   inputs.files inputFiles
2461 }
2462
2463
2464 task jalviewjsSyncResources (type: Sync) {
2465   dependsOn buildResources
2466
2467   def inputFiles = fileTree(dir: resourcesBuildDir)
2468   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
2469
2470   from inputFiles
2471   into outputDir
2472   def outputFiles = []
2473   rename { filename ->
2474     outputFiles += "${outputDir}/${filename}"
2475     null
2476   }
2477   preserve {
2478     include "**"
2479   }
2480   outputs.files outputFiles
2481   inputs.files inputFiles
2482 }
2483
2484
2485 task jalviewjsSyncSiteResources (type: Sync) {
2486   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
2487   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2488
2489   from inputFiles
2490   into outputDir
2491   def outputFiles = []
2492   rename { filename ->
2493     outputFiles += "${outputDir}/${filename}"
2494     null
2495   }
2496   preserve {
2497     include "**"
2498   }
2499   outputs.files outputFiles
2500   inputs.files inputFiles
2501 }
2502
2503
2504 task jalviewjsSyncBuildProperties (type: Sync) {
2505   dependsOn createBuildProperties
2506   def inputFiles = [file(buildProperties)]
2507   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
2508
2509   from inputFiles
2510   into outputDir
2511   def outputFiles = []
2512   rename { filename ->
2513     outputFiles += "${outputDir}/${filename}"
2514     null
2515   }
2516   preserve {
2517     include "**"
2518   }
2519   outputs.files outputFiles
2520   inputs.files inputFiles
2521 }
2522
2523
2524 task jalviewjsProjectImport(type: Exec) {
2525   dependsOn eclipseSetup
2526   dependsOn jalviewjsEclipsePaths
2527   dependsOn jalviewjsEclipseSetup
2528
2529   doFirst {
2530     // do not run a headless import when we claim to be in Eclipse
2531     if (IN_ECLIPSE) {
2532       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2533       throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2534     } else {
2535       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2536     }
2537   }
2538
2539   //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
2540   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
2541   executable(eclipseBinary)
2542   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
2543   if (eclipseDebug) {
2544     args += "-debug"
2545   }
2546   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
2547   if (!IN_ECLIPSE) {
2548     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
2549     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
2550   }
2551
2552   inputs.file("${jalviewDir}/.project")
2553   outputs.upToDateWhen { 
2554     file(projdir).exists()
2555   }
2556 }
2557
2558
2559 task jalviewjsTranspile(type: Exec) {
2560   dependsOn jalviewjsEclipseSetup 
2561   dependsOn jalviewjsProjectImport
2562   dependsOn jalviewjsEclipsePaths
2563   if (!IN_ECLIPSE) {
2564     dependsOn jalviewjsEnableAltFileProperty
2565   }
2566
2567   doFirst {
2568     // do not run a headless transpile when we claim to be in Eclipse
2569     if (IN_ECLIPSE) {
2570       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2571       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2572     } else {
2573       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2574     }
2575   }
2576
2577   executable(eclipseBinary)
2578   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
2579   if (eclipseDebug) {
2580     args += "-debug"
2581   }
2582   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
2583   if (!IN_ECLIPSE) {
2584     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
2585     args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
2586   }
2587
2588   def stdout
2589   def stderr
2590   doFirst {
2591     stdout = new ByteArrayOutputStream()
2592     stderr = new ByteArrayOutputStream()
2593
2594     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
2595     def logOutFile = file(logOutFileName)
2596     logOutFile.createNewFile()
2597     logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
2598 BINARY: ${eclipseBinary}
2599 VERSION: ${eclipseVersion}
2600 WORKSPACE: ${eclipseWorkspace}
2601 DEBUG: ${eclipseDebug}
2602 ----
2603 """
2604     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
2605     // combine stdout and stderr
2606     def logErrFOS = logOutFOS
2607
2608     if (jalviewjs_j2s_to_console.equals("true")) {
2609       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2610         new org.apache.tools.ant.util.TeeOutputStream(
2611           logOutFOS,
2612           stdout),
2613         System.out)
2614       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2615         new org.apache.tools.ant.util.TeeOutputStream(
2616           logErrFOS,
2617           stderr),
2618         System.err)
2619     } else {
2620       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2621         logOutFOS,
2622         stdout)
2623       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2624         logErrFOS,
2625         stderr)
2626     }
2627   }
2628
2629   doLast {
2630     if (stdout.toString().contains("Error processing ")) {
2631       // j2s did not complete transpile
2632       //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
2633       if (jalviewjs_ignore_transpile_errors.equals("true")) {
2634         println("IGNORING TRANSPILE ERRORS")
2635         println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
2636       } else {
2637         throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
2638       }
2639     }
2640   }
2641
2642   inputs.dir("${jalviewDir}/${sourceDir}")
2643   outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
2644   outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
2645 }
2646
2647
2648 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
2649
2650   def stdout = new ByteArrayOutputStream()
2651   def stderr = new ByteArrayOutputStream()
2652
2653   def coreFile = file(jsfile)
2654   def msg = ""
2655   msg = "Creating core for ${name}...\nGenerating ${jsfile}"
2656   println(msg)
2657   logOutFile.createNewFile()
2658   logOutFile.append(msg+"\n")
2659
2660   def coreTop = file(prefixFile)
2661   def coreBottom = file(suffixFile)
2662   coreFile.getParentFile().mkdirs()
2663   coreFile.createNewFile()
2664   coreFile.write( coreTop.getText("UTF-8") )
2665   list.each {
2666     f ->
2667     if (f.exists()) {
2668       def t = f.getText("UTF-8")
2669       t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
2670       coreFile.append( t )
2671     } else {
2672       msg = "...file '"+f.getPath()+"' does not exist, skipping"
2673       println(msg)
2674       logOutFile.append(msg+"\n")
2675     }
2676   }
2677   coreFile.append( coreBottom.getText("UTF-8") )
2678
2679   msg = "Generating ${zjsfile}"
2680   println(msg)
2681   logOutFile.append(msg+"\n")
2682   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
2683   def logErrFOS = logOutFOS
2684
2685   javaexec {
2686     classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
2687     main = "com.google.javascript.jscomp.CommandLineRunner"
2688     jvmArgs = [ "-Dfile.encoding=UTF-8" ]
2689     args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
2690     maxHeapSize = "2g"
2691
2692     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
2693     println(msg)
2694     logOutFile.append(msg+"\n")
2695
2696     if (logOutConsole) {
2697       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2698         new org.apache.tools.ant.util.TeeOutputStream(
2699           logOutFOS,
2700           stdout),
2701         standardOutput)
2702         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2703           new org.apache.tools.ant.util.TeeOutputStream(
2704             logErrFOS,
2705             stderr),
2706           System.err)
2707     } else {
2708       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2709         logOutFOS,
2710         stdout)
2711         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2712           logErrFOS,
2713           stderr)
2714     }
2715   }
2716   msg = "--"
2717   println(msg)
2718   logOutFile.append(msg+"\n")
2719 }
2720
2721
2722 task jalviewjsBuildAllCores {
2723   group "JalviewJS"
2724   description "Build the core js lib closures listed in the classlists dir"
2725   dependsOn jalviewjsTranspile
2726   dependsOn jalviewjsTransferUnzipSwingJs
2727
2728   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
2729   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
2730   def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
2731   def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
2732   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
2733   def prefixFile = "${jsDir}/core/coretop2.js"
2734   def suffixFile = "${jsDir}/core/corebottom2.js"
2735
2736   inputs.file prefixFile
2737   inputs.file suffixFile
2738
2739   def classlistFiles = []
2740   // add the classlists found int the jalviewjs_classlists_dir
2741   fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
2742     file ->
2743     def name = file.getName() - ".txt"
2744     classlistFiles += [
2745       'file': file,
2746       'name': name
2747     ]
2748   }
2749
2750   // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
2751   //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
2752   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
2753
2754   jalviewjsCoreClasslists = []
2755
2756   classlistFiles.each {
2757     hash ->
2758
2759     def file = hash['file']
2760     if (! file.exists()) {
2761       //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
2762       return false // this is a "continue" in groovy .each closure
2763     }
2764     def name = hash['name']
2765     if (name == null) {
2766       name = file.getName() - ".txt"
2767     }
2768
2769     def filelist = []
2770     file.eachLine {
2771       line ->
2772         filelist += line
2773     }
2774     def list = fileTree(dir: j2sDir, includes: filelist)
2775
2776     def jsfile = "${outputDir}/core${name}.js"
2777     def zjsfile = "${outputDir}/core${name}.z.js"
2778
2779     jalviewjsCoreClasslists += [
2780       'jsfile': jsfile,
2781       'zjsfile': zjsfile,
2782       'list': list,
2783       'name': name
2784     ]
2785
2786     inputs.file(file)
2787     inputs.files(list)
2788     outputs.file(jsfile)
2789     outputs.file(zjsfile)
2790   }
2791   
2792   // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
2793   def stevesoftClasslistName = "_stevesoft"
2794   def stevesoftClasslist = [
2795     'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
2796     'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
2797     'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
2798     'name': stevesoftClasslistName
2799   ]
2800   jalviewjsCoreClasslists += stevesoftClasslist
2801   inputs.files(stevesoftClasslist['list'])
2802   outputs.file(stevesoftClasslist['jsfile'])
2803   outputs.file(stevesoftClasslist['zjsfile'])
2804
2805   // _all core
2806   def allClasslistName = "_all"
2807   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
2808   allJsFiles += fileTree(
2809     dir: libJ2sDir,
2810     include: "**/*.js",
2811     excludes: [
2812       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2813       "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
2814       "**/org/jmol/export/JSExporter.js"
2815     ]
2816   )
2817   allJsFiles += fileTree(
2818     dir: swingJ2sDir,
2819     include: "**/*.js",
2820     excludes: [
2821       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2822       "**/sun/misc/Unsafe.js",
2823       "**/swingjs/jquery/jquery-editable-select.js",
2824       "**/swingjs/jquery/j2sComboBox.js",
2825       "**/sun/misc/FloatingDecimal.js"
2826     ]
2827   )
2828   def allClasslist = [
2829     'jsfile': "${outputDir}/core${allClasslistName}.js",
2830     'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
2831     'list': allJsFiles,
2832     'name': allClasslistName
2833   ]
2834   // not including this version of "all" core at the moment
2835   //jalviewjsCoreClasslists += allClasslist
2836   inputs.files(allClasslist['list'])
2837   outputs.file(allClasslist['jsfile'])
2838   outputs.file(allClasslist['zjsfile'])
2839
2840   doFirst {
2841     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
2842     logOutFile.getParentFile().mkdirs()
2843     logOutFile.createNewFile()
2844     logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
2845
2846     jalviewjsCoreClasslists.each {
2847       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
2848     }
2849   }
2850
2851 }
2852
2853
2854 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
2855   copy {
2856     from inputFile
2857     into file(outputFile).getParentFile()
2858     rename { filename ->
2859       if (filename.equals(inputFile.getName())) {
2860         return file(outputFile).getName()
2861       }
2862       return null
2863     }
2864     filter(ReplaceTokens,
2865       beginToken: '_',
2866       endToken: '_',
2867       tokens: [
2868         'MAIN': '"'+main_class+'"',
2869         'CODE': "null",
2870         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
2871         'COREKEY': jalviewjs_core_key,
2872         'CORENAME': coreName
2873       ]
2874     )
2875   }
2876 }
2877
2878
2879 task jalviewjsPublishCoreTemplates {
2880   dependsOn jalviewjsBuildAllCores
2881   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
2882   def inputFile = file(inputFileName)
2883   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2884
2885   def outputFiles = []
2886   jalviewjsCoreClasslists.each { cl ->
2887     def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
2888     cl['outputfile'] = outputFile
2889     outputFiles += outputFile
2890   }
2891
2892   doFirst {
2893     jalviewjsCoreClasslists.each { cl ->
2894       jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
2895     }
2896   }
2897   inputs.file(inputFile)
2898   outputs.files(outputFiles)
2899 }
2900
2901
2902 task jalviewjsSyncCore (type: Sync) {
2903   dependsOn jalviewjsBuildAllCores
2904   dependsOn jalviewjsPublishCoreTemplates
2905   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
2906   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2907
2908   from inputFiles
2909   into outputDir
2910   def outputFiles = []
2911   rename { filename ->
2912     outputFiles += "${outputDir}/${filename}"
2913     null
2914   }
2915   preserve {
2916     include "**"
2917   }
2918   outputs.files outputFiles
2919   inputs.files inputFiles
2920 }
2921
2922
2923 // this Copy version of TransferSiteJs will delete anything else in the target dir
2924 task jalviewjsCopyTransferSiteJs(type: Copy) {
2925   dependsOn jalviewjsTranspile
2926   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2927   into "${jalviewDir}/${jalviewjsSiteDir}"
2928 }
2929
2930
2931 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
2932 task jalviewjsSyncTransferSiteJs(type: Sync) {
2933   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2934   include "**/*.*"
2935   into "${jalviewDir}/${jalviewjsSiteDir}"
2936   preserve {
2937     include "**"
2938   }
2939 }
2940
2941
2942 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
2943 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
2944 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
2945 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
2946
2947 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
2948 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
2949 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
2950 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
2951
2952
2953 task jalviewjsPrepareSite {
2954   group "JalviewJS"
2955   description "Prepares the website folder including unzipping files and copying resources"
2956   dependsOn jalviewjsSyncAllLibs
2957   dependsOn jalviewjsSyncResources
2958   dependsOn jalviewjsSyncSiteResources
2959   dependsOn jalviewjsSyncBuildProperties
2960   dependsOn jalviewjsSyncCore
2961 }
2962
2963
2964 task jalviewjsBuildSite {
2965   group "JalviewJS"
2966   description "Builds the whole website including transpiled code"
2967   dependsOn jalviewjsCopyTransferSiteJs
2968   dependsOn jalviewjsPrepareSite
2969 }
2970
2971
2972 task cleanJalviewjsTransferSite {
2973   doFirst {
2974     delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2975     delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2976     delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2977     delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2978   }
2979 }
2980
2981
2982 task cleanJalviewjsSite {
2983   dependsOn cleanJalviewjsTransferSite
2984   doFirst {
2985     delete "${jalviewDir}/${jalviewjsSiteDir}"
2986   }
2987 }
2988
2989
2990 task jalviewjsSiteTar(type: Tar) {
2991   group "JalviewJS"
2992   description "Creates a tar.gz file for the website"
2993   dependsOn jalviewjsBuildSite
2994   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
2995   archiveFileName = outputFilename
2996
2997   compression Compression.GZIP
2998
2999   from "${jalviewDir}/${jalviewjsSiteDir}"
3000   into jalviewjs_site_dir // this is inside the tar file
3001
3002   inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
3003 }
3004
3005
3006 task jalviewjsServer {
3007   group "JalviewJS"
3008   def filename = "jalviewjsTest.html"
3009   description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
3010   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
3011   doLast {
3012
3013     def factory
3014     try {
3015       def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
3016       factory = f.newInstance()
3017     } catch (ClassNotFoundException e) {
3018       throw new GradleException("Unable to create SimpleHttpFileServerFactory")
3019     }
3020     def port = Integer.valueOf(jalviewjs_server_port)
3021     def start = port
3022     def running = false
3023     def url
3024     def jalviewjsServer
3025     while(port < start+1000 && !running) {
3026       try {
3027         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
3028         jalviewjsServer = factory.start(doc_root, port)
3029         running = true
3030         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
3031         println("SERVER STARTED with document root ${doc_root}.")
3032         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
3033         println("For debug: "+url+"?j2sdebug")
3034         println("For verbose: "+url+"?j2sverbose")
3035       } catch (Exception e) {
3036         port++;
3037       }
3038     }
3039     def htmlText = """
3040       <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
3041       <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
3042       <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
3043       """
3044     jalviewjsCoreClasslists.each { cl ->
3045       def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
3046       htmlText += """
3047       <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
3048       """
3049       println("For core ${cl.name}: "+urlcore)
3050     }
3051
3052     file(htmlFile).text = htmlText
3053   }
3054
3055   outputs.file(htmlFile)
3056   outputs.upToDateWhen({false})
3057 }
3058
3059
3060 task cleanJalviewjsAll {
3061   group "JalviewJS"
3062   description "Delete all configuration and build artifacts to do with JalviewJS build"
3063   dependsOn cleanJalviewjsSite
3064   dependsOn jalviewjsEclipsePaths
3065   
3066   doFirst {
3067     delete "${jalviewDir}/${jalviewjsBuildDir}"
3068     delete "${jalviewDir}/${eclipse_bin_dir}"
3069     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
3070       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
3071     }
3072     delete jalviewjsJ2sAltSettingsFileName
3073   }
3074
3075   outputs.upToDateWhen( { false } )
3076 }
3077
3078
3079 task jalviewjsIDE_checkJ2sPlugin {
3080   group "00 JalviewJS in Eclipse"
3081   description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
3082
3083   doFirst {
3084     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
3085     def j2sPluginFile = file(j2sPlugin)
3086     def eclipseHome = System.properties["eclipse.home.location"]
3087     if (eclipseHome == null || ! IN_ECLIPSE) {
3088       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
3089     }
3090     def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
3091     def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
3092     if (altPluginsDir != null && file(altPluginsDir).exists()) {
3093       eclipseJ2sPluginDirs += altPluginsDir
3094     }
3095     def foundPlugin = false
3096     def j2sPluginFileName = j2sPluginFile.getName()
3097     def eclipseJ2sPlugin
3098     def eclipseJ2sPluginFile
3099     eclipseJ2sPluginDirs.any { dir ->
3100       eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
3101       eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
3102       if (eclipseJ2sPluginFile.exists()) {
3103         foundPlugin = true
3104         return true
3105       }
3106     }
3107     if (!foundPlugin) {
3108       def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
3109       System.err.println(msg)
3110       throw new StopExecutionException(msg)
3111     }
3112
3113     def digest = MessageDigest.getInstance("MD5")
3114
3115     digest.update(j2sPluginFile.text.bytes)
3116     def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
3117
3118     digest.update(eclipseJ2sPluginFile.text.bytes)
3119     def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
3120      
3121     if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
3122       def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
3123       System.err.println(msg)
3124       throw new StopExecutionException(msg)
3125     } else {
3126       def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
3127       println(msg)
3128     }
3129   }
3130 }
3131
3132 task jalviewjsIDE_copyJ2sPlugin {
3133   group "00 JalviewJS in Eclipse"
3134   description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
3135
3136   doFirst {
3137     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
3138     def j2sPluginFile = file(j2sPlugin)
3139     def eclipseHome = System.properties["eclipse.home.location"]
3140     if (eclipseHome == null || ! IN_ECLIPSE) {
3141       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
3142     }
3143     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
3144     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
3145     def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
3146     System.err.println(msg)
3147     copy {
3148       from j2sPlugin
3149       eclipseJ2sPluginFile.getParentFile().mkdirs()
3150       into eclipseJ2sPluginFile.getParent()
3151     }
3152   }
3153 }
3154
3155
3156 task jalviewjsIDE_j2sFile {
3157   group "00 JalviewJS in Eclipse"
3158   description "Creates the .j2s file"
3159   dependsOn jalviewjsCreateJ2sSettings
3160 }
3161
3162
3163 task jalviewjsIDE_SyncCore {
3164   group "00 JalviewJS in Eclipse"
3165   description "Build the core js lib closures listed in the classlists dir and publish core html from template"
3166   dependsOn jalviewjsSyncCore
3167 }
3168
3169
3170 task jalviewjsIDE_SyncSiteAll {
3171   dependsOn jalviewjsSyncAllLibs
3172   dependsOn jalviewjsSyncResources
3173   dependsOn jalviewjsSyncSiteResources
3174   dependsOn jalviewjsSyncBuildProperties
3175 }
3176
3177
3178 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
3179
3180
3181 task jalviewjsIDE_PrepareSite {
3182   group "00 JalviewJS in Eclipse"
3183   description "Sync libs and resources to site dir, but not closure cores"
3184
3185   dependsOn jalviewjsIDE_SyncSiteAll
3186   //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
3187 }
3188
3189
3190 task jalviewjsIDE_AssembleSite {
3191   group "00 JalviewJS in Eclipse"
3192   description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
3193   dependsOn jalviewjsPrepareSite
3194 }
3195
3196
3197 task jalviewjsIDE_SiteClean {
3198   group "00 JalviewJS in Eclipse"
3199   description "Deletes the Eclipse transpiled site"
3200   dependsOn cleanJalviewjsSite
3201 }
3202
3203
3204 task jalviewjsIDE_Server {
3205   group "00 JalviewJS in Eclipse"
3206   description "Starts a webserver on localhost to test the website"
3207   dependsOn jalviewjsServer
3208 }
3209
3210
3211 // buildship runs this at import or gradle refresh
3212 task eclipseSynchronizationTask {
3213   //dependsOn eclipseSetup
3214   dependsOn createBuildProperties
3215   if (J2S_ENABLED) {
3216     dependsOn jalviewjsIDE_j2sFile
3217     dependsOn jalviewjsIDE_checkJ2sPlugin
3218     dependsOn jalviewjsIDE_PrepareSite
3219   }
3220 }
3221
3222
3223 // buildship runs this at build time or project refresh
3224 task eclipseAutoBuildTask {
3225   //dependsOn jalviewjsIDE_checkJ2sPlugin
3226   //dependsOn jalviewjsIDE_PrepareSite
3227 }
3228
3229
3230 task jalviewjs {
3231   group "JalviewJS"
3232   description "Build the site"
3233   dependsOn jalviewjsBuildSite
3234 }