+ outputs.dir("${classesDir}/doc")
+ outputs.dir("${classesDir}/help")
+ outputs.file("${workingDir}/JavaHelpSearch/DOCS")
+ outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
+ outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
+ outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
+ outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
+ outputs.file("${workingDir}/JavaHelpSearch/TMAP")
+}
+
+task buildResources {
+ dependsOn copyResources
+ dependsOn copyChannelResources
+ dependsOn createBuildProperties
+}
+
+task prepare {
+ dependsOn buildResources
+ dependsOn copyDocs
+ dependsOn copyHelp
+ dependsOn releasesTemplates
+ dependsOn convertMdFiles
+ dependsOn buildIndices
+}
+
+
+compileJava.dependsOn prepare
+run.dependsOn compileJava
+compileTestJava.dependsOn compileJava
+
+
+
+test {
+ group = "Verification"
+ description = "Runs all testTaskN tasks)"
+
+ if (useClover) {
+ dependsOn cloverClasses
+ } else { //?
+ dependsOn testClasses
+ }
+
+ // not running tests in this task
+ exclude "**/*"
+}
+/* testTask0 is the main test task */
+task testTask0(type: Test) {
+ group = "Verification"
+ description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
+ useTestNG() {
+ includeGroups testng_groups.split(",")
+ excludeGroups testng_excluded_groups.split(",")
+ tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
+ preserveOrder true
+ useDefaultListeners=true
+ }
+ timeout = Duration.ofMinutes(10)
+}
+
+/* separated tests */
+task testTask1(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+ timeout = Duration.ofMinutes(5)
+}
+
+task testTask2(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+ timeout = Duration.ofMinutes(5)
+}
+task testTask3(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+ timeout = Duration.ofMinutes(5)
+}
+
+/* insert more testTaskNs here -- change N to next digit or other string */
+/*
+task testTaskN(type: Test) {
+ group = "Verification"
+ description = "Tests that need to be isolated from the main test run"
+ useTestNG() {
+ includeGroups name
+ excludeGroups testng_excluded_groups.split(",")
+ preserveOrder true
+ useDefaultListeners=true
+ }
+}
+*/
+
+
+/*
+ * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
+ * to summarise test results from all Test tasks
+ */
+/* START of test tasks results summary */
+import groovy.time.TimeCategory
+import org.gradle.api.tasks.testing.logging.TestExceptionFormat
+import org.gradle.api.tasks.testing.logging.TestLogEvent
+rootProject.ext.testsResults = [] // Container for tests summaries
+
+tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
+
+ // from original test task
+ if (useClover) {
+ dependsOn cloverClasses
+ } else { //?
+ dependsOn testClasses //?
+ }
+
+ // run main tests first
+ if (!testTask.name.equals("testTask0"))
+ testTask.mustRunAfter "testTask0"
+
+ testTask.testLogging { logging ->
+ events TestLogEvent.FAILED
+// TestLogEvent.SKIPPED,
+// TestLogEvent.STANDARD_OUT,
+// TestLogEvent.STANDARD_ERROR
+
+ exceptionFormat TestExceptionFormat.FULL
+ showExceptions true
+ showCauses true
+ showStackTraces true
+ if (test_output) {
+ showStandardStreams true
+ }
+ info.events = [ TestLogEvent.FAILED ]
+ }
+
+ if (OperatingSystem.current().isMacOsX()) {
+ testTask.systemProperty "apple.awt.UIElement", "true"
+ testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
+ }
+
+
+ ignoreFailures = true // Always try to run all tests for all modules
+
+ afterSuite { desc, result ->
+ if (desc.parent)
+ return // Only summarize results for whole modules
+
+ def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
+
+ rootProject.ext.testsResults.add(resultsInfo)
+ }
+
+ // from original test task
+ maxHeapSize = "1024m"
+
+ workingDir = jalviewDir
+ def testLaf = project.findProperty("test_laf")
+ if (testLaf != null) {
+ println("Setting Test LaF to '${testLaf}'")
+ systemProperty "laf", testLaf
+ }
+ def testHiDPIScale = project.findProperty("test_HiDPIScale")
+ if (testHiDPIScale != null) {
+ println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
+ systemProperty "sun.java2d.uiScale", testHiDPIScale
+ }
+ sourceCompatibility = compile_source_compatibility
+ targetCompatibility = compile_target_compatibility
+ jvmArgs += additional_compiler_args
+
+ doFirst {
+ // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
+ // testTasks that include the tests, and exclude all from the others.
+ // get --test argument
+ filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
+ // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
+ // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
+
+
+ if (useClover) {
+ println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
+ }
+ }
+
+
+ /* don't fail on no matching tests (so --tests will run across all testTasks) */
+ testTask.filter.setFailOnNoMatchingTests(false)
+
+ /* ensure the "test" task dependsOn all the testTasks */
+ test.dependsOn testTask
+}
+
+gradle.buildFinished {
+ def allResults = rootProject.ext.testsResults
+
+ if (!allResults.isEmpty()) {
+ printResults allResults
+ allResults.each {r ->
+ if (r[2].resultType == TestResult.ResultType.FAILURE)
+ throw new GradleException("Failed tests!")
+ }
+ }
+}
+
+private static String colString(styler, col, colour, text) {
+ return col?"${styler[colour](text)}":text
+}
+
+private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
+ def colour = 'black'
+ def text = rt
+ def nocol = false
+ if (rc == 0) {
+ text = "-----"
+ nocol = true
+ } else {
+ switch(rt) {
+ case TestResult.ResultType.SUCCESS:
+ colour = 'green'
+ break;
+ case TestResult.ResultType.FAILURE:
+ colour = 'red'
+ break;
+ default:
+ nocol = true
+ break;
+ }
+ }
+ StringBuilder sb = new StringBuilder()
+ sb.append("${pn}")
+ if (tn != null)
+ sb.append(":${tn}")
+ sb.append(" results: ")
+ sb.append(colString(s, col && !nocol, colour, text))
+ sb.append(" (")
+ sb.append("${rc} tests, ")
+ sb.append(colString(s, col && rs > 0, 'green', rs))
+ sb.append(" successes, ")
+ sb.append(colString(s, col && rf > 0, 'red', rf))
+ sb.append(" failures, ")
+ sb.append("${rsk} skipped) in ${t}")
+ return sb.toString()
+}
+
+private static void printResults(allResults) {
+
+ // styler from https://stackoverflow.com/a/56139852
+ def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
+
+ def maxLength = 0
+ def failedTests = false
+ def summaryLines = []
+ def totalcount = 0
+ def totalsuccess = 0
+ def totalfail = 0
+ def totalskip = 0
+ def totaltime = TimeCategory.getSeconds(0)
+ // sort on project name then task name
+ allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
+ def projectName = it[0]
+ def taskName = it[1]
+ def result = it[2]
+ def time = it[3]
+ def report = it[4]
+ def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
+ def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
+ def reportLine = "Report file: ${report}"
+ def ls = summaryPlain.length()
+ def lr = reportLine.length()
+ def m = [ls, lr].max()
+ if (m > maxLength)
+ maxLength = m
+ def info = [ls, summaryCol, reportLine]
+ summaryLines.add(info)
+ failedTests |= result.resultType == TestResult.ResultType.FAILURE
+ totalcount += result.testCount
+ totalsuccess += result.successfulTestCount
+ totalfail += result.failedTestCount
+ totalskip += result.skippedTestCount
+ totaltime += time
+ }
+ def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
+ def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
+ def tls = totalSummaryPlain.length()
+ if (tls > maxLength)
+ maxLength = tls
+ def info = [tls, totalSummaryCol, null]
+ summaryLines.add(info)
+
+ def allSummaries = []
+ for(sInfo : summaryLines) {
+ def ls = sInfo[0]
+ def summary = sInfo[1]
+ def report = sInfo[2]
+
+ StringBuilder sb = new StringBuilder()
+ sb.append("│" + summary + " " * (maxLength - ls) + "│")
+ if (report != null) {
+ sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
+ }
+ allSummaries += sb.toString()
+ }
+
+ println "┌${"${"─" * maxLength}"}┐"
+ println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
+ println "└${"${"─" * maxLength}"}┘"
+}
+/* END of test tasks results summary */
+
+
+task compileLinkCheck(type: JavaCompile) {
+ options.fork = true
+ classpath = files("${jalviewDir}/${utils_dir}")
+ destinationDir = file("${jalviewDir}/${utils_dir}")
+ source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
+
+ inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
+ inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
+ outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
+ outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
+}
+
+
+task linkCheck(type: JavaExec) {
+ dependsOn prepare
+ dependsOn compileLinkCheck
+
+ def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
+ classpath = files("${jalviewDir}/${utils_dir}")
+ main = "HelpLinksChecker"
+ workingDir = "${helpBuildDir}"
+ args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
+
+ def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
+ standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+ outFOS,
+ System.out)
+ errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+ outFOS,
+ System.err)
+
+ inputs.dir(helpBuildDir)
+ outputs.file(helpLinksCheckerOutFile)
+}
+
+
+// import the pubhtmlhelp target
+ant.properties.basedir = "${jalviewDir}"
+ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
+ant.importBuild "${utils_dir}/publishHelp.xml"
+
+
+task cleanPackageDir(type: Delete) {
+ doFirst {
+ delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
+ }
+}
+
+
+jar {
+ dependsOn prepare
+ dependsOn linkCheck
+
+ manifest {
+ attributes "Main-Class": main_class,
+ "Permissions": "all-permissions",
+ "Application-Name": applicationName,
+ "Codebase": application_codebase,
+ "Implementation-Version": JALVIEW_VERSION
+ }
+
+ def outputDir = "${jalviewDir}/${package_dir}"
+ destinationDirectory = file(outputDir)
+ archiveFileName = rootProject.name+".jar"
+ duplicatesStrategy "EXCLUDE"
+
+
+ exclude "cache*/**"
+ exclude "*.jar"
+ exclude "*.jar.*"
+ exclude "**/*.jar"
+ exclude "**/*.jar.*"
+
+ inputs.dir(sourceSets.main.java.outputDir)
+ sourceSets.main.resources.srcDirs.each{ dir ->
+ inputs.dir(dir)
+ }
+ outputs.file("${outputDir}/${archiveFileName}")
+}
+
+
+task copyJars(type: Copy) {
+ from fileTree(dir: classesDir, include: "**/*.jar").files
+ into "${jalviewDir}/${package_dir}"
+}
+
+
+// doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
+task syncJars(type: Sync) {
+ dependsOn jar
+ from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
+ into "${jalviewDir}/${package_dir}"
+ preserve {
+ include jar.archiveFileName.getOrNull()
+ }
+}
+
+
+task makeDist {
+ group = "build"
+ description = "Put all required libraries in dist"
+ // order of "cleanPackageDir", "copyJars", "jar" important!
+ jar.mustRunAfter cleanPackageDir
+ syncJars.mustRunAfter cleanPackageDir
+ dependsOn cleanPackageDir
+ dependsOn syncJars
+ dependsOn jar
+ outputs.dir("${jalviewDir}/${package_dir}")
+}
+
+
+task cleanDist {
+ dependsOn cleanPackageDir
+ dependsOn cleanTest
+ dependsOn clean
+}
+
+
+task launcherJar(type: Jar) {
+ manifest {
+ attributes (
+ "Main-Class": shadow_jar_main_class,
+ "Implementation-Version": JALVIEW_VERSION,
+ "Application-Name": applicationName
+ )
+ }
+}
+
+shadowJar {
+ group = "distribution"
+ description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
+ if (buildDist) {
+ dependsOn makeDist
+ }
+
+ def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
+ def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
+ def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
+ from groovyJars
+ from otherJars
+
+ manifest {
+ // shadowJar manifest must inheritFrom another Jar task. Can't set attributes here.
+ inheritFrom(project.tasks.launcherJar.manifest)
+ }
+ // we need to include the groovy-swing Include-Package for it to run in the shadowJar
+ doFirst {
+ def jarFileManifests = []
+ groovyJars.each { jarFile ->
+ def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
+ if (mf != null) {
+ jarFileManifests += mf
+ }
+ }
+ manifest {
+ from (jarFileManifests) {
+ eachEntry { details ->
+ if (!details.key.equals("Import-Package")) {
+ details.exclude()
+ }
+ }
+ }
+ }
+ }
+
+ duplicatesStrategy "INCLUDE"
+
+ // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
+ mainClassName = shadow_jar_main_class
+ mergeServiceFiles()
+ classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
+ minimize()
+}
+
+task getdownImagesCopy() {
+ inputs.dir getdownImagesDir
+ outputs.dir getdownImagesBuildDir
+
+ doFirst {
+ copy {
+ from(getdownImagesDir) {
+ include("*getdown*.png")
+ }
+ into getdownImagesBuildDir
+ }
+ }
+}
+
+task getdownImagesProcess() {
+ dependsOn getdownImagesCopy
+
+ doFirst {
+ if (backgroundImageText) {
+ if (convertBinary == null) {
+ throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
+ }
+ if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
+ throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
+ }
+ fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
+ exec {
+ executable convertBinary
+ args = [
+ file.getPath(),
+ '-font', getdown_background_image_text_font,
+ '-fill', getdown_background_image_text_colour,
+ '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
+ '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
+ '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
+ file.getPath()
+ ]
+ }
+ }
+ }
+ }
+}
+
+task getdownImages() {
+ dependsOn getdownImagesProcess
+}
+
+task getdownWebsiteBuild() {
+ group = "distribution"
+ description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer. No digest is created."
+
+ dependsOn getdownImages
+ if (buildDist) {
+ dependsOn makeDist
+ }
+
+ def getdownWebsiteResourceFilenames = []
+ def getdownResourceDir = getdownResourceDir
+ def getdownResourceFilenames = []
+
+ doFirst {
+ // clean the getdown website and files dir before creating getdown folders
+ delete getdownAppBaseDir
+ delete getdownFilesDir
+
+ copy {
+ from buildProperties
+ rename(file(buildProperties).getName(), getdown_build_properties)
+ into getdownAppDir
+ }
+ getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
+
+ copy {
+ from channelPropsFile
+ filter(ReplaceTokens,
+ beginToken: '__',
+ endToken: '__',
+ tokens: [
+ 'SUFFIX': channelSuffix
+ ]
+ )
+ into getdownAppBaseDir
+ }
+ getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
+
+ // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
+ def props = project.properties.sort { it.key }
+ if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
+ props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
+ }
+ if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
+ props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
+ }
+ if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
+ props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
+ }
+ if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
+ props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
+ props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
+ props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
+ props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
+ props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
+ props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
+ }
+
+ props.put("getdown_txt_title", jalview_name)
+ props.put("getdown_txt_ui.name", applicationName)
+
+ // start with appbase
+ getdownTextLines += "appbase = ${getdownAppBase}"
+ props.each{ prop, val ->
+ if (prop.startsWith("getdown_txt_") && val != null) {
+ if (prop.startsWith("getdown_txt_multi_")) {
+ def key = prop.substring(18)
+ val.split(",").each{ v ->
+ def line = "${key} = ${v}"
+ getdownTextLines += line
+ }
+ } else {
+ // file values rationalised
+ if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
+ def r = null
+ if (val.indexOf('/') == 0) {
+ // absolute path
+ r = file(val)
+ } else if (val.indexOf('/') > 0) {
+ // relative path (relative to jalviewDir)
+ r = file( "${jalviewDir}/${val}" )
+ }
+ if (r.exists()) {
+ val = "${getdown_resource_dir}/" + r.getName()
+ getdownWebsiteResourceFilenames += val
+ getdownResourceFilenames += r.getPath()
+ }
+ }
+ if (! prop.startsWith("getdown_txt_resource")) {
+ def line = prop.substring(12) + " = ${val}"
+ getdownTextLines += line
+ }
+ }
+ }
+ }
+
+ getdownWebsiteResourceFilenames.each{ filename ->
+ getdownTextLines += "resource = ${filename}"
+ }
+ getdownResourceFilenames.each{ filename ->
+ copy {
+ from filename
+ into getdownResourceDir
+ }
+ }
+
+ def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
+ getdownWrapperScripts.each{ script ->
+ def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
+ if (s.exists()) {
+ copy {
+ from s
+ into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
+ }
+ getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
+ }
+ }
+
+ def codeFiles = []
+ fileTree(file(package_dir)).each{ f ->
+ if (f.isDirectory()) {
+ def files = fileTree(dir: f, include: ["*"]).getFiles()
+ codeFiles += files
+ } else if (f.exists()) {
+ codeFiles += f
+ }
+ }
+ def jalviewJar = jar.archiveFileName.getOrNull()
+ // put jalview.jar first for CLASSPATH and .properties files reasons
+ codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
+ def name = f.getName()
+ def line = "code = ${getdownAppDistDir}/${name}"
+ getdownTextLines += line
+ copy {
+ from f.getPath()
+ into getdownAppDir
+ }
+ }
+
+ // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
+ /*
+ if (JAVA_VERSION.equals("11")) {
+ def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
+ j11libFiles.sort().each{f ->
+ def name = f.getName()
+ def line = "code = ${getdown_j11lib_dir}/${name}"
+ getdownTextLines += line
+ copy {
+ from f.getPath()
+ into getdownJ11libDir
+ }
+ }
+ }
+ */
+
+ // 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.
+ //getdownTextLines += "class = " + file(getdownLauncher).getName()
+ getdownTextLines += "resource = ${getdown_launcher_new}"
+ getdownTextLines += "class = ${main_class}"
+ // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
+ if (getdownSetAppBaseProperty) {
+ getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
+ getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
+ }
+
+ def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
+ getdownTxt.write(getdownTextLines.join("\n"))
+
+ getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
+ def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
+ launchJvl.write("appbase=${getdownAppBase}")
+
+ // files going into the getdown website dir: getdown-launcher.jar
+ copy {
+ from getdownLauncher
+ rename(file(getdownLauncher).getName(), getdown_launcher_new)
+ into getdownAppBaseDir
+ }
+
+ // files going into the getdown website dir: getdown-launcher(-local).jar
+ copy {
+ from getdownLauncher
+ if (file(getdownLauncher).getName() != getdown_launcher) {
+ rename(file(getdownLauncher).getName(), getdown_launcher)
+ }
+ into getdownAppBaseDir
+ }
+
+ // files going into the getdown website dir: ./install dir and files
+ if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
+ copy {
+ from getdownTxt
+ from getdownLauncher
+ from "${getdownAppDir}/${getdown_build_properties}"
+ if (file(getdownLauncher).getName() != getdown_launcher) {
+ rename(file(getdownLauncher).getName(), getdown_launcher)
+ }
+ into getdownInstallDir
+ }
+
+ // and make a copy in the getdown files dir (these are not downloaded by getdown)
+ copy {
+ from getdownInstallDir
+ into getdownFilesInstallDir
+ }
+ }
+
+ // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
+ copy {
+ from getdownTxt
+ from launchJvl
+ from getdownLauncher
+ from "${getdownAppBaseDir}/${getdown_build_properties}"
+ from "${getdownAppBaseDir}/${channel_props}"
+ if (file(getdownLauncher).getName() != getdown_launcher) {
+ rename(file(getdownLauncher).getName(), getdown_launcher)
+ }
+ into getdownFilesDir
+ }
+
+ // and ./resource (not all downloaded by getdown)
+ copy {
+ from getdownResourceDir
+ into "${getdownFilesDir}/${getdown_resource_dir}"
+ }
+ }
+
+ if (buildDist) {
+ inputs.dir("${jalviewDir}/${package_dir}")
+ }
+ outputs.dir(getdownAppBaseDir)
+ outputs.dir(getdownFilesDir)
+}
+
+
+// a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
+task getdownDigestDir(type: JavaExec) {
+ group "Help"
+ description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
+
+ def digestDirPropertyName = "DIGESTDIR"
+ doFirst {
+ classpath = files(getdownLauncher)
+ def digestDir = findProperty(digestDirPropertyName)
+ if (digestDir == null) {
+ throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
+ }
+ args digestDir
+ }
+ main = "com.threerings.getdown.tools.Digester"
+}
+
+
+task getdownDigest(type: JavaExec) {
+ group = "distribution"
+ description = "Digest the getdown website folder"
+
+ dependsOn getdownWebsiteBuild
+
+ doFirst {
+ classpath = files(getdownLauncher)
+ }
+ main = "com.threerings.getdown.tools.Digester"
+ args getdownAppBaseDir
+ inputs.dir(getdownAppBaseDir)
+ outputs.file("${getdownAppBaseDir}/digest2.txt")
+}
+
+
+task getdown() {
+ group = "distribution"
+ description = "Create the minimal and full getdown app folder for installers and website and create digest file"
+ dependsOn getdownDigest
+ doLast {
+ if (reportRsyncCommand) {
+ def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
+ def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
+ println "LIKELY RSYNC COMMAND:"
+ println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
+ if (RUNRSYNC == "true") {
+ exec {
+ commandLine "mkdir", "-p", toDir
+ }
+ exec {
+ commandLine "rsync", "-avh", "--delete", fromDir, toDir
+ }
+ }
+ }
+ }
+}
+
+task getdownWebsite {
+ group = "distribution"
+ description = "A task to create the whole getdown channel website dir including digest file"
+
+ dependsOn getdownWebsiteBuild
+ dependsOn getdownDigest
+}
+
+task getdownArchiveBuild() {
+ group = "distribution"
+ description = "Put files in the archive dir to go on the website"
+
+ dependsOn getdownWebsiteBuild
+
+ def v = "v${JALVIEW_VERSION_UNDERSCORES}"
+ def vDir = "${getdownArchiveDir}/${v}"
+ getdownFullArchiveDir = "${vDir}/getdown"
+ getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
+
+ def vAltDir = "alt_${v}"
+ def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
+
+ doFirst {
+ // cleanup old "old" dir
+ delete getdownArchiveDir
+
+ def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
+ getdownArchiveTxt.getParentFile().mkdirs()
+ def getdownArchiveTextLines = []
+ def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
+
+ // the libdir
+ copy {
+ from "${getdownAppBaseDir}/${getdownAppDistDir}"
+ into "${getdownFullArchiveDir}/${vAltDir}"
+ }
+
+ getdownTextLines.each { line ->
+ line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
+ line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
+ line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
+ line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
+ line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
+ line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
+ // remove the existing resource = resource/ or bin/ lines
+ if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
+ getdownArchiveTextLines += line
+ }
+ }
+
+ // the resource dir -- add these files as resource lines in getdown.txt
+ copy {
+ from "${archiveImagesDir}"
+ into "${getdownFullArchiveDir}/${getdown_resource_dir}"
+ eachFile { file ->
+ getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
+ }
+ }
+
+ getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
+
+ def vLaunchJvl = file(getdownVersionLaunchJvl)
+ vLaunchJvl.getParentFile().mkdirs()
+ vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
+ def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
+ def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
+ // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
+ //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
+ java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
+
+ // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
+ copy {
+ from getdownLauncher
+ from "${getdownAppBaseDir}/${getdownLaunchJvl}"
+ from "${getdownAppBaseDir}/${getdown_launcher_new}"
+ from "${getdownAppBaseDir}/${channel_props}"
+ if (file(getdownLauncher).getName() != getdown_launcher) {
+ rename(file(getdownLauncher).getName(), getdown_launcher)
+ }
+ into getdownFullArchiveDir
+ }
+
+ }
+}
+
+task getdownArchiveDigest(type: JavaExec) {
+ group = "distribution"
+ description = "Digest the getdown archive folder"
+
+ dependsOn getdownArchiveBuild
+
+ doFirst {
+ classpath = files(getdownLauncher)
+ args getdownFullArchiveDir
+ }
+ main = "com.threerings.getdown.tools.Digester"
+ inputs.dir(getdownFullArchiveDir)
+ outputs.file("${getdownFullArchiveDir}/digest2.txt")
+}
+
+task getdownArchive() {
+ group = "distribution"
+ description = "Build the website archive dir with getdown digest"
+
+ dependsOn getdownArchiveBuild
+ dependsOn getdownArchiveDigest
+}
+
+tasks.withType(JavaCompile) {
+ options.encoding = 'UTF-8'
+}
+
+
+clean {
+ doFirst {
+ delete getdownAppBaseDir
+ delete getdownFilesDir
+ delete getdownArchiveDir
+ }
+}
+
+
+install4j {
+ if (file(install4jHomeDir).exists()) {
+ // good to go!
+ } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
+ install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
+ } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
+ install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
+ }
+ installDir(file(install4jHomeDir))
+
+ mediaTypes = Arrays.asList(install4j_media_types.split(","))
+}
+
+
+task copyInstall4jTemplate {
+ def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
+ def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
+ inputs.file(install4jTemplateFile)
+ inputs.file(install4jFileAssociationsFile)
+ inputs.property("CHANNEL", { CHANNEL })
+ outputs.file(install4jConfFile)
+
+ doLast {
+ def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
+
+ // turn off code signing if no OSX_KEYPASS
+ if (OSX_KEYPASS == "") {
+ install4jConfigXml.'**'.codeSigning.each { codeSigning ->
+ codeSigning.'@macEnabled' = "false"
+ }
+ install4jConfigXml.'**'.windows.each { windows ->
+ windows.'@runPostProcessor' = "false"
+ }
+ }
+
+ // disable install screen for OSX dmg (for 2.11.2.0)
+ install4jConfigXml.'**'.macosArchive.each { macosArchive ->
+ macosArchive.attributes().remove('executeSetupApp')
+ macosArchive.attributes().remove('setupAppId')
+ }
+
+ // turn off checksum creation for LOCAL channel
+ def e = install4jConfigXml.application[0]
+ e.'@createChecksums' = string(install4jCheckSums)
+
+ // put file association actions where placeholder action is
+ def install4jFileAssociationsText = install4jFileAssociationsFile.text
+ def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
+ install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
+ if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
+ def parent = a.parent()
+ parent.remove(a)
+ fileAssociationActions.each { faa ->
+ parent.append(faa)
+ }
+ // don't need to continue in .any loop once replacements have been made
+ return true
+ }
+ }
+
+ // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
+ // NB we're deleting the /other/ one!
+ // Also remove the examples subdir from non-release versions
+ def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
+ // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
+ if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
+ customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
+ } else {
+ // remove the examples subdir from Full File Set
+ def files = install4jConfigXml.files[0]
+ def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
+ def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
+ def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
+ def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
+ dirEntry.parent().remove(dirEntry)
+ }
+ install4jConfigXml.'**'.action.any { a ->
+ if (a.'@customizedId' == customizedIdToDelete) {
+ def parent = a.parent()
+ parent.remove(a)
+ return true
+ }
+ }
+
+ // write install4j file
+ install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
+ }
+}
+
+
+clean {
+ doFirst {
+ delete install4jConfFile
+ }
+}
+
+task cleanInstallersDataFiles {
+ def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
+ def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
+ def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
+ doFirst {
+ delete installersOutputTxt
+ delete installersSha256
+ delete hugoDataJsonFile
+ }
+}
+
+task install4jDMGBackgroundImageCopy {
+ inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
+ outputs.dir "${install4jDMGBackgroundImageBuildDir}"
+ doFirst {
+ copy {
+ from(install4jDMGBackgroundImageDir) {
+ include(install4jDMGBackgroundImageFile)
+ }
+ into install4jDMGBackgroundImageBuildDir
+ }
+ }
+}
+
+task install4jDMGBackgroundImageProcess {
+ dependsOn install4jDMGBackgroundImageCopy
+
+ doFirst {
+ if (backgroundImageText) {
+ if (convertBinary == null) {
+ throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
+ }
+ if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
+ throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
+ }
+ fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
+ exec {
+ executable convertBinary
+ args = [
+ file.getPath(),
+ '-font', install4j_background_image_text_font,
+ '-fill', install4j_background_image_text_colour,
+ '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
+ '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
+ '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
+ file.getPath()
+ ]
+ }
+ }
+ }
+ }
+}
+
+task install4jDMGBackgroundImage {
+ dependsOn install4jDMGBackgroundImageProcess
+}
+
+task installerFiles(type: com.install4j.gradle.Install4jTask) {
+ group = "distribution"
+ description = "Create the install4j installers"
+ dependsOn getdown
+ dependsOn copyInstall4jTemplate
+ dependsOn cleanInstallersDataFiles
+ dependsOn install4jDMGBackgroundImage
+
+ projectFile = install4jConfFile
+
+ // create an md5 for the input files to use as version for install4j conf file
+ def digest = MessageDigest.getInstance("MD5")
+ digest.update(
+ (file("${install4jDir}/${install4j_template}").text +
+ file("${install4jDir}/${install4j_info_plist_file_associations}").text +
+ file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
+ def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
+ if (filesMd5.length() >= 8) {
+ filesMd5 = filesMd5.substring(0,8)
+ }
+ def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
+
+ variables = [
+ 'JALVIEW_NAME': jalview_name,
+ 'JALVIEW_APPLICATION_NAME': applicationName,
+ 'JALVIEW_DIR': "../..",
+ 'OSX_KEYSTORE': OSX_KEYSTORE,
+ 'OSX_APPLEID': OSX_APPLEID,
+ 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
+ 'JSIGN_SH': JSIGN_SH,
+ 'JRE_DIR': getdown_app_dir_java,
+ 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
+ 'JALVIEW_VERSION': JALVIEW_VERSION,
+ 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
+ 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
+ 'JAVA_VERSION': JAVA_VERSION,
+ 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
+ 'VERSION': JALVIEW_VERSION,
+ 'COPYRIGHT_MESSAGE': install4j_copyright_message,
+ 'BUNDLE_ID': install4jBundleId,
+ 'INTERNAL_ID': install4jInternalId,
+ 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
+ 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
+ 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
+ 'WRAPPER_LINK': getdownWrapperLink,
+ 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
+ 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
+ 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
+ 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
+ 'INSTALLER_NAME': install4jInstallerName,
+ 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
+ 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
+ 'GETDOWN_FILES_DIR': getdown_files_dir,
+ 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
+ 'GETDOWN_DIST_DIR': getdownAppDistDir,
+ 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
+ 'GETDOWN_INSTALL_DIR': getdown_install_dir,
+ 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
+ 'BUILD_DIR': install4jBuildDir,
+ 'APPLICATION_CATEGORIES': install4j_application_categories,
+ 'APPLICATION_FOLDER': install4jApplicationFolder,
+ 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
+ 'EXECUTABLE_NAME': install4jExecutableName,
+ 'EXTRA_SCHEME': install4jExtraScheme,
+ 'MAC_ICONS_FILE': install4jMacIconsFile,
+ 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
+ 'PNG_ICON_FILE': install4jPngIconFile,
+ 'BACKGROUND': install4jBackground,
+ ]
+
+ def varNameMap = [
+ 'mac': 'MACOS',
+ 'windows': 'WINDOWS',
+ 'linux': 'LINUX'
+ ]
+
+ // these are the bundled OS/architecture VMs needed by install4j
+ def osArch = [
+ [ "mac", "x64" ],
+ [ "mac", "aarch64" ],
+ [ "windows", "x64" ],
+ [ "linux", "x64" ],
+ [ "linux", "aarch64" ]
+ ]
+ osArch.forEach { os, arch ->
+ variables[ sprintf("%s_%s_JAVA_VM_DIR", varNameMap[os], arch.toUpperCase(Locale.ROOT)) ] = sprintf("%s/jre-%s-%s-%s/jre", jreInstallsDir, JAVA_INTEGER_VERSION, os, arch)
+ // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
+ // otherwise running `gradle installers` generates a non-useful error:
+ // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
+ variables[ sprintf("%s_%s_JAVA_VM_TGZ", varNameMap[os], arch.toUpperCase(Locale.ROOT)) ] = sprintf("%s/tgz/jre_%s_%s_%s.tar.gz", jreInstallsDir, JAVA_INTEGER_VERSION, os, arch)
+ }
+
+ //println("INSTALL4J VARIABLES:")
+ //variables.each{k,v->println("${k}=${v}")}
+
+ destination = "${jalviewDir}/${install4jBuildDir}"
+ buildSelected = true
+
+ if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
+ faster = true
+ disableSigning = true
+ disableNotarization = true
+ }
+
+ if (OSX_KEYPASS) {
+ macKeystorePassword = OSX_KEYPASS
+ }
+
+ if (OSX_ALTOOLPASS) {
+ appleIdPassword = OSX_ALTOOLPASS
+ disableNotarization = false
+ } else {
+ disableNotarization = true
+ }
+
+ doFirst {
+ println("Using projectFile "+projectFile)
+ if (!disableNotarization) { println("Will notarize OSX App DMG") }
+ }
+ //verbose=true
+
+ inputs.dir(getdownAppBaseDir)
+ inputs.file(install4jConfFile)
+ inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
+ outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
+}
+
+def getDataHash(File myFile) {
+ HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
+ return myFile.exists()
+ ? [
+ "file" : myFile.getName(),
+ "filesize" : myFile.length(),
+ "sha256" : hash.toString()
+ ]
+ : null
+}
+
+def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
+ def hash = [
+ "channel" : getdownChannelName,
+ "date" : getDate("yyyy-MM-dd HH:mm:ss"),
+ "git-commit" : "${gitHash} [${gitBranch}]",
+ "version" : JALVIEW_VERSION
+ ]
+ // install4j installer files
+ if (installersOutputTxt.exists()) {
+ def idHash = [:]
+ installersOutputTxt.readLines().each { def line ->
+ if (line.startsWith("#")) {
+ return;
+ }
+ line.replaceAll("\n","")
+ def vals = line.split("\t")
+ def filename = vals[3]
+ def filesize = file(filename).length()
+ filename = filename.replaceAll(/^.*\//, "")
+ hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
+ idHash."${filename}" = vals[0]
+ }
+ if (install4jCheckSums && installersSha256.exists()) {
+ installersSha256.readLines().each { def line ->
+ if (line.startsWith("#")) {
+ return;
+ }
+ line.replaceAll("\n","")
+ def vals = line.split(/\s+\*?/)
+ def filename = vals[1]
+ def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
+ }
+ }
+ }
+
+ [
+ "JAR": shadowJar.archiveFile, // executable JAR
+ "JVL": getdownVersionLaunchJvl, // version JVL
+ "SOURCE": sourceDist.archiveFile // source TGZ
+ ].each { key, value ->
+ def file = file(value)
+ if (file.exists()) {
+ def fileHash = getDataHash(file)
+ if (fileHash != null) {
+ hash."${key}" = fileHash;
+ }
+ }
+ }
+ return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
+}
+
+task staticMakeInstallersJsonFile {
+ doFirst {
+ def output = findProperty("i4j_output")
+ def sha256 = findProperty("i4j_sha256")
+ def json = findProperty("i4j_json")
+ if (output == null || sha256 == null || json == null) {
+ throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
+ }
+ writeDataJsonFile(file(output), file(sha256), file(json))
+ }
+}
+
+task installers {
+ dependsOn installerFiles
+}
+
+
+spotless {
+ java {
+ eclipse().configFile(eclipse_codestyle_file)
+ }
+}
+
+task createSourceReleaseProperties(type: WriteProperties) {
+ group = "distribution"
+ description = "Create the source RELEASE properties file"
+
+ def sourceTarBuildDir = "${buildDir}/sourceTar"
+ def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
+ outputFile (sourceReleasePropertiesFile)
+
+ doFirst {
+ releaseProps.each{ key, val -> property key, val }
+ property "git.branch", gitBranch
+ property "git.hash", gitHash
+ }
+
+ outputs.file(outputFile)
+}
+
+task sourceDist(type: Tar) {
+ group "distribution"
+ description "Create a source .tar.gz file for distribution"
+
+ dependsOn createBuildProperties
+ dependsOn convertMdFiles
+ dependsOn eclipseAllPreferences
+ dependsOn createSourceReleaseProperties
+
+
+ def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
+ archiveFileName = outputFileName
+
+ compression Compression.GZIP
+
+ into project.name
+
+ def EXCLUDE_FILES=[
+ "dist/*",
+ "build/*",
+ "bin/*",
+ "test-output/",
+ "test-reports",
+ "tests",
+ "clover*/*",
+ ".*",
+ "benchmarking/*",
+ "**/.*",
+ "*.class",
+ "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
+ "*locales/**",
+ "utils/InstallAnywhere",
+ "**/*.log",
+ "RELEASE",
+ ]
+ def PROCESS_FILES=[
+ "AUTHORS",
+ "CITATION",
+ "FEATURETODO",
+ "JAVA-11-README",
+ "FEATURETODO",
+ "LICENSE",
+ "**/README",
+ "THIRDPARTYLIBS",
+ "TESTNG",
+ "build.gradle",
+ "gradle.properties",
+ "**/*.java",
+ "**/*.html",
+ "**/*.xml",
+ "**/*.gradle",
+ "**/*.groovy",
+ "**/*.properties",
+ "**/*.perl",
+ "**/*.sh",
+ ]
+ def INCLUDE_FILES=[
+ ".classpath",
+ ".settings/org.eclipse.buildship.core.prefs",
+ ".settings/org.eclipse.jdt.core.prefs"
+ ]
+
+ from(jalviewDir) {
+ exclude (EXCLUDE_FILES)
+ include (PROCESS_FILES)
+ filter(ReplaceTokens,
+ beginToken: '$$',
+ endToken: '$$',
+ tokens: [
+ 'Version-Rel': JALVIEW_VERSION,
+ 'Year-Rel': getDate("yyyy")
+ ]
+ )
+ }
+ from(jalviewDir) {
+ exclude (EXCLUDE_FILES)
+ exclude (PROCESS_FILES)
+ exclude ("appletlib")
+ exclude ("**/*locales")
+ exclude ("*locales/**")
+ exclude ("utils/InstallAnywhere")
+
+ exclude (getdown_files_dir)
+ // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
+ //exclude (getdown_website_dir)
+ //exclude (getdown_archive_dir)
+
+ // exluding these as not using jars as modules yet
+ exclude ("${j11modDir}/**/*.jar")
+ }
+ from(jalviewDir) {
+ include(INCLUDE_FILES)
+ }
+// from (jalviewDir) {
+// // explicit includes for stuff that seemed to not get included
+// include(fileTree("test/**/*."))
+// exclude(EXCLUDE_FILES)
+// exclude(PROCESS_FILES)
+// }
+
+ from(file(buildProperties).getParent()) {
+ include(file(buildProperties).getName())
+ rename(file(buildProperties).getName(), "build_properties")
+ filter({ line ->
+ line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
+ })
+ }
+
+ def sourceTarBuildDir = "${buildDir}/sourceTar"
+ from(sourceTarBuildDir) {
+ // this includes the appended RELEASE properties file
+ }
+}
+
+task dataInstallersJson {
+ group "website"
+ description "Create the installers-VERSION.json data file for installer files created"
+
+ mustRunAfter installers
+ mustRunAfter shadowJar
+ mustRunAfter sourceDist
+ mustRunAfter getdownArchive
+
+ def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
+ def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
+
+ if (installersOutputTxt.exists()) {
+ inputs.file(installersOutputTxt)
+ }
+ if (install4jCheckSums && installersSha256.exists()) {
+ inputs.file(installersSha256)
+ }
+ [
+ shadowJar.archiveFile, // executable JAR
+ getdownVersionLaunchJvl, // version JVL
+ sourceDist.archiveFile // source TGZ
+ ].each { fileName ->
+ if (file(fileName).exists()) {
+ inputs.file(fileName)
+ }
+ }
+
+ outputs.file(hugoDataJsonFile)
+
+ doFirst {
+ writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
+ }
+}
+
+task helppages {
+ group "help"
+ description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
+
+ dependsOn copyHelp
+ dependsOn pubhtmlhelp
+
+ inputs.dir("${helpBuildDir}/${help_dir}")
+ outputs.dir("${buildDir}/distributions/${help_dir}")
+}
+
+
+task j2sSetHeadlessBuild {
+ doFirst {
+ IN_ECLIPSE = false
+ }
+}
+
+
+task jalviewjsEnableAltFileProperty(type: WriteProperties) {
+ group "jalviewjs"
+ description "Enable the alternative J2S Config file for headless build"
+
+ outputFile = jalviewjsJ2sSettingsFileName
+ def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
+ def j2sProps = new Properties()
+ if (j2sPropsFile.exists()) {
+ try {
+ def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
+ j2sProps.load(j2sPropsFileFIS)
+ j2sPropsFileFIS.close()
+
+ j2sProps.each { prop, val ->
+ property(prop, val)
+ }
+ } catch (Exception e) {
+ println("Exception reading ${jalviewjsJ2sSettingsFileName}")
+ e.printStackTrace()
+ }
+ }
+ if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
+ property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
+ }
+}
+
+
+task jalviewjsSetEclipseWorkspace {
+ def propKey = "jalviewjs_eclipse_workspace"
+ def propVal = null
+ if (project.hasProperty(propKey)) {
+ propVal = project.getProperty(propKey)
+ if (propVal.startsWith("~/")) {
+ propVal = System.getProperty("user.home") + propVal.substring(1)
+ }
+ }
+ def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
+ def propsFile = file(propsFileName)
+ def eclipseWsDir = propVal
+ def props = new Properties()
+
+ def writeProps = true
+ if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
+ def ins = new FileInputStream(propsFileName)
+ props.load(ins)
+ ins.close()
+ if (props.getProperty(propKey, null) != null) {
+ eclipseWsDir = props.getProperty(propKey)
+ writeProps = false
+ }
+ }
+
+ if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
+ def tempDir = File.createTempDir()
+ eclipseWsDir = tempDir.getAbsolutePath()
+ writeProps = true
+ }
+ eclipseWorkspace = file(eclipseWsDir)
+
+ doFirst {
+ // do not run a headless transpile when we claim to be in Eclipse
+ if (IN_ECLIPSE) {
+ println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+ throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
+ } else {
+ println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+ }
+
+ if (writeProps) {
+ props.setProperty(propKey, eclipseWsDir)
+ propsFile.parentFile.mkdirs()
+ def bytes = new ByteArrayOutputStream()
+ props.store(bytes, null)
+ def propertiesString = bytes.toString()
+ propsFile.text = propertiesString
+ print("NEW ")
+ } else {
+ print("EXISTING ")
+ }
+
+ println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
+ }
+
+ //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
+ outputs.file(propsFileName)
+ outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
+}
+
+
+task jalviewjsEclipsePaths {
+ def eclipseProduct
+
+ def eclipseRoot = jalviewjs_eclipse_root
+ if (eclipseRoot.startsWith("~/")) {
+ eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
+ }
+ if (OperatingSystem.current().isMacOsX()) {
+ eclipseRoot += "/Eclipse.app"
+ eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
+ eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
+ } else if (OperatingSystem.current().isWindows()) { // check these paths!!
+ if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
+ eclipseRoot += "/eclipse"
+ }
+ eclipseBinary = "${eclipseRoot}/eclipse.exe"
+ eclipseProduct = "${eclipseRoot}/.eclipseproduct"
+ } else { // linux or unix
+ if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
+ eclipseRoot += "/eclipse"
+println("eclipseDir exists")
+ }
+ eclipseBinary = "${eclipseRoot}/eclipse"
+ eclipseProduct = "${eclipseRoot}/.eclipseproduct"
+ }
+
+ eclipseVersion = "4.13" // default
+ def assumedVersion = true
+ if (file(eclipseProduct).exists()) {
+ def fis = new FileInputStream(eclipseProduct)
+ def props = new Properties()
+ props.load(fis)
+ eclipseVersion = props.getProperty("version")
+ fis.close()
+ assumedVersion = false
+ }
+
+ def propKey = "eclipse_debug"
+ eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
+
+ doFirst {
+ // do not run a headless transpile when we claim to be in Eclipse
+ if (IN_ECLIPSE) {
+ println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+ throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
+ } else {
+ println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+ }
+
+ if (!assumedVersion) {
+ println("ECLIPSE VERSION=${eclipseVersion}")
+ }
+ }
+}
+
+
+task printProperties {
+ group "Debug"
+ description "Output to console all System.properties"
+ doFirst {
+ System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
+ }
+}
+
+
+task eclipseSetup {
+ dependsOn eclipseProject
+ dependsOn eclipseClasspath
+ dependsOn eclipseJdt
+}
+
+
+// this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
+task jalviewjsEclipseCopyDropins(type: Copy) {
+ dependsOn jalviewjsEclipsePaths
+
+ def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
+ inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
+ def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
+
+ from inputFiles
+ into outputDir