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