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