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