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