/* Convention for properties. Read from gradle.properties, use lower_case_underlines for property names. * For properties set within build.gradle, use camelCaseNoSpace. */ import org.apache.tools.ant.filters.ReplaceTokens import org.gradle.internal.os.OperatingSystem import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject import org.gradle.api.internal.PropertiesTransformer import org.gradle.util.ConfigureUtil 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 import groovy.json.JsonBuilder import com.vladsch.flexmark.util.ast.Node import com.vladsch.flexmark.html.HtmlRenderer import com.vladsch.flexmark.parser.Parser import com.vladsch.flexmark.util.data.MutableDataSet import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension import com.vladsch.flexmark.ext.tables.TablesExtension import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension import com.vladsch.flexmark.ext.autolink.AutolinkExtension import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension import com.vladsch.flexmark.ext.toc.TocExtension import com.google.common.hash.HashCode import com.google.common.hash.Hashing import com.google.common.io.Files import org.jsoup.Jsoup import org.jsoup.nodes.Element buildscript { repositories { mavenCentral() mavenLocal() } dependencies { classpath "com.vladsch.flexmark:flexmark-all:0.62.0" classpath "org.jsoup:jsoup:1.14.3" classpath "com.eowise:gradle-imagemagick:0.5.1" } } plugins { id 'java' id 'application' id 'eclipse' id "com.diffplug.gradle.spotless" version "3.28.0" id 'com.github.johnrengelman.shadow' version '4.0.3' 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 } repositories { jcenter() mavenCentral() mavenLocal() } // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use def string(Object o) { return o == null ? "" : o.toString() } def overrideProperties(String propsFileName, boolean output = false) { if (propsFileName == null) { return } def propsFile = file(propsFileName) if (propsFile != null && propsFile.exists()) { println("Using properties from file '${propsFileName}'") try { def p = new Properties() def localPropsFIS = new FileInputStream(propsFile) p.load(localPropsFIS) localPropsFIS.close() p.each { key, val -> def oldval if (project.hasProperty(key)) { oldval = project.findProperty(key) project.setProperty(key, val) if (output) { println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'") } } else { ext.setProperty(key, val) if (output) { println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'") } } } } catch (Exception e) { println("Exception reading local.properties") e.printStackTrace() } } } ext { jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath() jalviewDirRelativePath = jalviewDir date = new Date() getdownChannelName = CHANNEL.toLowerCase() // default to "default". Currently only has different cosmetics for "develop", "release", "default" propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default" channelDirName = propertiesChannelName // Import channel_properties if (getdownChannelName.startsWith("develop-")) { channelDirName = "develop-SUFFIX" } channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}") channelGradleProperties = string("${channelDir}/channel_gradle.properties") channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}") overrideProperties(channelGradleProperties, false) // local build environment properties // can be "projectDir/local.properties" overrideProperties("${projectDir}/local.properties", true) // or "../projectDir_local.properties" overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true) //// // Import releaseProps from the RELEASE file // or a file specified via JALVIEW_RELEASE_FILE if defined // Expect jalview.version and target release branch in jalview.release releaseProps = new Properties(); def releasePropFile = findProperty("JALVIEW_RELEASE_FILE"); def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE"; try { (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream { releaseProps.load(it) } } catch (Exception fileLoadError) { throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError); } //// // Set JALVIEW_VERSION if it is not already set if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) { JALVIEW_VERSION = releaseProps.get("jalview.version") } println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'") // this property set when running Eclipse headlessly j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild") // this property set by Eclipse eclipseApplicationProperty = string("eclipse.application") // CHECK IF RUNNING FROM WITHIN ECLIPSE def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty] IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.") // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET if (System.properties[j2sHeadlessBuildProperty].equals("true")) { println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'") IN_ECLIPSE = false } if (IN_ECLIPSE) { println("WITHIN ECLIPSE IDE") } else { println("HEADLESS BUILD") } J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable") if (J2S_ENABLED) { println("J2S ENABLED") } /* *-/ System.properties.sort { it.key }.each { key, val -> println("SYSTEM PROPERTY ${key}='${val}'") } /-* *-/ if (false && IN_ECLIPSE) { jalviewDir = jalviewDirAbsolutePath } */ // datestamp buildDate = new Date().format("yyyyMMdd") // essentials bareSourceDir = string(source_dir) sourceDir = string("${jalviewDir}/${bareSourceDir}") resourceDir = string("${jalviewDir}/${resource_dir}") bareTestSourceDir = string(test_source_dir) testDir = string("${jalviewDir}/${bareTestSourceDir}") classesDir = string("${jalviewDir}/${classes_dir}") // clover useClover = clover.equals("true") cloverBuildDir = "${buildDir}/clover" cloverInstrDir = file("${cloverBuildDir}/clover-instr") cloverClassesDir = file("${cloverBuildDir}/clover-classes") cloverReportDir = file("${buildDir}/reports/clover") cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr") cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes") //cloverTestClassesDir = cloverClassesDir cloverDb = string("${cloverBuildDir}/clover.db") testSourceDir = useClover ? cloverTestInstrDir : testDir testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}" channelSuffix = "" backgroundImageText = BACKGROUNDIMAGETEXT getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}") getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}") getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}") getdownFullArchiveDir = null getdownTextLines = [] getdownLaunchJvl = null getdownVersionLaunchJvl = null buildDist = true buildProperties = null // the following values might be overridden by the CHANNEL switch getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = string("${getdown_channel_base}/${getdownDir}") getdownArchiveAppBase = getdown_archive_base getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}") getdownAppDistDir = getdown_app_dir_alt getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}") getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown") getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application reportRsyncCommand = false jvlChannelName = CHANNEL.toLowerCase() install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}" install4jDMGBackgroundImageDir = "${install4j_images_dir}" install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j" install4jDMGBackgroundImageFile = "${install4j_dmg_background}" install4jInstallerName = "${jalview_name} Non-Release Installer" install4jExecutableName = install4j_executable_name install4jExtraScheme = "jalviewx" install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}") install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}") install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}") install4jBackground = string("${install4j_images_dir}/${install4j_background}") install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}" install4jCheckSums = true applicationName = "${jalview_name}" switch (CHANNEL) { case "BUILD": // TODO: get bamboo build artifact URL for getdown artifacts getdown_channel_base = bamboo_channelbase getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}") getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}") jvlChannelName += "_${getdownChannelName}" // automatically add the test group Not-bamboo for exclusion if ("".equals(testng_excluded_groups)) { testng_excluded_groups = "Not-bamboo" } install4jExtraScheme = "jalviewb" backgroundImageText = true break case [ "RELEASE", "JALVIEWJS-RELEASE" ]: getdownAppDistDir = getdown_app_dir_release getdownSetAppBaseProperty = true reportRsyncCommand = true install4jSuffix = "" install4jInstallerName = "${jalview_name} Installer" break case "ARCHIVE": getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}" getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = string("${getdown_channel_base}/${getdownDir}") if (!file("${ARCHIVEDIR}/${package_dir}").exists()) { throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution") } else { package_dir = string("${ARCHIVEDIR}/${package_dir}") buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}") buildDist = false } reportRsyncCommand = true install4jExtraScheme = "jalviewa" break case "ARCHIVELOCAL": getdownChannelName = string("archive/${JALVIEW_VERSION}") getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = file(getdownAppBaseDir).toURI().toString() if (!file("${ARCHIVEDIR}/${package_dir}").exists()) { throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']") } else { package_dir = string("${ARCHIVEDIR}/${package_dir}") buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}") buildDist = false } reportRsyncCommand = true getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}") install4jSuffix = "Archive" install4jExtraScheme = "jalviewa" break case ~/^DEVELOP-([\.\-\w]*)$/: def suffix = Matcher.lastMatcher[0][1] reportRsyncCommand = true getdownSetAppBaseProperty = true JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}" install4jSuffix = "Develop ${suffix}" install4jExtraScheme = "jalviewd" install4jInstallerName = "${jalview_name} Develop ${suffix} Installer" getdownChannelName = string("develop-${suffix}") getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}") getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}") getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = string("${getdown_channel_base}/${getdownDir}") channelSuffix = string(suffix) backgroundImageText = true break case "DEVELOP": reportRsyncCommand = true getdownSetAppBaseProperty = true // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}" install4jSuffix = "Develop" install4jExtraScheme = "jalviewd" install4jInstallerName = "${jalview_name} Develop Installer" backgroundImageText = true break case "TEST-RELEASE": reportRsyncCommand = true getdownSetAppBaseProperty = true // Don't ignore transpile errors for release build if (jalviewjs_ignore_transpile_errors.equals("true")) { jalviewjs_ignore_transpile_errors = "false" println("Setting jalviewjs_ignore_transpile_errors to 'false'") } JALVIEW_VERSION = JALVIEW_VERSION+"-test" install4jSuffix = "Test" install4jExtraScheme = "jalviewt" install4jInstallerName = "${jalview_name} Test Installer" backgroundImageText = true break case ~/^SCRATCH(|-[-\w]*)$/: getdownChannelName = CHANNEL JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL getdownDir = string("${getdownChannelName}/${JAVA_VERSION}") getdownAppBase = string("${getdown_channel_base}/${getdownDir}") reportRsyncCommand = true install4jSuffix = "Scratch" break case "TEST-LOCAL": if (!file("${LOCALDIR}").exists()) { throw new GradleException("Must provide a LOCALDIR value to produce a local distribution") } else { getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString() getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}") } JALVIEW_VERSION = "TEST" install4jSuffix = "Test-Local" install4jExtraScheme = "jalviewt" install4jInstallerName = "${jalview_name} Test Installer" backgroundImageText = true break case [ "LOCAL", "JALVIEWJS" ]: JALVIEW_VERSION = "TEST" getdownAppBase = file(getdownAppBaseDir).toURI().toString() getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString() getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}") install4jExtraScheme = "jalviewl" install4jCheckSums = false break default: // something wrong specified throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]") break } JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_") hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json") hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md") // override getdownAppBase if requested if (findProperty("getdown_appbase_override") != null) { // revert to LOCAL if empty string if (string(getdown_appbase_override) == "") { getdownAppBase = file(getdownAppBaseDir).toURI().toString() getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}") } else if (string(getdown_appbase_override).startsWith("file://")) { getdownAppBase = string(getdown_appbase_override) getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}") } else { getdownAppBase = string(getdown_appbase_override) } println("Overriding getdown appbase with '${getdownAppBase}'") } // sanitise file name for jalview launcher file for this channel jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_") // install4j application and folder names if (install4jSuffix == "") { install4jBundleId = "${install4j_bundle_id}" install4jWinApplicationId = install4j_release_win_application_id } else { applicationName = "${jalview_name} ${install4jSuffix}" install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase() // add int hash of install4jSuffix to the last part of the application_id def id = install4j_release_win_application_id def idsplitreverse = id.split("-").reverse() idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode() install4jWinApplicationId = idsplitreverse.reverse().join("-") } // sanitise folder and id names // install4jApplicationFolder = e.g. "Jalview Build" install4jApplicationFolder = applicationName .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \ .replaceAll("_+", "_") // collapse __ install4jInternalId = applicationName .replaceAll(" ","_") .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.] .replaceAll("_+", "") // collapse __ //.replaceAll("_*-_*", "-") // collapse _-_ install4jUnixApplicationFolder = applicationName .replaceAll(" ","_") .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.] .replaceAll("_+", "_") // collapse __ .replaceAll("_*-_*", "-") // collapse _-_ .toLowerCase() getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local" getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}") //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}" getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}") getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}") getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/") getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}") /* compile without modules -- using classpath libraries modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"]) modules_runtimeClasspath = modules_compileClasspath */ gitHash = "SOURCE" gitBranch = "Source" try { apply plugin: "com.palantir.git-version" def details = versionDetails() gitHash = details.gitHash gitBranch = details.branchName } catch(org.gradle.api.internal.plugins.PluginApplicationException e) { println("Not in a git repository. Using git values from RELEASE properties file.") gitHash = releaseProps.getProperty("git.hash") gitBranch = releaseProps.getProperty("git.branch") } catch(java.lang.RuntimeException e1) { throw new GradleException("Error with git-version plugin. Directory '.git' exists but versionDetails() cannot be found.") } println("Using a ${CHANNEL} profile.") additional_compiler_args = [] // configure classpath/args for j8/j11 compilation if (JAVA_VERSION.equals("1.8")) { JAVA_INTEGER_VERSION = string("8") //libDir = j8libDir libDir = j11libDir libDistDir = j8libDir compile_source_compatibility = 1.8 compile_target_compatibility = 1.8 // these are getdown.txt properties defined dependent on the JAVA_VERSION getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version")) getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version")) // this property is assigned below and expanded to multiple lines in the getdown task getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location")) // this property is for the Java library used in eclipse eclipseJavaRuntimeName = string("JavaSE-1.8") } else if (JAVA_VERSION.equals("11")) { JAVA_INTEGER_VERSION = string("11") libDir = j11libDir libDistDir = j11libDir compile_source_compatibility = 11 compile_target_compatibility = 11 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version")) getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version")) getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location")) eclipseJavaRuntimeName = string("JavaSE-11") /* compile without modules -- using classpath libraries additional_compiler_args += [ '--module-path', modules_compileClasspath.asPath, '--add-modules', j11modules ] */ } else if (JAVA_VERSION.equals("17")) { JAVA_INTEGER_VERSION = string("17") libDir = j17libDir libDistDir = j17libDir compile_source_compatibility = 17 compile_target_compatibility = 17 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version")) getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version")) getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location")) eclipseJavaRuntimeName = string("JavaSE-17") /* compile without modules -- using classpath libraries additional_compiler_args += [ '--module-path', modules_compileClasspath.asPath, '--add-modules', j11modules ] */ } else { throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview") } // for install4j JAVA_MIN_VERSION = JAVA_VERSION JAVA_MAX_VERSION = JAVA_VERSION jreInstallsDir = string(jre_installs_dir) if (jreInstallsDir.startsWith("~/")) { jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1) } install4jDir = string("${jalviewDir}/${install4j_utils_dir}") install4jConfFileName = string("jalview-install4j-conf.install4j") install4jConfFile = file("${install4jDir}/${install4jConfFileName}") install4jHomeDir = install4j_home_dir if (install4jHomeDir.startsWith("~/")) { install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1) } resourceBuildDir = string("${buildDir}/resources") resourcesBuildDir = string("${resourceBuildDir}/resources_build") helpBuildDir = string("${resourceBuildDir}/help_build") docBuildDir = string("${resourceBuildDir}/doc_build") if (buildProperties == null) { buildProperties = string("${resourcesBuildDir}/${build_properties_file}") } buildingHTML = string("${jalviewDir}/${doc_dir}/building.html") helpParentDir = string("${jalviewDir}/${help_parent_dir}") helpSourceDir = string("${helpParentDir}/${help_dir}") helpFile = string("${helpBuildDir}/${help_dir}/help.jhm") convertBinary = null convertBinaryExpectedLocation = imagemagick_convert if (convertBinaryExpectedLocation.startsWith("~/")) { convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1) } if (file(convertBinaryExpectedLocation).exists()) { convertBinary = convertBinaryExpectedLocation } relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath()) jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs") jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}") if (IN_ECLIPSE) { jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir) } else { jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js") } jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib") jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs") jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core") jalviewjsJalviewCoreHtmlFile = string("") jalviewjsJalviewCoreName = string(jalviewjs_core_name) jalviewjsCoreClasslists = [] jalviewjsJalviewTemplateName = string(jalviewjs_name) jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}") 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 } sourceSets { main { java { srcDirs sourceDir outputDir = file(classesDir) } resources { srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ] } compileClasspath = files(sourceSets.main.java.outputDir) compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) runtimeClasspath = compileClasspath runtimeClasspath += files(sourceSets.main.resources.srcDirs) } clover { java { srcDirs cloverInstrDir outputDir = cloverClassesDir } resources { srcDirs = sourceSets.main.resources.srcDirs } compileClasspath = files( sourceSets.clover.java.outputDir ) //compileClasspath += files( testClassesDir ) compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"]) compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"]) compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"]) runtimeClasspath = compileClasspath } test { java { srcDirs testSourceDir outputDir = file(testClassesDir) } resources { srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs } compileClasspath = files( sourceSets.test.java.outputDir ) compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"]) runtimeClasspath = compileClasspath runtimeClasspath += files(sourceSets.test.resources.srcDirs) } } // eclipse project and settings files creation, also used by buildship eclipse { project { name = eclipse_project_name natures 'org.eclipse.jdt.core.javanature', 'org.eclipse.jdt.groovy.core.groovyNature', 'org.eclipse.buildship.core.gradleprojectnature' buildCommand 'org.eclipse.jdt.core.javabuilder' buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' } classpath { //defaultOutputDir = sourceSets.main.java.outputDir configurations.each{ c-> if (c.isCanBeResolved()) { minusConfigurations += [c] } } plusConfigurations = [ ] file { whenMerged { cp -> def removeTheseToo = [] HashMap alreadyAddedSrcPath = new HashMap<>(); cp.entries.each { entry -> // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test". // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover // we add the resources and help/help dirs in as libs afterwards (see below) if (entry.kind == 'src') { if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) { removeTheseToo += entry } else { alreadyAddedSrcPath.putAt(entry.path, true) } } } cp.entries.removeAll(removeTheseToo) //cp.entries += new Output("${eclipse_bin_dir}/main") if (file(helpParentDir).isDirectory()) { cp.entries += new Library(fileReference(helpParentDir)) } if (file(resourceDir).isDirectory()) { cp.entries += new Library(fileReference(resourceDir)) } HashMap alreadyAddedLibPath = new HashMap<>(); sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any { //don't want to add outputDir as eclipse is using its own output dir in bin/main if (it.isDirectory() || ! it.exists()) { // don't add dirs to classpath, especially if they don't exist return false // groovy "continue" in .any closure } def itPath = it.toString() if (itPath.startsWith("${jalviewDirAbsolutePath}/")) { // make relative path itPath = itPath.substring(jalviewDirAbsolutePath.length()+1) } if (alreadyAddedLibPath.get(itPath)) { //println("Not adding duplicate entry "+itPath) } else { //println("Adding entry "+itPath) cp.entries += new Library(fileReference(itPath)) alreadyAddedLibPath.put(itPath, true) } } sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any { //no longer want to add outputDir as eclipse is using its own output dir in bin/main if (it.isDirectory() || ! it.exists()) { // don't add dirs to classpath return false // groovy "continue" in .any closure } def itPath = it.toString() if (itPath.startsWith("${jalviewDirAbsolutePath}/")) { itPath = itPath.substring(jalviewDirAbsolutePath.length()+1) } if (alreadyAddedLibPath.get(itPath)) { // don't duplicate } else { def lib = new Library(fileReference(itPath)) lib.entryAttributes["test"] = "true" cp.entries += lib alreadyAddedLibPath.put(itPath, true) } } } // whenMerged } // file containers 'org.eclipse.buildship.core.gradleclasspathcontainer' } // classpath jdt { // for the IDE, use java 11 compatibility sourceCompatibility = compile_source_compatibility targetCompatibility = compile_target_compatibility javaRuntimeName = eclipseJavaRuntimeName // add in jalview project specific properties/preferences into eclipse core preferences file { withProperties { props -> def jalview_prefs = new Properties() def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}") jalview_prefs.load(ins) ins.close() jalview_prefs.forEach { t, v -> if (props.getAt(t) == null) { props.putAt(t, v) } } // codestyle file -- overrides previous formatter prefs def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}") if (csFile.exists()) { XmlParser parser = new XmlParser() def profiles = parser.parse(csFile) def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") } if (profile != null) { profile.'setting'.each { s -> def id = s.'@id' def value = s.'@value' if (id != null && value != null) { props.putAt(id, value) } } } } } } } // jdt if (IN_ECLIPSE) { // Don't want these to be activated if in headless build synchronizationTasks "eclipseSynchronizationTask" //autoBuildTasks "eclipseAutoBuildTask" } } /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */ // Class to allow updating arbitrary properties files class PropertiesFile extends PropertiesPersistableConfigurationObject { public PropertiesFile(PropertiesTransformer t) { super(t); } @Override protected void load(Properties properties) { } @Override protected void store(Properties properties) { } @Override protected String getDefaultResourceName() { return ""; } // This is necessary, because PropertiesPersistableConfigurationObject fails // if no default properties file exists. @Override public void loadDefaults() { load(new StringBufferInputStream("")); } } // Task to update arbitrary properties files (set outputFile) class PropertiesFileTask extends PropertiesGeneratorTask { private final PropertiesFileContentMerger file; public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); } protected PropertiesFile create() { return new PropertiesFile(getTransformer()); } protected void configure(PropertiesFile props) { file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props); } public void file(Closure closure) { ConfigureUtil.configure(closure, file); } } task eclipseUIPreferences(type: PropertiesFileTask) { description = "Generate Eclipse additional settings" def filename = "org.eclipse.jdt.ui.prefs" outputFile = "$projectDir/.settings/${filename}" as File file { withProperties { it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String) } } } task eclipseGroovyCorePreferences(type: PropertiesFileTask) { description = "Generate Eclipse additional settings" def filename = "org.eclipse.jdt.groovy.core.prefs" outputFile = "$projectDir/.settings/${filename}" as File file { withProperties { it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String) } } } task eclipseAllPreferences { dependsOn eclipseJdt dependsOn eclipseUIPreferences dependsOn eclipseGroovyCorePreferences } eclipseUIPreferences.mustRunAfter eclipseJdt eclipseGroovyCorePreferences.mustRunAfter eclipseJdt /* end of eclipse preferences hack */ // clover bits task cleanClover { doFirst { delete cloverBuildDir delete cloverReportDir } } task cloverInstrJava(type: JavaExec) { group = "Verification" description = "Create clover instrumented source java files" dependsOn cleanClover inputs.files(sourceSets.main.allJava) outputs.dir(cloverInstrDir) //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"]) classpath = sourceSets.clover.compileClasspath main = "com.atlassian.clover.CloverInstr" def argsList = [ "--encoding", "UTF-8", "--initstring", cloverDb, "--destdir", cloverInstrDir.getPath(), ] def srcFiles = sourceSets.main.allJava.files argsList.addAll( srcFiles.collect( { file -> file.absolutePath } ) ) args argsList.toArray() doFirst { delete cloverInstrDir println("Clover: About to instrument "+srcFiles.size() +" files") } } task cloverInstrTests(type: JavaExec) { group = "Verification" description = "Create clover instrumented source test files" dependsOn cleanClover inputs.files(testDir) outputs.dir(cloverTestInstrDir) classpath = sourceSets.clover.compileClasspath main = "com.atlassian.clover.CloverInstr" def argsList = [ "--encoding", "UTF-8", "--initstring", cloverDb, "--srcdir", testDir, "--destdir", cloverTestInstrDir.getPath(), ] args argsList.toArray() doFirst { delete cloverTestInstrDir println("Clover: About to instrument test files") } } task cloverInstr { group = "Verification" description = "Create clover instrumented all source files" dependsOn cloverInstrJava dependsOn cloverInstrTests } cloverClasses.dependsOn cloverInstr task cloverConsoleReport(type: JavaExec) { group = "Verification" description = "Creates clover console report" onlyIf { file(cloverDb).exists() } inputs.dir cloverClassesDir classpath = sourceSets.clover.runtimeClasspath main = "com.atlassian.clover.reporters.console.ConsoleReporter" if (cloverreport_mem.length() > 0) { maxHeapSize = cloverreport_mem } if (cloverreport_jvmargs.length() > 0) { jvmArgs Arrays.asList(cloverreport_jvmargs.split(" ")) } def argsList = [ "--alwaysreport", "--initstring", cloverDb, "--unittests" ] args argsList.toArray() } task cloverHtmlReport(type: JavaExec) { group = "Verification" description = "Creates clover HTML report" onlyIf { file(cloverDb).exists() } def cloverHtmlDir = cloverReportDir inputs.dir cloverClassesDir outputs.dir cloverHtmlDir classpath = sourceSets.clover.runtimeClasspath main = "com.atlassian.clover.reporters.html.HtmlReporter" if (cloverreport_mem.length() > 0) { maxHeapSize = cloverreport_mem } if (cloverreport_jvmargs.length() > 0) { jvmArgs Arrays.asList(cloverreport_jvmargs.split(" ")) } def argsList = [ "--alwaysreport", "--initstring", cloverDb, "--outputdir", cloverHtmlDir ] if (cloverreport_html_options.length() > 0) { argsList += cloverreport_html_options.split(" ") } args argsList.toArray() } task cloverXmlReport(type: JavaExec) { group = "Verification" description = "Creates clover XML report" onlyIf { file(cloverDb).exists() } def cloverXmlFile = "${cloverReportDir}/clover.xml" inputs.dir cloverClassesDir outputs.file cloverXmlFile classpath = sourceSets.clover.runtimeClasspath main = "com.atlassian.clover.reporters.xml.XMLReporter" if (cloverreport_mem.length() > 0) { maxHeapSize = cloverreport_mem } if (cloverreport_jvmargs.length() > 0) { jvmArgs Arrays.asList(cloverreport_jvmargs.split(" ")) } def argsList = [ "--alwaysreport", "--initstring", cloverDb, "--outfile", cloverXmlFile ] if (cloverreport_xml_options.length() > 0) { argsList += cloverreport_xml_options.split(" ") } args argsList.toArray() } task cloverReport { group = "Verification" description = "Creates clover reports" dependsOn cloverXmlReport dependsOn cloverHtmlReport } compileCloverJava { doFirst { sourceCompatibility = compile_source_compatibility targetCompatibility = compile_target_compatibility options.compilerArgs += additional_compiler_args print ("Setting target compatibility to "+targetCompatibility+"\n") } //classpath += configurations.cloverRuntime } // end clover bits compileJava { // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ? sourceCompatibility = compile_source_compatibility targetCompatibility = compile_target_compatibility options.compilerArgs += additional_compiler_args options.encoding = "UTF-8" doFirst { print ("Setting target compatibility to "+compile_target_compatibility+"\n") } } compileTestJava { sourceCompatibility = compile_source_compatibility targetCompatibility = compile_target_compatibility options.compilerArgs += additional_compiler_args doFirst { print ("Setting target compatibility to "+targetCompatibility+"\n") } } clean { doFirst { delete sourceSets.main.java.outputDir } } cleanTest { dependsOn cleanClover doFirst { delete sourceSets.test.java.outputDir } } // format is a string like date.format("dd MMMM yyyy") def getDate(format) { return date.format(format) } def convertMdToHtml (FileTree mdFiles, File cssFile) { MutableDataSet options = new MutableDataSet() def extensions = new ArrayList<>() extensions.add(AnchorLinkExtension.create()) extensions.add(AutolinkExtension.create()) extensions.add(StrikethroughExtension.create()) extensions.add(TaskListExtension.create()) extensions.add(TablesExtension.create()) extensions.add(TocExtension.create()) options.set(Parser.EXTENSIONS, extensions) // set GFM table parsing options options.set(TablesExtension.WITH_CAPTION, false) options.set(TablesExtension.COLUMN_SPANS, false) options.set(TablesExtension.MIN_HEADER_ROWS, 1) options.set(TablesExtension.MAX_HEADER_ROWS, 1) options.set(TablesExtension.APPEND_MISSING_COLUMNS, true) options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true) options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true) // GFM anchor links options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false) options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor") options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true) options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "") Parser parser = Parser.builder(options).build() HtmlRenderer renderer = HtmlRenderer.builder(options).build() mdFiles.each { mdFile -> // add table of contents def mdText = "[TOC]\n"+mdFile.text // grab the first top-level title def title = null def titleRegex = /(?m)^#(\s+|([^#]))(.*)/ def matcher = mdText =~ titleRegex if (matcher.size() > 0) { // matcher[0][2] is the first character of the title if there wasn't any whitespace after the # title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3] } // or use the filename if none found if (title == null) { title = mdFile.getName() } Node document = parser.parse(mdText) String htmlBody = renderer.render(document) def htmlText = ''' ''' htmlText += ((title != null) ? " ${title}" : '' ) htmlText += ''' ''' htmlText += ((cssFile != null) ? cssFile.text : '') htmlText += ''' ''' htmlText += htmlBody htmlText += ''' ''' def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html") def htmlFile = file(htmlFilePath) println("Creating ${htmlFilePath}") htmlFile.text = htmlText } } task copyDocs(type: Copy) { def inputDir = "${jalviewDir}/${doc_dir}" def outputDir = "${docBuildDir}/${doc_dir}" from(inputDir) { include('**/*.txt') include('**/*.md') include('**/*.html') include('**/*.xml') filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: [ 'Version-Rel': JALVIEW_VERSION, 'Year-Rel': getDate("yyyy") ] ) } from(inputDir) { exclude('**/*.txt') exclude('**/*.md') exclude('**/*.html') exclude('**/*.xml') } into outputDir inputs.dir(inputDir) outputs.dir(outputDir) } task convertMdFiles { dependsOn copyDocs def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md") def cssFile = file("${jalviewDir}/${flexmark_css}") doLast { convertMdToHtml(mdFiles, cssFile) } inputs.files(mdFiles) inputs.file(cssFile) def htmlFiles = [] mdFiles.each { mdFile -> def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html") htmlFiles.add(file(htmlFilePath)) } outputs.files(htmlFiles) } def hugoTemplateSubstitutions(String input, Map extras=null) { def replacements = [ DATE: getDate("yyyy-MM-dd"), CHANNEL: propertiesChannelName, APPLICATION_NAME: applicationName, GIT_HASH: gitHash, GIT_BRANCH: gitBranch, VERSION: JALVIEW_VERSION, JAVA_VERSION: JAVA_VERSION, VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES, DRAFT: "false", JVL_HEADER: "" ] def output = input if (extras != null) { extras.each{ k, v -> output = output.replaceAll("__${k}__", ((v == null)?"":v)) } } replacements.each{ k, v -> output = output.replaceAll("__${k}__", ((v == null)?"":v)) } return output } def mdFileComponents(File mdFile, def dateOnly=false) { def map = [:] def content = "" if (mdFile.exists()) { def inFrontMatter = false def firstLine = true mdFile.eachLine { line -> if (line.matches("---")) { def prev = inFrontMatter inFrontMatter = firstLine if (inFrontMatter != prev) return false } if (inFrontMatter) { def m = null if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) { map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1]) } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) { map["date"] = new Date().parse("yyyy-MM-dd", m[0][1]) } else if (m = line =~ /^channel:\s*(\S+)/) { map["channel"] = m[0][1] } else if (m = line =~ /^version:\s*(\S+)/) { map["version"] = m[0][1] } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) { map[ m[0][1] ] = m[0][2] } if (dateOnly && map["date"] != null) { return false } } else { if (dateOnly) return false content += line+"\n" } firstLine = false } } return dateOnly ? map["date"] : [map, content] } task hugoTemplates { group "website" description "Create partially populated md pages for hugo website build" def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}") def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}" def templateFiles = fileTree(dir: hugoTemplatesDir) def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md") def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md") def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}") def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}") doFirst { // specific release template for version archive def changes = "" def whatsnew = null def givenDate = null def givenChannel = null def givenVersion = null if (CHANNEL == "RELEASE") { def (map, content) = mdFileComponents(releaseMdFile) givenDate = map.date givenChannel = map.channel givenVersion = map.version changes = content if (givenVersion != null && givenVersion != JALVIEW_VERSION) { throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})") } if (whatsnewMdFile.exists()) whatsnew = whatsnewMdFile.text } def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : [] def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : [] def changesHugo = null if (changes != null) { changesHugo = '
\n\n' def inSection = false changes.eachLine { line -> def m = null if (m = line =~ /^##([^#].*)$/) { if (inSection) { changesHugo += "
\n\n" } def section = m[0][1].trim() section = section.toLowerCase() section = section.replaceAll(/ +/, "_") section = section.replaceAll(/[^a-z0-9_\-]/, "") changesHugo += "
\n\n" inSection = true } else if (m = line =~ /^(\s*-\s*)(.*?)()?\s*$/) { def comment = m[0][2].trim() if (comment != "") { comment = comment.replaceAll('"', """) def issuekeys = [] comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal } def newline = m[0][1] if (comment.trim() != "") newline += "{{}}${comment}{{}} " newline += m[0][3].trim() if (issuekeys.size() > 0) newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}" if (m[0][4] != null) newline += m[0][4] line = newline } } changesHugo += line+"\n" } if (inSection) { changesHugo += "\n
\n\n" } changesHugo += '' } templateFiles.each{ templateFile -> def newFileName = string(hugoTemplateSubstitutions(templateFile.getName())) def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent() def newRelPathName = hugoTemplateSubstitutions( relPath.toString() ) def outPathName = string("${hugoBuildDir}/$newRelPathName") copy { from templateFile rename(templateFile.getName(), newFileName) into outPathName } def newFile = file("${outPathName}/${newFileName}".toString()) def content = newFile.text newFile.text = hugoTemplateSubstitutions(content, [ WHATSNEW: whatsnew, CHANGES: changesHugo, DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"), DRAFT: givenDate == null ? "true" : "false", JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false", JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : "" ] ) } } inputs.file(oldJvlFile) inputs.dir(hugoTemplatesDir) inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION }) inputs.property("CHANNEL", { CHANNEL }) } def getMdDate(File mdFile) { return mdFileComponents(mdFile, true) } def getMdSections(String content) { def sections = [:] def sectionContent = "" def sectionName = null content.eachLine { line -> def m = null if (m = line =~ /^##([^#].*)$/) { if (sectionName != null) { sections[sectionName] = sectionContent sectionName = null sectionContent = "" } sectionName = m[0][1].trim() sectionName = sectionName.toLowerCase() sectionName = sectionName.replaceAll(/ +/, "_") sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "") } else if (sectionName != null) { sectionContent += line+"\n" } } if (sectionContent != null) { sections[sectionName] = sectionContent } return sections } task copyHelp(type: Copy) { def inputDir = helpSourceDir def outputDir = "${helpBuildDir}/${help_dir}" from(inputDir) { include('**/*.txt') include('**/*.md') include('**/*.html') include('**/*.hs') include('**/*.xml') include('**/*.jhm') filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: [ 'Version-Rel': JALVIEW_VERSION, 'Year-Rel': getDate("yyyy") ] ) } from(inputDir) { exclude('**/*.txt') exclude('**/*.md') exclude('**/*.html') exclude('**/*.hs') exclude('**/*.xml') exclude('**/*.jhm') } into outputDir inputs.dir(inputDir) outputs.files(helpFile) outputs.dir(outputDir) } task releasesTemplates { group "help" description "Recreate whatsNew.html and releases.html from markdown files and templates in help" dependsOn copyHelp def releasesTemplateFile = file("${jalviewDir}/${releases_template}") def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}") def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}") def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}") def releasesMdDir = "${jalviewDir}/${releases_dir}" def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}" doFirst { def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md") def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md") if (CHANNEL == "RELEASE") { if (!releaseMdFile.exists()) { throw new GradleException("File ${releaseMdFile} must be created for RELEASE") } if (!whatsnewMdFile.exists()) { throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE") } } def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md") def releaseFilesDates = releaseFiles.collectEntries { [(it): getMdDate(it)] } releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) } def releasesTemplate = releasesTemplateFile.text def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/ def versionTemplate = m[0][1] MutableDataSet options = new MutableDataSet() def extensions = new ArrayList<>() options.set(Parser.EXTENSIONS, extensions) options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true) Parser parser = Parser.builder(options).build() HtmlRenderer renderer = HtmlRenderer.builder(options).build() def actualVersions = releaseFiles.collect { rf -> def (rfMap, rfContent) = mdFileComponents(rf) return rfMap.version } def versionsHtml = "" def linkedVersions = [] releaseFiles.reverse().each { rFile -> def (rMap, rContent) = mdFileComponents(rFile) def versionLink = "" def partialVersion = "" def firstPart = true rMap.version.split("\\.").each { part -> def displayPart = ( firstPart ? "" : "." ) + part partialVersion += displayPart if ( linkedVersions.contains(partialVersion) || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version ) ) { versionLink += displayPart } else { versionLink += "${displayPart}" linkedVersions += partialVersion } firstPart = false } def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy") def lm = null def rContentProcessed = "" rContent.eachLine { line -> if (lm = line =~ /^(\s*-)(\s*)(.*)$/) { line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}" } else if (lm = line =~ /^###([^#]+.*)$/) { line = "_${lm[0][1].trim()}_" } rContentProcessed += line + "\n" } def rContentSections = getMdSections(rContentProcessed) def rVersion = versionTemplate if (rVersion != "") { def rNewFeatures = rContentSections["new_features"] def rIssuesResolved = rContentSections["issues_resolved"] Node newFeaturesNode = parser.parse(rNewFeatures) String newFeaturesHtml = renderer.render(newFeaturesNode) Node issuesResolvedNode = parser.parse(rIssuesResolved) String issuesResolvedHtml = renderer.render(issuesResolvedNode) rVersion = hugoTemplateSubstitutions(rVersion, [ VERSION: rMap.version, VERSION_LINK: versionLink, DISPLAY_DATE: displayDate, NEW_FEATURES: newFeaturesHtml, ISSUES_RESOLVED: issuesResolvedHtml ] ) versionsHtml += rVersion } } releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml) releasesTemplate = hugoTemplateSubstitutions(releasesTemplate) releasesHtmlFile.text = releasesTemplate if (whatsnewMdFile.exists()) { def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : "" def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text) Node whatsnewNode = parser.parse(whatsnewMd) String whatsnewHtml = renderer.render(whatsnewNode) whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml) whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml, [ VERSION: JALVIEW_VERSION, DISPLAY_DATE: wnDisplayDate ] ) } else if (gradle.taskGraph.hasTask(":linkCheck")) { whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss") } } inputs.file(releasesTemplateFile) inputs.file(whatsnewTemplateFile) inputs.dir(releasesMdDir) inputs.dir(whatsnewMdDir) outputs.file(releasesHtmlFile) outputs.file(whatsnewHtmlFile) } task copyResources(type: Copy) { group = "build" description = "Copy (and make text substitutions in) the resources dir to the build area" def inputDir = resourceDir def outputDir = resourcesBuildDir from(inputDir) { include('**/*.txt') include('**/*.md') include('**/*.html') include('**/*.xml') filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: [ 'Version-Rel': JALVIEW_VERSION, 'Year-Rel': getDate("yyyy") ] ) } from(inputDir) { exclude('**/*.txt') exclude('**/*.md') exclude('**/*.html') exclude('**/*.xml') } into outputDir inputs.dir(inputDir) outputs.dir(outputDir) } 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 outputDir = resourcesBuildDir from(inputDir) { include(channel_props) filter(ReplaceTokens, beginToken: '__', endToken: '__', tokens: [ 'SUFFIX': channelSuffix ] ) } from(inputDir) { exclude(channel_props) } into outputDir inputs.dir(inputDir) outputs.dir(outputDir) } task createBuildProperties(type: WriteProperties) { dependsOn copyResources 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 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 } 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 } } /* 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 ] } 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 } 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() 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 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("^(?appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase) line = line.replaceAll("^(?(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/") line = line.replaceAll("^(?ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png") line = line.replaceAll("^(?ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png") line = line.replaceAll("^(?ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png") line = line.replaceAll("^(?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("${install4jFileAssociationsText}") 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=[ "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 } // this eclipse -clean doesn't actually work task jalviewjsCleanEclipse(type: Exec) { dependsOn eclipseSetup dependsOn jalviewjsEclipsePaths dependsOn jalviewjsEclipseCopyDropins executable(eclipseBinary) args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"]) if (eclipseDebug) { args += "-debug" } args += "-l" def inputString = """exit y """ def inputByteStream = new ByteArrayInputStream(inputString.getBytes()) standardInput = inputByteStream } /* not really working yet jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse */ task jalviewjsTransferUnzipSwingJs { def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}" doLast { copy { from zipTree(file_zip) into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}" } } inputs.file file_zip outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}" } task jalviewjsTransferUnzipLib { def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip") doLast { zipFiles.each { file_zip -> copy { from zipTree(file_zip) into "${jalviewDir}/${jalviewjsTransferSiteLibDir}" } } } inputs.files zipFiles outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}" } task jalviewjsTransferUnzipAllLibs { dependsOn jalviewjsTransferUnzipSwingJs dependsOn jalviewjsTransferUnzipLib } task jalviewjsCreateJ2sSettings(type: WriteProperties) { group "JalviewJS" description "Create the alternative j2s file from the j2s.* properties" jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key } def siteDirProperty = "j2s.site.directory" def setSiteDir = false jalviewjsJ2sProps.each { prop, val -> if (val != null) { if (prop == siteDirProperty) { if (!(val.startsWith('/') || val.startsWith("file://") )) { val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}" } setSiteDir = true } property(prop,val) } if (!setSiteDir) { // default site location, don't override specifically set property property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}") } } outputFile = jalviewjsJ2sAltSettingsFileName if (! IN_ECLIPSE) { inputs.properties(jalviewjsJ2sProps) outputs.file(jalviewjsJ2sAltSettingsFileName) } } task jalviewjsEclipseSetup { dependsOn jalviewjsEclipseCopyDropins dependsOn jalviewjsSetEclipseWorkspace dependsOn jalviewjsCreateJ2sSettings } task jalviewjsSyncAllLibs (type: Sync) { dependsOn jalviewjsTransferUnzipAllLibs def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}") inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}") def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" from inputFiles into outputDir def outputFiles = [] rename { filename -> outputFiles += "${outputDir}/${filename}" null } preserve { include "**" } // should this be exclude really ? duplicatesStrategy "INCLUDE" outputs.files outputFiles inputs.files inputFiles } task jalviewjsSyncResources (type: Sync) { dependsOn buildResources def inputFiles = fileTree(dir: resourcesBuildDir) def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" from inputFiles into outputDir def outputFiles = [] rename { filename -> outputFiles += "${outputDir}/${filename}" null } preserve { include "**" } outputs.files outputFiles inputs.files inputFiles } task jalviewjsSyncSiteResources (type: Sync) { def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}") def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" from inputFiles into outputDir def outputFiles = [] rename { filename -> outputFiles += "${outputDir}/${filename}" null } preserve { include "**" } outputs.files outputFiles inputs.files inputFiles } task jalviewjsSyncBuildProperties (type: Sync) { dependsOn createBuildProperties def inputFiles = [file(buildProperties)] def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}" from inputFiles into outputDir def outputFiles = [] rename { filename -> outputFiles += "${outputDir}/${filename}" null } preserve { include "**" } outputs.files outputFiles inputs.files inputFiles } task jalviewjsProjectImport(type: Exec) { dependsOn eclipseSetup dependsOn jalviewjsEclipsePaths dependsOn jalviewjsEclipseSetup doFirst { // do not run a headless import 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 import whilst IN_ECLIPSE is '${IN_ECLIPSE}'") } else { println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}") } } //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core" def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview" executable(eclipseBinary) args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath]) if (eclipseDebug) { args += "-debug" } args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ] if (!IN_ECLIPSE) { args += [ "-D${j2sHeadlessBuildProperty}=true" ] args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ] } inputs.file("${jalviewDir}/.project") outputs.upToDateWhen { file(projdir).exists() } } task jalviewjsTranspile(type: Exec) { dependsOn jalviewjsEclipseSetup dependsOn jalviewjsProjectImport dependsOn jalviewjsEclipsePaths if (!IN_ECLIPSE) { dependsOn jalviewjsEnableAltFileProperty } 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}") } } executable(eclipseBinary) args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ]) if (eclipseDebug) { args += "-debug" } args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ] if (!IN_ECLIPSE) { args += [ "-D${j2sHeadlessBuildProperty}=true" ] args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ] } def stdout def stderr doFirst { stdout = new ByteArrayOutputStream() stderr = new ByteArrayOutputStream() def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}" def logOutFile = file(logOutFileName) logOutFile.createNewFile() logOutFile.text = """ROOT: ${jalviewjs_eclipse_root} BINARY: ${eclipseBinary} VERSION: ${eclipseVersion} WORKSPACE: ${eclipseWorkspace} DEBUG: ${eclipseDebug} ---- """ def logOutFOS = new FileOutputStream(logOutFile, true) // true == append // combine stdout and stderr def logErrFOS = logOutFOS if (jalviewjs_j2s_to_console.equals("true")) { standardOutput = new org.apache.tools.ant.util.TeeOutputStream( new org.apache.tools.ant.util.TeeOutputStream( logOutFOS, stdout), System.out) errorOutput = new org.apache.tools.ant.util.TeeOutputStream( new org.apache.tools.ant.util.TeeOutputStream( logErrFOS, stderr), System.err) } else { standardOutput = new org.apache.tools.ant.util.TeeOutputStream( logOutFOS, stdout) errorOutput = new org.apache.tools.ant.util.TeeOutputStream( logErrFOS, stderr) } } doLast { if (stdout.toString().contains("Error processing ")) { // j2s did not complete transpile //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'") if (jalviewjs_ignore_transpile_errors.equals("true")) { println("IGNORING TRANSPILE ERRORS") println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'") } else { throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'") } } } inputs.dir("${jalviewDir}/${sourceDir}") outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}") outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } ) } def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) { def stdout = new ByteArrayOutputStream() def stderr = new ByteArrayOutputStream() def coreFile = file(jsfile) def msg = "" msg = "Creating core for ${name}...\nGenerating ${jsfile}" println(msg) logOutFile.createNewFile() logOutFile.append(msg+"\n") def coreTop = file(prefixFile) def coreBottom = file(suffixFile) coreFile.getParentFile().mkdirs() coreFile.createNewFile() coreFile.write( coreTop.getText("UTF-8") ) list.each { f -> if (f.exists()) { def t = f.getText("UTF-8") t.replaceAll("Clazz\\.([^_])","Clazz_${1}") coreFile.append( t ) } else { msg = "...file '"+f.getPath()+"' does not exist, skipping" println(msg) logOutFile.append(msg+"\n") } } coreFile.append( coreBottom.getText("UTF-8") ) msg = "Generating ${zjsfile}" println(msg) logOutFile.append(msg+"\n") def logOutFOS = new FileOutputStream(logOutFile, true) // true == append def logErrFOS = logOutFOS javaexec { 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 ] maxHeapSize = "2g" msg = "\nRunning '"+commandLine.join(' ')+"'\n" println(msg) logOutFile.append(msg+"\n") if (logOutConsole) { standardOutput = new org.apache.tools.ant.util.TeeOutputStream( new org.apache.tools.ant.util.TeeOutputStream( logOutFOS, stdout), standardOutput) errorOutput = new org.apache.tools.ant.util.TeeOutputStream( new org.apache.tools.ant.util.TeeOutputStream( logErrFOS, stderr), System.err) } else { standardOutput = new org.apache.tools.ant.util.TeeOutputStream( logOutFOS, stdout) errorOutput = new org.apache.tools.ant.util.TeeOutputStream( logErrFOS, stderr) } } msg = "--" println(msg) logOutFile.append(msg+"\n") } task jalviewjsBuildAllCores { group "JalviewJS" description "Build the core js lib closures listed in the classlists dir" dependsOn jalviewjsTranspile dependsOn jalviewjsTransferUnzipSwingJs 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 outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core" def prefixFile = "${jsDir}/core/coretop2.js" def suffixFile = "${jsDir}/core/corebottom2.js" inputs.file prefixFile inputs.file suffixFile def classlistFiles = [] // add the classlists found int the jalviewjs_classlists_dir fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each { file -> def name = file.getName() - ".txt" classlistFiles += [ 'file': file, 'name': name ] } // _jmol and _jalview cores. Add any other peculiar classlist.txt files here //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ] classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ] jalviewjsCoreClasslists = [] classlistFiles.each { hash -> def file = hash['file'] if (! file.exists()) { //println("...classlist file '"+file.getPath()+"' does not exist, skipping") return false // this is a "continue" in groovy .each closure } def name = hash['name'] if (name == null) { name = file.getName() - ".txt" } def filelist = [] file.eachLine { line -> filelist += line } def list = fileTree(dir: j2sDir, includes: filelist) def jsfile = "${outputDir}/core${name}.js" def zjsfile = "${outputDir}/core${name}.z.js" jalviewjsCoreClasslists += [ 'jsfile': jsfile, 'zjsfile': zjsfile, 'list': list, 'name': name ] inputs.file(file) inputs.files(list) outputs.file(jsfile) 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") allJsFiles += fileTree( dir: libJ2sDir, include: "**/*.js", excludes: [ // these exlusions are files that the closure-compiler produces errors for. Should fix them "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js", "**/org/jmol/export/JSExporter.js" ] ) allJsFiles += fileTree( dir: swingJ2sDir, include: "**/*.js", excludes: [ // these exlusions are files that the closure-compiler produces errors for. Should fix them "**/sun/misc/Unsafe.js", "**/swingjs/jquery/jquery-editable-select.js", "**/swingjs/jquery/j2sComboBox.js", "**/sun/misc/FloatingDecimal.js" ] ) def allClasslist = [ 'jsfile': "${outputDir}/core${allClasslistName}.js", 'zjsfile': "${outputDir}/core${allClasslistName}.z.js", 'list': allJsFiles, 'name': allClasslistName ] // not including this version of "all" core at the moment //jalviewjsCoreClasslists += allClasslist inputs.files(allClasslist['list']) outputs.file(allClasslist['jsfile']) outputs.file(allClasslist['zjsfile']) doFirst { def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}") logOutFile.getParentFile().mkdirs() logOutFile.createNewFile() logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n") jalviewjsCoreClasslists.each { jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true")) } } } def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) { copy { from inputFile into file(outputFile).getParentFile() rename { filename -> if (filename.equals(inputFile.getName())) { return file(outputFile).getName() } return null } filter(ReplaceTokens, beginToken: '_', endToken: '_', tokens: [ 'MAIN': '"'+main_class+'"', 'CODE': "null", 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]", 'COREKEY': jalviewjs_core_key, 'CORENAME': coreName ] ) } } task jalviewjsPublishCoreTemplates { dependsOn jalviewjsBuildAllCores def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}" def inputFile = file(inputFileName) def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" def outputFiles = [] jalviewjsCoreClasslists.each { cl -> def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html" cl['outputfile'] = outputFile outputFiles += outputFile } doFirst { jalviewjsCoreClasslists.each { cl -> jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile) } } inputs.file(inputFile) outputs.files(outputFiles) } task jalviewjsSyncCore (type: Sync) { dependsOn jalviewjsBuildAllCores dependsOn jalviewjsPublishCoreTemplates def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}") def outputDir = "${jalviewDir}/${jalviewjsSiteDir}" from inputFiles into outputDir def outputFiles = [] rename { filename -> outputFiles += "${outputDir}/${filename}" null } preserve { include "**" } outputs.files outputFiles inputs.files inputFiles } // 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}" } // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes task jalviewjsSyncTransferSiteJs(type: Sync) { from "${jalviewDir}/${jalviewjsTransferSiteJsDir}" include "**/*.*" into "${jalviewDir}/${jalviewjsSiteDir}" preserve { include "**" } } jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs task jalviewjsPrepareSite { group "JalviewJS" description "Prepares the website folder including unzipping files and copying resources" dependsOn jalviewjsSyncAllLibs dependsOn jalviewjsSyncResources dependsOn jalviewjsSyncSiteResources dependsOn jalviewjsSyncBuildProperties dependsOn jalviewjsSyncCore } task jalviewjsBuildSite { group "JalviewJS" description "Builds the whole website including transpiled code" dependsOn jalviewjsCopyTransferSiteJs dependsOn jalviewjsPrepareSite } task cleanJalviewjsTransferSite { doFirst { delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}" delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}" delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}" delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}" } } task cleanJalviewjsSite { dependsOn cleanJalviewjsTransferSite doFirst { delete "${jalviewDir}/${jalviewjsSiteDir}" } } task jalviewjsSiteTar(type: Tar) { group "JalviewJS" description "Creates a tar.gz file for the website" dependsOn jalviewjsBuildSite def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz" archiveFileName = outputFilename compression Compression.GZIP from "${jalviewDir}/${jalviewjsSiteDir}" into jalviewjs_site_dir // this is inside the tar file inputs.dir("${jalviewDir}/${jalviewjsSiteDir}") } task jalviewjsServer { group "JalviewJS" def filename = "jalviewjsTest.html" description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port." def htmlFile = "${jalviewDirAbsolutePath}/${filename}" doLast { def factory try { def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory") factory = f.newInstance() } catch (ClassNotFoundException e) { throw new GradleException("Unable to create SimpleHttpFileServerFactory") } def port = Integer.valueOf(jalviewjs_server_port) def start = port def running = false def url def jalviewjsServer while(port < start+1000 && !running) { try { def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}") jalviewjsServer = factory.start(doc_root, port) running = true url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource) println("SERVER STARTED with document root ${doc_root}.") println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).") println("For debug: "+url+"?j2sdebug") println("For verbose: "+url+"?j2sverbose") } catch (Exception e) { port++; } } def htmlText = """

