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