JAL-3225 small fixes for building within Eclipse
[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
996   doFirst {
997     def hashStdOut = new ByteArrayOutputStream()
998     def resultHash = exec {
999       commandLine "git", "rev-parse", "--short", "HEAD"
1000       standardOutput = hashStdOut
1001       ignoreExitValue true
1002     }
1003
1004     def branchStdOut = new ByteArrayOutputStream()
1005     def resultBranch = exec {
1006       commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
1007       standardOutput = branchStdOut
1008       ignoreExitValue true
1009     }
1010
1011     gitHash = resultHash.getExitValue() == 0 ? hashStdOut.toString().trim() : "NO_GIT_COMMITID_FOUND"
1012     gitBranch = resultBranch.getExitValue() == 0 ? branchStdOut.toString().trim() : "NO_GIT_BRANCH_FOUND"
1013   }
1014
1015   outputs.upToDateWhen { false }
1016 }
1017
1018
1019 task createBuildProperties(type: WriteProperties) {
1020   group = "build"
1021   description = "Create the ${buildProperties} file"
1022   
1023   dependsOn setGitVals
1024   inputs.dir(sourceDir)
1025   inputs.dir(resourceDir)
1026   file(buildProperties).getParentFile().mkdirs()
1027   outputFile (buildProperties)
1028   // taking time specific comment out to allow better incremental builds
1029   comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1030   //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1031   property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1032   property "VERSION", JALVIEW_VERSION
1033   property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1034   outputs.file(outputFile)
1035 }
1036
1037
1038 clean {
1039   doFirst {
1040     delete buildProperties
1041   }
1042 }
1043
1044
1045 task cleanBuildingHTML(type: Delete) {
1046   doFirst {
1047     delete buildingHTML
1048   }
1049 }
1050
1051
1052 task convertBuildingMD(type: Exec) {
1053   dependsOn cleanBuildingHTML
1054   def buildingMD = "${jalviewDir}/${doc_dir}/building.md"
1055   def css = "${jalviewDir}/${doc_dir}/github.css"
1056
1057   def pandoc = null
1058   pandoc_exec.split(",").each {
1059     if (file(it.trim()).exists()) {
1060       pandoc = it.trim()
1061       return true
1062     }
1063   }
1064
1065   def buildtoolsPandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
1066   if ((pandoc == null || ! file(pandoc).exists()) && file(buildtoolsPandoc).exists()) {
1067     pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
1068   }
1069
1070   doFirst {
1071     if (pandoc != null && file(pandoc).exists()) {
1072         commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
1073     } else {
1074         println("Cannot find pandoc. Skipping convert building.md to HTML")
1075         throw new StopExecutionException("Cannot find pandoc. Skipping convert building.md to HTML")
1076     }
1077   }
1078
1079   ignoreExitValue true
1080
1081   inputs.file(buildingMD)
1082   inputs.file(css)
1083   outputs.file(buildingHTML)
1084 }
1085
1086
1087 task syncDocs(type: Sync) {
1088   //dependsOn convertBuildingMD
1089   def syncDir = "${classesDir}/${doc_dir}"
1090   from fileTree("${jalviewDir}/${doc_dir}")
1091   into syncDir
1092
1093 }
1094
1095
1096 task copyHelp(type: Copy) {
1097   def inputDir = helpSourceDir
1098   def outputDir = "${resourceClassesDir}/${help_dir}"
1099   from(inputDir) {
1100     exclude '**/*.gif'
1101     exclude '**/*.jpg'
1102     exclude '**/*.png'
1103     filter(ReplaceTokens,
1104       beginToken: '$$',
1105       endToken: '$$',
1106       tokens: [
1107         'Version-Rel': JALVIEW_VERSION,
1108         'Year-Rel': getDate("yyyy")
1109       ]
1110     )
1111   }
1112   from(inputDir) {
1113     include '**/*.gif'
1114     include '**/*.jpg'
1115     include '**/*.png'
1116   }
1117   into outputDir
1118
1119   inputs.dir(inputDir)
1120   outputs.files(helpFile)
1121   outputs.dir(outputDir)
1122 }
1123
1124
1125 task syncLib(type: Sync) {
1126   def syncDir = "${resourceClassesDir}/${libDistDir}"
1127   from fileTree("${jalviewDir}/${libDistDir}")
1128   into syncDir
1129 }
1130
1131
1132 task syncResources(type: Sync) {
1133   dependsOn createBuildProperties
1134   from resourceDir
1135   include "**/*.*"
1136   into "${resourceClassesDir}"
1137   preserve {
1138     include "**"
1139   }
1140 }
1141
1142
1143 task prepare {
1144   dependsOn syncResources
1145   dependsOn syncDocs
1146   dependsOn copyHelp
1147 }
1148
1149
1150 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
1151 test {
1152   dependsOn prepare
1153   //dependsOn compileJava ////? DELETE
1154
1155   if (useClover) {
1156     dependsOn cloverClasses
1157    } else { //?
1158      dependsOn compileJava //?
1159   }
1160
1161   useTestNG() {
1162     includeGroups testng_groups
1163     excludeGroups testng_excluded_groups
1164     preserveOrder true
1165     useDefaultListeners=true
1166   }
1167
1168   maxHeapSize = "1024m"
1169
1170   workingDir = jalviewDir
1171   //systemProperties 'clover.jar' System.properties.clover.jar
1172   sourceCompatibility = compile_source_compatibility
1173   targetCompatibility = compile_target_compatibility
1174   jvmArgs += additional_compiler_args
1175
1176   doFirst {
1177     if (useClover) {
1178       println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1179     }
1180   }
1181 }
1182
1183
1184 task buildIndices(type: JavaExec) {
1185   dependsOn copyHelp
1186   classpath = sourceSets.main.compileClasspath
1187   main = "com.sun.java.help.search.Indexer"
1188   workingDir = "${classesDir}/${help_dir}"
1189   def argDir = "html"
1190   args = [ argDir ]
1191   inputs.dir("${workingDir}/${argDir}")
1192
1193   outputs.dir("${classesDir}/doc")
1194   outputs.dir("${classesDir}/help")
1195   outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1196   outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1197   outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1198   outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1199   outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1200   outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1201 }
1202
1203
1204 task compileLinkCheck(type: JavaCompile) {
1205   options.fork = true
1206   classpath = files("${jalviewDir}/${utils_dir}")
1207   destinationDir = file("${jalviewDir}/${utils_dir}")
1208   source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
1209
1210   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1211   inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1212   outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
1213   outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
1214 }
1215
1216
1217 task linkCheck(type: JavaExec) {
1218   dependsOn prepare, compileLinkCheck
1219
1220   def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
1221   classpath = files("${jalviewDir}/${utils_dir}")
1222   main = "HelpLinksChecker"
1223   workingDir = jalviewDir
1224   args = [ "${classesDir}/${help_dir}", "-nointernet" ]
1225
1226   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
1227   def errFOS = outFOS
1228   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1229     outFOS,
1230     standardOutput)
1231   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1232     outFOS,
1233     errorOutput)
1234
1235   inputs.dir("${classesDir}/${help_dir}")
1236   outputs.file(helpLinksCheckerOutFile)
1237 }
1238
1239 // import the pubhtmlhelp target
1240 ant.properties.basedir = "${jalviewDir}"
1241 ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${help_dir}"
1242 ant.importBuild "${utils_dir}/publishHelp.xml"
1243
1244
1245 task cleanPackageDir(type: Delete) {
1246   doFirst {
1247     delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
1248   }
1249 }
1250
1251
1252 jar {
1253   dependsOn linkCheck
1254   dependsOn buildIndices
1255   dependsOn createBuildProperties
1256
1257   manifest {
1258     attributes "Main-Class": main_class,
1259     "Permissions": "all-permissions",
1260     "Application-Name": "Jalview Desktop",
1261     "Codebase": application_codebase
1262   }
1263
1264   destinationDir = file("${jalviewDir}/${package_dir}")
1265   archiveName = rootProject.name+".jar"
1266
1267   exclude "cache*/**"
1268   exclude "*.jar"
1269   exclude "*.jar.*"
1270   exclude "**/*.jar"
1271   exclude "**/*.jar.*"
1272
1273   inputs.dir(classesDir)
1274   outputs.file("${jalviewDir}/${package_dir}/${archiveName}")
1275 }
1276
1277
1278 task copyJars(type: Copy) {
1279   from fileTree(dir: classesDir, include: "**/*.jar").files
1280   into "${jalviewDir}/${package_dir}"
1281 }
1282
1283
1284 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
1285 task syncJars(type: Sync) {
1286   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
1287   into "${jalviewDir}/${package_dir}"
1288   preserve {
1289     include jar.archiveName
1290   }
1291 }
1292
1293
1294 task makeDist {
1295   group = "build"
1296   description = "Put all required libraries in dist"
1297   // order of "cleanPackageDir", "copyJars", "jar" important!
1298   jar.mustRunAfter cleanPackageDir
1299   syncJars.mustRunAfter cleanPackageDir
1300   dependsOn cleanPackageDir
1301   dependsOn syncJars
1302   dependsOn jar
1303   outputs.dir("${jalviewDir}/${package_dir}")
1304 }
1305
1306
1307 task cleanDist {
1308   dependsOn cleanPackageDir
1309   dependsOn cleanTest
1310   dependsOn clean
1311 }
1312
1313 shadowJar {
1314   group = "distribution"
1315   description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
1316   if (buildDist) {
1317     dependsOn makeDist
1318   }
1319   from ("${jalviewDir}/${libDistDir}") {
1320     include("*.jar")
1321   }
1322   manifest {
1323     attributes 'Implementation-Version': JALVIEW_VERSION
1324   }
1325   mainClassName = shadow_jar_main_class
1326   mergeServiceFiles()
1327   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
1328   minimize()
1329 }
1330
1331
1332 task getdownWebsite() {
1333   group = "distribution"
1334   description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
1335   if (buildDist) {
1336     dependsOn makeDist
1337   }
1338
1339   def getdownWebsiteResourceFilenames = []
1340   def getdownTextString = ""
1341   def getdownResourceDir = getdownResourceDir
1342   def getdownResourceFilenames = []
1343
1344   doFirst {
1345     // clean the getdown website and files dir before creating getdown folders
1346     delete getdownWebsiteDir
1347     delete getdownFilesDir
1348
1349     copy {
1350       from buildProperties
1351       rename(build_properties_file, getdown_build_properties)
1352       into getdownAppDir
1353     }
1354     getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
1355
1356     // set some getdown_txt_ properties then go through all properties looking for getdown_txt_...
1357     def props = project.properties.sort { it.key }
1358     if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
1359       props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
1360     }
1361     if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
1362       props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
1363     }
1364     if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
1365       props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
1366     }
1367
1368     props.put("getdown_txt_title", jalview_name)
1369     props.put("getdown_txt_ui.name", install4jApplicationName)
1370
1371     // start with appbase
1372     getdownTextString += "appbase = ${getdownAppBase}\n"
1373     props.each{ prop, val ->
1374       if (prop.startsWith("getdown_txt_") && val != null) {
1375         if (prop.startsWith("getdown_txt_multi_")) {
1376           def key = prop.substring(18)
1377           val.split(",").each{ v ->
1378             def line = "${key} = ${v}\n"
1379             getdownTextString += line
1380           }
1381         } else {
1382           // file values rationalised
1383           if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
1384             def r = null
1385             if (val.indexOf('/') == 0) {
1386               // absolute path
1387               r = file(val)
1388             } else if (val.indexOf('/') > 0) {
1389               // relative path (relative to jalviewDir)
1390               r = file( "${jalviewDir}/${val}" )
1391             }
1392             if (r.exists()) {
1393               val = "${getdown_resource_dir}/" + r.getName()
1394               getdownWebsiteResourceFilenames += val
1395               getdownResourceFilenames += r.getPath()
1396             }
1397           }
1398           if (! prop.startsWith("getdown_txt_resource")) {
1399             def line = prop.substring(12) + " = ${val}\n"
1400             getdownTextString += line
1401           }
1402         }
1403       }
1404     }
1405
1406     getdownWebsiteResourceFilenames.each{ filename ->
1407       getdownTextString += "resource = ${filename}\n"
1408     }
1409     getdownResourceFilenames.each{ filename ->
1410       copy {
1411         from filename
1412         into getdownResourceDir
1413       }
1414     }
1415
1416     def codeFiles = []
1417     fileTree(file(package_dir)).each{ f ->
1418       if (f.isDirectory()) {
1419         def files = fileTree(dir: f, include: ["*"]).getFiles()
1420         codeFiles += files
1421       } else if (f.exists()) {
1422         codeFiles += f
1423       }
1424     }
1425     codeFiles.sort().each{f ->
1426       def name = f.getName()
1427       def line = "code = ${getdownAppDistDir}/${name}\n"
1428       getdownTextString += line
1429       copy {
1430         from f.getPath()
1431         into getdownAppDir
1432       }
1433     }
1434
1435     // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
1436     /*
1437     if (JAVA_VERSION.equals("11")) {
1438     def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
1439     j11libFiles.sort().each{f ->
1440     def name = f.getName()
1441     def line = "code = ${getdown_j11lib_dir}/${name}\n"
1442     getdownTextString += line
1443     copy {
1444     from f.getPath()
1445     into getdownJ11libDir
1446     }
1447     }
1448     }
1449      */
1450
1451     // 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.
1452     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
1453     getdownTextString += "resource = ${getdown_launcher_new}\n"
1454     getdownTextString += "class = ${main_class}\n"
1455
1456     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
1457     getdown_txt.write(getdownTextString)
1458
1459     def getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
1460     def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
1461     launchJvl.write("appbase=${getdownAppBase}")
1462
1463     copy {
1464       from getdownLauncher
1465       rename(file(getdownLauncher).getName(), getdown_launcher_new)
1466       into getdownWebsiteDir
1467     }
1468
1469     copy {
1470       from getdownLauncher
1471       if (file(getdownLauncher).getName() != getdown_launcher) {
1472         rename(file(getdownLauncher).getName(), getdown_launcher)
1473       }
1474       into getdownWebsiteDir
1475     }
1476
1477     if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
1478       copy {
1479         from getdown_txt
1480         from getdownLauncher
1481         from "${getdownWebsiteDir}/${getdown_build_properties}"
1482         if (file(getdownLauncher).getName() != getdown_launcher) {
1483           rename(file(getdownLauncher).getName(), getdown_launcher)
1484         }
1485         into getdownInstallDir
1486       }
1487
1488       copy {
1489         from getdownInstallDir
1490         into getdownFilesInstallDir
1491       }
1492     }
1493
1494     copy {
1495       from getdown_txt
1496       from launchJvl
1497       from getdownLauncher
1498       from "${getdownWebsiteDir}/${getdown_build_properties}"
1499       if (file(getdownLauncher).getName() != getdown_launcher) {
1500         rename(file(getdownLauncher).getName(), getdown_launcher)
1501       }
1502       into getdownFilesDir
1503     }
1504
1505     copy {
1506       from getdownResourceDir
1507       into "${getdownFilesDir}/${getdown_resource_dir}"
1508     }
1509   }
1510
1511   if (buildDist) {
1512     inputs.dir("${jalviewDir}/${package_dir}")
1513   }
1514   outputs.dir(getdownWebsiteDir)
1515   outputs.dir(getdownFilesDir)
1516 }
1517
1518
1519 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
1520 task getdownDigestDir(type: JavaExec) {
1521   group "Help"
1522   description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
1523
1524   def digestDirPropertyName = "DIGESTDIR"
1525   doFirst {
1526     classpath = files(getdownLauncher)
1527     def digestDir = findProperty(digestDirPropertyName)
1528     if (digestDir == null) {
1529       throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
1530     }
1531     args digestDir
1532   }
1533   main = "com.threerings.getdown.tools.Digester"
1534 }
1535
1536
1537 task getdownDigest(type: JavaExec) {
1538   group = "distribution"
1539   description = "Digest the getdown website folder"
1540   dependsOn getdownWebsite
1541   doFirst {
1542     classpath = files(getdownLauncher)
1543   }
1544   main = "com.threerings.getdown.tools.Digester"
1545   args getdownWebsiteDir
1546   inputs.dir(getdownWebsiteDir)
1547   outputs.file("${getdownWebsiteDir}/digest2.txt")
1548 }
1549
1550
1551 task getdown() {
1552   group = "distribution"
1553   description = "Create the minimal and full getdown app folder for installers and website and create digest file"
1554   dependsOn getdownDigest
1555   doLast {
1556     if (reportRsyncCommand) {
1557       def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
1558       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
1559       println "LIKELY RSYNC COMMAND:"
1560       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
1561       if (RUNRSYNC == "true") {
1562         exec {
1563           commandLine "mkdir", "-p", toDir
1564         }
1565         exec {
1566           commandLine "rsync", "-avh", "--delete", fromDir, toDir
1567         }
1568       }
1569     }
1570   }
1571 }
1572
1573
1574 tasks.withType(JavaCompile) {
1575         options.encoding = 'UTF-8'
1576 }
1577
1578
1579 clean {
1580   doFirst {
1581     delete getdownWebsiteDir
1582     delete getdownFilesDir
1583   }
1584 }
1585
1586
1587 install4j {
1588   if (file(install4jHomeDir).exists()) {
1589     // good to go!
1590   } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
1591     install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
1592   } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
1593     install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
1594   }
1595   installDir(file(install4jHomeDir))
1596
1597   mediaTypes = Arrays.asList(install4j_media_types.split(","))
1598 }
1599
1600
1601 task copyInstall4jTemplate {
1602   def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
1603   def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
1604   inputs.file(install4jTemplateFile)
1605   inputs.file(install4jFileAssociationsFile)
1606   inputs.property("CHANNEL", { CHANNEL })
1607   outputs.file(install4jConfFile)
1608
1609   doLast {
1610     def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
1611
1612     // turn off code signing if no OSX_KEYPASS
1613     if (OSX_KEYPASS == "") {
1614       install4jConfigXml.'**'.codeSigning.each { codeSigning ->
1615         codeSigning.'@macEnabled' = "false"
1616       }
1617       install4jConfigXml.'**'.windows.each { windows ->
1618         windows.'@runPostProcessor' = "false"
1619       }
1620     }
1621
1622     // turn off checksum creation for LOCAL channel
1623     def e = install4jConfigXml.application[0]
1624     if (CHANNEL == "LOCAL") {
1625       e.'@createChecksums' = "false"
1626     } else {
1627       e.'@createChecksums' = "true"
1628     }
1629
1630     // put file association actions where placeholder action is
1631     def install4jFileAssociationsText = install4jFileAssociationsFile.text
1632     def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
1633     install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
1634       if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
1635         def parent = a.parent()
1636         parent.remove(a)
1637         fileAssociationActions.each { faa ->
1638             parent.append(faa)
1639         }
1640         // don't need to continue in .any loop once replacements have been made
1641         return true
1642       }
1643     }
1644
1645     // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
1646     // NB we're deleting the /other/ one!
1647     // Also remove the examples subdir from non-release versions
1648     def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
1649     // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
1650     if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
1651       customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
1652     } else {
1653       // remove the examples subdir from Full File Set
1654       def files = install4jConfigXml.files[0]
1655       def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
1656       def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
1657       def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
1658       def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
1659       dirEntry.parent().remove(dirEntry)
1660     }
1661     install4jConfigXml.'**'.action.any { a ->
1662       if (a.'@customizedId' == customizedIdToDelete) {
1663         def parent = a.parent()
1664         parent.remove(a)
1665         return true
1666       }
1667     }
1668
1669     // remove the "Uninstall Old Jalview (optional)" symlink from DMG for non-release DS_Stores
1670     if (! (CHANNEL == "RELEASE" || CHANNEL == "TEST-RELEASE" ) ) {
1671       def symlink = install4jConfigXml.'**'.topLevelFiles.symlink.find { sl -> sl.'@name' == "Uninstall Old Jalview (optional).app" }
1672       symlink.parent().remove(symlink)
1673     }
1674
1675     // write install4j file
1676     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
1677   }
1678 }
1679
1680
1681 clean {
1682   doFirst {
1683     delete install4jConfFile
1684   }
1685 }
1686
1687
1688 task installers(type: com.install4j.gradle.Install4jTask) {
1689   group = "distribution"
1690   description = "Create the install4j installers"
1691   dependsOn setGitVals
1692   dependsOn getdown
1693   dependsOn copyInstall4jTemplate
1694
1695   projectFile = install4jConfFile
1696
1697   // create an md5 for the input files to use as version for install4j conf file
1698   def digest = MessageDigest.getInstance("MD5")
1699   digest.update(
1700     (file("${install4jDir}/${install4j_template}").text + 
1701     file("${install4jDir}/${install4j_info_plist_file_associations}").text +
1702     file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
1703   def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
1704   if (filesMd5.length() >= 8) {
1705     filesMd5 = filesMd5.substring(0,8)
1706   }
1707   def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
1708   // make install4jBuildDir relative to jalviewDir
1709   def install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
1710
1711   variables = [
1712     'JALVIEW_NAME': jalview_name,
1713     'JALVIEW_APPLICATION_NAME': install4jApplicationName,
1714     'JALVIEW_DIR': "../..",
1715     'OSX_KEYSTORE': OSX_KEYSTORE,
1716     'JSIGN_SH': JSIGN_SH,
1717     'JRE_DIR': getdown_app_dir_java,
1718     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
1719     'JALVIEW_VERSION': JALVIEW_VERSION,
1720     'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
1721     'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
1722     'JAVA_VERSION': JAVA_VERSION,
1723     'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
1724     'VERSION': JALVIEW_VERSION,
1725     'MACOS_JAVA_VM_DIR': macosJavaVMDir,
1726     'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
1727     'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
1728     'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
1729     'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
1730     'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
1731     'COPYRIGHT_MESSAGE': install4j_copyright_message,
1732     'BUNDLE_ID': install4jBundleId,
1733     'INTERNAL_ID': install4jInternalId,
1734     'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
1735     'MACOS_DS_STORE': install4jDSStore,
1736     'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
1737     'INSTALLER_NAME': install4jInstallerName,
1738     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
1739     'GETDOWN_WEBSITE_DIR': getdown_website_dir,
1740     'GETDOWN_FILES_DIR': getdown_files_dir,
1741     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
1742     'GETDOWN_DIST_DIR': getdownAppDistDir,
1743     'GETDOWN_ALT_DIR': getdown_app_dir_alt,
1744     'GETDOWN_INSTALL_DIR': getdown_install_dir,
1745     'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
1746     'BUILD_DIR': install4jBuildDir,
1747     'APPLICATION_CATEGORIES': install4j_application_categories,
1748     'APPLICATION_FOLDER': install4jApplicationFolder,
1749     'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
1750     'EXECUTABLE_NAME': install4jExecutableName,
1751     'EXTRA_SCHEME': install4jExtraScheme,
1752   ]
1753
1754   //println("INSTALL4J VARIABLES:")
1755   //variables.each{k,v->println("${k}=${v}")}
1756
1757   destination = "${jalviewDir}/${install4jBuildDir}"
1758   buildSelected = true
1759
1760   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
1761     faster = true
1762     disableSigning = true
1763   }
1764
1765   if (OSX_KEYPASS) {
1766     macKeystorePassword = OSX_KEYPASS
1767   }
1768
1769   doFirst {
1770     println("Using projectFile "+projectFile)
1771   }
1772
1773   inputs.dir(getdownWebsiteDir)
1774   inputs.file(install4jConfFile)
1775   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
1776   inputs.dir(macosJavaVMDir)
1777   inputs.dir(windowsJavaVMDir)
1778   outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
1779 }
1780
1781
1782 spotless {
1783   java {
1784     eclipse().configFile(eclipse_codestyle_file)
1785   }
1786 }
1787
1788
1789 task sourceDist(type: Tar) {
1790   group "distribution"
1791   description "Create a source .tar.gz file for distribution"
1792   
1793   dependsOn convertBuildingMD
1794
1795   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
1796   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
1797   // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
1798   try {
1799     archiveFileName = outputFileName
1800   } catch (Exception e) {
1801     archiveName = outputFileName
1802   }
1803   
1804   compression Compression.GZIP
1805   
1806   into project.name
1807
1808   def EXCLUDE_FILES=[
1809     "build/*",
1810     "bin/*",
1811     "test-output/",
1812     "test-reports",
1813     "tests",
1814     "clover*/*",
1815     ".*",
1816     "benchmarking/*",
1817     "**/.*",
1818     "*.class",
1819     "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
1820     "*locales/**",
1821     "utils/InstallAnywhere",
1822     "**/*.log",
1823   ] 
1824   def PROCESS_FILES=[
1825     "AUTHORS",
1826     "CITATION",
1827     "FEATURETODO",
1828     "JAVA-11-README",
1829     "FEATURETODO",
1830     "LICENSE",
1831     "**/README",
1832     "RELEASE",
1833     "THIRDPARTYLIBS",
1834     "TESTNG",
1835     "build.gradle",
1836     "gradle.properties",
1837     "**/*.java",
1838     "**/*.html",
1839     "**/*.xml",
1840     "**/*.gradle",
1841     "**/*.groovy",
1842     "**/*.properties",
1843     "**/*.perl",
1844     "**/*.sh",
1845   ]
1846   def INCLUDE_FILES=[
1847     ".settings/org.eclipse.jdt.core.jalview.prefs",
1848   ]
1849
1850   from(jalviewDir) {
1851     exclude (EXCLUDE_FILES)
1852     include (PROCESS_FILES)
1853     filter(ReplaceTokens,
1854       beginToken: '$$',
1855       endToken: '$$',
1856       tokens: [
1857         'Version-Rel': JALVIEW_VERSION,
1858         'Year-Rel': getDate("yyyy")
1859       ]
1860     )
1861   }
1862   from(jalviewDir) {
1863     exclude (EXCLUDE_FILES)
1864     exclude (PROCESS_FILES)
1865     exclude ("appletlib")
1866     exclude ("**/*locales")
1867     exclude ("*locales/**")
1868     exclude ("utils/InstallAnywhere")
1869
1870     exclude (getdown_files_dir)
1871     exclude (getdown_website_dir)
1872
1873     // exluding these as not using jars as modules yet
1874     exclude ("${j11modDir}/**/*.jar")
1875   }
1876   from(jalviewDir) {
1877     include(INCLUDE_FILES)
1878   }
1879 //  from (jalviewDir) {
1880 //    // explicit includes for stuff that seemed to not get included
1881 //    include(fileTree("test/**/*."))
1882 //    exclude(EXCLUDE_FILES)
1883 //    exclude(PROCESS_FILES)
1884 //  }
1885 }
1886
1887
1888 task helppages {
1889   dependsOn copyHelp
1890   dependsOn pubhtmlhelp
1891   
1892   inputs.dir("${classesDir}/${help_dir}")
1893   outputs.dir("${buildDir}/distributions/${help_dir}")
1894 }
1895
1896
1897 task j2sSetHeadlessBuild {
1898   doFirst {
1899     IN_ECLIPSE = false
1900   }
1901 }
1902
1903
1904 task jalviewjsSetEclipseWorkspace {
1905   def propKey = "jalviewjs_eclipse_workspace"
1906   def propVal = null
1907   if (project.hasProperty(propKey)) {
1908     propVal = project.getProperty(propKey)
1909     if (propVal.startsWith("~/")) {
1910       propVal = System.getProperty("user.home") + propVal.substring(1)
1911     }
1912   }
1913   def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
1914   def propsFile = file(propsFileName)
1915   def eclipseWsDir = propVal
1916   def props = new Properties()
1917
1918   def writeProps = true
1919   if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
1920     def ins = new FileInputStream(propsFileName)
1921     props.load(ins)
1922     ins.close()
1923     if (props.getProperty(propKey, null) != null) {
1924       eclipseWsDir = props.getProperty(propKey)
1925       writeProps = false
1926     }
1927   }
1928
1929   if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
1930     def tempDir = File.createTempDir()
1931     eclipseWsDir = tempDir.getAbsolutePath()
1932     writeProps = true
1933   }
1934   eclipseWorkspace = file(eclipseWsDir)
1935
1936   doFirst {
1937     // do not run a headless transpile when we claim to be in Eclipse
1938     if (IN_ECLIPSE) {
1939       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1940       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
1941     } else {
1942       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
1943     }
1944
1945     if (writeProps) {
1946       props.setProperty(propKey, eclipseWsDir)
1947       propsFile.parentFile.mkdirs()
1948       def bytes = new ByteArrayOutputStream()
1949       props.store(bytes, null)
1950       def propertiesString = bytes.toString()
1951       propsFile.text = propertiesString
1952       print("NEW ")
1953     } else {
1954       print("EXISTING ")
1955     }
1956
1957     println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
1958   }
1959
1960   //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
1961   outputs.file(propsFileName)
1962   outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
1963 }
1964
1965
1966 task jalviewjsEclipsePaths {
1967   def eclipseProduct
1968
1969   def eclipseRoot = jalviewjs_eclipse_root
1970   if (eclipseRoot.startsWith("~/")) {
1971     eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
1972   }
1973   if (OperatingSystem.current().isMacOsX()) {
1974     eclipseRoot += "/Eclipse.app"
1975     eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
1976     eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
1977   } else if (OperatingSystem.current().isWindows()) { // check these paths!!
1978     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1979       eclipseRoot += "/eclipse"
1980     }
1981     eclipseBinary = "${eclipseRoot}/eclipse.exe"
1982     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1983   } else { // linux or unix
1984     if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
1985       eclipseRoot += "/eclipse"
1986 println("eclipseDir exists")
1987     }
1988     eclipseBinary = "${eclipseRoot}/eclipse"
1989     eclipseProduct = "${eclipseRoot}/.eclipseproduct"
1990   }
1991
1992   eclipseVersion = "4.13" // default
1993   def assumedVersion = true
1994   if (file(eclipseProduct).exists()) {
1995     def fis = new FileInputStream(eclipseProduct)
1996     def props = new Properties()
1997     props.load(fis)
1998     eclipseVersion = props.getProperty("version")
1999     fis.close()
2000     assumedVersion = false
2001   }
2002   
2003   def propKey = "eclipse_debug"
2004   eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
2005
2006   doFirst {
2007     // do not run a headless transpile when we claim to be in Eclipse
2008     if (IN_ECLIPSE) {
2009       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2010       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2011     } else {
2012       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2013     }
2014
2015     if (!assumedVersion) {
2016       println("ECLIPSE VERSION=${eclipseVersion}")
2017     }
2018   }
2019 }
2020
2021
2022 task printProperties {
2023   group "Debug"
2024   description "Output to console all System.properties"
2025   doFirst {
2026     System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
2027   }
2028 }
2029
2030
2031 task eclipseSetup {
2032   dependsOn eclipseProject
2033   dependsOn eclipseClasspath
2034   dependsOn eclipseJdt
2035 }
2036
2037
2038 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
2039 task jalviewjsEclipseCopyDropins(type: Copy) {
2040   dependsOn jalviewjsEclipsePaths
2041
2042   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
2043   inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
2044   def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
2045
2046   from inputFiles
2047   into outputDir
2048 }
2049
2050
2051 // this eclipse -clean doesn't actually work
2052 task jalviewjsCleanEclipse(type: Exec) {
2053   dependsOn eclipseSetup
2054   dependsOn jalviewjsEclipsePaths
2055   dependsOn jalviewjsEclipseCopyDropins
2056
2057   executable(eclipseBinary)
2058   args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
2059   if (eclipseDebug) {
2060     args += "-debug"
2061   }
2062   args += "-l"
2063
2064   def inputString = """exit
2065 y
2066 """
2067   def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
2068   standardInput = inputByteStream
2069 }
2070
2071 /* not really working yet
2072 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
2073 */
2074
2075
2076 task jalviewjsTransferUnzipSwingJs {
2077   def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
2078
2079   doLast {
2080     copy {
2081       from zipTree(file_zip)
2082       into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2083     }
2084   }
2085
2086   inputs.file file_zip
2087   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2088 }
2089
2090
2091 task jalviewjsTransferUnzipLib {
2092   def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
2093
2094   doLast {
2095     zipFiles.each { file_zip -> 
2096       copy {
2097         from zipTree(file_zip)
2098         into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2099       }
2100     }
2101   }
2102
2103   inputs.files zipFiles
2104   outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2105 }
2106
2107
2108 task jalviewjsTransferUnzipAllLibs {
2109   dependsOn jalviewjsTransferUnzipSwingJs
2110   dependsOn jalviewjsTransferUnzipLib
2111 }
2112
2113
2114 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
2115   group "JalviewJS"
2116   description "Create the .j2s file from the j2s.* properties"
2117
2118   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
2119   def siteDirProperty = "j2s.site.directory"
2120   def setSiteDir = false
2121   jalviewjsJ2sProps.each { prop, val ->
2122     if (val != null) {
2123       if (prop == siteDirProperty) {
2124         if (!(val.startsWith('/') || val.startsWith("file://") )) {
2125           val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
2126         }
2127         setSiteDir = true
2128       }
2129       property(prop,val)
2130     }
2131     if (!setSiteDir) { // default site location, don't override specifically set property
2132       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
2133     }
2134   }
2135   outputFile = jalviewjsJ2sSettingsFileName
2136
2137   if (! IN_ECLIPSE) {
2138     inputs.properties(jalviewjsJ2sProps)
2139     outputs.file(jalviewjsJ2sSettingsFileName)
2140   }
2141 }
2142
2143
2144 task jalviewjsEclipseSetup {
2145   dependsOn jalviewjsEclipseCopyDropins
2146   dependsOn jalviewjsSetEclipseWorkspace
2147   dependsOn jalviewjsCreateJ2sSettings
2148 }
2149
2150
2151 task jalviewjsSyncAllLibs (type: Sync) {
2152   dependsOn jalviewjsTransferUnzipAllLibs
2153   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
2154   inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
2155   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2156
2157   from inputFiles
2158   into outputDir
2159   def outputFiles = []
2160   rename { filename ->
2161     outputFiles += "${outputDir}/${filename}"
2162     null
2163   }
2164   preserve {
2165     include "**"
2166   }
2167   outputs.files outputFiles
2168   inputs.files inputFiles
2169 }
2170
2171
2172 task jalviewjsSyncResources (type: Sync) {
2173   def inputFiles = fileTree(dir: resourceDir)
2174   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
2175
2176   from inputFiles
2177   into outputDir
2178   def outputFiles = []
2179   rename { filename ->
2180     outputFiles += "${outputDir}/${filename}"
2181     null
2182   }
2183   preserve {
2184     include "**"
2185   }
2186   outputs.files outputFiles
2187   inputs.files inputFiles
2188 }
2189
2190
2191 task jalviewjsSyncSiteResources (type: Sync) {
2192   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
2193   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2194
2195   from inputFiles
2196   into outputDir
2197   def outputFiles = []
2198   rename { filename ->
2199     outputFiles += "${outputDir}/${filename}"
2200     null
2201   }
2202   preserve {
2203     include "**"
2204   }
2205   outputs.files outputFiles
2206   inputs.files inputFiles
2207 }
2208
2209
2210 task jalviewjsSyncBuildProperties (type: Sync) {
2211   dependsOn createBuildProperties
2212   def inputFiles = [file(buildProperties)]
2213   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
2214
2215   from inputFiles
2216   into outputDir
2217   def outputFiles = []
2218   rename { filename ->
2219     outputFiles += "${outputDir}/${filename}"
2220     null
2221   }
2222   preserve {
2223     include "**"
2224   }
2225   outputs.files outputFiles
2226   inputs.files inputFiles
2227 }
2228
2229
2230 task jalviewjsProjectImport(type: Exec) {
2231   dependsOn eclipseSetup
2232   dependsOn jalviewjsEclipsePaths
2233   dependsOn jalviewjsEclipseSetup
2234
2235   doFirst {
2236     // do not run a headless import when we claim to be in Eclipse
2237     if (IN_ECLIPSE) {
2238       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2239       throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2240     } else {
2241       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2242     }
2243   }
2244
2245   //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
2246   def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
2247   executable(eclipseBinary)
2248   args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
2249   if (eclipseDebug) {
2250     args += "-debug"
2251   }
2252   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
2253   if (!IN_ECLIPSE) {
2254     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
2255   }
2256
2257   inputs.file("${jalviewDir}/.project")
2258   outputs.upToDateWhen { 
2259     file(projdir).exists()
2260   }
2261 }
2262
2263
2264 task jalviewjsTranspile(type: Exec) {
2265   dependsOn jalviewjsEclipseSetup 
2266   dependsOn jalviewjsProjectImport
2267   dependsOn jalviewjsEclipsePaths
2268
2269   doFirst {
2270     // do not run a headless transpile when we claim to be in Eclipse
2271     if (IN_ECLIPSE) {
2272       println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2273       throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
2274     } else {
2275       println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
2276     }
2277   }
2278
2279   executable(eclipseBinary)
2280   args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
2281   if (eclipseDebug) {
2282     args += "-debug"
2283   }
2284   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
2285   if (!IN_ECLIPSE) {
2286     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
2287   }
2288
2289   def stdout
2290   def stderr
2291   doFirst {
2292     stdout = new ByteArrayOutputStream()
2293     stderr = new ByteArrayOutputStream()
2294
2295     def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
2296     def logOutFile = file(logOutFileName)
2297     logOutFile.createNewFile()
2298     logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
2299 BINARY: ${eclipseBinary}
2300 VERSION: ${eclipseVersion}
2301 WORKSPACE: ${eclipseWorkspace}
2302 DEBUG: ${eclipseDebug}
2303 ----
2304 """
2305     def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
2306     // combine stdout and stderr
2307     def logErrFOS = logOutFOS
2308
2309     if (jalviewjs_j2s_to_console.equals("true")) {
2310       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2311         new org.apache.tools.ant.util.TeeOutputStream(
2312           logOutFOS,
2313           stdout),
2314         standardOutput)
2315       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2316         new org.apache.tools.ant.util.TeeOutputStream(
2317           logErrFOS,
2318           stderr),
2319         errorOutput)
2320     } else {
2321       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2322         logOutFOS,
2323         stdout)
2324       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2325         logErrFOS,
2326         stderr)
2327     }
2328   }
2329
2330   doLast {
2331     if (stdout.toString().contains("Error processing ")) {
2332       // j2s did not complete transpile
2333       //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
2334       if (jalviewjs_ignore_transpile_errors.equals("true")) {
2335         println("IGNORING TRANSPILE ERRORS")
2336         println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
2337       } else {
2338         throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
2339       }
2340     }
2341   }
2342
2343   inputs.dir("${jalviewDir}/${sourceDir}")
2344   outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
2345   outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
2346 }
2347
2348
2349 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
2350
2351   def stdout = new ByteArrayOutputStream()
2352   def stderr = new ByteArrayOutputStream()
2353
2354   def coreFile = file(jsfile)
2355   def msg = ""
2356   msg = "Creating core for ${name}...\nGenerating ${jsfile}"
2357   println(msg)
2358   logOutFile.createNewFile()
2359   logOutFile.append(msg+"\n")
2360
2361   def coreTop = file(prefixFile)
2362   def coreBottom = file(suffixFile)
2363   coreFile.getParentFile().mkdirs()
2364   coreFile.createNewFile()
2365   coreFile.write( coreTop.getText("UTF-8") )
2366   list.each {
2367     f ->
2368     if (f.exists()) {
2369       def t = f.getText("UTF-8")
2370       t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
2371       coreFile.append( t )
2372     } else {
2373       msg = "...file '"+f.getPath()+"' does not exist, skipping"
2374       println(msg)
2375       logOutFile.append(msg+"\n")
2376     }
2377   }
2378   coreFile.append( coreBottom.getText("UTF-8") )
2379
2380   msg = "Generating ${zjsfile}"
2381   println(msg)
2382   logOutFile.append(msg+"\n")
2383   def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
2384   def logErrFOS = logOutFOS
2385
2386   javaexec {
2387     classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
2388     main = "com.google.javascript.jscomp.CommandLineRunner"
2389     jvmArgs = [ "-Dfile.encoding=UTF-8" ]
2390     args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
2391     maxHeapSize = "2g"
2392
2393     msg = "\nRunning '"+commandLine.join(' ')+"'\n"
2394     println(msg)
2395     logOutFile.append(msg+"\n")
2396
2397     if (logOutConsole) {
2398       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2399         new org.apache.tools.ant.util.TeeOutputStream(
2400           logOutFOS,
2401           stdout),
2402         standardOutput)
2403         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2404           new org.apache.tools.ant.util.TeeOutputStream(
2405             logErrFOS,
2406             stderr),
2407           errorOutput)
2408     } else {
2409       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2410         logOutFOS,
2411         stdout)
2412         errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2413           logErrFOS,
2414           stderr)
2415     }
2416   }
2417   msg = "--"
2418   println(msg)
2419   logOutFile.append(msg+"\n")
2420 }
2421
2422
2423 task jalviewjsBuildAllCores {
2424   group "JalviewJS"
2425   description "Build the core js lib closures listed in the classlists dir"
2426   dependsOn jalviewjsTranspile
2427   dependsOn jalviewjsTransferUnzipSwingJs
2428
2429   def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
2430   def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
2431   def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
2432   def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
2433   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
2434   def prefixFile = "${jsDir}/core/coretop2.js"
2435   def suffixFile = "${jsDir}/core/corebottom2.js"
2436
2437   inputs.file prefixFile
2438   inputs.file suffixFile
2439
2440   def classlistFiles = []
2441   // add the classlists found int the jalviewjs_classlists_dir
2442   fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
2443     file ->
2444     def name = file.getName() - ".txt"
2445     classlistFiles += [
2446       'file': file,
2447       'name': name
2448     ]
2449   }
2450
2451   // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
2452   //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
2453   classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
2454
2455   jalviewjsCoreClasslists = []
2456
2457   classlistFiles.each {
2458     hash ->
2459
2460     def file = hash['file']
2461     if (! file.exists()) {
2462       //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
2463       return false // this is a "continue" in groovy .each closure
2464     }
2465     def name = hash['name']
2466     if (name == null) {
2467       name = file.getName() - ".txt"
2468     }
2469
2470     def filelist = []
2471     file.eachLine {
2472       line ->
2473         filelist += line
2474     }
2475     def list = fileTree(dir: j2sDir, includes: filelist)
2476
2477     def jsfile = "${outputDir}/core${name}.js"
2478     def zjsfile = "${outputDir}/core${name}.z.js"
2479
2480     jalviewjsCoreClasslists += [
2481       'jsfile': jsfile,
2482       'zjsfile': zjsfile,
2483       'list': list,
2484       'name': name
2485     ]
2486
2487     inputs.file(file)
2488     inputs.files(list)
2489     outputs.file(jsfile)
2490     outputs.file(zjsfile)
2491   }
2492   
2493   // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
2494   def stevesoftClasslistName = "_stevesoft"
2495   def stevesoftClasslist = [
2496     'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
2497     'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
2498     'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
2499     'name': stevesoftClasslistName
2500   ]
2501   jalviewjsCoreClasslists += stevesoftClasslist
2502   inputs.files(stevesoftClasslist['list'])
2503   outputs.file(stevesoftClasslist['jsfile'])
2504   outputs.file(stevesoftClasslist['zjsfile'])
2505
2506   // _all core
2507   def allClasslistName = "_all"
2508   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
2509   allJsFiles += fileTree(
2510     dir: libJ2sDir,
2511     include: "**/*.js",
2512     excludes: [
2513       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2514       "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
2515       "**/org/jmol/export/JSExporter.js"
2516     ]
2517   )
2518   allJsFiles += fileTree(
2519     dir: swingJ2sDir,
2520     include: "**/*.js",
2521     excludes: [
2522       // these exlusions are files that the closure-compiler produces errors for. Should fix them
2523       "**/sun/misc/Unsafe.js",
2524       "**/swingjs/jquery/jquery-editable-select.js",
2525       "**/swingjs/jquery/j2sComboBox.js",
2526       "**/sun/misc/FloatingDecimal.js"
2527     ]
2528   )
2529   def allClasslist = [
2530     'jsfile': "${outputDir}/core${allClasslistName}.js",
2531     'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
2532     'list': allJsFiles,
2533     'name': allClasslistName
2534   ]
2535   // not including this version of "all" core at the moment
2536   //jalviewjsCoreClasslists += allClasslist
2537   inputs.files(allClasslist['list'])
2538   outputs.file(allClasslist['jsfile'])
2539   outputs.file(allClasslist['zjsfile'])
2540
2541   doFirst {
2542     def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
2543     logOutFile.getParentFile().mkdirs()
2544     logOutFile.createNewFile()
2545     logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
2546
2547     jalviewjsCoreClasslists.each {
2548       jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
2549     }
2550   }
2551
2552 }
2553
2554
2555 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
2556   copy {
2557     from inputFile
2558     into file(outputFile).getParentFile()
2559     rename { filename ->
2560       if (filename.equals(inputFile.getName())) {
2561         return file(outputFile).getName()
2562       }
2563       return null
2564     }
2565     filter(ReplaceTokens,
2566       beginToken: '_',
2567       endToken: '_',
2568       tokens: [
2569         'MAIN': '"'+main_class+'"',
2570         'CODE': "null",
2571         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
2572         'COREKEY': jalviewjs_core_key,
2573         'CORENAME': coreName
2574       ]
2575     )
2576   }
2577 }
2578
2579
2580 task jalviewjsPublishCoreTemplates {
2581   dependsOn jalviewjsBuildAllCores
2582   def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
2583   def inputFile = file(inputFileName)
2584   def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2585
2586   def outputFiles = []
2587   jalviewjsCoreClasslists.each { cl ->
2588     def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
2589     cl['outputfile'] = outputFile
2590     outputFiles += outputFile
2591   }
2592
2593   doFirst {
2594     jalviewjsCoreClasslists.each { cl ->
2595       jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
2596     }
2597   }
2598   inputs.file(inputFile)
2599   outputs.files(outputFiles)
2600 }
2601
2602
2603 task jalviewjsSyncCore (type: Sync) {
2604   dependsOn jalviewjsBuildAllCores
2605   dependsOn jalviewjsPublishCoreTemplates
2606   def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
2607   def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
2608
2609   from inputFiles
2610   into outputDir
2611   def outputFiles = []
2612   rename { filename ->
2613     outputFiles += "${outputDir}/${filename}"
2614     null
2615   }
2616   preserve {
2617     include "**"
2618   }
2619   outputs.files outputFiles
2620   inputs.files inputFiles
2621 }
2622
2623
2624 // this Copy version of TransferSiteJs will delete anything else in the target dir
2625 task jalviewjsCopyTransferSiteJs(type: Copy) {
2626   dependsOn jalviewjsTranspile
2627   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2628   into "${jalviewDir}/${jalviewjsSiteDir}"
2629 }
2630
2631
2632 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
2633 task jalviewjsSyncTransferSiteJs(type: Sync) {
2634   from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2635   include "**/*.*"
2636   into "${jalviewDir}/${jalviewjsSiteDir}"
2637   preserve {
2638     include "**"
2639   }
2640 }
2641
2642
2643 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
2644 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
2645 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
2646 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
2647
2648 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
2649 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
2650 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
2651 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
2652
2653
2654 task jalviewjsPrepareSite {
2655   group "JalviewJS"
2656   description "Prepares the website folder including unzipping files and copying resources"
2657   dependsOn jalviewjsSyncAllLibs
2658   dependsOn jalviewjsSyncResources
2659   dependsOn jalviewjsSyncSiteResources
2660   dependsOn jalviewjsSyncBuildProperties
2661   dependsOn jalviewjsSyncCore
2662 }
2663
2664
2665 task jalviewjsBuildSite {
2666   group "JalviewJS"
2667   description "Builds the whole website including transpiled code"
2668   dependsOn jalviewjsCopyTransferSiteJs
2669   dependsOn jalviewjsPrepareSite
2670 }
2671
2672
2673 task cleanJalviewjsTransferSite {
2674   doFirst {
2675     delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
2676     delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
2677     delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
2678     delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
2679   }
2680 }
2681
2682
2683 task cleanJalviewjsSite {
2684   dependsOn cleanJalviewjsTransferSite
2685   doFirst {
2686     delete "${jalviewDir}/${jalviewjsSiteDir}"
2687   }
2688 }
2689
2690
2691 task jalviewjsSiteTar(type: Tar) {
2692   group "JalviewJS"
2693   description "Creates a tar.gz file for the website"
2694   dependsOn jalviewjsBuildSite
2695   def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
2696   try {
2697     archiveFileName = outputFilename
2698   } catch (Exception e) {
2699     archiveName = outputFilename
2700   }
2701
2702   compression Compression.GZIP
2703
2704   from "${jalviewDir}/${jalviewjsSiteDir}"
2705   into jalviewjs_site_dir // this is inside the tar file
2706
2707   inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
2708 }
2709
2710
2711 task jalviewjsServer {
2712   group "JalviewJS"
2713   def filename = "jalviewjsTest.html"
2714   description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
2715   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
2716   doLast {
2717
2718     SimpleHttpFileServerFactory factory = new SimpleHttpFileServerFactory()
2719     def port = Integer.valueOf(jalviewjs_server_port)
2720     def start = port
2721     def running = false
2722     def url
2723     def jalviewjsServer
2724     while(port < start+1000 && !running) {
2725       try {
2726         def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
2727         jalviewjsServer = factory.start(doc_root, port)
2728         running = true
2729         url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
2730         println("SERVER STARTED with document root ${doc_root}.")
2731         println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
2732         println("For debug: "+url+"?j2sdebug")
2733         println("For verbose: "+url+"?j2sverbose")
2734       } catch (Exception e) {
2735         port++;
2736       }
2737     }
2738     def htmlText = """
2739       <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
2740       <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
2741       <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
2742       """
2743     jalviewjsCoreClasslists.each { cl ->
2744       def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
2745       htmlText += """
2746       <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
2747       """
2748       println("For core ${cl.name}: "+urlcore)
2749     }
2750
2751     file(htmlFile).text = htmlText
2752   }
2753
2754   outputs.file(htmlFile)
2755   outputs.upToDateWhen({false})
2756 }
2757
2758
2759 task cleanJalviewjsAll {
2760   group "JalviewJS"
2761   description "Delete all configuration and build artifacts to do with JalviewJS build"
2762   dependsOn cleanJalviewjsSite
2763   dependsOn jalviewjsEclipsePaths
2764   
2765   doFirst {
2766     delete "${jalviewDir}/${jalviewjsBuildDir}"
2767     delete "${jalviewDir}/${eclipse_bin_dir}"
2768     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
2769       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
2770     }
2771     delete "${jalviewDir}/${jalviewjs_j2s_settings}"
2772   }
2773
2774   outputs.upToDateWhen( { false } )
2775 }
2776
2777
2778 task jalviewjsIDE_checkJ2sPlugin {
2779   group "00 JalviewJS in Eclipse"
2780   description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
2781
2782   doFirst {
2783     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
2784     def j2sPluginFile = file(j2sPlugin)
2785     def eclipseHome = System.properties["eclipse.home.location"]
2786     if (eclipseHome == null || ! IN_ECLIPSE) {
2787       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
2788     }
2789     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
2790     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
2791     if (!eclipseJ2sPluginFile.exists()) {
2792       def msg = "Eclipse J2S Plugin is not installed (could not find '${eclipseJ2sPlugin}')\nTry running task jalviewjsIDE_copyJ2sPlugin"
2793       System.err.println(msg)
2794       throw new StopExecutionException(msg)
2795     }
2796
2797     def digest = MessageDigest.getInstance("MD5")
2798
2799     digest.update(j2sPluginFile.text.bytes)
2800     def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
2801
2802     digest.update(eclipseJ2sPluginFile.text.bytes)
2803     def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
2804      
2805     if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
2806       def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
2807       System.err.println(msg)
2808       throw new StopExecutionException(msg)
2809     } else {
2810       def msg = "Eclipse J2S Plugin is the same as '${j2sPlugin}' (this is good)"
2811       println(msg)
2812     }
2813   }
2814 }
2815
2816 task jalviewjsIDE_copyJ2sPlugin {
2817   group "00 JalviewJS in Eclipse"
2818   description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
2819
2820   doFirst {
2821     def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
2822     def j2sPluginFile = file(j2sPlugin)
2823     def eclipseHome = System.properties["eclipse.home.location"]
2824     if (eclipseHome == null || ! IN_ECLIPSE) {
2825       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
2826     }
2827     def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
2828     def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
2829     def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
2830     System.err.println(msg)
2831     copy {
2832       from j2sPlugin
2833       eclipseJ2sPluginFile.getParentFile().mkdirs()
2834       into eclipseJ2sPluginFile.getParent()
2835     }
2836   }
2837 }
2838
2839
2840 task jalviewjsIDE_j2sFile {
2841   group "00 JalviewJS in Eclipse"
2842   description "Creates the .j2s file"
2843   dependsOn jalviewjsCreateJ2sSettings
2844 }
2845
2846
2847 task jalviewjsIDE_SyncCore {
2848   group "00 JalviewJS in Eclipse"
2849   description "Build the core js lib closures listed in the classlists dir and publish core html from template"
2850   dependsOn jalviewjsSyncCore
2851 }
2852
2853
2854 task jalviewjsIDE_SyncSiteAll {
2855   dependsOn jalviewjsSyncAllLibs
2856   dependsOn jalviewjsSyncResources
2857   dependsOn jalviewjsSyncSiteResources
2858   dependsOn jalviewjsSyncBuildProperties
2859 }
2860
2861
2862 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
2863
2864
2865 task jalviewjsIDE_PrepareSite {
2866   group "00 JalviewJS in Eclipse"
2867   description "Sync libs and resources to site dir, but not closure cores"
2868
2869   dependsOn jalviewjsIDE_SyncSiteAll
2870   //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
2871 }
2872
2873
2874 task jalviewjsIDE_AssembleSite {
2875   group "00 JalviewJS in Eclipse"
2876   description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
2877   dependsOn jalviewjsPrepareSite
2878 }
2879
2880
2881 task jalviewjsIDE_SiteClean {
2882   group "00 JalviewJS in Eclipse"
2883   description "Deletes the Eclipse transpiled site"
2884   dependsOn cleanJalviewjsSite
2885 }
2886
2887
2888 task jalviewjsIDE_Server {
2889   group "00 JalviewJS in Eclipse"
2890   description "Starts a webserver on localhost to test the website"
2891   dependsOn jalviewjsServer
2892 }
2893
2894
2895 // buildship runs this at import or gradle refresh
2896 task eclipseSynchronizationTask {
2897   //dependsOn eclipseSetup
2898   dependsOn createBuildProperties
2899   if (J2S_ENABLED) {
2900     dependsOn jalviewjsIDE_j2sFile
2901     dependsOn jalviewjsIDE_checkJ2sPlugin
2902     dependsOn jalviewjsIDE_PrepareSite
2903   }
2904 }
2905
2906
2907 // buildship runs this at build time or project refresh
2908 task eclipseAutoBuildTask {
2909   //dependsOn jalviewjsIDE_checkJ2sPlugin
2910   //dependsOn jalviewjsIDE_PrepareSite
2911 }
2912
2913
2914 task jalviewjs {
2915   group "JalviewJS"
2916   description "Build the site"
2917   dependsOn jalviewjsBuildSite
2918 }