JalviewJS Test. <${url}>

JalviewJS Test with debug. <${url}?j2sdebug>

JalviewJS Test with verbose. <${url}?j2sdebug>

""" jalviewjsCoreClasslists.each { cl -> def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName()) htmlText += """

${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}>

""" println("For core ${cl.name}: "+urlcore) } file(htmlFile).text = htmlText } outputs.file(htmlFile) outputs.upToDateWhen({false}) } task cleanJalviewjsAll { group "JalviewJS" description "Delete all configuration and build artifacts to do with JalviewJS build" dependsOn cleanJalviewjsSite dependsOn jalviewjsEclipsePaths doFirst { delete "${jalviewDir}/${jalviewjsBuildDir}" delete "${jalviewDir}/${eclipse_bin_dir}" if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) { delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata") } delete jalviewjsJ2sAltSettingsFileName } outputs.upToDateWhen( { false } ) } task jalviewjsIDE_checkJ2sPlugin { group "00 JalviewJS in Eclipse" description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)" doFirst { def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}") def j2sPluginFile = file(j2sPlugin) def eclipseHome = System.properties["eclipse.home.location"] if (eclipseHome == null || ! IN_ECLIPSE) { throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.") } def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ] def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"] if (altPluginsDir != null && file(altPluginsDir).exists()) { eclipseJ2sPluginDirs += altPluginsDir } def foundPlugin = false def j2sPluginFileName = j2sPluginFile.getName() def eclipseJ2sPlugin def eclipseJ2sPluginFile eclipseJ2sPluginDirs.any { dir -> eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}" eclipseJ2sPluginFile = file(eclipseJ2sPlugin) if (eclipseJ2sPluginFile.exists()) { foundPlugin = true return true } } if (!foundPlugin) { def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin" System.err.println(msg) throw new StopExecutionException(msg) } def digest = MessageDigest.getInstance("MD5") digest.update(j2sPluginFile.text.bytes) def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0') digest.update(eclipseJ2sPluginFile.text.bytes) def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0') if (j2sPluginMd5 != eclipseJ2sPluginMd5) { def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'" System.err.println(msg) throw new StopExecutionException(msg) } else { def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)" println(msg) } } } task jalviewjsIDE_copyJ2sPlugin { group "00 JalviewJS in Eclipse" description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir" doFirst { def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}") def j2sPluginFile = file(j2sPlugin) def eclipseHome = System.properties["eclipse.home.location"] if (eclipseHome == null || ! IN_ECLIPSE) { throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.") } def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}" def eclipseJ2sPluginFile = file(eclipseJ2sPlugin) def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart" System.err.println(msg) copy { from j2sPlugin eclipseJ2sPluginFile.getParentFile().mkdirs() into eclipseJ2sPluginFile.getParent() } } } task jalviewjsIDE_j2sFile { group "00 JalviewJS in Eclipse" description "Creates the .j2s file" dependsOn jalviewjsCreateJ2sSettings } task jalviewjsIDE_SyncCore { group "00 JalviewJS in Eclipse" description "Build the core js lib closures listed in the classlists dir and publish core html from template" dependsOn jalviewjsSyncCore } task jalviewjsIDE_SyncSiteAll { dependsOn jalviewjsSyncAllLibs dependsOn jalviewjsSyncResources dependsOn jalviewjsSyncSiteResources dependsOn jalviewjsSyncBuildProperties } cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll task jalviewjsIDE_PrepareSite { group "00 JalviewJS in Eclipse" description "Sync libs and resources to site dir, but not closure cores" dependsOn jalviewjsIDE_SyncSiteAll //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task } task jalviewjsIDE_AssembleSite { group "00 JalviewJS in Eclipse" description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site" dependsOn jalviewjsPrepareSite } task jalviewjsIDE_SiteClean { group "00 JalviewJS in Eclipse" description "Deletes the Eclipse transpiled site" dependsOn cleanJalviewjsSite } task jalviewjsIDE_Server { group "00 JalviewJS in Eclipse" description "Starts a webserver on localhost to test the website" dependsOn jalviewjsServer } // buildship runs this at import or gradle refresh task eclipseSynchronizationTask { //dependsOn eclipseSetup dependsOn createBuildProperties if (J2S_ENABLED) { dependsOn jalviewjsIDE_j2sFile dependsOn jalviewjsIDE_checkJ2sPlugin dependsOn jalviewjsIDE_PrepareSite } } // buildship runs this at build time or project refresh task eclipseAutoBuildTask { //dependsOn jalviewjsIDE_checkJ2sPlugin //dependsOn jalviewjsIDE_PrepareSite } 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 } def execArgs = [ "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER "--headless=new", "--disable-gpu", "--timeout=${timeoutms}", "--virtual-time-budget=${timeoutms}", "--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 JalviewJS site and run the launch test" dependsOn jalviewjsBuildSite dependsOn jalviewjsLaunchTest }