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