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