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