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