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