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 groovy.transform.ExternalizeMethods
14 import groovy.util.XmlParser
15 import groovy.xml.XmlUtil
16 import groovy.json.JsonBuilder
17 import com.vladsch.flexmark.util.ast.Node
18 import com.vladsch.flexmark.html.HtmlRenderer
19 import com.vladsch.flexmark.parser.Parser
20 import com.vladsch.flexmark.util.data.MutableDataSet
21 import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
22 import com.vladsch.flexmark.ext.tables.TablesExtension
23 import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
24 import com.vladsch.flexmark.ext.autolink.AutolinkExtension
25 import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
26 import com.vladsch.flexmark.ext.toc.TocExtension
27 import com.google.common.hash.HashCode
28 import com.google.common.hash.Hashing
29 import com.google.common.io.Files
30 import org.jsoup.Jsoup
31 import org.jsoup.nodes.Element
39 classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
40 classpath "org.jsoup:jsoup:1.14.3"
41 classpath "com.eowise:gradle-imagemagick:0.5.1"
50 id "com.diffplug.gradle.spotless" version "3.28.0"
51 id 'com.github.johnrengelman.shadow' version '4.0.3'
52 id 'com.install4j.gradle' version '10.0.3'
53 id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree
54 id 'com.palantir.git-version' version '0.13.0' apply false
65 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
66 def string(Object o) {
67 return o == null ? "" : o.toString()
70 def overrideProperties(String propsFileName, boolean output = false) {
71 if (propsFileName == null) {
74 def propsFile = file(propsFileName)
75 if (propsFile != null && propsFile.exists()) {
76 println("Using properties from file '${propsFileName}'")
78 def p = new Properties()
79 def localPropsFIS = new FileInputStream(propsFile)
85 if (project.hasProperty(key)) {
86 oldval = project.findProperty(key)
87 project.setProperty(key, val)
89 println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
92 ext.setProperty(key, val)
94 println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
98 } catch (Exception e) {
99 println("Exception reading local.properties")
106 jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
107 jalviewDirRelativePath = jalviewDir
110 getdownChannelName = CHANNEL.toLowerCase()
111 // default to "default". Currently only has different cosmetics for "develop", "release", "default"
112 propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
113 channelDirName = propertiesChannelName
114 // Import channel_properties
115 if (getdownChannelName.startsWith("develop-")) {
116 channelDirName = "develop-SUFFIX"
118 channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
119 channelGradleProperties = string("${channelDir}/channel_gradle.properties")
120 channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
121 overrideProperties(channelGradleProperties, false)
122 // local build environment properties
123 // can be "projectDir/local.properties"
124 overrideProperties("${projectDir}/local.properties", true)
125 // or "../projectDir_local.properties"
126 overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
129 // Import releaseProps from the RELEASE file
130 // or a file specified via JALVIEW_RELEASE_FILE if defined
131 // Expect jalview.version and target release branch in jalview.release
132 releaseProps = new Properties();
133 def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
134 def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
136 (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream {
137 releaseProps.load(it)
139 } catch (Exception fileLoadError) {
140 throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
143 // Set JALVIEW_VERSION if it is not already set
144 if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
145 JALVIEW_VERSION = releaseProps.get("jalview.version")
147 println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
149 // this property set when running Eclipse headlessly
150 j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
151 // this property set by Eclipse
152 eclipseApplicationProperty = string("eclipse.application")
153 // CHECK IF RUNNING FROM WITHIN ECLIPSE
154 def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
155 IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
156 // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
157 if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
158 println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
162 println("WITHIN ECLIPSE IDE")
164 println("HEADLESS BUILD")
167 J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
169 println("J2S ENABLED")
172 System.properties.sort { it.key }.each {
173 key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
176 if (false && IN_ECLIPSE) {
177 jalviewDir = jalviewDirAbsolutePath
182 buildDate = new Date().format("yyyyMMdd")
185 bareSourceDir = string(source_dir)
186 sourceDir = string("${jalviewDir}/${bareSourceDir}")
187 resourceDir = string("${jalviewDir}/${resource_dir}")
188 bareTestSourceDir = string(test_source_dir)
189 testDir = string("${jalviewDir}/${bareTestSourceDir}")
191 classesDir = string("${jalviewDir}/${classes_dir}")
194 useClover = clover.equals("true")
195 cloverBuildDir = "${buildDir}/clover"
196 cloverInstrDir = file("${cloverBuildDir}/clover-instr")
197 cloverClassesDir = file("${cloverBuildDir}/clover-classes")
198 cloverReportDir = file("${buildDir}/reports/clover")
199 cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
200 cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
201 //cloverTestClassesDir = cloverClassesDir
202 cloverDb = string("${cloverBuildDir}/clover.db")
204 testSourceDir = useClover ? cloverTestInstrDir : testDir
205 testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
208 backgroundImageText = BACKGROUNDIMAGETEXT
209 getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
210 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
211 getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
212 getdownFullArchiveDir = null
213 getdownTextLines = []
214 getdownLaunchJvl = null
215 getdownVersionLaunchJvl = null
217 buildProperties = null
219 // the following values might be overridden by the CHANNEL switch
220 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
221 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
222 getdownArchiveAppBase = getdown_archive_base
223 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
224 getdownAppDistDir = getdown_app_dir_alt
225 getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
226 getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown")
227 getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
228 reportRsyncCommand = false
229 jvlChannelName = CHANNEL.toLowerCase()
230 install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
231 install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
232 install4jDMGBackgroundImageDir = "${install4j_images_dir}"
233 install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j"
234 install4jDMGBackgroundImageFile = "${install4j_dmg_background}"
235 install4jInstallerName = "${jalview_name} Non-Release Installer"
236 install4jExecutableName = install4j_executable_name
237 install4jExtraScheme = "jalviewx"
238 install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
239 install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
240 install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
241 install4jBackground = string("${install4j_images_dir}/${install4j_background}")
242 install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
243 install4jCheckSums = true
245 applicationName = "${jalview_name}"
249 // TODO: get bamboo build artifact URL for getdown artifacts
250 getdown_channel_base = bamboo_channelbase
251 getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
252 getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
253 jvlChannelName += "_${getdownChannelName}"
254 // automatically add the test group Not-bamboo for exclusion
255 if ("".equals(testng_excluded_groups)) {
256 testng_excluded_groups = "Not-bamboo"
258 install4jExtraScheme = "jalviewb"
259 backgroundImageText = true
262 case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
263 getdownAppDistDir = getdown_app_dir_release
264 getdownSetAppBaseProperty = true
265 reportRsyncCommand = true
267 install4jInstallerName = "${jalview_name} Installer"
271 getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
272 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
273 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
274 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
275 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
277 package_dir = string("${ARCHIVEDIR}/${package_dir}")
278 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
281 reportRsyncCommand = true
282 install4jExtraScheme = "jalviewa"
286 getdownChannelName = string("archive/${JALVIEW_VERSION}")
287 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
288 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
289 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
290 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']")
292 package_dir = string("${ARCHIVEDIR}/${package_dir}")
293 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
296 reportRsyncCommand = true
297 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
298 install4jSuffix = "Archive"
299 install4jExtraScheme = "jalviewa"
302 case ~/^DEVELOP-([\.\-\w]*)$/:
303 def suffix = Matcher.lastMatcher[0][1]
304 reportRsyncCommand = true
305 getdownSetAppBaseProperty = true
306 JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}"
307 install4jSuffix = "Develop ${suffix}"
308 install4jExtraScheme = "jalviewd"
309 install4jInstallerName = "${jalview_name} Develop ${suffix} Installer"
310 getdownChannelName = string("develop-${suffix}")
311 getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}")
312 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
313 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
314 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
315 channelSuffix = string(suffix)
316 backgroundImageText = true
320 reportRsyncCommand = true
321 getdownSetAppBaseProperty = true
322 // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
323 JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
325 install4jSuffix = "Develop"
326 install4jExtraScheme = "jalviewd"
327 install4jInstallerName = "${jalview_name} Develop Installer"
328 backgroundImageText = true
332 reportRsyncCommand = true
333 getdownSetAppBaseProperty = true
334 // Don't ignore transpile errors for release build
335 if (jalviewjs_ignore_transpile_errors.equals("true")) {
336 jalviewjs_ignore_transpile_errors = "false"
337 println("Setting jalviewjs_ignore_transpile_errors to 'false'")
339 JALVIEW_VERSION = JALVIEW_VERSION+"-test"
340 install4jSuffix = "Test"
341 install4jExtraScheme = "jalviewt"
342 install4jInstallerName = "${jalview_name} Test Installer"
343 backgroundImageText = true
346 case ~/^SCRATCH(|-[-\w]*)$/:
347 getdownChannelName = CHANNEL
348 JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
350 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
351 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
352 reportRsyncCommand = true
353 install4jSuffix = "Scratch"
357 if (!file("${LOCALDIR}").exists()) {
358 throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
360 getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
361 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
363 JALVIEW_VERSION = "TEST"
364 install4jSuffix = "Test-Local"
365 install4jExtraScheme = "jalviewt"
366 install4jInstallerName = "${jalview_name} Test Installer"
367 backgroundImageText = true
370 case [ "LOCAL", "JALVIEWJS" ]:
371 JALVIEW_VERSION = "TEST"
372 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
373 getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
374 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
375 install4jExtraScheme = "jalviewl"
376 install4jCheckSums = false
379 default: // something wrong specified
380 throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
384 JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
385 hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
386 hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
387 // override getdownAppBase if requested
388 if (findProperty("getdown_appbase_override") != null) {
389 // revert to LOCAL if empty string
390 if (string(getdown_appbase_override) == "") {
391 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
392 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
393 } else if (string(getdown_appbase_override).startsWith("file://")) {
394 getdownAppBase = string(getdown_appbase_override)
395 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
397 getdownAppBase = string(getdown_appbase_override)
399 println("Overriding getdown appbase with '${getdownAppBase}'")
401 // sanitise file name for jalview launcher file for this channel
402 jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
403 // install4j application and folder names
404 if (install4jSuffix == "") {
405 install4jBundleId = "${install4j_bundle_id}"
406 install4jWinApplicationId = install4j_release_win_application_id
408 applicationName = "${jalview_name} ${install4jSuffix}"
409 install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
410 // add int hash of install4jSuffix to the last part of the application_id
411 def id = install4j_release_win_application_id
412 def idsplitreverse = id.split("-").reverse()
413 idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
414 install4jWinApplicationId = idsplitreverse.reverse().join("-")
416 // sanitise folder and id names
417 // install4jApplicationFolder = e.g. "Jalview Build"
418 install4jApplicationFolder = applicationName
419 .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
420 .replaceAll("_+", "_") // collapse __
421 install4jInternalId = applicationName
423 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
424 .replaceAll("_+", "") // collapse __
425 //.replaceAll("_*-_*", "-") // collapse _-_
426 install4jUnixApplicationFolder = applicationName
428 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
429 .replaceAll("_+", "_") // collapse __
430 .replaceAll("_*-_*", "-") // collapse _-_
433 getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
434 getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
435 //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
436 getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
437 getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
438 getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
439 getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
440 /* compile without modules -- using classpath libraries
441 modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
442 modules_runtimeClasspath = modules_compileClasspath
448 apply plugin: "com.palantir.git-version"
449 def details = versionDetails()
450 gitHash = details.gitHash
451 gitBranch = details.branchName
452 } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
453 println("Not in a git repository. Using git values from RELEASE properties file.")
454 gitHash = releaseProps.getProperty("git.hash")
455 gitBranch = releaseProps.getProperty("git.branch")
456 } catch(java.lang.RuntimeException e1) {
457 throw new GradleException("Error with git-version plugin. Directory '.git' exists but versionDetails() cannot be found.")
460 println("Using a ${CHANNEL} profile.")
462 additional_compiler_args = []
463 // configure classpath/args for j8/j11 compilation
464 if (JAVA_VERSION.equals("1.8")) {
465 JAVA_INTEGER_VERSION = string("8")
468 libDistDir = j8libDir
469 compile_source_compatibility = 1.8
470 compile_target_compatibility = 1.8
471 // these are getdown.txt properties defined dependent on the JAVA_VERSION
472 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
473 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
474 // this property is assigned below and expanded to multiple lines in the getdown task
475 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
476 // this property is for the Java library used in eclipse
477 eclipseJavaRuntimeName = string("JavaSE-1.8")
478 } else if (JAVA_VERSION.equals("11")) {
479 JAVA_INTEGER_VERSION = string("11")
481 libDistDir = j11libDir
482 compile_source_compatibility = 11
483 compile_target_compatibility = 11
484 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
485 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
486 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
487 eclipseJavaRuntimeName = string("JavaSE-11")
488 /* compile without modules -- using classpath libraries
489 additional_compiler_args += [
490 '--module-path', modules_compileClasspath.asPath,
491 '--add-modules', j11modules
494 } else if (JAVA_VERSION.equals("17")) {
495 JAVA_INTEGER_VERSION = string("17")
497 libDistDir = j17libDir
498 compile_source_compatibility = 17
499 compile_target_compatibility = 17
500 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
501 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
502 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
503 eclipseJavaRuntimeName = string("JavaSE-17")
504 /* compile without modules -- using classpath libraries
505 additional_compiler_args += [
506 '--module-path', modules_compileClasspath.asPath,
507 '--add-modules', j11modules
511 throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
516 JAVA_MIN_VERSION = JAVA_VERSION
517 JAVA_MAX_VERSION = JAVA_VERSION
518 jreInstallsDir = string(jre_installs_dir)
519 if (jreInstallsDir.startsWith("~/")) {
520 jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
522 install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
523 install4jConfFileName = string("jalview-install4j-conf.install4j")
524 install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
525 install4jHomeDir = install4j_home_dir
526 if (install4jHomeDir.startsWith("~/")) {
527 install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
530 resourceBuildDir = string("${buildDir}/resources")
531 resourcesBuildDir = string("${resourceBuildDir}/resources_build")
532 helpBuildDir = string("${resourceBuildDir}/help_build")
533 docBuildDir = string("${resourceBuildDir}/doc_build")
535 if (buildProperties == null) {
536 buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
538 buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
539 helpParentDir = string("${jalviewDir}/${help_parent_dir}")
540 helpSourceDir = string("${helpParentDir}/${help_dir}")
541 helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
544 convertBinaryExpectedLocation = imagemagick_convert
545 if (convertBinaryExpectedLocation.startsWith("~/")) {
546 convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1)
548 if (file(convertBinaryExpectedLocation).exists()) {
549 convertBinary = convertBinaryExpectedLocation
552 relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
553 jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
554 jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
556 jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
558 jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
560 jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
561 jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
562 jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
563 jalviewjsJalviewCoreHtmlFile = string("")
564 jalviewjsJalviewCoreName = string(jalviewjs_core_name)
565 jalviewjsCoreClasslists = []
566 jalviewjsJalviewTemplateName = string(jalviewjs_name)
567 jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
568 jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
569 jalviewjsJ2sProps = null
570 jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
572 eclipseWorkspace = null
573 eclipseBinary = string("")
574 eclipseVersion = string("")
584 outputDir = file(classesDir)
588 srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
591 compileClasspath = files(sourceSets.main.java.outputDir)
592 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
594 runtimeClasspath = compileClasspath
595 runtimeClasspath += files(sourceSets.main.resources.srcDirs)
600 srcDirs cloverInstrDir
601 outputDir = cloverClassesDir
605 srcDirs = sourceSets.main.resources.srcDirs
608 compileClasspath = files( sourceSets.clover.java.outputDir )
609 //compileClasspath += files( testClassesDir )
610 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
611 compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
612 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
614 runtimeClasspath = compileClasspath
619 srcDirs testSourceDir
620 outputDir = file(testClassesDir)
624 srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
627 compileClasspath = files( sourceSets.test.java.outputDir )
628 compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
629 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
631 runtimeClasspath = compileClasspath
632 runtimeClasspath += files(sourceSets.test.resources.srcDirs)
638 // eclipse project and settings files creation, also used by buildship
641 name = eclipse_project_name
643 natures 'org.eclipse.jdt.core.javanature',
644 'org.eclipse.jdt.groovy.core.groovyNature',
645 'org.eclipse.buildship.core.gradleprojectnature'
647 buildCommand 'org.eclipse.jdt.core.javabuilder'
648 buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
652 //defaultOutputDir = sourceSets.main.java.outputDir
653 configurations.each{ c->
654 if (c.isCanBeResolved()) {
655 minusConfigurations += [c]
659 plusConfigurations = [ ]
663 def removeTheseToo = []
664 HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
665 cp.entries.each { entry ->
666 // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
667 // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
668 // we add the resources and help/help dirs in as libs afterwards (see below)
669 if (entry.kind == 'src') {
670 if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
671 removeTheseToo += entry
673 alreadyAddedSrcPath.putAt(entry.path, true)
678 cp.entries.removeAll(removeTheseToo)
680 //cp.entries += new Output("${eclipse_bin_dir}/main")
681 if (file(helpParentDir).isDirectory()) {
682 cp.entries += new Library(fileReference(helpParentDir))
684 if (file(resourceDir).isDirectory()) {
685 cp.entries += new Library(fileReference(resourceDir))
688 HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
690 sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
691 //don't want to add outputDir as eclipse is using its own output dir in bin/main
692 if (it.isDirectory() || ! it.exists()) {
693 // don't add dirs to classpath, especially if they don't exist
694 return false // groovy "continue" in .any closure
696 def itPath = it.toString()
697 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
698 // make relative path
699 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
701 if (alreadyAddedLibPath.get(itPath)) {
702 //println("Not adding duplicate entry "+itPath)
704 //println("Adding entry "+itPath)
705 cp.entries += new Library(fileReference(itPath))
706 alreadyAddedLibPath.put(itPath, true)
710 sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
711 //no longer want to add outputDir as eclipse is using its own output dir in bin/main
712 if (it.isDirectory() || ! it.exists()) {
713 // don't add dirs to classpath
714 return false // groovy "continue" in .any closure
717 def itPath = it.toString()
718 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
719 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
721 if (alreadyAddedLibPath.get(itPath)) {
724 def lib = new Library(fileReference(itPath))
725 lib.entryAttributes["test"] = "true"
727 alreadyAddedLibPath.put(itPath, true)
735 containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
740 // for the IDE, use java 11 compatibility
741 sourceCompatibility = compile_source_compatibility
742 targetCompatibility = compile_target_compatibility
743 javaRuntimeName = eclipseJavaRuntimeName
745 // add in jalview project specific properties/preferences into eclipse core preferences
747 withProperties { props ->
748 def jalview_prefs = new Properties()
749 def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
750 jalview_prefs.load(ins)
752 jalview_prefs.forEach { t, v ->
753 if (props.getAt(t) == null) {
757 // codestyle file -- overrides previous formatter prefs
758 def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
759 if (csFile.exists()) {
760 XmlParser parser = new XmlParser()
761 def profiles = parser.parse(csFile)
762 def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
763 if (profile != null) {
764 profile.'setting'.each { s ->
766 def value = s.'@value'
767 if (id != null && value != null) {
768 props.putAt(id, value)
779 // Don't want these to be activated if in headless build
780 synchronizationTasks "eclipseSynchronizationTask"
781 //autoBuildTasks "eclipseAutoBuildTask"
787 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
788 // Class to allow updating arbitrary properties files
789 class PropertiesFile extends PropertiesPersistableConfigurationObject {
790 public PropertiesFile(PropertiesTransformer t) { super(t); }
791 @Override protected void load(Properties properties) { }
792 @Override protected void store(Properties properties) { }
793 @Override protected String getDefaultResourceName() { return ""; }
794 // This is necessary, because PropertiesPersistableConfigurationObject fails
795 // if no default properties file exists.
796 @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
799 // Task to update arbitrary properties files (set outputFile)
800 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
801 private final PropertiesFileContentMerger file;
802 public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
803 protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
804 protected void configure(PropertiesFile props) {
805 file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
807 public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
810 task eclipseUIPreferences(type: PropertiesFileTask) {
811 description = "Generate Eclipse additional settings"
812 def filename = "org.eclipse.jdt.ui.prefs"
813 outputFile = "$projectDir/.settings/${filename}" as File
816 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
821 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
822 description = "Generate Eclipse additional settings"
823 def filename = "org.eclipse.jdt.groovy.core.prefs"
824 outputFile = "$projectDir/.settings/${filename}" as File
827 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
832 task eclipseAllPreferences {
834 dependsOn eclipseUIPreferences
835 dependsOn eclipseGroovyCorePreferences
838 eclipseUIPreferences.mustRunAfter eclipseJdt
839 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
841 /* end of eclipse preferences hack */
849 delete cloverBuildDir
850 delete cloverReportDir
855 task cloverInstrJava(type: JavaExec) {
856 group = "Verification"
857 description = "Create clover instrumented source java files"
859 dependsOn cleanClover
861 inputs.files(sourceSets.main.allJava)
862 outputs.dir(cloverInstrDir)
864 //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
865 classpath = sourceSets.clover.compileClasspath
866 main = "com.atlassian.clover.CloverInstr"
874 cloverInstrDir.getPath(),
876 def srcFiles = sourceSets.main.allJava.files
879 { file -> file.absolutePath }
882 args argsList.toArray()
885 delete cloverInstrDir
886 println("Clover: About to instrument "+srcFiles.size() +" files")
891 task cloverInstrTests(type: JavaExec) {
892 group = "Verification"
893 description = "Create clover instrumented source test files"
895 dependsOn cleanClover
897 inputs.files(testDir)
898 outputs.dir(cloverTestInstrDir)
900 classpath = sourceSets.clover.compileClasspath
901 main = "com.atlassian.clover.CloverInstr"
911 cloverTestInstrDir.getPath(),
913 args argsList.toArray()
916 delete cloverTestInstrDir
917 println("Clover: About to instrument test files")
923 group = "Verification"
924 description = "Create clover instrumented all source files"
926 dependsOn cloverInstrJava
927 dependsOn cloverInstrTests
931 cloverClasses.dependsOn cloverInstr
934 task cloverConsoleReport(type: JavaExec) {
935 group = "Verification"
936 description = "Creates clover console report"
939 file(cloverDb).exists()
942 inputs.dir cloverClassesDir
944 classpath = sourceSets.clover.runtimeClasspath
945 main = "com.atlassian.clover.reporters.console.ConsoleReporter"
947 if (cloverreport_mem.length() > 0) {
948 maxHeapSize = cloverreport_mem
950 if (cloverreport_jvmargs.length() > 0) {
951 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
961 args argsList.toArray()
965 task cloverHtmlReport(type: JavaExec) {
966 group = "Verification"
967 description = "Creates clover HTML report"
970 file(cloverDb).exists()
973 def cloverHtmlDir = cloverReportDir
974 inputs.dir cloverClassesDir
975 outputs.dir cloverHtmlDir
977 classpath = sourceSets.clover.runtimeClasspath
978 main = "com.atlassian.clover.reporters.html.HtmlReporter"
980 if (cloverreport_mem.length() > 0) {
981 maxHeapSize = cloverreport_mem
983 if (cloverreport_jvmargs.length() > 0) {
984 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
995 if (cloverreport_html_options.length() > 0) {
996 argsList += cloverreport_html_options.split(" ")
999 args argsList.toArray()
1003 task cloverXmlReport(type: JavaExec) {
1004 group = "Verification"
1005 description = "Creates clover XML report"
1008 file(cloverDb).exists()
1011 def cloverXmlFile = "${cloverReportDir}/clover.xml"
1012 inputs.dir cloverClassesDir
1013 outputs.file cloverXmlFile
1015 classpath = sourceSets.clover.runtimeClasspath
1016 main = "com.atlassian.clover.reporters.xml.XMLReporter"
1018 if (cloverreport_mem.length() > 0) {
1019 maxHeapSize = cloverreport_mem
1021 if (cloverreport_jvmargs.length() > 0) {
1022 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1033 if (cloverreport_xml_options.length() > 0) {
1034 argsList += cloverreport_xml_options.split(" ")
1037 args argsList.toArray()
1042 group = "Verification"
1043 description = "Creates clover reports"
1045 dependsOn cloverXmlReport
1046 dependsOn cloverHtmlReport
1053 sourceCompatibility = compile_source_compatibility
1054 targetCompatibility = compile_target_compatibility
1055 options.compilerArgs += additional_compiler_args
1056 print ("Setting target compatibility to "+targetCompatibility+"\n")
1058 //classpath += configurations.cloverRuntime
1064 // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1065 sourceCompatibility = compile_source_compatibility
1066 targetCompatibility = compile_target_compatibility
1067 options.compilerArgs += additional_compiler_args
1068 options.encoding = "UTF-8"
1070 print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1077 sourceCompatibility = compile_source_compatibility
1078 targetCompatibility = compile_target_compatibility
1079 options.compilerArgs += additional_compiler_args
1081 print ("Setting target compatibility to "+targetCompatibility+"\n")
1088 delete sourceSets.main.java.outputDir
1094 dependsOn cleanClover
1096 delete sourceSets.test.java.outputDir
1101 // format is a string like date.format("dd MMMM yyyy")
1102 def getDate(format) {
1103 return date.format(format)
1107 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1108 MutableDataSet options = new MutableDataSet()
1110 def extensions = new ArrayList<>()
1111 extensions.add(AnchorLinkExtension.create())
1112 extensions.add(AutolinkExtension.create())
1113 extensions.add(StrikethroughExtension.create())
1114 extensions.add(TaskListExtension.create())
1115 extensions.add(TablesExtension.create())
1116 extensions.add(TocExtension.create())
1118 options.set(Parser.EXTENSIONS, extensions)
1120 // set GFM table parsing options
1121 options.set(TablesExtension.WITH_CAPTION, false)
1122 options.set(TablesExtension.COLUMN_SPANS, false)
1123 options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1124 options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1125 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1126 options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1127 options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1129 options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1130 options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1131 options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1132 options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1134 Parser parser = Parser.builder(options).build()
1135 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1137 mdFiles.each { mdFile ->
1138 // add table of contents
1139 def mdText = "[TOC]\n"+mdFile.text
1141 // grab the first top-level title
1143 def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1144 def matcher = mdText =~ titleRegex
1145 if (matcher.size() > 0) {
1146 // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1147 title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1149 // or use the filename if none found
1150 if (title == null) {
1151 title = mdFile.getName()
1154 Node document = parser.parse(mdText)
1155 String htmlBody = renderer.render(document)
1156 def htmlText = '''<html>
1157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1158 <html xmlns="http://www.w3.org/1999/xhtml">
1160 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1161 <meta http-equiv="Content-Style-Type" content="text/css" />
1162 <meta name="generator" content="flexmark" />
1164 htmlText += ((title != null) ? " <title>${title}</title>" : '' )
1166 <style type="text/css">code{white-space: pre;}</style>
1168 htmlText += ((cssFile != null) ? cssFile.text : '')
1169 htmlText += '''</head>
1172 htmlText += htmlBody
1178 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1179 def htmlFile = file(htmlFilePath)
1180 println("Creating ${htmlFilePath}")
1181 htmlFile.text = htmlText
1186 task copyDocs(type: Copy) {
1187 def inputDir = "${jalviewDir}/${doc_dir}"
1188 def outputDir = "${docBuildDir}/${doc_dir}"
1192 include('**/*.html')
1194 filter(ReplaceTokens,
1198 'Version-Rel': JALVIEW_VERSION,
1199 'Year-Rel': getDate("yyyy")
1206 exclude('**/*.html')
1211 inputs.dir(inputDir)
1212 outputs.dir(outputDir)
1216 task convertMdFiles {
1218 def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1219 def cssFile = file("${jalviewDir}/${flexmark_css}")
1222 convertMdToHtml(mdFiles, cssFile)
1225 inputs.files(mdFiles)
1226 inputs.file(cssFile)
1229 mdFiles.each { mdFile ->
1230 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1231 htmlFiles.add(file(htmlFilePath))
1233 outputs.files(htmlFiles)
1237 def hugoTemplateSubstitutions(String input, Map extras=null) {
1238 def replacements = [
1239 DATE: getDate("yyyy-MM-dd"),
1240 CHANNEL: propertiesChannelName,
1241 APPLICATION_NAME: applicationName,
1243 GIT_BRANCH: gitBranch,
1244 VERSION: JALVIEW_VERSION,
1245 JAVA_VERSION: JAVA_VERSION,
1246 VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
1251 if (extras != null) {
1252 extras.each{ k, v ->
1253 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1256 replacements.each{ k, v ->
1257 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1262 def mdFileComponents(File mdFile, def dateOnly=false) {
1265 if (mdFile.exists()) {
1266 def inFrontMatter = false
1267 def firstLine = true
1268 mdFile.eachLine { line ->
1269 if (line.matches("---")) {
1270 def prev = inFrontMatter
1271 inFrontMatter = firstLine
1272 if (inFrontMatter != prev)
1275 if (inFrontMatter) {
1277 if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
1278 map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
1279 } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
1280 map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
1281 } else if (m = line =~ /^channel:\s*(\S+)/) {
1282 map["channel"] = m[0][1]
1283 } else if (m = line =~ /^version:\s*(\S+)/) {
1284 map["version"] = m[0][1]
1285 } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
1286 map[ m[0][1] ] = m[0][2]
1288 if (dateOnly && map["date"] != null) {
1294 content += line+"\n"
1299 return dateOnly ? map["date"] : [map, content]
1302 task hugoTemplates {
1304 description "Create partially populated md pages for hugo website build"
1306 def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
1307 def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
1308 def templateFiles = fileTree(dir: hugoTemplatesDir)
1309 def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1310 def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1311 def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
1312 def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
1315 // specific release template for version archive
1318 def givenDate = null
1319 def givenChannel = null
1320 def givenVersion = null
1321 if (CHANNEL == "RELEASE") {
1322 def (map, content) = mdFileComponents(releaseMdFile)
1323 givenDate = map.date
1324 givenChannel = map.channel
1325 givenVersion = map.version
1327 if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
1328 throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
1331 if (whatsnewMdFile.exists())
1332 whatsnew = whatsnewMdFile.text
1335 def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
1336 def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
1338 def changesHugo = null
1339 if (changes != null) {
1340 changesHugo = '<div class="release_notes">\n\n'
1341 def inSection = false
1342 changes.eachLine { line ->
1344 if (m = line =~ /^##([^#].*)$/) {
1346 changesHugo += "</div>\n\n"
1348 def section = m[0][1].trim()
1349 section = section.toLowerCase()
1350 section = section.replaceAll(/ +/, "_")
1351 section = section.replaceAll(/[^a-z0-9_\-]/, "")
1352 changesHugo += "<div class=\"${section}\">\n\n"
1354 } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
1355 def comment = m[0][2].trim()
1356 if (comment != "") {
1357 comment = comment.replaceAll('"', """)
1359 comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
1360 def newline = m[0][1]
1361 if (comment.trim() != "")
1362 newline += "{{<comment>}}${comment}{{</comment>}} "
1363 newline += m[0][3].trim()
1364 if (issuekeys.size() > 0)
1365 newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
1366 if (m[0][4] != null)
1371 changesHugo += line+"\n"
1374 changesHugo += "\n</div>\n\n"
1376 changesHugo += '</div>'
1379 templateFiles.each{ templateFile ->
1380 def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
1381 def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
1382 def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
1384 def outPathName = string("${hugoBuildDir}/$newRelPathName")
1388 rename(templateFile.getName(), newFileName)
1392 def newFile = file("${outPathName}/${newFileName}".toString())
1393 def content = newFile.text
1394 newFile.text = hugoTemplateSubstitutions(content,
1397 CHANGES: changesHugo,
1398 DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
1399 DRAFT: givenDate == null ? "true" : "false",
1400 JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
1401 JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
1408 inputs.file(oldJvlFile)
1409 inputs.dir(hugoTemplatesDir)
1410 inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
1411 inputs.property("CHANNEL", { CHANNEL })
1414 def getMdDate(File mdFile) {
1415 return mdFileComponents(mdFile, true)
1418 def getMdSections(String content) {
1420 def sectionContent = ""
1421 def sectionName = null
1422 content.eachLine { line ->
1424 if (m = line =~ /^##([^#].*)$/) {
1425 if (sectionName != null) {
1426 sections[sectionName] = sectionContent
1430 sectionName = m[0][1].trim()
1431 sectionName = sectionName.toLowerCase()
1432 sectionName = sectionName.replaceAll(/ +/, "_")
1433 sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
1434 } else if (sectionName != null) {
1435 sectionContent += line+"\n"
1438 if (sectionContent != null) {
1439 sections[sectionName] = sectionContent
1445 task copyHelp(type: Copy) {
1446 def inputDir = helpSourceDir
1447 def outputDir = "${helpBuildDir}/${help_dir}"
1451 include('**/*.html')
1455 filter(ReplaceTokens,
1459 'Version-Rel': JALVIEW_VERSION,
1460 'Year-Rel': getDate("yyyy")
1467 exclude('**/*.html')
1474 inputs.dir(inputDir)
1475 outputs.files(helpFile)
1476 outputs.dir(outputDir)
1480 task releasesTemplates {
1482 description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1486 def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1487 def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1488 def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
1489 def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
1490 def releasesMdDir = "${jalviewDir}/${releases_dir}"
1491 def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1494 def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1495 def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1497 if (CHANNEL == "RELEASE") {
1498 if (!releaseMdFile.exists()) {
1499 throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1501 if (!whatsnewMdFile.exists()) {
1502 throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1506 def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1507 def releaseFilesDates = releaseFiles.collectEntries {
1508 [(it): getMdDate(it)]
1510 releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1512 def releasesTemplate = releasesTemplateFile.text
1513 def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1514 def versionTemplate = m[0][1]
1516 MutableDataSet options = new MutableDataSet()
1518 def extensions = new ArrayList<>()
1519 options.set(Parser.EXTENSIONS, extensions)
1520 options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1522 Parser parser = Parser.builder(options).build()
1523 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1525 def actualVersions = releaseFiles.collect { rf ->
1526 def (rfMap, rfContent) = mdFileComponents(rf)
1527 return rfMap.version
1529 def versionsHtml = ""
1530 def linkedVersions = []
1531 releaseFiles.reverse().each { rFile ->
1532 def (rMap, rContent) = mdFileComponents(rFile)
1534 def versionLink = ""
1535 def partialVersion = ""
1536 def firstPart = true
1537 rMap.version.split("\\.").each { part ->
1538 def displayPart = ( firstPart ? "" : "." ) + part
1539 partialVersion += displayPart
1541 linkedVersions.contains(partialVersion)
1542 || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1544 versionLink += displayPart
1546 versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1547 linkedVersions += partialVersion
1551 def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1554 def rContentProcessed = ""
1555 rContent.eachLine { line ->
1556 if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1557 line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1558 } else if (lm = line =~ /^###([^#]+.*)$/) {
1559 line = "_${lm[0][1].trim()}_"
1561 rContentProcessed += line + "\n"
1564 def rContentSections = getMdSections(rContentProcessed)
1565 def rVersion = versionTemplate
1566 if (rVersion != "") {
1567 def rNewFeatures = rContentSections["new_features"]
1568 def rIssuesResolved = rContentSections["issues_resolved"]
1569 Node newFeaturesNode = parser.parse(rNewFeatures)
1570 String newFeaturesHtml = renderer.render(newFeaturesNode)
1571 Node issuesResolvedNode = parser.parse(rIssuesResolved)
1572 String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1573 rVersion = hugoTemplateSubstitutions(rVersion,
1575 VERSION: rMap.version,
1576 VERSION_LINK: versionLink,
1577 DISPLAY_DATE: displayDate,
1578 NEW_FEATURES: newFeaturesHtml,
1579 ISSUES_RESOLVED: issuesResolvedHtml
1582 versionsHtml += rVersion
1586 releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1587 releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1588 releasesHtmlFile.text = releasesTemplate
1590 if (whatsnewMdFile.exists()) {
1591 def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1592 def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1593 Node whatsnewNode = parser.parse(whatsnewMd)
1594 String whatsnewHtml = renderer.render(whatsnewNode)
1595 whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1596 whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1598 VERSION: JALVIEW_VERSION,
1599 DISPLAY_DATE: wnDisplayDate
1602 } else if (gradle.taskGraph.hasTask(":linkCheck")) {
1603 whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
1608 inputs.file(releasesTemplateFile)
1609 inputs.file(whatsnewTemplateFile)
1610 inputs.dir(releasesMdDir)
1611 inputs.dir(whatsnewMdDir)
1612 outputs.file(releasesHtmlFile)
1613 outputs.file(whatsnewHtmlFile)
1617 task copyResources(type: Copy) {
1619 description = "Copy (and make text substitutions in) the resources dir to the build area"
1621 def inputDir = resourceDir
1622 def outputDir = resourcesBuildDir
1626 include('**/*.html')
1628 filter(ReplaceTokens,
1632 'Version-Rel': JALVIEW_VERSION,
1633 'Year-Rel': getDate("yyyy")
1640 exclude('**/*.html')
1645 inputs.dir(inputDir)
1646 outputs.dir(outputDir)
1649 task copyChannelResources(type: Copy) {
1650 dependsOn copyResources
1652 description = "Copy the channel resources dir to the build resources area"
1654 def inputDir = "${channelDir}/${resource_dir}"
1655 def outputDir = resourcesBuildDir
1657 include(channel_props)
1658 filter(ReplaceTokens,
1662 'SUFFIX': channelSuffix
1667 exclude(channel_props)
1671 inputs.dir(inputDir)
1672 outputs.dir(outputDir)
1675 task createBuildProperties(type: WriteProperties) {
1676 dependsOn copyResources
1678 description = "Create the ${buildProperties} file"
1680 inputs.dir(sourceDir)
1681 inputs.dir(resourcesBuildDir)
1682 outputFile (buildProperties)
1683 // taking time specific comment out to allow better incremental builds
1684 comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1685 //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1686 property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1687 property "VERSION", JALVIEW_VERSION
1688 property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1689 property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
1690 if (getdownSetAppBaseProperty) {
1691 property "GETDOWNAPPBASE", getdownAppBase
1692 property "GETDOWNAPPDISTDIR", getdownAppDistDir
1694 outputs.file(outputFile)
1698 task buildIndices(type: JavaExec) {
1700 classpath = sourceSets.main.compileClasspath
1701 main = "com.sun.java.help.search.Indexer"
1702 workingDir = "${helpBuildDir}/${help_dir}"
1705 inputs.dir("${workingDir}/${argDir}")
1707 outputs.dir("${classesDir}/doc")
1708 outputs.dir("${classesDir}/help")
1709 outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1710 outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1711 outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1712 outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1713 outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1714 outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1717 task buildResources {
1718 dependsOn copyResources
1719 dependsOn copyChannelResources
1720 dependsOn createBuildProperties
1724 dependsOn buildResources
1727 dependsOn releasesTemplates
1728 dependsOn convertMdFiles
1729 dependsOn buildIndices
1733 compileJava.dependsOn prepare
1734 run.dependsOn compileJava
1735 compileTestJava.dependsOn compileJava
1738 /* testTask0 is the main test task */
1739 task testTask0(type: Test) {
1740 group = "Verification"
1741 description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
1743 includeGroups testng_groups.split(",")
1744 excludeGroups testng_excluded_groups.split(",")
1745 tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
1747 useDefaultListeners=true
1751 /* separated tests */
1752 task testTask1(type: Test) {
1753 group = "Verification"
1754 description = "Tests that need to be isolated from the main test run"
1757 excludeGroups testng_excluded_groups.split(",")
1759 useDefaultListeners=true
1764 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1765 * to summarise test results from all Test tasks
1767 /* START of test tasks results summary */
1768 import groovy.time.TimeCategory
1769 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1770 import org.gradle.api.tasks.testing.logging.TestLogEvent
1771 rootProject.ext.testsResults = [] // Container for tests summaries
1773 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1775 // from original test task
1777 dependsOn cloverClasses
1779 dependsOn testClasses //?
1782 // run main tests first
1783 if (!testTask.name.equals("testTask0"))
1784 testTask.mustRunAfter "testTask0"
1786 testTask.testLogging { logging ->
1787 events TestLogEvent.FAILED,
1788 TestLogEvent.SKIPPED,
1789 TestLogEvent.STANDARD_OUT,
1790 TestLogEvent.STANDARD_ERROR
1792 exceptionFormat TestExceptionFormat.FULL
1795 showStackTraces true
1798 ignoreFailures = true // Always try to run all tests for all modules
1800 afterSuite { desc, result ->
1802 if (desc.parent) return // Only summarize results for whole modules
1804 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1806 rootProject.ext.testsResults.add(resultsInfo)
1809 // from original test task
1810 maxHeapSize = "1024m"
1812 workingDir = jalviewDir
1813 def testLaf = project.findProperty("test_laf")
1814 if (testLaf != null) {
1815 println("Setting Test LaF to '${testLaf}'")
1816 systemProperty "laf", testLaf
1818 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1819 if (testHiDPIScale != null) {
1820 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1821 systemProperty "sun.java2d.uiScale", testHiDPIScale
1823 sourceCompatibility = compile_source_compatibility
1824 targetCompatibility = compile_target_compatibility
1825 jvmArgs += additional_compiler_args
1829 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1835 gradle.buildFinished {
1836 def allResults = rootProject.ext.testsResults
1838 if (!allResults.isEmpty()) {
1839 printResults allResults
1840 allResults.each {r ->
1841 if (r[2].resultType == TestResult.ResultType.FAILURE)
1842 throw new GradleException("Failed tests!")
1847 private static String colString(styler, col, colour, text) {
1848 return col?"${styler[colour](text)}":text
1851 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1852 def colour = 'black'
1855 case TestResult.ResultType.SUCCESS:
1858 case TestResult.ResultType.FAILURE:
1865 StringBuilder sb = new StringBuilder()
1869 sb.append(" results: ")
1870 sb.append(colString(s, col && !nocol, colour, rt))
1872 sb.append("${rc} tests, ")
1873 sb.append(colString(s, col && rs > 0, 'green', rs))
1874 sb.append(" successes, ")
1875 sb.append(colString(s, col && rf > 0, 'red', rf))
1876 sb.append(" failures, ")
1877 sb.append("${rsk} skipped) in ${t}")
1878 return sb.toString()
1881 private static void printResults(allResults) {
1883 // styler from https://stackoverflow.com/a/56139852
1884 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
1887 def failedTests = false
1888 def summaryLines = []
1890 def totalsuccess = 0
1893 def totaltime = TimeCategory.getSeconds(0)
1894 // sort on project name then task name
1895 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
1896 def projectName = it[0]
1897 def taskName = it[1]
1901 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
1902 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
1903 def reportLine = "Report file: ${report}"
1904 def ls = summaryPlain.length()
1905 def lr = reportLine.length()
1906 def m = [ls, lr].max()
1909 def info = [ls, summaryCol, reportLine]
1910 summaryLines.add(info)
1911 failedTests |= result.resultType == TestResult.ResultType.FAILURE
1912 totalcount += result.testCount
1913 totalsuccess += result.successfulTestCount
1914 totalfail += result.failedTestCount
1915 totalskip += result.skippedTestCount
1918 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
1919 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
1920 def tls = totalSummaryPlain.length()
1921 if (tls > maxLength)
1923 def info = [tls, totalSummaryCol, null]
1924 summaryLines.add(info)
1926 def allSummaries = []
1927 for(sInfo : summaryLines) {
1929 def summary = sInfo[1]
1930 def report = sInfo[2]
1932 StringBuilder sb = new StringBuilder()
1933 sb.append("│" + summary + " " * (maxLength - ls) + "│")
1934 if (report != null) {
1935 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
1937 allSummaries += sb.toString()
1940 println "┌${"${"─" * maxLength}"}┐"
1941 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
1942 println "└${"${"─" * maxLength}"}┘"
1944 /* END of test tasks results summary */
1947 // from original test task
1949 dependsOn cloverClasses
1951 dependsOn testClasses
1953 dependsOn tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}
1955 // not running tests in this task
1960 task compileLinkCheck(type: JavaCompile) {
1962 classpath = files("${jalviewDir}/${utils_dir}")
1963 destinationDir = file("${jalviewDir}/${utils_dir}")
1964 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
1966 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1967 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
1968 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
1969 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
1973 task linkCheck(type: JavaExec) {
1975 dependsOn compileLinkCheck
1977 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
1978 classpath = files("${jalviewDir}/${utils_dir}")
1979 main = "HelpLinksChecker"
1980 workingDir = "${helpBuildDir}"
1981 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
1983 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
1984 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
1987 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
1991 inputs.dir(helpBuildDir)
1992 outputs.file(helpLinksCheckerOutFile)
1996 // import the pubhtmlhelp target
1997 ant.properties.basedir = "${jalviewDir}"
1998 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
1999 ant.importBuild "${utils_dir}/publishHelp.xml"
2002 task cleanPackageDir(type: Delete) {
2004 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2014 attributes "Main-Class": main_class,
2015 "Permissions": "all-permissions",
2016 "Application-Name": applicationName,
2017 "Codebase": application_codebase,
2018 "Implementation-Version": JALVIEW_VERSION
2021 def outputDir = "${jalviewDir}/${package_dir}"
2022 destinationDirectory = file(outputDir)
2023 archiveFileName = rootProject.name+".jar"
2024 duplicatesStrategy "EXCLUDE"
2031 exclude "**/*.jar.*"
2033 inputs.dir(sourceSets.main.java.outputDir)
2034 sourceSets.main.resources.srcDirs.each{ dir ->
2037 outputs.file("${outputDir}/${archiveFileName}")
2041 task copyJars(type: Copy) {
2042 from fileTree(dir: classesDir, include: "**/*.jar").files
2043 into "${jalviewDir}/${package_dir}"
2047 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2048 task syncJars(type: Sync) {
2050 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2051 into "${jalviewDir}/${package_dir}"
2053 include jar.archiveFileName.getOrNull()
2060 description = "Put all required libraries in dist"
2061 // order of "cleanPackageDir", "copyJars", "jar" important!
2062 jar.mustRunAfter cleanPackageDir
2063 syncJars.mustRunAfter cleanPackageDir
2064 dependsOn cleanPackageDir
2067 outputs.dir("${jalviewDir}/${package_dir}")
2072 dependsOn cleanPackageDir
2079 group = "distribution"
2080 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2084 from ("${jalviewDir}/${libDistDir}") {
2088 attributes "Implementation-Version": JALVIEW_VERSION,
2089 "Application-Name": applicationName
2092 duplicatesStrategy "INCLUDE"
2094 mainClassName = shadow_jar_main_class
2096 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2100 task getdownImagesCopy() {
2101 inputs.dir getdownImagesDir
2102 outputs.dir getdownImagesBuildDir
2106 from(getdownImagesDir) {
2107 include("*getdown*.png")
2109 into getdownImagesBuildDir
2114 task getdownImagesProcess() {
2115 dependsOn getdownImagesCopy
2118 if (backgroundImageText) {
2119 if (convertBinary == null) {
2120 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2122 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2123 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2125 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2127 executable convertBinary
2130 '-font', getdown_background_image_text_font,
2131 '-fill', getdown_background_image_text_colour,
2132 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2133 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2134 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2143 task getdownImages() {
2144 dependsOn getdownImagesProcess
2147 task getdownWebsite() {
2148 group = "distribution"
2149 description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
2151 dependsOn getdownImages
2156 def getdownWebsiteResourceFilenames = []
2157 def getdownResourceDir = getdownResourceDir
2158 def getdownResourceFilenames = []
2161 // clean the getdown website and files dir before creating getdown folders
2162 delete getdownAppBaseDir
2163 delete getdownFilesDir
2166 from buildProperties
2167 rename(file(buildProperties).getName(), getdown_build_properties)
2170 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2173 from channelPropsFile
2174 filter(ReplaceTokens,
2178 'SUFFIX': channelSuffix
2181 into getdownAppBaseDir
2183 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2185 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2186 def props = project.properties.sort { it.key }
2187 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2188 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2190 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2191 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2193 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2194 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2196 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2197 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2198 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2199 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2200 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2201 props.put("getdown_txt_ui.icon", "${getdownImagesBuildDir}/${getdown_icon}")
2202 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesBuildDir}/${getdown_mac_dock_icon}")
2205 props.put("getdown_txt_title", jalview_name)
2206 props.put("getdown_txt_ui.name", applicationName)
2208 // start with appbase
2209 getdownTextLines += "appbase = ${getdownAppBase}"
2210 props.each{ prop, val ->
2211 if (prop.startsWith("getdown_txt_") && val != null) {
2212 if (prop.startsWith("getdown_txt_multi_")) {
2213 def key = prop.substring(18)
2214 val.split(",").each{ v ->
2215 def line = "${key} = ${v}"
2216 getdownTextLines += line
2219 // file values rationalised
2220 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2222 if (val.indexOf('/') == 0) {
2225 } else if (val.indexOf('/') > 0) {
2226 // relative path (relative to jalviewDir)
2227 r = file( "${jalviewDir}/${val}" )
2230 val = "${getdown_resource_dir}/" + r.getName()
2231 getdownWebsiteResourceFilenames += val
2232 getdownResourceFilenames += r.getPath()
2235 if (! prop.startsWith("getdown_txt_resource")) {
2236 def line = prop.substring(12) + " = ${val}"
2237 getdownTextLines += line
2243 getdownWebsiteResourceFilenames.each{ filename ->
2244 getdownTextLines += "resource = ${filename}"
2246 getdownResourceFilenames.each{ filename ->
2249 into getdownResourceDir
2253 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2254 getdownWrapperScripts.each{ script ->
2255 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2259 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2261 getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}"
2266 fileTree(file(package_dir)).each{ f ->
2267 if (f.isDirectory()) {
2268 def files = fileTree(dir: f, include: ["*"]).getFiles()
2270 } else if (f.exists()) {
2274 def jalviewJar = jar.archiveFileName.getOrNull()
2275 // put jalview.jar first for CLASSPATH and .properties files reasons
2276 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2277 def name = f.getName()
2278 def line = "code = ${getdownAppDistDir}/${name}"
2279 getdownTextLines += line
2286 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2288 if (JAVA_VERSION.equals("11")) {
2289 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2290 j11libFiles.sort().each{f ->
2291 def name = f.getName()
2292 def line = "code = ${getdown_j11lib_dir}/${name}"
2293 getdownTextLines += line
2296 into getdownJ11libDir
2302 // 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.
2303 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2304 getdownTextLines += "resource = ${getdown_launcher_new}"
2305 getdownTextLines += "class = ${main_class}"
2306 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2307 if (getdownSetAppBaseProperty) {
2308 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2309 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2312 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2313 getdownTxt.write(getdownTextLines.join("\n"))
2315 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2316 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2317 launchJvl.write("appbase=${getdownAppBase}")
2319 // files going into the getdown website dir: getdown-launcher.jar
2321 from getdownLauncher
2322 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2323 into getdownAppBaseDir
2326 // files going into the getdown website dir: getdown-launcher(-local).jar
2328 from getdownLauncher
2329 if (file(getdownLauncher).getName() != getdown_launcher) {
2330 rename(file(getdownLauncher).getName(), getdown_launcher)
2332 into getdownAppBaseDir
2335 // files going into the getdown website dir: ./install dir and files
2336 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2339 from getdownLauncher
2340 from "${getdownAppDir}/${getdown_build_properties}"
2341 if (file(getdownLauncher).getName() != getdown_launcher) {
2342 rename(file(getdownLauncher).getName(), getdown_launcher)
2344 into getdownInstallDir
2347 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2349 from getdownInstallDir
2350 into getdownFilesInstallDir
2354 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2358 from getdownLauncher
2359 from "${getdownAppBaseDir}/${getdown_build_properties}"
2360 from "${getdownAppBaseDir}/${channel_props}"
2361 if (file(getdownLauncher).getName() != getdown_launcher) {
2362 rename(file(getdownLauncher).getName(), getdown_launcher)
2364 into getdownFilesDir
2367 // and ./resource (not all downloaded by getdown)
2369 from getdownResourceDir
2370 into "${getdownFilesDir}/${getdown_resource_dir}"
2375 inputs.dir("${jalviewDir}/${package_dir}")
2377 outputs.dir(getdownAppBaseDir)
2378 outputs.dir(getdownFilesDir)
2382 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2383 task getdownDigestDir(type: JavaExec) {
2385 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2387 def digestDirPropertyName = "DIGESTDIR"
2389 classpath = files(getdownLauncher)
2390 def digestDir = findProperty(digestDirPropertyName)
2391 if (digestDir == null) {
2392 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2396 main = "com.threerings.getdown.tools.Digester"
2400 task getdownDigest(type: JavaExec) {
2401 group = "distribution"
2402 description = "Digest the getdown website folder"
2403 dependsOn getdownWebsite
2405 classpath = files(getdownLauncher)
2407 main = "com.threerings.getdown.tools.Digester"
2408 args getdownAppBaseDir
2409 inputs.dir(getdownAppBaseDir)
2410 outputs.file("${getdownAppBaseDir}/digest2.txt")
2415 group = "distribution"
2416 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2417 dependsOn getdownDigest
2419 if (reportRsyncCommand) {
2420 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2421 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2422 println "LIKELY RSYNC COMMAND:"
2423 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2424 if (RUNRSYNC == "true") {
2426 commandLine "mkdir", "-p", toDir
2429 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2437 task getdownArchiveBuild() {
2438 group = "distribution"
2439 description = "Put files in the archive dir to go on the website"
2441 dependsOn getdownWebsite
2443 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2444 def vDir = "${getdownArchiveDir}/${v}"
2445 getdownFullArchiveDir = "${vDir}/getdown"
2446 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2448 def vAltDir = "alt_${v}"
2449 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2452 // cleanup old "old" dir
2453 delete getdownArchiveDir
2455 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2456 getdownArchiveTxt.getParentFile().mkdirs()
2457 def getdownArchiveTextLines = []
2458 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2462 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2463 into "${getdownFullArchiveDir}/${vAltDir}"
2466 getdownTextLines.each { line ->
2467 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2468 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2469 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2470 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2471 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2472 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2473 // remove the existing resource = resource/ or bin/ lines
2474 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2475 getdownArchiveTextLines += line
2479 // the resource dir -- add these files as resource lines in getdown.txt
2481 from "${archiveImagesDir}"
2482 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2484 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2488 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2490 def vLaunchJvl = file(getdownVersionLaunchJvl)
2491 vLaunchJvl.getParentFile().mkdirs()
2492 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2493 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2494 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2495 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2496 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2497 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2499 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2501 from getdownLauncher
2502 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2503 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2504 from "${getdownAppBaseDir}/${channel_props}"
2505 if (file(getdownLauncher).getName() != getdown_launcher) {
2506 rename(file(getdownLauncher).getName(), getdown_launcher)
2508 into getdownFullArchiveDir
2514 task getdownArchiveDigest(type: JavaExec) {
2515 group = "distribution"
2516 description = "Digest the getdown archive folder"
2518 dependsOn getdownArchiveBuild
2521 classpath = files(getdownLauncher)
2522 args getdownFullArchiveDir
2524 main = "com.threerings.getdown.tools.Digester"
2525 inputs.dir(getdownFullArchiveDir)
2526 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2529 task getdownArchive() {
2530 group = "distribution"
2531 description = "Build the website archive dir with getdown digest"
2533 dependsOn getdownArchiveBuild
2534 dependsOn getdownArchiveDigest
2537 tasks.withType(JavaCompile) {
2538 options.encoding = 'UTF-8'
2544 delete getdownAppBaseDir
2545 delete getdownFilesDir
2546 delete getdownArchiveDir
2552 if (file(install4jHomeDir).exists()) {
2554 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2555 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2556 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2557 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2559 installDir(file(install4jHomeDir))
2561 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2565 task copyInstall4jTemplate {
2566 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2567 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2568 inputs.file(install4jTemplateFile)
2569 inputs.file(install4jFileAssociationsFile)
2570 inputs.property("CHANNEL", { CHANNEL })
2571 outputs.file(install4jConfFile)
2574 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2576 // turn off code signing if no OSX_KEYPASS
2577 if (OSX_KEYPASS == "") {
2578 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2579 codeSigning.'@macEnabled' = "false"
2581 install4jConfigXml.'**'.windows.each { windows ->
2582 windows.'@runPostProcessor' = "false"
2586 // disable install screen for OSX dmg (for 2.11.2.0)
2587 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2588 macosArchive.attributes().remove('executeSetupApp')
2589 macosArchive.attributes().remove('setupAppId')
2592 // turn off checksum creation for LOCAL channel
2593 def e = install4jConfigXml.application[0]
2594 e.'@createChecksums' = string(install4jCheckSums)
2596 // put file association actions where placeholder action is
2597 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2598 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2599 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2600 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2601 def parent = a.parent()
2603 fileAssociationActions.each { faa ->
2606 // don't need to continue in .any loop once replacements have been made
2611 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2612 // NB we're deleting the /other/ one!
2613 // Also remove the examples subdir from non-release versions
2614 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2615 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2616 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2617 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2619 // remove the examples subdir from Full File Set
2620 def files = install4jConfigXml.files[0]
2621 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2622 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2623 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2624 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2625 dirEntry.parent().remove(dirEntry)
2627 install4jConfigXml.'**'.action.any { a ->
2628 if (a.'@customizedId' == customizedIdToDelete) {
2629 def parent = a.parent()
2635 // write install4j file
2636 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2643 delete install4jConfFile
2647 task cleanInstallersDataFiles {
2648 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2649 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2650 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2652 delete installersOutputTxt
2653 delete installersSha256
2654 delete hugoDataJsonFile
2658 task install4jDMGBackgroundImageCopy {
2659 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2660 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2663 from(install4jDMGBackgroundImageDir) {
2664 include(install4jDMGBackgroundImageFile)
2666 into install4jDMGBackgroundImageBuildDir
2671 task install4jDMGBackgroundImageProcess {
2672 dependsOn install4jDMGBackgroundImageCopy
2675 if (backgroundImageText) {
2676 if (convertBinary == null) {
2677 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2679 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2680 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2682 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2684 executable convertBinary
2687 '-font', install4j_background_image_text_font,
2688 '-fill', install4j_background_image_text_colour,
2689 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2690 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2691 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2700 task install4jDMGBackgroundImage {
2701 dependsOn install4jDMGBackgroundImageProcess
2704 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2705 group = "distribution"
2706 description = "Create the install4j installers"
2708 dependsOn copyInstall4jTemplate
2709 dependsOn cleanInstallersDataFiles
2710 dependsOn install4jDMGBackgroundImage
2712 projectFile = install4jConfFile
2714 // create an md5 for the input files to use as version for install4j conf file
2715 def digest = MessageDigest.getInstance("MD5")
2717 (file("${install4jDir}/${install4j_template}").text +
2718 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2719 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2720 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2721 if (filesMd5.length() >= 8) {
2722 filesMd5 = filesMd5.substring(0,8)
2724 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2727 'JALVIEW_NAME': jalview_name,
2728 'JALVIEW_APPLICATION_NAME': applicationName,
2729 'JALVIEW_DIR': "../..",
2730 'OSX_KEYSTORE': OSX_KEYSTORE,
2731 'OSX_APPLEID': OSX_APPLEID,
2732 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2733 'JSIGN_SH': JSIGN_SH,
2734 'JRE_DIR': getdown_app_dir_java,
2735 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2736 'JALVIEW_VERSION': JALVIEW_VERSION,
2737 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2738 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2739 'JAVA_VERSION': JAVA_VERSION,
2740 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2741 'VERSION': JALVIEW_VERSION,
2742 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2743 'BUNDLE_ID': install4jBundleId,
2744 'INTERNAL_ID': install4jInternalId,
2745 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2746 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2747 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2748 'WRAPPER_LINK': getdownWrapperLink,
2749 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2750 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2751 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2752 'INSTALLER_NAME': install4jInstallerName,
2753 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2754 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2755 'GETDOWN_FILES_DIR': getdown_files_dir,
2756 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2757 'GETDOWN_DIST_DIR': getdownAppDistDir,
2758 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2759 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2760 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2761 'BUILD_DIR': install4jBuildDir,
2762 'APPLICATION_CATEGORIES': install4j_application_categories,
2763 'APPLICATION_FOLDER': install4jApplicationFolder,
2764 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2765 'EXECUTABLE_NAME': install4jExecutableName,
2766 'EXTRA_SCHEME': install4jExtraScheme,
2767 'MAC_ICONS_FILE': install4jMacIconsFile,
2768 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2769 'PNG_ICON_FILE': install4jPngIconFile,
2770 'BACKGROUND': install4jBackground,
2775 'windows': 'WINDOWS',
2779 // these are the bundled OS/architecture VMs needed by install4j
2782 [ "mac", "aarch64" ],
2783 [ "windows", "x64" ],
2785 [ "linux", "aarch64" ]
2787 osArch.forEach { os, arch ->
2788 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)
2789 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2790 // otherwise running `gradle installers` generates a non-useful error:
2791 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2792 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)
2795 //println("INSTALL4J VARIABLES:")
2796 //variables.each{k,v->println("${k}=${v}")}
2798 destination = "${jalviewDir}/${install4jBuildDir}"
2799 buildSelected = true
2801 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2803 disableSigning = true
2804 disableNotarization = true
2808 macKeystorePassword = OSX_KEYPASS
2811 if (OSX_ALTOOLPASS) {
2812 appleIdPassword = OSX_ALTOOLPASS
2813 disableNotarization = false
2815 disableNotarization = true
2819 println("Using projectFile "+projectFile)
2820 if (!disableNotarization) { println("Will notarize OSX App DMG") }
2824 inputs.dir(getdownAppBaseDir)
2825 inputs.file(install4jConfFile)
2826 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2827 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2830 def getDataHash(File myFile) {
2831 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2832 return myFile.exists()
2834 "file" : myFile.getName(),
2835 "filesize" : myFile.length(),
2836 "sha256" : hash.toString()
2841 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2843 "channel" : getdownChannelName,
2844 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2845 "git-commit" : "${gitHash} [${gitBranch}]",
2846 "version" : JALVIEW_VERSION
2848 // install4j installer files
2849 if (installersOutputTxt.exists()) {
2851 installersOutputTxt.readLines().each { def line ->
2852 if (line.startsWith("#")) {
2855 line.replaceAll("\n","")
2856 def vals = line.split("\t")
2857 def filename = vals[3]
2858 def filesize = file(filename).length()
2859 filename = filename.replaceAll(/^.*\//, "")
2860 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2861 idHash."${filename}" = vals[0]
2863 if (install4jCheckSums && installersSha256.exists()) {
2864 installersSha256.readLines().each { def line ->
2865 if (line.startsWith("#")) {
2868 line.replaceAll("\n","")
2869 def vals = line.split(/\s+\*?/)
2870 def filename = vals[1]
2871 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
2877 "JAR": shadowJar.archiveFile, // executable JAR
2878 "JVL": getdownVersionLaunchJvl, // version JVL
2879 "SOURCE": sourceDist.archiveFile // source TGZ
2880 ].each { key, value ->
2881 def file = file(value)
2882 if (file.exists()) {
2883 def fileHash = getDataHash(file)
2884 if (fileHash != null) {
2885 hash."${key}" = fileHash;
2889 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
2892 task staticMakeInstallersJsonFile {
2894 def output = findProperty("i4j_output")
2895 def sha256 = findProperty("i4j_sha256")
2896 def json = findProperty("i4j_json")
2897 if (output == null || sha256 == null || json == null) {
2898 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
2900 writeDataJsonFile(file(output), file(sha256), file(json))
2905 dependsOn installerFiles
2911 eclipse().configFile(eclipse_codestyle_file)
2915 task createSourceReleaseProperties(type: WriteProperties) {
2916 group = "distribution"
2917 description = "Create the source RELEASE properties file"
2919 def sourceTarBuildDir = "${buildDir}/sourceTar"
2920 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
2921 outputFile (sourceReleasePropertiesFile)
2924 releaseProps.each{ key, val -> property key, val }
2925 property "git.branch", gitBranch
2926 property "git.hash", gitHash
2929 outputs.file(outputFile)
2932 task sourceDist(type: Tar) {
2933 group "distribution"
2934 description "Create a source .tar.gz file for distribution"
2936 dependsOn createBuildProperties
2937 dependsOn convertMdFiles
2938 dependsOn eclipseAllPreferences
2939 dependsOn createSourceReleaseProperties
2942 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
2943 archiveFileName = outputFileName
2945 compression Compression.GZIP
2960 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
2962 "utils/InstallAnywhere",
2977 "gradle.properties",
2989 ".settings/org.eclipse.buildship.core.prefs",
2990 ".settings/org.eclipse.jdt.core.prefs"
2994 exclude (EXCLUDE_FILES)
2995 include (PROCESS_FILES)
2996 filter(ReplaceTokens,
3000 'Version-Rel': JALVIEW_VERSION,
3001 'Year-Rel': getDate("yyyy")
3006 exclude (EXCLUDE_FILES)
3007 exclude (PROCESS_FILES)
3008 exclude ("appletlib")
3009 exclude ("**/*locales")
3010 exclude ("*locales/**")
3011 exclude ("utils/InstallAnywhere")
3013 exclude (getdown_files_dir)
3014 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3015 //exclude (getdown_website_dir)
3016 //exclude (getdown_archive_dir)
3018 // exluding these as not using jars as modules yet
3019 exclude ("${j11modDir}/**/*.jar")
3022 include(INCLUDE_FILES)
3024 // from (jalviewDir) {
3025 // // explicit includes for stuff that seemed to not get included
3026 // include(fileTree("test/**/*."))
3027 // exclude(EXCLUDE_FILES)
3028 // exclude(PROCESS_FILES)
3031 from(file(buildProperties).getParent()) {
3032 include(file(buildProperties).getName())
3033 rename(file(buildProperties).getName(), "build_properties")
3035 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3039 def sourceTarBuildDir = "${buildDir}/sourceTar"
3040 from(sourceTarBuildDir) {
3041 // this includes the appended RELEASE properties file
3045 task dataInstallersJson {
3047 description "Create the installers-VERSION.json data file for installer files created"
3049 mustRunAfter installers
3050 mustRunAfter shadowJar
3051 mustRunAfter sourceDist
3052 mustRunAfter getdownArchive
3054 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3055 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3057 if (installersOutputTxt.exists()) {
3058 inputs.file(installersOutputTxt)
3060 if (install4jCheckSums && installersSha256.exists()) {
3061 inputs.file(installersSha256)
3064 shadowJar.archiveFile, // executable JAR
3065 getdownVersionLaunchJvl, // version JVL
3066 sourceDist.archiveFile // source TGZ
3067 ].each { fileName ->
3068 if (file(fileName).exists()) {
3069 inputs.file(fileName)
3073 outputs.file(hugoDataJsonFile)
3076 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3082 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3085 dependsOn pubhtmlhelp
3087 inputs.dir("${helpBuildDir}/${help_dir}")
3088 outputs.dir("${buildDir}/distributions/${help_dir}")
3092 task j2sSetHeadlessBuild {
3099 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3101 description "Enable the alternative J2S Config file for headless build"
3103 outputFile = jalviewjsJ2sSettingsFileName
3104 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3105 def j2sProps = new Properties()
3106 if (j2sPropsFile.exists()) {
3108 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3109 j2sProps.load(j2sPropsFileFIS)
3110 j2sPropsFileFIS.close()
3112 j2sProps.each { prop, val ->
3115 } catch (Exception e) {
3116 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3120 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3121 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3126 task jalviewjsSetEclipseWorkspace {
3127 def propKey = "jalviewjs_eclipse_workspace"
3129 if (project.hasProperty(propKey)) {
3130 propVal = project.getProperty(propKey)
3131 if (propVal.startsWith("~/")) {
3132 propVal = System.getProperty("user.home") + propVal.substring(1)
3135 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3136 def propsFile = file(propsFileName)
3137 def eclipseWsDir = propVal
3138 def props = new Properties()
3140 def writeProps = true
3141 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3142 def ins = new FileInputStream(propsFileName)
3145 if (props.getProperty(propKey, null) != null) {
3146 eclipseWsDir = props.getProperty(propKey)
3151 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3152 def tempDir = File.createTempDir()
3153 eclipseWsDir = tempDir.getAbsolutePath()
3156 eclipseWorkspace = file(eclipseWsDir)
3159 // do not run a headless transpile when we claim to be in Eclipse
3161 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3162 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3164 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3168 props.setProperty(propKey, eclipseWsDir)
3169 propsFile.parentFile.mkdirs()
3170 def bytes = new ByteArrayOutputStream()
3171 props.store(bytes, null)
3172 def propertiesString = bytes.toString()
3173 propsFile.text = propertiesString
3179 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3182 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3183 outputs.file(propsFileName)
3184 outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
3188 task jalviewjsEclipsePaths {
3191 def eclipseRoot = jalviewjs_eclipse_root
3192 if (eclipseRoot.startsWith("~/")) {
3193 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3195 if (OperatingSystem.current().isMacOsX()) {
3196 eclipseRoot += "/Eclipse.app"
3197 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3198 eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3199 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3200 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3201 eclipseRoot += "/eclipse"
3203 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3204 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3205 } else { // linux or unix
3206 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3207 eclipseRoot += "/eclipse"
3208 println("eclipseDir exists")
3210 eclipseBinary = "${eclipseRoot}/eclipse"
3211 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3214 eclipseVersion = "4.13" // default
3215 def assumedVersion = true
3216 if (file(eclipseProduct).exists()) {
3217 def fis = new FileInputStream(eclipseProduct)
3218 def props = new Properties()
3220 eclipseVersion = props.getProperty("version")
3222 assumedVersion = false
3225 def propKey = "eclipse_debug"
3226 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3229 // do not run a headless transpile when we claim to be in Eclipse
3231 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3232 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3234 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3237 if (!assumedVersion) {
3238 println("ECLIPSE VERSION=${eclipseVersion}")
3244 task printProperties {
3246 description "Output to console all System.properties"
3248 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3254 dependsOn eclipseProject
3255 dependsOn eclipseClasspath
3256 dependsOn eclipseJdt
3260 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3261 task jalviewjsEclipseCopyDropins(type: Copy) {
3262 dependsOn jalviewjsEclipsePaths
3264 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3265 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3266 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3273 // this eclipse -clean doesn't actually work
3274 task jalviewjsCleanEclipse(type: Exec) {
3275 dependsOn eclipseSetup
3276 dependsOn jalviewjsEclipsePaths
3277 dependsOn jalviewjsEclipseCopyDropins
3279 executable(eclipseBinary)
3280 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3286 def inputString = """exit
3289 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3290 standardInput = inputByteStream
3293 /* not really working yet
3294 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3298 task jalviewjsTransferUnzipSwingJs {
3299 def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3303 from zipTree(file_zip)
3304 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3308 inputs.file file_zip
3309 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3313 task jalviewjsTransferUnzipLib {
3314 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
3317 zipFiles.each { file_zip ->
3319 from zipTree(file_zip)
3320 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3325 inputs.files zipFiles
3326 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3330 task jalviewjsTransferUnzipAllLibs {
3331 dependsOn jalviewjsTransferUnzipSwingJs
3332 dependsOn jalviewjsTransferUnzipLib
3336 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3338 description "Create the alternative j2s file from the j2s.* properties"
3340 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3341 def siteDirProperty = "j2s.site.directory"
3342 def setSiteDir = false
3343 jalviewjsJ2sProps.each { prop, val ->
3345 if (prop == siteDirProperty) {
3346 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3347 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3353 if (!setSiteDir) { // default site location, don't override specifically set property
3354 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3357 outputFile = jalviewjsJ2sAltSettingsFileName
3360 inputs.properties(jalviewjsJ2sProps)
3361 outputs.file(jalviewjsJ2sAltSettingsFileName)
3366 task jalviewjsEclipseSetup {
3367 dependsOn jalviewjsEclipseCopyDropins
3368 dependsOn jalviewjsSetEclipseWorkspace
3369 dependsOn jalviewjsCreateJ2sSettings
3373 task jalviewjsSyncAllLibs (type: Sync) {
3374 dependsOn jalviewjsTransferUnzipAllLibs
3375 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3376 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3377 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3381 def outputFiles = []
3382 rename { filename ->
3383 outputFiles += "${outputDir}/${filename}"
3390 // should this be exclude really ?
3391 duplicatesStrategy "INCLUDE"
3393 outputs.files outputFiles
3394 inputs.files inputFiles
3398 task jalviewjsSyncResources (type: Sync) {
3399 dependsOn buildResources
3401 def inputFiles = fileTree(dir: resourcesBuildDir)
3402 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3406 def outputFiles = []
3407 rename { filename ->
3408 outputFiles += "${outputDir}/${filename}"
3414 outputs.files outputFiles
3415 inputs.files inputFiles
3419 task jalviewjsSyncSiteResources (type: Sync) {
3420 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3421 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3425 def outputFiles = []
3426 rename { filename ->
3427 outputFiles += "${outputDir}/${filename}"
3433 outputs.files outputFiles
3434 inputs.files inputFiles
3438 task jalviewjsSyncBuildProperties (type: Sync) {
3439 dependsOn createBuildProperties
3440 def inputFiles = [file(buildProperties)]
3441 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3445 def outputFiles = []
3446 rename { filename ->
3447 outputFiles += "${outputDir}/${filename}"
3453 outputs.files outputFiles
3454 inputs.files inputFiles
3458 task jalviewjsProjectImport(type: Exec) {
3459 dependsOn eclipseSetup
3460 dependsOn jalviewjsEclipsePaths
3461 dependsOn jalviewjsEclipseSetup
3464 // do not run a headless import when we claim to be in Eclipse
3466 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3467 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3469 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3473 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3474 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3475 executable(eclipseBinary)
3476 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3480 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3482 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3483 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3486 inputs.file("${jalviewDir}/.project")
3487 outputs.upToDateWhen {
3488 file(projdir).exists()
3493 task jalviewjsTranspile(type: Exec) {
3494 dependsOn jalviewjsEclipseSetup
3495 dependsOn jalviewjsProjectImport
3496 dependsOn jalviewjsEclipsePaths
3498 dependsOn jalviewjsEnableAltFileProperty
3502 // do not run a headless transpile when we claim to be in Eclipse
3504 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3505 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3507 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3511 executable(eclipseBinary)
3512 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3516 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3518 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3519 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3525 stdout = new ByteArrayOutputStream()
3526 stderr = new ByteArrayOutputStream()
3528 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3529 def logOutFile = file(logOutFileName)
3530 logOutFile.createNewFile()
3531 logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3532 BINARY: ${eclipseBinary}
3533 VERSION: ${eclipseVersion}
3534 WORKSPACE: ${eclipseWorkspace}
3535 DEBUG: ${eclipseDebug}
3538 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3539 // combine stdout and stderr
3540 def logErrFOS = logOutFOS
3542 if (jalviewjs_j2s_to_console.equals("true")) {
3543 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3544 new org.apache.tools.ant.util.TeeOutputStream(
3548 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3549 new org.apache.tools.ant.util.TeeOutputStream(
3554 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3557 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3564 if (stdout.toString().contains("Error processing ")) {
3565 // j2s did not complete transpile
3566 //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3567 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3568 println("IGNORING TRANSPILE ERRORS")
3569 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3571 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3576 inputs.dir("${jalviewDir}/${sourceDir}")
3577 outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3578 outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3582 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3584 def stdout = new ByteArrayOutputStream()
3585 def stderr = new ByteArrayOutputStream()
3587 def coreFile = file(jsfile)
3589 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3591 logOutFile.createNewFile()
3592 logOutFile.append(msg+"\n")
3594 def coreTop = file(prefixFile)
3595 def coreBottom = file(suffixFile)
3596 coreFile.getParentFile().mkdirs()
3597 coreFile.createNewFile()
3598 coreFile.write( coreTop.getText("UTF-8") )
3602 def t = f.getText("UTF-8")
3603 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3604 coreFile.append( t )
3606 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3608 logOutFile.append(msg+"\n")
3611 coreFile.append( coreBottom.getText("UTF-8") )
3613 msg = "Generating ${zjsfile}"
3615 logOutFile.append(msg+"\n")
3616 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3617 def logErrFOS = logOutFOS
3620 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3621 main = "com.google.javascript.jscomp.CommandLineRunner"
3622 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3623 args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3626 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3628 logOutFile.append(msg+"\n")
3630 if (logOutConsole) {
3631 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3632 new org.apache.tools.ant.util.TeeOutputStream(
3636 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3637 new org.apache.tools.ant.util.TeeOutputStream(
3642 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3645 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3652 logOutFile.append(msg+"\n")
3656 task jalviewjsBuildAllCores {
3658 description "Build the core js lib closures listed in the classlists dir"
3659 dependsOn jalviewjsTranspile
3660 dependsOn jalviewjsTransferUnzipSwingJs
3662 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3663 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3664 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3665 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3666 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3667 def prefixFile = "${jsDir}/core/coretop2.js"
3668 def suffixFile = "${jsDir}/core/corebottom2.js"
3670 inputs.file prefixFile
3671 inputs.file suffixFile
3673 def classlistFiles = []
3674 // add the classlists found int the jalviewjs_classlists_dir
3675 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3677 def name = file.getName() - ".txt"
3684 // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3685 //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3686 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3688 jalviewjsCoreClasslists = []
3690 classlistFiles.each {
3693 def file = hash['file']
3694 if (! file.exists()) {
3695 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3696 return false // this is a "continue" in groovy .each closure
3698 def name = hash['name']
3700 name = file.getName() - ".txt"
3708 def list = fileTree(dir: j2sDir, includes: filelist)
3710 def jsfile = "${outputDir}/core${name}.js"
3711 def zjsfile = "${outputDir}/core${name}.z.js"
3713 jalviewjsCoreClasslists += [
3722 outputs.file(jsfile)
3723 outputs.file(zjsfile)
3726 // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3727 def stevesoftClasslistName = "_stevesoft"
3728 def stevesoftClasslist = [
3729 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3730 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3731 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3732 'name': stevesoftClasslistName
3734 jalviewjsCoreClasslists += stevesoftClasslist
3735 inputs.files(stevesoftClasslist['list'])
3736 outputs.file(stevesoftClasslist['jsfile'])
3737 outputs.file(stevesoftClasslist['zjsfile'])
3740 def allClasslistName = "_all"
3741 def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3742 allJsFiles += fileTree(
3746 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3747 "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3748 "**/org/jmol/export/JSExporter.js"
3751 allJsFiles += fileTree(
3755 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3756 "**/sun/misc/Unsafe.js",
3757 "**/swingjs/jquery/jquery-editable-select.js",
3758 "**/swingjs/jquery/j2sComboBox.js",
3759 "**/sun/misc/FloatingDecimal.js"
3762 def allClasslist = [
3763 'jsfile': "${outputDir}/core${allClasslistName}.js",
3764 'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3766 'name': allClasslistName
3768 // not including this version of "all" core at the moment
3769 //jalviewjsCoreClasslists += allClasslist
3770 inputs.files(allClasslist['list'])
3771 outputs.file(allClasslist['jsfile'])
3772 outputs.file(allClasslist['zjsfile'])
3775 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
3776 logOutFile.getParentFile().mkdirs()
3777 logOutFile.createNewFile()
3778 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
3780 jalviewjsCoreClasslists.each {
3781 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
3788 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
3791 into file(outputFile).getParentFile()
3792 rename { filename ->
3793 if (filename.equals(inputFile.getName())) {
3794 return file(outputFile).getName()
3798 filter(ReplaceTokens,
3802 'MAIN': '"'+main_class+'"',
3804 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
3805 'COREKEY': jalviewjs_core_key,
3806 'CORENAME': coreName
3813 task jalviewjsPublishCoreTemplates {
3814 dependsOn jalviewjsBuildAllCores
3815 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
3816 def inputFile = file(inputFileName)
3817 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3819 def outputFiles = []
3820 jalviewjsCoreClasslists.each { cl ->
3821 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
3822 cl['outputfile'] = outputFile
3823 outputFiles += outputFile
3827 jalviewjsCoreClasslists.each { cl ->
3828 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
3831 inputs.file(inputFile)
3832 outputs.files(outputFiles)
3836 task jalviewjsSyncCore (type: Sync) {
3837 dependsOn jalviewjsBuildAllCores
3838 dependsOn jalviewjsPublishCoreTemplates
3839 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
3840 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3844 def outputFiles = []
3845 rename { filename ->
3846 outputFiles += "${outputDir}/${filename}"
3852 outputs.files outputFiles
3853 inputs.files inputFiles
3857 // this Copy version of TransferSiteJs will delete anything else in the target dir
3858 task jalviewjsCopyTransferSiteJs(type: Copy) {
3859 dependsOn jalviewjsTranspile
3860 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3861 into "${jalviewDir}/${jalviewjsSiteDir}"
3865 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
3866 task jalviewjsSyncTransferSiteJs(type: Sync) {
3867 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3869 into "${jalviewDir}/${jalviewjsSiteDir}"
3876 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
3877 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
3878 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
3879 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
3881 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
3882 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
3883 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
3884 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
3887 task jalviewjsPrepareSite {
3889 description "Prepares the website folder including unzipping files and copying resources"
3890 dependsOn jalviewjsSyncAllLibs
3891 dependsOn jalviewjsSyncResources
3892 dependsOn jalviewjsSyncSiteResources
3893 dependsOn jalviewjsSyncBuildProperties
3894 dependsOn jalviewjsSyncCore
3898 task jalviewjsBuildSite {
3900 description "Builds the whole website including transpiled code"
3901 dependsOn jalviewjsCopyTransferSiteJs
3902 dependsOn jalviewjsPrepareSite
3906 task cleanJalviewjsTransferSite {
3908 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3909 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3910 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3911 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3916 task cleanJalviewjsSite {
3917 dependsOn cleanJalviewjsTransferSite
3919 delete "${jalviewDir}/${jalviewjsSiteDir}"
3924 task jalviewjsSiteTar(type: Tar) {
3926 description "Creates a tar.gz file for the website"
3927 dependsOn jalviewjsBuildSite
3928 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
3929 archiveFileName = outputFilename
3931 compression Compression.GZIP
3933 from "${jalviewDir}/${jalviewjsSiteDir}"
3934 into jalviewjs_site_dir // this is inside the tar file
3936 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
3940 task jalviewjsServer {
3942 def filename = "jalviewjsTest.html"
3943 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
3944 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
3949 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
3950 factory = f.newInstance()
3951 } catch (ClassNotFoundException e) {
3952 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
3954 def port = Integer.valueOf(jalviewjs_server_port)
3959 while(port < start+1000 && !running) {
3961 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
3962 jalviewjsServer = factory.start(doc_root, port)
3964 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
3965 println("SERVER STARTED with document root ${doc_root}.")
3966 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
3967 println("For debug: "+url+"?j2sdebug")
3968 println("For verbose: "+url+"?j2sverbose")
3969 } catch (Exception e) {
3974 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
3975 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
3976 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
3978 jalviewjsCoreClasslists.each { cl ->
3979 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
3981 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
3983 println("For core ${cl.name}: "+urlcore)
3986 file(htmlFile).text = htmlText
3989 outputs.file(htmlFile)
3990 outputs.upToDateWhen({false})
3994 task cleanJalviewjsAll {
3996 description "Delete all configuration and build artifacts to do with JalviewJS build"
3997 dependsOn cleanJalviewjsSite
3998 dependsOn jalviewjsEclipsePaths
4001 delete "${jalviewDir}/${jalviewjsBuildDir}"
4002 delete "${jalviewDir}/${eclipse_bin_dir}"
4003 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4004 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4006 delete jalviewjsJ2sAltSettingsFileName
4009 outputs.upToDateWhen( { false } )
4013 task jalviewjsIDE_checkJ2sPlugin {
4014 group "00 JalviewJS in Eclipse"
4015 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4018 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4019 def j2sPluginFile = file(j2sPlugin)
4020 def eclipseHome = System.properties["eclipse.home.location"]
4021 if (eclipseHome == null || ! IN_ECLIPSE) {
4022 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4024 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4025 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4026 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4027 eclipseJ2sPluginDirs += altPluginsDir
4029 def foundPlugin = false
4030 def j2sPluginFileName = j2sPluginFile.getName()
4031 def eclipseJ2sPlugin
4032 def eclipseJ2sPluginFile
4033 eclipseJ2sPluginDirs.any { dir ->
4034 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4035 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4036 if (eclipseJ2sPluginFile.exists()) {
4042 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4043 System.err.println(msg)
4044 throw new StopExecutionException(msg)
4047 def digest = MessageDigest.getInstance("MD5")
4049 digest.update(j2sPluginFile.text.bytes)
4050 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4052 digest.update(eclipseJ2sPluginFile.text.bytes)
4053 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4055 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4056 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4057 System.err.println(msg)
4058 throw new StopExecutionException(msg)
4060 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4066 task jalviewjsIDE_copyJ2sPlugin {
4067 group "00 JalviewJS in Eclipse"
4068 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4071 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4072 def j2sPluginFile = file(j2sPlugin)
4073 def eclipseHome = System.properties["eclipse.home.location"]
4074 if (eclipseHome == null || ! IN_ECLIPSE) {
4075 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4077 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4078 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4079 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4080 System.err.println(msg)
4083 eclipseJ2sPluginFile.getParentFile().mkdirs()
4084 into eclipseJ2sPluginFile.getParent()
4090 task jalviewjsIDE_j2sFile {
4091 group "00 JalviewJS in Eclipse"
4092 description "Creates the .j2s file"
4093 dependsOn jalviewjsCreateJ2sSettings
4097 task jalviewjsIDE_SyncCore {
4098 group "00 JalviewJS in Eclipse"
4099 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4100 dependsOn jalviewjsSyncCore
4104 task jalviewjsIDE_SyncSiteAll {
4105 dependsOn jalviewjsSyncAllLibs
4106 dependsOn jalviewjsSyncResources
4107 dependsOn jalviewjsSyncSiteResources
4108 dependsOn jalviewjsSyncBuildProperties
4112 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4115 task jalviewjsIDE_PrepareSite {
4116 group "00 JalviewJS in Eclipse"
4117 description "Sync libs and resources to site dir, but not closure cores"
4119 dependsOn jalviewjsIDE_SyncSiteAll
4120 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4124 task jalviewjsIDE_AssembleSite {
4125 group "00 JalviewJS in Eclipse"
4126 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4127 dependsOn jalviewjsPrepareSite
4131 task jalviewjsIDE_SiteClean {
4132 group "00 JalviewJS in Eclipse"
4133 description "Deletes the Eclipse transpiled site"
4134 dependsOn cleanJalviewjsSite
4138 task jalviewjsIDE_Server {
4139 group "00 JalviewJS in Eclipse"
4140 description "Starts a webserver on localhost to test the website"
4141 dependsOn jalviewjsServer
4145 // buildship runs this at import or gradle refresh
4146 task eclipseSynchronizationTask {
4147 //dependsOn eclipseSetup
4148 dependsOn createBuildProperties
4150 dependsOn jalviewjsIDE_j2sFile
4151 dependsOn jalviewjsIDE_checkJ2sPlugin
4152 dependsOn jalviewjsIDE_PrepareSite
4157 // buildship runs this at build time or project refresh
4158 task eclipseAutoBuildTask {
4159 //dependsOn jalviewjsIDE_checkJ2sPlugin
4160 //dependsOn jalviewjsIDE_PrepareSite
4166 description "Build the site"
4167 dependsOn jalviewjsBuildSite