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
1774 /* separated tests */
1775 task testTask1(type: Test) {
1776 group = "Verification"
1777 description = "Tests that need to be isolated from the main test run"
1780 excludeGroups testng_excluded_groups.split(",")
1782 useDefaultListeners=true
1786 task testTask2(type: Test) {
1787 group = "Verification"
1788 description = "Tests that need to be isolated from the main test run"
1791 excludeGroups testng_excluded_groups.split(",")
1793 useDefaultListeners=true
1796 task testTask3(type: Test) {
1797 group = "Verification"
1798 description = "Tests that need to be isolated from the main test run"
1801 excludeGroups testng_excluded_groups.split(",")
1803 useDefaultListeners=true
1807 /* insert more testTaskNs here -- change N to next digit or other string */
1809 task testTaskN(type: Test) {
1810 group = "Verification"
1811 description = "Tests that need to be isolated from the main test run"
1814 excludeGroups testng_excluded_groups.split(",")
1816 useDefaultListeners=true
1822 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1823 * to summarise test results from all Test tasks
1825 /* START of test tasks results summary */
1826 import groovy.time.TimeCategory
1827 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1828 import org.gradle.api.tasks.testing.logging.TestLogEvent
1829 rootProject.ext.testsResults = [] // Container for tests summaries
1831 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1833 // from original test task
1835 dependsOn cloverClasses
1837 dependsOn testClasses //?
1840 // run main tests first
1841 if (!testTask.name.equals("testTask0"))
1842 testTask.mustRunAfter "testTask0"
1844 testTask.testLogging { logging ->
1845 events TestLogEvent.FAILED
1846 // TestLogEvent.SKIPPED,
1847 // TestLogEvent.STANDARD_OUT,
1848 // TestLogEvent.STANDARD_ERROR
1850 exceptionFormat TestExceptionFormat.FULL
1853 showStackTraces true
1855 showStandardStreams true
1857 info.events = [ TestLogEvent.FAILED ]
1860 if (OperatingSystem.current().isMacOsX()) {
1861 testTask.systemProperty "apple.awt.UIElement", "true"
1862 testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
1866 ignoreFailures = true // Always try to run all tests for all modules
1868 afterSuite { desc, result ->
1870 return // Only summarize results for whole modules
1872 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1874 rootProject.ext.testsResults.add(resultsInfo)
1877 // from original test task
1878 maxHeapSize = "1024m"
1880 workingDir = jalviewDir
1881 def testLaf = project.findProperty("test_laf")
1882 if (testLaf != null) {
1883 println("Setting Test LaF to '${testLaf}'")
1884 systemProperty "laf", testLaf
1886 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1887 if (testHiDPIScale != null) {
1888 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1889 systemProperty "sun.java2d.uiScale", testHiDPIScale
1891 sourceCompatibility = compile_source_compatibility
1892 targetCompatibility = compile_target_compatibility
1893 jvmArgs += additional_compiler_args
1896 // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
1897 // testTasks that include the tests, and exclude all from the others.
1898 // get --test argument
1899 filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
1900 // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
1901 // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
1905 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1910 /* don't fail on no matching tests (so --tests will run across all testTasks) */
1911 testTask.filter.setFailOnNoMatchingTests(false)
1913 /* ensure the "test" task dependsOn all the testTasks */
1914 test.dependsOn testTask
1917 gradle.buildFinished {
1918 def allResults = rootProject.ext.testsResults
1920 if (!allResults.isEmpty()) {
1921 printResults allResults
1922 allResults.each {r ->
1923 if (r[2].resultType == TestResult.ResultType.FAILURE)
1924 throw new GradleException("Failed tests!")
1929 private static String colString(styler, col, colour, text) {
1930 return col?"${styler[colour](text)}":text
1933 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1934 def colour = 'black'
1942 case TestResult.ResultType.SUCCESS:
1945 case TestResult.ResultType.FAILURE:
1953 StringBuilder sb = new StringBuilder()
1957 sb.append(" results: ")
1958 sb.append(colString(s, col && !nocol, colour, text))
1960 sb.append("${rc} tests, ")
1961 sb.append(colString(s, col && rs > 0, 'green', rs))
1962 sb.append(" successes, ")
1963 sb.append(colString(s, col && rf > 0, 'red', rf))
1964 sb.append(" failures, ")
1965 sb.append("${rsk} skipped) in ${t}")
1966 return sb.toString()
1969 private static void printResults(allResults) {
1971 // styler from https://stackoverflow.com/a/56139852
1972 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
1975 def failedTests = false
1976 def summaryLines = []
1978 def totalsuccess = 0
1981 def totaltime = TimeCategory.getSeconds(0)
1982 // sort on project name then task name
1983 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
1984 def projectName = it[0]
1985 def taskName = it[1]
1989 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
1990 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
1991 def reportLine = "Report file: ${report}"
1992 def ls = summaryPlain.length()
1993 def lr = reportLine.length()
1994 def m = [ls, lr].max()
1997 def info = [ls, summaryCol, reportLine]
1998 summaryLines.add(info)
1999 failedTests |= result.resultType == TestResult.ResultType.FAILURE
2000 totalcount += result.testCount
2001 totalsuccess += result.successfulTestCount
2002 totalfail += result.failedTestCount
2003 totalskip += result.skippedTestCount
2006 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
2007 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
2008 def tls = totalSummaryPlain.length()
2009 if (tls > maxLength)
2011 def info = [tls, totalSummaryCol, null]
2012 summaryLines.add(info)
2014 def allSummaries = []
2015 for(sInfo : summaryLines) {
2017 def summary = sInfo[1]
2018 def report = sInfo[2]
2020 StringBuilder sb = new StringBuilder()
2021 sb.append("│" + summary + " " * (maxLength - ls) + "│")
2022 if (report != null) {
2023 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
2025 allSummaries += sb.toString()
2028 println "┌${"${"─" * maxLength}"}┐"
2029 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
2030 println "└${"${"─" * maxLength}"}┘"
2032 /* END of test tasks results summary */
2035 task compileLinkCheck(type: JavaCompile) {
2037 classpath = files("${jalviewDir}/${utils_dir}")
2038 destinationDir = file("${jalviewDir}/${utils_dir}")
2039 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
2041 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2042 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2043 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
2044 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
2048 task linkCheck(type: JavaExec) {
2050 dependsOn compileLinkCheck
2052 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
2053 classpath = files("${jalviewDir}/${utils_dir}")
2054 main = "HelpLinksChecker"
2055 workingDir = "${helpBuildDir}"
2056 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
2058 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
2059 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2062 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2066 inputs.dir(helpBuildDir)
2067 outputs.file(helpLinksCheckerOutFile)
2071 // import the pubhtmlhelp target
2072 ant.properties.basedir = "${jalviewDir}"
2073 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
2074 ant.importBuild "${utils_dir}/publishHelp.xml"
2077 task cleanPackageDir(type: Delete) {
2079 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2089 attributes "Main-Class": main_class,
2090 "Permissions": "all-permissions",
2091 "Application-Name": applicationName,
2092 "Codebase": application_codebase,
2093 "Implementation-Version": JALVIEW_VERSION
2096 def outputDir = "${jalviewDir}/${package_dir}"
2097 destinationDirectory = file(outputDir)
2098 archiveFileName = rootProject.name+".jar"
2099 duplicatesStrategy "EXCLUDE"
2106 exclude "**/*.jar.*"
2108 inputs.dir(sourceSets.main.java.outputDir)
2109 sourceSets.main.resources.srcDirs.each{ dir ->
2112 outputs.file("${outputDir}/${archiveFileName}")
2116 task copyJars(type: Copy) {
2117 from fileTree(dir: classesDir, include: "**/*.jar").files
2118 into "${jalviewDir}/${package_dir}"
2122 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2123 task syncJars(type: Sync) {
2125 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2126 into "${jalviewDir}/${package_dir}"
2128 include jar.archiveFileName.getOrNull()
2135 description = "Put all required libraries in dist"
2136 // order of "cleanPackageDir", "copyJars", "jar" important!
2137 jar.mustRunAfter cleanPackageDir
2138 syncJars.mustRunAfter cleanPackageDir
2139 dependsOn cleanPackageDir
2142 outputs.dir("${jalviewDir}/${package_dir}")
2147 dependsOn cleanPackageDir
2154 group = "distribution"
2155 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2160 def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
2161 def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
2162 def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
2166 // we need to include the groovy-swing Include-Package for it to run in the shadowJar
2168 def jarFileManifests = []
2169 groovyJars.each { jarFile ->
2170 def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
2172 jarFileManifests += mf
2177 attributes "Implementation-Version": JALVIEW_VERSION, "Application-Name": applicationName
2178 from (jarFileManifests) {
2179 eachEntry { details ->
2180 if (!details.key.equals("Import-Package")) {
2188 duplicatesStrategy "INCLUDE"
2190 mainClassName = shadow_jar_main_class
2192 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2196 task getdownImagesCopy() {
2197 inputs.dir getdownImagesDir
2198 outputs.dir getdownImagesBuildDir
2202 from(getdownImagesDir) {
2203 include("*getdown*.png")
2205 into getdownImagesBuildDir
2210 task getdownImagesProcess() {
2211 dependsOn getdownImagesCopy
2214 if (backgroundImageText) {
2215 if (convertBinary == null) {
2216 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2218 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2219 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2221 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2223 executable convertBinary
2226 '-font', getdown_background_image_text_font,
2227 '-fill', getdown_background_image_text_colour,
2228 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2229 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2230 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2239 task getdownImages() {
2240 dependsOn getdownImagesProcess
2243 task getdownWebsiteBuild() {
2244 group = "distribution"
2245 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."
2247 dependsOn getdownImages
2252 def getdownWebsiteResourceFilenames = []
2253 def getdownResourceDir = getdownResourceDir
2254 def getdownResourceFilenames = []
2257 // clean the getdown website and files dir before creating getdown folders
2258 delete getdownAppBaseDir
2259 delete getdownFilesDir
2262 from buildProperties
2263 rename(file(buildProperties).getName(), getdown_build_properties)
2266 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2269 from channelPropsFile
2270 filter(ReplaceTokens,
2274 'SUFFIX': channelSuffix
2277 into getdownAppBaseDir
2279 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2281 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2282 def props = project.properties.sort { it.key }
2283 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2284 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2286 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2287 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2289 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2290 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2292 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2293 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2294 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2295 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2296 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2297 props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
2298 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
2301 props.put("getdown_txt_title", jalview_name)
2302 props.put("getdown_txt_ui.name", applicationName)
2304 // start with appbase
2305 getdownTextLines += "appbase = ${getdownAppBase}"
2306 props.each{ prop, val ->
2307 if (prop.startsWith("getdown_txt_") && val != null) {
2308 if (prop.startsWith("getdown_txt_multi_")) {
2309 def key = prop.substring(18)
2310 val.split(",").each{ v ->
2311 def line = "${key} = ${v}"
2312 getdownTextLines += line
2315 // file values rationalised
2316 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2318 if (val.indexOf('/') == 0) {
2321 } else if (val.indexOf('/') > 0) {
2322 // relative path (relative to jalviewDir)
2323 r = file( "${jalviewDir}/${val}" )
2326 val = "${getdown_resource_dir}/" + r.getName()
2327 getdownWebsiteResourceFilenames += val
2328 getdownResourceFilenames += r.getPath()
2331 if (! prop.startsWith("getdown_txt_resource")) {
2332 def line = prop.substring(12) + " = ${val}"
2333 getdownTextLines += line
2339 getdownWebsiteResourceFilenames.each{ filename ->
2340 getdownTextLines += "resource = ${filename}"
2342 getdownResourceFilenames.each{ filename ->
2345 into getdownResourceDir
2349 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2350 getdownWrapperScripts.each{ script ->
2351 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2355 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2357 getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
2362 fileTree(file(package_dir)).each{ f ->
2363 if (f.isDirectory()) {
2364 def files = fileTree(dir: f, include: ["*"]).getFiles()
2366 } else if (f.exists()) {
2370 def jalviewJar = jar.archiveFileName.getOrNull()
2371 // put jalview.jar first for CLASSPATH and .properties files reasons
2372 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2373 def name = f.getName()
2374 def line = "code = ${getdownAppDistDir}/${name}"
2375 getdownTextLines += line
2382 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2384 if (JAVA_VERSION.equals("11")) {
2385 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2386 j11libFiles.sort().each{f ->
2387 def name = f.getName()
2388 def line = "code = ${getdown_j11lib_dir}/${name}"
2389 getdownTextLines += line
2392 into getdownJ11libDir
2398 // 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.
2399 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2400 getdownTextLines += "resource = ${getdown_launcher_new}"
2401 getdownTextLines += "class = ${main_class}"
2402 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2403 if (getdownSetAppBaseProperty) {
2404 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2405 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2408 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2409 getdownTxt.write(getdownTextLines.join("\n"))
2411 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2412 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2413 launchJvl.write("appbase=${getdownAppBase}")
2415 // files going into the getdown website dir: getdown-launcher.jar
2417 from getdownLauncher
2418 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2419 into getdownAppBaseDir
2422 // files going into the getdown website dir: getdown-launcher(-local).jar
2424 from getdownLauncher
2425 if (file(getdownLauncher).getName() != getdown_launcher) {
2426 rename(file(getdownLauncher).getName(), getdown_launcher)
2428 into getdownAppBaseDir
2431 // files going into the getdown website dir: ./install dir and files
2432 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2435 from getdownLauncher
2436 from "${getdownAppDir}/${getdown_build_properties}"
2437 if (file(getdownLauncher).getName() != getdown_launcher) {
2438 rename(file(getdownLauncher).getName(), getdown_launcher)
2440 into getdownInstallDir
2443 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2445 from getdownInstallDir
2446 into getdownFilesInstallDir
2450 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2454 from getdownLauncher
2455 from "${getdownAppBaseDir}/${getdown_build_properties}"
2456 from "${getdownAppBaseDir}/${channel_props}"
2457 if (file(getdownLauncher).getName() != getdown_launcher) {
2458 rename(file(getdownLauncher).getName(), getdown_launcher)
2460 into getdownFilesDir
2463 // and ./resource (not all downloaded by getdown)
2465 from getdownResourceDir
2466 into "${getdownFilesDir}/${getdown_resource_dir}"
2471 inputs.dir("${jalviewDir}/${package_dir}")
2473 outputs.dir(getdownAppBaseDir)
2474 outputs.dir(getdownFilesDir)
2478 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2479 task getdownDigestDir(type: JavaExec) {
2481 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2483 def digestDirPropertyName = "DIGESTDIR"
2485 classpath = files(getdownLauncher)
2486 def digestDir = findProperty(digestDirPropertyName)
2487 if (digestDir == null) {
2488 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2492 main = "com.threerings.getdown.tools.Digester"
2496 task getdownDigest(type: JavaExec) {
2497 group = "distribution"
2498 description = "Digest the getdown website folder"
2500 dependsOn getdownWebsiteBuild
2503 classpath = files(getdownLauncher)
2505 main = "com.threerings.getdown.tools.Digester"
2506 args getdownAppBaseDir
2507 inputs.dir(getdownAppBaseDir)
2508 outputs.file("${getdownAppBaseDir}/digest2.txt")
2513 group = "distribution"
2514 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2515 dependsOn getdownDigest
2517 if (reportRsyncCommand) {
2518 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2519 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2520 println "LIKELY RSYNC COMMAND:"
2521 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2522 if (RUNRSYNC == "true") {
2524 commandLine "mkdir", "-p", toDir
2527 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2534 task getdownWebsite {
2535 group = "distribution"
2536 description = "A task to create the whole getdown channel website dir including digest file"
2538 dependsOn getdownWebsiteBuild
2539 dependsOn getdownDigest
2542 task getdownArchiveBuild() {
2543 group = "distribution"
2544 description = "Put files in the archive dir to go on the website"
2546 dependsOn getdownWebsiteBuild
2548 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2549 def vDir = "${getdownArchiveDir}/${v}"
2550 getdownFullArchiveDir = "${vDir}/getdown"
2551 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2553 def vAltDir = "alt_${v}"
2554 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2557 // cleanup old "old" dir
2558 delete getdownArchiveDir
2560 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2561 getdownArchiveTxt.getParentFile().mkdirs()
2562 def getdownArchiveTextLines = []
2563 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2567 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2568 into "${getdownFullArchiveDir}/${vAltDir}"
2571 getdownTextLines.each { line ->
2572 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2573 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2574 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2575 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2576 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2577 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2578 // remove the existing resource = resource/ or bin/ lines
2579 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2580 getdownArchiveTextLines += line
2584 // the resource dir -- add these files as resource lines in getdown.txt
2586 from "${archiveImagesDir}"
2587 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2589 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2593 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2595 def vLaunchJvl = file(getdownVersionLaunchJvl)
2596 vLaunchJvl.getParentFile().mkdirs()
2597 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2598 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2599 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2600 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2601 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2602 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2604 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2606 from getdownLauncher
2607 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2608 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2609 from "${getdownAppBaseDir}/${channel_props}"
2610 if (file(getdownLauncher).getName() != getdown_launcher) {
2611 rename(file(getdownLauncher).getName(), getdown_launcher)
2613 into getdownFullArchiveDir
2619 task getdownArchiveDigest(type: JavaExec) {
2620 group = "distribution"
2621 description = "Digest the getdown archive folder"
2623 dependsOn getdownArchiveBuild
2626 classpath = files(getdownLauncher)
2627 args getdownFullArchiveDir
2629 main = "com.threerings.getdown.tools.Digester"
2630 inputs.dir(getdownFullArchiveDir)
2631 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2634 task getdownArchive() {
2635 group = "distribution"
2636 description = "Build the website archive dir with getdown digest"
2638 dependsOn getdownArchiveBuild
2639 dependsOn getdownArchiveDigest
2642 tasks.withType(JavaCompile) {
2643 options.encoding = 'UTF-8'
2649 delete getdownAppBaseDir
2650 delete getdownFilesDir
2651 delete getdownArchiveDir
2657 if (file(install4jHomeDir).exists()) {
2659 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2660 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2661 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2662 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2664 installDir(file(install4jHomeDir))
2666 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2670 task copyInstall4jTemplate {
2671 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2672 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2673 inputs.file(install4jTemplateFile)
2674 inputs.file(install4jFileAssociationsFile)
2675 inputs.property("CHANNEL", { CHANNEL })
2676 outputs.file(install4jConfFile)
2679 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2681 // turn off code signing if no OSX_KEYPASS
2682 if (OSX_KEYPASS == "") {
2683 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2684 codeSigning.'@macEnabled' = "false"
2686 install4jConfigXml.'**'.windows.each { windows ->
2687 windows.'@runPostProcessor' = "false"
2691 // disable install screen for OSX dmg (for 2.11.2.0)
2692 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2693 macosArchive.attributes().remove('executeSetupApp')
2694 macosArchive.attributes().remove('setupAppId')
2697 // turn off checksum creation for LOCAL channel
2698 def e = install4jConfigXml.application[0]
2699 e.'@createChecksums' = string(install4jCheckSums)
2701 // put file association actions where placeholder action is
2702 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2703 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2704 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2705 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2706 def parent = a.parent()
2708 fileAssociationActions.each { faa ->
2711 // don't need to continue in .any loop once replacements have been made
2716 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2717 // NB we're deleting the /other/ one!
2718 // Also remove the examples subdir from non-release versions
2719 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2720 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2721 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2722 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2724 // remove the examples subdir from Full File Set
2725 def files = install4jConfigXml.files[0]
2726 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2727 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2728 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2729 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2730 dirEntry.parent().remove(dirEntry)
2732 install4jConfigXml.'**'.action.any { a ->
2733 if (a.'@customizedId' == customizedIdToDelete) {
2734 def parent = a.parent()
2740 // write install4j file
2741 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2748 delete install4jConfFile
2752 task cleanInstallersDataFiles {
2753 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2754 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2755 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2757 delete installersOutputTxt
2758 delete installersSha256
2759 delete hugoDataJsonFile
2763 task install4jDMGBackgroundImageCopy {
2764 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2765 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2768 from(install4jDMGBackgroundImageDir) {
2769 include(install4jDMGBackgroundImageFile)
2771 into install4jDMGBackgroundImageBuildDir
2776 task install4jDMGBackgroundImageProcess {
2777 dependsOn install4jDMGBackgroundImageCopy
2780 if (backgroundImageText) {
2781 if (convertBinary == null) {
2782 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2784 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2785 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2787 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2789 executable convertBinary
2792 '-font', install4j_background_image_text_font,
2793 '-fill', install4j_background_image_text_colour,
2794 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2795 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2796 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2805 task install4jDMGBackgroundImage {
2806 dependsOn install4jDMGBackgroundImageProcess
2809 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2810 group = "distribution"
2811 description = "Create the install4j installers"
2813 dependsOn copyInstall4jTemplate
2814 dependsOn cleanInstallersDataFiles
2815 dependsOn install4jDMGBackgroundImage
2817 projectFile = install4jConfFile
2819 // create an md5 for the input files to use as version for install4j conf file
2820 def digest = MessageDigest.getInstance("MD5")
2822 (file("${install4jDir}/${install4j_template}").text +
2823 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2824 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2825 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2826 if (filesMd5.length() >= 8) {
2827 filesMd5 = filesMd5.substring(0,8)
2829 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2832 'JALVIEW_NAME': jalview_name,
2833 'JALVIEW_APPLICATION_NAME': applicationName,
2834 'JALVIEW_DIR': "../..",
2835 'OSX_KEYSTORE': OSX_KEYSTORE,
2836 'OSX_APPLEID': OSX_APPLEID,
2837 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2838 'JSIGN_SH': JSIGN_SH,
2839 'JRE_DIR': getdown_app_dir_java,
2840 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2841 'JALVIEW_VERSION': JALVIEW_VERSION,
2842 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2843 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2844 'JAVA_VERSION': JAVA_VERSION,
2845 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2846 'VERSION': JALVIEW_VERSION,
2847 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2848 'BUNDLE_ID': install4jBundleId,
2849 'INTERNAL_ID': install4jInternalId,
2850 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2851 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2852 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2853 'WRAPPER_LINK': getdownWrapperLink,
2854 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2855 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2856 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2857 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2858 'INSTALLER_NAME': install4jInstallerName,
2859 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2860 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2861 'GETDOWN_FILES_DIR': getdown_files_dir,
2862 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2863 'GETDOWN_DIST_DIR': getdownAppDistDir,
2864 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2865 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2866 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2867 'BUILD_DIR': install4jBuildDir,
2868 'APPLICATION_CATEGORIES': install4j_application_categories,
2869 'APPLICATION_FOLDER': install4jApplicationFolder,
2870 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2871 'EXECUTABLE_NAME': install4jExecutableName,
2872 'EXTRA_SCHEME': install4jExtraScheme,
2873 'MAC_ICONS_FILE': install4jMacIconsFile,
2874 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2875 'PNG_ICON_FILE': install4jPngIconFile,
2876 'BACKGROUND': install4jBackground,
2881 'windows': 'WINDOWS',
2885 // these are the bundled OS/architecture VMs needed by install4j
2888 [ "mac", "aarch64" ],
2889 [ "windows", "x64" ],
2891 [ "linux", "aarch64" ]
2893 osArch.forEach { os, arch ->
2894 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)
2895 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2896 // otherwise running `gradle installers` generates a non-useful error:
2897 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2898 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)
2901 //println("INSTALL4J VARIABLES:")
2902 //variables.each{k,v->println("${k}=${v}")}
2904 destination = "${jalviewDir}/${install4jBuildDir}"
2905 buildSelected = true
2907 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2909 disableSigning = true
2910 disableNotarization = true
2914 macKeystorePassword = OSX_KEYPASS
2917 if (OSX_ALTOOLPASS) {
2918 appleIdPassword = OSX_ALTOOLPASS
2919 disableNotarization = false
2921 disableNotarization = true
2925 println("Using projectFile "+projectFile)
2926 if (!disableNotarization) { println("Will notarize OSX App DMG") }
2930 inputs.dir(getdownAppBaseDir)
2931 inputs.file(install4jConfFile)
2932 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2933 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2936 def getDataHash(File myFile) {
2937 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2938 return myFile.exists()
2940 "file" : myFile.getName(),
2941 "filesize" : myFile.length(),
2942 "sha256" : hash.toString()
2947 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2949 "channel" : getdownChannelName,
2950 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2951 "git-commit" : "${gitHash} [${gitBranch}]",
2952 "version" : JALVIEW_VERSION
2954 // install4j installer files
2955 if (installersOutputTxt.exists()) {
2957 installersOutputTxt.readLines().each { def line ->
2958 if (line.startsWith("#")) {
2961 line.replaceAll("\n","")
2962 def vals = line.split("\t")
2963 def filename = vals[3]
2964 def filesize = file(filename).length()
2965 filename = filename.replaceAll(/^.*\//, "")
2966 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2967 idHash."${filename}" = vals[0]
2969 if (install4jCheckSums && installersSha256.exists()) {
2970 installersSha256.readLines().each { def line ->
2971 if (line.startsWith("#")) {
2974 line.replaceAll("\n","")
2975 def vals = line.split(/\s+\*?/)
2976 def filename = vals[1]
2977 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
2983 "JAR": shadowJar.archiveFile, // executable JAR
2984 "JVL": getdownVersionLaunchJvl, // version JVL
2985 "SOURCE": sourceDist.archiveFile // source TGZ
2986 ].each { key, value ->
2987 def file = file(value)
2988 if (file.exists()) {
2989 def fileHash = getDataHash(file)
2990 if (fileHash != null) {
2991 hash."${key}" = fileHash;
2995 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
2998 task staticMakeInstallersJsonFile {
3000 def output = findProperty("i4j_output")
3001 def sha256 = findProperty("i4j_sha256")
3002 def json = findProperty("i4j_json")
3003 if (output == null || sha256 == null || json == null) {
3004 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3006 writeDataJsonFile(file(output), file(sha256), file(json))
3011 dependsOn installerFiles
3017 eclipse().configFile(eclipse_codestyle_file)
3021 task createSourceReleaseProperties(type: WriteProperties) {
3022 group = "distribution"
3023 description = "Create the source RELEASE properties file"
3025 def sourceTarBuildDir = "${buildDir}/sourceTar"
3026 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3027 outputFile (sourceReleasePropertiesFile)
3030 releaseProps.each{ key, val -> property key, val }
3031 property "git.branch", gitBranch
3032 property "git.hash", gitHash
3035 outputs.file(outputFile)
3038 task sourceDist(type: Tar) {
3039 group "distribution"
3040 description "Create a source .tar.gz file for distribution"
3042 dependsOn createBuildProperties
3043 dependsOn convertMdFiles
3044 dependsOn eclipseAllPreferences
3045 dependsOn createSourceReleaseProperties
3048 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3049 archiveFileName = outputFileName
3051 compression Compression.GZIP
3067 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3069 "utils/InstallAnywhere",
3084 "gradle.properties",
3096 ".settings/org.eclipse.buildship.core.prefs",
3097 ".settings/org.eclipse.jdt.core.prefs"
3101 exclude (EXCLUDE_FILES)
3102 include (PROCESS_FILES)
3103 filter(ReplaceTokens,
3107 'Version-Rel': JALVIEW_VERSION,
3108 'Year-Rel': getDate("yyyy")
3113 exclude (EXCLUDE_FILES)
3114 exclude (PROCESS_FILES)
3115 exclude ("appletlib")
3116 exclude ("**/*locales")
3117 exclude ("*locales/**")
3118 exclude ("utils/InstallAnywhere")
3120 exclude (getdown_files_dir)
3121 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3122 //exclude (getdown_website_dir)
3123 //exclude (getdown_archive_dir)
3125 // exluding these as not using jars as modules yet
3126 exclude ("${j11modDir}/**/*.jar")
3129 include(INCLUDE_FILES)
3131 // from (jalviewDir) {
3132 // // explicit includes for stuff that seemed to not get included
3133 // include(fileTree("test/**/*."))
3134 // exclude(EXCLUDE_FILES)
3135 // exclude(PROCESS_FILES)
3138 from(file(buildProperties).getParent()) {
3139 include(file(buildProperties).getName())
3140 rename(file(buildProperties).getName(), "build_properties")
3142 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3146 def sourceTarBuildDir = "${buildDir}/sourceTar"
3147 from(sourceTarBuildDir) {
3148 // this includes the appended RELEASE properties file
3152 task dataInstallersJson {
3154 description "Create the installers-VERSION.json data file for installer files created"
3156 mustRunAfter installers
3157 mustRunAfter shadowJar
3158 mustRunAfter sourceDist
3159 mustRunAfter getdownArchive
3161 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3162 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3164 if (installersOutputTxt.exists()) {
3165 inputs.file(installersOutputTxt)
3167 if (install4jCheckSums && installersSha256.exists()) {
3168 inputs.file(installersSha256)
3171 shadowJar.archiveFile, // executable JAR
3172 getdownVersionLaunchJvl, // version JVL
3173 sourceDist.archiveFile // source TGZ
3174 ].each { fileName ->
3175 if (file(fileName).exists()) {
3176 inputs.file(fileName)
3180 outputs.file(hugoDataJsonFile)
3183 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3189 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3192 dependsOn pubhtmlhelp
3194 inputs.dir("${helpBuildDir}/${help_dir}")
3195 outputs.dir("${buildDir}/distributions/${help_dir}")
3199 task j2sSetHeadlessBuild {
3206 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3208 description "Enable the alternative J2S Config file for headless build"
3210 outputFile = jalviewjsJ2sSettingsFileName
3211 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3212 def j2sProps = new Properties()
3213 if (j2sPropsFile.exists()) {
3215 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3216 j2sProps.load(j2sPropsFileFIS)
3217 j2sPropsFileFIS.close()
3219 j2sProps.each { prop, val ->
3222 } catch (Exception e) {
3223 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3227 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3228 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3233 task jalviewjsSetEclipseWorkspace {
3234 def propKey = "jalviewjs_eclipse_workspace"
3236 if (project.hasProperty(propKey)) {
3237 propVal = project.getProperty(propKey)
3238 if (propVal.startsWith("~/")) {
3239 propVal = System.getProperty("user.home") + propVal.substring(1)
3242 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3243 def propsFile = file(propsFileName)
3244 def eclipseWsDir = propVal
3245 def props = new Properties()
3247 def writeProps = true
3248 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3249 def ins = new FileInputStream(propsFileName)
3252 if (props.getProperty(propKey, null) != null) {
3253 eclipseWsDir = props.getProperty(propKey)
3258 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3259 def tempDir = File.createTempDir()
3260 eclipseWsDir = tempDir.getAbsolutePath()
3263 eclipseWorkspace = file(eclipseWsDir)
3266 // do not run a headless transpile when we claim to be in Eclipse
3268 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3269 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3271 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3275 props.setProperty(propKey, eclipseWsDir)
3276 propsFile.parentFile.mkdirs()
3277 def bytes = new ByteArrayOutputStream()
3278 props.store(bytes, null)
3279 def propertiesString = bytes.toString()
3280 propsFile.text = propertiesString
3286 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3289 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3290 outputs.file(propsFileName)
3291 outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
3295 task jalviewjsEclipsePaths {
3298 def eclipseRoot = jalviewjs_eclipse_root
3299 if (eclipseRoot.startsWith("~/")) {
3300 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3302 if (OperatingSystem.current().isMacOsX()) {
3303 eclipseRoot += "/Eclipse.app"
3304 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3305 eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3306 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3307 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3308 eclipseRoot += "/eclipse"
3310 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3311 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3312 } else { // linux or unix
3313 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3314 eclipseRoot += "/eclipse"
3315 println("eclipseDir exists")
3317 eclipseBinary = "${eclipseRoot}/eclipse"
3318 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3321 eclipseVersion = "4.13" // default
3322 def assumedVersion = true
3323 if (file(eclipseProduct).exists()) {
3324 def fis = new FileInputStream(eclipseProduct)
3325 def props = new Properties()
3327 eclipseVersion = props.getProperty("version")
3329 assumedVersion = false
3332 def propKey = "eclipse_debug"
3333 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3336 // do not run a headless transpile when we claim to be in Eclipse
3338 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3339 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3341 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3344 if (!assumedVersion) {
3345 println("ECLIPSE VERSION=${eclipseVersion}")
3351 task printProperties {
3353 description "Output to console all System.properties"
3355 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3361 dependsOn eclipseProject
3362 dependsOn eclipseClasspath
3363 dependsOn eclipseJdt
3367 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3368 task jalviewjsEclipseCopyDropins(type: Copy) {
3369 dependsOn jalviewjsEclipsePaths
3371 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3372 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3373 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3380 // this eclipse -clean doesn't actually work
3381 task jalviewjsCleanEclipse(type: Exec) {
3382 dependsOn eclipseSetup
3383 dependsOn jalviewjsEclipsePaths
3384 dependsOn jalviewjsEclipseCopyDropins
3386 executable(eclipseBinary)
3387 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3393 def inputString = """exit
3396 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3397 standardInput = inputByteStream
3400 /* not really working yet
3401 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3405 task jalviewjsTransferUnzipSwingJs {
3406 def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3410 from zipTree(file_zip)
3411 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3415 inputs.file file_zip
3416 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3420 task jalviewjsTransferUnzipLib {
3421 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
3424 zipFiles.each { file_zip ->
3426 from zipTree(file_zip)
3427 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3432 inputs.files zipFiles
3433 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3437 task jalviewjsTransferUnzipAllLibs {
3438 dependsOn jalviewjsTransferUnzipSwingJs
3439 dependsOn jalviewjsTransferUnzipLib
3443 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3445 description "Create the alternative j2s file from the j2s.* properties"
3447 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3448 def siteDirProperty = "j2s.site.directory"
3449 def setSiteDir = false
3450 jalviewjsJ2sProps.each { prop, val ->
3452 if (prop == siteDirProperty) {
3453 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3454 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3460 if (!setSiteDir) { // default site location, don't override specifically set property
3461 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3464 outputFile = jalviewjsJ2sAltSettingsFileName
3467 inputs.properties(jalviewjsJ2sProps)
3468 outputs.file(jalviewjsJ2sAltSettingsFileName)
3473 task jalviewjsEclipseSetup {
3474 dependsOn jalviewjsEclipseCopyDropins
3475 dependsOn jalviewjsSetEclipseWorkspace
3476 dependsOn jalviewjsCreateJ2sSettings
3480 task jalviewjsSyncAllLibs (type: Sync) {
3481 dependsOn jalviewjsTransferUnzipAllLibs
3482 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3483 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3484 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3488 def outputFiles = []
3489 rename { filename ->
3490 outputFiles += "${outputDir}/${filename}"
3497 // should this be exclude really ?
3498 duplicatesStrategy "INCLUDE"
3500 outputs.files outputFiles
3501 inputs.files inputFiles
3505 task jalviewjsSyncResources (type: Sync) {
3506 dependsOn buildResources
3508 def inputFiles = fileTree(dir: resourcesBuildDir)
3509 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3513 def outputFiles = []
3514 rename { filename ->
3515 outputFiles += "${outputDir}/${filename}"
3521 outputs.files outputFiles
3522 inputs.files inputFiles
3526 task jalviewjsSyncSiteResources (type: Sync) {
3527 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3528 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3532 def outputFiles = []
3533 rename { filename ->
3534 outputFiles += "${outputDir}/${filename}"
3540 outputs.files outputFiles
3541 inputs.files inputFiles
3545 task jalviewjsSyncBuildProperties (type: Sync) {
3546 dependsOn createBuildProperties
3547 def inputFiles = [file(buildProperties)]
3548 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3552 def outputFiles = []
3553 rename { filename ->
3554 outputFiles += "${outputDir}/${filename}"
3560 outputs.files outputFiles
3561 inputs.files inputFiles
3565 task jalviewjsProjectImport(type: Exec) {
3566 dependsOn eclipseSetup
3567 dependsOn jalviewjsEclipsePaths
3568 dependsOn jalviewjsEclipseSetup
3571 // do not run a headless import when we claim to be in Eclipse
3573 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3574 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3576 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3580 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3581 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3582 executable(eclipseBinary)
3583 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3587 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3589 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3590 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3593 inputs.file("${jalviewDir}/.project")
3594 outputs.upToDateWhen {
3595 file(projdir).exists()
3600 task jalviewjsTranspile(type: Exec) {
3601 dependsOn jalviewjsEclipseSetup
3602 dependsOn jalviewjsProjectImport
3603 dependsOn jalviewjsEclipsePaths
3605 dependsOn jalviewjsEnableAltFileProperty
3609 // do not run a headless transpile when we claim to be in Eclipse
3611 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3612 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3614 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3618 executable(eclipseBinary)
3619 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3623 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3625 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3626 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3632 stdout = new ByteArrayOutputStream()
3633 stderr = new ByteArrayOutputStream()
3635 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3636 def logOutFile = file(logOutFileName)
3637 logOutFile.createNewFile()
3638 logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3639 BINARY: ${eclipseBinary}
3640 VERSION: ${eclipseVersion}
3641 WORKSPACE: ${eclipseWorkspace}
3642 DEBUG: ${eclipseDebug}
3645 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3646 // combine stdout and stderr
3647 def logErrFOS = logOutFOS
3649 if (jalviewjs_j2s_to_console.equals("true")) {
3650 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3651 new org.apache.tools.ant.util.TeeOutputStream(
3655 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3656 new org.apache.tools.ant.util.TeeOutputStream(
3661 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3664 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3671 if (stdout.toString().contains("Error processing ")) {
3672 // j2s did not complete transpile
3673 //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3674 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3675 println("IGNORING TRANSPILE ERRORS")
3676 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3678 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3683 inputs.dir("${jalviewDir}/${sourceDir}")
3684 outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3685 outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3689 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3691 def stdout = new ByteArrayOutputStream()
3692 def stderr = new ByteArrayOutputStream()
3694 def coreFile = file(jsfile)
3696 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3698 logOutFile.createNewFile()
3699 logOutFile.append(msg+"\n")
3701 def coreTop = file(prefixFile)
3702 def coreBottom = file(suffixFile)
3703 coreFile.getParentFile().mkdirs()
3704 coreFile.createNewFile()
3705 coreFile.write( coreTop.getText("UTF-8") )
3709 def t = f.getText("UTF-8")
3710 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3711 coreFile.append( t )
3713 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3715 logOutFile.append(msg+"\n")
3718 coreFile.append( coreBottom.getText("UTF-8") )
3720 msg = "Generating ${zjsfile}"
3722 logOutFile.append(msg+"\n")
3723 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3724 def logErrFOS = logOutFOS
3727 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3728 main = "com.google.javascript.jscomp.CommandLineRunner"
3729 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3730 args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3733 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3735 logOutFile.append(msg+"\n")
3737 if (logOutConsole) {
3738 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3739 new org.apache.tools.ant.util.TeeOutputStream(
3743 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3744 new org.apache.tools.ant.util.TeeOutputStream(
3749 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3752 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3759 logOutFile.append(msg+"\n")
3763 task jalviewjsBuildAllCores {
3765 description "Build the core js lib closures listed in the classlists dir"
3766 dependsOn jalviewjsTranspile
3767 dependsOn jalviewjsTransferUnzipSwingJs
3769 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3770 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3771 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3772 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3773 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3774 def prefixFile = "${jsDir}/core/coretop2.js"
3775 def suffixFile = "${jsDir}/core/corebottom2.js"
3777 inputs.file prefixFile
3778 inputs.file suffixFile
3780 def classlistFiles = []
3781 // add the classlists found int the jalviewjs_classlists_dir
3782 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3784 def name = file.getName() - ".txt"
3791 // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3792 //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3793 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3795 jalviewjsCoreClasslists = []
3797 classlistFiles.each {
3800 def file = hash['file']
3801 if (! file.exists()) {
3802 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3803 return false // this is a "continue" in groovy .each closure
3805 def name = hash['name']
3807 name = file.getName() - ".txt"
3815 def list = fileTree(dir: j2sDir, includes: filelist)
3817 def jsfile = "${outputDir}/core${name}.js"
3818 def zjsfile = "${outputDir}/core${name}.z.js"
3820 jalviewjsCoreClasslists += [
3829 outputs.file(jsfile)
3830 outputs.file(zjsfile)
3833 // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3834 def stevesoftClasslistName = "_stevesoft"
3835 def stevesoftClasslist = [
3836 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3837 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3838 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3839 'name': stevesoftClasslistName
3841 jalviewjsCoreClasslists += stevesoftClasslist
3842 inputs.files(stevesoftClasslist['list'])
3843 outputs.file(stevesoftClasslist['jsfile'])
3844 outputs.file(stevesoftClasslist['zjsfile'])
3847 def allClasslistName = "_all"
3848 def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3849 allJsFiles += fileTree(
3853 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3854 "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3855 "**/org/jmol/export/JSExporter.js"
3858 allJsFiles += fileTree(
3862 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3863 "**/sun/misc/Unsafe.js",
3864 "**/swingjs/jquery/jquery-editable-select.js",
3865 "**/swingjs/jquery/j2sComboBox.js",
3866 "**/sun/misc/FloatingDecimal.js"
3869 def allClasslist = [
3870 'jsfile': "${outputDir}/core${allClasslistName}.js",
3871 'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3873 'name': allClasslistName
3875 // not including this version of "all" core at the moment
3876 //jalviewjsCoreClasslists += allClasslist
3877 inputs.files(allClasslist['list'])
3878 outputs.file(allClasslist['jsfile'])
3879 outputs.file(allClasslist['zjsfile'])
3882 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
3883 logOutFile.getParentFile().mkdirs()
3884 logOutFile.createNewFile()
3885 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
3887 jalviewjsCoreClasslists.each {
3888 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
3895 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
3898 into file(outputFile).getParentFile()
3899 rename { filename ->
3900 if (filename.equals(inputFile.getName())) {
3901 return file(outputFile).getName()
3905 filter(ReplaceTokens,
3909 'MAIN': '"'+main_class+'"',
3911 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
3912 'COREKEY': jalviewjs_core_key,
3913 'CORENAME': coreName
3920 task jalviewjsPublishCoreTemplates {
3921 dependsOn jalviewjsBuildAllCores
3922 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
3923 def inputFile = file(inputFileName)
3924 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
3926 def outputFiles = []
3927 jalviewjsCoreClasslists.each { cl ->
3928 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
3929 cl['outputfile'] = outputFile
3930 outputFiles += outputFile
3934 jalviewjsCoreClasslists.each { cl ->
3935 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
3938 inputs.file(inputFile)
3939 outputs.files(outputFiles)
3943 task jalviewjsSyncCore (type: Sync) {
3944 dependsOn jalviewjsBuildAllCores
3945 dependsOn jalviewjsPublishCoreTemplates
3946 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
3947 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3951 def outputFiles = []
3952 rename { filename ->
3953 outputFiles += "${outputDir}/${filename}"
3959 outputs.files outputFiles
3960 inputs.files inputFiles
3964 // this Copy version of TransferSiteJs will delete anything else in the target dir
3965 task jalviewjsCopyTransferSiteJs(type: Copy) {
3966 dependsOn jalviewjsTranspile
3967 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3968 into "${jalviewDir}/${jalviewjsSiteDir}"
3972 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
3973 task jalviewjsSyncTransferSiteJs(type: Sync) {
3974 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
3976 into "${jalviewDir}/${jalviewjsSiteDir}"
3983 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
3984 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
3985 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
3986 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
3988 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
3989 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
3990 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
3991 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
3994 task jalviewjsPrepareSite {
3996 description "Prepares the website folder including unzipping files and copying resources"
3997 dependsOn jalviewjsSyncAllLibs
3998 dependsOn jalviewjsSyncResources
3999 dependsOn jalviewjsSyncSiteResources
4000 dependsOn jalviewjsSyncBuildProperties
4001 dependsOn jalviewjsSyncCore
4005 task jalviewjsBuildSite {
4007 description "Builds the whole website including transpiled code"
4008 dependsOn jalviewjsCopyTransferSiteJs
4009 dependsOn jalviewjsPrepareSite
4013 task cleanJalviewjsTransferSite {
4015 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4016 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4017 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4018 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4023 task cleanJalviewjsSite {
4024 dependsOn cleanJalviewjsTransferSite
4026 delete "${jalviewDir}/${jalviewjsSiteDir}"
4031 task jalviewjsSiteTar(type: Tar) {
4033 description "Creates a tar.gz file for the website"
4034 dependsOn jalviewjsBuildSite
4035 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4036 archiveFileName = outputFilename
4038 compression Compression.GZIP
4040 from "${jalviewDir}/${jalviewjsSiteDir}"
4041 into jalviewjs_site_dir // this is inside the tar file
4043 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4047 task jalviewjsServer {
4049 def filename = "jalviewjsTest.html"
4050 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4051 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4056 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4057 factory = f.newInstance()
4058 } catch (ClassNotFoundException e) {
4059 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4061 def port = Integer.valueOf(jalviewjs_server_port)
4066 while(port < start+1000 && !running) {
4068 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4069 jalviewjsServer = factory.start(doc_root, port)
4071 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4072 println("SERVER STARTED with document root ${doc_root}.")
4073 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4074 println("For debug: "+url+"?j2sdebug")
4075 println("For verbose: "+url+"?j2sverbose")
4076 } catch (Exception e) {
4081 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4082 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4083 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4085 jalviewjsCoreClasslists.each { cl ->
4086 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4088 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4090 println("For core ${cl.name}: "+urlcore)
4093 file(htmlFile).text = htmlText
4096 outputs.file(htmlFile)
4097 outputs.upToDateWhen({false})
4101 task cleanJalviewjsAll {
4103 description "Delete all configuration and build artifacts to do with JalviewJS build"
4104 dependsOn cleanJalviewjsSite
4105 dependsOn jalviewjsEclipsePaths
4108 delete "${jalviewDir}/${jalviewjsBuildDir}"
4109 delete "${jalviewDir}/${eclipse_bin_dir}"
4110 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4111 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4113 delete jalviewjsJ2sAltSettingsFileName
4116 outputs.upToDateWhen( { false } )
4120 task jalviewjsIDE_checkJ2sPlugin {
4121 group "00 JalviewJS in Eclipse"
4122 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4125 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4126 def j2sPluginFile = file(j2sPlugin)
4127 def eclipseHome = System.properties["eclipse.home.location"]
4128 if (eclipseHome == null || ! IN_ECLIPSE) {
4129 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4131 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4132 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4133 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4134 eclipseJ2sPluginDirs += altPluginsDir
4136 def foundPlugin = false
4137 def j2sPluginFileName = j2sPluginFile.getName()
4138 def eclipseJ2sPlugin
4139 def eclipseJ2sPluginFile
4140 eclipseJ2sPluginDirs.any { dir ->
4141 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4142 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4143 if (eclipseJ2sPluginFile.exists()) {
4149 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4150 System.err.println(msg)
4151 throw new StopExecutionException(msg)
4154 def digest = MessageDigest.getInstance("MD5")
4156 digest.update(j2sPluginFile.text.bytes)
4157 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4159 digest.update(eclipseJ2sPluginFile.text.bytes)
4160 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4162 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4163 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4164 System.err.println(msg)
4165 throw new StopExecutionException(msg)
4167 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4173 task jalviewjsIDE_copyJ2sPlugin {
4174 group "00 JalviewJS in Eclipse"
4175 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4178 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4179 def j2sPluginFile = file(j2sPlugin)
4180 def eclipseHome = System.properties["eclipse.home.location"]
4181 if (eclipseHome == null || ! IN_ECLIPSE) {
4182 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4184 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4185 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4186 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4187 System.err.println(msg)
4190 eclipseJ2sPluginFile.getParentFile().mkdirs()
4191 into eclipseJ2sPluginFile.getParent()
4197 task jalviewjsIDE_j2sFile {
4198 group "00 JalviewJS in Eclipse"
4199 description "Creates the .j2s file"
4200 dependsOn jalviewjsCreateJ2sSettings
4204 task jalviewjsIDE_SyncCore {
4205 group "00 JalviewJS in Eclipse"
4206 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4207 dependsOn jalviewjsSyncCore
4211 task jalviewjsIDE_SyncSiteAll {
4212 dependsOn jalviewjsSyncAllLibs
4213 dependsOn jalviewjsSyncResources
4214 dependsOn jalviewjsSyncSiteResources
4215 dependsOn jalviewjsSyncBuildProperties
4219 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4222 task jalviewjsIDE_PrepareSite {
4223 group "00 JalviewJS in Eclipse"
4224 description "Sync libs and resources to site dir, but not closure cores"
4226 dependsOn jalviewjsIDE_SyncSiteAll
4227 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4231 task jalviewjsIDE_AssembleSite {
4232 group "00 JalviewJS in Eclipse"
4233 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4234 dependsOn jalviewjsPrepareSite
4238 task jalviewjsIDE_SiteClean {
4239 group "00 JalviewJS in Eclipse"
4240 description "Deletes the Eclipse transpiled site"
4241 dependsOn cleanJalviewjsSite
4245 task jalviewjsIDE_Server {
4246 group "00 JalviewJS in Eclipse"
4247 description "Starts a webserver on localhost to test the website"
4248 dependsOn jalviewjsServer
4252 // buildship runs this at import or gradle refresh
4253 task eclipseSynchronizationTask {
4254 //dependsOn eclipseSetup
4255 dependsOn createBuildProperties
4257 dependsOn jalviewjsIDE_j2sFile
4258 dependsOn jalviewjsIDE_checkJ2sPlugin
4259 dependsOn jalviewjsIDE_PrepareSite
4264 // buildship runs this at build time or project refresh
4265 task eclipseAutoBuildTask {
4266 //dependsOn jalviewjsIDE_checkJ2sPlugin
4267 //dependsOn jalviewjsIDE_PrepareSite
4271 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4272 from file(jalviewjs_stderr_launch)
4273 into jalviewjsSiteDir
4275 inputs.file jalviewjs_stderr_launch
4276 outputs.file jalviewjsStderrLaunchFilename
4279 task cleanJalviewjsChromiumUserDir {
4281 delete jalviewjsChromiumUserDir
4283 outputs.dir jalviewjsChromiumUserDir
4284 // always run when depended on
4285 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4288 task jalviewjsChromiumProfile {
4289 dependsOn cleanJalviewjsChromiumUserDir
4290 mustRunAfter cleanJalviewjsChromiumUserDir
4292 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4295 mkdir jalviewjsChromiumProfileDir
4298 outputs.file firstRun
4301 task jalviewjsLaunchTest {
4303 description "Check JalviewJS opens in a browser"
4304 dependsOn jalviewjsBuildSite
4305 dependsOn jalviewjsCopyStderrLaunchFile
4306 dependsOn jalviewjsChromiumProfile
4308 def macOS = OperatingSystem.current().isMacOsX()
4309 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4310 if (chromiumBinary.startsWith("~/")) {
4311 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4317 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4319 def binary = file(chromiumBinary)
4320 if (!binary.exists()) {
4321 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4323 stdout = new ByteArrayOutputStream()
4324 stderr = new ByteArrayOutputStream()
4327 if (jalviewjs_j2s_to_console.equals("true")) {
4328 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4331 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4339 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4342 "--timeout=${timeoutms}",
4343 "--virtual-time-budget=${timeoutms}",
4344 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4345 "--profile-directory=${jalviewjs_chromium_profile_name}",
4346 "--allow-file-access-from-files",
4347 "--enable-logging=stderr",
4348 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4351 if (true || macOS) {
4352 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4353 Future f1 = executor.submit(
4356 standardOutput = execStdout
4357 errorOutput = execStderr
4358 executable(chromiumBinary)
4360 println "COMMAND: '"+commandLine.join(" ")+"'"
4362 executor.shutdownNow()
4366 def noChangeBytes = 0
4367 def noChangeIterations = 0
4368 executor.scheduleAtFixedRate(
4370 String stderrString = stderr.toString()
4371 // shutdown the task if we have a success string
4372 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4375 executor.shutdownNow()
4377 // if no change in stderr for 10s then also end
4378 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4379 executor.shutdownNow()
4381 if (stderrString.length() == noChangeBytes) {
4382 noChangeIterations++
4384 noChangeBytes = stderrString.length()
4385 noChangeIterations = 0
4388 1, 1, TimeUnit.SECONDS)
4390 executor.schedule(new Runnable(){
4393 executor.shutdownNow()
4395 }, timeoutms, TimeUnit.MILLISECONDS)
4397 executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
4398 executor.shutdownNow()
4405 stderr.toString().eachLine { line ->
4406 if (line.contains(jalviewjs_desktop_init_string)) {
4407 println("Found line '"+line+"'")
4413 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4421 description "Build the JalviewJS site and run the launch test"
4422 dependsOn jalviewjsBuildSite
4423 dependsOn jalviewjsLaunchTest