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