X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=build.gradle;h=6137539a7192b168dd893c2a7543ff741f89233b;hb=8881839853584ea4181e4f1600d5cb7b4da89bec;hp=2d2dad0e81b15a0c105653924f457123b06aaa1e;hpb=3b59fa4f77a679254cc31c715299d9760a681aad;p=jalview.git diff --git a/build.gradle b/build.gradle index 2d2dad0..6137539 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,10 @@ import org.gradle.plugins.ide.eclipse.model.Output import org.gradle.plugins.ide.eclipse.model.Library import java.security.MessageDigest import java.util.regex.Matcher +import java.util.concurrent.Executors +import java.util.concurrent.Future +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit import groovy.transform.ExternalizeMethods import groovy.util.XmlParser import groovy.xml.XmlUtil @@ -48,7 +52,7 @@ plugins { id 'application' id 'eclipse' id "com.diffplug.gradle.spotless" version "3.28.0" - id 'com.github.johnrengelman.shadow' version '4.0.3' + id 'com.github.johnrengelman.shadow' version '6.0.0' id 'com.install4j.gradle' version '10.0.3' id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree id 'com.palantir.git-version' version '0.13.0' apply false @@ -559,6 +563,7 @@ ext { } jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib") jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs") + jalviewjsTransferSiteMergeDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_merge") jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core") jalviewjsJalviewCoreHtmlFile = string("") jalviewjsJalviewCoreName = string(jalviewjs_core_name) @@ -568,11 +573,16 @@ ext { jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}") jalviewjsJ2sProps = null jalviewjsJ2sPlugin = jalviewjs_j2s_plugin + jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName()) eclipseWorkspace = null eclipseBinary = string("") eclipseVersion = string("") eclipseDebug = false + + jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}" + jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}" + // ENDEXT } @@ -1735,9 +1745,24 @@ run.dependsOn compileJava compileTestJava.dependsOn compileJava -ext.testsFailed = false + +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(",") @@ -1749,15 +1774,51 @@ task testTask0(type: Test) { /* 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(",") - tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name} preserveOrder true useDefaultListeners=true } } +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 + } +} +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 + } +} + +/* 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 @@ -1766,7 +1827,6 @@ task testTask1(type: Test) { 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 -> @@ -1775,7 +1835,7 @@ tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { te if (useClover) { dependsOn cloverClasses } else { //? - dependsOn compileJava //? + dependsOn testClasses //? } // run main tests first @@ -1783,44 +1843,36 @@ tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { te testTask.mustRunAfter "testTask0" testTask.testLogging { logging -> - events TestLogEvent.FAILED, - TestLogEvent.SKIPPED, - TestLogEvent.STANDARD_OUT, - TestLogEvent.STANDARD_ERROR + 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 - if (desc.parent) return // Only summarize results for whole modules - - String summary = "${testTask.project.name}:${testTask.name} results: ${result.resultType} " + - "(" + - "${result.testCount} tests, " + - "${result.successfulTestCount} successes, " + - "${result.failedTestCount} failures, " + - "${result.skippedTestCount} skipped" + - ") " + - "in ${TimeCategory.minus(new Date(result.endTime), new Date(result.startTime))}" + - "\n" + - "Report file: ${testTask.reports.html.entryPoint}" - - // Add reports in `testsResults`, keep failed suites at the end - if (result.resultType == TestResult.ResultType.SUCCESS) { - rootProject.ext.testsResults.add(0, summary) - } else { - rootProject.ext.testsResults += summary - } + def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint] - if (result.resultType == TestResult.ResultType.FAILURE) { - testsFailed = true - } + rootProject.ext.testsResults.add(resultsInfo) } // from original test task @@ -1842,11 +1894,25 @@ tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { te 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 { @@ -1854,47 +1920,117 @@ gradle.buildFinished { if (!allResults.isEmpty()) { printResults allResults + allResults.each {r -> + if (r[2].resultType == TestResult.ResultType.FAILURE) + throw new GradleException("Failed tests!") + } } } -private static void printResults(allResults) { - def maxLength = allResults*.readLines().flatten().collect { it.length() }.max() - - println "┌${"${"─" * maxLength}"}┐" - - println allResults.collect { - it.readLines().collect { - "│" + it + " " * (maxLength - it.length()) + "│" - }.join("\n") - }.join("\n├${"${"─" * maxLength}"}┤\n") - - println "└${"${"─" * maxLength}"}┘" +private static String colString(styler, col, colour, text) { + return col?"${styler[colour](text)}":text } -/* END of test tasks results summary */ -task verifyTestStatus { - doLast { - if (testsFailed) { - throw new GradleException("There were failing tests!") +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() } -test { - // from original test task - if (useClover) { - //dependsOn.clear() - dependsOn cloverClasses - } else { //? - dependsOn compileJava //? - } - dependsOn tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")} - finalizedBy verifyTestStatus +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() + } - // not running tests in this task - exclude "**/*" + println "┌${"${"─" * maxLength}"}┐" + println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n") + println "└${"${"─" * maxLength}"}┘" } +/* END of test tasks results summary */ task compileLinkCheck(type: JavaCompile) { @@ -2021,12 +2157,33 @@ shadowJar { if (buildDist) { dependsOn makeDist } - from ("${jalviewDir}/${libDistDir}") { - include("*.jar") - } - manifest { - attributes "Implementation-Version": JALVIEW_VERSION, - "Application-Name": applicationName + + 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 + + // 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 { + attributes "Implementation-Version": JALVIEW_VERSION, "Application-Name": applicationName + from (jarFileManifests) { + eachEntry { details -> + if (!details.key.equals("Import-Package")) { + details.exclude() + } + } + } + } } duplicatesStrategy "INCLUDE" @@ -2084,9 +2241,9 @@ task getdownImages() { dependsOn getdownImagesProcess } -task getdownWebsite() { +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" + 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) { @@ -2138,8 +2295,8 @@ task getdownWebsite() { 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", "${getdownImagesBuildDir}/${getdown_icon}") - props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesBuildDir}/${getdown_mac_dock_icon}") + 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) @@ -2198,7 +2355,7 @@ task getdownWebsite() { from s into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}" } - getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}" + getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}" } } @@ -2340,7 +2497,9 @@ task getdownDigestDir(type: JavaExec) { task getdownDigest(type: JavaExec) { group = "distribution" description = "Digest the getdown website folder" - dependsOn getdownWebsite + + dependsOn getdownWebsiteBuild + doFirst { classpath = files(getdownLauncher) } @@ -2373,12 +2532,19 @@ task getdown() { } } +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 getdownWebsite + dependsOn getdownWebsiteBuild def v = "v${JALVIEW_VERSION_UNDERSCORES}" def vDir = "${getdownArchiveDir}/${v}" @@ -2688,6 +2854,7 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) { '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, @@ -2887,6 +3054,7 @@ task sourceDist(type: Tar) { into project.name def EXCLUDE_FILES=[ + "dist/*", "build/*", "bin/*", "test-output/", @@ -3251,13 +3419,40 @@ task jalviewjsTransferUnzipSwingJs { task jalviewjsTransferUnzipLib { - def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip") + def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip").sort() doLast { zipFiles.each { file_zip -> copy { from zipTree(file_zip) into "${jalviewDir}/${jalviewjsTransferSiteLibDir}" + + // rename files in jsmol/j2s/... to swingjs/j2s/... + if (file_zip.getName().startsWith("Jmol-j2s-site")) { + eachFile { fcd -> + // jsmol/... -> swingjs/... + def relPathSegments = fcd.relativePath.getSegments() + if (relPathSegments[0] == "jsmol") { + def newRelPathSegments = relPathSegments + newRelPathSegments[0] = "swingjs" + fcd.relativePath = new RelativePath(true, newRelPathSegments) + } + } + } + + // The following replace() is needed due to a mismatch in Jmol calls to + // colorPtToFFRGB$javajs_util_T3d when only colorPtToFFRGB$javajs_util_T3 is defined + // in the SwingJS.zip (github or the one distributed with JSmol) + if (file_zip.getName().startsWith("Jmol-SwingJS")) { + filter { line -> + def l = "" + while(!line.equals(l)) { + line = line.replace('colorPtToFFRGB$javajs_util_T3d', 'colorPtToFFRGB$javajs_util_T3') + l = line + } + return line + } + } } } } @@ -3268,8 +3463,8 @@ task jalviewjsTransferUnzipLib { task jalviewjsTransferUnzipAllLibs { - dependsOn jalviewjsTransferUnzipSwingJs dependsOn jalviewjsTransferUnzipLib + dependsOn jalviewjsTransferUnzipSwingJs } @@ -3312,7 +3507,8 @@ task jalviewjsEclipseSetup { task jalviewjsSyncAllLibs (type: Sync) { dependsOn jalviewjsTransferUnzipAllLibs - def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}") + def inputFiles = [] + inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}") inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}") def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" @@ -3327,7 +3523,7 @@ task jalviewjsSyncAllLibs (type: Sync) { include "**" } - // should this be exclude really ? + // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite) duplicatesStrategy "INCLUDE" outputs.files outputFiles @@ -3519,6 +3715,71 @@ DEBUG: ${eclipseDebug} } +task jalviewjsTranserSiteMergeLibDirs (type: Sync) { + dependsOn jalviewjsTransferUnzipAllLibs + dependsOn jalviewjsTransferUnzipSwingJs + dependsOn jalviewjsTranspile + + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}") + // merge swingjs lib last + inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}") + + def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}" + + from inputFiles + into outputDir + def outputFiles = [] + rename { filename -> + outputFiles += "${outputDir}/${filename}" + null + } + + exclude "**/*.html" + exclude "**/*.htm" + + // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite) + duplicatesStrategy "INCLUDE" + + outputs.files outputFiles + inputs.files inputFiles +} + + +task jalviewjsTranserSiteMergeSwingDir (type: Sync) { + dependsOn jalviewjsTransferUnzipAllLibs + dependsOn jalviewjsTransferUnzipSwingJs + dependsOn jalviewjsTranspile + + // merge jalview files very last + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteJsDir}") + + def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}" + + from inputFiles + into outputDir + def outputFiles = [] + rename { filename -> + outputFiles += "${outputDir}/${filename}" + null + } + preserve { + include "**" + } + + // should this be exclude really ? No, jalview dir should be transferred last (and overwrite) + duplicatesStrategy "INCLUDE" + + outputs.files outputFiles + inputs.files inputFiles +} + + +task jalviewjsTranserSiteMergeDirs { + dependsOn jalviewjsTranserSiteMergeLibDirs + dependsOn jalviewjsTranserSiteMergeSwingDir +} + + def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) { def stdout = new ByteArrayOutputStream() @@ -3533,6 +3794,7 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin def coreTop = file(prefixFile) def coreBottom = file(suffixFile) + def missingFiles = [] coreFile.getParentFile().mkdirs() coreFile.createNewFile() coreFile.write( coreTop.getText("UTF-8") ) @@ -3546,6 +3808,7 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin msg = "...file '"+f.getPath()+"' does not exist, skipping" println(msg) logOutFile.append(msg+"\n") + missingFiles += f } } coreFile.append( coreBottom.getText("UTF-8") ) @@ -3560,7 +3823,7 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"]) main = "com.google.javascript.jscomp.CommandLineRunner" jvmArgs = [ "-Dfile.encoding=UTF-8" ] - args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ] + args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ] maxHeapSize = "2g" msg = "\nRunning '"+commandLine.join(' ')+"'\n" @@ -3588,6 +3851,11 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin } } msg = "--" + if (missingFiles.size() > 0) { + msg += "\n!!! These files were listed but missing:\n" + missingFiles.each { file -> msg += "!!! " + file.getPath() + "\n" } + msg = "--" + } println(msg) logOutFile.append(msg+"\n") } @@ -3596,13 +3864,12 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin task jalviewjsBuildAllCores { group "JalviewJS" description "Build the core js lib closures listed in the classlists dir" - dependsOn jalviewjsTranspile - dependsOn jalviewjsTransferUnzipSwingJs + dependsOn jalviewjsTranserSiteMergeDirs - def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}" - def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}" - def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}" - def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}" + def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}" + def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}" + def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}" + def jsDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_js_subdir}" def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core" def prefixFile = "${jsDir}/core/coretop2.js" def suffixFile = "${jsDir}/core/corebottom2.js" @@ -3663,19 +3930,6 @@ task jalviewjsBuildAllCores { outputs.file(zjsfile) } - // _stevesoft core. add any cores without a classlist here (and the inputs and outputs) - def stevesoftClasslistName = "_stevesoft" - def stevesoftClasslist = [ - 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js", - 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js", - 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"), - 'name': stevesoftClasslistName - ] - jalviewjsCoreClasslists += stevesoftClasslist - inputs.files(stevesoftClasslist['list']) - outputs.file(stevesoftClasslist['jsfile']) - outputs.file(stevesoftClasslist['zjsfile']) - // _all core def allClasslistName = "_all" def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js") @@ -3752,6 +4006,7 @@ def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inpu task jalviewjsPublishCoreTemplates { dependsOn jalviewjsBuildAllCores + def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}" def inputFile = file(inputFileName) def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" @@ -3776,6 +4031,7 @@ task jalviewjsPublishCoreTemplates { task jalviewjsSyncCore (type: Sync) { dependsOn jalviewjsBuildAllCores dependsOn jalviewjsPublishCoreTemplates + def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}") def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" @@ -3795,8 +4051,18 @@ task jalviewjsSyncCore (type: Sync) { // this Copy version of TransferSiteJs will delete anything else in the target dir +task jalviewjsCopyTransferSiteMergeDir(type: Copy) { + dependsOn jalviewjsTranserSiteMergeDirs + + from "${jalviewDir}/${jalviewjsTransferSiteMergeDir}" + into "${jalviewDir}/${jalviewjsSiteDir}" +} + + +// this Copy version of TransferSiteJs will delete anything else in the target dir task jalviewjsCopyTransferSiteJs(type: Copy) { dependsOn jalviewjsTranspile + from "${jalviewDir}/${jalviewjsTransferSiteJsDir}" into "${jalviewDir}/${jalviewjsSiteDir}" } @@ -3827,7 +4093,7 @@ jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs task jalviewjsPrepareSite { group "JalviewJS" description "Prepares the website folder including unzipping files and copying resources" - dependsOn jalviewjsSyncAllLibs + //dependsOn jalviewjsSyncAllLibs // now using jalviewjsCopyTransferSiteMergeDir dependsOn jalviewjsSyncResources dependsOn jalviewjsSyncSiteResources dependsOn jalviewjsSyncBuildProperties @@ -3838,7 +4104,7 @@ task jalviewjsPrepareSite { task jalviewjsBuildSite { group "JalviewJS" description "Builds the whole website including transpiled code" - dependsOn jalviewjsCopyTransferSiteJs + dependsOn jalviewjsCopyTransferSiteMergeDir dependsOn jalviewjsPrepareSite } @@ -4101,9 +4367,159 @@ task eclipseAutoBuildTask { } +task jalviewjsCopyStderrLaunchFile(type: Copy) { + from file(jalviewjs_stderr_launch) + into jalviewjsSiteDir + + inputs.file jalviewjs_stderr_launch + outputs.file jalviewjsStderrLaunchFilename +} + +task cleanJalviewjsChromiumUserDir { + doFirst { + delete jalviewjsChromiumUserDir + } + outputs.dir jalviewjsChromiumUserDir + // always run when depended on + outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() } +} + +task jalviewjsChromiumProfile { + dependsOn cleanJalviewjsChromiumUserDir + mustRunAfter cleanJalviewjsChromiumUserDir + + def firstRun = file("${jalviewjsChromiumUserDir}/First Run") + + doFirst { + mkdir jalviewjsChromiumProfileDir + firstRun.text = "" + } + outputs.file firstRun +} + +task jalviewjsLaunchTest { + group "Test" + description "Check JalviewJS opens in a browser" + dependsOn jalviewjsBuildSite + dependsOn jalviewjsCopyStderrLaunchFile + dependsOn jalviewjsChromiumProfile + + def macOS = OperatingSystem.current().isMacOsX() + def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary + if (chromiumBinary.startsWith("~/")) { + chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1) + } + + def stdout + def stderr + doFirst { + def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000 + + def binary = file(chromiumBinary) + if (!binary.exists()) { + throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.") + } + stdout = new ByteArrayOutputStream() + stderr = new ByteArrayOutputStream() + def execStdout + def execStderr + if (jalviewjs_j2s_to_console.equals("true")) { + execStdout = new org.apache.tools.ant.util.TeeOutputStream( + stdout, + System.out) + execStderr = new org.apache.tools.ant.util.TeeOutputStream( + stderr, + System.err) + } else { + execStdout = stdout + execStderr = stderr + } + // macOS not running properly with timeout arguments + def execArgs = macOS ? [] : [ + "--virtual-time-budget=${timeoutms}", + ] + execArgs += [ + "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER + "--headless=new", + "--disable-gpu", + "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}", + "--profile-directory=${jalviewjs_chromium_profile_name}", + "--allow-file-access-from-files", + "--enable-logging=stderr", + "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}" + ] + + if (true || macOS) { + ScheduledExecutorService executor = Executors.newScheduledThreadPool(3); + Future f1 = executor.submit( + () -> { + exec { + standardOutput = execStdout + errorOutput = execStderr + executable(chromiumBinary) + args(execArgs) + println "COMMAND: '"+commandLine.join(" ")+"'" + } + executor.shutdownNow() + } + ) + + def noChangeBytes = 0 + def noChangeIterations = 0 + executor.scheduleAtFixedRate( + () -> { + String stderrString = stderr.toString() + // shutdown the task if we have a success string + if (stderrString.contains(jalviewjs_desktop_init_string)) { + f1.cancel() + Thread.sleep(1000) + executor.shutdownNow() + } + // if no change in stderr for 10s then also end + if (noChangeIterations >= jalviewjs_chromium_idle_timeout) { + executor.shutdownNow() + } + if (stderrString.length() == noChangeBytes) { + noChangeIterations++ + } else { + noChangeBytes = stderrString.length() + noChangeIterations = 0 + } + }, + 1, 1, TimeUnit.SECONDS) + + executor.schedule(new Runnable(){ + public void run(){ + f1.cancel() + executor.shutdownNow() + } + }, timeoutms, TimeUnit.MILLISECONDS) + + executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS) + executor.shutdownNow() + } + + } + + doLast { + def found = false + stderr.toString().eachLine { line -> + if (line.contains(jalviewjs_desktop_init_string)) { + println("Found line '"+line+"'") + found = true + return + } + } + if (!found) { + throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.") + } + } +} + + task jalviewjs { group "JalviewJS" - description "Build the site" + description "Build the JalviewJS site and run the launch test" dependsOn jalviewjsBuildSite + dependsOn jalviewjsLaunchTest } -