1 /* Convention for properties. Read from gradle.properties, use lower_case_underlines for property names.
2 * For properties set within build.gradle, use camelCaseNoSpace.
4 import org.apache.tools.ant.filters.ReplaceTokens
5 import org.gradle.internal.os.OperatingSystem
6 import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject
7 import org.gradle.api.internal.PropertiesTransformer
8 import org.gradle.util.ConfigureUtil
9 import org.gradle.plugins.ide.eclipse.model.Output
10 import org.gradle.plugins.ide.eclipse.model.Library
11 import java.security.MessageDigest
12 import java.util.regex.Matcher
13 import java.util.concurrent.Executors
14 import java.util.concurrent.Future
15 import java.util.concurrent.ScheduledExecutorService
16 import java.util.concurrent.TimeUnit
17 import java.nio.file.Path
18 import groovy.transform.ExternalizeMethods
19 import groovy.util.XmlParser
20 import groovy.xml.XmlUtil
21 import groovy.json.JsonBuilder
22 import com.vladsch.flexmark.util.ast.Node
23 import com.vladsch.flexmark.html.HtmlRenderer
24 import com.vladsch.flexmark.parser.Parser
25 import com.vladsch.flexmark.util.data.MutableDataSet
26 import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
27 import com.vladsch.flexmark.ext.tables.TablesExtension
28 import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
29 import com.vladsch.flexmark.ext.autolink.AutolinkExtension
30 import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
31 import com.vladsch.flexmark.ext.toc.TocExtension
32 import com.google.common.hash.HashCode
33 import com.google.common.hash.Hashing
34 import com.google.common.io.Files
35 import org.jsoup.Jsoup
36 import org.jsoup.nodes.Element
44 classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
45 classpath "org.jsoup:jsoup:1.14.3"
46 classpath "com.eowise:gradle-imagemagick:0.5.1"
55 id "com.diffplug.gradle.spotless" version "3.28.0"
56 id 'com.github.johnrengelman.shadow' version '6.0.0'
57 id 'com.install4j.gradle' version '10.0.3'
58 id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree
59 id 'com.palantir.git-version' version '0.13.0' apply false
70 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
71 def string(Object o) {
72 return o == null ? "" : o.toString()
75 def Properties readPropertiesFile(String propsFileName) {
77 def propsFile = file(propsFileName)
78 if (propsFile != null && propsFile.exists()) {
79 println("Using properties from file '${propsFileName}'")
82 def localPropsFIS = new FileInputStream(propsFile)
85 } catch (Exception e) {
86 println("Exception reading properties file '${propsFileName}'")
93 def overrideProperties(String propsFileName, boolean output = false) {
94 if (propsFileName == null) {
97 def propsFile = file(propsFileName)
98 if (propsFile != null && propsFile.exists()) {
99 println("Using properties from file '${propsFileName}'")
100 def p = readPropertiesFile(propsFileName)
103 if (project.hasProperty(key)) {
104 oldval = project.findProperty(key)
105 project.setProperty(key, val)
107 println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
110 ext.setProperty(key, val)
112 println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
120 jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
121 jalviewDirRelativePath = jalviewDir
124 getdownChannelName = CHANNEL.toLowerCase()
125 // default to "default". Currently only has different cosmetics for "develop", "release", "default"
126 propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
127 channelDirName = propertiesChannelName
128 // Import channel_properties
129 if (getdownChannelName.startsWith("develop-")) {
130 channelDirName = "develop-SUFFIX"
132 channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
133 channelGradleProperties = string("${channelDir}/channel_gradle.properties")
134 channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
135 localProperties = "local.properties"
136 localEclipseProperties = "local_eclipse.properties"
137 overrideProperties(channelGradleProperties, false)
138 // local build environment properties
139 // can be "projectDir/local.properties"
140 overrideProperties("${projectDir}/${localProperties}", true)
141 // or "../projectDir_local.properties"
142 overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_${localProperties}", true)
145 // Import releaseProps from the RELEASE file
146 // or a file specified via JALVIEW_RELEASE_FILE if defined
147 // Expect jalview.version and target release branch in jalview.release
148 releaseProps = new Properties();
149 def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
150 def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
152 (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream {
153 releaseProps.load(it)
155 } catch (Exception fileLoadError) {
156 throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
159 // Set JALVIEW_VERSION if it is not already set
160 if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
161 JALVIEW_VERSION = releaseProps.get("jalview.version")
163 println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
165 // this property set when running Eclipse headlessly
166 j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
167 // this property set by Eclipse
168 eclipseApplicationProperty = string("eclipse.application")
169 // CHECK IF RUNNING FROM WITHIN ECLIPSE
170 def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
171 IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
172 // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
173 if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
174 println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
178 println("WITHIN ECLIPSE IDE")
180 println("HEADLESS BUILD")
183 J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
185 println("J2S ENABLED")
188 System.properties.sort { it.key }.each {
189 key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
192 if (false && IN_ECLIPSE) {
193 jalviewDir = jalviewDirAbsolutePath
198 buildDate = new Date().format("yyyyMMdd")
201 bareSourceDir = string(source_dir)
202 sourceDir = string("${jalviewDir}/${bareSourceDir}")
203 resourceDir = string("${jalviewDir}/${resource_dir}")
204 bareTestSourceDir = string(test_source_dir)
205 testDir = string("${jalviewDir}/${bareTestSourceDir}")
207 classesDir = string("${jalviewDir}/${classes_dir}")
210 useClover = clover.equals("true")
211 cloverBuildDir = "${buildDir}/clover"
212 cloverInstrDir = file("${cloverBuildDir}/clover-instr")
213 cloverClassesDir = file("${cloverBuildDir}/clover-classes")
214 cloverReportDir = file("${buildDir}/reports/clover")
215 cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
216 cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
217 //cloverTestClassesDir = cloverClassesDir
218 cloverDb = string("${cloverBuildDir}/clover.db")
220 testSourceDir = useClover ? cloverTestInstrDir : testDir
221 testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
224 backgroundImageText = BACKGROUNDIMAGETEXT
225 getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
226 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
227 getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
228 getdownFullArchiveDir = null
229 getdownTextLines = []
230 getdownLaunchJvl = null
231 getdownVersionLaunchJvl = null
233 buildProperties = null
235 // the following values might be overridden by the CHANNEL switch
236 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
237 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
238 getdownArchiveAppBase = getdown_archive_base
239 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
240 getdownAppDistDir = getdown_app_dir_alt
241 getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
242 getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown")
243 getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
244 reportRsyncCommand = false
245 jvlChannelName = CHANNEL.toLowerCase()
246 install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
247 install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
248 install4jDMGBackgroundImageDir = "${install4j_images_dir}"
249 install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j"
250 install4jDMGBackgroundImageFile = "${install4j_dmg_background}"
251 install4jInstallerName = "${jalview_name} Non-Release Installer"
252 install4jExecutableName = install4j_executable_name
253 install4jExtraScheme = "jalviewx"
254 install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
255 install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
256 install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
257 install4jBackground = string("${install4j_images_dir}/${install4j_background}")
258 install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
259 install4jCheckSums = true
261 applicationName = "${jalview_name}"
265 // TODO: get bamboo build artifact URL for getdown artifacts
266 getdown_channel_base = bamboo_channelbase
267 getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
268 getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
269 jvlChannelName += "_${getdownChannelName}"
270 // automatically add the test group Not-bamboo for exclusion
271 if ("".equals(testng_excluded_groups)) {
272 testng_excluded_groups = "Not-bamboo"
274 install4jExtraScheme = "jalviewb"
275 backgroundImageText = true
278 case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
279 getdownAppDistDir = getdown_app_dir_release
280 getdownSetAppBaseProperty = true
281 reportRsyncCommand = true
283 install4jInstallerName = "${jalview_name} Installer"
287 getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
288 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
289 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
290 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
291 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
293 package_dir = string("${ARCHIVEDIR}/${package_dir}")
294 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
297 reportRsyncCommand = true
298 install4jExtraScheme = "jalviewa"
302 getdownChannelName = string("archive/${JALVIEW_VERSION}")
303 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
304 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
305 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
306 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']")
308 package_dir = string("${ARCHIVEDIR}/${package_dir}")
309 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
312 reportRsyncCommand = true
313 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
314 install4jSuffix = "Archive"
315 install4jExtraScheme = "jalviewa"
318 case ~/^DEVELOP-([\.\-\w]*)$/:
319 def suffix = Matcher.lastMatcher[0][1]
320 reportRsyncCommand = true
321 getdownSetAppBaseProperty = true
322 JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}"
323 install4jSuffix = "Develop ${suffix}"
324 install4jExtraScheme = "jalviewd"
325 install4jInstallerName = "${jalview_name} Develop ${suffix} Installer"
326 getdownChannelName = string("develop-${suffix}")
327 getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}")
328 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
329 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
330 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
331 channelSuffix = string(suffix)
332 backgroundImageText = true
336 reportRsyncCommand = true
337 getdownSetAppBaseProperty = true
338 // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
339 JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
341 install4jSuffix = "Develop"
342 install4jExtraScheme = "jalviewd"
343 install4jInstallerName = "${jalview_name} Develop Installer"
344 backgroundImageText = true
348 reportRsyncCommand = true
349 getdownSetAppBaseProperty = true
350 // Don't ignore transpile errors for release build
351 if (jalviewjs_ignore_transpile_errors.equals("true")) {
352 jalviewjs_ignore_transpile_errors = "false"
353 println("Setting jalviewjs_ignore_transpile_errors to 'false'")
355 JALVIEW_VERSION = JALVIEW_VERSION+"-test"
356 install4jSuffix = "Test"
357 install4jExtraScheme = "jalviewt"
358 install4jInstallerName = "${jalview_name} Test Installer"
359 backgroundImageText = true
362 case ~/^SCRATCH(|-[-\w]*)$/:
363 getdownChannelName = CHANNEL
364 JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
366 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
367 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
368 reportRsyncCommand = true
369 install4jSuffix = "Scratch"
373 if (!file("${LOCALDIR}").exists()) {
374 throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
376 getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
377 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
379 JALVIEW_VERSION = "TEST"
380 install4jSuffix = "Test-Local"
381 install4jExtraScheme = "jalviewt"
382 install4jInstallerName = "${jalview_name} Test Installer"
383 backgroundImageText = true
386 case [ "LOCAL", "JALVIEWJS" ]:
387 JALVIEW_VERSION = "TEST"
388 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
389 getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
390 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
391 install4jExtraScheme = "jalviewl"
392 install4jCheckSums = false
395 default: // something wrong specified
396 throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
400 JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
401 hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
402 hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
403 // override getdownAppBase if requested
404 if (findProperty("getdown_appbase_override") != null) {
405 // revert to LOCAL if empty string
406 if (string(getdown_appbase_override) == "") {
407 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
408 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
409 } else if (string(getdown_appbase_override).startsWith("file://")) {
410 getdownAppBase = string(getdown_appbase_override)
411 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
413 getdownAppBase = string(getdown_appbase_override)
415 println("Overriding getdown appbase with '${getdownAppBase}'")
417 // sanitise file name for jalview launcher file for this channel
418 jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
419 // install4j application and folder names
420 if (install4jSuffix == "") {
421 install4jBundleId = "${install4j_bundle_id}"
422 install4jWinApplicationId = install4j_release_win_application_id
424 applicationName = "${jalview_name} ${install4jSuffix}"
425 install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
426 // add int hash of install4jSuffix to the last part of the application_id
427 def id = install4j_release_win_application_id
428 def idsplitreverse = id.split("-").reverse()
429 idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
430 install4jWinApplicationId = idsplitreverse.reverse().join("-")
432 // sanitise folder and id names
433 // install4jApplicationFolder = e.g. "Jalview Build"
434 install4jApplicationFolder = applicationName
435 .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
436 .replaceAll("_+", "_") // collapse __
437 install4jInternalId = applicationName
439 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
440 .replaceAll("_+", "") // collapse __
441 //.replaceAll("_*-_*", "-") // collapse _-_
442 install4jUnixApplicationFolder = applicationName
444 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
445 .replaceAll("_+", "_") // collapse __
446 .replaceAll("_*-_*", "-") // collapse _-_
449 getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
450 getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
451 //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
452 getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
453 getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
454 getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
455 getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
456 /* compile without modules -- using classpath libraries
457 modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
458 modules_runtimeClasspath = modules_compileClasspath
464 apply plugin: "com.palantir.git-version"
465 def details = versionDetails()
466 gitHash = details.gitHash
467 gitBranch = details.branchName
468 } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
469 println("Not in a git repository. Using git values from RELEASE properties file.")
470 gitHash = releaseProps.getProperty("git.hash")
471 gitBranch = releaseProps.getProperty("git.branch")
472 } catch(java.lang.RuntimeException e1) {
473 throw new GradleException("Error with git-version plugin. Directory '.git' exists but versionDetails() cannot be found.")
476 println("Using a ${CHANNEL} profile.")
478 additional_compiler_args = []
479 // configure classpath/args for j8/j11 compilation
480 if (JAVA_VERSION.equals("1.8")) {
481 JAVA_INTEGER_VERSION = string("8")
484 libDistDir = j8libDir
485 compile_source_compatibility = 1.8
486 compile_target_compatibility = 1.8
487 // these are getdown.txt properties defined dependent on the JAVA_VERSION
488 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
489 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
490 // this property is assigned below and expanded to multiple lines in the getdown task
491 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
492 // this property is for the Java library used in eclipse
493 eclipseJavaRuntimeName = string("JavaSE-1.8")
494 } else if (JAVA_VERSION.equals("11")) {
495 JAVA_INTEGER_VERSION = string("11")
497 libDistDir = j11libDir
498 compile_source_compatibility = 11
499 compile_target_compatibility = 11
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-11")
504 /* compile without modules -- using classpath libraries
505 additional_compiler_args += [
506 '--module-path', modules_compileClasspath.asPath,
507 '--add-modules', j11modules
510 } else if (JAVA_VERSION.equals("17")) {
511 JAVA_INTEGER_VERSION = string("17")
513 libDistDir = j17libDir
514 compile_source_compatibility = 17
515 compile_target_compatibility = 17
516 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
517 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
518 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
519 eclipseJavaRuntimeName = string("JavaSE-17")
520 /* compile without modules -- using classpath libraries
521 additional_compiler_args += [
522 '--module-path', modules_compileClasspath.asPath,
523 '--add-modules', j11modules
527 throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
532 JAVA_MIN_VERSION = JAVA_VERSION
533 JAVA_MAX_VERSION = JAVA_VERSION
534 jreInstallsDir = string(jre_installs_dir)
535 if (jreInstallsDir.startsWith("~/")) {
536 jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
538 install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
539 install4jConfFileName = string("jalview-install4j-conf.install4j")
540 install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
541 install4jHomeDir = install4j_home_dir
542 if (install4jHomeDir.startsWith("~/")) {
543 install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
546 resourceBuildDir = string("${buildDir}/resources")
547 resourcesBuildDir = string("${resourceBuildDir}/resources_build")
548 helpBuildDir = string("${resourceBuildDir}/help_build")
549 docBuildDir = string("${resourceBuildDir}/doc_build")
551 if (buildProperties == null) {
552 buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
554 buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
555 helpParentDir = string("${jalviewDir}/${help_parent_dir}")
556 helpSourceDir = string("${helpParentDir}/${help_dir}")
557 helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
560 convertBinaryExpectedLocation = imagemagick_convert
561 if (convertBinaryExpectedLocation.startsWith("~/")) {
562 convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1)
564 if (file(convertBinaryExpectedLocation).exists()) {
565 convertBinary = convertBinaryExpectedLocation
568 relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
569 jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
570 jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
572 jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
574 jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/sitejs")
576 jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/lib")
577 jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/swingjs")
578 jalviewjsTransferSiteMergeDir = string("${jalviewjsBuildDir}/merge/${jalviewjs_site_dir}")
579 jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}/core")
580 jalviewjsJalviewCoreHtmlFile = string("")
581 jalviewjsJalviewCoreName = string(jalviewjs_core_name)
582 jalviewjsCoreClasslists = []
583 jalviewjsJalviewTemplateName = string(jalviewjs_name)
584 jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
585 jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
586 jalviewjsJ2sProps = null
587 jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
588 jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
590 closureCompilerJar = "${jalviewDir}/${jalviewjs_closure_compiler}"
592 eclipseWorkspace = null
593 eclipseBinary = string("")
594 eclipseVersion = string("")
595 eclipseProductVersion = string("")
598 jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
599 jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}"
609 outputDir = file(classesDir)
613 srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
616 compileClasspath = files(sourceSets.main.java.outputDir)
617 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
619 runtimeClasspath = compileClasspath
620 runtimeClasspath += files(sourceSets.main.resources.srcDirs)
625 srcDirs cloverInstrDir
626 outputDir = cloverClassesDir
630 srcDirs = sourceSets.main.resources.srcDirs
633 compileClasspath = files( sourceSets.clover.java.outputDir )
634 //compileClasspath += files( testClassesDir )
635 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
636 compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
637 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
639 runtimeClasspath = compileClasspath
644 srcDirs testSourceDir
645 outputDir = file(testClassesDir)
649 srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
652 compileClasspath = files( sourceSets.test.java.outputDir )
653 compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
654 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
656 runtimeClasspath = compileClasspath
657 runtimeClasspath += files(sourceSets.test.resources.srcDirs)
663 // eclipse project and settings files creation, also used by buildship
666 name = eclipse_project_name
668 natures 'org.eclipse.jdt.core.javanature',
669 'org.eclipse.jdt.groovy.core.groovyNature',
670 'org.eclipse.buildship.core.gradleprojectnature'
672 buildCommand 'org.eclipse.jdt.core.javabuilder'
673 buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
677 //defaultOutputDir = sourceSets.main.java.outputDir
678 configurations.each{ c->
679 if (c.isCanBeResolved()) {
680 minusConfigurations += [c]
684 plusConfigurations = [ ]
688 def removeTheseToo = []
689 HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
690 cp.entries.each { entry ->
691 // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
692 // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
693 // we add the resources and help/help dirs in as libs afterwards (see below)
694 if (entry.kind == 'src') {
695 if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
696 removeTheseToo += entry
698 alreadyAddedSrcPath.putAt(entry.path, true)
703 cp.entries.removeAll(removeTheseToo)
705 //cp.entries += new Output("${eclipse_bin_dir}/main")
706 if (file(helpParentDir).isDirectory()) {
707 cp.entries += new Library(fileReference(helpParentDir))
709 if (file(resourceDir).isDirectory()) {
710 cp.entries += new Library(fileReference(resourceDir))
713 HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
715 sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
716 //don't want to add outputDir as eclipse is using its own output dir in bin/main
717 if (it.isDirectory() || ! it.exists()) {
718 // don't add dirs to classpath, especially if they don't exist
719 return false // groovy "continue" in .any closure
721 def itPath = it.toString()
722 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
723 // make relative path
724 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
726 if (alreadyAddedLibPath.get(itPath)) {
727 //println("Not adding duplicate entry "+itPath)
729 //println("Adding entry "+itPath)
730 cp.entries += new Library(fileReference(itPath))
731 alreadyAddedLibPath.put(itPath, true)
735 sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
736 //no longer want to add outputDir as eclipse is using its own output dir in bin/main
737 if (it.isDirectory() || ! it.exists()) {
738 // don't add dirs to classpath
739 return false // groovy "continue" in .any closure
742 def itPath = it.toString()
743 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
744 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
746 if (alreadyAddedLibPath.get(itPath)) {
749 def lib = new Library(fileReference(itPath))
750 lib.entryAttributes["test"] = "true"
752 alreadyAddedLibPath.put(itPath, true)
760 containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
765 // for the IDE, use java 11 compatibility
766 sourceCompatibility = compile_source_compatibility
767 targetCompatibility = compile_target_compatibility
768 javaRuntimeName = eclipseJavaRuntimeName
770 // add in jalview project specific properties/preferences into eclipse core preferences
772 withProperties { props ->
773 def jalview_prefs = new Properties()
774 def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
775 jalview_prefs.load(ins)
777 jalview_prefs.forEach { t, v ->
778 if (props.getAt(t) == null) {
782 // codestyle file -- overrides previous formatter prefs
783 def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
784 if (csFile.exists()) {
785 XmlParser parser = new XmlParser()
786 def profiles = parser.parse(csFile)
787 def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
788 if (profile != null) {
789 profile.'setting'.each { s ->
791 def value = s.'@value'
792 if (id != null && value != null) {
793 props.putAt(id, value)
798 // local eclipse settings
799 def localEclipsePropertiesFile = file("${jalviewDirAbsolutePath}/${localEclipseProperties}")
800 if (localEclipsePropertiesFile.exists()) {
801 def eclipse_prefs = new Properties()
802 def ins2 = new FileInputStream(localEclipsePropertiesFile)
803 println("Loading Eclipse Preferences from '${localEclipsePropertiesFile}'")
804 eclipse_prefs.load(ins2)
806 eclipse_prefs.forEach { t, v ->
810 println("No local Eclipse Preferences file '${localEclipsePropertiesFile}'")
818 // Don't want these to be activated if in headless build
819 synchronizationTasks "eclipseSynchronizationTask"
820 //autoBuildTasks "eclipseAutoBuildTask"
826 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
827 // Class to allow updating arbitrary properties files
828 class PropertiesFile extends PropertiesPersistableConfigurationObject {
829 public PropertiesFile(PropertiesTransformer t) { super(t); }
830 @Override protected void load(Properties properties) { }
831 @Override protected void store(Properties properties) { }
832 @Override protected String getDefaultResourceName() { return ""; }
833 // This is necessary, because PropertiesPersistableConfigurationObject fails
834 // if no default properties file exists.
835 @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
838 // Task to update arbitrary properties files (set outputFile)
839 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
840 private final PropertiesFileContentMerger file;
841 public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
842 protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
843 protected void configure(PropertiesFile props) {
844 file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
846 public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
849 task eclipseUIPreferences(type: PropertiesFileTask) {
850 description = "Generate Eclipse additional settings"
851 def filename = "org.eclipse.jdt.ui.prefs"
852 outputFile = "$projectDir/.settings/${filename}" as File
855 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
860 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
861 description = "Generate Eclipse additional settings"
862 def filename = "org.eclipse.jdt.groovy.core.prefs"
863 outputFile = "$projectDir/.settings/${filename}" as File
866 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
871 task eclipseAllPreferences {
873 dependsOn eclipseUIPreferences
874 dependsOn eclipseGroovyCorePreferences
877 eclipseUIPreferences.mustRunAfter eclipseJdt
878 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
880 /* end of eclipse preferences hack */
888 delete cloverBuildDir
889 delete cloverReportDir
894 task cloverInstrJava(type: JavaExec) {
895 group = "Verification"
896 description = "Create clover instrumented source java files"
898 dependsOn cleanClover
900 inputs.files(sourceSets.main.allJava)
901 outputs.dir(cloverInstrDir)
903 //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
904 classpath = sourceSets.clover.compileClasspath
905 main = "com.atlassian.clover.CloverInstr"
913 cloverInstrDir.getPath(),
915 def srcFiles = sourceSets.main.allJava.files
918 { file -> file.absolutePath }
921 args argsList.toArray()
924 delete cloverInstrDir
925 println("Clover: About to instrument "+srcFiles.size() +" files")
930 task cloverInstrTests(type: JavaExec) {
931 group = "Verification"
932 description = "Create clover instrumented source test files"
934 dependsOn cleanClover
936 inputs.files(testDir)
937 outputs.dir(cloverTestInstrDir)
939 classpath = sourceSets.clover.compileClasspath
940 main = "com.atlassian.clover.CloverInstr"
950 cloverTestInstrDir.getPath(),
952 args argsList.toArray()
955 delete cloverTestInstrDir
956 println("Clover: About to instrument test files")
962 group = "Verification"
963 description = "Create clover instrumented all source files"
965 dependsOn cloverInstrJava
966 dependsOn cloverInstrTests
970 cloverClasses.dependsOn cloverInstr
973 task cloverConsoleReport(type: JavaExec) {
974 group = "Verification"
975 description = "Creates clover console report"
978 file(cloverDb).exists()
981 inputs.dir cloverClassesDir
983 classpath = sourceSets.clover.runtimeClasspath
984 main = "com.atlassian.clover.reporters.console.ConsoleReporter"
986 if (cloverreport_mem.length() > 0) {
987 maxHeapSize = cloverreport_mem
989 if (cloverreport_jvmargs.length() > 0) {
990 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1000 args argsList.toArray()
1004 task cloverHtmlReport(type: JavaExec) {
1005 group = "Verification"
1006 description = "Creates clover HTML report"
1009 file(cloverDb).exists()
1012 def cloverHtmlDir = cloverReportDir
1013 inputs.dir cloverClassesDir
1014 outputs.dir cloverHtmlDir
1016 classpath = sourceSets.clover.runtimeClasspath
1017 main = "com.atlassian.clover.reporters.html.HtmlReporter"
1019 if (cloverreport_mem.length() > 0) {
1020 maxHeapSize = cloverreport_mem
1022 if (cloverreport_jvmargs.length() > 0) {
1023 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1034 if (cloverreport_html_options.length() > 0) {
1035 argsList += cloverreport_html_options.split(" ")
1038 args argsList.toArray()
1042 task cloverXmlReport(type: JavaExec) {
1043 group = "Verification"
1044 description = "Creates clover XML report"
1047 file(cloverDb).exists()
1050 def cloverXmlFile = "${cloverReportDir}/clover.xml"
1051 inputs.dir cloverClassesDir
1052 outputs.file cloverXmlFile
1054 classpath = sourceSets.clover.runtimeClasspath
1055 main = "com.atlassian.clover.reporters.xml.XMLReporter"
1057 if (cloverreport_mem.length() > 0) {
1058 maxHeapSize = cloverreport_mem
1060 if (cloverreport_jvmargs.length() > 0) {
1061 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1072 if (cloverreport_xml_options.length() > 0) {
1073 argsList += cloverreport_xml_options.split(" ")
1076 args argsList.toArray()
1081 group = "Verification"
1082 description = "Creates clover reports"
1084 dependsOn cloverXmlReport
1085 dependsOn cloverHtmlReport
1092 sourceCompatibility = compile_source_compatibility
1093 targetCompatibility = compile_target_compatibility
1094 options.compilerArgs += additional_compiler_args
1095 print ("Setting target compatibility to "+targetCompatibility+"\n")
1097 //classpath += configurations.cloverRuntime
1103 // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1104 sourceCompatibility = compile_source_compatibility
1105 targetCompatibility = compile_target_compatibility
1106 options.compilerArgs += additional_compiler_args
1107 options.encoding = "UTF-8"
1109 print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1116 sourceCompatibility = compile_source_compatibility
1117 targetCompatibility = compile_target_compatibility
1118 options.compilerArgs += additional_compiler_args
1120 print ("Setting target compatibility to "+targetCompatibility+"\n")
1127 delete sourceSets.main.java.outputDir
1133 dependsOn cleanClover
1135 delete sourceSets.test.java.outputDir
1140 // format is a string like date.format("dd MMMM yyyy")
1141 def getDate(format) {
1142 return date.format(format)
1146 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1147 MutableDataSet options = new MutableDataSet()
1149 def extensions = new ArrayList<>()
1150 extensions.add(AnchorLinkExtension.create())
1151 extensions.add(AutolinkExtension.create())
1152 extensions.add(StrikethroughExtension.create())
1153 extensions.add(TaskListExtension.create())
1154 extensions.add(TablesExtension.create())
1155 extensions.add(TocExtension.create())
1157 options.set(Parser.EXTENSIONS, extensions)
1159 // set GFM table parsing options
1160 options.set(TablesExtension.WITH_CAPTION, false)
1161 options.set(TablesExtension.COLUMN_SPANS, false)
1162 options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1163 options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1164 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1165 options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1166 options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1168 options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1169 options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1170 options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1171 options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1173 Parser parser = Parser.builder(options).build()
1174 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1176 mdFiles.each { mdFile ->
1177 // add table of contents
1178 def mdText = "[TOC]\n"+mdFile.text
1180 // grab the first top-level title
1182 def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1183 def matcher = mdText =~ titleRegex
1184 if (matcher.size() > 0) {
1185 // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1186 title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1188 // or use the filename if none found
1189 if (title == null) {
1190 title = mdFile.getName()
1193 Node document = parser.parse(mdText)
1194 String htmlBody = renderer.render(document)
1195 def htmlText = '''<html>
1196 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1197 <html xmlns="http://www.w3.org/1999/xhtml">
1199 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1200 <meta http-equiv="Content-Style-Type" content="text/css" />
1201 <meta name="generator" content="flexmark" />
1203 htmlText += ((title != null) ? " <title>${title}</title>" : '' )
1205 <style type="text/css">code{white-space: pre;}</style>
1207 htmlText += ((cssFile != null) ? cssFile.text : '')
1208 htmlText += '''</head>
1211 htmlText += htmlBody
1217 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1218 def htmlFile = file(htmlFilePath)
1219 println("Creating ${htmlFilePath}")
1220 htmlFile.text = htmlText
1225 task copyDocs(type: Copy) {
1226 def inputDir = "${jalviewDir}/${doc_dir}"
1227 def outputDir = "${docBuildDir}/${doc_dir}"
1231 include('**/*.html')
1233 filter(ReplaceTokens,
1237 'Version-Rel': JALVIEW_VERSION,
1238 'Year-Rel': getDate("yyyy")
1245 exclude('**/*.html')
1250 inputs.dir(inputDir)
1251 outputs.dir(outputDir)
1255 task convertMdFiles {
1257 def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1258 def cssFile = file("${jalviewDir}/${flexmark_css}")
1261 convertMdToHtml(mdFiles, cssFile)
1264 inputs.files(mdFiles)
1265 inputs.file(cssFile)
1268 mdFiles.each { mdFile ->
1269 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1270 htmlFiles.add(file(htmlFilePath))
1272 outputs.files(htmlFiles)
1276 def hugoTemplateSubstitutions(String input, Map extras=null) {
1277 def replacements = [
1278 DATE: getDate("yyyy-MM-dd"),
1279 CHANNEL: propertiesChannelName,
1280 APPLICATION_NAME: applicationName,
1282 GIT_BRANCH: gitBranch,
1283 VERSION: JALVIEW_VERSION,
1284 JAVA_VERSION: JAVA_VERSION,
1285 VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
1290 if (extras != null) {
1291 extras.each{ k, v ->
1292 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1295 replacements.each{ k, v ->
1296 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1301 def mdFileComponents(File mdFile, def dateOnly=false) {
1304 if (mdFile.exists()) {
1305 def inFrontMatter = false
1306 def firstLine = true
1307 mdFile.eachLine { line ->
1308 if (line.matches("---")) {
1309 def prev = inFrontMatter
1310 inFrontMatter = firstLine
1311 if (inFrontMatter != prev)
1314 if (inFrontMatter) {
1316 if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
1317 map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
1318 } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
1319 map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
1320 } else if (m = line =~ /^channel:\s*(\S+)/) {
1321 map["channel"] = m[0][1]
1322 } else if (m = line =~ /^version:\s*(\S+)/) {
1323 map["version"] = m[0][1]
1324 } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
1325 map[ m[0][1] ] = m[0][2]
1327 if (dateOnly && map["date"] != null) {
1333 content += line+"\n"
1338 return dateOnly ? map["date"] : [map, content]
1341 task hugoTemplates {
1343 description "Create partially populated md pages for hugo website build"
1345 def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
1346 def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
1347 def templateFiles = fileTree(dir: hugoTemplatesDir)
1348 def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1349 def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1350 def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
1351 def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
1354 // specific release template for version archive
1357 def givenDate = null
1358 def givenChannel = null
1359 def givenVersion = null
1360 if (CHANNEL == "RELEASE") {
1361 def (map, content) = mdFileComponents(releaseMdFile)
1362 givenDate = map.date
1363 givenChannel = map.channel
1364 givenVersion = map.version
1366 if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
1367 throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
1370 if (whatsnewMdFile.exists())
1371 whatsnew = whatsnewMdFile.text
1374 def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
1375 def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
1377 def changesHugo = null
1378 if (changes != null) {
1379 changesHugo = '<div class="release_notes">\n\n'
1380 def inSection = false
1381 changes.eachLine { line ->
1383 if (m = line =~ /^##([^#].*)$/) {
1385 changesHugo += "</div>\n\n"
1387 def section = m[0][1].trim()
1388 section = section.toLowerCase()
1389 section = section.replaceAll(/ +/, "_")
1390 section = section.replaceAll(/[^a-z0-9_\-]/, "")
1391 changesHugo += "<div class=\"${section}\">\n\n"
1393 } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
1394 def comment = m[0][2].trim()
1395 if (comment != "") {
1396 comment = comment.replaceAll('"', """)
1398 comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
1399 def newline = m[0][1]
1400 if (comment.trim() != "")
1401 newline += "{{<comment>}}${comment}{{</comment>}} "
1402 newline += m[0][3].trim()
1403 if (issuekeys.size() > 0)
1404 newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
1405 if (m[0][4] != null)
1410 changesHugo += line+"\n"
1413 changesHugo += "\n</div>\n\n"
1415 changesHugo += '</div>'
1418 templateFiles.each{ templateFile ->
1419 def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
1420 def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
1421 def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
1423 def outPathName = string("${hugoBuildDir}/$newRelPathName")
1427 rename(templateFile.getName(), newFileName)
1431 def newFile = file("${outPathName}/${newFileName}".toString())
1432 def content = newFile.text
1433 newFile.text = hugoTemplateSubstitutions(content,
1436 CHANGES: changesHugo,
1437 DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
1438 DRAFT: givenDate == null ? "true" : "false",
1439 JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
1440 JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
1447 inputs.file(oldJvlFile)
1448 inputs.dir(hugoTemplatesDir)
1449 inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
1450 inputs.property("CHANNEL", { CHANNEL })
1453 def getMdDate(File mdFile) {
1454 return mdFileComponents(mdFile, true)
1457 def getMdSections(String content) {
1459 def sectionContent = ""
1460 def sectionName = null
1461 content.eachLine { line ->
1463 if (m = line =~ /^##([^#].*)$/) {
1464 if (sectionName != null) {
1465 sections[sectionName] = sectionContent
1469 sectionName = m[0][1].trim()
1470 sectionName = sectionName.toLowerCase()
1471 sectionName = sectionName.replaceAll(/ +/, "_")
1472 sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
1473 } else if (sectionName != null) {
1474 sectionContent += line+"\n"
1477 if (sectionContent != null) {
1478 sections[sectionName] = sectionContent
1484 task copyHelp(type: Copy) {
1485 def inputDir = helpSourceDir
1486 def outputDir = "${helpBuildDir}/${help_dir}"
1490 include('**/*.html')
1494 filter(ReplaceTokens,
1498 'Version-Rel': JALVIEW_VERSION,
1499 'Year-Rel': getDate("yyyy")
1506 exclude('**/*.html')
1513 inputs.dir(inputDir)
1514 outputs.files(helpFile)
1515 outputs.dir(outputDir)
1519 task releasesTemplates {
1521 description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1525 def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1526 def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1527 def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
1528 def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
1529 def releasesMdDir = "${jalviewDir}/${releases_dir}"
1530 def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1533 def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1534 def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1536 if (CHANNEL == "RELEASE") {
1537 if (!releaseMdFile.exists()) {
1538 throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1540 if (!whatsnewMdFile.exists()) {
1541 throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1545 def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1546 def releaseFilesDates = releaseFiles.collectEntries {
1547 [(it): getMdDate(it)]
1549 releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1551 def releasesTemplate = releasesTemplateFile.text
1552 def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1553 def versionTemplate = m[0][1]
1555 MutableDataSet options = new MutableDataSet()
1557 def extensions = new ArrayList<>()
1558 options.set(Parser.EXTENSIONS, extensions)
1559 options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1561 Parser parser = Parser.builder(options).build()
1562 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1564 def actualVersions = releaseFiles.collect { rf ->
1565 def (rfMap, rfContent) = mdFileComponents(rf)
1566 return rfMap.version
1568 def versionsHtml = ""
1569 def linkedVersions = []
1570 releaseFiles.reverse().each { rFile ->
1571 def (rMap, rContent) = mdFileComponents(rFile)
1573 def versionLink = ""
1574 def partialVersion = ""
1575 def firstPart = true
1576 rMap.version.split("\\.").each { part ->
1577 def displayPart = ( firstPart ? "" : "." ) + part
1578 partialVersion += displayPart
1580 linkedVersions.contains(partialVersion)
1581 || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1583 versionLink += displayPart
1585 versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1586 linkedVersions += partialVersion
1590 def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1593 def rContentProcessed = ""
1594 rContent.eachLine { line ->
1595 if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1596 line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1597 } else if (lm = line =~ /^###([^#]+.*)$/) {
1598 line = "_${lm[0][1].trim()}_"
1600 rContentProcessed += line + "\n"
1603 def rContentSections = getMdSections(rContentProcessed)
1604 def rVersion = versionTemplate
1605 if (rVersion != "") {
1606 def rNewFeatures = rContentSections["new_features"]
1607 def rIssuesResolved = rContentSections["issues_resolved"]
1608 Node newFeaturesNode = parser.parse(rNewFeatures)
1609 String newFeaturesHtml = renderer.render(newFeaturesNode)
1610 Node issuesResolvedNode = parser.parse(rIssuesResolved)
1611 String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1612 rVersion = hugoTemplateSubstitutions(rVersion,
1614 VERSION: rMap.version,
1615 VERSION_LINK: versionLink,
1616 DISPLAY_DATE: displayDate,
1617 NEW_FEATURES: newFeaturesHtml,
1618 ISSUES_RESOLVED: issuesResolvedHtml
1621 versionsHtml += rVersion
1625 releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1626 releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1627 releasesHtmlFile.text = releasesTemplate
1629 if (whatsnewMdFile.exists()) {
1630 def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1631 def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1632 Node whatsnewNode = parser.parse(whatsnewMd)
1633 String whatsnewHtml = renderer.render(whatsnewNode)
1634 whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1635 whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1637 VERSION: JALVIEW_VERSION,
1638 DISPLAY_DATE: wnDisplayDate
1641 } else if (gradle.taskGraph.hasTask(":linkCheck")) {
1642 whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
1647 inputs.file(releasesTemplateFile)
1648 inputs.file(whatsnewTemplateFile)
1649 inputs.dir(releasesMdDir)
1650 inputs.dir(whatsnewMdDir)
1651 outputs.file(releasesHtmlFile)
1652 outputs.file(whatsnewHtmlFile)
1656 task copyResources(type: Copy) {
1658 description = "Copy (and make text substitutions in) the resources dir to the build area"
1660 def inputDir = resourceDir
1661 def outputDir = resourcesBuildDir
1665 include('**/*.html')
1667 filter(ReplaceTokens,
1671 'Version-Rel': JALVIEW_VERSION,
1672 'Year-Rel': getDate("yyyy")
1679 exclude('**/*.html')
1684 inputs.dir(inputDir)
1685 outputs.dir(outputDir)
1688 task copyChannelResources(type: Copy) {
1689 dependsOn copyResources
1691 description = "Copy the channel resources dir to the build resources area"
1693 def inputDir = "${channelDir}/${resource_dir}"
1694 def outputDir = resourcesBuildDir
1696 include(channel_props)
1697 filter(ReplaceTokens,
1701 'SUFFIX': channelSuffix
1706 exclude(channel_props)
1710 inputs.dir(inputDir)
1711 outputs.dir(outputDir)
1714 task createBuildProperties(type: WriteProperties) {
1715 dependsOn copyResources
1717 description = "Create the ${buildProperties} file"
1719 inputs.dir(sourceDir)
1720 inputs.dir(resourcesBuildDir)
1721 outputFile (buildProperties)
1722 // taking time specific comment out to allow better incremental builds
1723 comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1724 //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1725 property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1726 property "VERSION", JALVIEW_VERSION
1727 property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1728 property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
1729 if (getdownSetAppBaseProperty) {
1730 property "GETDOWNAPPBASE", getdownAppBase
1731 property "GETDOWNAPPDISTDIR", getdownAppDistDir
1733 outputs.file(outputFile)
1737 task buildIndices(type: JavaExec) {
1739 classpath = sourceSets.main.compileClasspath
1740 main = "com.sun.java.help.search.Indexer"
1741 workingDir = "${helpBuildDir}/${help_dir}"
1744 inputs.dir("${workingDir}/${argDir}")
1746 outputs.dir("${classesDir}/doc")
1747 outputs.dir("${classesDir}/help")
1748 outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1749 outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1750 outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1751 outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1752 outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1753 outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1756 task buildResources {
1757 dependsOn copyResources
1758 dependsOn copyChannelResources
1759 dependsOn createBuildProperties
1763 dependsOn buildResources
1766 dependsOn releasesTemplates
1767 dependsOn convertMdFiles
1768 dependsOn buildIndices
1772 compileJava.dependsOn prepare
1773 run.dependsOn compileJava
1774 compileTestJava.dependsOn compileJava
1779 group = "Verification"
1780 description = "Runs all testTaskN tasks)"
1783 dependsOn cloverClasses
1785 dependsOn testClasses
1788 // not running tests in this task
1791 /* testTask0 is the main test task */
1792 task testTask0(type: Test) {
1793 group = "Verification"
1794 description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
1796 includeGroups testng_groups.split(",")
1797 excludeGroups testng_excluded_groups.split(",")
1798 tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
1800 useDefaultListeners=true
1804 /* separated tests */
1805 task testTask1(type: Test) {
1806 group = "Verification"
1807 description = "Tests that need to be isolated from the main test run"
1810 excludeGroups testng_excluded_groups.split(",")
1812 useDefaultListeners=true
1816 task testTask2(type: Test) {
1817 group = "Verification"
1818 description = "Tests that need to be isolated from the main test run"
1821 excludeGroups testng_excluded_groups.split(",")
1823 useDefaultListeners=true
1826 task testTask3(type: Test) {
1827 group = "Verification"
1828 description = "Tests that need to be isolated from the main test run"
1831 excludeGroups testng_excluded_groups.split(",")
1833 useDefaultListeners=true
1837 /* insert more testTaskNs here -- change N to next digit or other string */
1839 task testTaskN(type: Test) {
1840 group = "Verification"
1841 description = "Tests that need to be isolated from the main test run"
1844 excludeGroups testng_excluded_groups.split(",")
1846 useDefaultListeners=true
1852 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1853 * to summarise test results from all Test tasks
1855 /* START of test tasks results summary */
1856 import groovy.time.TimeCategory
1857 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1858 import org.gradle.api.tasks.testing.logging.TestLogEvent
1859 rootProject.ext.testsResults = [] // Container for tests summaries
1861 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1863 // from original test task
1865 dependsOn cloverClasses
1867 dependsOn testClasses //?
1870 // run main tests first
1871 if (!testTask.name.equals("testTask0"))
1872 testTask.mustRunAfter "testTask0"
1874 testTask.testLogging { logging ->
1875 events TestLogEvent.FAILED
1876 // TestLogEvent.SKIPPED,
1877 // TestLogEvent.STANDARD_OUT,
1878 // TestLogEvent.STANDARD_ERROR
1880 exceptionFormat TestExceptionFormat.FULL
1883 showStackTraces true
1885 showStandardStreams true
1887 info.events = [ TestLogEvent.FAILED ]
1890 if (OperatingSystem.current().isMacOsX()) {
1891 testTask.systemProperty "apple.awt.UIElement", "true"
1892 testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
1896 ignoreFailures = true // Always try to run all tests for all modules
1898 afterSuite { desc, result ->
1900 return // Only summarize results for whole modules
1902 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1904 rootProject.ext.testsResults.add(resultsInfo)
1907 // from original test task
1908 maxHeapSize = "1024m"
1910 workingDir = jalviewDir
1911 def testLaf = project.findProperty("test_laf")
1912 if (testLaf != null) {
1913 println("Setting Test LaF to '${testLaf}'")
1914 systemProperty "laf", testLaf
1916 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1917 if (testHiDPIScale != null) {
1918 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1919 systemProperty "sun.java2d.uiScale", testHiDPIScale
1921 sourceCompatibility = compile_source_compatibility
1922 targetCompatibility = compile_target_compatibility
1923 jvmArgs += additional_compiler_args
1926 // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
1927 // testTasks that include the tests, and exclude all from the others.
1928 // get --test argument
1929 filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
1930 // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
1931 // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
1935 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1940 /* don't fail on no matching tests (so --tests will run across all testTasks) */
1941 testTask.filter.setFailOnNoMatchingTests(false)
1943 /* ensure the "test" task dependsOn all the testTasks */
1944 test.dependsOn testTask
1947 gradle.buildFinished {
1948 def allResults = rootProject.ext.testsResults
1950 if (!allResults.isEmpty()) {
1951 printResults allResults
1952 allResults.each {r ->
1953 if (r[2].resultType == TestResult.ResultType.FAILURE)
1954 throw new GradleException("Failed tests!")
1959 private static String colString(styler, col, colour, text) {
1960 return col?"${styler[colour](text)}":text
1963 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1964 def colour = 'black'
1972 case TestResult.ResultType.SUCCESS:
1975 case TestResult.ResultType.FAILURE:
1983 StringBuilder sb = new StringBuilder()
1987 sb.append(" results: ")
1988 sb.append(colString(s, col && !nocol, colour, text))
1990 sb.append("${rc} tests, ")
1991 sb.append(colString(s, col && rs > 0, 'green', rs))
1992 sb.append(" successes, ")
1993 sb.append(colString(s, col && rf > 0, 'red', rf))
1994 sb.append(" failures, ")
1995 sb.append("${rsk} skipped) in ${t}")
1996 return sb.toString()
1999 private static void printResults(allResults) {
2001 // styler from https://stackoverflow.com/a/56139852
2002 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
2005 def failedTests = false
2006 def summaryLines = []
2008 def totalsuccess = 0
2011 def totaltime = TimeCategory.getSeconds(0)
2012 // sort on project name then task name
2013 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
2014 def projectName = it[0]
2015 def taskName = it[1]
2019 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
2020 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
2021 def reportLine = "Report file: ${report}"
2022 def ls = summaryPlain.length()
2023 def lr = reportLine.length()
2024 def m = [ls, lr].max()
2027 def info = [ls, summaryCol, reportLine]
2028 summaryLines.add(info)
2029 failedTests |= result.resultType == TestResult.ResultType.FAILURE
2030 totalcount += result.testCount
2031 totalsuccess += result.successfulTestCount
2032 totalfail += result.failedTestCount
2033 totalskip += result.skippedTestCount
2036 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
2037 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
2038 def tls = totalSummaryPlain.length()
2039 if (tls > maxLength)
2041 def info = [tls, totalSummaryCol, null]
2042 summaryLines.add(info)
2044 def allSummaries = []
2045 for(sInfo : summaryLines) {
2047 def summary = sInfo[1]
2048 def report = sInfo[2]
2050 StringBuilder sb = new StringBuilder()
2051 sb.append("│" + summary + " " * (maxLength - ls) + "│")
2052 if (report != null) {
2053 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
2055 allSummaries += sb.toString()
2058 println "┌${"${"─" * maxLength}"}┐"
2059 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
2060 println "└${"${"─" * maxLength}"}┘"
2062 /* END of test tasks results summary */
2065 task compileLinkCheck(type: JavaCompile) {
2067 classpath = files("${jalviewDir}/${utils_dir}")
2068 destinationDir = file("${jalviewDir}/${utils_dir}")
2069 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
2071 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2072 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2073 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
2074 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
2078 task linkCheck(type: JavaExec) {
2080 dependsOn compileLinkCheck
2082 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
2083 classpath = files("${jalviewDir}/${utils_dir}")
2084 main = "HelpLinksChecker"
2085 workingDir = "${helpBuildDir}"
2086 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
2088 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
2089 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2092 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2096 inputs.dir(helpBuildDir)
2097 outputs.file(helpLinksCheckerOutFile)
2101 // import the pubhtmlhelp target
2102 ant.properties.basedir = "${jalviewDir}"
2103 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
2104 ant.importBuild "${utils_dir}/publishHelp.xml"
2107 task cleanPackageDir(type: Delete) {
2109 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2119 attributes "Main-Class": main_class,
2120 "Permissions": "all-permissions",
2121 "Application-Name": applicationName,
2122 "Codebase": application_codebase,
2123 "Implementation-Version": JALVIEW_VERSION
2126 def outputDir = "${jalviewDir}/${package_dir}"
2127 destinationDirectory = file(outputDir)
2128 archiveFileName = rootProject.name+".jar"
2129 duplicatesStrategy "EXCLUDE"
2136 exclude "**/*.jar.*"
2138 inputs.dir(sourceSets.main.java.outputDir)
2139 sourceSets.main.resources.srcDirs.each{ dir ->
2142 outputs.file("${outputDir}/${archiveFileName}")
2146 task copyJars(type: Copy) {
2147 from fileTree(dir: classesDir, include: "**/*.jar").files
2148 into "${jalviewDir}/${package_dir}"
2152 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2153 task syncJars(type: Sync) {
2155 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2156 into "${jalviewDir}/${package_dir}"
2158 include jar.archiveFileName.getOrNull()
2165 description = "Put all required libraries in dist"
2166 // order of "cleanPackageDir", "copyJars", "jar" important!
2167 jar.mustRunAfter cleanPackageDir
2168 syncJars.mustRunAfter cleanPackageDir
2169 dependsOn cleanPackageDir
2172 outputs.dir("${jalviewDir}/${package_dir}")
2177 dependsOn cleanPackageDir
2183 task launcherJar(type: Jar) {
2186 "Main-Class": shadow_jar_main_class,
2187 "Implementation-Version": JALVIEW_VERSION,
2188 "Application-Name": applicationName
2194 group = "distribution"
2195 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2200 def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
2201 def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
2202 def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
2207 // shadowJar manifest must inheritFrom another Jar task. Can't set attributes here.
2208 inheritFrom(project.tasks.launcherJar.manifest)
2210 // we need to include the groovy-swing Include-Package for it to run in the shadowJar
2212 def jarFileManifests = []
2213 groovyJars.each { jarFile ->
2214 def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
2216 jarFileManifests += mf
2220 from (jarFileManifests) {
2221 eachEntry { details ->
2222 if (!details.key.equals("Import-Package")) {
2230 duplicatesStrategy "INCLUDE"
2232 // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
2233 mainClassName = shadow_jar_main_class
2235 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2239 task getdownImagesCopy() {
2240 inputs.dir getdownImagesDir
2241 outputs.dir getdownImagesBuildDir
2245 from(getdownImagesDir) {
2246 include("*getdown*.png")
2248 into getdownImagesBuildDir
2253 task getdownImagesProcess() {
2254 dependsOn getdownImagesCopy
2257 if (backgroundImageText) {
2258 if (convertBinary == null) {
2259 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2261 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2262 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2264 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2266 executable convertBinary
2269 '-font', getdown_background_image_text_font,
2270 '-fill', getdown_background_image_text_colour,
2271 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2272 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2273 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2282 task getdownImages() {
2283 dependsOn getdownImagesProcess
2286 task getdownWebsiteBuild() {
2287 group = "distribution"
2288 description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer. No digest is created."
2290 dependsOn getdownImages
2295 def getdownWebsiteResourceFilenames = []
2296 def getdownResourceDir = getdownResourceDir
2297 def getdownResourceFilenames = []
2300 // clean the getdown website and files dir before creating getdown folders
2301 delete getdownAppBaseDir
2302 delete getdownFilesDir
2305 from buildProperties
2306 rename(file(buildProperties).getName(), getdown_build_properties)
2309 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2312 from channelPropsFile
2313 filter(ReplaceTokens,
2317 'SUFFIX': channelSuffix
2320 into getdownAppBaseDir
2322 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2324 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2325 def props = project.properties.sort { it.key }
2326 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2327 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2329 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2330 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2332 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2333 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2335 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2336 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2337 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2338 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2339 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2340 props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
2341 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
2344 props.put("getdown_txt_title", jalview_name)
2345 props.put("getdown_txt_ui.name", applicationName)
2347 // start with appbase
2348 getdownTextLines += "appbase = ${getdownAppBase}"
2349 props.each{ prop, val ->
2350 if (prop.startsWith("getdown_txt_") && val != null) {
2351 if (prop.startsWith("getdown_txt_multi_")) {
2352 def key = prop.substring(18)
2353 val.split(",").each{ v ->
2354 def line = "${key} = ${v}"
2355 getdownTextLines += line
2358 // file values rationalised
2359 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2361 if (val.indexOf('/') == 0) {
2364 } else if (val.indexOf('/') > 0) {
2365 // relative path (relative to jalviewDir)
2366 r = file( "${jalviewDir}/${val}" )
2369 val = "${getdown_resource_dir}/" + r.getName()
2370 getdownWebsiteResourceFilenames += val
2371 getdownResourceFilenames += r.getPath()
2374 if (! prop.startsWith("getdown_txt_resource")) {
2375 def line = prop.substring(12) + " = ${val}"
2376 getdownTextLines += line
2382 getdownWebsiteResourceFilenames.each{ filename ->
2383 getdownTextLines += "resource = ${filename}"
2385 getdownResourceFilenames.each{ filename ->
2388 into getdownResourceDir
2392 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2393 getdownWrapperScripts.each{ script ->
2394 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2398 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2400 getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
2405 fileTree(file(package_dir)).each{ f ->
2406 if (f.isDirectory()) {
2407 def files = fileTree(dir: f, include: ["*"]).getFiles()
2409 } else if (f.exists()) {
2413 def jalviewJar = jar.archiveFileName.getOrNull()
2414 // put jalview.jar first for CLASSPATH and .properties files reasons
2415 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2416 def name = f.getName()
2417 def line = "code = ${getdownAppDistDir}/${name}"
2418 getdownTextLines += line
2425 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2427 if (JAVA_VERSION.equals("11")) {
2428 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2429 j11libFiles.sort().each{f ->
2430 def name = f.getName()
2431 def line = "code = ${getdown_j11lib_dir}/${name}"
2432 getdownTextLines += line
2435 into getdownJ11libDir
2441 // 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.
2442 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2443 getdownTextLines += "resource = ${getdown_launcher_new}"
2444 getdownTextLines += "class = ${main_class}"
2445 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2446 if (getdownSetAppBaseProperty) {
2447 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2448 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2451 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2452 getdownTxt.write(getdownTextLines.join("\n"))
2454 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2455 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2456 launchJvl.write("appbase=${getdownAppBase}")
2458 // files going into the getdown website dir: getdown-launcher.jar
2460 from getdownLauncher
2461 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2462 into getdownAppBaseDir
2465 // files going into the getdown website dir: getdown-launcher(-local).jar
2467 from getdownLauncher
2468 if (file(getdownLauncher).getName() != getdown_launcher) {
2469 rename(file(getdownLauncher).getName(), getdown_launcher)
2471 into getdownAppBaseDir
2474 // files going into the getdown website dir: ./install dir and files
2475 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2478 from getdownLauncher
2479 from "${getdownAppDir}/${getdown_build_properties}"
2480 if (file(getdownLauncher).getName() != getdown_launcher) {
2481 rename(file(getdownLauncher).getName(), getdown_launcher)
2483 into getdownInstallDir
2486 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2488 from getdownInstallDir
2489 into getdownFilesInstallDir
2493 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2497 from getdownLauncher
2498 from "${getdownAppBaseDir}/${getdown_build_properties}"
2499 from "${getdownAppBaseDir}/${channel_props}"
2500 if (file(getdownLauncher).getName() != getdown_launcher) {
2501 rename(file(getdownLauncher).getName(), getdown_launcher)
2503 into getdownFilesDir
2506 // and ./resource (not all downloaded by getdown)
2508 from getdownResourceDir
2509 into "${getdownFilesDir}/${getdown_resource_dir}"
2514 inputs.dir("${jalviewDir}/${package_dir}")
2516 outputs.dir(getdownAppBaseDir)
2517 outputs.dir(getdownFilesDir)
2521 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2522 task getdownDigestDir(type: JavaExec) {
2524 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2526 def digestDirPropertyName = "DIGESTDIR"
2528 classpath = files(getdownLauncher)
2529 def digestDir = findProperty(digestDirPropertyName)
2530 if (digestDir == null) {
2531 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2535 main = "com.threerings.getdown.tools.Digester"
2539 task getdownDigest(type: JavaExec) {
2540 group = "distribution"
2541 description = "Digest the getdown website folder"
2543 dependsOn getdownWebsiteBuild
2546 classpath = files(getdownLauncher)
2548 main = "com.threerings.getdown.tools.Digester"
2549 args getdownAppBaseDir
2550 inputs.dir(getdownAppBaseDir)
2551 outputs.file("${getdownAppBaseDir}/digest2.txt")
2556 group = "distribution"
2557 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2558 dependsOn getdownDigest
2560 if (reportRsyncCommand) {
2561 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2562 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2563 println "LIKELY RSYNC COMMAND:"
2564 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2565 if (RUNRSYNC == "true") {
2567 commandLine "mkdir", "-p", toDir
2570 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2577 task getdownWebsite {
2578 group = "distribution"
2579 description = "A task to create the whole getdown channel website dir including digest file"
2581 dependsOn getdownWebsiteBuild
2582 dependsOn getdownDigest
2585 task getdownArchiveBuild() {
2586 group = "distribution"
2587 description = "Put files in the archive dir to go on the website"
2589 dependsOn getdownWebsiteBuild
2591 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2592 def vDir = "${getdownArchiveDir}/${v}"
2593 getdownFullArchiveDir = "${vDir}/getdown"
2594 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2596 def vAltDir = "alt_${v}"
2597 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2600 // cleanup old "old" dir
2601 delete getdownArchiveDir
2603 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2604 getdownArchiveTxt.getParentFile().mkdirs()
2605 def getdownArchiveTextLines = []
2606 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2610 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2611 into "${getdownFullArchiveDir}/${vAltDir}"
2614 getdownTextLines.each { line ->
2615 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2616 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2617 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2618 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2619 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2620 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2621 // remove the existing resource = resource/ or bin/ lines
2622 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2623 getdownArchiveTextLines += line
2627 // the resource dir -- add these files as resource lines in getdown.txt
2629 from "${archiveImagesDir}"
2630 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2632 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2636 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2638 def vLaunchJvl = file(getdownVersionLaunchJvl)
2639 vLaunchJvl.getParentFile().mkdirs()
2640 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2641 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2642 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2643 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2644 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2645 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2647 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2649 from getdownLauncher
2650 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2651 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2652 from "${getdownAppBaseDir}/${channel_props}"
2653 if (file(getdownLauncher).getName() != getdown_launcher) {
2654 rename(file(getdownLauncher).getName(), getdown_launcher)
2656 into getdownFullArchiveDir
2662 task getdownArchiveDigest(type: JavaExec) {
2663 group = "distribution"
2664 description = "Digest the getdown archive folder"
2666 dependsOn getdownArchiveBuild
2669 classpath = files(getdownLauncher)
2670 args getdownFullArchiveDir
2672 main = "com.threerings.getdown.tools.Digester"
2673 inputs.dir(getdownFullArchiveDir)
2674 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2677 task getdownArchive() {
2678 group = "distribution"
2679 description = "Build the website archive dir with getdown digest"
2681 dependsOn getdownArchiveBuild
2682 dependsOn getdownArchiveDigest
2685 tasks.withType(JavaCompile) {
2686 options.encoding = 'UTF-8'
2692 delete getdownAppBaseDir
2693 delete getdownFilesDir
2694 delete getdownArchiveDir
2700 if (file(install4jHomeDir).exists()) {
2702 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2703 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2704 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2705 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2707 installDir(file(install4jHomeDir))
2709 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2713 task copyInstall4jTemplate {
2714 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2715 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2716 inputs.file(install4jTemplateFile)
2717 inputs.file(install4jFileAssociationsFile)
2718 inputs.property("CHANNEL", { CHANNEL })
2719 outputs.file(install4jConfFile)
2722 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2724 // turn off code signing if no OSX_KEYPASS
2725 if (OSX_KEYPASS == "") {
2726 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2727 codeSigning.'@macEnabled' = "false"
2729 install4jConfigXml.'**'.windows.each { windows ->
2730 windows.'@runPostProcessor' = "false"
2734 // disable install screen for OSX dmg (for 2.11.2.0)
2735 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2736 macosArchive.attributes().remove('executeSetupApp')
2737 macosArchive.attributes().remove('setupAppId')
2740 // turn off checksum creation for LOCAL channel
2741 def e = install4jConfigXml.application[0]
2742 e.'@createChecksums' = string(install4jCheckSums)
2744 // put file association actions where placeholder action is
2745 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2746 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2747 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2748 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2749 def parent = a.parent()
2751 fileAssociationActions.each { faa ->
2754 // don't need to continue in .any loop once replacements have been made
2759 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2760 // NB we're deleting the /other/ one!
2761 // Also remove the examples subdir from non-release versions
2762 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2763 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2764 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2765 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2767 // remove the examples subdir from Full File Set
2768 def files = install4jConfigXml.files[0]
2769 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2770 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2771 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2772 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2773 dirEntry.parent().remove(dirEntry)
2775 install4jConfigXml.'**'.action.any { a ->
2776 if (a.'@customizedId' == customizedIdToDelete) {
2777 def parent = a.parent()
2783 // write install4j file
2784 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2791 delete install4jConfFile
2795 task cleanInstallersDataFiles {
2796 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2797 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2798 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2800 delete installersOutputTxt
2801 delete installersSha256
2802 delete hugoDataJsonFile
2806 task install4jDMGBackgroundImageCopy {
2807 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2808 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2811 from(install4jDMGBackgroundImageDir) {
2812 include(install4jDMGBackgroundImageFile)
2814 into install4jDMGBackgroundImageBuildDir
2819 task install4jDMGBackgroundImageProcess {
2820 dependsOn install4jDMGBackgroundImageCopy
2823 if (backgroundImageText) {
2824 if (convertBinary == null) {
2825 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2827 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2828 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2830 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2832 executable convertBinary
2835 '-font', install4j_background_image_text_font,
2836 '-fill', install4j_background_image_text_colour,
2837 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2838 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2839 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2848 task install4jDMGBackgroundImage {
2849 dependsOn install4jDMGBackgroundImageProcess
2852 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2853 group = "distribution"
2854 description = "Create the install4j installers"
2856 dependsOn copyInstall4jTemplate
2857 dependsOn cleanInstallersDataFiles
2858 dependsOn install4jDMGBackgroundImage
2860 projectFile = install4jConfFile
2862 // create an md5 for the input files to use as version for install4j conf file
2863 def digest = MessageDigest.getInstance("MD5")
2865 (file("${install4jDir}/${install4j_template}").text +
2866 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2867 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2868 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2869 if (filesMd5.length() >= 8) {
2870 filesMd5 = filesMd5.substring(0,8)
2872 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2875 'JALVIEW_NAME': jalview_name,
2876 'JALVIEW_APPLICATION_NAME': applicationName,
2877 'JALVIEW_DIR': "../..",
2878 'OSX_KEYSTORE': OSX_KEYSTORE,
2879 'OSX_APPLEID': OSX_APPLEID,
2880 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2881 'JSIGN_SH': JSIGN_SH,
2882 'JRE_DIR': getdown_app_dir_java,
2883 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2884 'JALVIEW_VERSION': JALVIEW_VERSION,
2885 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2886 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2887 'JAVA_VERSION': JAVA_VERSION,
2888 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2889 'VERSION': JALVIEW_VERSION,
2890 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2891 'BUNDLE_ID': install4jBundleId,
2892 'INTERNAL_ID': install4jInternalId,
2893 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2894 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2895 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2896 'WRAPPER_LINK': getdownWrapperLink,
2897 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2898 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2899 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2900 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2901 'INSTALLER_NAME': install4jInstallerName,
2902 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2903 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2904 'GETDOWN_FILES_DIR': getdown_files_dir,
2905 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2906 'GETDOWN_DIST_DIR': getdownAppDistDir,
2907 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2908 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2909 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2910 'BUILD_DIR': install4jBuildDir,
2911 'APPLICATION_CATEGORIES': install4j_application_categories,
2912 'APPLICATION_FOLDER': install4jApplicationFolder,
2913 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2914 'EXECUTABLE_NAME': install4jExecutableName,
2915 'EXTRA_SCHEME': install4jExtraScheme,
2916 'MAC_ICONS_FILE': install4jMacIconsFile,
2917 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2918 'PNG_ICON_FILE': install4jPngIconFile,
2919 'BACKGROUND': install4jBackground,
2924 'windows': 'WINDOWS',
2928 // these are the bundled OS/architecture VMs needed by install4j
2931 [ "mac", "aarch64" ],
2932 [ "windows", "x64" ],
2934 [ "linux", "aarch64" ]
2936 osArch.forEach { os, arch ->
2937 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)
2938 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2939 // otherwise running `gradle installers` generates a non-useful error:
2940 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2941 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)
2944 //println("INSTALL4J VARIABLES:")
2945 //variables.each{k,v->println("${k}=${v}")}
2947 destination = "${jalviewDir}/${install4jBuildDir}"
2948 buildSelected = true
2950 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2952 disableSigning = true
2953 disableNotarization = true
2957 macKeystorePassword = OSX_KEYPASS
2960 if (OSX_ALTOOLPASS) {
2961 appleIdPassword = OSX_ALTOOLPASS
2962 disableNotarization = false
2964 disableNotarization = true
2968 println("Using projectFile "+projectFile)
2969 if (!disableNotarization) { println("Will notarize OSX App DMG") }
2973 inputs.dir(getdownAppBaseDir)
2974 inputs.file(install4jConfFile)
2975 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2976 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2979 def getDataHash(File myFile) {
2980 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2981 return myFile.exists()
2983 "file" : myFile.getName(),
2984 "filesize" : myFile.length(),
2985 "sha256" : hash.toString()
2990 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2992 "channel" : getdownChannelName,
2993 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2994 "git-commit" : "${gitHash} [${gitBranch}]",
2995 "version" : JALVIEW_VERSION
2997 // install4j installer files
2998 if (installersOutputTxt.exists()) {
3000 installersOutputTxt.readLines().each { def line ->
3001 if (line.startsWith("#")) {
3004 line.replaceAll("\n","")
3005 def vals = line.split("\t")
3006 def filename = vals[3]
3007 def filesize = file(filename).length()
3008 filename = filename.replaceAll(/^.*\//, "")
3009 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
3010 idHash."${filename}" = vals[0]
3012 if (install4jCheckSums && installersSha256.exists()) {
3013 installersSha256.readLines().each { def line ->
3014 if (line.startsWith("#")) {
3017 line.replaceAll("\n","")
3018 def vals = line.split(/\s+\*?/)
3019 def filename = vals[1]
3020 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
3026 "JAR": shadowJar.archiveFile, // executable JAR
3027 "JVL": getdownVersionLaunchJvl, // version JVL
3028 "SOURCE": sourceDist.archiveFile // source TGZ
3029 ].each { key, value ->
3030 def file = file(value)
3031 if (file.exists()) {
3032 def fileHash = getDataHash(file)
3033 if (fileHash != null) {
3034 hash."${key}" = fileHash;
3038 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
3041 task staticMakeInstallersJsonFile {
3043 def output = findProperty("i4j_output")
3044 def sha256 = findProperty("i4j_sha256")
3045 def json = findProperty("i4j_json")
3046 if (output == null || sha256 == null || json == null) {
3047 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3049 writeDataJsonFile(file(output), file(sha256), file(json))
3054 dependsOn installerFiles
3060 eclipse().configFile(eclipse_codestyle_file)
3064 task createSourceReleaseProperties(type: WriteProperties) {
3065 group = "distribution"
3066 description = "Create the source RELEASE properties file"
3068 def sourceTarBuildDir = "${buildDir}/sourceTar"
3069 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3070 outputFile (sourceReleasePropertiesFile)
3073 releaseProps.each{ key, val -> property key, val }
3074 property "git.branch", gitBranch
3075 property "git.hash", gitHash
3078 outputs.file(outputFile)
3081 task sourceDist(type: Tar) {
3082 group "distribution"
3083 description "Create a source .tar.gz file for distribution"
3085 dependsOn createBuildProperties
3086 dependsOn convertMdFiles
3087 dependsOn eclipseAllPreferences
3088 dependsOn createSourceReleaseProperties
3091 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3092 archiveFileName = outputFileName
3094 compression Compression.GZIP
3110 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3112 "utils/InstallAnywhere",
3127 "gradle.properties",
3139 ".settings/org.eclipse.buildship.core.prefs",
3140 ".settings/org.eclipse.jdt.core.prefs"
3144 exclude (EXCLUDE_FILES)
3145 include (PROCESS_FILES)
3146 filter(ReplaceTokens,
3150 'Version-Rel': JALVIEW_VERSION,
3151 'Year-Rel': getDate("yyyy")
3156 exclude (EXCLUDE_FILES)
3157 exclude (PROCESS_FILES)
3158 exclude ("appletlib")
3159 exclude ("**/*locales")
3160 exclude ("*locales/**")
3161 exclude ("utils/InstallAnywhere")
3163 exclude (getdown_files_dir)
3164 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3165 //exclude (getdown_website_dir)
3166 //exclude (getdown_archive_dir)
3168 // exluding these as not using jars as modules yet
3169 exclude ("${j11modDir}/**/*.jar")
3172 include(INCLUDE_FILES)
3174 // from (jalviewDir) {
3175 // // explicit includes for stuff that seemed to not get included
3176 // include(fileTree("test/**/*."))
3177 // exclude(EXCLUDE_FILES)
3178 // exclude(PROCESS_FILES)
3181 from(file(buildProperties).getParent()) {
3182 include(file(buildProperties).getName())
3183 rename(file(buildProperties).getName(), "build_properties")
3185 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3189 def sourceTarBuildDir = "${buildDir}/sourceTar"
3190 from(sourceTarBuildDir) {
3191 // this includes the appended RELEASE properties file
3195 task dataInstallersJson {
3197 description "Create the installers-VERSION.json data file for installer files created"
3199 mustRunAfter installers
3200 mustRunAfter shadowJar
3201 mustRunAfter sourceDist
3202 mustRunAfter getdownArchive
3204 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3205 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3207 if (installersOutputTxt.exists()) {
3208 inputs.file(installersOutputTxt)
3210 if (install4jCheckSums && installersSha256.exists()) {
3211 inputs.file(installersSha256)
3214 shadowJar.archiveFile, // executable JAR
3215 getdownVersionLaunchJvl, // version JVL
3216 sourceDist.archiveFile // source TGZ
3217 ].each { fileName ->
3218 if (file(fileName).exists()) {
3219 inputs.file(fileName)
3223 outputs.file(hugoDataJsonFile)
3226 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3232 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3235 dependsOn pubhtmlhelp
3237 inputs.dir("${helpBuildDir}/${help_dir}")
3238 outputs.dir("${buildDir}/distributions/${help_dir}")
3242 task j2sSetHeadlessBuild {
3249 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3251 description "Enable the alternative J2S Config file for headless build"
3253 outputFile = jalviewjsJ2sSettingsFileName
3254 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3255 def j2sProps = new Properties()
3256 if (j2sPropsFile.exists()) {
3258 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3259 j2sProps.load(j2sPropsFileFIS)
3260 j2sPropsFileFIS.close()
3262 j2sProps.each { prop, val ->
3265 } catch (Exception e) {
3266 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3270 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3271 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3276 task jalviewjsSetEclipseWorkspace {
3277 def propKey = "jalviewjs_eclipse_workspace"
3279 // see if jalviewjs_eclipse_workspace is set by a property
3280 if (project.hasProperty(propKey)) {
3281 propVal = project.getProperty(propKey)
3282 if (propVal.startsWith("~/")) {
3283 propVal = System.getProperty("user.home") + propVal.substring(1)
3286 // else look for an existing build/jalviewjs/eclipse_workspace_location file
3287 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3288 def propsFile = file(propsFileName)
3289 def eclipseWsDir = propVal
3290 def props = new Properties()
3292 def writeProps = true
3293 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3294 def ins = new FileInputStream(propsFileName)
3297 if (props.getProperty(propKey, null) != null) {
3298 eclipseWsDir = props.getProperty(propKey)
3303 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3304 def tempDir = File.createTempDir()
3305 eclipseWsDir = tempDir.getAbsolutePath()
3308 eclipseWorkspace = file(eclipseWsDir)
3311 // do not run a headless transpile when we claim to be in Eclipse
3313 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3314 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3316 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3320 props.setProperty(propKey, eclipseWsDir)
3321 propsFile.parentFile.mkdirs()
3322 def bytes = new ByteArrayOutputStream()
3323 props.store(bytes, null)
3324 def propertiesString = bytes.toString()
3325 propsFile.text = propertiesString
3331 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3334 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3335 //outputs.file(propsFileName) // don't want this to be deleted because of falsely "stale" task
3336 outputs.upToDateWhen { eclipseWorkspace.exists() && (propsFile.exists() || !writeProps) }
3340 task jalviewjsEclipsePaths {
3341 def eclipseProductFile
3344 def eclipseRoot = jalviewjs_eclipse_root
3345 if (eclipseRoot.startsWith("~/")) {
3346 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3348 if (OperatingSystem.current().isMacOsX()) {
3349 eclipseRoot += "/Eclipse.app"
3350 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3351 eclipseProductFile = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3352 eclipseSetupLog = "${eclipseRoot}/Contents/Eclipse/configuration/org.eclipse.oomph.setup/setup.log"
3353 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3354 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3355 eclipseRoot += "/eclipse"
3357 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3358 eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
3359 eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
3360 } else { // linux or unix
3361 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3362 eclipseRoot += "/eclipse"
3364 eclipseBinary = "${eclipseRoot}/eclipse"
3365 eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
3366 eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
3369 eclipseVersion = "unknown" // default
3370 def assumedVersion = true
3371 if (file(eclipseProductFile).exists()) {
3372 def fis = new FileInputStream(eclipseProductFile)
3373 def props = new Properties()
3375 eclipseVersion = props.getProperty("version")
3377 assumedVersion = false
3379 if (file(eclipseSetupLog).exists()) {
3380 def productRegex = /(?m)^\[[^\]]+\]\s+Product\s+(org\.eclipse.\S*)/
3382 file(eclipseSetupLog).eachLine { String line ->
3383 def matcher = line =~ productRegex
3384 if (matcher.size() > 0) {
3385 eclipseProductVersion = matcher[0][1]
3388 if (lineCount >= 100) {
3395 def propKey = "eclipse_debug"
3396 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3399 // do not run a headless transpile when we claim to be in Eclipse
3401 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3402 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3404 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3407 if (!assumedVersion) {
3408 println("ECLIPSE VERSION=${eclipseVersion}")
3409 if (eclipseProductVersion.length() != 0) {
3410 println("ECLIPSE PRODUCT=${eclipseProductVersion}")
3417 task printProperties {
3419 description "Output to console all System.properties"
3421 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3427 dependsOn eclipseProject
3428 dependsOn eclipseClasspath
3429 dependsOn eclipseJdt
3433 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3434 task jalviewjsEclipseCopyDropins(type: Copy) {
3435 dependsOn jalviewjsEclipsePaths
3437 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3438 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3439 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3446 // this eclipse -clean doesn't actually work
3447 task jalviewjsCleanEclipse(type: Exec) {
3448 dependsOn eclipseSetup
3449 dependsOn jalviewjsEclipsePaths
3450 dependsOn jalviewjsEclipseCopyDropins
3452 executable(eclipseBinary)
3453 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3459 def inputString = """exit
3462 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3463 standardInput = inputByteStream
3466 /* not really working yet
3467 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3471 task jalviewjsTransferUnzipSwingJs(type: Copy) {
3472 def swingJsZipFile = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3473 from zipTree( "${jalviewDir}/${jalviewjs_swingjs_zip}" )
3474 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3476 inputs.file swingJsZipFile
3480 task jalviewjsTransferUnzipLib(type: Copy) {
3481 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip").sort()
3483 zipFiles.each { file_zip ->
3484 from zipTree(file_zip)
3486 // The following replace() is needed due to a mismatch in Jmol calls to
3487 // colorPtToFFRGB$javajs_util_T3d when only colorPtToFFRGB$javajs_util_T3 is defined
3488 // in the SwingJS.zip (github or the one distributed with JSmol)
3489 if (file_zip.getName().startsWith("Jmol-SwingJS")) {
3492 while(!line.equals(l)) {
3493 line = line.replace('colorPtToFFRGB$javajs_util_T3d', 'colorPtToFFRGB$javajs_util_T3')
3501 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3505 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3507 description "Create the alternative j2s file from the j2s.* properties"
3509 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3510 def siteDirProperty = "j2s.site.directory"
3511 def setSiteDir = false
3512 jalviewjsJ2sProps.each { prop, val ->
3514 if (prop == siteDirProperty) {
3515 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3516 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3522 if (!setSiteDir) { // default site location, don't override specifically set property
3523 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3526 outputFile = jalviewjsJ2sAltSettingsFileName
3529 inputs.properties(jalviewjsJ2sProps)
3534 task jalviewjsEclipseSetup {
3535 dependsOn jalviewjsEclipseCopyDropins
3536 dependsOn jalviewjsSetEclipseWorkspace
3537 dependsOn jalviewjsCreateJ2sSettings
3541 task jalviewjsSyncLibs (type: Sync) {
3542 dependsOn jalviewjsTransferUnzipLib
3543 dependsOn jalviewjsTransferUnzipSwingJs
3545 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3546 def inputFiles = fileTree(dir: inputDir)
3547 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3551 def outputFiles = []
3552 inputFiles.each{ file ->
3553 def rfile = inputDir.toPath().relativize(file.toPath())
3554 def ofile = new File("${outputDir}/${rfile}")
3555 outputFiles += "${ofile}"
3561 duplicatesStrategy "EXCLUDE"
3563 outputs.files outputFiles
3564 inputs.files inputFiles
3567 task jalviewjsSyncSwingJS (type: Sync) {
3568 dependsOn jalviewjsTransferUnzipSwingJs
3569 mustRunAfter jalviewjsSyncLibs
3571 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3572 def inputFiles = fileTree(dir: inputDir)
3573 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3577 def outputFiles = []
3578 inputFiles.each{ file ->
3579 def rfile = inputDir.toPath().relativize(file.toPath())
3580 def ofile = new File("${outputDir}/${rfile}")
3581 outputFiles += "${ofile}"
3587 duplicatesStrategy "INCLUDE"
3589 outputs.files outputFiles
3590 inputs.files inputFiles
3593 task jalviewjsSyncAllLibs {
3594 dependsOn jalviewjsSyncLibs
3595 dependsOn jalviewjsSyncSwingJS
3599 task jalviewjsSyncResources (type: Sync) {
3600 dependsOn buildResources
3602 def inputDir = file(resourcesBuildDir)
3603 def inputFiles = fileTree(dir: inputDir)
3604 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3608 def outputFiles = []
3609 inputFiles.each{ file ->
3610 def rfile = inputDir.toPath().relativize(file.toPath())
3611 def ofile = new File("${outputDir}/${rfile}")
3612 outputFiles += "${ofile}"
3617 outputs.files outputFiles
3618 inputs.files inputFiles
3622 task jalviewjsSyncSiteResources (type: Sync) {
3623 def inputDir = file("${jalviewDir}/${jalviewjs_site_resource_dir}")
3624 def inputFiles = fileTree(dir: inputDir)
3625 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3629 def outputFiles = []
3630 inputFiles.each{ file ->
3631 def rfile = inputDir.toPath().relativize(file.toPath())
3632 def ofile = new File("${outputDir}/${rfile}")
3633 outputFiles += "${ofile}"
3639 outputs.files outputFiles
3640 inputs.files inputFiles
3644 task jalviewjsSyncBuildProperties (type: Sync) {
3645 dependsOn createBuildProperties
3647 def f = file(buildProperties)
3648 def inputDir = f.getParentFile()
3649 def inputFiles = [f]
3650 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3654 def outputFiles = []
3655 inputFiles.each{ file ->
3656 def rfile = inputDir.toPath().relativize(file.toPath())
3657 def ofile = new File("${outputDir}/${rfile}")
3658 outputFiles += "${ofile}"
3664 outputs.files outputFiles
3665 inputs.files inputFiles
3669 task jalviewjsProjectImport(type: Exec) {
3670 dependsOn eclipseSetup
3671 dependsOn jalviewjsEclipsePaths
3672 dependsOn jalviewjsEclipseSetup
3675 // do not run a headless import when we claim to be in Eclipse
3677 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3678 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3680 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3684 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3685 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/${eclipse_project_name}"
3687 executable(eclipseBinary)
3688 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3692 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3694 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3695 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3696 inputs.file("${jalviewjsJ2sAltSettingsFileName}")
3699 outputs.upToDateWhen( {
3703 def projDirExists = file(projdir).exists()
3704 return projDirExists
3708 // jalviewjs_eclipse_workspace_location_file
3709 task jalviewjsTranspile(type: Exec) {
3710 dependsOn jalviewjsProjectImport
3713 dependsOn jalviewjsEnableAltFileProperty
3717 // do not run a headless transpile when we claim to be in Eclipse
3719 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3720 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3722 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3726 executable(eclipseBinary)
3727 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3731 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3733 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3734 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3740 stdout = new ByteArrayOutputStream()
3741 stderr = new ByteArrayOutputStream()
3743 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3744 def logOutFile = file(logOutFileName)
3745 logOutFile.createNewFile()
3746 def info = """ROOT: ${jalviewjs_eclipse_root}
3747 ECLIPSE BINARY: ${eclipseBinary}
3748 ECLIPSE VERSION: ${eclipseVersion}
3749 ECLIPSE PRODUCT: ${eclipseProductVersion}
3750 ECLIPSE WORKSPACE: ${eclipseWorkspace}
3751 ECLIPSE DEBUG: ${eclipseDebug}
3754 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3755 // combine stdout and stderr
3756 def logErrFOS = logOutFOS
3758 if (jalviewjs_j2s_to_console.equals("true")) {
3759 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3760 new org.apache.tools.ant.util.TeeOutputStream(
3764 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3765 new org.apache.tools.ant.util.TeeOutputStream(
3770 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3773 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3777 standardOutput.write(string(info).getBytes("UTF-8"))
3781 def transpileError = false
3782 def j2sIsActive = false
3783 def j2sBuildStarting = false
3784 def compilingLines = 0
3785 def j2sBuildingJavascript = false
3786 def j2sBuildingJavascriptRegex = /(?m)^J2S building JavaScript for (\d+) files/
3788 def transpilingLines = 0
3789 stdout.toString().eachLine { String line ->
3790 if (line.startsWith("J2S isActive true")) {
3793 if (line.startsWith("J2S buildStarting")) {
3794 j2sBuildStarting = true
3796 if (line =~ / Compiling /) {
3799 if (!j2sBuildingJavascript) {
3800 def matcher = line =~ j2sBuildingJavascriptRegex
3801 if (matcher.size() > 0) {
3802 numFiles = Integer.valueOf(matcher[0][1])
3803 j2sBuildingJavascript = true
3806 if (line.startsWith("J2S transpiling ")) {
3809 if (line.contains("Error processing ")) {
3810 transpileError = true
3814 println("J2S IS ACTIVE=${j2sIsActive}")
3815 println("J2S BUILD STARTING=${j2sBuildStarting}")
3816 println("J2S BUILDING JAVASCRIPT=${j2sBuildingJavascript}")
3817 println("NUM FILES=${numFiles}")
3818 println("COMPILING LINES=${compilingLines}")
3819 println("TRANSPILING LINES=${transpilingLines}")
3820 println("TRANSPILE ERROR=${transpileError}")
3824 || (j2sBuildStarting && transpilingLines == 0)
3825 || (transpilingLines < compilingLines)
3826 || (transpilingLines != numFiles)
3828 // j2s did not complete transpile
3829 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3830 println("IGNORING TRANSPILE ERRORS")
3831 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3833 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3839 inputs.file(jalviewjsJ2sSettingsFileName)
3841 inputs.file(jalviewjsJ2sAltSettingsFileName)
3843 inputs.dir("${jalviewDir}/${sourceDir}")
3845 def inputJavaDir = file("${jalviewDir}/${sourceDir}")
3846 def inputJavaFiles = fileTree(dir: inputJavaDir)
3847 def outputJsDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3848 def outputJsFiles = []
3849 inputJavaFiles.each{ file ->
3850 def rfile = inputJavaDir.toPath().relativize(file.toPath())
3851 def ofile = new File("${outputJsDir}/${rfile}")
3852 def ofilenamejs = ofile.getPath()
3853 if (ofilenamejs.endsWith(".java")) {
3854 ofilenamejs = ofilenamejs.substring(0,ofilenamejs.length()-4)+"js"
3856 outputJsFiles += "${ofilenamejs}"
3859 outputs.files outputJsFiles
3860 outputs.file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}")
3864 task jalviewjsTransferSiteMergeSiteJsDir (type: Copy) {
3865 dependsOn jalviewjsTranspile
3867 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3868 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3871 def inputFiles = fileTree(dir: inputDir)
3875 includeEmptyDirs = false
3879 // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
3880 duplicatesStrategy "INCLUDE"
3882 // SiteJs files should take priority and write over existing files if different
3883 // so we define the output files
3884 outputs.upToDateWhen(
3886 def transpiledFiles = jalviewjsTransferSiteMergeSiteJsDir.getOutputs().getFiles()
3887 def inputFiles = fileTree(dir: inputDir)
3888 if (inputFiles.size() < transpiledFiles.size()) {
3891 def retVal = ! inputFiles.any { file ->
3892 def rfile = inputDir.toPath().relativize(file.toPath())
3893 def ofile = new File("${outputDir}/${rfile}")
3894 if (!ofile.exists() || ofile.lastModified() < file.lastModified()) {
3895 return true // this is NOTted to false
3903 inputs.files jalviewjsTranspile
3906 task jalviewjsTransferSiteMergeLibDir (type: Copy) {
3907 dependsOn jalviewjsTransferUnzipLib
3909 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3911 // This takes the outputs of jalviewjsTransferUnzipLib
3912 from jalviewjsTransferUnzipLib
3915 includeEmptyDirs = false
3919 // don't overwrite files in the destination
3920 // Note, this closure gets run at run stage not config stage
3922 if (it.getRelativePath().getFile(file(outputDir)).exists()) {
3927 duplicatesStrategy "INCLUDE"
3930 task jalviewjsTransferSiteMergeSwingJsDir (type: Copy) {
3931 dependsOn jalviewjsTransferUnzipSwingJs
3933 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3935 // This takes the outputs of jalviewjsTransferUnzipSwingJs
3936 from jalviewjsTransferUnzipSwingJs
3939 includeEmptyDirs = false
3943 // DO overwrite files in the destination
3945 // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
3946 duplicatesStrategy "INCLUDE"
3949 // we run after SiteJs and exclude overwriting files
3950 jalviewjsTransferSiteMergeLibDir.mustRunAfter jalviewjsTransferSiteMergeSiteJsDir
3951 jalviewjsTransferSiteMergeLibDir.mustRunAfter jalviewjsTransferSiteMergeSwingJsDir
3952 // we run this last, overwriting files from sitejs and lib, to ensure a consistent SwingJS
3953 jalviewjsTransferSiteMergeSwingJsDir.mustRunAfter jalviewjsTransferSiteMergeSiteJsDir
3955 task jalviewjsTransferSiteMergeDirs {
3956 dependsOn jalviewjsTransferSiteMergeLibDir
3957 dependsOn jalviewjsTransferSiteMergeSwingJsDir
3958 dependsOn jalviewjsTransferSiteMergeSiteJsDir
3962 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3964 def stdout = new ByteArrayOutputStream()
3965 def stderr = new ByteArrayOutputStream()
3967 def coreFile = file(jsfile)
3969 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3971 logOutFile.createNewFile()
3972 logOutFile.append(msg+"\n")
3974 def coreTop = file(prefixFile)
3975 def coreBottom = file(suffixFile)
3976 def missingFiles = []
3977 coreFile.getParentFile().mkdirs()
3978 coreFile.createNewFile()
3979 coreFile.write( coreTop.getText("UTF-8") )
3983 def t = f.getText("UTF-8")
3984 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3985 coreFile.append( t )
3987 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3989 logOutFile.append(msg+"\n")
3993 coreFile.append( coreBottom.getText("UTF-8") )
3995 msg = "Generating ${zjsfile}"
3997 logOutFile.append(msg+"\n")
3998 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3999 def logErrFOS = logOutFOS
4002 classpath = files([closureCompilerJar])
4003 main = "com.google.javascript.jscomp.CommandLineRunner"
4004 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
4005 args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
4008 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
4010 logOutFile.append(msg+"\n")
4012 if (logOutConsole) {
4013 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
4014 new org.apache.tools.ant.util.TeeOutputStream(
4018 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
4019 new org.apache.tools.ant.util.TeeOutputStream(
4024 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
4027 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
4033 if (missingFiles.size() > 0) {
4034 msg += "\n!!! These files were listed but missing:\n"
4035 missingFiles.each { file -> msg += "!!! " + file.getPath() + "\n" }
4039 logOutFile.append(msg+"\n")
4043 task jalviewjsBuildCore {
4045 description "Build the core js lib closures listed in the classlists dir"
4046 dependsOn jalviewjsTransferSiteMergeDirs
4048 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
4049 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
4050 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
4051 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_js_subdir}"
4052 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
4053 def prefixFile = "${jsDir}/core/coretop2.js"
4054 def suffixFile = "${jsDir}/core/corebottom2.js"
4056 inputs.file prefixFile
4057 inputs.file suffixFile
4059 def classlistFiles = []
4060 // add the classlists found int the jalviewjs_classlists_dir
4061 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
4063 def name = file.getName() - ".txt"
4071 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
4073 jalviewjsCoreClasslists = []
4075 classlistFiles.each {
4078 def file = hash['file']
4079 if (! file.exists()) {
4080 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
4081 return false // this is a "continue" in groovy .each closure
4083 def name = hash['name']
4085 name = file.getName() - ".txt"
4093 def list = fileTree(dir: j2sDir, includes: filelist)
4095 def jsfile = "${outputDir}/core${name}.js"
4096 def zjsfile = "${outputDir}/core${name}.z.js"
4098 jalviewjsCoreClasslists += [
4107 outputs.file(jsfile)
4108 outputs.file(zjsfile)
4112 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
4113 logOutFile.getParentFile().mkdirs()
4114 logOutFile.createNewFile()
4115 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildCore\n----\n")
4117 jalviewjsCoreClasslists.each {
4118 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
4122 inputs.file(closureCompilerJar)
4126 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
4129 into file(outputFile).getParentFile()
4130 rename { filename ->
4131 if (filename.equals(inputFile.getName())) {
4132 return file(outputFile).getName()
4136 filter(ReplaceTokens,
4140 'MAIN': '"'+main_class+'"',
4142 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
4143 'COREKEY': jalviewjs_core_key,
4144 'CORENAME': coreName
4151 task jalviewjsPublishCoreTemplates {
4152 dependsOn jalviewjsBuildCore
4154 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
4155 def inputFile = file(inputFileName)
4156 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4158 def outputFiles = []
4159 jalviewjsCoreClasslists.each { cl ->
4160 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
4161 cl['outputfile'] = outputFile
4162 outputFiles += outputFile
4166 jalviewjsCoreClasslists.each { cl ->
4167 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
4170 inputs.file(inputFile)
4171 outputs.files(outputFiles)
4175 task jalviewjsSyncCore (type: Sync) {
4176 dependsOn jalviewjsBuildCore
4177 dependsOn jalviewjsPublishCoreTemplates
4179 def inputDir = file("${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
4180 def inputFiles = fileTree(dir: inputDir)
4181 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
4185 def outputFiles = []
4186 inputFiles.each{ file ->
4187 def rfile = inputDir.toPath().relativize(file.toPath())
4188 def ofile = new File("${outputDir}/${rfile}")
4189 outputFiles += "${ofile}"
4195 outputs.files outputFiles
4196 inputs.files inputFiles
4200 // this Copy version of TransferSiteJs will delete anything else in the target dir
4201 task jalviewjsCopyTransferSiteMergeDir(type: Copy) {
4202 dependsOn jalviewjsTransferSiteMergeDirs
4204 from "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
4205 into "${jalviewDir}/${jalviewjsSiteDir}"
4209 // this Copy version of TransferSiteJs will delete anything else in the target dir
4210 task jalviewjsCopyTransferSiteJs(type: Copy) {
4211 dependsOn jalviewjsTranspile
4213 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4214 into "${jalviewDir}/${jalviewjsSiteDir}"
4218 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
4219 task jalviewjsSyncTransferSiteJs(type: Sync) {
4220 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4222 into "${jalviewDir}/${jalviewjsSiteDir}"
4229 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
4230 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
4231 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
4232 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
4234 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
4235 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
4236 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
4237 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
4240 task jalviewjsPrepareSite {
4242 description "Prepares the website folder including unzipping files and copying resources"
4243 //dependsOn jalviewjsSyncAllLibs // now using jalviewjsCopyTransferSiteMergeDir
4244 dependsOn jalviewjsSyncResources
4245 dependsOn jalviewjsSyncSiteResources
4246 dependsOn jalviewjsSyncBuildProperties
4247 dependsOn jalviewjsSyncCore
4251 task jalviewjsBuildSite {
4253 description "Builds the whole website including transpiled code"
4254 dependsOn jalviewjsCopyTransferSiteMergeDir
4255 dependsOn jalviewjsPrepareSite
4259 task cleanJalviewjsTransferSite {
4261 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4262 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4263 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4264 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4269 task cleanJalviewjsSite {
4270 dependsOn cleanJalviewjsTransferSite
4272 delete "${jalviewDir}/${jalviewjsSiteDir}"
4277 task jalviewjsSiteTar(type: Tar) {
4279 description "Creates a tar.gz file for the website"
4280 dependsOn jalviewjsBuildSite
4281 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4282 archiveFileName = outputFilename
4284 compression Compression.GZIP
4286 from "${jalviewDir}/${jalviewjsSiteDir}"
4287 into jalviewjs_site_dir // this is inside the tar file
4289 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4293 task jalviewjsServer {
4295 def filename = "jalviewjsTest.html"
4296 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4297 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4302 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4303 factory = f.newInstance()
4304 } catch (ClassNotFoundException e) {
4305 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4307 def port = Integer.valueOf(jalviewjs_server_port)
4312 while(port < start+1000 && !running) {
4314 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4315 jalviewjsServer = factory.start(doc_root, port)
4317 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4318 println("SERVER STARTED with document root ${doc_root}.")
4319 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4320 println("For debug: "+url+"?j2sdebug")
4321 println("For verbose: "+url+"?j2sverbose")
4322 } catch (Exception e) {
4327 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4328 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4329 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4331 jalviewjsCoreClasslists.each { cl ->
4332 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4334 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4336 println("For core ${cl.name}: "+urlcore)
4339 file(htmlFile).text = htmlText
4342 outputs.file(htmlFile)
4343 outputs.upToDateWhen({false})
4347 task cleanJalviewjsAll {
4349 description "Delete all configuration and build artifacts to do with JalviewJS build"
4350 dependsOn cleanJalviewjsSite
4351 dependsOn jalviewjsEclipsePaths
4354 delete "${jalviewDir}/${jalviewjsBuildDir}"
4355 delete "${jalviewDir}/${eclipse_bin_dir}"
4356 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4357 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4359 delete jalviewjsJ2sAltSettingsFileName
4362 outputs.upToDateWhen( { false } )
4366 task jalviewjsIDE_checkJ2sPlugin {
4367 group "00 JalviewJS in Eclipse"
4368 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4371 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4372 def j2sPluginFile = file(j2sPlugin)
4373 def eclipseHome = System.properties["eclipse.home.location"]
4374 if (eclipseHome == null || ! IN_ECLIPSE) {
4375 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4377 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4378 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4379 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4380 eclipseJ2sPluginDirs += altPluginsDir
4382 def foundPlugin = false
4383 def j2sPluginFileName = j2sPluginFile.getName()
4384 def eclipseJ2sPlugin
4385 def eclipseJ2sPluginFile
4386 eclipseJ2sPluginDirs.any { dir ->
4387 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4388 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4389 if (eclipseJ2sPluginFile.exists()) {
4395 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4396 System.err.println(msg)
4397 throw new StopExecutionException(msg)
4400 def digest = MessageDigest.getInstance("MD5")
4402 digest.update(j2sPluginFile.text.bytes)
4403 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4405 digest.update(eclipseJ2sPluginFile.text.bytes)
4406 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4408 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4409 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4410 System.err.println(msg)
4411 throw new StopExecutionException(msg)
4413 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4419 task jalviewjsIDE_copyJ2sPlugin {
4420 group "00 JalviewJS in Eclipse"
4421 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4424 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4425 def j2sPluginFile = file(j2sPlugin)
4426 def eclipseHome = System.properties["eclipse.home.location"]
4427 if (eclipseHome == null || ! IN_ECLIPSE) {
4428 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4430 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4431 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4432 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4433 System.err.println(msg)
4436 eclipseJ2sPluginFile.getParentFile().mkdirs()
4437 into eclipseJ2sPluginFile.getParent()
4443 task jalviewjsIDE_j2sFile {
4444 group "00 JalviewJS in Eclipse"
4445 description "Creates the .j2s file"
4446 dependsOn jalviewjsCreateJ2sSettings
4450 task jalviewjsIDE_SyncCore {
4451 group "00 JalviewJS in Eclipse"
4452 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4453 dependsOn jalviewjsSyncCore
4457 task jalviewjsIDE_SyncSiteAll {
4458 dependsOn jalviewjsSyncAllLibs
4459 dependsOn jalviewjsSyncResources
4460 dependsOn jalviewjsSyncSiteResources
4461 dependsOn jalviewjsSyncBuildProperties
4465 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4468 task jalviewjsIDE_PrepareSite {
4469 group "00 JalviewJS in Eclipse"
4470 description "Sync libs and resources to site dir, but not closure cores"
4472 dependsOn jalviewjsIDE_SyncSiteAll
4473 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4477 task jalviewjsIDE_AssembleSite {
4478 group "00 JalviewJS in Eclipse"
4479 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4480 dependsOn jalviewjsPrepareSite
4484 task jalviewjsIDE_SiteClean {
4485 group "00 JalviewJS in Eclipse"
4486 description "Deletes the Eclipse transpiled site"
4487 dependsOn cleanJalviewjsSite
4491 task jalviewjsIDE_Server {
4492 group "00 JalviewJS in Eclipse"
4493 description "Starts a webserver on localhost to test the website"
4494 dependsOn jalviewjsServer
4498 // buildship runs this at import or gradle refresh
4499 task eclipseSynchronizationTask {
4500 //dependsOn eclipseSetup
4501 dependsOn createBuildProperties
4503 dependsOn jalviewjsIDE_j2sFile
4504 dependsOn jalviewjsIDE_checkJ2sPlugin
4505 dependsOn jalviewjsIDE_PrepareSite
4510 // buildship runs this at build time or project refresh
4511 task eclipseAutoBuildTask {
4512 //dependsOn jalviewjsIDE_checkJ2sPlugin
4513 //dependsOn jalviewjsIDE_PrepareSite
4517 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4518 from file(jalviewjs_stderr_launch)
4519 into jalviewjsSiteDir
4521 inputs.file jalviewjs_stderr_launch
4522 outputs.file jalviewjsStderrLaunchFilename
4525 task cleanJalviewjsChromiumUserDir {
4527 delete jalviewjsChromiumUserDir
4529 outputs.dir jalviewjsChromiumUserDir
4530 // always run when depended on
4531 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4534 task jalviewjsChromiumProfile {
4535 dependsOn cleanJalviewjsChromiumUserDir
4536 mustRunAfter cleanJalviewjsChromiumUserDir
4538 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4541 mkdir jalviewjsChromiumProfileDir
4544 outputs.file firstRun
4547 task jalviewjsLaunchTest {
4549 description "Check JalviewJS opens in a browser"
4550 dependsOn jalviewjsBuildSite
4551 dependsOn jalviewjsCopyStderrLaunchFile
4552 dependsOn jalviewjsChromiumProfile
4554 def macOS = OperatingSystem.current().isMacOsX()
4555 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4556 if (chromiumBinary.startsWith("~/")) {
4557 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4563 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4565 def binary = file(chromiumBinary)
4566 if (!binary.exists()) {
4567 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4569 stdout = new ByteArrayOutputStream()
4570 stderr = new ByteArrayOutputStream()
4573 if (jalviewjs_j2s_to_console.equals("true")) {
4574 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4577 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4584 // macOS seems okay now with timeout arguments
4586 "--virtual-time-budget=${timeoutms}",
4587 "--timeout=${timeoutms}",
4590 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4593 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4594 "--profile-directory=${jalviewjs_chromium_profile_name}",
4595 "--allow-file-access-from-files",
4596 "--enable-logging=stderr",
4597 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4600 java.lang.Runnable runChrome = () -> {
4602 standardOutput = execStdout
4603 errorOutput = execStderr
4604 executable(chromiumBinary)
4606 println "COMMAND: '"+commandLine.join(" ")+"'"
4611 // we create our own timeout executor as --timeout doesn't work on macOS
4612 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4614 Future f1 = executor.submit( runChrome )
4616 def noChangeBytes = 0
4617 def noChangeIterations = 0
4618 executor.scheduleAtFixedRate(
4620 String stderrString = stderr.toString()
4621 // shutdown the task if we have a success string
4622 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4625 executor.shutdownNow()
4627 // if no change in stderr for 10s then also end
4628 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4629 executor.shutdownNow()
4631 if (stderrString.length() == noChangeBytes) {
4632 noChangeIterations++
4634 noChangeBytes = stderrString.length()
4635 noChangeIterations = 0
4638 200, 200, TimeUnit.MILLISECONDS)
4640 executor.schedule(new Runnable(){
4643 executor.shutdownNow()
4645 }, timeoutms, TimeUnit.MILLISECONDS)
4647 executor.awaitTermination(timeoutms+200, TimeUnit.MILLISECONDS)
4649 executor.shutdownNow()
4651 // just run chrome and rely on --virtual-time-budget and --timeout
4659 stderr.toString().eachLine { line ->
4660 if (line.contains(jalviewjs_desktop_init_string)) {
4661 println("Found line '"+line+"'")
4667 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4675 description "Build the JalviewJS site and run the launch test"
4676 dependsOn jalviewjsBuildSite
4677 dependsOn jalviewjsLaunchTest