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