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