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