+task copyChannelResources(type: Copy) {
+ dependsOn copyResources
+ group = "build"
+ description = "Copy the channel resources dir to the build resources area"
+
+ def inputDir = "${channelDir}/${resource_dir}"
+ def destinationDirectory = resourcesBuildDir
+ from(inputDir) {
+ include(channel_props)
+ filter(ReplaceTokens,
+ beginToken: '__',
+ endToken: '__',
+ tokens: [
+ 'SUFFIX': channelSuffix
+ ]
+ )
+ }
+ from(inputDir) {
+ exclude(channel_props)
+ }
+ into destinationDirectory
+
+ inputs.dir(inputDir)
+ outputs.dir(destinationDirectory)
+}
+
+task createBuildProperties(type: WriteProperties) {
+ dependsOn copyResources
+ dependsOn copyChannelResources
+ group = "build"
+ description = "Create the ${buildProperties} file"
+
+ inputs.dir(sourceDir)
+ inputs.dir(resourcesBuildDir)
+ outputFile (buildProperties)
+ // taking time specific comment out to allow better incremental builds
+ comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
+ //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
+ property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
+ property "VERSION", JALVIEW_VERSION
+ property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
+ property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
+ if (getdownSetAppBaseProperty) {
+ property "GETDOWNAPPBASE", getdownAppBase
+ property "GETDOWNAPPDISTDIR", getdownAppDistDir
+ }
+ outputs.file(outputFile)
+}
+
+
+task buildIndices(type: JavaExec) {
+ dependsOn copyHelp
+ //dependsOn releasesTemplates
+ classpath = sourceSets.main.compileClasspath
+ main = "com.sun.java.help.search.Indexer"
+ workingDir = "${helpBuildDir}/${help_dir}"
+ def argDir = "html"
+ args = [ argDir ]
+ inputs.dir("${workingDir}/${argDir}")
+
+ 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
+}
+
+
+// random block of dependencies
+compileJava.dependsOn prepare
+run.dependsOn compileJava
+//run.dependsOn prepare
+compileTestJava.dependsOn compileJava //
+compileTestJava.dependsOn buildIndices //
+processResources.dependsOn copyChannelResources //
+processResources.dependsOn copyResources //
+processResources.dependsOn createBuildProperties //
+processResources.dependsOn copyDocs //
+processResources.dependsOn convertMdFiles //
+processResources.dependsOn copyHelp //
+processResources.dependsOn buildIndices //
+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
+ }
+}
+
+/* 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
+ }
+}
+
+/* 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
+
+ info.events = [ TestLogEvent.FAILED ]
+ }
+
+
+
+ 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")
+ }
+}
+
+// block of dependencies
+//compileTestJava.dependsOn compileLinkCheck //
+//copyChannelResources.dependsOn compileLinkCheck //
+//convertMdFiles.dependsOn compileLinkCheck //
+
+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 destinationDirectory = "${jalviewDir}/${package_dir}"
+ destinationDirectory = file(destinationDirectory)
+ archiveFileName = rootProject.name+".jar"
+ duplicatesStrategy "EXCLUDE"
+
+
+ exclude "cache*/**"
+ exclude "*.jar"
+ exclude "*.jar.*"
+ exclude "**/*.jar"
+ exclude "**/*.jar.*"
+
+ inputs.dir(sourceSets.main.java.destinationDirectory)
+ sourceSets.main.resources.srcDirs.each{ dir ->
+ inputs.dir(dir)
+ }
+ outputs.file("${destinationDirectory}/${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
+}
+
+
+shadowJar {
+ group = "distribution"
+ description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
+ if (buildDist) {
+ dependsOn makeDist
+ }
+ from ("${jalviewDir}/${libDistDir}") {
+ include("*.jar")
+ }
+ manifest {
+ attributes "Implementation-Version": JALVIEW_VERSION,
+ "Application-Name": applicationName
+ }
+
+ duplicatesStrategy "INCLUDE"
+
+ mainClassName = shadow_jar_main_class
+ mergeServiceFiles()
+ archiveClassifier = "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 getdownWebsite() {
+ 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"
+
+ 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 += "resource = ${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 getdownWebsite
+ 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 getdownArchiveBuild() {
+ group = "distribution"
+ description = "Put files in the archive dir to go on the website"
+
+ dependsOn getdownWebsite
+
+ 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)
+ }
+}
+
+