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