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