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