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