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