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