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