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 jalviewjsTransferSiteMergeDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_merge")
567 jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
568 jalviewjsJalviewCoreHtmlFile = string("")
569 jalviewjsJalviewCoreName = string(jalviewjs_core_name)
570 jalviewjsCoreClasslists = []
571 jalviewjsJalviewTemplateName = string(jalviewjs_name)
572 jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
573 jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
574 jalviewjsJ2sProps = null
575 jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
576 jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
578 eclipseWorkspace = null
579 eclipseBinary = string("")
580 eclipseVersion = string("")
581 eclipseProductVersion = string("")
584 jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
585 jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}"
595 outputDir = file(classesDir)
599 srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
602 compileClasspath = files(sourceSets.main.java.outputDir)
603 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
605 runtimeClasspath = compileClasspath
606 runtimeClasspath += files(sourceSets.main.resources.srcDirs)
611 srcDirs cloverInstrDir
612 outputDir = cloverClassesDir
616 srcDirs = sourceSets.main.resources.srcDirs
619 compileClasspath = files( sourceSets.clover.java.outputDir )
620 //compileClasspath += files( testClassesDir )
621 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
622 compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
623 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
625 runtimeClasspath = compileClasspath
630 srcDirs testSourceDir
631 outputDir = file(testClassesDir)
635 srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
638 compileClasspath = files( sourceSets.test.java.outputDir )
639 compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
640 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
642 runtimeClasspath = compileClasspath
643 runtimeClasspath += files(sourceSets.test.resources.srcDirs)
649 // eclipse project and settings files creation, also used by buildship
652 name = eclipse_project_name
654 natures 'org.eclipse.jdt.core.javanature',
655 'org.eclipse.jdt.groovy.core.groovyNature',
656 'org.eclipse.buildship.core.gradleprojectnature'
658 buildCommand 'org.eclipse.jdt.core.javabuilder'
659 buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
663 //defaultOutputDir = sourceSets.main.java.outputDir
664 configurations.each{ c->
665 if (c.isCanBeResolved()) {
666 minusConfigurations += [c]
670 plusConfigurations = [ ]
674 def removeTheseToo = []
675 HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
676 cp.entries.each { entry ->
677 // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
678 // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
679 // we add the resources and help/help dirs in as libs afterwards (see below)
680 if (entry.kind == 'src') {
681 if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
682 removeTheseToo += entry
684 alreadyAddedSrcPath.putAt(entry.path, true)
689 cp.entries.removeAll(removeTheseToo)
691 //cp.entries += new Output("${eclipse_bin_dir}/main")
692 if (file(helpParentDir).isDirectory()) {
693 cp.entries += new Library(fileReference(helpParentDir))
695 if (file(resourceDir).isDirectory()) {
696 cp.entries += new Library(fileReference(resourceDir))
699 HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
701 sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
702 //don't want to add outputDir as eclipse is using its own output dir in bin/main
703 if (it.isDirectory() || ! it.exists()) {
704 // don't add dirs to classpath, especially if they don't exist
705 return false // groovy "continue" in .any closure
707 def itPath = it.toString()
708 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
709 // make relative path
710 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
712 if (alreadyAddedLibPath.get(itPath)) {
713 //println("Not adding duplicate entry "+itPath)
715 //println("Adding entry "+itPath)
716 cp.entries += new Library(fileReference(itPath))
717 alreadyAddedLibPath.put(itPath, true)
721 sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
722 //no longer want to add outputDir as eclipse is using its own output dir in bin/main
723 if (it.isDirectory() || ! it.exists()) {
724 // don't add dirs to classpath
725 return false // groovy "continue" in .any closure
728 def itPath = it.toString()
729 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
730 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
732 if (alreadyAddedLibPath.get(itPath)) {
735 def lib = new Library(fileReference(itPath))
736 lib.entryAttributes["test"] = "true"
738 alreadyAddedLibPath.put(itPath, true)
746 containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
751 // for the IDE, use java 11 compatibility
752 sourceCompatibility = compile_source_compatibility
753 targetCompatibility = compile_target_compatibility
754 javaRuntimeName = eclipseJavaRuntimeName
756 // add in jalview project specific properties/preferences into eclipse core preferences
758 withProperties { props ->
759 def jalview_prefs = new Properties()
760 def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
761 jalview_prefs.load(ins)
763 jalview_prefs.forEach { t, v ->
764 if (props.getAt(t) == null) {
768 // codestyle file -- overrides previous formatter prefs
769 def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
770 if (csFile.exists()) {
771 XmlParser parser = new XmlParser()
772 def profiles = parser.parse(csFile)
773 def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
774 if (profile != null) {
775 profile.'setting'.each { s ->
777 def value = s.'@value'
778 if (id != null && value != null) {
779 props.putAt(id, value)
790 // Don't want these to be activated if in headless build
791 synchronizationTasks "eclipseSynchronizationTask"
792 //autoBuildTasks "eclipseAutoBuildTask"
798 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
799 // Class to allow updating arbitrary properties files
800 class PropertiesFile extends PropertiesPersistableConfigurationObject {
801 public PropertiesFile(PropertiesTransformer t) { super(t); }
802 @Override protected void load(Properties properties) { }
803 @Override protected void store(Properties properties) { }
804 @Override protected String getDefaultResourceName() { return ""; }
805 // This is necessary, because PropertiesPersistableConfigurationObject fails
806 // if no default properties file exists.
807 @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
810 // Task to update arbitrary properties files (set outputFile)
811 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
812 private final PropertiesFileContentMerger file;
813 public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
814 protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
815 protected void configure(PropertiesFile props) {
816 file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
818 public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
821 task eclipseUIPreferences(type: PropertiesFileTask) {
822 description = "Generate Eclipse additional settings"
823 def filename = "org.eclipse.jdt.ui.prefs"
824 outputFile = "$projectDir/.settings/${filename}" as File
827 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
832 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
833 description = "Generate Eclipse additional settings"
834 def filename = "org.eclipse.jdt.groovy.core.prefs"
835 outputFile = "$projectDir/.settings/${filename}" as File
838 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
843 task eclipseAllPreferences {
845 dependsOn eclipseUIPreferences
846 dependsOn eclipseGroovyCorePreferences
849 eclipseUIPreferences.mustRunAfter eclipseJdt
850 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
852 /* end of eclipse preferences hack */
860 delete cloverBuildDir
861 delete cloverReportDir
866 task cloverInstrJava(type: JavaExec) {
867 group = "Verification"
868 description = "Create clover instrumented source java files"
870 dependsOn cleanClover
872 inputs.files(sourceSets.main.allJava)
873 outputs.dir(cloverInstrDir)
875 //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
876 classpath = sourceSets.clover.compileClasspath
877 main = "com.atlassian.clover.CloverInstr"
885 cloverInstrDir.getPath(),
887 def srcFiles = sourceSets.main.allJava.files
890 { file -> file.absolutePath }
893 args argsList.toArray()
896 delete cloverInstrDir
897 println("Clover: About to instrument "+srcFiles.size() +" files")
902 task cloverInstrTests(type: JavaExec) {
903 group = "Verification"
904 description = "Create clover instrumented source test files"
906 dependsOn cleanClover
908 inputs.files(testDir)
909 outputs.dir(cloverTestInstrDir)
911 classpath = sourceSets.clover.compileClasspath
912 main = "com.atlassian.clover.CloverInstr"
922 cloverTestInstrDir.getPath(),
924 args argsList.toArray()
927 delete cloverTestInstrDir
928 println("Clover: About to instrument test files")
934 group = "Verification"
935 description = "Create clover instrumented all source files"
937 dependsOn cloverInstrJava
938 dependsOn cloverInstrTests
942 cloverClasses.dependsOn cloverInstr
945 task cloverConsoleReport(type: JavaExec) {
946 group = "Verification"
947 description = "Creates clover console report"
950 file(cloverDb).exists()
953 inputs.dir cloverClassesDir
955 classpath = sourceSets.clover.runtimeClasspath
956 main = "com.atlassian.clover.reporters.console.ConsoleReporter"
958 if (cloverreport_mem.length() > 0) {
959 maxHeapSize = cloverreport_mem
961 if (cloverreport_jvmargs.length() > 0) {
962 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
972 args argsList.toArray()
976 task cloverHtmlReport(type: JavaExec) {
977 group = "Verification"
978 description = "Creates clover HTML report"
981 file(cloverDb).exists()
984 def cloverHtmlDir = cloverReportDir
985 inputs.dir cloverClassesDir
986 outputs.dir cloverHtmlDir
988 classpath = sourceSets.clover.runtimeClasspath
989 main = "com.atlassian.clover.reporters.html.HtmlReporter"
991 if (cloverreport_mem.length() > 0) {
992 maxHeapSize = cloverreport_mem
994 if (cloverreport_jvmargs.length() > 0) {
995 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1006 if (cloverreport_html_options.length() > 0) {
1007 argsList += cloverreport_html_options.split(" ")
1010 args argsList.toArray()
1014 task cloverXmlReport(type: JavaExec) {
1015 group = "Verification"
1016 description = "Creates clover XML report"
1019 file(cloverDb).exists()
1022 def cloverXmlFile = "${cloverReportDir}/clover.xml"
1023 inputs.dir cloverClassesDir
1024 outputs.file cloverXmlFile
1026 classpath = sourceSets.clover.runtimeClasspath
1027 main = "com.atlassian.clover.reporters.xml.XMLReporter"
1029 if (cloverreport_mem.length() > 0) {
1030 maxHeapSize = cloverreport_mem
1032 if (cloverreport_jvmargs.length() > 0) {
1033 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1044 if (cloverreport_xml_options.length() > 0) {
1045 argsList += cloverreport_xml_options.split(" ")
1048 args argsList.toArray()
1053 group = "Verification"
1054 description = "Creates clover reports"
1056 dependsOn cloverXmlReport
1057 dependsOn cloverHtmlReport
1064 sourceCompatibility = compile_source_compatibility
1065 targetCompatibility = compile_target_compatibility
1066 options.compilerArgs += additional_compiler_args
1067 print ("Setting target compatibility to "+targetCompatibility+"\n")
1069 //classpath += configurations.cloverRuntime
1075 // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1076 sourceCompatibility = compile_source_compatibility
1077 targetCompatibility = compile_target_compatibility
1078 options.compilerArgs += additional_compiler_args
1079 options.encoding = "UTF-8"
1081 print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1088 sourceCompatibility = compile_source_compatibility
1089 targetCompatibility = compile_target_compatibility
1090 options.compilerArgs += additional_compiler_args
1092 print ("Setting target compatibility to "+targetCompatibility+"\n")
1099 delete sourceSets.main.java.outputDir
1105 dependsOn cleanClover
1107 delete sourceSets.test.java.outputDir
1112 // format is a string like date.format("dd MMMM yyyy")
1113 def getDate(format) {
1114 return date.format(format)
1118 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1119 MutableDataSet options = new MutableDataSet()
1121 def extensions = new ArrayList<>()
1122 extensions.add(AnchorLinkExtension.create())
1123 extensions.add(AutolinkExtension.create())
1124 extensions.add(StrikethroughExtension.create())
1125 extensions.add(TaskListExtension.create())
1126 extensions.add(TablesExtension.create())
1127 extensions.add(TocExtension.create())
1129 options.set(Parser.EXTENSIONS, extensions)
1131 // set GFM table parsing options
1132 options.set(TablesExtension.WITH_CAPTION, false)
1133 options.set(TablesExtension.COLUMN_SPANS, false)
1134 options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1135 options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1136 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1137 options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1138 options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1140 options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1141 options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1142 options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1143 options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1145 Parser parser = Parser.builder(options).build()
1146 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1148 mdFiles.each { mdFile ->
1149 // add table of contents
1150 def mdText = "[TOC]\n"+mdFile.text
1152 // grab the first top-level title
1154 def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1155 def matcher = mdText =~ titleRegex
1156 if (matcher.size() > 0) {
1157 // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1158 title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1160 // or use the filename if none found
1161 if (title == null) {
1162 title = mdFile.getName()
1165 Node document = parser.parse(mdText)
1166 String htmlBody = renderer.render(document)
1167 def htmlText = '''<html>
1168 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1169 <html xmlns="http://www.w3.org/1999/xhtml">
1171 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1172 <meta http-equiv="Content-Style-Type" content="text/css" />
1173 <meta name="generator" content="flexmark" />
1175 htmlText += ((title != null) ? " <title>${title}</title>" : '' )
1177 <style type="text/css">code{white-space: pre;}</style>
1179 htmlText += ((cssFile != null) ? cssFile.text : '')
1180 htmlText += '''</head>
1183 htmlText += htmlBody
1189 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1190 def htmlFile = file(htmlFilePath)
1191 println("Creating ${htmlFilePath}")
1192 htmlFile.text = htmlText
1197 task copyDocs(type: Copy) {
1198 def inputDir = "${jalviewDir}/${doc_dir}"
1199 def outputDir = "${docBuildDir}/${doc_dir}"
1203 include('**/*.html')
1205 filter(ReplaceTokens,
1209 'Version-Rel': JALVIEW_VERSION,
1210 'Year-Rel': getDate("yyyy")
1217 exclude('**/*.html')
1222 inputs.dir(inputDir)
1223 outputs.dir(outputDir)
1227 task convertMdFiles {
1229 def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1230 def cssFile = file("${jalviewDir}/${flexmark_css}")
1233 convertMdToHtml(mdFiles, cssFile)
1236 inputs.files(mdFiles)
1237 inputs.file(cssFile)
1240 mdFiles.each { mdFile ->
1241 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1242 htmlFiles.add(file(htmlFilePath))
1244 outputs.files(htmlFiles)
1248 def hugoTemplateSubstitutions(String input, Map extras=null) {
1249 def replacements = [
1250 DATE: getDate("yyyy-MM-dd"),
1251 CHANNEL: propertiesChannelName,
1252 APPLICATION_NAME: applicationName,
1254 GIT_BRANCH: gitBranch,
1255 VERSION: JALVIEW_VERSION,
1256 JAVA_VERSION: JAVA_VERSION,
1257 VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
1262 if (extras != null) {
1263 extras.each{ k, v ->
1264 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1267 replacements.each{ k, v ->
1268 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1273 def mdFileComponents(File mdFile, def dateOnly=false) {
1276 if (mdFile.exists()) {
1277 def inFrontMatter = false
1278 def firstLine = true
1279 mdFile.eachLine { line ->
1280 if (line.matches("---")) {
1281 def prev = inFrontMatter
1282 inFrontMatter = firstLine
1283 if (inFrontMatter != prev)
1286 if (inFrontMatter) {
1288 if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
1289 map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
1290 } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
1291 map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
1292 } else if (m = line =~ /^channel:\s*(\S+)/) {
1293 map["channel"] = m[0][1]
1294 } else if (m = line =~ /^version:\s*(\S+)/) {
1295 map["version"] = m[0][1]
1296 } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
1297 map[ m[0][1] ] = m[0][2]
1299 if (dateOnly && map["date"] != null) {
1305 content += line+"\n"
1310 return dateOnly ? map["date"] : [map, content]
1313 task hugoTemplates {
1315 description "Create partially populated md pages for hugo website build"
1317 def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
1318 def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
1319 def templateFiles = fileTree(dir: hugoTemplatesDir)
1320 def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1321 def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1322 def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
1323 def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
1326 // specific release template for version archive
1329 def givenDate = null
1330 def givenChannel = null
1331 def givenVersion = null
1332 if (CHANNEL == "RELEASE") {
1333 def (map, content) = mdFileComponents(releaseMdFile)
1334 givenDate = map.date
1335 givenChannel = map.channel
1336 givenVersion = map.version
1338 if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
1339 throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
1342 if (whatsnewMdFile.exists())
1343 whatsnew = whatsnewMdFile.text
1346 def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
1347 def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
1349 def changesHugo = null
1350 if (changes != null) {
1351 changesHugo = '<div class="release_notes">\n\n'
1352 def inSection = false
1353 changes.eachLine { line ->
1355 if (m = line =~ /^##([^#].*)$/) {
1357 changesHugo += "</div>\n\n"
1359 def section = m[0][1].trim()
1360 section = section.toLowerCase()
1361 section = section.replaceAll(/ +/, "_")
1362 section = section.replaceAll(/[^a-z0-9_\-]/, "")
1363 changesHugo += "<div class=\"${section}\">\n\n"
1365 } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
1366 def comment = m[0][2].trim()
1367 if (comment != "") {
1368 comment = comment.replaceAll('"', """)
1370 comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
1371 def newline = m[0][1]
1372 if (comment.trim() != "")
1373 newline += "{{<comment>}}${comment}{{</comment>}} "
1374 newline += m[0][3].trim()
1375 if (issuekeys.size() > 0)
1376 newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
1377 if (m[0][4] != null)
1382 changesHugo += line+"\n"
1385 changesHugo += "\n</div>\n\n"
1387 changesHugo += '</div>'
1390 templateFiles.each{ templateFile ->
1391 def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
1392 def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
1393 def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
1395 def outPathName = string("${hugoBuildDir}/$newRelPathName")
1399 rename(templateFile.getName(), newFileName)
1403 def newFile = file("${outPathName}/${newFileName}".toString())
1404 def content = newFile.text
1405 newFile.text = hugoTemplateSubstitutions(content,
1408 CHANGES: changesHugo,
1409 DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
1410 DRAFT: givenDate == null ? "true" : "false",
1411 JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
1412 JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
1419 inputs.file(oldJvlFile)
1420 inputs.dir(hugoTemplatesDir)
1421 inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
1422 inputs.property("CHANNEL", { CHANNEL })
1425 def getMdDate(File mdFile) {
1426 return mdFileComponents(mdFile, true)
1429 def getMdSections(String content) {
1431 def sectionContent = ""
1432 def sectionName = null
1433 content.eachLine { line ->
1435 if (m = line =~ /^##([^#].*)$/) {
1436 if (sectionName != null) {
1437 sections[sectionName] = sectionContent
1441 sectionName = m[0][1].trim()
1442 sectionName = sectionName.toLowerCase()
1443 sectionName = sectionName.replaceAll(/ +/, "_")
1444 sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
1445 } else if (sectionName != null) {
1446 sectionContent += line+"\n"
1449 if (sectionContent != null) {
1450 sections[sectionName] = sectionContent
1456 task copyHelp(type: Copy) {
1457 def inputDir = helpSourceDir
1458 def outputDir = "${helpBuildDir}/${help_dir}"
1462 include('**/*.html')
1466 filter(ReplaceTokens,
1470 'Version-Rel': JALVIEW_VERSION,
1471 'Year-Rel': getDate("yyyy")
1478 exclude('**/*.html')
1485 inputs.dir(inputDir)
1486 outputs.files(helpFile)
1487 outputs.dir(outputDir)
1491 task releasesTemplates {
1493 description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1497 def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1498 def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1499 def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
1500 def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
1501 def releasesMdDir = "${jalviewDir}/${releases_dir}"
1502 def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1505 def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1506 def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1508 if (CHANNEL == "RELEASE") {
1509 if (!releaseMdFile.exists()) {
1510 throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1512 if (!whatsnewMdFile.exists()) {
1513 throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1517 def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1518 def releaseFilesDates = releaseFiles.collectEntries {
1519 [(it): getMdDate(it)]
1521 releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1523 def releasesTemplate = releasesTemplateFile.text
1524 def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1525 def versionTemplate = m[0][1]
1527 MutableDataSet options = new MutableDataSet()
1529 def extensions = new ArrayList<>()
1530 options.set(Parser.EXTENSIONS, extensions)
1531 options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1533 Parser parser = Parser.builder(options).build()
1534 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1536 def actualVersions = releaseFiles.collect { rf ->
1537 def (rfMap, rfContent) = mdFileComponents(rf)
1538 return rfMap.version
1540 def versionsHtml = ""
1541 def linkedVersions = []
1542 releaseFiles.reverse().each { rFile ->
1543 def (rMap, rContent) = mdFileComponents(rFile)
1545 def versionLink = ""
1546 def partialVersion = ""
1547 def firstPart = true
1548 rMap.version.split("\\.").each { part ->
1549 def displayPart = ( firstPart ? "" : "." ) + part
1550 partialVersion += displayPart
1552 linkedVersions.contains(partialVersion)
1553 || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1555 versionLink += displayPart
1557 versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1558 linkedVersions += partialVersion
1562 def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1565 def rContentProcessed = ""
1566 rContent.eachLine { line ->
1567 if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1568 line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1569 } else if (lm = line =~ /^###([^#]+.*)$/) {
1570 line = "_${lm[0][1].trim()}_"
1572 rContentProcessed += line + "\n"
1575 def rContentSections = getMdSections(rContentProcessed)
1576 def rVersion = versionTemplate
1577 if (rVersion != "") {
1578 def rNewFeatures = rContentSections["new_features"]
1579 def rIssuesResolved = rContentSections["issues_resolved"]
1580 Node newFeaturesNode = parser.parse(rNewFeatures)
1581 String newFeaturesHtml = renderer.render(newFeaturesNode)
1582 Node issuesResolvedNode = parser.parse(rIssuesResolved)
1583 String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1584 rVersion = hugoTemplateSubstitutions(rVersion,
1586 VERSION: rMap.version,
1587 VERSION_LINK: versionLink,
1588 DISPLAY_DATE: displayDate,
1589 NEW_FEATURES: newFeaturesHtml,
1590 ISSUES_RESOLVED: issuesResolvedHtml
1593 versionsHtml += rVersion
1597 releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1598 releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1599 releasesHtmlFile.text = releasesTemplate
1601 if (whatsnewMdFile.exists()) {
1602 def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1603 def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1604 Node whatsnewNode = parser.parse(whatsnewMd)
1605 String whatsnewHtml = renderer.render(whatsnewNode)
1606 whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1607 whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1609 VERSION: JALVIEW_VERSION,
1610 DISPLAY_DATE: wnDisplayDate
1613 } else if (gradle.taskGraph.hasTask(":linkCheck")) {
1614 whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
1619 inputs.file(releasesTemplateFile)
1620 inputs.file(whatsnewTemplateFile)
1621 inputs.dir(releasesMdDir)
1622 inputs.dir(whatsnewMdDir)
1623 outputs.file(releasesHtmlFile)
1624 outputs.file(whatsnewHtmlFile)
1628 task copyResources(type: Copy) {
1630 description = "Copy (and make text substitutions in) the resources dir to the build area"
1632 def inputDir = resourceDir
1633 def outputDir = resourcesBuildDir
1637 include('**/*.html')
1639 filter(ReplaceTokens,
1643 'Version-Rel': JALVIEW_VERSION,
1644 'Year-Rel': getDate("yyyy")
1651 exclude('**/*.html')
1656 inputs.dir(inputDir)
1657 outputs.dir(outputDir)
1660 task copyChannelResources(type: Copy) {
1661 dependsOn copyResources
1663 description = "Copy the channel resources dir to the build resources area"
1665 def inputDir = "${channelDir}/${resource_dir}"
1666 def outputDir = resourcesBuildDir
1668 include(channel_props)
1669 filter(ReplaceTokens,
1673 'SUFFIX': channelSuffix
1678 exclude(channel_props)
1682 inputs.dir(inputDir)
1683 outputs.dir(outputDir)
1686 task createBuildProperties(type: WriteProperties) {
1687 dependsOn copyResources
1689 description = "Create the ${buildProperties} file"
1691 inputs.dir(sourceDir)
1692 inputs.dir(resourcesBuildDir)
1693 outputFile (buildProperties)
1694 // taking time specific comment out to allow better incremental builds
1695 comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1696 //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1697 property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1698 property "VERSION", JALVIEW_VERSION
1699 property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1700 property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
1701 if (getdownSetAppBaseProperty) {
1702 property "GETDOWNAPPBASE", getdownAppBase
1703 property "GETDOWNAPPDISTDIR", getdownAppDistDir
1705 outputs.file(outputFile)
1709 task buildIndices(type: JavaExec) {
1711 classpath = sourceSets.main.compileClasspath
1712 main = "com.sun.java.help.search.Indexer"
1713 workingDir = "${helpBuildDir}/${help_dir}"
1716 inputs.dir("${workingDir}/${argDir}")
1718 outputs.dir("${classesDir}/doc")
1719 outputs.dir("${classesDir}/help")
1720 outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1721 outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1722 outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1723 outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1724 outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1725 outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1728 task buildResources {
1729 dependsOn copyResources
1730 dependsOn copyChannelResources
1731 dependsOn createBuildProperties
1735 dependsOn buildResources
1738 dependsOn releasesTemplates
1739 dependsOn convertMdFiles
1740 dependsOn buildIndices
1744 compileJava.dependsOn prepare
1745 run.dependsOn compileJava
1746 compileTestJava.dependsOn compileJava
1751 group = "Verification"
1752 description = "Runs all testTaskN tasks)"
1755 dependsOn cloverClasses
1757 dependsOn testClasses
1760 // not running tests in this task
1763 /* testTask0 is the main test task */
1764 task testTask0(type: Test) {
1765 group = "Verification"
1766 description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
1768 includeGroups testng_groups.split(",")
1769 excludeGroups testng_excluded_groups.split(",")
1770 tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
1772 useDefaultListeners=true
1776 /* separated tests */
1777 task testTask1(type: Test) {
1778 group = "Verification"
1779 description = "Tests that need to be isolated from the main test run"
1782 excludeGroups testng_excluded_groups.split(",")
1784 useDefaultListeners=true
1788 task testTask2(type: Test) {
1789 group = "Verification"
1790 description = "Tests that need to be isolated from the main test run"
1793 excludeGroups testng_excluded_groups.split(",")
1795 useDefaultListeners=true
1798 task testTask3(type: Test) {
1799 group = "Verification"
1800 description = "Tests that need to be isolated from the main test run"
1803 excludeGroups testng_excluded_groups.split(",")
1805 useDefaultListeners=true
1809 /* insert more testTaskNs here -- change N to next digit or other string */
1811 task testTaskN(type: Test) {
1812 group = "Verification"
1813 description = "Tests that need to be isolated from the main test run"
1816 excludeGroups testng_excluded_groups.split(",")
1818 useDefaultListeners=true
1824 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1825 * to summarise test results from all Test tasks
1827 /* START of test tasks results summary */
1828 import groovy.time.TimeCategory
1829 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1830 import org.gradle.api.tasks.testing.logging.TestLogEvent
1831 rootProject.ext.testsResults = [] // Container for tests summaries
1833 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1835 // from original test task
1837 dependsOn cloverClasses
1839 dependsOn testClasses //?
1842 // run main tests first
1843 if (!testTask.name.equals("testTask0"))
1844 testTask.mustRunAfter "testTask0"
1846 testTask.testLogging { logging ->
1847 events TestLogEvent.FAILED
1848 // TestLogEvent.SKIPPED,
1849 // TestLogEvent.STANDARD_OUT,
1850 // TestLogEvent.STANDARD_ERROR
1852 exceptionFormat TestExceptionFormat.FULL
1855 showStackTraces true
1857 showStandardStreams true
1859 info.events = [ TestLogEvent.FAILED ]
1862 if (OperatingSystem.current().isMacOsX()) {
1863 testTask.systemProperty "apple.awt.UIElement", "true"
1864 testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
1868 ignoreFailures = true // Always try to run all tests for all modules
1870 afterSuite { desc, result ->
1872 return // Only summarize results for whole modules
1874 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1876 rootProject.ext.testsResults.add(resultsInfo)
1879 // from original test task
1880 maxHeapSize = "1024m"
1882 workingDir = jalviewDir
1883 def testLaf = project.findProperty("test_laf")
1884 if (testLaf != null) {
1885 println("Setting Test LaF to '${testLaf}'")
1886 systemProperty "laf", testLaf
1888 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1889 if (testHiDPIScale != null) {
1890 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1891 systemProperty "sun.java2d.uiScale", testHiDPIScale
1893 sourceCompatibility = compile_source_compatibility
1894 targetCompatibility = compile_target_compatibility
1895 jvmArgs += additional_compiler_args
1898 // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
1899 // testTasks that include the tests, and exclude all from the others.
1900 // get --test argument
1901 filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
1902 // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
1903 // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
1907 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1912 /* don't fail on no matching tests (so --tests will run across all testTasks) */
1913 testTask.filter.setFailOnNoMatchingTests(false)
1915 /* ensure the "test" task dependsOn all the testTasks */
1916 test.dependsOn testTask
1919 gradle.buildFinished {
1920 def allResults = rootProject.ext.testsResults
1922 if (!allResults.isEmpty()) {
1923 printResults allResults
1924 allResults.each {r ->
1925 if (r[2].resultType == TestResult.ResultType.FAILURE)
1926 throw new GradleException("Failed tests!")
1931 private static String colString(styler, col, colour, text) {
1932 return col?"${styler[colour](text)}":text
1935 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1936 def colour = 'black'
1944 case TestResult.ResultType.SUCCESS:
1947 case TestResult.ResultType.FAILURE:
1955 StringBuilder sb = new StringBuilder()
1959 sb.append(" results: ")
1960 sb.append(colString(s, col && !nocol, colour, text))
1962 sb.append("${rc} tests, ")
1963 sb.append(colString(s, col && rs > 0, 'green', rs))
1964 sb.append(" successes, ")
1965 sb.append(colString(s, col && rf > 0, 'red', rf))
1966 sb.append(" failures, ")
1967 sb.append("${rsk} skipped) in ${t}")
1968 return sb.toString()
1971 private static void printResults(allResults) {
1973 // styler from https://stackoverflow.com/a/56139852
1974 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
1977 def failedTests = false
1978 def summaryLines = []
1980 def totalsuccess = 0
1983 def totaltime = TimeCategory.getSeconds(0)
1984 // sort on project name then task name
1985 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
1986 def projectName = it[0]
1987 def taskName = it[1]
1991 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
1992 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
1993 def reportLine = "Report file: ${report}"
1994 def ls = summaryPlain.length()
1995 def lr = reportLine.length()
1996 def m = [ls, lr].max()
1999 def info = [ls, summaryCol, reportLine]
2000 summaryLines.add(info)
2001 failedTests |= result.resultType == TestResult.ResultType.FAILURE
2002 totalcount += result.testCount
2003 totalsuccess += result.successfulTestCount
2004 totalfail += result.failedTestCount
2005 totalskip += result.skippedTestCount
2008 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
2009 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
2010 def tls = totalSummaryPlain.length()
2011 if (tls > maxLength)
2013 def info = [tls, totalSummaryCol, null]
2014 summaryLines.add(info)
2016 def allSummaries = []
2017 for(sInfo : summaryLines) {
2019 def summary = sInfo[1]
2020 def report = sInfo[2]
2022 StringBuilder sb = new StringBuilder()
2023 sb.append("│" + summary + " " * (maxLength - ls) + "│")
2024 if (report != null) {
2025 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
2027 allSummaries += sb.toString()
2030 println "┌${"${"─" * maxLength}"}┐"
2031 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
2032 println "└${"${"─" * maxLength}"}┘"
2034 /* END of test tasks results summary */
2037 task compileLinkCheck(type: JavaCompile) {
2039 classpath = files("${jalviewDir}/${utils_dir}")
2040 destinationDir = file("${jalviewDir}/${utils_dir}")
2041 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
2043 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2044 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2045 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
2046 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
2050 task linkCheck(type: JavaExec) {
2052 dependsOn compileLinkCheck
2054 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
2055 classpath = files("${jalviewDir}/${utils_dir}")
2056 main = "HelpLinksChecker"
2057 workingDir = "${helpBuildDir}"
2058 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
2060 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
2061 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2064 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2068 inputs.dir(helpBuildDir)
2069 outputs.file(helpLinksCheckerOutFile)
2073 // import the pubhtmlhelp target
2074 ant.properties.basedir = "${jalviewDir}"
2075 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
2076 ant.importBuild "${utils_dir}/publishHelp.xml"
2079 task cleanPackageDir(type: Delete) {
2081 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2091 attributes "Main-Class": main_class,
2092 "Permissions": "all-permissions",
2093 "Application-Name": applicationName,
2094 "Codebase": application_codebase,
2095 "Implementation-Version": JALVIEW_VERSION
2098 def outputDir = "${jalviewDir}/${package_dir}"
2099 destinationDirectory = file(outputDir)
2100 archiveFileName = rootProject.name+".jar"
2101 duplicatesStrategy "EXCLUDE"
2108 exclude "**/*.jar.*"
2110 inputs.dir(sourceSets.main.java.outputDir)
2111 sourceSets.main.resources.srcDirs.each{ dir ->
2114 outputs.file("${outputDir}/${archiveFileName}")
2118 task copyJars(type: Copy) {
2119 from fileTree(dir: classesDir, include: "**/*.jar").files
2120 into "${jalviewDir}/${package_dir}"
2124 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2125 task syncJars(type: Sync) {
2127 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2128 into "${jalviewDir}/${package_dir}"
2130 include jar.archiveFileName.getOrNull()
2137 description = "Put all required libraries in dist"
2138 // order of "cleanPackageDir", "copyJars", "jar" important!
2139 jar.mustRunAfter cleanPackageDir
2140 syncJars.mustRunAfter cleanPackageDir
2141 dependsOn cleanPackageDir
2144 outputs.dir("${jalviewDir}/${package_dir}")
2149 dependsOn cleanPackageDir
2155 task launcherJar(type: Jar) {
2158 "Main-Class": shadow_jar_main_class,
2159 "Implementation-Version": JALVIEW_VERSION,
2160 "Application-Name": applicationName
2166 group = "distribution"
2167 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2172 def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
2173 def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
2174 def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
2179 // shadowJar manifest must inheritFrom another Jar task. Can't set attributes here.
2180 inheritFrom(project.tasks.launcherJar.manifest)
2182 // we need to include the groovy-swing Include-Package for it to run in the shadowJar
2184 def jarFileManifests = []
2185 groovyJars.each { jarFile ->
2186 def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
2188 jarFileManifests += mf
2192 from (jarFileManifests) {
2193 eachEntry { details ->
2194 if (!details.key.equals("Import-Package")) {
2202 duplicatesStrategy "INCLUDE"
2204 // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
2205 mainClassName = shadow_jar_main_class
2207 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2211 task getdownImagesCopy() {
2212 inputs.dir getdownImagesDir
2213 outputs.dir getdownImagesBuildDir
2217 from(getdownImagesDir) {
2218 include("*getdown*.png")
2220 into getdownImagesBuildDir
2225 task getdownImagesProcess() {
2226 dependsOn getdownImagesCopy
2229 if (backgroundImageText) {
2230 if (convertBinary == null) {
2231 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2233 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2234 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2236 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2238 executable convertBinary
2241 '-font', getdown_background_image_text_font,
2242 '-fill', getdown_background_image_text_colour,
2243 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2244 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2245 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2254 task getdownImages() {
2255 dependsOn getdownImagesProcess
2258 task getdownWebsiteBuild() {
2259 group = "distribution"
2260 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."
2262 dependsOn getdownImages
2267 def getdownWebsiteResourceFilenames = []
2268 def getdownResourceDir = getdownResourceDir
2269 def getdownResourceFilenames = []
2272 // clean the getdown website and files dir before creating getdown folders
2273 delete getdownAppBaseDir
2274 delete getdownFilesDir
2277 from buildProperties
2278 rename(file(buildProperties).getName(), getdown_build_properties)
2281 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2284 from channelPropsFile
2285 filter(ReplaceTokens,
2289 'SUFFIX': channelSuffix
2292 into getdownAppBaseDir
2294 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2296 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2297 def props = project.properties.sort { it.key }
2298 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2299 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2301 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2302 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2304 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2305 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2307 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2308 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2309 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2310 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2311 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2312 props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
2313 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
2316 props.put("getdown_txt_title", jalview_name)
2317 props.put("getdown_txt_ui.name", applicationName)
2319 // start with appbase
2320 getdownTextLines += "appbase = ${getdownAppBase}"
2321 props.each{ prop, val ->
2322 if (prop.startsWith("getdown_txt_") && val != null) {
2323 if (prop.startsWith("getdown_txt_multi_")) {
2324 def key = prop.substring(18)
2325 val.split(",").each{ v ->
2326 def line = "${key} = ${v}"
2327 getdownTextLines += line
2330 // file values rationalised
2331 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2333 if (val.indexOf('/') == 0) {
2336 } else if (val.indexOf('/') > 0) {
2337 // relative path (relative to jalviewDir)
2338 r = file( "${jalviewDir}/${val}" )
2341 val = "${getdown_resource_dir}/" + r.getName()
2342 getdownWebsiteResourceFilenames += val
2343 getdownResourceFilenames += r.getPath()
2346 if (! prop.startsWith("getdown_txt_resource")) {
2347 def line = prop.substring(12) + " = ${val}"
2348 getdownTextLines += line
2354 getdownWebsiteResourceFilenames.each{ filename ->
2355 getdownTextLines += "resource = ${filename}"
2357 getdownResourceFilenames.each{ filename ->
2360 into getdownResourceDir
2364 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2365 getdownWrapperScripts.each{ script ->
2366 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2370 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2372 getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
2377 fileTree(file(package_dir)).each{ f ->
2378 if (f.isDirectory()) {
2379 def files = fileTree(dir: f, include: ["*"]).getFiles()
2381 } else if (f.exists()) {
2385 def jalviewJar = jar.archiveFileName.getOrNull()
2386 // put jalview.jar first for CLASSPATH and .properties files reasons
2387 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2388 def name = f.getName()
2389 def line = "code = ${getdownAppDistDir}/${name}"
2390 getdownTextLines += line
2397 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2399 if (JAVA_VERSION.equals("11")) {
2400 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2401 j11libFiles.sort().each{f ->
2402 def name = f.getName()
2403 def line = "code = ${getdown_j11lib_dir}/${name}"
2404 getdownTextLines += line
2407 into getdownJ11libDir
2413 // 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.
2414 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2415 getdownTextLines += "resource = ${getdown_launcher_new}"
2416 getdownTextLines += "class = ${main_class}"
2417 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2418 if (getdownSetAppBaseProperty) {
2419 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2420 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2423 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2424 getdownTxt.write(getdownTextLines.join("\n"))
2426 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2427 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2428 launchJvl.write("appbase=${getdownAppBase}")
2430 // files going into the getdown website dir: getdown-launcher.jar
2432 from getdownLauncher
2433 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2434 into getdownAppBaseDir
2437 // files going into the getdown website dir: getdown-launcher(-local).jar
2439 from getdownLauncher
2440 if (file(getdownLauncher).getName() != getdown_launcher) {
2441 rename(file(getdownLauncher).getName(), getdown_launcher)
2443 into getdownAppBaseDir
2446 // files going into the getdown website dir: ./install dir and files
2447 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2450 from getdownLauncher
2451 from "${getdownAppDir}/${getdown_build_properties}"
2452 if (file(getdownLauncher).getName() != getdown_launcher) {
2453 rename(file(getdownLauncher).getName(), getdown_launcher)
2455 into getdownInstallDir
2458 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2460 from getdownInstallDir
2461 into getdownFilesInstallDir
2465 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2469 from getdownLauncher
2470 from "${getdownAppBaseDir}/${getdown_build_properties}"
2471 from "${getdownAppBaseDir}/${channel_props}"
2472 if (file(getdownLauncher).getName() != getdown_launcher) {
2473 rename(file(getdownLauncher).getName(), getdown_launcher)
2475 into getdownFilesDir
2478 // and ./resource (not all downloaded by getdown)
2480 from getdownResourceDir
2481 into "${getdownFilesDir}/${getdown_resource_dir}"
2486 inputs.dir("${jalviewDir}/${package_dir}")
2488 outputs.dir(getdownAppBaseDir)
2489 outputs.dir(getdownFilesDir)
2493 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2494 task getdownDigestDir(type: JavaExec) {
2496 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2498 def digestDirPropertyName = "DIGESTDIR"
2500 classpath = files(getdownLauncher)
2501 def digestDir = findProperty(digestDirPropertyName)
2502 if (digestDir == null) {
2503 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2507 main = "com.threerings.getdown.tools.Digester"
2511 task getdownDigest(type: JavaExec) {
2512 group = "distribution"
2513 description = "Digest the getdown website folder"
2515 dependsOn getdownWebsiteBuild
2518 classpath = files(getdownLauncher)
2520 main = "com.threerings.getdown.tools.Digester"
2521 args getdownAppBaseDir
2522 inputs.dir(getdownAppBaseDir)
2523 outputs.file("${getdownAppBaseDir}/digest2.txt")
2528 group = "distribution"
2529 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2530 dependsOn getdownDigest
2532 if (reportRsyncCommand) {
2533 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2534 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2535 println "LIKELY RSYNC COMMAND:"
2536 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2537 if (RUNRSYNC == "true") {
2539 commandLine "mkdir", "-p", toDir
2542 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2549 task getdownWebsite {
2550 group = "distribution"
2551 description = "A task to create the whole getdown channel website dir including digest file"
2553 dependsOn getdownWebsiteBuild
2554 dependsOn getdownDigest
2557 task getdownArchiveBuild() {
2558 group = "distribution"
2559 description = "Put files in the archive dir to go on the website"
2561 dependsOn getdownWebsiteBuild
2563 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2564 def vDir = "${getdownArchiveDir}/${v}"
2565 getdownFullArchiveDir = "${vDir}/getdown"
2566 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2568 def vAltDir = "alt_${v}"
2569 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2572 // cleanup old "old" dir
2573 delete getdownArchiveDir
2575 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2576 getdownArchiveTxt.getParentFile().mkdirs()
2577 def getdownArchiveTextLines = []
2578 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2582 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2583 into "${getdownFullArchiveDir}/${vAltDir}"
2586 getdownTextLines.each { line ->
2587 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2588 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2589 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2590 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2591 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2592 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2593 // remove the existing resource = resource/ or bin/ lines
2594 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2595 getdownArchiveTextLines += line
2599 // the resource dir -- add these files as resource lines in getdown.txt
2601 from "${archiveImagesDir}"
2602 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2604 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2608 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2610 def vLaunchJvl = file(getdownVersionLaunchJvl)
2611 vLaunchJvl.getParentFile().mkdirs()
2612 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2613 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2614 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2615 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2616 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2617 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2619 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2621 from getdownLauncher
2622 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2623 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2624 from "${getdownAppBaseDir}/${channel_props}"
2625 if (file(getdownLauncher).getName() != getdown_launcher) {
2626 rename(file(getdownLauncher).getName(), getdown_launcher)
2628 into getdownFullArchiveDir
2634 task getdownArchiveDigest(type: JavaExec) {
2635 group = "distribution"
2636 description = "Digest the getdown archive folder"
2638 dependsOn getdownArchiveBuild
2641 classpath = files(getdownLauncher)
2642 args getdownFullArchiveDir
2644 main = "com.threerings.getdown.tools.Digester"
2645 inputs.dir(getdownFullArchiveDir)
2646 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2649 task getdownArchive() {
2650 group = "distribution"
2651 description = "Build the website archive dir with getdown digest"
2653 dependsOn getdownArchiveBuild
2654 dependsOn getdownArchiveDigest
2657 tasks.withType(JavaCompile) {
2658 options.encoding = 'UTF-8'
2664 delete getdownAppBaseDir
2665 delete getdownFilesDir
2666 delete getdownArchiveDir
2672 if (file(install4jHomeDir).exists()) {
2674 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2675 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2676 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2677 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2679 installDir(file(install4jHomeDir))
2681 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2685 task copyInstall4jTemplate {
2686 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2687 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2688 inputs.file(install4jTemplateFile)
2689 inputs.file(install4jFileAssociationsFile)
2690 inputs.property("CHANNEL", { CHANNEL })
2691 outputs.file(install4jConfFile)
2694 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2696 // turn off code signing if no OSX_KEYPASS
2697 if (OSX_KEYPASS == "") {
2698 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2699 codeSigning.'@macEnabled' = "false"
2701 install4jConfigXml.'**'.windows.each { windows ->
2702 windows.'@runPostProcessor' = "false"
2706 // disable install screen for OSX dmg (for 2.11.2.0)
2707 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2708 macosArchive.attributes().remove('executeSetupApp')
2709 macosArchive.attributes().remove('setupAppId')
2712 // turn off checksum creation for LOCAL channel
2713 def e = install4jConfigXml.application[0]
2714 e.'@createChecksums' = string(install4jCheckSums)
2716 // put file association actions where placeholder action is
2717 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2718 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2719 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2720 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2721 def parent = a.parent()
2723 fileAssociationActions.each { faa ->
2726 // don't need to continue in .any loop once replacements have been made
2731 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2732 // NB we're deleting the /other/ one!
2733 // Also remove the examples subdir from non-release versions
2734 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2735 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2736 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2737 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2739 // remove the examples subdir from Full File Set
2740 def files = install4jConfigXml.files[0]
2741 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2742 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2743 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2744 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2745 dirEntry.parent().remove(dirEntry)
2747 install4jConfigXml.'**'.action.any { a ->
2748 if (a.'@customizedId' == customizedIdToDelete) {
2749 def parent = a.parent()
2755 // write install4j file
2756 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2763 delete install4jConfFile
2767 task cleanInstallersDataFiles {
2768 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2769 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2770 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2772 delete installersOutputTxt
2773 delete installersSha256
2774 delete hugoDataJsonFile
2778 task install4jDMGBackgroundImageCopy {
2779 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2780 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2783 from(install4jDMGBackgroundImageDir) {
2784 include(install4jDMGBackgroundImageFile)
2786 into install4jDMGBackgroundImageBuildDir
2791 task install4jDMGBackgroundImageProcess {
2792 dependsOn install4jDMGBackgroundImageCopy
2795 if (backgroundImageText) {
2796 if (convertBinary == null) {
2797 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2799 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2800 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2802 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2804 executable convertBinary
2807 '-font', install4j_background_image_text_font,
2808 '-fill', install4j_background_image_text_colour,
2809 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2810 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2811 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2820 task install4jDMGBackgroundImage {
2821 dependsOn install4jDMGBackgroundImageProcess
2824 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2825 group = "distribution"
2826 description = "Create the install4j installers"
2828 dependsOn copyInstall4jTemplate
2829 dependsOn cleanInstallersDataFiles
2830 dependsOn install4jDMGBackgroundImage
2832 projectFile = install4jConfFile
2834 // create an md5 for the input files to use as version for install4j conf file
2835 def digest = MessageDigest.getInstance("MD5")
2837 (file("${install4jDir}/${install4j_template}").text +
2838 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2839 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2840 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2841 if (filesMd5.length() >= 8) {
2842 filesMd5 = filesMd5.substring(0,8)
2844 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2847 'JALVIEW_NAME': jalview_name,
2848 'JALVIEW_APPLICATION_NAME': applicationName,
2849 'JALVIEW_DIR': "../..",
2850 'OSX_KEYSTORE': OSX_KEYSTORE,
2851 'OSX_APPLEID': OSX_APPLEID,
2852 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2853 'JSIGN_SH': JSIGN_SH,
2854 'JRE_DIR': getdown_app_dir_java,
2855 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2856 'JALVIEW_VERSION': JALVIEW_VERSION,
2857 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2858 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2859 'JAVA_VERSION': JAVA_VERSION,
2860 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2861 'VERSION': JALVIEW_VERSION,
2862 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2863 'BUNDLE_ID': install4jBundleId,
2864 'INTERNAL_ID': install4jInternalId,
2865 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2866 'MACOS_DMG_DS_STORE': install4jDMGDSStore,
2867 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2868 'WRAPPER_LINK': getdownWrapperLink,
2869 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2870 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2871 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2872 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2873 'INSTALLER_NAME': install4jInstallerName,
2874 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2875 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2876 'GETDOWN_FILES_DIR': getdown_files_dir,
2877 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2878 'GETDOWN_DIST_DIR': getdownAppDistDir,
2879 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2880 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2881 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2882 'BUILD_DIR': install4jBuildDir,
2883 'APPLICATION_CATEGORIES': install4j_application_categories,
2884 'APPLICATION_FOLDER': install4jApplicationFolder,
2885 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2886 'EXECUTABLE_NAME': install4jExecutableName,
2887 'EXTRA_SCHEME': install4jExtraScheme,
2888 'MAC_ICONS_FILE': install4jMacIconsFile,
2889 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2890 'PNG_ICON_FILE': install4jPngIconFile,
2891 'BACKGROUND': install4jBackground,
2896 'windows': 'WINDOWS',
2900 // these are the bundled OS/architecture VMs needed by install4j
2903 [ "mac", "aarch64" ],
2904 [ "windows", "x64" ],
2906 [ "linux", "aarch64" ]
2908 osArch.forEach { os, arch ->
2909 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)
2910 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2911 // otherwise running `gradle installers` generates a non-useful error:
2912 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2913 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)
2916 //println("INSTALL4J VARIABLES:")
2917 //variables.each{k,v->println("${k}=${v}")}
2919 destination = "${jalviewDir}/${install4jBuildDir}"
2920 buildSelected = true
2922 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
2924 disableSigning = true
2925 disableNotarization = true
2929 macKeystorePassword = OSX_KEYPASS
2932 if (OSX_ALTOOLPASS) {
2933 appleIdPassword = OSX_ALTOOLPASS
2934 disableNotarization = false
2936 disableNotarization = true
2940 println("Using projectFile "+projectFile)
2941 if (!disableNotarization) { println("Will notarize OSX App DMG") }
2945 inputs.dir(getdownAppBaseDir)
2946 inputs.file(install4jConfFile)
2947 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
2948 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
2951 def getDataHash(File myFile) {
2952 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
2953 return myFile.exists()
2955 "file" : myFile.getName(),
2956 "filesize" : myFile.length(),
2957 "sha256" : hash.toString()
2962 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
2964 "channel" : getdownChannelName,
2965 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
2966 "git-commit" : "${gitHash} [${gitBranch}]",
2967 "version" : JALVIEW_VERSION
2969 // install4j installer files
2970 if (installersOutputTxt.exists()) {
2972 installersOutputTxt.readLines().each { def line ->
2973 if (line.startsWith("#")) {
2976 line.replaceAll("\n","")
2977 def vals = line.split("\t")
2978 def filename = vals[3]
2979 def filesize = file(filename).length()
2980 filename = filename.replaceAll(/^.*\//, "")
2981 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
2982 idHash."${filename}" = vals[0]
2984 if (install4jCheckSums && installersSha256.exists()) {
2985 installersSha256.readLines().each { def line ->
2986 if (line.startsWith("#")) {
2989 line.replaceAll("\n","")
2990 def vals = line.split(/\s+\*?/)
2991 def filename = vals[1]
2992 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
2998 "JAR": shadowJar.archiveFile, // executable JAR
2999 "JVL": getdownVersionLaunchJvl, // version JVL
3000 "SOURCE": sourceDist.archiveFile // source TGZ
3001 ].each { key, value ->
3002 def file = file(value)
3003 if (file.exists()) {
3004 def fileHash = getDataHash(file)
3005 if (fileHash != null) {
3006 hash."${key}" = fileHash;
3010 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
3013 task staticMakeInstallersJsonFile {
3015 def output = findProperty("i4j_output")
3016 def sha256 = findProperty("i4j_sha256")
3017 def json = findProperty("i4j_json")
3018 if (output == null || sha256 == null || json == null) {
3019 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3021 writeDataJsonFile(file(output), file(sha256), file(json))
3026 dependsOn installerFiles
3032 eclipse().configFile(eclipse_codestyle_file)
3036 task createSourceReleaseProperties(type: WriteProperties) {
3037 group = "distribution"
3038 description = "Create the source RELEASE properties file"
3040 def sourceTarBuildDir = "${buildDir}/sourceTar"
3041 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3042 outputFile (sourceReleasePropertiesFile)
3045 releaseProps.each{ key, val -> property key, val }
3046 property "git.branch", gitBranch
3047 property "git.hash", gitHash
3050 outputs.file(outputFile)
3053 task sourceDist(type: Tar) {
3054 group "distribution"
3055 description "Create a source .tar.gz file for distribution"
3057 dependsOn createBuildProperties
3058 dependsOn convertMdFiles
3059 dependsOn eclipseAllPreferences
3060 dependsOn createSourceReleaseProperties
3063 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3064 archiveFileName = outputFileName
3066 compression Compression.GZIP
3082 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3084 "utils/InstallAnywhere",
3099 "gradle.properties",
3111 ".settings/org.eclipse.buildship.core.prefs",
3112 ".settings/org.eclipse.jdt.core.prefs"
3116 exclude (EXCLUDE_FILES)
3117 include (PROCESS_FILES)
3118 filter(ReplaceTokens,
3122 'Version-Rel': JALVIEW_VERSION,
3123 'Year-Rel': getDate("yyyy")
3128 exclude (EXCLUDE_FILES)
3129 exclude (PROCESS_FILES)
3130 exclude ("appletlib")
3131 exclude ("**/*locales")
3132 exclude ("*locales/**")
3133 exclude ("utils/InstallAnywhere")
3135 exclude (getdown_files_dir)
3136 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3137 //exclude (getdown_website_dir)
3138 //exclude (getdown_archive_dir)
3140 // exluding these as not using jars as modules yet
3141 exclude ("${j11modDir}/**/*.jar")
3144 include(INCLUDE_FILES)
3146 // from (jalviewDir) {
3147 // // explicit includes for stuff that seemed to not get included
3148 // include(fileTree("test/**/*."))
3149 // exclude(EXCLUDE_FILES)
3150 // exclude(PROCESS_FILES)
3153 from(file(buildProperties).getParent()) {
3154 include(file(buildProperties).getName())
3155 rename(file(buildProperties).getName(), "build_properties")
3157 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3161 def sourceTarBuildDir = "${buildDir}/sourceTar"
3162 from(sourceTarBuildDir) {
3163 // this includes the appended RELEASE properties file
3167 task dataInstallersJson {
3169 description "Create the installers-VERSION.json data file for installer files created"
3171 mustRunAfter installers
3172 mustRunAfter shadowJar
3173 mustRunAfter sourceDist
3174 mustRunAfter getdownArchive
3176 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3177 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3179 if (installersOutputTxt.exists()) {
3180 inputs.file(installersOutputTxt)
3182 if (install4jCheckSums && installersSha256.exists()) {
3183 inputs.file(installersSha256)
3186 shadowJar.archiveFile, // executable JAR
3187 getdownVersionLaunchJvl, // version JVL
3188 sourceDist.archiveFile // source TGZ
3189 ].each { fileName ->
3190 if (file(fileName).exists()) {
3191 inputs.file(fileName)
3195 outputs.file(hugoDataJsonFile)
3198 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3204 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3207 dependsOn pubhtmlhelp
3209 inputs.dir("${helpBuildDir}/${help_dir}")
3210 outputs.dir("${buildDir}/distributions/${help_dir}")
3214 task j2sSetHeadlessBuild {
3221 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3223 description "Enable the alternative J2S Config file for headless build"
3225 outputFile = jalviewjsJ2sSettingsFileName
3226 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3227 def j2sProps = new Properties()
3228 if (j2sPropsFile.exists()) {
3230 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3231 j2sProps.load(j2sPropsFileFIS)
3232 j2sPropsFileFIS.close()
3234 j2sProps.each { prop, val ->
3237 } catch (Exception e) {
3238 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3242 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3243 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3248 task jalviewjsSetEclipseWorkspace {
3249 def propKey = "jalviewjs_eclipse_workspace"
3251 if (project.hasProperty(propKey)) {
3252 propVal = project.getProperty(propKey)
3253 if (propVal.startsWith("~/")) {
3254 propVal = System.getProperty("user.home") + propVal.substring(1)
3257 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3258 def propsFile = file(propsFileName)
3259 def eclipseWsDir = propVal
3260 def props = new Properties()
3262 def writeProps = true
3263 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3264 def ins = new FileInputStream(propsFileName)
3267 if (props.getProperty(propKey, null) != null) {
3268 eclipseWsDir = props.getProperty(propKey)
3273 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3274 def tempDir = File.createTempDir()
3275 eclipseWsDir = tempDir.getAbsolutePath()
3278 eclipseWorkspace = file(eclipseWsDir)
3281 // do not run a headless transpile when we claim to be in Eclipse
3283 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3284 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3286 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3290 props.setProperty(propKey, eclipseWsDir)
3291 propsFile.parentFile.mkdirs()
3292 def bytes = new ByteArrayOutputStream()
3293 props.store(bytes, null)
3294 def propertiesString = bytes.toString()
3295 propsFile.text = propertiesString
3301 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3304 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3305 outputs.file(propsFileName)
3306 outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
3310 task jalviewjsEclipsePaths {
3311 def eclipseProductFile
3314 def eclipseRoot = jalviewjs_eclipse_root
3315 if (eclipseRoot.startsWith("~/")) {
3316 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3318 if (OperatingSystem.current().isMacOsX()) {
3319 eclipseRoot += "/Eclipse.app"
3320 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3321 eclipseProductFile = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3322 eclipseSetupLog = "${eclipseRoot}/Contents/Eclipse/configuration/org.eclipse.oomph.setup/setup.log"
3323 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3324 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3325 eclipseRoot += "/eclipse"
3327 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3328 eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
3329 eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
3330 } else { // linux or unix
3331 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3332 eclipseRoot += "/eclipse"
3334 eclipseBinary = "${eclipseRoot}/eclipse"
3335 eclipseProductFile = "${eclipseRoot}/.eclipseproduct"
3336 eclipseSetupLog = "${eclipseRoot}/configuration/org.eclipse.oomph.setup/setup.log"
3339 eclipseVersion = "unknown" // default
3340 def assumedVersion = true
3341 if (file(eclipseProductFile).exists()) {
3342 def fis = new FileInputStream(eclipseProductFile)
3343 def props = new Properties()
3345 eclipseVersion = props.getProperty("version")
3347 assumedVersion = false
3349 if (file(eclipseSetupLog).exists()) {
3350 def productRegex = /(?m)^\[[^\]]+\]\s+Product\s+(org\.eclipse.\S*)/
3352 file(eclipseSetupLog).eachLine { String line ->
3353 def matcher = line =~ productRegex
3354 if (matcher.size() > 0) {
3355 eclipseProductVersion = matcher[0][1]
3358 if (lineCount >= 100) {
3365 def propKey = "eclipse_debug"
3366 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3369 // do not run a headless transpile when we claim to be in Eclipse
3371 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3372 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3374 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3377 if (!assumedVersion) {
3378 println("ECLIPSE VERSION=${eclipseVersion}")
3379 if (eclipseProductVersion.length() != 0) {
3380 println("ECLIPSE PRODUCT=${eclipseProductVersion}")
3387 task printProperties {
3389 description "Output to console all System.properties"
3391 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3397 dependsOn eclipseProject
3398 dependsOn eclipseClasspath
3399 dependsOn eclipseJdt
3403 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3404 task jalviewjsEclipseCopyDropins(type: Copy) {
3405 dependsOn jalviewjsEclipsePaths
3407 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3408 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3409 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3416 // this eclipse -clean doesn't actually work
3417 task jalviewjsCleanEclipse(type: Exec) {
3418 dependsOn eclipseSetup
3419 dependsOn jalviewjsEclipsePaths
3420 dependsOn jalviewjsEclipseCopyDropins
3422 executable(eclipseBinary)
3423 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3429 def inputString = """exit
3432 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3433 standardInput = inputByteStream
3436 /* not really working yet
3437 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3441 task jalviewjsTransferUnzipSwingJs {
3442 def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3446 from zipTree(file_zip)
3447 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3451 inputs.file file_zip
3452 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3456 task jalviewjsTransferUnzipLib {
3457 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip").sort()
3460 zipFiles.each { file_zip ->
3462 from zipTree(file_zip)
3463 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3465 // rename files in jsmol/j2s/... to swingjs/j2s/...
3466 if (file_zip.getName().startsWith("Jmol-j2s-site")) {
3468 // jsmol/... -> swingjs/...
3469 def relPathSegments = fcd.relativePath.getSegments()
3470 if (relPathSegments[0] == "jsmol") {
3471 def newRelPathSegments = relPathSegments
3472 newRelPathSegments[0] = "swingjs"
3473 fcd.relativePath = new RelativePath(true, newRelPathSegments)
3478 // The following replace() is needed due to a mismatch in Jmol calls to
3479 // colorPtToFFRGB$javajs_util_T3d when only colorPtToFFRGB$javajs_util_T3 is defined
3480 // in the SwingJS.zip (github or the one distributed with JSmol)
3481 if (file_zip.getName().startsWith("Jmol-SwingJS")) {
3484 while(!line.equals(l)) {
3485 line = line.replace('colorPtToFFRGB$javajs_util_T3d', 'colorPtToFFRGB$javajs_util_T3')
3495 inputs.files zipFiles
3496 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3500 task jalviewjsTransferUnzipAllLibs {
3501 dependsOn jalviewjsTransferUnzipLib
3502 dependsOn jalviewjsTransferUnzipSwingJs
3506 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3508 description "Create the alternative j2s file from the j2s.* properties"
3510 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3511 def siteDirProperty = "j2s.site.directory"
3512 def setSiteDir = false
3513 jalviewjsJ2sProps.each { prop, val ->
3515 if (prop == siteDirProperty) {
3516 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3517 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3523 if (!setSiteDir) { // default site location, don't override specifically set property
3524 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3527 outputFile = jalviewjsJ2sAltSettingsFileName
3530 inputs.properties(jalviewjsJ2sProps)
3531 outputs.file(jalviewjsJ2sAltSettingsFileName)
3536 task jalviewjsEclipseSetup {
3537 dependsOn jalviewjsEclipseCopyDropins
3538 dependsOn jalviewjsSetEclipseWorkspace
3539 dependsOn jalviewjsCreateJ2sSettings
3543 task jalviewjsSyncAllLibs (type: Sync) {
3544 dependsOn jalviewjsTransferUnzipAllLibs
3546 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3547 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3548 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3552 def outputFiles = []
3553 rename { filename ->
3554 outputFiles += "${outputDir}/${filename}"
3561 // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
3562 duplicatesStrategy "INCLUDE"
3564 outputs.files outputFiles
3565 inputs.files inputFiles
3569 task jalviewjsSyncResources (type: Sync) {
3570 dependsOn buildResources
3572 def inputFiles = fileTree(dir: resourcesBuildDir)
3573 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3577 def outputFiles = []
3578 rename { filename ->
3579 outputFiles += "${outputDir}/${filename}"
3585 outputs.files outputFiles
3586 inputs.files inputFiles
3590 task jalviewjsSyncSiteResources (type: Sync) {
3591 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3592 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3596 def outputFiles = []
3597 rename { filename ->
3598 outputFiles += "${outputDir}/${filename}"
3604 outputs.files outputFiles
3605 inputs.files inputFiles
3609 task jalviewjsSyncBuildProperties (type: Sync) {
3610 dependsOn createBuildProperties
3611 def inputFiles = [file(buildProperties)]
3612 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3616 def outputFiles = []
3617 rename { filename ->
3618 outputFiles += "${outputDir}/${filename}"
3624 outputs.files outputFiles
3625 inputs.files inputFiles
3629 task jalviewjsProjectImport(type: Exec) {
3630 dependsOn eclipseSetup
3631 dependsOn jalviewjsEclipsePaths
3632 dependsOn jalviewjsEclipseSetup
3635 // do not run a headless import when we claim to be in Eclipse
3637 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3638 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3640 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3644 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3645 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3646 executable(eclipseBinary)
3647 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3651 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3653 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3654 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3657 inputs.file("${jalviewDir}/.project")
3658 outputs.upToDateWhen {
3659 file(projdir).exists()
3664 task jalviewjsTranspile(type: Exec) {
3665 dependsOn jalviewjsEclipseSetup
3666 dependsOn jalviewjsProjectImport
3667 dependsOn jalviewjsEclipsePaths
3669 dependsOn jalviewjsEnableAltFileProperty
3673 // do not run a headless transpile when we claim to be in Eclipse
3675 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3676 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3678 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3682 executable(eclipseBinary)
3683 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3687 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3689 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3690 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3696 stdout = new ByteArrayOutputStream()
3697 stderr = new ByteArrayOutputStream()
3699 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3700 def logOutFile = file(logOutFileName)
3701 logOutFile.createNewFile()
3702 def info = """ROOT: ${jalviewjs_eclipse_root}
3703 ECLIPSE BINARY: ${eclipseBinary}
3704 ECLIPSE VERSION: ${eclipseVersion}
3705 ECLIPSE PRODUCT: ${eclipseProductVersion}
3706 ECLIPSE WORKSPACE: ${eclipseWorkspace}
3707 ECLIPSE DEBUG: ${eclipseDebug}
3710 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3711 // combine stdout and stderr
3712 def logErrFOS = logOutFOS
3714 if (jalviewjs_j2s_to_console.equals("true")) {
3715 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3716 new org.apache.tools.ant.util.TeeOutputStream(
3720 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3721 new org.apache.tools.ant.util.TeeOutputStream(
3726 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3729 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3733 standardOutput.write(string(info).getBytes("UTF-8"))
3737 def transpileError = false
3738 def j2sIsActive = false
3739 def j2sBuildStarting = false
3740 def compilingLines = 0
3741 def j2sBuildingJavascript = false
3742 def j2sBuildingJavascriptRegex = /(?m)^J2S building JavaScript for (\d+) files/
3744 def transpilingLines = 0
3745 stdout.toString().eachLine { String line ->
3746 if (line.startsWith("J2S isActive true")) {
3749 if (line.startsWith("J2S buildStarting")) {
3750 j2sBuildStarting = true
3752 if (line =~ / Compiling /) {
3755 if (!j2sBuildingJavascript) {
3756 def matcher = line =~ j2sBuildingJavascriptRegex
3757 if (matcher.size() > 0) {
3758 numFiles = Integer.valueOf(matcher[0][1])
3759 j2sBuildingJavascript = true
3762 if (line.startsWith("J2S transpiling ")) {
3765 if (line.contains("Error processing ")) {
3766 transpileError = true
3770 println("J2S IS ACTIVE=${j2sIsActive}")
3771 println("J2S BUILD STARTING=${j2sBuildStarting}")
3772 println("J2S BUILDING JAVASCRIPT=${j2sBuildingJavascript}")
3773 println("NUM FILES=${numFiles}")
3774 println("COMPILING LINES=${compilingLines}")
3775 println("TRANSPILING LINES=${transpilingLines}")
3776 println("TRANSPILE ERROR=${transpileError}")
3780 || (j2sBuildStarting && transpilingLines == 0)
3781 || (transpilingLines < compilingLines)
3782 || (transpilingLines != numFiles)
3784 // j2s did not complete transpile
3785 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3786 println("IGNORING TRANSPILE ERRORS")
3787 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3789 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3794 inputs.dir("${jalviewDir}/${sourceDir}")
3795 outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3796 outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3800 task jalviewjsTranserSiteMergeLibDirs (type: Sync) {
3801 dependsOn jalviewjsTransferUnzipAllLibs
3802 dependsOn jalviewjsTransferUnzipSwingJs
3803 dependsOn jalviewjsTranspile
3805 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3806 // merge swingjs lib last
3807 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3809 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3813 def outputFiles = []
3814 rename { filename ->
3815 outputFiles += "${outputDir}/${filename}"
3822 // should this be exclude really ? No, swingjs dir should be transferred last (and overwrite)
3823 duplicatesStrategy "INCLUDE"
3825 outputs.files outputFiles
3826 inputs.files inputFiles
3830 task jalviewjsTranserSiteMergeSwingDir (type: Sync) {
3831 dependsOn jalviewjsTransferUnzipAllLibs
3832 dependsOn jalviewjsTransferUnzipSwingJs
3833 dependsOn jalviewjsTranspile
3835 // merge jalview files very last
3836 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3838 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
3842 def outputFiles = []
3843 rename { filename ->
3844 outputFiles += "${outputDir}/${filename}"
3851 // should this be exclude really ? No, jalview dir should be transferred last (and overwrite)
3852 duplicatesStrategy "INCLUDE"
3854 outputs.files outputFiles
3855 inputs.files inputFiles
3859 task jalviewjsTranserSiteMergeDirs {
3860 dependsOn jalviewjsTranserSiteMergeLibDirs
3861 dependsOn jalviewjsTranserSiteMergeSwingDir
3865 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3867 def stdout = new ByteArrayOutputStream()
3868 def stderr = new ByteArrayOutputStream()
3870 def coreFile = file(jsfile)
3872 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3874 logOutFile.createNewFile()
3875 logOutFile.append(msg+"\n")
3877 def coreTop = file(prefixFile)
3878 def coreBottom = file(suffixFile)
3879 def missingFiles = []
3880 coreFile.getParentFile().mkdirs()
3881 coreFile.createNewFile()
3882 coreFile.write( coreTop.getText("UTF-8") )
3886 def t = f.getText("UTF-8")
3887 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3888 coreFile.append( t )
3890 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3892 logOutFile.append(msg+"\n")
3896 coreFile.append( coreBottom.getText("UTF-8") )
3898 msg = "Generating ${zjsfile}"
3900 logOutFile.append(msg+"\n")
3901 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3902 def logErrFOS = logOutFOS
3905 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3906 main = "com.google.javascript.jscomp.CommandLineRunner"
3907 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3908 args = [ "--compilation_level", jalviewjs_closure_compiler_optimization_level, "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3911 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3913 logOutFile.append(msg+"\n")
3915 if (logOutConsole) {
3916 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3917 new org.apache.tools.ant.util.TeeOutputStream(
3921 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3922 new org.apache.tools.ant.util.TeeOutputStream(
3927 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3930 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3936 if (missingFiles.size() > 0) {
3937 msg += "\n!!! These files were listed but missing:\n"
3938 missingFiles.each { file -> msg += "!!! " + file.getPath() + "\n" }
3942 logOutFile.append(msg+"\n")
3946 task jalviewjsBuildAllCores {
3948 description "Build the core js lib closures listed in the classlists dir"
3949 dependsOn jalviewjsTranserSiteMergeDirs
3951 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
3952 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
3953 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_j2s_subdir}"
3954 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteMergeDir}/${jalviewjs_js_subdir}"
3955 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3956 def prefixFile = "${jsDir}/core/coretop2.js"
3957 def suffixFile = "${jsDir}/core/corebottom2.js"
3959 inputs.file prefixFile
3960 inputs.file suffixFile
3962 def classlistFiles = []
3963 // add the classlists found int the jalviewjs_classlists_dir
3964 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3966 def name = file.getName() - ".txt"
3973 // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3974 //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3975 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3977 jalviewjsCoreClasslists = []
3979 classlistFiles.each {
3982 def file = hash['file']
3983 if (! file.exists()) {
3984 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3985 return false // this is a "continue" in groovy .each closure
3987 def name = hash['name']
3989 name = file.getName() - ".txt"
3997 def list = fileTree(dir: j2sDir, includes: filelist)
3999 def jsfile = "${outputDir}/core${name}.js"
4000 def zjsfile = "${outputDir}/core${name}.z.js"
4002 jalviewjsCoreClasslists += [
4011 outputs.file(jsfile)
4012 outputs.file(zjsfile)
4016 def allClasslistName = "_all"
4017 def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
4018 allJsFiles += fileTree(
4022 // these exlusions are files that the closure-compiler produces errors for. Should fix them
4023 "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
4024 "**/org/jmol/export/JSExporter.js"
4027 allJsFiles += fileTree(
4031 // these exlusions are files that the closure-compiler produces errors for. Should fix them
4032 "**/sun/misc/Unsafe.js",
4033 "**/swingjs/jquery/jquery-editable-select.js",
4034 "**/swingjs/jquery/j2sComboBox.js",
4035 "**/sun/misc/FloatingDecimal.js"
4038 def allClasslist = [
4039 'jsfile': "${outputDir}/core${allClasslistName}.js",
4040 'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
4042 'name': allClasslistName
4044 // not including this version of "all" core at the moment
4045 //jalviewjsCoreClasslists += allClasslist
4046 inputs.files(allClasslist['list'])
4047 outputs.file(allClasslist['jsfile'])
4048 outputs.file(allClasslist['zjsfile'])
4051 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
4052 logOutFile.getParentFile().mkdirs()
4053 logOutFile.createNewFile()
4054 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
4056 jalviewjsCoreClasslists.each {
4057 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
4064 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
4067 into file(outputFile).getParentFile()
4068 rename { filename ->
4069 if (filename.equals(inputFile.getName())) {
4070 return file(outputFile).getName()
4074 filter(ReplaceTokens,
4078 'MAIN': '"'+main_class+'"',
4080 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
4081 'COREKEY': jalviewjs_core_key,
4082 'CORENAME': coreName
4089 task jalviewjsPublishCoreTemplates {
4090 dependsOn jalviewjsBuildAllCores
4092 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
4093 def inputFile = file(inputFileName)
4094 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4096 def outputFiles = []
4097 jalviewjsCoreClasslists.each { cl ->
4098 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
4099 cl['outputfile'] = outputFile
4100 outputFiles += outputFile
4104 jalviewjsCoreClasslists.each { cl ->
4105 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
4108 inputs.file(inputFile)
4109 outputs.files(outputFiles)
4113 task jalviewjsSyncCore (type: Sync) {
4114 dependsOn jalviewjsBuildAllCores
4115 dependsOn jalviewjsPublishCoreTemplates
4117 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
4118 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
4122 def outputFiles = []
4123 rename { filename ->
4124 outputFiles += "${outputDir}/${filename}"
4130 outputs.files outputFiles
4131 inputs.files inputFiles
4135 // this Copy version of TransferSiteJs will delete anything else in the target dir
4136 task jalviewjsCopyTransferSiteMergeDir(type: Copy) {
4137 dependsOn jalviewjsTranserSiteMergeDirs
4139 from "${jalviewDir}/${jalviewjsTransferSiteMergeDir}"
4140 into "${jalviewDir}/${jalviewjsSiteDir}"
4144 // this Copy version of TransferSiteJs will delete anything else in the target dir
4145 task jalviewjsCopyTransferSiteJs(type: Copy) {
4146 dependsOn jalviewjsTranspile
4148 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4149 into "${jalviewDir}/${jalviewjsSiteDir}"
4153 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
4154 task jalviewjsSyncTransferSiteJs(type: Sync) {
4155 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4157 into "${jalviewDir}/${jalviewjsSiteDir}"
4164 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
4165 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
4166 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
4167 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
4169 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
4170 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
4171 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
4172 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
4175 task jalviewjsPrepareSite {
4177 description "Prepares the website folder including unzipping files and copying resources"
4178 //dependsOn jalviewjsSyncAllLibs // now using jalviewjsCopyTransferSiteMergeDir
4179 dependsOn jalviewjsSyncResources
4180 dependsOn jalviewjsSyncSiteResources
4181 dependsOn jalviewjsSyncBuildProperties
4182 dependsOn jalviewjsSyncCore
4186 task jalviewjsBuildSite {
4188 description "Builds the whole website including transpiled code"
4189 dependsOn jalviewjsCopyTransferSiteMergeDir
4190 dependsOn jalviewjsPrepareSite
4194 task cleanJalviewjsTransferSite {
4196 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4197 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4198 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4199 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4204 task cleanJalviewjsSite {
4205 dependsOn cleanJalviewjsTransferSite
4207 delete "${jalviewDir}/${jalviewjsSiteDir}"
4212 task jalviewjsSiteTar(type: Tar) {
4214 description "Creates a tar.gz file for the website"
4215 dependsOn jalviewjsBuildSite
4216 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4217 archiveFileName = outputFilename
4219 compression Compression.GZIP
4221 from "${jalviewDir}/${jalviewjsSiteDir}"
4222 into jalviewjs_site_dir // this is inside the tar file
4224 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4228 task jalviewjsServer {
4230 def filename = "jalviewjsTest.html"
4231 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4232 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4237 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4238 factory = f.newInstance()
4239 } catch (ClassNotFoundException e) {
4240 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4242 def port = Integer.valueOf(jalviewjs_server_port)
4247 while(port < start+1000 && !running) {
4249 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4250 jalviewjsServer = factory.start(doc_root, port)
4252 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4253 println("SERVER STARTED with document root ${doc_root}.")
4254 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4255 println("For debug: "+url+"?j2sdebug")
4256 println("For verbose: "+url+"?j2sverbose")
4257 } catch (Exception e) {
4262 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4263 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4264 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4266 jalviewjsCoreClasslists.each { cl ->
4267 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4269 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4271 println("For core ${cl.name}: "+urlcore)
4274 file(htmlFile).text = htmlText
4277 outputs.file(htmlFile)
4278 outputs.upToDateWhen({false})
4282 task cleanJalviewjsAll {
4284 description "Delete all configuration and build artifacts to do with JalviewJS build"
4285 dependsOn cleanJalviewjsSite
4286 dependsOn jalviewjsEclipsePaths
4289 delete "${jalviewDir}/${jalviewjsBuildDir}"
4290 delete "${jalviewDir}/${eclipse_bin_dir}"
4291 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4292 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4294 delete jalviewjsJ2sAltSettingsFileName
4297 outputs.upToDateWhen( { false } )
4301 task jalviewjsIDE_checkJ2sPlugin {
4302 group "00 JalviewJS in Eclipse"
4303 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4306 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4307 def j2sPluginFile = file(j2sPlugin)
4308 def eclipseHome = System.properties["eclipse.home.location"]
4309 if (eclipseHome == null || ! IN_ECLIPSE) {
4310 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4312 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4313 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4314 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4315 eclipseJ2sPluginDirs += altPluginsDir
4317 def foundPlugin = false
4318 def j2sPluginFileName = j2sPluginFile.getName()
4319 def eclipseJ2sPlugin
4320 def eclipseJ2sPluginFile
4321 eclipseJ2sPluginDirs.any { dir ->
4322 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4323 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4324 if (eclipseJ2sPluginFile.exists()) {
4330 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4331 System.err.println(msg)
4332 throw new StopExecutionException(msg)
4335 def digest = MessageDigest.getInstance("MD5")
4337 digest.update(j2sPluginFile.text.bytes)
4338 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4340 digest.update(eclipseJ2sPluginFile.text.bytes)
4341 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4343 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4344 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4345 System.err.println(msg)
4346 throw new StopExecutionException(msg)
4348 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4354 task jalviewjsIDE_copyJ2sPlugin {
4355 group "00 JalviewJS in Eclipse"
4356 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4359 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4360 def j2sPluginFile = file(j2sPlugin)
4361 def eclipseHome = System.properties["eclipse.home.location"]
4362 if (eclipseHome == null || ! IN_ECLIPSE) {
4363 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4365 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4366 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4367 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4368 System.err.println(msg)
4371 eclipseJ2sPluginFile.getParentFile().mkdirs()
4372 into eclipseJ2sPluginFile.getParent()
4378 task jalviewjsIDE_j2sFile {
4379 group "00 JalviewJS in Eclipse"
4380 description "Creates the .j2s file"
4381 dependsOn jalviewjsCreateJ2sSettings
4385 task jalviewjsIDE_SyncCore {
4386 group "00 JalviewJS in Eclipse"
4387 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4388 dependsOn jalviewjsSyncCore
4392 task jalviewjsIDE_SyncSiteAll {
4393 dependsOn jalviewjsSyncAllLibs
4394 dependsOn jalviewjsSyncResources
4395 dependsOn jalviewjsSyncSiteResources
4396 dependsOn jalviewjsSyncBuildProperties
4400 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4403 task jalviewjsIDE_PrepareSite {
4404 group "00 JalviewJS in Eclipse"
4405 description "Sync libs and resources to site dir, but not closure cores"
4407 dependsOn jalviewjsIDE_SyncSiteAll
4408 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4412 task jalviewjsIDE_AssembleSite {
4413 group "00 JalviewJS in Eclipse"
4414 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4415 dependsOn jalviewjsPrepareSite
4419 task jalviewjsIDE_SiteClean {
4420 group "00 JalviewJS in Eclipse"
4421 description "Deletes the Eclipse transpiled site"
4422 dependsOn cleanJalviewjsSite
4426 task jalviewjsIDE_Server {
4427 group "00 JalviewJS in Eclipse"
4428 description "Starts a webserver on localhost to test the website"
4429 dependsOn jalviewjsServer
4433 // buildship runs this at import or gradle refresh
4434 task eclipseSynchronizationTask {
4435 //dependsOn eclipseSetup
4436 dependsOn createBuildProperties
4438 dependsOn jalviewjsIDE_j2sFile
4439 dependsOn jalviewjsIDE_checkJ2sPlugin
4440 dependsOn jalviewjsIDE_PrepareSite
4445 // buildship runs this at build time or project refresh
4446 task eclipseAutoBuildTask {
4447 //dependsOn jalviewjsIDE_checkJ2sPlugin
4448 //dependsOn jalviewjsIDE_PrepareSite
4452 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4453 from file(jalviewjs_stderr_launch)
4454 into jalviewjsSiteDir
4456 inputs.file jalviewjs_stderr_launch
4457 outputs.file jalviewjsStderrLaunchFilename
4460 task cleanJalviewjsChromiumUserDir {
4462 delete jalviewjsChromiumUserDir
4464 outputs.dir jalviewjsChromiumUserDir
4465 // always run when depended on
4466 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4469 task jalviewjsChromiumProfile {
4470 dependsOn cleanJalviewjsChromiumUserDir
4471 mustRunAfter cleanJalviewjsChromiumUserDir
4473 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4476 mkdir jalviewjsChromiumProfileDir
4479 outputs.file firstRun
4482 task jalviewjsLaunchTest {
4484 description "Check JalviewJS opens in a browser"
4485 dependsOn jalviewjsBuildSite
4486 dependsOn jalviewjsCopyStderrLaunchFile
4487 dependsOn jalviewjsChromiumProfile
4489 def macOS = OperatingSystem.current().isMacOsX()
4490 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4491 if (chromiumBinary.startsWith("~/")) {
4492 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4498 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4500 def binary = file(chromiumBinary)
4501 if (!binary.exists()) {
4502 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4504 stdout = new ByteArrayOutputStream()
4505 stderr = new ByteArrayOutputStream()
4508 if (jalviewjs_j2s_to_console.equals("true")) {
4509 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4512 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4519 // macOS not running properly with timeout arguments
4520 def execArgs = macOS ? [] : [
4521 "--virtual-time-budget=${timeoutms}",
4524 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4527 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4528 "--profile-directory=${jalviewjs_chromium_profile_name}",
4529 "--allow-file-access-from-files",
4530 "--enable-logging=stderr",
4531 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4534 if (true || macOS) {
4535 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4536 Future f1 = executor.submit(
4539 standardOutput = execStdout
4540 errorOutput = execStderr
4541 executable(chromiumBinary)
4543 println "COMMAND: '"+commandLine.join(" ")+"'"
4545 executor.shutdownNow()
4549 def noChangeBytes = 0
4550 def noChangeIterations = 0
4551 executor.scheduleAtFixedRate(
4553 String stderrString = stderr.toString()
4554 // shutdown the task if we have a success string
4555 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4558 executor.shutdownNow()
4560 // if no change in stderr for 10s then also end
4561 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4562 executor.shutdownNow()
4564 if (stderrString.length() == noChangeBytes) {
4565 noChangeIterations++
4567 noChangeBytes = stderrString.length()
4568 noChangeIterations = 0
4571 1, 1, TimeUnit.SECONDS)
4573 executor.schedule(new Runnable(){
4576 executor.shutdownNow()
4578 }, timeoutms, TimeUnit.MILLISECONDS)
4580 executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
4581 executor.shutdownNow()
4588 stderr.toString().eachLine { line ->
4589 if (line.contains(jalviewjs_desktop_init_string)) {
4590 println("Found line '"+line+"'")
4596 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4604 description "Build the JalviewJS site and run the launch test"
4605 dependsOn jalviewjsBuildSite
4606 dependsOn jalviewjsLaunchTest