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