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