1 /* Convention for properties. Read from gradle.properties, use lower_case_underlines for property names.
2 * For properties set within build.gradle, use camelCaseNoSpace.
4 import org.apache.tools.ant.filters.ReplaceTokens
5 import org.gradle.internal.os.OperatingSystem
6 import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject
7 import org.gradle.api.internal.PropertiesTransformer
8 import org.gradle.util.ConfigureUtil
9 import org.gradle.plugins.ide.eclipse.model.Output
10 import org.gradle.plugins.ide.eclipse.model.Library
11 import java.security.MessageDigest
12 import java.util.regex.Matcher
13 import java.util.concurrent.Executors
14 import java.util.concurrent.Future
15 import java.util.concurrent.ScheduledExecutorService
16 import java.util.concurrent.TimeUnit
17 import java.nio.file.Path
18 import groovy.transform.ExternalizeMethods
19 import groovy.util.XmlParser
20 import groovy.xml.XmlUtil
21 import groovy.json.JsonBuilder
22 import com.vladsch.flexmark.util.ast.Node
23 import com.vladsch.flexmark.html.HtmlRenderer
24 import com.vladsch.flexmark.parser.Parser
25 import com.vladsch.flexmark.util.data.MutableDataSet
26 import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
27 import com.vladsch.flexmark.ext.tables.TablesExtension
28 import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
29 import com.vladsch.flexmark.ext.autolink.AutolinkExtension
30 import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
31 import com.vladsch.flexmark.ext.toc.TocExtension
32 import com.google.common.hash.HashCode
33 import com.google.common.hash.Hashing
34 import com.google.common.io.Files
35 import org.jsoup.Jsoup
36 import org.jsoup.nodes.Element
44 classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
45 classpath "org.jsoup:jsoup:1.14.3"
46 classpath "com.eowise:gradle-imagemagick:0.5.1"
55 id "com.diffplug.gradle.spotless" version "3.28.0"
56 id 'com.github.johnrengelman.shadow' version '6.0.0'
57 id 'com.install4j.gradle' version '10.0.3'
58 id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree
59 id 'com.palantir.git-version' version '0.13.0' apply false
70 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
71 def string(Object o) {
72 return o == null ? "" : o.toString()
75 def overrideProperties(String propsFileName, boolean output = false) {
76 if (propsFileName == null) {
79 def propsFile = file(propsFileName)
80 if (propsFile != null && propsFile.exists()) {
81 println("Using properties from file '${propsFileName}'")
83 def p = new Properties()
84 def localPropsFIS = new FileInputStream(propsFile)
90 if (project.hasProperty(key)) {
91 oldval = project.findProperty(key)
92 project.setProperty(key, val)
94 println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
97 ext.setProperty(key, val)
99 println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
103 } catch (Exception e) {
104 println("Exception reading local.properties")
111 jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
112 jalviewDirRelativePath = jalviewDir
115 getdownChannelName = CHANNEL.toLowerCase()
116 // default to "default". Currently only has different cosmetics for "develop", "release", "default"
117 propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
118 channelDirName = propertiesChannelName
119 // Import channel_properties
120 if (getdownChannelName.startsWith("develop-")) {
121 channelDirName = "develop-SUFFIX"
123 channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
124 channelGradleProperties = string("${channelDir}/channel_gradle.properties")
125 channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
126 overrideProperties(channelGradleProperties, false)
127 // local build environment properties
128 // can be "projectDir/local.properties"
129 overrideProperties("${projectDir}/local.properties", true)
130 // or "../projectDir_local.properties"
131 overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
134 // Import releaseProps from the RELEASE file
135 // or a file specified via JALVIEW_RELEASE_FILE if defined
136 // Expect jalview.version and target release branch in jalview.release
137 releaseProps = new Properties();
138 def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
139 def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
141 (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream {
142 releaseProps.load(it)
144 } catch (Exception fileLoadError) {
145 throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
148 // Set JALVIEW_VERSION if it is not already set
149 if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
150 JALVIEW_VERSION = releaseProps.get("jalview.version")
152 println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
154 // this property set when running Eclipse headlessly
155 j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
156 // this property set by Eclipse
157 eclipseApplicationProperty = string("eclipse.application")
158 // CHECK IF RUNNING FROM WITHIN ECLIPSE
159 def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
160 IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
161 // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
162 if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
163 println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
167 println("WITHIN ECLIPSE IDE")
169 println("HEADLESS BUILD")
172 J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
174 println("J2S ENABLED")
177 System.properties.sort { it.key }.each {
178 key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
181 if (false && IN_ECLIPSE) {
182 jalviewDir = jalviewDirAbsolutePath
187 buildDate = new Date().format("yyyyMMdd")
190 bareSourceDir = string(source_dir)
191 sourceDir = string("${jalviewDir}/${bareSourceDir}")
192 resourceDir = string("${jalviewDir}/${resource_dir}")
193 bareTestSourceDir = string(test_source_dir)
194 testDir = string("${jalviewDir}/${bareTestSourceDir}")
196 classesDir = string("${jalviewDir}/${classes_dir}")
199 useClover = clover.equals("true")
200 cloverBuildDir = "${buildDir}/clover"
201 cloverInstrDir = file("${cloverBuildDir}/clover-instr")
202 cloverClassesDir = file("${cloverBuildDir}/clover-classes")
203 cloverReportDir = file("${buildDir}/reports/clover")
204 cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
205 cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
206 //cloverTestClassesDir = cloverClassesDir
207 cloverDb = string("${cloverBuildDir}/clover.db")
209 testSourceDir = useClover ? cloverTestInstrDir : testDir
210 testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
213 backgroundImageText = BACKGROUNDIMAGETEXT
214 getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
215 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
216 getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
217 getdownFullArchiveDir = null
218 getdownTextLines = []
219 getdownLaunchJvl = null
220 getdownVersionLaunchJvl = null
222 buildProperties = null
224 // the following values might be overridden by the CHANNEL switch
225 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
226 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
227 getdownArchiveAppBase = getdown_archive_base
228 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
229 getdownAppDistDir = getdown_app_dir_alt
230 getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
231 getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown")
232 getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
233 reportRsyncCommand = false
234 jvlChannelName = CHANNEL.toLowerCase()
235 install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
236 install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
237 install4jDMGBackgroundImageDir = "${install4j_images_dir}"
238 install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j"
239 install4jDMGBackgroundImageFile = "${install4j_dmg_background}"
240 install4jInstallerName = "${jalview_name} Non-Release Installer"
241 install4jExecutableName = install4j_executable_name
242 install4jExtraScheme = "jalviewx"
243 install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
244 install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
245 install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
246 install4jBackground = string("${install4j_images_dir}/${install4j_background}")
247 install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
248 install4jCheckSums = true
250 applicationName = "${jalview_name}"
254 // TODO: get bamboo build artifact URL for getdown artifacts
255 getdown_channel_base = bamboo_channelbase
256 getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
257 getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
258 jvlChannelName += "_${getdownChannelName}"
259 // automatically add the test group Not-bamboo for exclusion
260 if ("".equals(testng_excluded_groups)) {
261 testng_excluded_groups = "Not-bamboo"
263 install4jExtraScheme = "jalviewb"
264 backgroundImageText = true
267 case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
268 getdownAppDistDir = getdown_app_dir_release
269 getdownSetAppBaseProperty = true
270 reportRsyncCommand = true
272 install4jInstallerName = "${jalview_name} Installer"
276 getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
277 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
278 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
279 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
280 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
282 package_dir = string("${ARCHIVEDIR}/${package_dir}")
283 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
286 reportRsyncCommand = true
287 install4jExtraScheme = "jalviewa"
291 getdownChannelName = string("archive/${JALVIEW_VERSION}")
292 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
293 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
294 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
295 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']")
297 package_dir = string("${ARCHIVEDIR}/${package_dir}")
298 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
301 reportRsyncCommand = true
302 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
303 install4jSuffix = "Archive"
304 install4jExtraScheme = "jalviewa"
307 case ~/^DEVELOP-([\.\-\w]*)$/:
308 def suffix = Matcher.lastMatcher[0][1]
309 reportRsyncCommand = true
310 getdownSetAppBaseProperty = true
311 JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}"
312 install4jSuffix = "Develop ${suffix}"
313 install4jExtraScheme = "jalviewd"
314 install4jInstallerName = "${jalview_name} Develop ${suffix} Installer"
315 getdownChannelName = string("develop-${suffix}")
316 getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}")
317 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
318 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
319 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
320 channelSuffix = string(suffix)
321 backgroundImageText = true
325 reportRsyncCommand = true
326 getdownSetAppBaseProperty = true
327 // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
328 JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
330 install4jSuffix = "Develop"
331 install4jExtraScheme = "jalviewd"
332 install4jInstallerName = "${jalview_name} Develop Installer"
333 backgroundImageText = true
337 reportRsyncCommand = true
338 getdownSetAppBaseProperty = true
339 // Don't ignore transpile errors for release build
340 if (jalviewjs_ignore_transpile_errors.equals("true")) {
341 jalviewjs_ignore_transpile_errors = "false"
342 println("Setting jalviewjs_ignore_transpile_errors to 'false'")
344 JALVIEW_VERSION = JALVIEW_VERSION+"-test"
345 install4jSuffix = "Test"
346 install4jExtraScheme = "jalviewt"
347 install4jInstallerName = "${jalview_name} Test Installer"
348 backgroundImageText = true
351 case ~/^SCRATCH(|-[-\w]*)$/:
352 getdownChannelName = CHANNEL
353 JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
355 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
356 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
357 reportRsyncCommand = true
358 install4jSuffix = "Scratch"
362 if (!file("${LOCALDIR}").exists()) {
363 throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
365 getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
366 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
368 JALVIEW_VERSION = "TEST"
369 install4jSuffix = "Test-Local"
370 install4jExtraScheme = "jalviewt"
371 install4jInstallerName = "${jalview_name} Test Installer"
372 backgroundImageText = true
375 case [ "LOCAL", "JALVIEWJS" ]:
376 JALVIEW_VERSION = "TEST"
377 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
378 getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
379 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
380 install4jExtraScheme = "jalviewl"
381 install4jCheckSums = false
384 default: // something wrong specified
385 throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
389 JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
390 hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
391 hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
392 // override getdownAppBase if requested
393 if (findProperty("getdown_appbase_override") != null) {
394 // revert to LOCAL if empty string
395 if (string(getdown_appbase_override) == "") {
396 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
397 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
398 } else if (string(getdown_appbase_override).startsWith("file://")) {
399 getdownAppBase = string(getdown_appbase_override)
400 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
402 getdownAppBase = string(getdown_appbase_override)
404 println("Overriding getdown appbase with '${getdownAppBase}'")
406 // sanitise file name for jalview launcher file for this channel
407 jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
408 // install4j application and folder names
409 if (install4jSuffix == "") {
410 install4jBundleId = "${install4j_bundle_id}"
411 install4jWinApplicationId = install4j_release_win_application_id
413 applicationName = "${jalview_name} ${install4jSuffix}"
414 install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
415 // add int hash of install4jSuffix to the last part of the application_id
416 def id = install4j_release_win_application_id
417 def idsplitreverse = id.split("-").reverse()
418 idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
419 install4jWinApplicationId = idsplitreverse.reverse().join("-")
421 // sanitise folder and id names
422 // install4jApplicationFolder = e.g. "Jalview Build"
423 install4jApplicationFolder = applicationName
424 .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
425 .replaceAll("_+", "_") // collapse __
426 install4jInternalId = applicationName
428 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
429 .replaceAll("_+", "") // collapse __
430 //.replaceAll("_*-_*", "-") // collapse _-_
431 install4jUnixApplicationFolder = applicationName
433 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
434 .replaceAll("_+", "_") // collapse __
435 .replaceAll("_*-_*", "-") // collapse _-_
438 getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
439 getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
440 //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
441 getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
442 getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
443 getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
444 getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
445 /* compile without modules -- using classpath libraries
446 modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
447 modules_runtimeClasspath = modules_compileClasspath
453 apply plugin: "com.palantir.git-version"
454 def details = versionDetails()
455 gitHash = details.gitHash
456 gitBranch = details.branchName
457 } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
458 println("Not in a git repository. Using git values from RELEASE properties file.")
459 gitHash = releaseProps.getProperty("git.hash")
460 gitBranch = releaseProps.getProperty("git.branch")
461 } catch(java.lang.RuntimeException e1) {
462 throw new GradleException("Error with git-version plugin. Directory '.git' exists but versionDetails() cannot be found.")
465 println("Using a ${CHANNEL} profile.")
467 additional_compiler_args = []
468 // configure classpath/args for j8/j11 compilation
469 if (JAVA_VERSION.equals("1.8")) {
470 JAVA_INTEGER_VERSION = string("8")
473 libDistDir = j8libDir
474 compile_source_compatibility = 1.8
475 compile_target_compatibility = 1.8
476 // these are getdown.txt properties defined dependent on the JAVA_VERSION
477 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
478 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
479 // this property is assigned below and expanded to multiple lines in the getdown task
480 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
481 // this property is for the Java library used in eclipse
482 eclipseJavaRuntimeName = string("JavaSE-1.8")
483 } else if (JAVA_VERSION.equals("11")) {
484 JAVA_INTEGER_VERSION = string("11")
486 libDistDir = j11libDir
487 compile_source_compatibility = 11
488 compile_target_compatibility = 11
489 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
490 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
491 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
492 eclipseJavaRuntimeName = string("JavaSE-11")
493 /* compile without modules -- using classpath libraries
494 additional_compiler_args += [
495 '--module-path', modules_compileClasspath.asPath,
496 '--add-modules', j11modules
499 } else if (JAVA_VERSION.equals("17")) {
500 JAVA_INTEGER_VERSION = string("17")
502 libDistDir = j17libDir
503 compile_source_compatibility = 17
504 compile_target_compatibility = 17
505 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
506 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
507 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
508 eclipseJavaRuntimeName = string("JavaSE-17")
509 /* compile without modules -- using classpath libraries
510 additional_compiler_args += [
511 '--module-path', modules_compileClasspath.asPath,
512 '--add-modules', j11modules
516 throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
521 JAVA_MIN_VERSION = JAVA_VERSION
522 JAVA_MAX_VERSION = JAVA_VERSION
523 jreInstallsDir = string(jre_installs_dir)
524 if (jreInstallsDir.startsWith("~/")) {
525 jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
527 install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
528 install4jConfFileName = string("jalview-install4j-conf.install4j")
529 install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
530 install4jHomeDir = install4j_home_dir
531 if (install4jHomeDir.startsWith("~/")) {
532 install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
535 resourceBuildDir = string("${buildDir}/resources")
536 resourcesBuildDir = string("${resourceBuildDir}/resources_build")
537 helpBuildDir = string("${resourceBuildDir}/help_build")
538 docBuildDir = string("${resourceBuildDir}/doc_build")
540 if (buildProperties == null) {
541 buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
543 buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
544 helpParentDir = string("${jalviewDir}/${help_parent_dir}")
545 helpSourceDir = string("${helpParentDir}/${help_dir}")
546 helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
549 convertBinaryExpectedLocation = imagemagick_convert
550 if (convertBinaryExpectedLocation.startsWith("~/")) {
551 convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1)
553 if (file(convertBinaryExpectedLocation).exists()) {
554 convertBinary = convertBinaryExpectedLocation
557 relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
558 jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
559 jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
561 jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
563 jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/sitejs")
565 jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/lib")
566 jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/swingjs")
567 jalviewjsTransferSiteMergeDir = string("${jalviewjsBuildDir}/merge/${jalviewjs_site_dir}")
568 jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/core")
569 jalviewjsJalviewCoreHtmlFile = string("")
570 jalviewjsJalviewCoreName = string(jalviewjs_core_name)
571 jalviewjsCoreClasslists = []
572 jalviewjsJalviewTemplateName = string(jalviewjs_name)
573 jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
574 jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
575 jalviewjsJ2sProps = null
576 jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
577 jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
579 eclipseWorkspace = null
580 eclipseBinary = string("")
581 eclipseVersion = string("")
582 eclipseProductVersion = string("")
585 jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
586 jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}"
596 outputDir = file(classesDir)
600 srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
603 compileClasspath = files(sourceSets.main.java.outputDir)
604 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
606 runtimeClasspath = compileClasspath
607 runtimeClasspath += files(sourceSets.main.resources.srcDirs)
612 srcDirs cloverInstrDir
613 outputDir = cloverClassesDir
617 srcDirs = sourceSets.main.resources.srcDirs
620 compileClasspath = files( sourceSets.clover.java.outputDir )
621 //compileClasspath += files( testClassesDir )
622 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
623 compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
624 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
626 runtimeClasspath = compileClasspath
631 srcDirs testSourceDir
632 outputDir = file(testClassesDir)
636 srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
639 compileClasspath = files( sourceSets.test.java.outputDir )
640 compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
641 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
643 runtimeClasspath = compileClasspath
644 runtimeClasspath += files(sourceSets.test.resources.srcDirs)
650 // eclipse project and settings files creation, also used by buildship
653 name = eclipse_project_name
655 natures 'org.eclipse.jdt.core.javanature',
656 'org.eclipse.jdt.groovy.core.groovyNature',
657 'org.eclipse.buildship.core.gradleprojectnature'
659 buildCommand 'org.eclipse.jdt.core.javabuilder'
660 buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
664 //defaultOutputDir = sourceSets.main.java.outputDir
665 configurations.each{ c->
666 if (c.isCanBeResolved()) {
667 minusConfigurations += [c]
671 plusConfigurations = [ ]
675 def removeTheseToo = []
676 HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
677 cp.entries.each { entry ->
678 // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
679 // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
680 // we add the resources and help/help dirs in as libs afterwards (see below)
681 if (entry.kind == 'src') {
682 if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
683 removeTheseToo += entry
685 alreadyAddedSrcPath.putAt(entry.path, true)
690 cp.entries.removeAll(removeTheseToo)
692 //cp.entries += new Output("${eclipse_bin_dir}/main")
693 if (file(helpParentDir).isDirectory()) {
694 cp.entries += new Library(fileReference(helpParentDir))
696 if (file(resourceDir).isDirectory()) {
697 cp.entries += new Library(fileReference(resourceDir))
700 HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
702 sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
703 //don't want to add outputDir as eclipse is using its own output dir in bin/main
704 if (it.isDirectory() || ! it.exists()) {
705 // don't add dirs to classpath, especially if they don't exist
706 return false // groovy "continue" in .any closure
708 def itPath = it.toString()
709 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
710 // make relative path
711 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
713 if (alreadyAddedLibPath.get(itPath)) {
714 //println("Not adding duplicate entry "+itPath)
716 //println("Adding entry "+itPath)
717 cp.entries += new Library(fileReference(itPath))
718 alreadyAddedLibPath.put(itPath, true)
722 sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
723 //no longer want to add outputDir as eclipse is using its own output dir in bin/main
724 if (it.isDirectory() || ! it.exists()) {
725 // don't add dirs to classpath
726 return false // groovy "continue" in .any closure
729 def itPath = it.toString()
730 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
731 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
733 if (alreadyAddedLibPath.get(itPath)) {
736 def lib = new Library(fileReference(itPath))
737 lib.entryAttributes["test"] = "true"
739 alreadyAddedLibPath.put(itPath, true)
747 containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
752 // for the IDE, use java 11 compatibility
753 sourceCompatibility = compile_source_compatibility
754 targetCompatibility = compile_target_compatibility
755 javaRuntimeName = eclipseJavaRuntimeName
757 // add in jalview project specific properties/preferences into eclipse core preferences
759 withProperties { props ->
760 def jalview_prefs = new Properties()
761 def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
762 jalview_prefs.load(ins)
764 jalview_prefs.forEach { t, v ->
765 if (props.getAt(t) == null) {
769 // codestyle file -- overrides previous formatter prefs
770 def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
771 if (csFile.exists()) {
772 XmlParser parser = new XmlParser()
773 def profiles = parser.parse(csFile)
774 def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
775 if (profile != null) {
776 profile.'setting'.each { s ->
778 def value = s.'@value'
779 if (id != null && value != null) {
780 props.putAt(id, value)
791 // Don't want these to be activated if in headless build
792 synchronizationTasks "eclipseSynchronizationTask"
793 //autoBuildTasks "eclipseAutoBuildTask"
799 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
800 // Class to allow updating arbitrary properties files
801 class PropertiesFile extends PropertiesPersistableConfigurationObject {
802 public PropertiesFile(PropertiesTransformer t) { super(t); }
803 @Override protected void load(Properties properties) { }
804 @Override protected void store(Properties properties) { }
805 @Override protected String getDefaultResourceName() { return ""; }
806 // This is necessary, because PropertiesPersistableConfigurationObject fails
807 // if no default properties file exists.
808 @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
811 // Task to update arbitrary properties files (set outputFile)
812 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
813 private final PropertiesFileContentMerger file;
814 public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
815 protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
816 protected void configure(PropertiesFile props) {
817 file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
819 public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
822 task eclipseUIPreferences(type: PropertiesFileTask) {
823 description = "Generate Eclipse additional settings"
824 def filename = "org.eclipse.jdt.ui.prefs"
825 outputFile = "$projectDir/.settings/${filename}" as File
828 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
833 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
834 description = "Generate Eclipse additional settings"
835 def filename = "org.eclipse.jdt.groovy.core.prefs"
836 outputFile = "$projectDir/.settings/${filename}" as File
839 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
844 task eclipseAllPreferences {
846 dependsOn eclipseUIPreferences
847 dependsOn eclipseGroovyCorePreferences
850 eclipseUIPreferences.mustRunAfter eclipseJdt
851 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
853 /* end of eclipse preferences hack */
861 delete cloverBuildDir
862 delete cloverReportDir
867 task cloverInstrJava(type: JavaExec) {
868 group = "Verification"
869 description = "Create clover instrumented source java files"
871 dependsOn cleanClover
873 inputs.files(sourceSets.main.allJava)
874 outputs.dir(cloverInstrDir)
876 //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
877 classpath = sourceSets.clover.compileClasspath
878 main = "com.atlassian.clover.CloverInstr"
886 cloverInstrDir.getPath(),
888 def srcFiles = sourceSets.main.allJava.files
891 { file -> file.absolutePath }
894 args argsList.toArray()
897 delete cloverInstrDir
898 println("Clover: About to instrument "+srcFiles.size() +" files")
903 task cloverInstrTests(type: JavaExec) {
904 group = "Verification"
905 description = "Create clover instrumented source test files"
907 dependsOn cleanClover
909 inputs.files(testDir)
910 outputs.dir(cloverTestInstrDir)
912 classpath = sourceSets.clover.compileClasspath
913 main = "com.atlassian.clover.CloverInstr"
923 cloverTestInstrDir.getPath(),
925 args argsList.toArray()
928 delete cloverTestInstrDir
929 println("Clover: About to instrument test files")
935 group = "Verification"
936 description = "Create clover instrumented all source files"
938 dependsOn cloverInstrJava
939 dependsOn cloverInstrTests
943 cloverClasses.dependsOn cloverInstr
946 task cloverConsoleReport(type: JavaExec) {
947 group = "Verification"
948 description = "Creates clover console report"
951 file(cloverDb).exists()
954 inputs.dir cloverClassesDir
956 classpath = sourceSets.clover.runtimeClasspath
957 main = "com.atlassian.clover.reporters.console.ConsoleReporter"
959 if (cloverreport_mem.length() > 0) {
960 maxHeapSize = cloverreport_mem
962 if (cloverreport_jvmargs.length() > 0) {
963 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
973 args argsList.toArray()
977 task cloverHtmlReport(type: JavaExec) {
978 group = "Verification"
979 description = "Creates clover HTML report"
982 file(cloverDb).exists()
985 def cloverHtmlDir = cloverReportDir
986 inputs.dir cloverClassesDir
987 outputs.dir cloverHtmlDir
989 classpath = sourceSets.clover.runtimeClasspath
990 main = "com.atlassian.clover.reporters.html.HtmlReporter"
992 if (cloverreport_mem.length() > 0) {
993 maxHeapSize = cloverreport_mem
995 if (cloverreport_jvmargs.length() > 0) {
996 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1007 if (cloverreport_html_options.length() > 0) {
1008 argsList += cloverreport_html_options.split(" ")
1011 args argsList.toArray()
1015 task cloverXmlReport(type: JavaExec) {
1016 group = "Verification"
1017 description = "Creates clover XML report"
1020 file(cloverDb).exists()
1023 def cloverXmlFile = "${cloverReportDir}/clover.xml"
1024 inputs.dir cloverClassesDir
1025 outputs.file cloverXmlFile
1027 classpath = sourceSets.clover.runtimeClasspath
1028 main = "com.atlassian.clover.reporters.xml.XMLReporter"
1030 if (cloverreport_mem.length() > 0) {
1031 maxHeapSize = cloverreport_mem
1033 if (cloverreport_jvmargs.length() > 0) {
1034 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1045 if (cloverreport_xml_options.length() > 0) {
1046 argsList += cloverreport_xml_options.split(" ")
1049 args argsList.toArray()
1054 group = "Verification"
1055 description = "Creates clover reports"
1057 dependsOn cloverXmlReport
1058 dependsOn cloverHtmlReport
1065 sourceCompatibility = compile_source_compatibility
1066 targetCompatibility = compile_target_compatibility
1067 options.compilerArgs += additional_compiler_args
1068 print ("Setting target compatibility to "+targetCompatibility+"\n")
1070 //classpath += configurations.cloverRuntime
1076 // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1077 sourceCompatibility = compile_source_compatibility
1078 targetCompatibility = compile_target_compatibility
1079 options.compilerArgs += additional_compiler_args
1080 options.encoding = "UTF-8"
1082 print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1089 sourceCompatibility = compile_source_compatibility
1090 targetCompatibility = compile_target_compatibility
1091 options.compilerArgs += additional_compiler_args
1093 print ("Setting target compatibility to "+targetCompatibility+"\n")
1100 delete sourceSets.main.java.outputDir
1106 dependsOn cleanClover
1108 delete sourceSets.test.java.outputDir
1113 // format is a string like date.format("dd MMMM yyyy")
1114 def getDate(format) {
1115 return date.format(format)
1119 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1120 MutableDataSet options = new MutableDataSet()
1122 def extensions = new ArrayList<>()
1123 extensions.add(AnchorLinkExtension.create())
1124 extensions.add(AutolinkExtension.create())
1125 extensions.add(StrikethroughExtension.create())
1126 extensions.add(TaskListExtension.create())
1127 extensions.add(TablesExtension.create())
1128 extensions.add(TocExtension.create())
1130 options.set(Parser.EXTENSIONS, extensions)
1132 // set GFM table parsing options
1133 options.set(TablesExtension.WITH_CAPTION, false)
1134 options.set(TablesExtension.COLUMN_SPANS, false)
1135 options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1136 options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1137 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1138 options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1139 options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1141 options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1142 options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1143 options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1144 options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1146 Parser parser = Parser.builder(options).build()
1147 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1149 mdFiles.each { mdFile ->
1150 // add table of contents
1151 def mdText = "[TOC]\n"+mdFile.text
1153 // grab the first top-level title
1155 def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1156 def matcher = mdText =~ titleRegex
1157 if (matcher.size() > 0) {
1158 // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1159 title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1161 // or use the filename if none found
1162 if (title == null) {
1163 title = mdFile.getName()
1166 Node document = parser.parse(mdText)
1167 String htmlBody = renderer.render(document)
1168 def htmlText = '''<html>
1169 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1170 <html xmlns="http://www.w3.org/1999/xhtml">
1172 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1173 <meta http-equiv="Content-Style-Type" content="text/css" />
1174 <meta name="generator" content="flexmark" />
1176 htmlText += ((title != null) ? " <title>${title}</title>" : '' )
1178 <style type="text/css">code{white-space: pre;}</style>
1180 htmlText += ((cssFile != null) ? cssFile.text : '')
1181 htmlText += '''</head>
1184 htmlText += htmlBody
1190 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1191 def htmlFile = file(htmlFilePath)
1192 println("Creating ${htmlFilePath}")
1193 htmlFile.text = htmlText
1198 task copyDocs(type: Copy) {
1199 def inputDir = "${jalviewDir}/${doc_dir}"
1200 def outputDir = "${docBuildDir}/${doc_dir}"
1204 include('**/*.html')
1206 filter(ReplaceTokens,
1210 'Version-Rel': JALVIEW_VERSION,
1211 'Year-Rel': getDate("yyyy")
1218 exclude('**/*.html')
1223 inputs.dir(inputDir)
1224 outputs.dir(outputDir)
1228 task convertMdFiles {
1230 def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1231 def cssFile = file("${jalviewDir}/${flexmark_css}")
1234 convertMdToHtml(mdFiles, cssFile)
1237 inputs.files(mdFiles)
1238 inputs.file(cssFile)
1241 mdFiles.each { mdFile ->
1242 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1243 htmlFiles.add(file(htmlFilePath))
1245 outputs.files(htmlFiles)
1249 def hugoTemplateSubstitutions(String input, Map extras=null) {
1250 def replacements = [
1251 DATE: getDate("yyyy-MM-dd"),
1252 CHANNEL: propertiesChannelName,
1253 APPLICATION_NAME: applicationName,
1255 GIT_BRANCH: gitBranch,
1256 VERSION: JALVIEW_VERSION,
1257 JAVA_VERSION: JAVA_VERSION,
1258 VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
1263 if (extras != null) {
1264 extras.each{ k, v ->
1265 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1268 replacements.each{ k, v ->
1269 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1274 def mdFileComponents(File mdFile, def dateOnly=false) {
1277 if (mdFile.exists()) {
1278 def inFrontMatter = false
1279 def firstLine = true
1280 mdFile.eachLine { line ->
1281 if (line.matches("---")) {
1282 def prev = inFrontMatter
1283 inFrontMatter = firstLine
1284 if (inFrontMatter != prev)
1287 if (inFrontMatter) {
1289 if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
1290 map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
1291 } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
1292 map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
1293 } else if (m = line =~ /^channel:\s*(\S+)/) {
1294 map["channel"] = m[0][1]
1295 } else if (m = line =~ /^version:\s*(\S+)/) {
1296 map["version"] = m[0][1]
1297 } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
1298 map[ m[0][1] ] = m[0][2]
1300 if (dateOnly && map["date"] != null) {
1306 content += line+"\n"
1311 return dateOnly ? map["date"] : [map, content]
1314 task hugoTemplates {
1316 description "Create partially populated md pages for hugo website build"
1318 def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
1319 def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
1320 def templateFiles = fileTree(dir: hugoTemplatesDir)
1321 def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1322 def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1323 def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
1324 def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
1327 // specific release template for version archive
1330 def givenDate = null
1331 def givenChannel = null
1332 def givenVersion = null
1333 if (CHANNEL == "RELEASE") {
1334 def (map, content) = mdFileComponents(releaseMdFile)
1335 givenDate = map.date
1336 givenChannel = map.channel
1337 givenVersion = map.version
1339 if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
1340 throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
1343 if (whatsnewMdFile.exists())
1344 whatsnew = whatsnewMdFile.text
1347 def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
1348 def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
1350 def changesHugo = null
1351 if (changes != null) {
1352 changesHugo = '<div class="release_notes">\n\n'
1353 def inSection = false
1354 changes.eachLine { line ->
1356 if (m = line =~ /^##([^#].*)$/) {
1358 changesHugo += "</div>\n\n"
1360 def section = m[0][1].trim()
1361 section = section.toLowerCase()
1362 section = section.replaceAll(/ +/, "_")
1363 section = section.replaceAll(/[^a-z0-9_\-]/, "")
1364 changesHugo += "<div class=\"${section}\">\n\n"
1366 } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
1367 def comment = m[0][2].trim()
1368 if (comment != "") {
1369 comment = comment.replaceAll('"', """)
1371 comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
1372 def newline = m[0][1]
1373 if (comment.trim() != "")
1374 newline += "{{<comment>}}${comment}{{</comment>}} "
1375 newline += m[0][3].trim()
1376 if (issuekeys.size() > 0)
1377 newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
1378 if (m[0][4] != null)
1383 changesHugo += line+"\n"
1386 changesHugo += "\n</div>\n\n"
1388 changesHugo += '</div>'
1391 templateFiles.each{ templateFile ->
1392 def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
1393 def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
1394 def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
1396 def outPathName = string("${hugoBuildDir}/$newRelPathName")
1400 rename(templateFile.getName(), newFileName)
1404 def newFile = file("${outPathName}/${newFileName}".toString())
1405 def content = newFile.text
1406 newFile.text = hugoTemplateSubstitutions(content,
1409 CHANGES: changesHugo,
1410 DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
1411 DRAFT: givenDate == null ? "true" : "false",
1412 JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
1413 JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
1420 inputs.file(oldJvlFile)
1421 inputs.dir(hugoTemplatesDir)
1422 inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
1423 inputs.property("CHANNEL", { CHANNEL })
1426 def getMdDate(File mdFile) {
1427 return mdFileComponents(mdFile, true)
1430 def getMdSections(String content) {
1432 def sectionContent = ""
1433 def sectionName = null
1434 content.eachLine { line ->
1436 if (m = line =~ /^##([^#].*)$/) {
1437 if (sectionName != null) {
1438 sections[sectionName] = sectionContent
1442 sectionName = m[0][1].trim()
1443 sectionName = sectionName.toLowerCase()
1444 sectionName = sectionName.replaceAll(/ +/, "_")
1445 sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
1446 } else if (sectionName != null) {
1447 sectionContent += line+"\n"
1450 if (sectionContent != null) {
1451 sections[sectionName] = sectionContent
1457 task copyHelp(type: Copy) {
1458 def inputDir = helpSourceDir
1459 def outputDir = "${helpBuildDir}/${help_dir}"
1463 include('**/*.html')
1467 filter(ReplaceTokens,
1471 'Version-Rel': JALVIEW_VERSION,
1472 'Year-Rel': getDate("yyyy")
1479 exclude('**/*.html')
1486 inputs.dir(inputDir)
1487 outputs.files(helpFile)
1488 outputs.dir(outputDir)
1492 task releasesTemplates {
1494 description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1498 def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1499 def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1500 def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
1501 def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
1502 def releasesMdDir = "${jalviewDir}/${releases_dir}"
1503 def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1506 def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1507 def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1509 if (CHANNEL == "RELEASE") {
1510 if (!releaseMdFile.exists()) {
1511 throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1513 if (!whatsnewMdFile.exists()) {
1514 throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1518 def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1519 def releaseFilesDates = releaseFiles.collectEntries {
1520 [(it): getMdDate(it)]
1522 releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1524 def releasesTemplate = releasesTemplateFile.text
1525 def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1526 def versionTemplate = m[0][1]
1528 MutableDataSet options = new MutableDataSet()
1530 def extensions = new ArrayList<>()
1531 options.set(Parser.EXTENSIONS, extensions)
1532 options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1534 Parser parser = Parser.builder(options).build()
1535 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1537 def actualVersions = releaseFiles.collect { rf ->
1538 def (rfMap, rfContent) = mdFileComponents(rf)
1539 return rfMap.version
1541 def versionsHtml = ""
1542 def linkedVersions = []
1543 releaseFiles.reverse().each { rFile ->
1544 def (rMap, rContent) = mdFileComponents(rFile)
1546 def versionLink = ""
1547 def partialVersion = ""
1548 def firstPart = true
1549 rMap.version.split("\\.").each { part ->
1550 def displayPart = ( firstPart ? "" : "." ) + part
1551 partialVersion += displayPart
1553 linkedVersions.contains(partialVersion)
1554 || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1556 versionLink += displayPart
1558 versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1559 linkedVersions += partialVersion
1563 def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1566 def rContentProcessed = ""
1567 rContent.eachLine { line ->
1568 if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1569 line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1570 } else if (lm = line =~ /^###([^#]+.*)$/) {
1571 line = "_${lm[0][1].trim()}_"
1573 rContentProcessed += line + "\n"
1576 def rContentSections = getMdSections(rContentProcessed)
1577 def rVersion = versionTemplate
1578 if (rVersion != "") {
1579 def rNewFeatures = rContentSections["new_features"]
1580 def rIssuesResolved = rContentSections["issues_resolved"]
1581 Node newFeaturesNode = parser.parse(rNewFeatures)
1582 String newFeaturesHtml = renderer.render(newFeaturesNode)
1583 Node issuesResolvedNode = parser.parse(rIssuesResolved)
1584 String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1585 rVersion = hugoTemplateSubstitutions(rVersion,
1587 VERSION: rMap.version,
1588 VERSION_LINK: versionLink,
1589 DISPLAY_DATE: displayDate,
1590 NEW_FEATURES: newFeaturesHtml,
1591 ISSUES_RESOLVED: issuesResolvedHtml
1594 versionsHtml += rVersion
1598 releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1599 releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1600 releasesHtmlFile.text = releasesTemplate
1602 if (whatsnewMdFile.exists()) {
1603 def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1604 def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1605 Node whatsnewNode = parser.parse(whatsnewMd)
1606 String whatsnewHtml = renderer.render(whatsnewNode)
1607 whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1608 whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1610 VERSION: JALVIEW_VERSION,
1611 DISPLAY_DATE: wnDisplayDate
1614 } else if (gradle.taskGraph.hasTask(":linkCheck")) {
1615 whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
1620 inputs.file(releasesTemplateFile)
1621 inputs.file(whatsnewTemplateFile)
1622 inputs.dir(releasesMdDir)
1623 inputs.dir(whatsnewMdDir)
1624 outputs.file(releasesHtmlFile)
1625 outputs.file(whatsnewHtmlFile)
1629 task copyResources(type: Copy) {
1631 description = "Copy (and make text substitutions in) the resources dir to the build area"
1633 def inputDir = resourceDir
1634 def outputDir = resourcesBuildDir
1638 include('**/*.html')
1640 filter(ReplaceTokens,
1644 'Version-Rel': JALVIEW_VERSION,
1645 'Year-Rel': getDate("yyyy")
1652 exclude('**/*.html')
1657 inputs.dir(inputDir)
1658 outputs.dir(outputDir)
1661 task copyChannelResources(type: Copy) {
1662 dependsOn copyResources
1664 description = "Copy the channel resources dir to the build resources area"
1666 def inputDir = "${channelDir}/${resource_dir}"
1667 def outputDir = resourcesBuildDir
1669 include(channel_props)
1670 filter(ReplaceTokens,
1674 'SUFFIX': channelSuffix
1679 exclude(channel_props)
1683 inputs.dir(inputDir)
1684 outputs.dir(outputDir)
1687 task createBuildProperties(type: WriteProperties) {
1688 dependsOn copyResources
1690 description = "Create the ${buildProperties} file"
1692 inputs.dir(sourceDir)
1693 inputs.dir(resourcesBuildDir)
1694 outputFile (buildProperties)
1695 // taking time specific comment out to allow better incremental builds
1696 comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1697 //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1698 property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1699 property "VERSION", JALVIEW_VERSION
1700 property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1701 property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
1702 if (getdownSetAppBaseProperty) {
1703 property "GETDOWNAPPBASE", getdownAppBase
1704 property "GETDOWNAPPDISTDIR", getdownAppDistDir
1706 outputs.file(outputFile)
1710 task buildIndices(type: JavaExec) {
1712 classpath = sourceSets.main.compileClasspath
1713 main = "com.sun.java.help.search.Indexer"
1714 workingDir = "${helpBuildDir}/${help_dir}"
1717 inputs.dir("${workingDir}/${argDir}")
1719 outputs.dir("${classesDir}/doc")
1720 outputs.dir("${classesDir}/help")
1721 outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1722 outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1723 outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1724 outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1725 outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1726 outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1729 task buildResources {
1730 dependsOn copyResources
1731 dependsOn copyChannelResources
1732 dependsOn createBuildProperties
1736 dependsOn buildResources
1739 dependsOn releasesTemplates
1740 dependsOn convertMdFiles
1741 dependsOn buildIndices
1745 compileJava.dependsOn prepare
1746 run.dependsOn compileJava
1747 compileTestJava.dependsOn compileJava
1752 group = "Verification"
1753 description = "Runs all testTaskN tasks)"
1756 dependsOn cloverClasses
1758 dependsOn testClasses
1761 // not running tests in this task
1764 /* testTask0 is the main test task */
1765 task testTask0(type: Test) {
1766 group = "Verification"
1767 description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
1769 includeGroups testng_groups.split(",")
1770 excludeGroups testng_excluded_groups.split(",")
1771 tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
1773 useDefaultListeners=true
1777 /* separated tests */
1778 task testTask1(type: Test) {
1779 group = "Verification"
1780 description = "Tests that need to be isolated from the main test run"
1783 excludeGroups testng_excluded_groups.split(",")
1785 useDefaultListeners=true
1789 task testTask2(type: Test) {
1790 group = "Verification"
1791 description = "Tests that need to be isolated from the main test run"
1794 excludeGroups testng_excluded_groups.split(",")
1796 useDefaultListeners=true
1799 task testTask3(type: Test) {
1800 group = "Verification"
1801 description = "Tests that need to be isolated from the main test run"
1804 excludeGroups testng_excluded_groups.split(",")
1806 useDefaultListeners=true
1810 /* insert more testTaskNs here -- change N to next digit or other string */
1812 task testTaskN(type: Test) {
1813 group = "Verification"
1814 description = "Tests that need to be isolated from the main test run"
1817 excludeGroups testng_excluded_groups.split(",")
1819 useDefaultListeners=true
1825 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1826 * to summarise test results from all Test tasks
1828 /* START of test tasks results summary */
1829 import groovy.time.TimeCategory
1830 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1831 import org.gradle.api.tasks.testing.logging.TestLogEvent
1832 rootProject.ext.testsResults = [] // Container for tests summaries
1834 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1836 // from original test task
1838 dependsOn cloverClasses
1840 dependsOn testClasses //?
1843 // run main tests first
1844 if (!testTask.name.equals("testTask0"))
1845 testTask.mustRunAfter "testTask0"
1847 testTask.testLogging { logging ->
1848 events TestLogEvent.FAILED
1849 // TestLogEvent.SKIPPED,
1850 // TestLogEvent.STANDARD_OUT,
1851 // TestLogEvent.STANDARD_ERROR
1853 exceptionFormat TestExceptionFormat.FULL
1856 showStackTraces true
1858 showStandardStreams true
1860 info.events = [ TestLogEvent.FAILED ]
1863 if (OperatingSystem.current().isMacOsX()) {
1864 testTask.systemProperty "apple.awt.UIElement", "true"
1865 testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
1869 ignoreFailures = true // Always try to run all tests for all modules
1871 afterSuite { desc, result ->
1873 return // Only summarize results for whole modules
1875 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1877 rootProject.ext.testsResults.add(resultsInfo)
1880 // from original test task
1881 maxHeapSize = "1024m"
1883 workingDir = jalviewDir
1884 def testLaf = project.findProperty("test_laf")
1885 if (testLaf != null) {
1886 println("Setting Test LaF to '${testLaf}'")
1887 systemProperty "laf", testLaf
1889 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1890 if (testHiDPIScale != null) {
1891 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1892 systemProperty "sun.java2d.uiScale", testHiDPIScale
1894 sourceCompatibility = compile_source_compatibility
1895 targetCompatibility = compile_target_compatibility
1896 jvmArgs += additional_compiler_args
1899 // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
1900 // testTasks that include the tests, and exclude all from the others.
1901 // get --test argument
1902 filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
1903 // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
1904 // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
1908 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1913 /* don't fail on no matching tests (so --tests will run across all testTasks) */
1914 testTask.filter.setFailOnNoMatchingTests(false)
1916 /* ensure the "test" task dependsOn all the testTasks */
1917 test.dependsOn testTask
1920 gradle.buildFinished {
1921 def allResults = rootProject.ext.testsResults
1923 if (!allResults.isEmpty()) {
1924 printResults allResults
1925 allResults.each {r ->
1926 if (r[2].resultType == TestResult.ResultType.FAILURE)
1927 throw new GradleException("Failed tests!")
1932 private static String colString(styler, col, colour, text) {
1933 return col?"${styler[colour](text)}":text
1936 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1937 def colour = 'black'
1945 case TestResult.ResultType.SUCCESS:
1948 case TestResult.ResultType.FAILURE:
1956 StringBuilder sb = new StringBuilder()
1960 sb.append(" results: ")
1961 sb.append(colString(s, col && !nocol, colour, text))
1963 sb.append("${rc} tests, ")
1964 sb.append(colString(s, col && rs > 0, 'green', rs))
1965 sb.append(" successes, ")
1966 sb.append(colString(s, col && rf > 0, 'red', rf))
1967 sb.append(" failures, ")
1968 sb.append("${rsk} skipped) in ${t}")
1969 return sb.toString()
1972 private static void printResults(allResults) {
1974 // styler from https://stackoverflow.com/a/56139852
1975 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
1978 def failedTests = false
1979 def summaryLines = []
1981 def totalsuccess = 0
1984 def totaltime = TimeCategory.getSeconds(0)
1985 // sort on project name then task name
1986 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
1987 def projectName = it[0]
1988 def taskName = it[1]
1992 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
1993 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
1994 def reportLine = "Report file: ${report}"
1995 def ls = summaryPlain.length()
1996 def lr = reportLine.length()
1997 def m = [ls, lr].max()
2000 def info = [ls, summaryCol, reportLine]
2001 summaryLines.add(info)
2002 failedTests |= result.resultType == TestResult.ResultType.FAILURE
2003 totalcount += result.testCount
2004 totalsuccess += result.successfulTestCount
2005 totalfail += result.failedTestCount
2006 totalskip += result.skippedTestCount
2009 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
2010 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
2011 def tls = totalSummaryPlain.length()
2012 if (tls > maxLength)
2014 def info = [tls, totalSummaryCol, null]
2015 summaryLines.add(info)
2017 def allSummaries = []
2018 for(sInfo : summaryLines) {
2020 def summary = sInfo[1]
2021 def report = sInfo[2]
2023 StringBuilder sb = new StringBuilder()
2024 sb.append("│" + summary + " " * (maxLength - ls) + "│")
2025 if (report != null) {
2026 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
2028 allSummaries += sb.toString()
2031 println "┌${"${"─" * maxLength}"}┐"
2032 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
2033 println "└${"${"─" * maxLength}"}┘"
2035 /* END of test tasks results summary */
2038 task compileLinkCheck(type: JavaCompile) {
2040 classpath = files("${jalviewDir}/${utils_dir}")
2041 destinationDir = file("${jalviewDir}/${utils_dir}")
2042 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
2044 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2045 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2046 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
2047 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
2051 task linkCheck(type: JavaExec) {
2053 dependsOn compileLinkCheck
2055 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
2056 classpath = files("${jalviewDir}/${utils_dir}")
2057 main = "HelpLinksChecker"
2058 workingDir = "${helpBuildDir}"
2059 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
2061 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
2062 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2065 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2069 inputs.dir(helpBuildDir)
2070 outputs.file(helpLinksCheckerOutFile)
2074 // import the pubhtmlhelp target
2075 ant.properties.basedir = "${jalviewDir}"
2076 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
2077 ant.importBuild "${utils_dir}/publishHelp.xml"
2080 task cleanPackageDir(type: Delete) {
2082 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2092 attributes "Main-Class": main_class,
2093 "Permissions": "all-permissions",
2094 "Application-Name": applicationName,
2095 "Codebase": application_codebase,
2096 "Implementation-Version": JALVIEW_VERSION
2099 def outputDir = "${jalviewDir}/${package_dir}"
2100 destinationDirectory = file(outputDir)
2101 archiveFileName = rootProject.name+".jar"
2102 duplicatesStrategy "EXCLUDE"
2109 exclude "**/*.jar.*"
2111 inputs.dir(sourceSets.main.java.outputDir)
2112 sourceSets.main.resources.srcDirs.each{ dir ->
2115 outputs.file("${outputDir}/${archiveFileName}")
2119 task copyJars(type: Copy) {
2120 from fileTree(dir: classesDir, include: "**/*.jar").files
2121 into "${jalviewDir}/${package_dir}"
2125 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2126 task syncJars(type: Sync) {
2128 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2129 into "${jalviewDir}/${package_dir}"
2131 include jar.archiveFileName.getOrNull()
2138 description = "Put all required libraries in dist"
2139 // order of "cleanPackageDir", "copyJars", "jar" important!
2140 jar.mustRunAfter cleanPackageDir
2141 syncJars.mustRunAfter cleanPackageDir
2142 dependsOn cleanPackageDir
2145 outputs.dir("${jalviewDir}/${package_dir}")
2150 dependsOn cleanPackageDir
2156 task launcherJar(type: Jar) {
2159 "Main-Class": shadow_jar_main_class,
2160 "Implementation-Version": JALVIEW_VERSION,
2161 "Application-Name": applicationName
2167 group = "distribution"
2168 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2173 def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
2174 def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
2175 def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
2180 // shadowJar manifest must inheritFrom another Jar task. Can't set attributes here.
2181 inheritFrom(project.tasks.launcherJar.manifest)
2183 // we need to include the groovy-swing Include-Package for it to run in the shadowJar
2185 def jarFileManifests = []
2186 groovyJars.each { jarFile ->
2187 def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
2189 jarFileManifests += mf
2193 from (jarFileManifests) {
2194 eachEntry { details ->
2195 if (!details.key.equals("Import-Package")) {
2203 duplicatesStrategy "INCLUDE"
2205 // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
2206 mainClassName = shadow_jar_main_class
2208 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2212 task getdownImagesCopy() {
2213 inputs.dir getdownImagesDir
2214 outputs.dir getdownImagesBuildDir
2218 from(getdownImagesDir) {
2219 include("*getdown*.png")
2221 into getdownImagesBuildDir
2226 task getdownImagesProcess() {
2227 dependsOn getdownImagesCopy
2230 if (backgroundImageText) {
2231 if (convertBinary == null) {
2232 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2234 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2235 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2237 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2239 executable convertBinary
2242 '-font', getdown_background_image_text_font,
2243 '-fill', getdown_background_image_text_colour,
2244 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2245 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2246 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2255 task getdownImages() {
2256 dependsOn getdownImagesProcess
2259 task getdownWebsiteBuild() {
2260 group = "distribution"
2261 description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer. No digest is created."
2263 dependsOn getdownImages
2268 def getdownWebsiteResourceFilenames = []
2269 def getdownResourceDir = getdownResourceDir
2270 def getdownResourceFilenames = []
2273 // clean the getdown website and files dir before creating getdown folders
2274 delete getdownAppBaseDir
2275 delete getdownFilesDir
2278 from buildProperties
2279 rename(file(buildProperties).getName(), getdown_build_properties)
2282 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2285 from channelPropsFile
2286 filter(ReplaceTokens,
2290 'SUFFIX': channelSuffix
2293 into getdownAppBaseDir
2295 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2297 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2298 def props = project.properties.sort { it.key }
2299 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2300 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2302 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2303 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2305 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2306 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2308 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2309 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2310 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2311 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2312 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2313 props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
2314 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
2317 props.put("getdown_txt_title", jalview_name)
2318 props.put("getdown_txt_ui.name", applicationName)
2320 // start with appbase
2321 getdownTextLines += "appbase = ${getdownAppBase}"
2322 props.each{ prop, val ->
2323 if (prop.startsWith("getdown_txt_") && val != null) {
2324 if (prop.startsWith("getdown_txt_multi_")) {
2325 def key = prop.substring(18)
2326 val.split(",").each{ v ->
2327 def line = "${key} = ${v}"
2328 getdownTextLines += line
2331 // file values rationalised
2332 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2334 if (val.indexOf('/') == 0) {
2337 } else if (val.indexOf('/') > 0) {
2338 // relative path (relative to jalviewDir)
2339 r = file( "${jalviewDir}/${val}" )
2342 val = "${getdown_resource_dir}/" + r.getName()
2343 getdownWebsiteResourceFilenames += val
2344 getdownResourceFilenames += r.getPath()
2347 if (! prop.startsWith("getdown_txt_resource")) {
2348 def line = prop.substring(12) + " = ${val}"
2349 getdownTextLines += line
2355 getdownWebsiteResourceFilenames.each{ filename ->
2356 getdownTextLines += "resource = ${filename}"
2358 getdownResourceFilenames.each{ filename ->
2361 into getdownResourceDir
2365 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2366 getdownWrapperScripts.each{ script ->
2367 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2371 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2373 getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
2378 fileTree(file(package_dir)).each{ f ->
2379 if (f.isDirectory()) {
2380 def files = fileTree(dir: f, include: ["*"]).getFiles()
2382 } else if (f.exists()) {
2386 def jalviewJar = jar.archiveFileName.getOrNull()
2387 // put jalview.jar first for CLASSPATH and .properties files reasons
2388 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2389 def name = f.getName()
2390 def line = "code = ${getdownAppDistDir}/${name}"
2391 getdownTextLines += line
2398 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2400 if (JAVA_VERSION.equals("11")) {
2401 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2402 j11libFiles.sort().each{f ->
2403 def name = f.getName()
2404 def line = "code = ${getdown_j11lib_dir}/${name}"
2405 getdownTextLines += line
2408 into getdownJ11libDir
2414 // 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.
2415 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2416 getdownTextLines += "resource = ${getdown_launcher_new}"
2417 getdownTextLines += "class = ${main_class}"
2418 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2419 if (getdownSetAppBaseProperty) {
2420 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2421 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2424 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2425 getdownTxt.write(getdownTextLines.join("\n"))
2427 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2428 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2429 launchJvl.write("appbase=${getdownAppBase}")
2431 // files going into the getdown website dir: getdown-launcher.jar
2433 from getdownLauncher
2434 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2435 into getdownAppBaseDir
2438 // files going into the getdown website dir: getdown-launcher(-local).jar
2440 from getdownLauncher
2441 if (file(getdownLauncher).getName() != getdown_launcher) {
2442 rename(file(getdownLauncher).getName(), getdown_launcher)
2444 into getdownAppBaseDir
2447 // files going into the getdown website dir: ./install dir and files
2448 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2451 from getdownLauncher
2452 from "${getdownAppDir}/${getdown_build_properties}"
2453 if (file(getdownLauncher).getName() != getdown_launcher) {
2454 rename(file(getdownLauncher).getName(), getdown_launcher)
2456 into getdownInstallDir
2459 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2461 from getdownInstallDir
2462 into getdownFilesInstallDir
2466 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2470 from getdownLauncher
2471 from "${getdownAppBaseDir}/${getdown_build_properties}"
2472 from "${getdownAppBaseDir}/${channel_props}"
2473 if (file(getdownLauncher).getName() != getdown_launcher) {
2474 rename(file(getdownLauncher).getName(), getdown_launcher)
2476 into getdownFilesDir
2479 // and ./resource (not all downloaded by getdown)
2481 from getdownResourceDir
2482 into "${getdownFilesDir}/${getdown_resource_dir}"
2487 inputs.dir("${jalviewDir}/${package_dir}")
2489 outputs.dir(getdownAppBaseDir)
2490 outputs.dir(getdownFilesDir)
2494 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2495 task getdownDigestDir(type: JavaExec) {
2497 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2499 def digestDirPropertyName = "DIGESTDIR"
2501 classpath = files(getdownLauncher)
2502 def digestDir = findProperty(digestDirPropertyName)
2503 if (digestDir == null) {
2504 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2508 main = "com.threerings.getdown.tools.Digester"
2512 task getdownDigest(type: JavaExec) {
2513 group = "distribution"
2514 description = "Digest the getdown website folder"
2516 dependsOn getdownWebsiteBuild
2519 classpath = files(getdownLauncher)
2521 main = "com.threerings.getdown.tools.Digester"
2522 args getdownAppBaseDir
2523 inputs.dir(getdownAppBaseDir)
2524 outputs.file("${getdownAppBaseDir}/digest2.txt")
2529 group = "distribution"
2530 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2531 dependsOn getdownDigest
2533 if (reportRsyncCommand) {
2534 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2535 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2536 println "LIKELY RSYNC COMMAND:"
2537 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2538 if (RUNRSYNC == "true") {
2540 commandLine "mkdir", "-p", toDir
2543 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2550 task getdownWebsite {
2551 group = "distribution"
2552 description = "A task to create the whole getdown channel website dir including digest file"
2554 dependsOn getdownWebsiteBuild
2555 dependsOn getdownDigest
2558 task getdownArchiveBuild() {
2559 group = "distribution"
2560 description = "Put files in the archive dir to go on the website"
2562 dependsOn getdownWebsiteBuild
2564 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2565 def vDir = "${getdownArchiveDir}/${v}"
2566 getdownFullArchiveDir = "${vDir}/getdown"
2567 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2569 def vAltDir = "alt_${v}"
2570 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2573 // cleanup old "old" dir
2574 delete getdownArchiveDir
2576 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2577 getdownArchiveTxt.getParentFile().mkdirs()
2578 def getdownArchiveTextLines = []
2579 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2583 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2584 into "${getdownFullArchiveDir}/${vAltDir}"
2587 getdownTextLines.each { line ->
2588 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2589 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2590 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2591 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2592 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2593 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2594 // remove the existing resource = resource/ or bin/ lines
2595 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2596 getdownArchiveTextLines += line
2600 // the resource dir -- add these files as resource lines in getdown.txt
2602 from "${archiveImagesDir}"
2603 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2605 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2609 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2611 def vLaunchJvl = file(getdownVersionLaunchJvl)
2612 vLaunchJvl.getParentFile().mkdirs()
2613 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2614 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2615 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2616 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2617 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2618 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2620 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2622 from getdownLauncher
2623 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2624 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2625 from "${getdownAppBaseDir}/${channel_props}"
2626 if (file(getdownLauncher).getName() != getdown_launcher) {
2627 rename(file(getdownLauncher).getName(), getdown_launcher)
2629 into getdownFullArchiveDir
2635 task getdownArchiveDigest(type: JavaExec) {
2636 group = "distribution"
2637 description = "Digest the getdown archive folder"
2639 dependsOn getdownArchiveBuild
2642 classpath = files(getdownLauncher)
2643 args getdownFullArchiveDir
2645 main = "com.threerings.getdown.tools.Digester"
2646 inputs.dir(getdownFullArchiveDir)
2647 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2650 task getdownArchive() {
2651 group = "distribution"
2652 description = "Build the website archive dir with getdown digest"
2654 dependsOn getdownArchiveBuild
2655 dependsOn getdownArchiveDigest
2658 tasks.withType(JavaCompile) {
2659 options.encoding = 'UTF-8'
2665 delete getdownAppBaseDir
2666 delete getdownFilesDir
2667 delete getdownArchiveDir
2673 if (file(install4jHomeDir).exists()) {
2675 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2676 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2677 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2678 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2680 installDir(file(install4jHomeDir))
2682 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2686 task copyInstall4jTemplate {
2687 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2688 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2689 inputs.file(install4jTemplateFile)
2690 inputs.file(install4jFileAssociationsFile)
2691 inputs.property("CHANNEL", { CHANNEL })
2692 outputs.file(install4jConfFile)
2695 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2697 // turn off code signing if no OSX_KEYPASS
2698 if (OSX_KEYPASS == "") {
2699 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2700 codeSigning.'@macEnabled' = "false"
2702 install4jConfigXml.'**'.windows.each { windows ->
2703 windows.'@runPostProcessor' = "false"
2707 // disable install screen for OSX dmg (for 2.11.2.0)
2708 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2709 macosArchive.attributes().remove('executeSetupApp')
2710 macosArchive.attributes().remove('setupAppId')
2713 // turn off checksum creation for LOCAL channel
2714 def e = install4jConfigXml.application[0]
2715 e.'@createChecksums' = string(install4jCheckSums)
2717 // put file association actions where placeholder action is
2718 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2719 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2720 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2721 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2722 def parent = a.parent()
2724 fileAssociationActions.each { faa ->
2727 // don't need to continue in .any loop once replacements have been made
2732 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2733 // NB we're deleting the /other/ one!
2734 // Also remove the examples subdir from non-release versions
2735 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2736 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2737 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2738 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2740 // remove the examples subdir from Full File Set
2741 def files = install4jConfigXml.files[0]
2742 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2743 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2744 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2745 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2746 dirEntry.parent().remove(dirEntry)
2748 install4jConfigXml.'**'.action.any { a ->
2749 if (a.'@customizedId' == customizedIdToDelete) {
2750 def parent = a.parent()
2756 // write install4j file
2757 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2764 delete install4jConfFile
2768 task cleanInstallersDataFiles {
2769 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2770 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2771 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2773 delete installersOutputTxt
2774 delete installersSha256
2775 delete hugoDataJsonFile
2779 task install4jDMGBackgroundImageCopy {
2780 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2781 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2784 from(install4jDMGBackgroundImageDir) {
2785 include(install4jDMGBackgroundImageFile)
2787 into install4jDMGBackgroundImageBuildDir
2792 task install4jDMGBackgroundImageProcess {
2793 dependsOn install4jDMGBackgroundImageCopy
2796 if (backgroundImageText) {
2797 if (convertBinary == null) {
2798 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2800 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2801 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2803 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2805 executable convertBinary
2808 '-font', install4j_background_image_text_font,
2809 '-fill', install4j_background_image_text_colour,
2810 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2811 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2812 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2821 task install4jDMGBackgroundImage {
2822 dependsOn install4jDMGBackgroundImageProcess
2825 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2826 group = "distribution"
2827 description = "Create the install4j installers"
2829 dependsOn copyInstall4jTemplate
2830 dependsOn cleanInstallersDataFiles
2831 dependsOn install4jDMGBackgroundImage
2833 projectFile = install4jConfFile
2835 // create an md5 for the input files to use as version for install4j conf file
2836 def digest = MessageDigest.getInstance("MD5")
2838 (file("${install4jDir}/${install4j_template}").text +
2839 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2840 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2841 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2842 if (filesMd5.length() >= 8) {
2843 filesMd5 = filesMd5.substring(0,8)
2845 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2848 'JALVIEW_NAME': jalview_name,
2849 'JALVIEW_APPLICATION_NAME': applicationName,
2850 'JALVIEW_DIR': "../..",
2851 'OSX_KEYSTORE': OSX_KEYSTORE,
2852 'OSX_APPLEID': OSX_APPLEID,
2853 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2854 'JSIGN_SH': JSIGN_SH,
2855 'JRE_DIR': getdown_app_dir_java,
2856 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2857 'JALVIEW_VERSION': JALVIEW_VERSION,
2858 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2859 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2860 'JAVA_VERSION': JAVA_VERSION,
2861 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2862 'VERSION': JALVIEW_VERSION,
2863 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2864 'BUNDLE_ID': install4jBundleId,
2865 'INTERNAL_ID': install4jInternalId,
2866 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2867 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2868 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2869 'WRAPPER_LINK': getdownWrapperLink,
2870 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2871 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2872 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2873 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2874 'INSTALLER_NAME': install4jInstallerName,
2875 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2876 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2877 'GETDOWN_FILES_DIR': getdown_files_dir,
2878 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2879 'GETDOWN_DIST_DIR': getdownAppDistDir,
2880 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2881 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2882 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2883 'BUILD_DIR': install4jBuildDir,
2884 'APPLICATION_CATEGORIES': install4j_application_categories,
2885 'APPLICATION_FOLDER': install4jApplicationFolder,
2886 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2887 'EXECUTABLE_NAME': install4jExecutableName,
2888 'EXTRA_SCHEME': install4jExtraScheme,
2889 'MAC_ICONS_FILE': install4jMacIconsFile,
2890 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2891 'PNG_ICON_FILE': install4jPngIconFile,
2892 'BACKGROUND': install4jBackground,
2897 'windows': 'WINDOWS',
2901 // these are the bundled OS/architecture VMs needed by install4j
2904 [ "mac", "aarch64" ],
2905 [ "windows", "x64" ],
2907 [ "linux", "aarch64" ]
2909 osArch.forEach { os, arch ->
2910 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)
2911 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2912 // otherwise running `gradle installers` generates a non-useful error:
2913 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2914 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)
2917 //println("INSTALL4J VARIABLES:")
2918 //variables.each{k,v->println("${k}=${v}")}
2920 destination = "${jalviewDir}/${install4jBuildDir}"
2921 buildSelected = true
2923 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2925 disableSigning = true
2926 disableNotarization = true
2930 macKeystorePassword = OSX_KEYPASS
2933 if (OSX_ALTOOLPASS) {
2934 appleIdPassword = OSX_ALTOOLPASS
2935 disableNotarization = false
2937 disableNotarization = true
2941 println("Using projectFile "+projectFile)
2942 if (!disableNotarization) { println("Will notarize OSX App DMG") }
2946 inputs.dir(getdownAppBaseDir)
2947 inputs.file(install4jConfFile)
2948 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2949 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2952 def getDataHash(File myFile) {
2953 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2954 return myFile.exists()
2956 "file" : myFile.getName(),
2957 "filesize" : myFile.length(),
2958 "sha256" : hash.toString()
2963 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2965 "channel" : getdownChannelName,
2966 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2967 "git-commit" : "${gitHash} [${gitBranch}]",
2968 "version" : JALVIEW_VERSION
2970 // install4j installer files
2971 if (installersOutputTxt.exists()) {
2973 installersOutputTxt.readLines().each { def line ->
2974 if (line.startsWith("#")) {
2977 line.replaceAll("\n","")
2978 def vals = line.split("\t")
2979 def filename = vals[3]
2980 def filesize = file(filename).length()
2981 filename = filename.replaceAll(/^.*\//, "")
2982 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2983 idHash."${filename}" = vals[0]
2985 if (install4jCheckSums && installersSha256.exists()) {
2986 installersSha256.readLines().each { def line ->
2987 if (line.startsWith("#")) {
2990 line.replaceAll("\n","")
2991 def vals = line.split(/\s+\*?/)
2992 def filename = vals[1]
2993 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
2999 "JAR": shadowJar.archiveFile, // executable JAR
3000 "JVL": getdownVersionLaunchJvl, // version JVL
3001 "SOURCE": sourceDist.archiveFile // source TGZ
3002 ].each { key, value ->
3003 def file = file(value)
3004 if (file.exists()) {
3005 def fileHash = getDataHash(file)
3006 if (fileHash != null) {
3007 hash."${key}" = fileHash;
3011 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
3014 task staticMakeInstallersJsonFile {
3016 def output = findProperty("i4j_output")
3017 def sha256 = findProperty("i4j_sha256")
3018 def json = findProperty("i4j_json")
3019 if (output == null || sha256 == null || json == null) {
3020 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3022 writeDataJsonFile(file(output), file(sha256), file(json))
3027 dependsOn installerFiles
3033 eclipse().configFile(eclipse_codestyle_file)
3037 task createSourceReleaseProperties(type: WriteProperties) {
3038 group = "distribution"
3039 description = "Create the source RELEASE properties file"
3041 def sourceTarBuildDir = "${buildDir}/sourceTar"
3042 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3043 outputFile (sourceReleasePropertiesFile)
3046 releaseProps.each{ key, val -> property key, val }
3047 property "git.branch", gitBranch
3048 property "git.hash", gitHash
3051 outputs.file(outputFile)
3054 task sourceDist(type: Tar) {
3055 group "distribution"
3056 description "Create a source .tar.gz file for distribution"
3058 dependsOn createBuildProperties
3059 dependsOn convertMdFiles
3060 dependsOn eclipseAllPreferences
3061 dependsOn createSourceReleaseProperties
3064 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3065 archiveFileName = outputFileName
3067 compression Compression.GZIP
3083 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3085 "utils/InstallAnywhere",
3100 "gradle.properties",
3112 ".settings/org.eclipse.buildship.core.prefs",
3113 ".settings/org.eclipse.jdt.core.prefs"
3117 exclude (EXCLUDE_FILES)
3118 include (PROCESS_FILES)
3119 filter(ReplaceTokens,
3123 'Version-Rel': JALVIEW_VERSION,
3124 'Year-Rel': getDate("yyyy")
3129 exclude (EXCLUDE_FILES)
3130 exclude (PROCESS_FILES)
3131 exclude ("appletlib")
3132 exclude ("**/*locales")
3133 exclude ("*locales/**")
3134 exclude ("utils/InstallAnywhere")
3136 exclude (getdown_files_dir)
3137 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3138 //exclude (getdown_website_dir)
3139 //exclude (getdown_archive_dir)
3141 // exluding these as not using jars as modules yet
3142 exclude ("${j11modDir}/**/*.jar")
3145 include(INCLUDE_FILES)
3147 // from (jalviewDir) {
3148 // // explicit includes for stuff that seemed to not get included
3149 // include(fileTree("test/**/*."))
3150 // exclude(EXCLUDE_FILES)
3151 // exclude(PROCESS_FILES)
3154 from(file(buildProperties).getParent()) {
3155 include(file(buildProperties).getName())
3156 rename(file(buildProperties).getName(), "build_properties")
3158 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3162 def sourceTarBuildDir = "${buildDir}/sourceTar"
3163 from(sourceTarBuildDir) {
3164 // this includes the appended RELEASE properties file
3168 task dataInstallersJson {
3170 description "Create the installers-VERSION.json data file for installer files created"
3172 mustRunAfter installers
3173 mustRunAfter shadowJar
3174 mustRunAfter sourceDist
3175 mustRunAfter getdownArchive
3177 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3178 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3180 if (installersOutputTxt.exists()) {
3181 inputs.file(installersOutputTxt)
3183 if (install4jCheckSums && installersSha256.exists()) {
3184 inputs.file(installersSha256)
3187 shadowJar.archiveFile, // executable JAR
3188 getdownVersionLaunchJvl, // version JVL
3189 sourceDist.archiveFile // source TGZ
3190 ].each { fileName ->
3191 if (file(fileName).exists()) {
3192 inputs.file(fileName)
3196 outputs.file(hugoDataJsonFile)
3199 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3205 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3208 dependsOn pubhtmlhelp
3210 inputs.dir("${helpBuildDir}/${help_dir}")
3211 outputs.dir("${buildDir}/distributions/${help_dir}")
3215 task j2sSetHeadlessBuild {
3222 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3224 description "Enable the alternative J2S Config file for headless build"
3226 outputFile = jalviewjsJ2sSettingsFileName
3227 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3228 def j2sProps = new Properties()
3229 if (j2sPropsFile.exists()) {
3231 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3232 j2sProps.load(j2sPropsFileFIS)
3233 j2sPropsFileFIS.close()
3235 j2sProps.each { prop, val ->
3238 } catch (Exception e) {
3239 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3243 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3244 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3249 task jalviewjsSetEclipseWorkspace {
3250 def propKey = "jalviewjs_eclipse_workspace"
3252 // see if jalviewjs_eclipse_workspace is set by a property
3253 if (project.hasProperty(propKey)) {
3254 propVal = project.getProperty(propKey)
3255 if (propVal.startsWith("~/")) {
3256 propVal = System.getProperty("user.home") + propVal.substring(1)
3259 // else look for an existing build/jalviewjs/eclipse_workspace_location file
3260 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3261 def propsFile = file(propsFileName)
3262 def eclipseWsDir = propVal
3263 def props = new Properties()
3265 def writeProps = true
3266 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3267 def ins = new FileInputStream(propsFileName)
3270 if (props.getProperty(propKey, null) != null) {
3271 eclipseWsDir = props.getProperty(propKey)
3276 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3277 def tempDir = File.createTempDir()
3278 eclipseWsDir = tempDir.getAbsolutePath()
3281 eclipseWorkspace = file(eclipseWsDir)
3284 // do not run a headless transpile when we claim to be in Eclipse
3286 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3287 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3289 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3293 props.setProperty(propKey, eclipseWsDir)
3294 propsFile.parentFile.mkdirs()
3295 def bytes = new ByteArrayOutputStream()
3296 props.store(bytes, null)
3297 def propertiesString = bytes.toString()
3298 propsFile.text = propertiesString
3304 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3307 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3308 //outputs.file(propsFileName) // don't want this to be deleted because of falsely "stale" task
3309 outputs.upToDateWhen { eclipseWorkspace.exists() && (propsFile.exists() || !writeProps) }
3313 task jalviewjsEclipsePaths {
3314 def eclipseProductFile
3317 def eclipseRoot = jalviewjs_eclipse_root
3318 if (eclipseRoot.startsWith("~/")) {
3319 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3321 if (OperatingSystem.current().isMacOsX()) {
3322 eclipseRoot += "/Eclipse.app"
3323 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3324 eclipseProductFile = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3325 eclipseSetupLog = "${eclipseRoot}/Contents/Eclipse/configuration/org.eclipse.oomph.setup/setup.log"
3326 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3327 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3328 eclipseRoot += "/eclipse"
3330 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3331 eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
3332 eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
3333 } else { // linux or unix
3334 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3335 eclipseRoot += "/eclipse"
3337 eclipseBinary = "${eclipseRoot}/eclipse"
3338 eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
3339 eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
3342 eclipseVersion = "unknown" // default
3343 def assumedVersion = true
3344 if (file(eclipseProductFile).exists()) {
3345 def fis = new FileInputStream(eclipseProductFile)
3346 def props = new Properties()
3348 eclipseVersion = props.getProperty("version")
3350 assumedVersion = false
3352 if (file(eclipseSetupLog).exists()) {
3353 def productRegex = /(?m)^\[[^\]]+\]\s+Product\s+(org\.eclipse.\S*)/
3355 file(eclipseSetupLog).eachLine { String line ->
3356 def matcher = line =~ productRegex
3357 if (matcher.size() > 0) {
3358 eclipseProductVersion = matcher[0][1]
3361 if (lineCount >= 100) {
3368 def propKey = "eclipse_debug"
3369 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3372 // do not run a headless transpile when we claim to be in Eclipse
3374 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3375 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3377 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3380 if (!assumedVersion) {
3381 println("ECLIPSE VERSION=${eclipseVersion}")
3382 if (eclipseProductVersion.length() != 0) {
3383 println("ECLIPSE PRODUCT=${eclipseProductVersion}")
3390 task printProperties {
3392 description "Output to console all System.properties"
3394 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3400 dependsOn eclipseProject
3401 dependsOn eclipseClasspath
3402 dependsOn eclipseJdt
3406 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3407 task jalviewjsEclipseCopyDropins(type: Copy) {
3408 dependsOn jalviewjsEclipsePaths
3410 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3411 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3412 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3419 // this eclipse -clean doesn't actually work
3420 task jalviewjsCleanEclipse(type: Exec) {
3421 dependsOn eclipseSetup
3422 dependsOn jalviewjsEclipsePaths
3423 dependsOn jalviewjsEclipseCopyDropins
3425 executable(eclipseBinary)
3426 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3432 def inputString = """exit
3435 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3436 standardInput = inputByteStream
3439 /* not really working yet
3440 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3444 task jalviewjsTransferUnzipSwingJs(type: Copy) {
3445 def swingJsZipFile = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3446 from zipTree( "${jalviewDir}/${jalviewjs_swingjs_zip}" )
3447 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3449 inputs.file swingJsZipFile
3453 task jalviewjsTransferUnzipLib(type: Copy) {
3454 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip").sort()
3456 zipFiles.each { file_zip ->
3457 from zipTree(file_zip)
3459 // The following replace() is needed due to a mismatch in Jmol calls to
3460 // colorPtToFFRGB$javajs_util_T3d when only colorPtToFFRGB$javajs_util_T3 is defined
3461 // in the SwingJS.zip (github or the one distributed with JSmol)
3462 if (file_zip.getName().startsWith("Jmol-SwingJS")) {
3465 while(!line.equals(l)) {
3466 line = line.replace('colorPtToFFRGB$javajs_util_T3d', 'colorPtToFFRGB$javajs_util_T3')
3474 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3478 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3480 description "Create the alternative j2s file from the j2s.* properties"
3482 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3483 def siteDirProperty = "j2s.site.directory"
3484 def setSiteDir = false
3485 jalviewjsJ2sProps.each { prop, val ->
3487 if (prop == siteDirProperty) {
3488 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3489 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3495 if (!setSiteDir) { // default site location, don't override specifically set property
3496 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3499 outputFile = jalviewjsJ2sAltSettingsFileName
3502 inputs.properties(jalviewjsJ2sProps)
3507 task jalviewjsEclipseSetup {
3508 dependsOn jalviewjsEclipseCopyDropins
3509 dependsOn jalviewjsSetEclipseWorkspace
3510 dependsOn jalviewjsCreateJ2sSettings
3514 task jalviewjsSyncLibs (type: Sync) {
3515 dependsOn jalviewjsTransferUnzipLib
3516 dependsOn jalviewjsTransferUnzipSwingJs
3518 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3519 def inputFiles = fileTree(dir: inputDir)
3520 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3524 def outputFiles = []
3525 inputFiles.each{ file ->
3526 def rfile = inputDir.toPath().relativize(file.toPath())
3527 def ofile = new File("${outputDir}/${rfile}")
3528 outputFiles += "${ofile}"
3534 duplicatesStrategy "EXCLUDE"
3536 outputs.files outputFiles
3537 inputs.files inputFiles
3540 task jalviewjsSyncSwingJS (type: Sync) {
3541 dependsOn jalviewjsTransferUnzipSwingJs
3542 mustRunAfter jalviewjsSyncLibs
3544 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3545 def inputFiles = fileTree(dir: inputDir)
3546 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3550 def outputFiles = []
3551 inputFiles.each{ file ->
3552 def rfile = inputDir.toPath().relativize(file.toPath())
3553 def ofile = new File("${outputDir}/${rfile}")
3554 outputFiles += "${ofile}"
3560 duplicatesStrategy "INCLUDE"
3562 outputs.files outputFiles
3563 inputs.files inputFiles
3566 task jalviewjsSyncAllLibs {
3567 dependsOn jalviewjsSyncLibs
3568 dependsOn jalviewjsSyncSwingJS
3572 task jalviewjsSyncResources (type: Sync) {
3573 dependsOn buildResources
3575 def inputDir = file(resourcesBuildDir)
3576 def inputFiles = fileTree(dir: inputDir)
3577 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3581 def outputFiles = []
3582 inputFiles.each{ file ->
3583 def rfile = inputDir.toPath().relativize(file.toPath())
3584 def ofile = new File("${outputDir}/${rfile}")
3585 outputFiles += "${ofile}"
3590 outputs.files outputFiles
3591 inputs.files inputFiles
3595 task jalviewjsSyncSiteResources (type: Sync) {
3596 def inputDir = file("${jalviewDir}/${jalviewjs_site_resource_dir}")
3597 def inputFiles = fileTree(dir: inputDir)
3598 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3602 def outputFiles = []
3603 inputFiles.each{ file ->
3604 def rfile = inputDir.toPath().relativize(file.toPath())
3605 def ofile = new File("${outputDir}/${rfile}")
3606 outputFiles += "${ofile}"
3612 outputs.files outputFiles
3613 inputs.files inputFiles
3617 task jalviewjsSyncBuildProperties (type: Sync) {
3618 dependsOn createBuildProperties
3620 def f = file(buildProperties)
3621 def inputDir = f.getParentFile()
3622 def inputFiles = [f]
3623 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3627 def outputFiles = []
3628 inputFiles.each{ file ->
3629 def rfile = inputDir.toPath().relativize(file.toPath())
3630 def ofile = new File("${outputDir}/${rfile}")
3631 outputFiles += "${ofile}"
3637 outputs.files outputFiles
3638 inputs.files inputFiles
3642 task jalviewjsProjectImport(type: Exec) {
3643 dependsOn eclipseSetup
3644 dependsOn jalviewjsEclipsePaths
3645 dependsOn jalviewjsEclipseSetup
3648 // do not run a headless import when we claim to be in Eclipse
3650 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3651 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3653 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3657 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3658 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/${eclipse_project_name}"
3660 executable(eclipseBinary)
3661 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3665 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3667 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3668 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3669 inputs.file("${jalviewjsJ2sAltSettingsFileName}")
3672 outputs.upToDateWhen( {
3676 def projDirExists = file(projdir).exists()
3677 return projDirExists
3681 // jalviewjs_eclipse_workspace_location_file
3682 task jalviewjsTranspile(type: Exec) {
3683 dependsOn jalviewjsProjectImport
3686 dependsOn jalviewjsEnableAltFileProperty
3690 // do not run a headless transpile when we claim to be in Eclipse
3692 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3693 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3695 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3699 executable(eclipseBinary)
3700 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3704 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3706 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3707 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3713 stdout = new ByteArrayOutputStream()
3714 stderr = new ByteArrayOutputStream()
3716 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3717 def logOutFile = file(logOutFileName)
3718 logOutFile.createNewFile()
3719 def info = """ROOT: ${jalviewjs_eclipse_root}
3720 ECLIPSE BINARY: ${eclipseBinary}
3721 ECLIPSE VERSION: ${eclipseVersion}
3722 ECLIPSE PRODUCT: ${eclipseProductVersion}
3723 ECLIPSE WORKSPACE: ${eclipseWorkspace}
3724 ECLIPSE DEBUG: ${eclipseDebug}
3727 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3728 // combine stdout and stderr
3729 def logErrFOS = logOutFOS
3731 if (jalviewjs_j2s_to_console.equals("true")) {
3732 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3733 new org.apache.tools.ant.util.TeeOutputStream(
3737 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3738 new org.apache.tools.ant.util.TeeOutputStream(
3743 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3746 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3750 standardOutput.write(string(info).getBytes("UTF-8"))
3754 def transpileError = false
3755 def j2sIsActive = false
3756 def j2sBuildStarting = false
3757 def compilingLines = 0
3758 def j2sBuildingJavascript = false
3759 def j2sBuildingJavascriptRegex = /(?m)^J2S building JavaScript for (\d+) files/
3761 def transpilingLines = 0
3762 stdout.toString().eachLine { String line ->
3763 if (line.startsWith("J2S isActive true")) {
3766 if (line.startsWith("J2S buildStarting")) {
3767 j2sBuildStarting = true
3769 if (line =~ / Compiling /) {
3772 if (!j2sBuildingJavascript) {
3773 def matcher = line =~ j2sBuildingJavascriptRegex
3774 if (matcher.size() > 0) {
3775 numFiles = Integer.valueOf(matcher[0][1])
3776 j2sBuildingJavascript = true
3779 if (line.startsWith("J2S transpiling ")) {
3782 if (line.contains("Error processing ")) {
3783 transpileError = true
3787 println("J2S IS ACTIVE=${j2sIsActive}")
3788 println("J2S BUILD STARTING=${j2sBuildStarting}")
3789 println("J2S BUILDING JAVASCRIPT=${j2sBuildingJavascript}")
3790 println("NUM FILES=${numFiles}")
3791 println("COMPILING LINES=${compilingLines}")
3792 println("TRANSPILING LINES=${transpilingLines}")
3793 println("TRANSPILE ERROR=${transpileError}")
3797 || (j2sBuildStarting && transpilingLines == 0)
3798 || (transpilingLines < compilingLines)
3799 || (transpilingLines != numFiles)
3801 // j2s did not complete transpile
3802 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3803 println("IGNORING TRANSPILE ERRORS")
3804 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3806 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3812 inputs.file(jalviewjsJ2sSettingsFileName)
3814 inputs.file(jalviewjsJ2sAltSettingsFileName)
3816 inputs.dir("${jalviewDir}/${sourceDir}")
3818 def inputJavaDir = file("${jalviewDir}/${sourceDir}")
3819 def inputJavaFiles = fileTree(dir: inputJavaDir)
3820 def outputJsDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3821 def outputJsFiles = []
3822 inputJavaFiles.each{ file ->
3823 def rfile = inputJavaDir.toPath().relativize(file.toPath())
3824 def ofile = new File("${outputJsDir}/${rfile}")
3825 def ofilenamejs = ofile.getPath()
3826 if (ofilenamejs.endsWith(".java")) {
3827 ofilenamejs = ofilenamejs.substring(0,ofilenamejs.length()-4)+"js"
3829 outputJsFiles += "${ofilenamejs}"
3832 outputs.files outputJsFiles
3833 outputs.file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}")
3837 task jalviewjsTransferSiteMergeSiteJsDir (type: Copy) {
3838 dependsOn jalviewjsTranspile
3840 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3841 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3844 def inputFiles = fileTree(dir: inputDir)
3848 includeEmptyDirs = false
3852 // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
3853 duplicatesStrategy "INCLUDE"
3855 // SiteJs files should take priority and write over existing files if different
3856 // so we define the output files
3857 outputs.upToDateWhen(
3859 def transpiledFiles = jalviewjsTransferSiteMergeSiteJsDir.getOutputs().getFiles()
3860 def inputFiles = fileTree(dir: inputDir)
3861 if (inputFiles.size() < transpiledFiles.size()) {
3864 def retVal = ! inputFiles.any { file ->
3865 def rfile = inputDir.toPath().relativize(file.toPath())
3866 def ofile = new File("${outputDir}/${rfile}")
3867 if (!ofile.exists() || ofile.lastModified() < file.lastModified()) {
3868 return true // this is NOTted to false
3876 inputs.files jalviewjsTranspile
3879 task jalviewjsTransferSiteMergeLibDir (type: Copy) {
3880 dependsOn jalviewjsTransferUnzipLib
3882 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3884 // This takes the outputs of jalviewjsTransferUnzipLib
3885 from jalviewjsTransferUnzipLib
3888 includeEmptyDirs = false
3892 // don't overwrite files in the destination
3893 // Note, this closure gets run at run stage not config stage
3895 if (it.getRelativePath().getFile(file(outputDir)).exists()) {
3900 duplicatesStrategy "INCLUDE"
3903 task jalviewjsTransferSiteMergeSwingJsDir (type: Copy) {
3904 dependsOn jalviewjsTransferUnzipSwingJs
3906 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3908 // This takes the outputs of jalviewjsTransferUnzipSwingJs
3909 from jalviewjsTransferUnzipSwingJs
3912 includeEmptyDirs = false
3916 // DO overwrite files in the destination
3918 // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
3919 duplicatesStrategy "INCLUDE"
3922 // we run after SiteJs and exclude overwriting files
3923 jalviewjsTransferSiteMergeLibDir.mustRunAfter jalviewjsTransferSiteMergeSiteJsDir
3924 jalviewjsTransferSiteMergeLibDir.mustRunAfter jalviewjsTransferSiteMergeSwingJsDir
3925 // we run this last, overwriting files from sitejs and lib, to ensure a consistent SwingJS
3926 jalviewjsTransferSiteMergeSwingJsDir.mustRunAfter jalviewjsTransferSiteMergeSiteJsDir
3928 task jalviewjsTransferSiteMergeDirs {
3929 dependsOn jalviewjsTransferSiteMergeLibDir
3930 dependsOn jalviewjsTransferSiteMergeSwingJsDir
3931 dependsOn jalviewjsTransferSiteMergeSiteJsDir
3935 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3937 def stdout = new ByteArrayOutputStream()
3938 def stderr = new ByteArrayOutputStream()
3940 def coreFile = file(jsfile)
3942 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3944 logOutFile.createNewFile()
3945 logOutFile.append(msg+"\n")
3947 def coreTop = file(prefixFile)
3948 def coreBottom = file(suffixFile)
3949 def missingFiles = []
3950 coreFile.getParentFile().mkdirs()
3951 coreFile.createNewFile()
3952 coreFile.write( coreTop.getText("UTF-8") )
3956 def t = f.getText("UTF-8")
3957 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3958 coreFile.append( t )
3960 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3962 logOutFile.append(msg+"\n")
3966 coreFile.append( coreBottom.getText("UTF-8") )
3968 msg = "Generating ${zjsfile}"
3970 logOutFile.append(msg+"\n")
3971 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3972 def logErrFOS = logOutFOS
3975 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3976 main = "com.google.javascript.jscomp.CommandLineRunner"
3977 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3978 args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3981 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3983 logOutFile.append(msg+"\n")
3985 if (logOutConsole) {
3986 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3987 new org.apache.tools.ant.util.TeeOutputStream(
3991 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3992 new org.apache.tools.ant.util.TeeOutputStream(
3997 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
4000 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
4006 if (missingFiles.size() > 0) {
4007 msg += "\n!!! These files were listed but missing:\n"
4008 missingFiles.each { file -> msg += "!!! " + file.getPath() + "\n" }
4012 logOutFile.append(msg+"\n")
4016 task jalviewjsBuildCore {
4018 description "Build the core js lib closures listed in the classlists dir"
4019 dependsOn jalviewjsTransferSiteMergeDirs
4021 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
4022 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
4023 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
4024 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_js_subdir}"
4025 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
4026 def prefixFile = "${jsDir}/core/coretop2.js"
4027 def suffixFile = "${jsDir}/core/corebottom2.js"
4029 inputs.file prefixFile
4030 inputs.file suffixFile
4032 def classlistFiles = []
4033 // add the classlists found int the jalviewjs_classlists_dir
4034 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
4036 def name = file.getName() - ".txt"
4044 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
4046 jalviewjsCoreClasslists = []
4048 classlistFiles.each {
4051 def file = hash['file']
4052 if (! file.exists()) {
4053 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
4054 return false // this is a "continue" in groovy .each closure
4056 def name = hash['name']
4058 name = file.getName() - ".txt"
4066 def list = fileTree(dir: j2sDir, includes: filelist)
4068 def jsfile = "${outputDir}/core${name}.js"
4069 def zjsfile = "${outputDir}/core${name}.z.js"
4071 jalviewjsCoreClasslists += [
4080 outputs.file(jsfile)
4081 outputs.file(zjsfile)
4085 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
4086 logOutFile.getParentFile().mkdirs()
4087 logOutFile.createNewFile()
4088 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildCore\n----\n")
4090 jalviewjsCoreClasslists.each {
4091 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
4098 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
4101 into file(outputFile).getParentFile()
4102 rename { filename ->
4103 if (filename.equals(inputFile.getName())) {
4104 return file(outputFile).getName()
4108 filter(ReplaceTokens,
4112 'MAIN': '"'+main_class+'"',
4114 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
4115 'COREKEY': jalviewjs_core_key,
4116 'CORENAME': coreName
4123 task jalviewjsPublishCoreTemplates {
4124 dependsOn jalviewjsBuildCore
4126 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
4127 def inputFile = file(inputFileName)
4128 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4130 def outputFiles = []
4131 jalviewjsCoreClasslists.each { cl ->
4132 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
4133 cl['outputfile'] = outputFile
4134 outputFiles += outputFile
4138 jalviewjsCoreClasslists.each { cl ->
4139 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
4142 inputs.file(inputFile)
4143 outputs.files(outputFiles)
4147 task jalviewjsSyncCore (type: Sync) {
4148 dependsOn jalviewjsBuildCore
4149 dependsOn jalviewjsPublishCoreTemplates
4151 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
4152 def inputFiles = fileTree(dir: inputDir)
4153 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
4157 def outputFiles = []
4158 inputFiles.each{ file ->
4159 def rfile = inputDir.toPath().relativize(file.toPath())
4160 def ofile = new File("${outputDir}/${rfile}")
4161 outputFiles += "${ofile}"
4167 outputs.files outputFiles
4168 inputs.files inputFiles
4172 // this Copy version of TransferSiteJs will delete anything else in the target dir
4173 task jalviewjsCopyTransferSiteMergeDir(type: Copy) {
4174 dependsOn jalviewjsTransferSiteMergeDirs
4176 from "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
4177 into "${jalviewDir}/${jalviewjsSiteDir}"
4181 // this Copy version of TransferSiteJs will delete anything else in the target dir
4182 task jalviewjsCopyTransferSiteJs(type: Copy) {
4183 dependsOn jalviewjsTranspile
4185 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4186 into "${jalviewDir}/${jalviewjsSiteDir}"
4190 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
4191 task jalviewjsSyncTransferSiteJs(type: Sync) {
4192 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4194 into "${jalviewDir}/${jalviewjsSiteDir}"
4201 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
4202 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
4203 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
4204 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
4206 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
4207 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
4208 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
4209 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
4212 task jalviewjsPrepareSite {
4214 description "Prepares the website folder including unzipping files and copying resources"
4215 //dependsOn jalviewjsSyncAllLibs // now using jalviewjsCopyTransferSiteMergeDir
4216 dependsOn jalviewjsSyncResources
4217 dependsOn jalviewjsSyncSiteResources
4218 dependsOn jalviewjsSyncBuildProperties
4219 dependsOn jalviewjsSyncCore
4223 task jalviewjsBuildSite {
4225 description "Builds the whole website including transpiled code"
4226 dependsOn jalviewjsCopyTransferSiteMergeDir
4227 dependsOn jalviewjsPrepareSite
4231 task cleanJalviewjsTransferSite {
4233 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4234 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4235 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4236 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4241 task cleanJalviewjsSite {
4242 dependsOn cleanJalviewjsTransferSite
4244 delete "${jalviewDir}/${jalviewjsSiteDir}"
4249 task jalviewjsSiteTar(type: Tar) {
4251 description "Creates a tar.gz file for the website"
4252 dependsOn jalviewjsBuildSite
4253 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4254 archiveFileName = outputFilename
4256 compression Compression.GZIP
4258 from "${jalviewDir}/${jalviewjsSiteDir}"
4259 into jalviewjs_site_dir // this is inside the tar file
4261 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4265 task jalviewjsServer {
4267 def filename = "jalviewjsTest.html"
4268 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4269 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4274 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4275 factory = f.newInstance()
4276 } catch (ClassNotFoundException e) {
4277 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4279 def port = Integer.valueOf(jalviewjs_server_port)
4284 while(port < start+1000 && !running) {
4286 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4287 jalviewjsServer = factory.start(doc_root, port)
4289 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4290 println("SERVER STARTED with document root ${doc_root}.")
4291 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4292 println("For debug: "+url+"?j2sdebug")
4293 println("For verbose: "+url+"?j2sverbose")
4294 } catch (Exception e) {
4299 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4300 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4301 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4303 jalviewjsCoreClasslists.each { cl ->
4304 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4306 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4308 println("For core ${cl.name}: "+urlcore)
4311 file(htmlFile).text = htmlText
4314 outputs.file(htmlFile)
4315 outputs.upToDateWhen({false})
4319 task cleanJalviewjsAll {
4321 description "Delete all configuration and build artifacts to do with JalviewJS build"
4322 dependsOn cleanJalviewjsSite
4323 dependsOn jalviewjsEclipsePaths
4326 delete "${jalviewDir}/${jalviewjsBuildDir}"
4327 delete "${jalviewDir}/${eclipse_bin_dir}"
4328 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4329 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4331 delete jalviewjsJ2sAltSettingsFileName
4334 outputs.upToDateWhen( { false } )
4338 task jalviewjsIDE_checkJ2sPlugin {
4339 group "00 JalviewJS in Eclipse"
4340 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4343 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4344 def j2sPluginFile = file(j2sPlugin)
4345 def eclipseHome = System.properties["eclipse.home.location"]
4346 if (eclipseHome == null || ! IN_ECLIPSE) {
4347 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4349 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4350 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4351 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4352 eclipseJ2sPluginDirs += altPluginsDir
4354 def foundPlugin = false
4355 def j2sPluginFileName = j2sPluginFile.getName()
4356 def eclipseJ2sPlugin
4357 def eclipseJ2sPluginFile
4358 eclipseJ2sPluginDirs.any { dir ->
4359 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4360 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4361 if (eclipseJ2sPluginFile.exists()) {
4367 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4368 System.err.println(msg)
4369 throw new StopExecutionException(msg)
4372 def digest = MessageDigest.getInstance("MD5")
4374 digest.update(j2sPluginFile.text.bytes)
4375 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4377 digest.update(eclipseJ2sPluginFile.text.bytes)
4378 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4380 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4381 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4382 System.err.println(msg)
4383 throw new StopExecutionException(msg)
4385 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4391 task jalviewjsIDE_copyJ2sPlugin {
4392 group "00 JalviewJS in Eclipse"
4393 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4396 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4397 def j2sPluginFile = file(j2sPlugin)
4398 def eclipseHome = System.properties["eclipse.home.location"]
4399 if (eclipseHome == null || ! IN_ECLIPSE) {
4400 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4402 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4403 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4404 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4405 System.err.println(msg)
4408 eclipseJ2sPluginFile.getParentFile().mkdirs()
4409 into eclipseJ2sPluginFile.getParent()
4415 task jalviewjsIDE_j2sFile {
4416 group "00 JalviewJS in Eclipse"
4417 description "Creates the .j2s file"
4418 dependsOn jalviewjsCreateJ2sSettings
4422 task jalviewjsIDE_SyncCore {
4423 group "00 JalviewJS in Eclipse"
4424 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4425 dependsOn jalviewjsSyncCore
4429 task jalviewjsIDE_SyncSiteAll {
4430 dependsOn jalviewjsSyncAllLibs
4431 dependsOn jalviewjsSyncResources
4432 dependsOn jalviewjsSyncSiteResources
4433 dependsOn jalviewjsSyncBuildProperties
4437 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4440 task jalviewjsIDE_PrepareSite {
4441 group "00 JalviewJS in Eclipse"
4442 description "Sync libs and resources to site dir, but not closure cores"
4444 dependsOn jalviewjsIDE_SyncSiteAll
4445 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4449 task jalviewjsIDE_AssembleSite {
4450 group "00 JalviewJS in Eclipse"
4451 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4452 dependsOn jalviewjsPrepareSite
4456 task jalviewjsIDE_SiteClean {
4457 group "00 JalviewJS in Eclipse"
4458 description "Deletes the Eclipse transpiled site"
4459 dependsOn cleanJalviewjsSite
4463 task jalviewjsIDE_Server {
4464 group "00 JalviewJS in Eclipse"
4465 description "Starts a webserver on localhost to test the website"
4466 dependsOn jalviewjsServer
4470 // buildship runs this at import or gradle refresh
4471 task eclipseSynchronizationTask {
4472 //dependsOn eclipseSetup
4473 dependsOn createBuildProperties
4475 dependsOn jalviewjsIDE_j2sFile
4476 dependsOn jalviewjsIDE_checkJ2sPlugin
4477 dependsOn jalviewjsIDE_PrepareSite
4482 // buildship runs this at build time or project refresh
4483 task eclipseAutoBuildTask {
4484 //dependsOn jalviewjsIDE_checkJ2sPlugin
4485 //dependsOn jalviewjsIDE_PrepareSite
4489 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4490 from file(jalviewjs_stderr_launch)
4491 into jalviewjsSiteDir
4493 inputs.file jalviewjs_stderr_launch
4494 outputs.file jalviewjsStderrLaunchFilename
4497 task cleanJalviewjsChromiumUserDir {
4499 delete jalviewjsChromiumUserDir
4501 outputs.dir jalviewjsChromiumUserDir
4502 // always run when depended on
4503 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4506 task jalviewjsChromiumProfile {
4507 dependsOn cleanJalviewjsChromiumUserDir
4508 mustRunAfter cleanJalviewjsChromiumUserDir
4510 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4513 mkdir jalviewjsChromiumProfileDir
4516 outputs.file firstRun
4519 task jalviewjsLaunchTest {
4521 description "Check JalviewJS opens in a browser"
4522 dependsOn jalviewjsBuildSite
4523 dependsOn jalviewjsCopyStderrLaunchFile
4524 dependsOn jalviewjsChromiumProfile
4526 def macOS = OperatingSystem.current().isMacOsX()
4527 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4528 if (chromiumBinary.startsWith("~/")) {
4529 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4535 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4537 def binary = file(chromiumBinary)
4538 if (!binary.exists()) {
4539 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4541 stdout = new ByteArrayOutputStream()
4542 stderr = new ByteArrayOutputStream()
4545 if (jalviewjs_j2s_to_console.equals("true")) {
4546 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4549 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4556 // macOS seems okay now with timeout arguments
4558 "--virtual-time-budget=${timeoutms}",
4559 "--timeout=${timeoutms}",
4562 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4565 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4566 "--profile-directory=${jalviewjs_chromium_profile_name}",
4567 "--allow-file-access-from-files",
4568 "--enable-logging=stderr",
4569 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4572 java.lang.Runnable runChrome = () -> {
4574 standardOutput = execStdout
4575 errorOutput = execStderr
4576 executable(chromiumBinary)
4578 println "COMMAND: '"+commandLine.join(" ")+"'"
4583 // we create our own timeout executor as --timeout doesn't work on macOS
4584 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4586 Future f1 = executor.submit( runChrome )
4588 def noChangeBytes = 0
4589 def noChangeIterations = 0
4590 executor.scheduleAtFixedRate(
4592 String stderrString = stderr.toString()
4593 // shutdown the task if we have a success string
4594 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4597 executor.shutdownNow()
4599 // if no change in stderr for 10s then also end
4600 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4601 executor.shutdownNow()
4603 if (stderrString.length() == noChangeBytes) {
4604 noChangeIterations++
4606 noChangeBytes = stderrString.length()
4607 noChangeIterations = 0
4610 200, 200, TimeUnit.MILLISECONDS)
4612 executor.schedule(new Runnable(){
4615 executor.shutdownNow()
4617 }, timeoutms, TimeUnit.MILLISECONDS)
4619 executor.awaitTermination(timeoutms+200, TimeUnit.MILLISECONDS)
4621 executor.shutdownNow()
4623 // just run chrome and rely on --virtual-time-budget and --timeout
4631 stderr.toString().eachLine { line ->
4632 if (line.contains(jalviewjs_desktop_init_string)) {
4633 println("Found line '"+line+"'")
4639 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4647 description "Build the JalviewJS site and run the launch test"
4648 dependsOn jalviewjsBuildSite
4649 dependsOn jalviewjsLaunchTest