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 groovy.transform.ExternalizeMethods
18 import groovy.util.XmlParser
19 import groovy.xml.XmlUtil
20 import groovy.json.JsonBuilder
21 import com.vladsch.flexmark.util.ast.Node
22 import com.vladsch.flexmark.html.HtmlRenderer
23 import com.vladsch.flexmark.parser.Parser
24 import com.vladsch.flexmark.util.data.MutableDataSet
25 import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
26 import com.vladsch.flexmark.ext.tables.TablesExtension
27 import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
28 import com.vladsch.flexmark.ext.autolink.AutolinkExtension
29 import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
30 import com.vladsch.flexmark.ext.toc.TocExtension
31 import com.google.common.hash.HashCode
32 import com.google.common.hash.Hashing
33 import com.google.common.io.Files
34 import org.jsoup.Jsoup
35 import org.jsoup.nodes.Element
43 classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
44 classpath "org.jsoup:jsoup:1.14.3"
45 classpath "com.eowise:gradle-imagemagick:0.5.1"
54 id "com.diffplug.gradle.spotless" version "3.28.0"
55 id 'com.github.johnrengelman.shadow' version '6.0.0'
56 id 'com.install4j.gradle' version '10.0.3'
57 id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree
58 id 'com.palantir.git-version' version '0.13.0' apply false
69 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
70 def string(Object o) {
71 return o == null ? "" : o.toString()
74 def overrideProperties(String propsFileName, boolean output = false) {
75 if (propsFileName == null) {
78 def propsFile = file(propsFileName)
79 if (propsFile != null && propsFile.exists()) {
80 println("Using properties from file '${propsFileName}'")
82 def p = new Properties()
83 def localPropsFIS = new FileInputStream(propsFile)
89 if (project.hasProperty(key)) {
90 oldval = project.findProperty(key)
91 project.setProperty(key, val)
93 println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
96 ext.setProperty(key, val)
98 println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
102 } catch (Exception e) {
103 println("Exception reading local.properties")
110 jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
111 jalviewDirRelativePath = jalviewDir
114 getdownChannelName = CHANNEL.toLowerCase()
115 // default to "default". Currently only has different cosmetics for "develop", "release", "default"
116 propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
117 channelDirName = propertiesChannelName
118 // Import channel_properties
119 if (getdownChannelName.startsWith("develop-")) {
120 channelDirName = "develop-SUFFIX"
122 channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
123 channelGradleProperties = string("${channelDir}/channel_gradle.properties")
124 channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
125 overrideProperties(channelGradleProperties, false)
126 // local build environment properties
127 // can be "projectDir/local.properties"
128 overrideProperties("${projectDir}/local.properties", true)
129 // or "../projectDir_local.properties"
130 overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
133 // Import releaseProps from the RELEASE file
134 // or a file specified via JALVIEW_RELEASE_FILE if defined
135 // Expect jalview.version and target release branch in jalview.release
136 releaseProps = new Properties();
137 def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
138 def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
140 (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream {
141 releaseProps.load(it)
143 } catch (Exception fileLoadError) {
144 throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
147 // Set JALVIEW_VERSION if it is not already set
148 if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
149 JALVIEW_VERSION = releaseProps.get("jalview.version")
151 println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
153 // this property set when running Eclipse headlessly
154 j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
155 // this property set by Eclipse
156 eclipseApplicationProperty = string("eclipse.application")
157 // CHECK IF RUNNING FROM WITHIN ECLIPSE
158 def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
159 IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
160 // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
161 if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
162 println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
166 println("WITHIN ECLIPSE IDE")
168 println("HEADLESS BUILD")
171 J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
173 println("J2S ENABLED")
176 System.properties.sort { it.key }.each {
177 key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
180 if (false && IN_ECLIPSE) {
181 jalviewDir = jalviewDirAbsolutePath
186 buildDate = new Date().format("yyyyMMdd")
189 bareSourceDir = string(source_dir)
190 sourceDir = string("${jalviewDir}/${bareSourceDir}")
191 resourceDir = string("${jalviewDir}/${resource_dir}")
192 bareTestSourceDir = string(test_source_dir)
193 testDir = string("${jalviewDir}/${bareTestSourceDir}")
195 classesDir = string("${jalviewDir}/${classes_dir}")
198 useClover = clover.equals("true")
199 cloverBuildDir = "${buildDir}/clover"
200 cloverInstrDir = file("${cloverBuildDir}/clover-instr")
201 cloverClassesDir = file("${cloverBuildDir}/clover-classes")
202 cloverReportDir = file("${buildDir}/reports/clover")
203 cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
204 cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
205 //cloverTestClassesDir = cloverClassesDir
206 cloverDb = string("${cloverBuildDir}/clover.db")
208 testSourceDir = useClover ? cloverTestInstrDir : testDir
209 testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
212 backgroundImageText = BACKGROUNDIMAGETEXT
213 getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
214 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
215 getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
216 getdownFullArchiveDir = null
217 getdownTextLines = []
218 getdownLaunchJvl = null
219 getdownVersionLaunchJvl = null
221 buildProperties = null
223 // the following values might be overridden by the CHANNEL switch
224 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
225 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
226 getdownArchiveAppBase = getdown_archive_base
227 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
228 getdownAppDistDir = getdown_app_dir_alt
229 getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
230 getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown")
231 getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
232 reportRsyncCommand = false
233 jvlChannelName = CHANNEL.toLowerCase()
234 install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
235 install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
236 install4jDMGBackgroundImageDir = "${install4j_images_dir}"
237 install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j"
238 install4jDMGBackgroundImageFile = "${install4j_dmg_background}"
239 install4jInstallerName = "${jalview_name} Non-Release Installer"
240 install4jExecutableName = install4j_executable_name
241 install4jExtraScheme = "jalviewx"
242 install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
243 install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
244 install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
245 install4jBackground = string("${install4j_images_dir}/${install4j_background}")
246 install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
247 install4jCheckSums = true
249 applicationName = "${jalview_name}"
253 // TODO: get bamboo build artifact URL for getdown artifacts
254 getdown_channel_base = bamboo_channelbase
255 getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
256 getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
257 jvlChannelName += "_${getdownChannelName}"
258 // automatically add the test group Not-bamboo for exclusion
259 if ("".equals(testng_excluded_groups)) {
260 testng_excluded_groups = "Not-bamboo"
262 install4jExtraScheme = "jalviewb"
263 backgroundImageText = true
266 case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
267 getdownAppDistDir = getdown_app_dir_release
268 getdownSetAppBaseProperty = true
269 reportRsyncCommand = true
271 install4jInstallerName = "${jalview_name} Installer"
275 getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
276 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
277 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
278 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
279 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
281 package_dir = string("${ARCHIVEDIR}/${package_dir}")
282 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
285 reportRsyncCommand = true
286 install4jExtraScheme = "jalviewa"
290 getdownChannelName = string("archive/${JALVIEW_VERSION}")
291 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
292 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
293 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
294 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']")
296 package_dir = string("${ARCHIVEDIR}/${package_dir}")
297 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
300 reportRsyncCommand = true
301 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
302 install4jSuffix = "Archive"
303 install4jExtraScheme = "jalviewa"
306 case ~/^DEVELOP-([\.\-\w]*)$/:
307 def suffix = Matcher.lastMatcher[0][1]
308 reportRsyncCommand = true
309 getdownSetAppBaseProperty = true
310 JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}"
311 install4jSuffix = "Develop ${suffix}"
312 install4jExtraScheme = "jalviewd"
313 install4jInstallerName = "${jalview_name} Develop ${suffix} Installer"
314 getdownChannelName = string("develop-${suffix}")
315 getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}")
316 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
317 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
318 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
319 channelSuffix = string(suffix)
320 backgroundImageText = true
324 reportRsyncCommand = true
325 getdownSetAppBaseProperty = true
326 // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
327 JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
329 install4jSuffix = "Develop"
330 install4jExtraScheme = "jalviewd"
331 install4jInstallerName = "${jalview_name} Develop Installer"
332 backgroundImageText = true
336 reportRsyncCommand = true
337 getdownSetAppBaseProperty = true
338 // Don't ignore transpile errors for release build
339 if (jalviewjs_ignore_transpile_errors.equals("true")) {
340 jalviewjs_ignore_transpile_errors = "false"
341 println("Setting jalviewjs_ignore_transpile_errors to 'false'")
343 JALVIEW_VERSION = JALVIEW_VERSION+"-test"
344 install4jSuffix = "Test"
345 install4jExtraScheme = "jalviewt"
346 install4jInstallerName = "${jalview_name} Test Installer"
347 backgroundImageText = true
350 case ~/^SCRATCH(|-[-\w]*)$/:
351 getdownChannelName = CHANNEL
352 JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
354 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
355 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
356 reportRsyncCommand = true
357 install4jSuffix = "Scratch"
361 if (!file("${LOCALDIR}").exists()) {
362 throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
364 getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
365 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
367 JALVIEW_VERSION = "TEST"
368 install4jSuffix = "Test-Local"
369 install4jExtraScheme = "jalviewt"
370 install4jInstallerName = "${jalview_name} Test Installer"
371 backgroundImageText = true
374 case [ "LOCAL", "JALVIEWJS" ]:
375 JALVIEW_VERSION = "TEST"
376 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
377 getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
378 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
379 install4jExtraScheme = "jalviewl"
380 install4jCheckSums = false
383 default: // something wrong specified
384 throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
388 JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
389 hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
390 hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
391 // override getdownAppBase if requested
392 if (findProperty("getdown_appbase_override") != null) {
393 // revert to LOCAL if empty string
394 if (string(getdown_appbase_override) == "") {
395 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
396 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
397 } else if (string(getdown_appbase_override).startsWith("file://")) {
398 getdownAppBase = string(getdown_appbase_override)
399 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
401 getdownAppBase = string(getdown_appbase_override)
403 println("Overriding getdown appbase with '${getdownAppBase}'")
405 // sanitise file name for jalview launcher file for this channel
406 jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
407 // install4j application and folder names
408 if (install4jSuffix == "") {
409 install4jBundleId = "${install4j_bundle_id}"
410 install4jWinApplicationId = install4j_release_win_application_id
412 applicationName = "${jalview_name} ${install4jSuffix}"
413 install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
414 // add int hash of install4jSuffix to the last part of the application_id
415 def id = install4j_release_win_application_id
416 def idsplitreverse = id.split("-").reverse()
417 idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
418 install4jWinApplicationId = idsplitreverse.reverse().join("-")
420 // sanitise folder and id names
421 // install4jApplicationFolder = e.g. "Jalview Build"
422 install4jApplicationFolder = applicationName
423 .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
424 .replaceAll("_+", "_") // collapse __
425 install4jInternalId = applicationName
427 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
428 .replaceAll("_+", "") // collapse __
429 //.replaceAll("_*-_*", "-") // collapse _-_
430 install4jUnixApplicationFolder = applicationName
432 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
433 .replaceAll("_+", "_") // collapse __
434 .replaceAll("_*-_*", "-") // collapse _-_
437 getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
438 getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
439 //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
440 getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
441 getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
442 getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
443 getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
444 /* compile without modules -- using classpath libraries
445 modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
446 modules_runtimeClasspath = modules_compileClasspath
452 apply plugin: "com.palantir.git-version"
453 def details = versionDetails()
454 gitHash = details.gitHash
455 gitBranch = details.branchName
456 } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
457 println("Not in a git repository. Using git values from RELEASE properties file.")
458 gitHash = releaseProps.getProperty("git.hash")
459 gitBranch = releaseProps.getProperty("git.branch")
460 } catch(java.lang.RuntimeException e1) {
461 throw new GradleException("Error with git-version plugin. Directory '.git' exists but versionDetails() cannot be found.")
464 println("Using a ${CHANNEL} profile.")
466 additional_compiler_args = []
467 // configure classpath/args for j8/j11 compilation
468 if (JAVA_VERSION.equals("1.8")) {
469 JAVA_INTEGER_VERSION = string("8")
472 libDistDir = j8libDir
473 compile_source_compatibility = 1.8
474 compile_target_compatibility = 1.8
475 // these are getdown.txt properties defined dependent on the JAVA_VERSION
476 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
477 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
478 // this property is assigned below and expanded to multiple lines in the getdown task
479 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
480 // this property is for the Java library used in eclipse
481 eclipseJavaRuntimeName = string("JavaSE-1.8")
482 } else if (JAVA_VERSION.equals("11")) {
483 JAVA_INTEGER_VERSION = string("11")
485 libDistDir = j11libDir
486 compile_source_compatibility = 11
487 compile_target_compatibility = 11
488 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
489 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
490 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
491 eclipseJavaRuntimeName = string("JavaSE-11")
492 /* compile without modules -- using classpath libraries
493 additional_compiler_args += [
494 '--module-path', modules_compileClasspath.asPath,
495 '--add-modules', j11modules
498 } else if (JAVA_VERSION.equals("17")) {
499 JAVA_INTEGER_VERSION = string("17")
501 libDistDir = j17libDir
502 compile_source_compatibility = 17
503 compile_target_compatibility = 17
504 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
505 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
506 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
507 eclipseJavaRuntimeName = string("JavaSE-17")
508 /* compile without modules -- using classpath libraries
509 additional_compiler_args += [
510 '--module-path', modules_compileClasspath.asPath,
511 '--add-modules', j11modules
515 throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
520 JAVA_MIN_VERSION = JAVA_VERSION
521 JAVA_MAX_VERSION = JAVA_VERSION
522 jreInstallsDir = string(jre_installs_dir)
523 if (jreInstallsDir.startsWith("~/")) {
524 jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
526 install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
527 install4jConfFileName = string("jalview-install4j-conf.install4j")
528 install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
529 install4jHomeDir = install4j_home_dir
530 if (install4jHomeDir.startsWith("~/")) {
531 install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
534 resourceBuildDir = string("${buildDir}/resources")
535 resourcesBuildDir = string("${resourceBuildDir}/resources_build")
536 helpBuildDir = string("${resourceBuildDir}/help_build")
537 docBuildDir = string("${resourceBuildDir}/doc_build")
539 if (buildProperties == null) {
540 buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
542 buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
543 helpParentDir = string("${jalviewDir}/${help_parent_dir}")
544 helpSourceDir = string("${helpParentDir}/${help_dir}")
545 helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
548 convertBinaryExpectedLocation = imagemagick_convert
549 if (convertBinaryExpectedLocation.startsWith("~/")) {
550 convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1)
552 if (file(convertBinaryExpectedLocation).exists()) {
553 convertBinary = convertBinaryExpectedLocation
556 relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
557 jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
558 jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
560 jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
562 jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
564 jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
565 jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
566 jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
567 jalviewjsJalviewCoreHtmlFile = string("")
568 jalviewjsJalviewCoreName = string(jalviewjs_core_name)
569 jalviewjsCoreClasslists = []
570 jalviewjsJalviewTemplateName = string(jalviewjs_name)
571 jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
572 jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
573 jalviewjsJ2sProps = null
574 jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
575 jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
577 eclipseWorkspace = null
578 eclipseBinary = string("")
579 eclipseVersion = string("")
582 jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
583 jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}"
593 outputDir = file(classesDir)
597 srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
600 compileClasspath = files(sourceSets.main.java.outputDir)
601 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
603 runtimeClasspath = compileClasspath
604 runtimeClasspath += files(sourceSets.main.resources.srcDirs)
609 srcDirs cloverInstrDir
610 outputDir = cloverClassesDir
614 srcDirs = sourceSets.main.resources.srcDirs
617 compileClasspath = files( sourceSets.clover.java.outputDir )
618 //compileClasspath += files( testClassesDir )
619 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
620 compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
621 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
623 runtimeClasspath = compileClasspath
628 srcDirs testSourceDir
629 outputDir = file(testClassesDir)
633 srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
636 compileClasspath = files( sourceSets.test.java.outputDir )
637 compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
638 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
640 runtimeClasspath = compileClasspath
641 runtimeClasspath += files(sourceSets.test.resources.srcDirs)
647 // eclipse project and settings files creation, also used by buildship
650 name = eclipse_project_name
652 natures 'org.eclipse.jdt.core.javanature',
653 'org.eclipse.jdt.groovy.core.groovyNature',
654 'org.eclipse.buildship.core.gradleprojectnature'
656 buildCommand 'org.eclipse.jdt.core.javabuilder'
657 buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
661 //defaultOutputDir = sourceSets.main.java.outputDir
662 configurations.each{ c->
663 if (c.isCanBeResolved()) {
664 minusConfigurations += [c]
668 plusConfigurations = [ ]
672 def removeTheseToo = []
673 HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
674 cp.entries.each { entry ->
675 // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
676 // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
677 // we add the resources and help/help dirs in as libs afterwards (see below)
678 if (entry.kind == 'src') {
679 if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
680 removeTheseToo += entry
682 alreadyAddedSrcPath.putAt(entry.path, true)
687 cp.entries.removeAll(removeTheseToo)
689 //cp.entries += new Output("${eclipse_bin_dir}/main")
690 if (file(helpParentDir).isDirectory()) {
691 cp.entries += new Library(fileReference(helpParentDir))
693 if (file(resourceDir).isDirectory()) {
694 cp.entries += new Library(fileReference(resourceDir))
697 HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
699 sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
700 //don't want to add outputDir as eclipse is using its own output dir in bin/main
701 if (it.isDirectory() || ! it.exists()) {
702 // don't add dirs to classpath, especially if they don't exist
703 return false // groovy "continue" in .any closure
705 def itPath = it.toString()
706 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
707 // make relative path
708 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
710 if (alreadyAddedLibPath.get(itPath)) {
711 //println("Not adding duplicate entry "+itPath)
713 //println("Adding entry "+itPath)
714 cp.entries += new Library(fileReference(itPath))
715 alreadyAddedLibPath.put(itPath, true)
719 sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
720 //no longer want to add outputDir as eclipse is using its own output dir in bin/main
721 if (it.isDirectory() || ! it.exists()) {
722 // don't add dirs to classpath
723 return false // groovy "continue" in .any closure
726 def itPath = it.toString()
727 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
728 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
730 if (alreadyAddedLibPath.get(itPath)) {
733 def lib = new Library(fileReference(itPath))
734 lib.entryAttributes["test"] = "true"
736 alreadyAddedLibPath.put(itPath, true)
744 containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
749 // for the IDE, use java 11 compatibility
750 sourceCompatibility = compile_source_compatibility
751 targetCompatibility = compile_target_compatibility
752 javaRuntimeName = eclipseJavaRuntimeName
754 // add in jalview project specific properties/preferences into eclipse core preferences
756 withProperties { props ->
757 def jalview_prefs = new Properties()
758 def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
759 jalview_prefs.load(ins)
761 jalview_prefs.forEach { t, v ->
762 if (props.getAt(t) == null) {
766 // codestyle file -- overrides previous formatter prefs
767 def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
768 if (csFile.exists()) {
769 XmlParser parser = new XmlParser()
770 def profiles = parser.parse(csFile)
771 def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
772 if (profile != null) {
773 profile.'setting'.each { s ->
775 def value = s.'@value'
776 if (id != null && value != null) {
777 props.putAt(id, value)
788 // Don't want these to be activated if in headless build
789 synchronizationTasks "eclipseSynchronizationTask"
790 //autoBuildTasks "eclipseAutoBuildTask"
796 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
797 // Class to allow updating arbitrary properties files
798 class PropertiesFile extends PropertiesPersistableConfigurationObject {
799 public PropertiesFile(PropertiesTransformer t) { super(t); }
800 @Override protected void load(Properties properties) { }
801 @Override protected void store(Properties properties) { }
802 @Override protected String getDefaultResourceName() { return ""; }
803 // This is necessary, because PropertiesPersistableConfigurationObject fails
804 // if no default properties file exists.
805 @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
808 // Task to update arbitrary properties files (set outputFile)
809 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
810 private final PropertiesFileContentMerger file;
811 public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
812 protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
813 protected void configure(PropertiesFile props) {
814 file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
816 public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
819 task eclipseUIPreferences(type: PropertiesFileTask) {
820 description = "Generate Eclipse additional settings"
821 def filename = "org.eclipse.jdt.ui.prefs"
822 outputFile = "$projectDir/.settings/${filename}" as File
825 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
830 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
831 description = "Generate Eclipse additional settings"
832 def filename = "org.eclipse.jdt.groovy.core.prefs"
833 outputFile = "$projectDir/.settings/${filename}" as File
836 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
841 task eclipseAllPreferences {
843 dependsOn eclipseUIPreferences
844 dependsOn eclipseGroovyCorePreferences
847 eclipseUIPreferences.mustRunAfter eclipseJdt
848 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
850 /* end of eclipse preferences hack */
858 delete cloverBuildDir
859 delete cloverReportDir
864 task cloverInstrJava(type: JavaExec) {
865 group = "Verification"
866 description = "Create clover instrumented source java files"
868 dependsOn cleanClover
870 inputs.files(sourceSets.main.allJava)
871 outputs.dir(cloverInstrDir)
873 //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
874 classpath = sourceSets.clover.compileClasspath
875 main = "com.atlassian.clover.CloverInstr"
883 cloverInstrDir.getPath(),
885 def srcFiles = sourceSets.main.allJava.files
888 { file -> file.absolutePath }
891 args argsList.toArray()
894 delete cloverInstrDir
895 println("Clover: About to instrument "+srcFiles.size() +" files")
900 task cloverInstrTests(type: JavaExec) {
901 group = "Verification"
902 description = "Create clover instrumented source test files"
904 dependsOn cleanClover
906 inputs.files(testDir)
907 outputs.dir(cloverTestInstrDir)
909 classpath = sourceSets.clover.compileClasspath
910 main = "com.atlassian.clover.CloverInstr"
920 cloverTestInstrDir.getPath(),
922 args argsList.toArray()
925 delete cloverTestInstrDir
926 println("Clover: About to instrument test files")
932 group = "Verification"
933 description = "Create clover instrumented all source files"
935 dependsOn cloverInstrJava
936 dependsOn cloverInstrTests
940 cloverClasses.dependsOn cloverInstr
943 task cloverConsoleReport(type: JavaExec) {
944 group = "Verification"
945 description = "Creates clover console report"
948 file(cloverDb).exists()
951 inputs.dir cloverClassesDir
953 classpath = sourceSets.clover.runtimeClasspath
954 main = "com.atlassian.clover.reporters.console.ConsoleReporter"
956 if (cloverreport_mem.length() > 0) {
957 maxHeapSize = cloverreport_mem
959 if (cloverreport_jvmargs.length() > 0) {
960 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
970 args argsList.toArray()
974 task cloverHtmlReport(type: JavaExec) {
975 group = "Verification"
976 description = "Creates clover HTML report"
979 file(cloverDb).exists()
982 def cloverHtmlDir = cloverReportDir
983 inputs.dir cloverClassesDir
984 outputs.dir cloverHtmlDir
986 classpath = sourceSets.clover.runtimeClasspath
987 main = "com.atlassian.clover.reporters.html.HtmlReporter"
989 if (cloverreport_mem.length() > 0) {
990 maxHeapSize = cloverreport_mem
992 if (cloverreport_jvmargs.length() > 0) {
993 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1004 if (cloverreport_html_options.length() > 0) {
1005 argsList += cloverreport_html_options.split(" ")
1008 args argsList.toArray()
1012 task cloverXmlReport(type: JavaExec) {
1013 group = "Verification"
1014 description = "Creates clover XML report"
1017 file(cloverDb).exists()
1020 def cloverXmlFile = "${cloverReportDir}/clover.xml"
1021 inputs.dir cloverClassesDir
1022 outputs.file cloverXmlFile
1024 classpath = sourceSets.clover.runtimeClasspath
1025 main = "com.atlassian.clover.reporters.xml.XMLReporter"
1027 if (cloverreport_mem.length() > 0) {
1028 maxHeapSize = cloverreport_mem
1030 if (cloverreport_jvmargs.length() > 0) {
1031 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1042 if (cloverreport_xml_options.length() > 0) {
1043 argsList += cloverreport_xml_options.split(" ")
1046 args argsList.toArray()
1051 group = "Verification"
1052 description = "Creates clover reports"
1054 dependsOn cloverXmlReport
1055 dependsOn cloverHtmlReport
1062 sourceCompatibility = compile_source_compatibility
1063 targetCompatibility = compile_target_compatibility
1064 options.compilerArgs += additional_compiler_args
1065 print ("Setting target compatibility to "+targetCompatibility+"\n")
1067 //classpath += configurations.cloverRuntime
1073 // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1074 sourceCompatibility = compile_source_compatibility
1075 targetCompatibility = compile_target_compatibility
1076 options.compilerArgs += additional_compiler_args
1077 options.encoding = "UTF-8"
1079 print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1086 sourceCompatibility = compile_source_compatibility
1087 targetCompatibility = compile_target_compatibility
1088 options.compilerArgs += additional_compiler_args
1090 print ("Setting target compatibility to "+targetCompatibility+"\n")
1097 delete sourceSets.main.java.outputDir
1103 dependsOn cleanClover
1105 delete sourceSets.test.java.outputDir
1110 // format is a string like date.format("dd MMMM yyyy")
1111 def getDate(format) {
1112 return date.format(format)
1116 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1117 MutableDataSet options = new MutableDataSet()
1119 def extensions = new ArrayList<>()
1120 extensions.add(AnchorLinkExtension.create())
1121 extensions.add(AutolinkExtension.create())
1122 extensions.add(StrikethroughExtension.create())
1123 extensions.add(TaskListExtension.create())
1124 extensions.add(TablesExtension.create())
1125 extensions.add(TocExtension.create())
1127 options.set(Parser.EXTENSIONS, extensions)
1129 // set GFM table parsing options
1130 options.set(TablesExtension.WITH_CAPTION, false)
1131 options.set(TablesExtension.COLUMN_SPANS, false)
1132 options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1133 options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1134 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1135 options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1136 options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1138 options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1139 options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1140 options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1141 options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1143 Parser parser = Parser.builder(options).build()
1144 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1146 mdFiles.each { mdFile ->
1147 // add table of contents
1148 def mdText = "[TOC]\n"+mdFile.text
1150 // grab the first top-level title
1152 def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1153 def matcher = mdText =~ titleRegex
1154 if (matcher.size() > 0) {
1155 // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1156 title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1158 // or use the filename if none found
1159 if (title == null) {
1160 title = mdFile.getName()
1163 Node document = parser.parse(mdText)
1164 String htmlBody = renderer.render(document)
1165 def htmlText = '''<html>
1166 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1167 <html xmlns="http://www.w3.org/1999/xhtml">
1169 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1170 <meta http-equiv="Content-Style-Type" content="text/css" />
1171 <meta name="generator" content="flexmark" />
1173 htmlText += ((title != null) ? " <title>${title}</title>" : '' )
1175 <style type="text/css">code{white-space: pre;}</style>
1177 htmlText += ((cssFile != null) ? cssFile.text : '')
1178 htmlText += '''</head>
1181 htmlText += htmlBody
1187 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1188 def htmlFile = file(htmlFilePath)
1189 println("Creating ${htmlFilePath}")
1190 htmlFile.text = htmlText
1195 task copyDocs(type: Copy) {
1196 def inputDir = "${jalviewDir}/${doc_dir}"
1197 def outputDir = "${docBuildDir}/${doc_dir}"
1201 include('**/*.html')
1203 filter(ReplaceTokens,
1207 'Version-Rel': JALVIEW_VERSION,
1208 'Year-Rel': getDate("yyyy")
1215 exclude('**/*.html')
1220 inputs.dir(inputDir)
1221 outputs.dir(outputDir)
1225 task convertMdFiles {
1227 def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1228 def cssFile = file("${jalviewDir}/${flexmark_css}")
1231 convertMdToHtml(mdFiles, cssFile)
1234 inputs.files(mdFiles)
1235 inputs.file(cssFile)
1238 mdFiles.each { mdFile ->
1239 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1240 htmlFiles.add(file(htmlFilePath))
1242 outputs.files(htmlFiles)
1246 def hugoTemplateSubstitutions(String input, Map extras=null) {
1247 def replacements = [
1248 DATE: getDate("yyyy-MM-dd"),
1249 CHANNEL: propertiesChannelName,
1250 APPLICATION_NAME: applicationName,
1252 GIT_BRANCH: gitBranch,
1253 VERSION: JALVIEW_VERSION,
1254 JAVA_VERSION: JAVA_VERSION,
1255 VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
1260 if (extras != null) {
1261 extras.each{ k, v ->
1262 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1265 replacements.each{ k, v ->
1266 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1271 def mdFileComponents(File mdFile, def dateOnly=false) {
1274 if (mdFile.exists()) {
1275 def inFrontMatter = false
1276 def firstLine = true
1277 mdFile.eachLine { line ->
1278 if (line.matches("---")) {
1279 def prev = inFrontMatter
1280 inFrontMatter = firstLine
1281 if (inFrontMatter != prev)
1284 if (inFrontMatter) {
1286 if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
1287 map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
1288 } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
1289 map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
1290 } else if (m = line =~ /^channel:\s*(\S+)/) {
1291 map["channel"] = m[0][1]
1292 } else if (m = line =~ /^version:\s*(\S+)/) {
1293 map["version"] = m[0][1]
1294 } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
1295 map[ m[0][1] ] = m[0][2]
1297 if (dateOnly && map["date"] != null) {
1303 content += line+"\n"
1308 return dateOnly ? map["date"] : [map, content]
1311 task hugoTemplates {
1313 description "Create partially populated md pages for hugo website build"
1315 def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
1316 def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
1317 def templateFiles = fileTree(dir: hugoTemplatesDir)
1318 def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1319 def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1320 def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
1321 def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
1324 // specific release template for version archive
1327 def givenDate = null
1328 def givenChannel = null
1329 def givenVersion = null
1330 if (CHANNEL == "RELEASE") {
1331 def (map, content) = mdFileComponents(releaseMdFile)
1332 givenDate = map.date
1333 givenChannel = map.channel
1334 givenVersion = map.version
1336 if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
1337 throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
1340 if (whatsnewMdFile.exists())
1341 whatsnew = whatsnewMdFile.text
1344 def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
1345 def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
1347 def changesHugo = null
1348 if (changes != null) {
1349 changesHugo = '<div class="release_notes">\n\n'
1350 def inSection = false
1351 changes.eachLine { line ->
1353 if (m = line =~ /^##([^#].*)$/) {
1355 changesHugo += "</div>\n\n"
1357 def section = m[0][1].trim()
1358 section = section.toLowerCase()
1359 section = section.replaceAll(/ +/, "_")
1360 section = section.replaceAll(/[^a-z0-9_\-]/, "")
1361 changesHugo += "<div class=\"${section}\">\n\n"
1363 } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
1364 def comment = m[0][2].trim()
1365 if (comment != "") {
1366 comment = comment.replaceAll('"', """)
1368 comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
1369 def newline = m[0][1]
1370 if (comment.trim() != "")
1371 newline += "{{<comment>}}${comment}{{</comment>}} "
1372 newline += m[0][3].trim()
1373 if (issuekeys.size() > 0)
1374 newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
1375 if (m[0][4] != null)
1380 changesHugo += line+"\n"
1383 changesHugo += "\n</div>\n\n"
1385 changesHugo += '</div>'
1388 templateFiles.each{ templateFile ->
1389 def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
1390 def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
1391 def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
1393 def outPathName = string("${hugoBuildDir}/$newRelPathName")
1397 rename(templateFile.getName(), newFileName)
1401 def newFile = file("${outPathName}/${newFileName}".toString())
1402 def content = newFile.text
1403 newFile.text = hugoTemplateSubstitutions(content,
1406 CHANGES: changesHugo,
1407 DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
1408 DRAFT: givenDate == null ? "true" : "false",
1409 JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
1410 JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
1417 inputs.file(oldJvlFile)
1418 inputs.dir(hugoTemplatesDir)
1419 inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
1420 inputs.property("CHANNEL", { CHANNEL })
1423 def getMdDate(File mdFile) {
1424 return mdFileComponents(mdFile, true)
1427 def getMdSections(String content) {
1429 def sectionContent = ""
1430 def sectionName = null
1431 content.eachLine { line ->
1433 if (m = line =~ /^##([^#].*)$/) {
1434 if (sectionName != null) {
1435 sections[sectionName] = sectionContent
1439 sectionName = m[0][1].trim()
1440 sectionName = sectionName.toLowerCase()
1441 sectionName = sectionName.replaceAll(/ +/, "_")
1442 sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
1443 } else if (sectionName != null) {
1444 sectionContent += line+"\n"
1447 if (sectionContent != null) {
1448 sections[sectionName] = sectionContent
1454 task copyHelp(type: Copy) {
1455 def inputDir = helpSourceDir
1456 def outputDir = "${helpBuildDir}/${help_dir}"
1460 include('**/*.html')
1464 filter(ReplaceTokens,
1468 'Version-Rel': JALVIEW_VERSION,
1469 'Year-Rel': getDate("yyyy")
1476 exclude('**/*.html')
1483 inputs.dir(inputDir)
1484 outputs.files(helpFile)
1485 outputs.dir(outputDir)
1489 task releasesTemplates {
1491 description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1495 def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1496 def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1497 def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
1498 def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
1499 def releasesMdDir = "${jalviewDir}/${releases_dir}"
1500 def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1503 def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1504 def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1506 if (CHANNEL == "RELEASE") {
1507 if (!releaseMdFile.exists()) {
1508 throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1510 if (!whatsnewMdFile.exists()) {
1511 throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1515 def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1516 def releaseFilesDates = releaseFiles.collectEntries {
1517 [(it): getMdDate(it)]
1519 releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1521 def releasesTemplate = releasesTemplateFile.text
1522 def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1523 def versionTemplate = m[0][1]
1525 MutableDataSet options = new MutableDataSet()
1527 def extensions = new ArrayList<>()
1528 options.set(Parser.EXTENSIONS, extensions)
1529 options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1531 Parser parser = Parser.builder(options).build()
1532 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1534 def actualVersions = releaseFiles.collect { rf ->
1535 def (rfMap, rfContent) = mdFileComponents(rf)
1536 return rfMap.version
1538 def versionsHtml = ""
1539 def linkedVersions = []
1540 releaseFiles.reverse().each { rFile ->
1541 def (rMap, rContent) = mdFileComponents(rFile)
1543 def versionLink = ""
1544 def partialVersion = ""
1545 def firstPart = true
1546 rMap.version.split("\\.").each { part ->
1547 def displayPart = ( firstPart ? "" : "." ) + part
1548 partialVersion += displayPart
1550 linkedVersions.contains(partialVersion)
1551 || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1553 versionLink += displayPart
1555 versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1556 linkedVersions += partialVersion
1560 def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1563 def rContentProcessed = ""
1564 rContent.eachLine { line ->
1565 if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1566 line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1567 } else if (lm = line =~ /^###([^#]+.*)$/) {
1568 line = "_${lm[0][1].trim()}_"
1570 rContentProcessed += line + "\n"
1573 def rContentSections = getMdSections(rContentProcessed)
1574 def rVersion = versionTemplate
1575 if (rVersion != "") {
1576 def rNewFeatures = rContentSections["new_features"]
1577 def rIssuesResolved = rContentSections["issues_resolved"]
1578 Node newFeaturesNode = parser.parse(rNewFeatures)
1579 String newFeaturesHtml = renderer.render(newFeaturesNode)
1580 Node issuesResolvedNode = parser.parse(rIssuesResolved)
1581 String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1582 rVersion = hugoTemplateSubstitutions(rVersion,
1584 VERSION: rMap.version,
1585 VERSION_LINK: versionLink,
1586 DISPLAY_DATE: displayDate,
1587 NEW_FEATURES: newFeaturesHtml,
1588 ISSUES_RESOLVED: issuesResolvedHtml
1591 versionsHtml += rVersion
1595 releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1596 releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1597 releasesHtmlFile.text = releasesTemplate
1599 if (whatsnewMdFile.exists()) {
1600 def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1601 def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1602 Node whatsnewNode = parser.parse(whatsnewMd)
1603 String whatsnewHtml = renderer.render(whatsnewNode)
1604 whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1605 whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1607 VERSION: JALVIEW_VERSION,
1608 DISPLAY_DATE: wnDisplayDate
1611 } else if (gradle.taskGraph.hasTask(":linkCheck")) {
1612 whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
1617 inputs.file(releasesTemplateFile)
1618 inputs.file(whatsnewTemplateFile)
1619 inputs.dir(releasesMdDir)
1620 inputs.dir(whatsnewMdDir)
1621 outputs.file(releasesHtmlFile)
1622 outputs.file(whatsnewHtmlFile)
1626 task copyResources(type: Copy) {
1628 description = "Copy (and make text substitutions in) the resources dir to the build area"
1630 def inputDir = resourceDir
1631 def outputDir = resourcesBuildDir
1635 include('**/*.html')
1637 filter(ReplaceTokens,
1641 'Version-Rel': JALVIEW_VERSION,
1642 'Year-Rel': getDate("yyyy")
1649 exclude('**/*.html')
1654 inputs.dir(inputDir)
1655 outputs.dir(outputDir)
1658 task copyChannelResources(type: Copy) {
1659 dependsOn copyResources
1661 description = "Copy the channel resources dir to the build resources area"
1663 def inputDir = "${channelDir}/${resource_dir}"
1664 def outputDir = resourcesBuildDir
1666 include(channel_props)
1667 filter(ReplaceTokens,
1671 'SUFFIX': channelSuffix
1676 exclude(channel_props)
1680 inputs.dir(inputDir)
1681 outputs.dir(outputDir)
1684 task createBuildProperties(type: WriteProperties) {
1685 dependsOn copyResources
1687 description = "Create the ${buildProperties} file"
1689 inputs.dir(sourceDir)
1690 inputs.dir(resourcesBuildDir)
1691 outputFile (buildProperties)
1692 // taking time specific comment out to allow better incremental builds
1693 comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1694 //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1695 property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1696 property "VERSION", JALVIEW_VERSION
1697 property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1698 property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
1699 if (getdownSetAppBaseProperty) {
1700 property "GETDOWNAPPBASE", getdownAppBase
1701 property "GETDOWNAPPDISTDIR", getdownAppDistDir
1703 outputs.file(outputFile)
1707 task buildIndices(type: JavaExec) {
1709 classpath = sourceSets.main.compileClasspath
1710 main = "com.sun.java.help.search.Indexer"
1711 workingDir = "${helpBuildDir}/${help_dir}"
1714 inputs.dir("${workingDir}/${argDir}")
1716 outputs.dir("${classesDir}/doc")
1717 outputs.dir("${classesDir}/help")
1718 outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1719 outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1720 outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1721 outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1722 outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1723 outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1726 task buildResources {
1727 dependsOn copyResources
1728 dependsOn copyChannelResources
1729 dependsOn createBuildProperties
1733 dependsOn buildResources
1736 dependsOn releasesTemplates
1737 dependsOn convertMdFiles
1738 dependsOn buildIndices
1742 compileJava.dependsOn prepare
1743 run.dependsOn compileJava
1744 compileTestJava.dependsOn compileJava
1749 group = "Verification"
1750 description = "Runs all testTaskN tasks)"
1753 dependsOn cloverClasses
1755 dependsOn testClasses
1758 // not running tests in this task
1761 /* testTask0 is the main test task */
1762 task testTask0(type: Test) {
1763 group = "Verification"
1764 description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
1766 includeGroups testng_groups.split(",")
1767 excludeGroups testng_excluded_groups.split(",")
1768 tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
1770 useDefaultListeners=true
1772 timeout = Duration.ofMinutes(15)
1775 /* separated tests */
1776 task testTask1(type: Test) {
1777 group = "Verification"
1778 description = "Tests that need to be isolated from the main test run"
1781 excludeGroups testng_excluded_groups.split(",")
1783 useDefaultListeners=true
1785 timeout = Duration.ofMinutes(5)
1788 task testTask2(type: Test) {
1789 group = "Verification"
1790 description = "Tests that need to be isolated from the main test run"
1793 excludeGroups testng_excluded_groups.split(",")
1795 useDefaultListeners=true
1797 timeout = Duration.ofMinutes(5)
1799 task testTask3(type: Test) {
1800 group = "Verification"
1801 description = "Tests that need to be isolated from the main test run"
1804 excludeGroups testng_excluded_groups.split(",")
1806 useDefaultListeners=true
1808 timeout = Duration.ofMinutes(5)
1811 /* insert more testTaskNs here -- change N to next digit or other string */
1813 task testTaskN(type: Test) {
1814 group = "Verification"
1815 description = "Tests that need to be isolated from the main test run"
1818 excludeGroups testng_excluded_groups.split(",")
1820 useDefaultListeners=true
1827 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1828 * to summarise test results from all Test tasks
1830 /* START of test tasks results summary */
1831 import groovy.time.TimeCategory
1832 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1833 import org.gradle.api.tasks.testing.logging.TestLogEvent
1834 rootProject.ext.testsResults = [] // Container for tests summaries
1836 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1838 // from original test task
1840 dependsOn cloverClasses
1842 dependsOn testClasses //?
1845 // run main tests first
1846 if (!testTask.name.equals("testTask0"))
1847 testTask.mustRunAfter "testTask0"
1849 testTask.testLogging { logging ->
1850 events TestLogEvent.FAILED
1851 // TestLogEvent.SKIPPED,
1852 // TestLogEvent.STANDARD_OUT,
1853 // TestLogEvent.STANDARD_ERROR
1855 exceptionFormat TestExceptionFormat.FULL
1858 showStackTraces true
1860 showStandardStreams true
1862 info.events = [ TestLogEvent.FAILED ]
1865 if (OperatingSystem.current().isMacOsX()) {
1866 testTask.systemProperty "apple.awt.UIElement", "true"
1867 testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
1871 ignoreFailures = true // Always try to run all tests for all modules
1873 afterSuite { desc, result ->
1875 return // Only summarize results for whole modules
1877 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1879 rootProject.ext.testsResults.add(resultsInfo)
1882 // from original test task
1883 maxHeapSize = "1024m"
1885 workingDir = jalviewDir
1886 def testLaf = project.findProperty("test_laf")
1887 if (testLaf != null) {
1888 println("Setting Test LaF to '${testLaf}'")
1889 systemProperty "laf", testLaf
1891 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1892 if (testHiDPIScale != null) {
1893 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1894 systemProperty "sun.java2d.uiScale", testHiDPIScale
1896 sourceCompatibility = compile_source_compatibility
1897 targetCompatibility = compile_target_compatibility
1898 jvmArgs += additional_compiler_args
1901 // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
1902 // testTasks that include the tests, and exclude all from the others.
1903 // get --test argument
1904 filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
1905 // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
1906 // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
1910 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1915 /* don't fail on no matching tests (so --tests will run across all testTasks) */
1916 testTask.filter.setFailOnNoMatchingTests(false)
1918 /* ensure the "test" task dependsOn all the testTasks */
1919 test.dependsOn testTask
1922 gradle.buildFinished {
1923 def allResults = rootProject.ext.testsResults
1925 if (!allResults.isEmpty()) {
1926 printResults allResults
1927 allResults.each {r ->
1928 if (r[2].resultType == TestResult.ResultType.FAILURE)
1929 throw new GradleException("Failed tests!")
1934 private static String colString(styler, col, colour, text) {
1935 return col?"${styler[colour](text)}":text
1938 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1939 def colour = 'black'
1947 case TestResult.ResultType.SUCCESS:
1950 case TestResult.ResultType.FAILURE:
1958 StringBuilder sb = new StringBuilder()
1962 sb.append(" results: ")
1963 sb.append(colString(s, col && !nocol, colour, text))
1965 sb.append("${rc} tests, ")
1966 sb.append(colString(s, col && rs > 0, 'green', rs))
1967 sb.append(" successes, ")
1968 sb.append(colString(s, col && rf > 0, 'red', rf))
1969 sb.append(" failures, ")
1970 sb.append("${rsk} skipped) in ${t}")
1971 return sb.toString()
1974 private static void printResults(allResults) {
1976 // styler from https://stackoverflow.com/a/56139852
1977 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
1980 def failedTests = false
1981 def summaryLines = []
1983 def totalsuccess = 0
1986 def totaltime = TimeCategory.getSeconds(0)
1987 // sort on project name then task name
1988 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
1989 def projectName = it[0]
1990 def taskName = it[1]
1994 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
1995 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
1996 def reportLine = "Report file: ${report}"
1997 def ls = summaryPlain.length()
1998 def lr = reportLine.length()
1999 def m = [ls, lr].max()
2002 def info = [ls, summaryCol, reportLine]
2003 summaryLines.add(info)
2004 failedTests |= result.resultType == TestResult.ResultType.FAILURE
2005 totalcount += result.testCount
2006 totalsuccess += result.successfulTestCount
2007 totalfail += result.failedTestCount
2008 totalskip += result.skippedTestCount
2011 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
2012 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
2013 def tls = totalSummaryPlain.length()
2014 if (tls > maxLength)
2016 def info = [tls, totalSummaryCol, null]
2017 summaryLines.add(info)
2019 def allSummaries = []
2020 for(sInfo : summaryLines) {
2022 def summary = sInfo[1]
2023 def report = sInfo[2]
2025 StringBuilder sb = new StringBuilder()
2026 sb.append("│" + summary + " " * (maxLength - ls) + "│")
2027 if (report != null) {
2028 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
2030 allSummaries += sb.toString()
2033 println "┌${"${"─" * maxLength}"}┐"
2034 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
2035 println "└${"${"─" * maxLength}"}┘"
2037 /* END of test tasks results summary */
2040 task compileLinkCheck(type: JavaCompile) {
2042 classpath = files("${jalviewDir}/${utils_dir}")
2043 destinationDir = file("${jalviewDir}/${utils_dir}")
2044 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
2046 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2047 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2048 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
2049 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
2053 task linkCheck(type: JavaExec) {
2055 dependsOn compileLinkCheck
2057 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
2058 classpath = files("${jalviewDir}/${utils_dir}")
2059 main = "HelpLinksChecker"
2060 workingDir = "${helpBuildDir}"
2061 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
2063 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
2064 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2067 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2071 inputs.dir(helpBuildDir)
2072 outputs.file(helpLinksCheckerOutFile)
2076 // import the pubhtmlhelp target
2077 ant.properties.basedir = "${jalviewDir}"
2078 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
2079 ant.importBuild "${utils_dir}/publishHelp.xml"
2082 task cleanPackageDir(type: Delete) {
2084 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2094 attributes "Main-Class": main_class,
2095 "Permissions": "all-permissions",
2096 "Application-Name": applicationName,
2097 "Codebase": application_codebase,
2098 "Implementation-Version": JALVIEW_VERSION
2101 def outputDir = "${jalviewDir}/${package_dir}"
2102 destinationDirectory = file(outputDir)
2103 archiveFileName = rootProject.name+".jar"
2104 duplicatesStrategy "EXCLUDE"
2111 exclude "**/*.jar.*"
2113 inputs.dir(sourceSets.main.java.outputDir)
2114 sourceSets.main.resources.srcDirs.each{ dir ->
2117 outputs.file("${outputDir}/${archiveFileName}")
2121 task copyJars(type: Copy) {
2122 from fileTree(dir: classesDir, include: "**/*.jar").files
2123 into "${jalviewDir}/${package_dir}"
2127 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2128 task syncJars(type: Sync) {
2130 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2131 into "${jalviewDir}/${package_dir}"
2133 include jar.archiveFileName.getOrNull()
2140 description = "Put all required libraries in dist"
2141 // order of "cleanPackageDir", "copyJars", "jar" important!
2142 jar.mustRunAfter cleanPackageDir
2143 syncJars.mustRunAfter cleanPackageDir
2144 dependsOn cleanPackageDir
2147 outputs.dir("${jalviewDir}/${package_dir}")
2152 dependsOn cleanPackageDir
2158 task launcherJar(type: Jar) {
2161 "Main-Class": shadow_jar_main_class,
2162 "Implementation-Version": JALVIEW_VERSION,
2163 "Application-Name": applicationName
2169 group = "distribution"
2170 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2175 def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
2176 def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
2177 def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
2182 // shadowJar manifest must inheritFrom another Jar task. Can't set attributes here.
2183 inheritFrom(project.tasks.launcherJar.manifest)
2185 // we need to include the groovy-swing Include-Package for it to run in the shadowJar
2187 def jarFileManifests = []
2188 groovyJars.each { jarFile ->
2189 def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
2191 jarFileManifests += mf
2195 from (jarFileManifests) {
2196 eachEntry { details ->
2197 if (!details.key.equals("Import-Package")) {
2205 duplicatesStrategy "INCLUDE"
2207 // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
2208 mainClassName = shadow_jar_main_class
2210 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2214 task getdownImagesCopy() {
2215 inputs.dir getdownImagesDir
2216 outputs.dir getdownImagesBuildDir
2220 from(getdownImagesDir) {
2221 include("*getdown*.png")
2223 into getdownImagesBuildDir
2228 task getdownImagesProcess() {
2229 dependsOn getdownImagesCopy
2232 if (backgroundImageText) {
2233 if (convertBinary == null) {
2234 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2236 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2237 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2239 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2241 executable convertBinary
2244 '-font', getdown_background_image_text_font,
2245 '-fill', getdown_background_image_text_colour,
2246 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2247 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2248 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2257 task getdownImages() {
2258 dependsOn getdownImagesProcess
2261 task getdownWebsiteBuild() {
2262 group = "distribution"
2263 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."
2265 dependsOn getdownImages
2270 def getdownWebsiteResourceFilenames = []
2271 def getdownResourceDir = getdownResourceDir
2272 def getdownResourceFilenames = []
2275 // clean the getdown website and files dir before creating getdown folders
2276 delete getdownAppBaseDir
2277 delete getdownFilesDir
2280 from buildProperties
2281 rename(file(buildProperties).getName(), getdown_build_properties)
2284 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2287 from channelPropsFile
2288 filter(ReplaceTokens,
2292 'SUFFIX': channelSuffix
2295 into getdownAppBaseDir
2297 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2299 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2300 def props = project.properties.sort { it.key }
2301 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2302 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2304 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2305 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2307 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2308 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2310 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2311 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2312 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2313 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2314 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2315 props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
2316 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
2319 props.put("getdown_txt_title", jalview_name)
2320 props.put("getdown_txt_ui.name", applicationName)
2322 // start with appbase
2323 getdownTextLines += "appbase = ${getdownAppBase}"
2324 props.each{ prop, val ->
2325 if (prop.startsWith("getdown_txt_") && val != null) {
2326 if (prop.startsWith("getdown_txt_multi_")) {
2327 def key = prop.substring(18)
2328 val.split(",").each{ v ->
2329 def line = "${key} = ${v}"
2330 getdownTextLines += line
2333 // file values rationalised
2334 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2336 if (val.indexOf('/') == 0) {
2339 } else if (val.indexOf('/') > 0) {
2340 // relative path (relative to jalviewDir)
2341 r = file( "${jalviewDir}/${val}" )
2344 val = "${getdown_resource_dir}/" + r.getName()
2345 getdownWebsiteResourceFilenames += val
2346 getdownResourceFilenames += r.getPath()
2349 if (! prop.startsWith("getdown_txt_resource")) {
2350 def line = prop.substring(12) + " = ${val}"
2351 getdownTextLines += line
2357 getdownWebsiteResourceFilenames.each{ filename ->
2358 getdownTextLines += "resource = ${filename}"
2360 getdownResourceFilenames.each{ filename ->
2363 into getdownResourceDir
2367 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2368 getdownWrapperScripts.each{ script ->
2369 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2373 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2375 getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
2380 fileTree(file(package_dir)).each{ f ->
2381 if (f.isDirectory()) {
2382 def files = fileTree(dir: f, include: ["*"]).getFiles()
2384 } else if (f.exists()) {
2388 def jalviewJar = jar.archiveFileName.getOrNull()
2389 // put jalview.jar first for CLASSPATH and .properties files reasons
2390 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2391 def name = f.getName()
2392 def line = "code = ${getdownAppDistDir}/${name}"
2393 getdownTextLines += line
2400 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2402 if (JAVA_VERSION.equals("11")) {
2403 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2404 j11libFiles.sort().each{f ->
2405 def name = f.getName()
2406 def line = "code = ${getdown_j11lib_dir}/${name}"
2407 getdownTextLines += line
2410 into getdownJ11libDir
2416 // 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.
2417 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2418 getdownTextLines += "resource = ${getdown_launcher_new}"
2419 getdownTextLines += "class = ${main_class}"
2420 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2421 if (getdownSetAppBaseProperty) {
2422 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2423 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2426 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2427 getdownTxt.write(getdownTextLines.join("\n"))
2429 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2430 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2431 launchJvl.write("appbase=${getdownAppBase}")
2433 // files going into the getdown website dir: getdown-launcher.jar
2435 from getdownLauncher
2436 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2437 into getdownAppBaseDir
2440 // files going into the getdown website dir: getdown-launcher(-local).jar
2442 from getdownLauncher
2443 if (file(getdownLauncher).getName() != getdown_launcher) {
2444 rename(file(getdownLauncher).getName(), getdown_launcher)
2446 into getdownAppBaseDir
2449 // files going into the getdown website dir: ./install dir and files
2450 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2453 from getdownLauncher
2454 from "${getdownAppDir}/${getdown_build_properties}"
2455 if (file(getdownLauncher).getName() != getdown_launcher) {
2456 rename(file(getdownLauncher).getName(), getdown_launcher)
2458 into getdownInstallDir
2461 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2463 from getdownInstallDir
2464 into getdownFilesInstallDir
2468 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2472 from getdownLauncher
2473 from "${getdownAppBaseDir}/${getdown_build_properties}"
2474 from "${getdownAppBaseDir}/${channel_props}"
2475 if (file(getdownLauncher).getName() != getdown_launcher) {
2476 rename(file(getdownLauncher).getName(), getdown_launcher)
2478 into getdownFilesDir
2481 // and ./resource (not all downloaded by getdown)
2483 from getdownResourceDir
2484 into "${getdownFilesDir}/${getdown_resource_dir}"
2489 inputs.dir("${jalviewDir}/${package_dir}")
2491 outputs.dir(getdownAppBaseDir)
2492 outputs.dir(getdownFilesDir)
2496 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2497 task getdownDigestDir(type: JavaExec) {
2499 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2501 def digestDirPropertyName = "DIGESTDIR"
2503 classpath = files(getdownLauncher)
2504 def digestDir = findProperty(digestDirPropertyName)
2505 if (digestDir == null) {
2506 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2510 main = "com.threerings.getdown.tools.Digester"
2514 task getdownDigest(type: JavaExec) {
2515 group = "distribution"
2516 description = "Digest the getdown website folder"
2518 dependsOn getdownWebsiteBuild
2521 classpath = files(getdownLauncher)
2523 main = "com.threerings.getdown.tools.Digester"
2524 args getdownAppBaseDir
2525 inputs.dir(getdownAppBaseDir)
2526 outputs.file("${getdownAppBaseDir}/digest2.txt")
2531 group = "distribution"
2532 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2533 dependsOn getdownDigest
2535 if (reportRsyncCommand) {
2536 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2537 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2538 println "LIKELY RSYNC COMMAND:"
2539 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2540 if (RUNRSYNC == "true") {
2542 commandLine "mkdir", "-p", toDir
2545 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2552 task getdownWebsite {
2553 group = "distribution"
2554 description = "A task to create the whole getdown channel website dir including digest file"
2556 dependsOn getdownWebsiteBuild
2557 dependsOn getdownDigest
2560 task getdownArchiveBuild() {
2561 group = "distribution"
2562 description = "Put files in the archive dir to go on the website"
2564 dependsOn getdownWebsiteBuild
2566 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2567 def vDir = "${getdownArchiveDir}/${v}"
2568 getdownFullArchiveDir = "${vDir}/getdown"
2569 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2571 def vAltDir = "alt_${v}"
2572 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2575 // cleanup old "old" dir
2576 delete getdownArchiveDir
2578 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2579 getdownArchiveTxt.getParentFile().mkdirs()
2580 def getdownArchiveTextLines = []
2581 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2585 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2586 into "${getdownFullArchiveDir}/${vAltDir}"
2589 getdownTextLines.each { line ->
2590 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2591 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2592 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2593 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2594 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2595 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2596 // remove the existing resource = resource/ or bin/ lines
2597 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2598 getdownArchiveTextLines += line
2602 // the resource dir -- add these files as resource lines in getdown.txt
2604 from "${archiveImagesDir}"
2605 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2607 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2611 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2613 def vLaunchJvl = file(getdownVersionLaunchJvl)
2614 vLaunchJvl.getParentFile().mkdirs()
2615 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2616 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2617 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2618 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2619 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2620 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2622 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2624 from getdownLauncher
2625 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2626 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2627 from "${getdownAppBaseDir}/${channel_props}"
2628 if (file(getdownLauncher).getName() != getdown_launcher) {
2629 rename(file(getdownLauncher).getName(), getdown_launcher)
2631 into getdownFullArchiveDir
2637 task getdownArchiveDigest(type: JavaExec) {
2638 group = "distribution"
2639 description = "Digest the getdown archive folder"
2641 dependsOn getdownArchiveBuild
2644 classpath = files(getdownLauncher)
2645 args getdownFullArchiveDir
2647 main = "com.threerings.getdown.tools.Digester"
2648 inputs.dir(getdownFullArchiveDir)
2649 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2652 task getdownArchive() {
2653 group = "distribution"
2654 description = "Build the website archive dir with getdown digest"
2656 dependsOn getdownArchiveBuild
2657 dependsOn getdownArchiveDigest
2660 tasks.withType(JavaCompile) {
2661 options.encoding = 'UTF-8'
2667 delete getdownAppBaseDir
2668 delete getdownFilesDir
2669 delete getdownArchiveDir
2675 if (file(install4jHomeDir).exists()) {
2677 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2678 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2679 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2680 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2682 installDir(file(install4jHomeDir))
2684 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2688 task copyInstall4jTemplate {
2689 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2690 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2691 inputs.file(install4jTemplateFile)
2692 inputs.file(install4jFileAssociationsFile)
2693 inputs.property("CHANNEL", { CHANNEL })
2694 outputs.file(install4jConfFile)
2697 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2699 // turn off code signing if no OSX_KEYPASS
2700 if (OSX_KEYPASS == "") {
2701 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2702 codeSigning.'@macEnabled' = "false"
2704 install4jConfigXml.'**'.windows.each { windows ->
2705 windows.'@runPostProcessor' = "false"
2709 // disable install screen for OSX dmg (for 2.11.2.0)
2710 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2711 macosArchive.attributes().remove('executeSetupApp')
2712 macosArchive.attributes().remove('setupAppId')
2715 // turn off checksum creation for LOCAL channel
2716 def e = install4jConfigXml.application[0]
2717 e.'@createChecksums' = string(install4jCheckSums)
2719 // put file association actions where placeholder action is
2720 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2721 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2722 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2723 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2724 def parent = a.parent()
2726 fileAssociationActions.each { faa ->
2729 // don't need to continue in .any loop once replacements have been made
2734 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2735 // NB we're deleting the /other/ one!
2736 // Also remove the examples subdir from non-release versions
2737 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2738 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2739 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2740 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2742 // remove the examples subdir from Full File Set
2743 def files = install4jConfigXml.files[0]
2744 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2745 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2746 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2747 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2748 dirEntry.parent().remove(dirEntry)
2750 install4jConfigXml.'**'.action.any { a ->
2751 if (a.'@customizedId' == customizedIdToDelete) {
2752 def parent = a.parent()
2758 // write install4j file
2759 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2766 delete install4jConfFile
2770 task cleanInstallersDataFiles {
2771 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2772 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2773 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2775 delete installersOutputTxt
2776 delete installersSha256
2777 delete hugoDataJsonFile
2781 task install4jDMGBackgroundImageCopy {
2782 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2783 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2786 from(install4jDMGBackgroundImageDir) {
2787 include(install4jDMGBackgroundImageFile)
2789 into install4jDMGBackgroundImageBuildDir
2794 task install4jDMGBackgroundImageProcess {
2795 dependsOn install4jDMGBackgroundImageCopy
2798 if (backgroundImageText) {
2799 if (convertBinary == null) {
2800 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2802 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2803 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2805 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2807 executable convertBinary
2810 '-font', install4j_background_image_text_font,
2811 '-fill', install4j_background_image_text_colour,
2812 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2813 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2814 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2823 task install4jDMGBackgroundImage {
2824 dependsOn install4jDMGBackgroundImageProcess
2827 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2828 group = "distribution"
2829 description = "Create the install4j installers"
2831 dependsOn copyInstall4jTemplate
2832 dependsOn cleanInstallersDataFiles
2833 dependsOn install4jDMGBackgroundImage
2835 projectFile = install4jConfFile
2837 // create an md5 for the input files to use as version for install4j conf file
2838 def digest = MessageDigest.getInstance("MD5")
2840 (file("${install4jDir}/${install4j_template}").text +
2841 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2842 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2843 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2844 if (filesMd5.length() >= 8) {
2845 filesMd5 = filesMd5.substring(0,8)
2847 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2850 'JALVIEW_NAME': jalview_name,
2851 'JALVIEW_APPLICATION_NAME': applicationName,
2852 'JALVIEW_DIR': "../..",
2853 'OSX_KEYSTORE': OSX_KEYSTORE,
2854 'OSX_APPLEID': OSX_APPLEID,
2855 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2856 'JSIGN_SH': JSIGN_SH,
2857 'JRE_DIR': getdown_app_dir_java,
2858 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2859 'JALVIEW_VERSION': JALVIEW_VERSION,
2860 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2861 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2862 'JAVA_VERSION': JAVA_VERSION,
2863 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2864 'VERSION': JALVIEW_VERSION,
2865 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2866 'BUNDLE_ID': install4jBundleId,
2867 'INTERNAL_ID': install4jInternalId,
2868 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2869 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2870 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2871 'WRAPPER_LINK': getdownWrapperLink,
2872 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2873 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2874 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2875 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2876 'INSTALLER_NAME': install4jInstallerName,
2877 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2878 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2879 'GETDOWN_FILES_DIR': getdown_files_dir,
2880 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2881 'GETDOWN_DIST_DIR': getdownAppDistDir,
2882 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2883 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2884 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2885 'BUILD_DIR': install4jBuildDir,
2886 'APPLICATION_CATEGORIES': install4j_application_categories,
2887 'APPLICATION_FOLDER': install4jApplicationFolder,
2888 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2889 'EXECUTABLE_NAME': install4jExecutableName,
2890 'EXTRA_SCHEME': install4jExtraScheme,
2891 'MAC_ICONS_FILE': install4jMacIconsFile,
2892 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2893 'PNG_ICON_FILE': install4jPngIconFile,
2894 'BACKGROUND': install4jBackground,
2899 'windows': 'WINDOWS',
2903 // these are the bundled OS/architecture VMs needed by install4j
2906 [ "mac", "aarch64" ],
2907 [ "windows", "x64" ],
2909 [ "linux", "aarch64" ]
2911 osArch.forEach { os, arch ->
2912 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)
2913 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2914 // otherwise running `gradle installers` generates a non-useful error:
2915 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2916 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)
2919 //println("INSTALL4J VARIABLES:")
2920 //variables.each{k,v->println("${k}=${v}")}
2922 destination = "${jalviewDir}/${install4jBuildDir}"
2923 buildSelected = true
2925 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2927 disableSigning = true
2928 disableNotarization = true
2932 macKeystorePassword = OSX_KEYPASS
2935 if (OSX_ALTOOLPASS) {
2936 appleIdPassword = OSX_ALTOOLPASS
2937 disableNotarization = false
2939 disableNotarization = true
2943 println("Using projectFile "+projectFile)
2944 if (!disableNotarization) { println("Will notarize OSX App DMG") }
2948 inputs.dir(getdownAppBaseDir)
2949 inputs.file(install4jConfFile)
2950 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2951 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2954 def getDataHash(File myFile) {
2955 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2956 return myFile.exists()
2958 "file" : myFile.getName(),
2959 "filesize" : myFile.length(),
2960 "sha256" : hash.toString()
2965 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2967 "channel" : getdownChannelName,
2968 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2969 "git-commit" : "${gitHash} [${gitBranch}]",
2970 "version" : JALVIEW_VERSION
2972 // install4j installer files
2973 if (installersOutputTxt.exists()) {
2975 installersOutputTxt.readLines().each { def line ->
2976 if (line.startsWith("#")) {
2979 line.replaceAll("\n","")
2980 def vals = line.split("\t")
2981 def filename = vals[3]
2982 def filesize = file(filename).length()
2983 filename = filename.replaceAll(/^.*\//, "")
2984 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2985 idHash."${filename}" = vals[0]
2987 if (install4jCheckSums && installersSha256.exists()) {
2988 installersSha256.readLines().each { def line ->
2989 if (line.startsWith("#")) {
2992 line.replaceAll("\n","")
2993 def vals = line.split(/\s+\*?/)
2994 def filename = vals[1]
2995 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
3001 "JAR": shadowJar.archiveFile, // executable JAR
3002 "JVL": getdownVersionLaunchJvl, // version JVL
3003 "SOURCE": sourceDist.archiveFile // source TGZ
3004 ].each { key, value ->
3005 def file = file(value)
3006 if (file.exists()) {
3007 def fileHash = getDataHash(file)
3008 if (fileHash != null) {
3009 hash."${key}" = fileHash;
3013 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
3016 task staticMakeInstallersJsonFile {
3018 def output = findProperty("i4j_output")
3019 def sha256 = findProperty("i4j_sha256")
3020 def json = findProperty("i4j_json")
3021 if (output == null || sha256 == null || json == null) {
3022 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3024 writeDataJsonFile(file(output), file(sha256), file(json))
3029 dependsOn installerFiles
3035 eclipse().configFile(eclipse_codestyle_file)
3039 task createSourceReleaseProperties(type: WriteProperties) {
3040 group = "distribution"
3041 description = "Create the source RELEASE properties file"
3043 def sourceTarBuildDir = "${buildDir}/sourceTar"
3044 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3045 outputFile (sourceReleasePropertiesFile)
3048 releaseProps.each{ key, val -> property key, val }
3049 property "git.branch", gitBranch
3050 property "git.hash", gitHash
3053 outputs.file(outputFile)
3056 task sourceDist(type: Tar) {
3057 group "distribution"
3058 description "Create a source .tar.gz file for distribution"
3060 dependsOn createBuildProperties
3061 dependsOn convertMdFiles
3062 dependsOn eclipseAllPreferences
3063 dependsOn createSourceReleaseProperties
3066 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3067 archiveFileName = outputFileName
3069 compression Compression.GZIP
3085 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3087 "utils/InstallAnywhere",
3102 "gradle.properties",
3114 ".settings/org.eclipse.buildship.core.prefs",
3115 ".settings/org.eclipse.jdt.core.prefs"
3119 exclude (EXCLUDE_FILES)
3120 include (PROCESS_FILES)
3121 filter(ReplaceTokens,
3125 'Version-Rel': JALVIEW_VERSION,
3126 'Year-Rel': getDate("yyyy")
3131 exclude (EXCLUDE_FILES)
3132 exclude (PROCESS_FILES)
3133 exclude ("appletlib")
3134 exclude ("**/*locales")
3135 exclude ("*locales/**")
3136 exclude ("utils/InstallAnywhere")
3138 exclude (getdown_files_dir)
3139 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3140 //exclude (getdown_website_dir)
3141 //exclude (getdown_archive_dir)
3143 // exluding these as not using jars as modules yet
3144 exclude ("${j11modDir}/**/*.jar")
3147 include(INCLUDE_FILES)
3149 // from (jalviewDir) {
3150 // // explicit includes for stuff that seemed to not get included
3151 // include(fileTree("test/**/*."))
3152 // exclude(EXCLUDE_FILES)
3153 // exclude(PROCESS_FILES)
3156 from(file(buildProperties).getParent()) {
3157 include(file(buildProperties).getName())
3158 rename(file(buildProperties).getName(), "build_properties")
3160 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3164 def sourceTarBuildDir = "${buildDir}/sourceTar"
3165 from(sourceTarBuildDir) {
3166 // this includes the appended RELEASE properties file
3170 task dataInstallersJson {
3172 description "Create the installers-VERSION.json data file for installer files created"
3174 mustRunAfter installers
3175 mustRunAfter shadowJar
3176 mustRunAfter sourceDist
3177 mustRunAfter getdownArchive
3179 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3180 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3182 if (installersOutputTxt.exists()) {
3183 inputs.file(installersOutputTxt)
3185 if (install4jCheckSums && installersSha256.exists()) {
3186 inputs.file(installersSha256)
3189 shadowJar.archiveFile, // executable JAR
3190 getdownVersionLaunchJvl, // version JVL
3191 sourceDist.archiveFile // source TGZ
3192 ].each { fileName ->
3193 if (file(fileName).exists()) {
3194 inputs.file(fileName)
3198 outputs.file(hugoDataJsonFile)
3201 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3207 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3210 dependsOn pubhtmlhelp
3212 inputs.dir("${helpBuildDir}/${help_dir}")
3213 outputs.dir("${buildDir}/distributions/${help_dir}")
3217 task j2sSetHeadlessBuild {
3224 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3226 description "Enable the alternative J2S Config file for headless build"
3228 outputFile = jalviewjsJ2sSettingsFileName
3229 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3230 def j2sProps = new Properties()
3231 if (j2sPropsFile.exists()) {
3233 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3234 j2sProps.load(j2sPropsFileFIS)
3235 j2sPropsFileFIS.close()
3237 j2sProps.each { prop, val ->
3240 } catch (Exception e) {
3241 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3245 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3246 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3251 task jalviewjsSetEclipseWorkspace {
3252 def propKey = "jalviewjs_eclipse_workspace"
3254 if (project.hasProperty(propKey)) {
3255 propVal = project.getProperty(propKey)
3256 if (propVal.startsWith("~/")) {
3257 propVal = System.getProperty("user.home") + propVal.substring(1)
3260 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3261 def propsFile = file(propsFileName)
3262 def eclipseWsDir = propVal
3263 def props = new Properties()
3265 def writeProps = true
3266 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3267 def ins = new FileInputStream(propsFileName)
3270 if (props.getProperty(propKey, null) != null) {
3271 eclipseWsDir = props.getProperty(propKey)
3276 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3277 def tempDir = File.createTempDir()
3278 eclipseWsDir = tempDir.getAbsolutePath()
3281 eclipseWorkspace = file(eclipseWsDir)
3284 // do not run a headless transpile when we claim to be in Eclipse
3286 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3287 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3289 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3293 props.setProperty(propKey, eclipseWsDir)
3294 propsFile.parentFile.mkdirs()
3295 def bytes = new ByteArrayOutputStream()
3296 props.store(bytes, null)
3297 def propertiesString = bytes.toString()
3298 propsFile.text = propertiesString
3304 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3307 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3308 outputs.file(propsFileName)
3309 outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
3313 task jalviewjsEclipsePaths {
3316 def eclipseRoot = jalviewjs_eclipse_root
3317 if (eclipseRoot.startsWith("~/")) {
3318 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3320 if (OperatingSystem.current().isMacOsX()) {
3321 eclipseRoot += "/Eclipse.app"
3322 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3323 eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3324 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3325 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3326 eclipseRoot += "/eclipse"
3328 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3329 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3330 } else { // linux or unix
3331 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3332 eclipseRoot += "/eclipse"
3333 println("eclipseDir exists")
3335 eclipseBinary = "${eclipseRoot}/eclipse"
3336 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3339 eclipseVersion = "4.13" // default
3340 def assumedVersion = true
3341 if (file(eclipseProduct).exists()) {
3342 def fis = new FileInputStream(eclipseProduct)
3343 def props = new Properties()
3345 eclipseVersion = props.getProperty("version")
3347 assumedVersion = false
3350 def propKey = "eclipse_debug"
3351 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3354 // do not run a headless transpile when we claim to be in Eclipse
3356 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3357 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3359 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3362 if (!assumedVersion) {
3363 println("ECLIPSE VERSION=${eclipseVersion}")
3369 task printProperties {
3371 description "Output to console all System.properties"
3373 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3379 dependsOn eclipseProject
3380 dependsOn eclipseClasspath
3381 dependsOn eclipseJdt
3385 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3386 task jalviewjsEclipseCopyDropins(type: Copy) {
3387 dependsOn jalviewjsEclipsePaths
3389 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3390 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3391 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3398 // this eclipse -clean doesn't actually work
3399 task jalviewjsCleanEclipse(type: Exec) {
3400 dependsOn eclipseSetup
3401 dependsOn jalviewjsEclipsePaths
3402 dependsOn jalviewjsEclipseCopyDropins
3404 executable(eclipseBinary)
3405 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3411 def inputString = """exit
3414 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3415 standardInput = inputByteStream
3418 /* not really working yet
3419 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3423 task jalviewjsTransferUnzipSwingJs {
3424 def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3428 from zipTree(file_zip)
3429 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3433 inputs.file file_zip
3434 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3438 task jalviewjsTransferUnzipLib {
3439 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
3442 zipFiles.each { file_zip ->
3444 from zipTree(file_zip)
3445 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3450 inputs.files zipFiles
3451 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3455 task jalviewjsTransferUnzipAllLibs {
3456 dependsOn jalviewjsTransferUnzipSwingJs
3457 dependsOn jalviewjsTransferUnzipLib
3461 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3463 description "Create the alternative j2s file from the j2s.* properties"
3465 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3466 def siteDirProperty = "j2s.site.directory"
3467 def setSiteDir = false
3468 jalviewjsJ2sProps.each { prop, val ->
3470 if (prop == siteDirProperty) {
3471 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3472 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3478 if (!setSiteDir) { // default site location, don't override specifically set property
3479 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3482 outputFile = jalviewjsJ2sAltSettingsFileName
3485 inputs.properties(jalviewjsJ2sProps)
3486 outputs.file(jalviewjsJ2sAltSettingsFileName)
3491 task jalviewjsEclipseSetup {
3492 dependsOn jalviewjsEclipseCopyDropins
3493 dependsOn jalviewjsSetEclipseWorkspace
3494 dependsOn jalviewjsCreateJ2sSettings
3498 task jalviewjsSyncAllLibs (type: Sync) {
3499 dependsOn jalviewjsTransferUnzipAllLibs
3500 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3501 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3502 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3506 def outputFiles = []
3507 rename { filename ->
3508 outputFiles += "${outputDir}/${filename}"
3515 // should this be exclude really ?
3516 duplicatesStrategy "INCLUDE"
3518 outputs.files outputFiles
3519 inputs.files inputFiles
3523 task jalviewjsSyncResources (type: Sync) {
3524 dependsOn buildResources
3526 def inputFiles = fileTree(dir: resourcesBuildDir)
3527 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3531 def outputFiles = []
3532 rename { filename ->
3533 outputFiles += "${outputDir}/${filename}"
3539 outputs.files outputFiles
3540 inputs.files inputFiles
3544 task jalviewjsSyncSiteResources (type: Sync) {
3545 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3546 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3550 def outputFiles = []
3551 rename { filename ->
3552 outputFiles += "${outputDir}/${filename}"
3558 outputs.files outputFiles
3559 inputs.files inputFiles
3563 task jalviewjsSyncBuildProperties (type: Sync) {
3564 dependsOn createBuildProperties
3565 def inputFiles = [file(buildProperties)]
3566 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3570 def outputFiles = []
3571 rename { filename ->
3572 outputFiles += "${outputDir}/${filename}"
3578 outputs.files outputFiles
3579 inputs.files inputFiles
3583 task jalviewjsProjectImport(type: Exec) {
3584 dependsOn eclipseSetup
3585 dependsOn jalviewjsEclipsePaths
3586 dependsOn jalviewjsEclipseSetup
3589 // do not run a headless import when we claim to be in Eclipse
3591 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3592 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3594 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3598 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3599 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3600 executable(eclipseBinary)
3601 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3605 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3607 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3608 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3611 inputs.file("${jalviewDir}/.project")
3612 outputs.upToDateWhen {
3613 file(projdir).exists()
3618 task jalviewjsTranspile(type: Exec) {
3619 dependsOn jalviewjsEclipseSetup
3620 dependsOn jalviewjsProjectImport
3621 dependsOn jalviewjsEclipsePaths
3623 dependsOn jalviewjsEnableAltFileProperty
3627 // do not run a headless transpile when we claim to be in Eclipse
3629 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3630 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3632 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3636 executable(eclipseBinary)
3637 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3641 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3643 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3644 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3650 stdout = new ByteArrayOutputStream()
3651 stderr = new ByteArrayOutputStream()
3653 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3654 def logOutFile = file(logOutFileName)
3655 logOutFile.createNewFile()
3656 logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3657 BINARY: ${eclipseBinary}
3658 VERSION: ${eclipseVersion}
3659 WORKSPACE: ${eclipseWorkspace}
3660 DEBUG: ${eclipseDebug}
3663 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3664 // combine stdout and stderr
3665 def logErrFOS = logOutFOS
3667 if (jalviewjs_j2s_to_console.equals("true")) {
3668 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3669 new org.apache.tools.ant.util.TeeOutputStream(
3673 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3674 new org.apache.tools.ant.util.TeeOutputStream(
3679 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3682 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3689 if (stdout.toString().contains("Error processing ")) {
3690 // j2s did not complete transpile
3691 //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3692 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3693 println("IGNORING TRANSPILE ERRORS")
3694 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3696 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3701 inputs.dir("${jalviewDir}/${sourceDir}")
3702 outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3703 outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3707 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3709 def stdout = new ByteArrayOutputStream()
3710 def stderr = new ByteArrayOutputStream()
3712 def coreFile = file(jsfile)
3714 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3716 logOutFile.createNewFile()
3717 logOutFile.append(msg+"\n")
3719 def coreTop = file(prefixFile)
3720 def coreBottom = file(suffixFile)
3721 coreFile.getParentFile().mkdirs()
3722 coreFile.createNewFile()
3723 coreFile.write( coreTop.getText("UTF-8") )
3727 def t = f.getText("UTF-8")
3728 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3729 coreFile.append( t )
3731 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3733 logOutFile.append(msg+"\n")
3736 coreFile.append( coreBottom.getText("UTF-8") )
3738 msg = "Generating ${zjsfile}"
3740 logOutFile.append(msg+"\n")
3741 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3742 def logErrFOS = logOutFOS
3745 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3746 main = "com.google.javascript.jscomp.CommandLineRunner"
3747 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3748 args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3751 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3753 logOutFile.append(msg+"\n")
3755 if (logOutConsole) {
3756 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3757 new org.apache.tools.ant.util.TeeOutputStream(
3761 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3762 new org.apache.tools.ant.util.TeeOutputStream(
3767 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3770 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3777 logOutFile.append(msg+"\n")
3781 task jalviewjsBuildAllCores {
3783 description "Build the core js lib closures listed in the classlists dir"
3784 dependsOn jalviewjsTranspile
3785 dependsOn jalviewjsTransferUnzipSwingJs
3787 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3788 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3789 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3790 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3791 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3792 def prefixFile = "${jsDir}/core/coretop2.js"
3793 def suffixFile = "${jsDir}/core/corebottom2.js"
3795 inputs.file prefixFile
3796 inputs.file suffixFile
3798 def classlistFiles = []
3799 // add the classlists found int the jalviewjs_classlists_dir
3800 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3802 def name = file.getName() - ".txt"
3809 // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3810 //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3811 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3813 jalviewjsCoreClasslists = []
3815 classlistFiles.each {
3818 def file = hash['file']
3819 if (! file.exists()) {
3820 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3821 return false // this is a "continue" in groovy .each closure
3823 def name = hash['name']
3825 name = file.getName() - ".txt"
3833 def list = fileTree(dir: j2sDir, includes: filelist)
3835 def jsfile = "${outputDir}/core${name}.js"
3836 def zjsfile = "${outputDir}/core${name}.z.js"
3838 jalviewjsCoreClasslists += [
3847 outputs.file(jsfile)
3848 outputs.file(zjsfile)
3851 // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3852 def stevesoftClasslistName = "_stevesoft"
3853 def stevesoftClasslist = [
3854 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3855 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3856 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3857 'name': stevesoftClasslistName
3859 jalviewjsCoreClasslists += stevesoftClasslist
3860 inputs.files(stevesoftClasslist['list'])
3861 outputs.file(stevesoftClasslist['jsfile'])
3862 outputs.file(stevesoftClasslist['zjsfile'])
3865 def allClasslistName = "_all"
3866 def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3867 allJsFiles += fileTree(
3871 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3872 "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3873 "**/org/jmol/export/JSExporter.js"
3876 allJsFiles += fileTree(
3880 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3881 "**/sun/misc/Unsafe.js",
3882 "**/swingjs/jquery/jquery-editable-select.js",
3883 "**/swingjs/jquery/j2sComboBox.js",
3884 "**/sun/misc/FloatingDecimal.js"
3887 def allClasslist = [
3888 'jsfile': "${outputDir}/core${allClasslistName}.js",
3889 'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3891 'name': allClasslistName
3893 // not including this version of "all" core at the moment
3894 //jalviewjsCoreClasslists += allClasslist
3895 inputs.files(allClasslist['list'])
3896 outputs.file(allClasslist['jsfile'])
3897 outputs.file(allClasslist['zjsfile'])
3900 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
3901 logOutFile.getParentFile().mkdirs()
3902 logOutFile.createNewFile()
3903 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
3905 jalviewjsCoreClasslists.each {
3906 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
3913 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
3916 into file(outputFile).getParentFile()
3917 rename { filename ->
3918 if (filename.equals(inputFile.getName())) {
3919 return file(outputFile).getName()
3923 filter(ReplaceTokens,
3927 'MAIN': '"'+main_class+'"',
3929 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
3930 'COREKEY': jalviewjs_core_key,
3931 'CORENAME': coreName
3938 task jalviewjsPublishCoreTemplates {
3939 dependsOn jalviewjsBuildAllCores
3940 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
3941 def inputFile = file(inputFileName)
3942 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3944 def outputFiles = []
3945 jalviewjsCoreClasslists.each { cl ->
3946 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
3947 cl['outputfile'] = outputFile
3948 outputFiles += outputFile
3952 jalviewjsCoreClasslists.each { cl ->
3953 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
3956 inputs.file(inputFile)
3957 outputs.files(outputFiles)
3961 task jalviewjsSyncCore (type: Sync) {
3962 dependsOn jalviewjsBuildAllCores
3963 dependsOn jalviewjsPublishCoreTemplates
3964 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
3965 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3969 def outputFiles = []
3970 rename { filename ->
3971 outputFiles += "${outputDir}/${filename}"
3977 outputs.files outputFiles
3978 inputs.files inputFiles
3982 // this Copy version of TransferSiteJs will delete anything else in the target dir
3983 task jalviewjsCopyTransferSiteJs(type: Copy) {
3984 dependsOn jalviewjsTranspile
3985 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3986 into "${jalviewDir}/${jalviewjsSiteDir}"
3990 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
3991 task jalviewjsSyncTransferSiteJs(type: Sync) {
3992 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3994 into "${jalviewDir}/${jalviewjsSiteDir}"
4001 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
4002 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
4003 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
4004 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
4006 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
4007 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
4008 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
4009 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
4012 task jalviewjsPrepareSite {
4014 description "Prepares the website folder including unzipping files and copying resources"
4015 dependsOn jalviewjsSyncAllLibs
4016 dependsOn jalviewjsSyncResources
4017 dependsOn jalviewjsSyncSiteResources
4018 dependsOn jalviewjsSyncBuildProperties
4019 dependsOn jalviewjsSyncCore
4023 task jalviewjsBuildSite {
4025 description "Builds the whole website including transpiled code"
4026 dependsOn jalviewjsCopyTransferSiteJs
4027 dependsOn jalviewjsPrepareSite
4031 task cleanJalviewjsTransferSite {
4033 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4034 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4035 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4036 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4041 task cleanJalviewjsSite {
4042 dependsOn cleanJalviewjsTransferSite
4044 delete "${jalviewDir}/${jalviewjsSiteDir}"
4049 task jalviewjsSiteTar(type: Tar) {
4051 description "Creates a tar.gz file for the website"
4052 dependsOn jalviewjsBuildSite
4053 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4054 archiveFileName = outputFilename
4056 compression Compression.GZIP
4058 from "${jalviewDir}/${jalviewjsSiteDir}"
4059 into jalviewjs_site_dir // this is inside the tar file
4061 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4065 task jalviewjsServer {
4067 def filename = "jalviewjsTest.html"
4068 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4069 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4074 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4075 factory = f.newInstance()
4076 } catch (ClassNotFoundException e) {
4077 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4079 def port = Integer.valueOf(jalviewjs_server_port)
4084 while(port < start+1000 && !running) {
4086 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4087 jalviewjsServer = factory.start(doc_root, port)
4089 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4090 println("SERVER STARTED with document root ${doc_root}.")
4091 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4092 println("For debug: "+url+"?j2sdebug")
4093 println("For verbose: "+url+"?j2sverbose")
4094 } catch (Exception e) {
4099 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4100 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4101 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4103 jalviewjsCoreClasslists.each { cl ->
4104 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4106 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4108 println("For core ${cl.name}: "+urlcore)
4111 file(htmlFile).text = htmlText
4114 outputs.file(htmlFile)
4115 outputs.upToDateWhen({false})
4119 task cleanJalviewjsAll {
4121 description "Delete all configuration and build artifacts to do with JalviewJS build"
4122 dependsOn cleanJalviewjsSite
4123 dependsOn jalviewjsEclipsePaths
4126 delete "${jalviewDir}/${jalviewjsBuildDir}"
4127 delete "${jalviewDir}/${eclipse_bin_dir}"
4128 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4129 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4131 delete jalviewjsJ2sAltSettingsFileName
4134 outputs.upToDateWhen( { false } )
4138 task jalviewjsIDE_checkJ2sPlugin {
4139 group "00 JalviewJS in Eclipse"
4140 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4143 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4144 def j2sPluginFile = file(j2sPlugin)
4145 def eclipseHome = System.properties["eclipse.home.location"]
4146 if (eclipseHome == null || ! IN_ECLIPSE) {
4147 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4149 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4150 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4151 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4152 eclipseJ2sPluginDirs += altPluginsDir
4154 def foundPlugin = false
4155 def j2sPluginFileName = j2sPluginFile.getName()
4156 def eclipseJ2sPlugin
4157 def eclipseJ2sPluginFile
4158 eclipseJ2sPluginDirs.any { dir ->
4159 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4160 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4161 if (eclipseJ2sPluginFile.exists()) {
4167 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4168 System.err.println(msg)
4169 throw new StopExecutionException(msg)
4172 def digest = MessageDigest.getInstance("MD5")
4174 digest.update(j2sPluginFile.text.bytes)
4175 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4177 digest.update(eclipseJ2sPluginFile.text.bytes)
4178 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4180 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4181 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4182 System.err.println(msg)
4183 throw new StopExecutionException(msg)
4185 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4191 task jalviewjsIDE_copyJ2sPlugin {
4192 group "00 JalviewJS in Eclipse"
4193 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4196 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4197 def j2sPluginFile = file(j2sPlugin)
4198 def eclipseHome = System.properties["eclipse.home.location"]
4199 if (eclipseHome == null || ! IN_ECLIPSE) {
4200 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4202 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4203 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4204 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4205 System.err.println(msg)
4208 eclipseJ2sPluginFile.getParentFile().mkdirs()
4209 into eclipseJ2sPluginFile.getParent()
4215 task jalviewjsIDE_j2sFile {
4216 group "00 JalviewJS in Eclipse"
4217 description "Creates the .j2s file"
4218 dependsOn jalviewjsCreateJ2sSettings
4222 task jalviewjsIDE_SyncCore {
4223 group "00 JalviewJS in Eclipse"
4224 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4225 dependsOn jalviewjsSyncCore
4229 task jalviewjsIDE_SyncSiteAll {
4230 dependsOn jalviewjsSyncAllLibs
4231 dependsOn jalviewjsSyncResources
4232 dependsOn jalviewjsSyncSiteResources
4233 dependsOn jalviewjsSyncBuildProperties
4237 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4240 task jalviewjsIDE_PrepareSite {
4241 group "00 JalviewJS in Eclipse"
4242 description "Sync libs and resources to site dir, but not closure cores"
4244 dependsOn jalviewjsIDE_SyncSiteAll
4245 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4249 task jalviewjsIDE_AssembleSite {
4250 group "00 JalviewJS in Eclipse"
4251 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4252 dependsOn jalviewjsPrepareSite
4256 task jalviewjsIDE_SiteClean {
4257 group "00 JalviewJS in Eclipse"
4258 description "Deletes the Eclipse transpiled site"
4259 dependsOn cleanJalviewjsSite
4263 task jalviewjsIDE_Server {
4264 group "00 JalviewJS in Eclipse"
4265 description "Starts a webserver on localhost to test the website"
4266 dependsOn jalviewjsServer
4270 // buildship runs this at import or gradle refresh
4271 task eclipseSynchronizationTask {
4272 //dependsOn eclipseSetup
4273 dependsOn createBuildProperties
4275 dependsOn jalviewjsIDE_j2sFile
4276 dependsOn jalviewjsIDE_checkJ2sPlugin
4277 dependsOn jalviewjsIDE_PrepareSite
4282 // buildship runs this at build time or project refresh
4283 task eclipseAutoBuildTask {
4284 //dependsOn jalviewjsIDE_checkJ2sPlugin
4285 //dependsOn jalviewjsIDE_PrepareSite
4289 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4290 from file(jalviewjs_stderr_launch)
4291 into jalviewjsSiteDir
4293 inputs.file jalviewjs_stderr_launch
4294 outputs.file jalviewjsStderrLaunchFilename
4297 task cleanJalviewjsChromiumUserDir {
4299 delete jalviewjsChromiumUserDir
4301 outputs.dir jalviewjsChromiumUserDir
4302 // always run when depended on
4303 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4306 task jalviewjsChromiumProfile {
4307 dependsOn cleanJalviewjsChromiumUserDir
4308 mustRunAfter cleanJalviewjsChromiumUserDir
4310 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4313 mkdir jalviewjsChromiumProfileDir
4316 outputs.file firstRun
4319 task jalviewjsLaunchTest {
4321 description "Check JalviewJS opens in a browser"
4322 dependsOn jalviewjsBuildSite
4323 dependsOn jalviewjsCopyStderrLaunchFile
4324 dependsOn jalviewjsChromiumProfile
4326 def macOS = OperatingSystem.current().isMacOsX()
4327 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4328 if (chromiumBinary.startsWith("~/")) {
4329 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4335 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4337 def binary = file(chromiumBinary)
4338 if (!binary.exists()) {
4339 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4341 stdout = new ByteArrayOutputStream()
4342 stderr = new ByteArrayOutputStream()
4345 if (jalviewjs_j2s_to_console.equals("true")) {
4346 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4349 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4357 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4360 "--timeout=${timeoutms}",
4361 "--virtual-time-budget=${timeoutms}",
4362 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4363 "--profile-directory=${jalviewjs_chromium_profile_name}",
4364 "--allow-file-access-from-files",
4365 "--enable-logging=stderr",
4366 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4369 if (true || macOS) {
4370 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4371 Future f1 = executor.submit(
4374 standardOutput = execStdout
4375 errorOutput = execStderr
4376 executable(chromiumBinary)
4378 println "COMMAND: '"+commandLine.join(" ")+"'"
4380 executor.shutdownNow()
4384 def noChangeBytes = 0
4385 def noChangeIterations = 0
4386 executor.scheduleAtFixedRate(
4388 String stderrString = stderr.toString()
4389 // shutdown the task if we have a success string
4390 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4393 executor.shutdownNow()
4395 // if no change in stderr for 10s then also end
4396 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4397 executor.shutdownNow()
4399 if (stderrString.length() == noChangeBytes) {
4400 noChangeIterations++
4402 noChangeBytes = stderrString.length()
4403 noChangeIterations = 0
4406 1, 1, TimeUnit.SECONDS)
4408 executor.schedule(new Runnable(){
4411 executor.shutdownNow()
4413 }, timeoutms, TimeUnit.MILLISECONDS)
4415 executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
4416 executor.shutdownNow()
4423 stderr.toString().eachLine { line ->
4424 if (line.contains(jalviewjs_desktop_init_string)) {
4425 println("Found line '"+line+"'")
4431 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4439 description "Build the JalviewJS site and run the launch test"
4440 dependsOn jalviewjsBuildSite
4441 dependsOn jalviewjsLaunchTest