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