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"
46 classpath 'ru.vyarus:gradle-use-python-plugin:4.0.0'
55 id "com.diffplug.gradle.spotless" version "3.28.0"
56 id 'com.github.johnrengelman.shadow' version '6.0.0'
57 id 'com.install4j.gradle' version '10.0.3'
58 id 'com.dorongold.task-tree' version '2.1.1' // only needed to display task dependency tree with gradle task1 [task2 ...] taskTree
59 id 'com.palantir.git-version' version '0.13.0' apply false
60 id 'ru.vyarus.use-python' version '4.0.0'
71 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
72 def string(Object o) {
73 return o == null ? "" : o.toString()
76 def overrideProperties(String propsFileName, boolean output = false) {
77 if (propsFileName == null) {
80 def propsFile = file(propsFileName)
81 if (propsFile != null && propsFile.exists()) {
82 println("Using properties from file '${propsFileName}'")
84 def p = new Properties()
85 def localPropsFIS = new FileInputStream(propsFile)
91 if (project.hasProperty(key)) {
92 oldval = project.findProperty(key)
93 project.setProperty(key, val)
95 println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
98 ext.setProperty(key, val)
100 println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
104 } catch (Exception e) {
105 println("Exception reading local.properties")
112 jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
113 jalviewDirRelativePath = jalviewDir
116 getdownChannelName = CHANNEL.toLowerCase()
117 // default to "default". Currently only has different cosmetics for "develop", "release", "default"
118 propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
119 channelDirName = propertiesChannelName
120 // Import channel_properties
121 if (getdownChannelName.startsWith("develop-")) {
122 channelDirName = "develop-SUFFIX"
124 channelDir = string("${jalviewDir}/${channel_properties_dir}/${channelDirName}")
125 channelGradleProperties = string("${channelDir}/channel_gradle.properties")
126 channelPropsFile = string("${channelDir}/${resource_dir}/${channel_props}")
127 overrideProperties(channelGradleProperties, false)
128 // local build environment properties
129 // can be "projectDir/local.properties"
130 overrideProperties("${projectDir}/local.properties", true)
131 // or "../projectDir_local.properties"
132 overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
135 // Import releaseProps from the RELEASE file
136 // or a file specified via JALVIEW_RELEASE_FILE if defined
137 // Expect jalview.version and target release branch in jalview.release
138 releaseProps = new Properties();
139 def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
140 def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
142 (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream {
143 releaseProps.load(it)
145 } catch (Exception fileLoadError) {
146 throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
149 // Set JALVIEW_VERSION if it is not already set
150 if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
151 JALVIEW_VERSION = releaseProps.get("jalview.version")
153 println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
155 // this property set when running Eclipse headlessly
156 j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
157 // this property set by Eclipse
158 eclipseApplicationProperty = string("eclipse.application")
159 // CHECK IF RUNNING FROM WITHIN ECLIPSE
160 def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
161 IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
162 // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
163 if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
164 println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
168 println("WITHIN ECLIPSE IDE")
170 println("HEADLESS BUILD")
173 J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
175 println("J2S ENABLED")
178 System.properties.sort { it.key }.each {
179 key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
182 if (false && IN_ECLIPSE) {
183 jalviewDir = jalviewDirAbsolutePath
188 buildDate = new Date().format("yyyyMMdd")
191 bareSourceDir = string(source_dir)
192 sourceDir = string("${jalviewDir}/${bareSourceDir}")
193 resourceDir = string("${jalviewDir}/${resource_dir}")
194 bareTestSourceDir = string(test_source_dir)
195 testDir = string("${jalviewDir}/${bareTestSourceDir}")
197 classesDir = string("${jalviewDir}/${classes_dir}")
200 useClover = clover.equals("true")
201 cloverBuildDir = "${buildDir}/clover"
202 cloverInstrDir = file("${cloverBuildDir}/clover-instr")
203 cloverClassesDir = file("${cloverBuildDir}/clover-classes")
204 cloverReportDir = file("${buildDir}/reports/clover")
205 cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
206 cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
207 //cloverTestClassesDir = cloverClassesDir
208 cloverDb = string("${cloverBuildDir}/clover.db")
210 testSourceDir = useClover ? cloverTestInstrDir : testDir
211 testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
214 backgroundImageText = BACKGROUNDIMAGETEXT
215 getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
216 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
217 getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
218 getdownFullArchiveDir = null
219 getdownTextLines = []
220 getdownLaunchJvl = null
221 getdownVersionLaunchJvl = null
223 buildProperties = null
225 // the following values might be overridden by the CHANNEL switch
226 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
227 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
228 getdownArchiveAppBase = getdown_archive_base
229 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
230 getdownAppDistDir = getdown_app_dir_alt
231 getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
232 getdownImagesBuildDir = string("${buildDir}/imagemagick/getdown")
233 getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
234 reportRsyncCommand = false
235 jvlChannelName = CHANNEL.toLowerCase()
236 install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
237 install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
238 install4jDMGDSStoreJSON = "${install4j_images_dir}/${install4j_dmg_ds_store_json}"
239 install4jDMGBackgroundImageDir = "${install4j_images_dir}"
240 install4jDMGBackgroundImageBuildDir = "build/imagemagick/install4j"
241 install4jDMGBackgroundImageFile = "${install4j_dmg_background}"
242 install4jmacOSArchiveName = "${jalview_name} Non-Release ${JALVIEW_VERSION} Installer"
243 install4jExecutableName = install4j_executable_name
244 install4jExtraScheme = "jalviewextra"
245 install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
246 install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
247 install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
248 install4jBackground = string("${install4j_images_dir}/${install4j_background}")
249 install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
250 install4jDMGFixedDSStoreX64 = "build/macos_dmg/${install4j_dmg_ds_store}-x64"
251 install4jDMGFixedDSStoreAarch64 = "build/macos_dmg/${install4j_dmg_ds_store}-aarch64"
252 install4jDMGVolumeIcon = string("${install4j_images_dir}/${install4j_dmg_volume_icon}")
253 install4jCheckSums = true
255 applicationName = "${jalview_name}"
259 // TODO: get bamboo build artifact URL for getdown artifacts
260 getdown_channel_base = bamboo_channelbase
261 getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
262 getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
263 jvlChannelName += "_${getdownChannelName}"
264 // automatically add the test group Not-bamboo for exclusion
265 if ("".equals(testng_excluded_groups)) {
266 testng_excluded_groups = "Not-bamboo"
268 install4jExtraScheme = "jalviewb"
269 backgroundImageText = true
272 case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
273 getdownAppDistDir = getdown_app_dir_release
274 getdownSetAppBaseProperty = true
275 reportRsyncCommand = true
277 install4jmacOSArchiveName = "Install ${jalview_name} ${JALVIEW_VERSION}"
278 install4jExtraScheme = (CHANNEL=="RELEASE")?"jalviewx":"jalviewjs"
282 getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
283 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
284 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
285 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
286 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
288 package_dir = string("${ARCHIVEDIR}/${package_dir}")
289 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
292 reportRsyncCommand = true
293 install4jExtraScheme = "jalviewa"
297 getdownChannelName = string("archive/${JALVIEW_VERSION}")
298 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
299 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
300 if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
301 throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution [did not find '${ARCHIVEDIR}/${package_dir}']")
303 package_dir = string("${ARCHIVEDIR}/${package_dir}")
304 buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
307 reportRsyncCommand = true
308 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
309 install4jSuffix = "Archive"
310 install4jExtraScheme = "jalviewa"
313 case ~/^DEVELOP-([\.\-\w]*)$/:
314 def suffix = Matcher.lastMatcher[0][1]
315 reportRsyncCommand = true
316 getdownSetAppBaseProperty = true
317 JALVIEW_VERSION=JALVIEW_VERSION+"-d${suffix}-${buildDate}"
318 install4jSuffix = "Develop ${suffix}"
319 install4jExtraScheme = "jalviewd"
320 install4jmacOSArchiveName = "Install ${jalview_name} Develop ${suffix} ${JALVIEW_VERSION}"
321 getdownChannelName = string("develop-${suffix}")
322 getdownChannelDir = string("${getdown_website_dir}/${getdownChannelName}")
323 getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
324 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
325 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
326 channelSuffix = string(suffix)
327 backgroundImageText = true
331 reportRsyncCommand = true
332 getdownSetAppBaseProperty = true
333 // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
334 JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
336 install4jSuffix = "Develop"
337 install4jExtraScheme = "jalviewd"
338 install4jmacOSArchiveName = "Install ${jalview_name} Develop ${JALVIEW_VERSION}"
339 backgroundImageText = true
343 reportRsyncCommand = true
344 getdownSetAppBaseProperty = true
345 // Don't ignore transpile errors for release build
346 if (jalviewjs_ignore_transpile_errors.equals("true")) {
347 jalviewjs_ignore_transpile_errors = "false"
348 println("Setting jalviewjs_ignore_transpile_errors to 'false'")
350 JALVIEW_VERSION = JALVIEW_VERSION+"-test"
351 install4jSuffix = "Test"
352 install4jExtraScheme = "jalviewt"
353 install4jmacOSArchiveName = "Install ${jalview_name} Test ${JALVIEW_VERSION}"
354 backgroundImageText = true
357 case ~/^SCRATCH(|-[-\w]*)$/:
358 getdownChannelName = CHANNEL
359 JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
361 getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
362 getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
363 reportRsyncCommand = true
364 install4jSuffix = "Scratch"
368 if (!file("${LOCALDIR}").exists()) {
369 throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
371 getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
372 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
374 JALVIEW_VERSION = "TEST"
375 install4jSuffix = "Test-Local"
376 install4jExtraScheme = "jalviewt"
377 install4jmacOSArchiveName = "Install ${jalview_name} Test ${JALVIEW_VERSION}"
378 backgroundImageText = true
381 case [ "LOCAL", "JALVIEWJS" ]:
382 JALVIEW_VERSION = "TEST"
383 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
384 getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
385 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
386 install4jExtraScheme = "jalviewl"
387 install4jCheckSums = false
390 default: // something wrong specified
391 throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
395 JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
396 hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
397 hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
398 // override getdownAppBase if requested
399 if (findProperty("getdown_appbase_override") != null) {
400 // revert to LOCAL if empty string
401 if (string(getdown_appbase_override) == "") {
402 getdownAppBase = file(getdownAppBaseDir).toURI().toString()
403 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
404 } else if (string(getdown_appbase_override).startsWith("file://")) {
405 getdownAppBase = string(getdown_appbase_override)
406 getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
408 getdownAppBase = string(getdown_appbase_override)
410 println("Overriding getdown appbase with '${getdownAppBase}'")
412 // sanitise file name for jalview launcher file for this channel
413 jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
414 // install4j application and folder names
415 if (install4jSuffix == "") {
416 install4jBundleId = "${install4j_bundle_id}"
417 install4jWinApplicationId = install4j_release_win_application_id
419 applicationName = "${jalview_name} ${install4jSuffix}"
420 install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
421 // add int hash of install4jSuffix to the last part of the application_id
422 def id = install4j_release_win_application_id
423 def idsplitreverse = id.split("-").reverse()
424 idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
425 install4jWinApplicationId = idsplitreverse.reverse().join("-")
427 // sanitise folder and id names
428 // install4jApplicationFolder = e.g. "Jalview Build"
429 install4jApplicationFolder = applicationName
430 .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
431 .replaceAll("_+", "_") // collapse __
432 install4jInternalId = applicationName
434 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
435 .replaceAll("_+", "") // collapse __
436 //.replaceAll("_*-_*", "-") // collapse _-_
437 install4jUnixApplicationFolder = applicationName
439 .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
440 .replaceAll("_+", "_") // collapse __
441 .replaceAll("_*-_*", "-") // collapse _-_
443 install4jmacOSArchiveX64Name = "${install4jmacOSArchiveName} (Intel)"
444 install4jmacOSArchiveAarch64Name = "${install4jmacOSArchiveName} (Apple Silicon)"
446 getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
447 getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
448 //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
449 getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
450 getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
451 getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
452 getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
453 /* compile without modules -- using classpath libraries
454 modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
455 modules_runtimeClasspath = modules_compileClasspath
461 apply plugin: "com.palantir.git-version"
462 def details = versionDetails()
463 gitHash = details.gitHash
464 gitBranch = details.branchName
465 } catch(org.gradle.api.internal.plugins.PluginApplicationException e) {
466 println("Not in a git repository. Using git values from RELEASE properties file.")
467 gitHash = releaseProps.getProperty("git.hash")
468 gitBranch = releaseProps.getProperty("git.branch")
469 } catch(java.lang.RuntimeException e1) {
470 throw new GradleException("Error with git-version plugin. Directory '.git' exists but versionDetails() cannot be found.")
473 println("Using a ${CHANNEL} profile.")
475 additional_compiler_args = []
476 // configure classpath/args for j8/j11 compilation
477 if (JAVA_VERSION.equals("1.8")) {
478 JAVA_INTEGER_VERSION = string("8")
481 libDistDir = j8libDir
482 compile_source_compatibility = 1.8
483 compile_target_compatibility = 1.8
484 // these are getdown.txt properties defined dependent on the JAVA_VERSION
485 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
486 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
487 // this property is assigned below and expanded to multiple lines in the getdown task
488 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
489 // this property is for the Java library used in eclipse
490 eclipseJavaRuntimeName = string("JavaSE-1.8")
491 } else if (JAVA_VERSION.equals("11")) {
492 JAVA_INTEGER_VERSION = string("11")
494 libDistDir = j11libDir
495 compile_source_compatibility = 11
496 compile_target_compatibility = 11
497 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
498 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
499 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
500 eclipseJavaRuntimeName = string("JavaSE-11")
501 /* compile without modules -- using classpath libraries
502 additional_compiler_args += [
503 '--module-path', modules_compileClasspath.asPath,
504 '--add-modules', j11modules
507 } else if (JAVA_VERSION.equals("17")) {
508 JAVA_INTEGER_VERSION = string("17")
510 libDistDir = j17libDir
511 compile_source_compatibility = 17
512 compile_target_compatibility = 17
513 getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
514 getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
515 getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
516 eclipseJavaRuntimeName = string("JavaSE-17")
517 /* compile without modules -- using classpath libraries
518 additional_compiler_args += [
519 '--module-path', modules_compileClasspath.asPath,
520 '--add-modules', j11modules
524 throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
529 JAVA_MIN_VERSION = JAVA_VERSION
530 JAVA_MAX_VERSION = JAVA_VERSION
531 jreInstallsDir = string(jre_installs_dir)
532 if (jreInstallsDir.startsWith("~/")) {
533 jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
535 install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
536 install4jConfFileName = string("jalview-install4j-conf.install4j")
537 install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
538 install4jHomeDir = install4j_home_dir
539 if (install4jHomeDir.startsWith("~/")) {
540 install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
542 install4jmacOSArchiveX64DMGFilename = "${install4jApplicationFolder}-${JALVIEW_VERSION}-macos-x64-java_${JAVA_INTEGER_VERSION}"
543 install4jmacOSArchiveAarch64DMGFilename = "${install4jApplicationFolder}-${JALVIEW_VERSION}-macos-aarch64-java_${JAVA_INTEGER_VERSION}"
546 resourceBuildDir = string("${buildDir}/resources")
547 resourcesBuildDir = string("${resourceBuildDir}/resources_build")
548 helpBuildDir = string("${resourceBuildDir}/help_build")
549 docBuildDir = string("${resourceBuildDir}/doc_build")
551 if (buildProperties == null) {
552 buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
554 buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
555 helpParentDir = string("${jalviewDir}/${help_parent_dir}")
556 helpSourceDir = string("${helpParentDir}/${help_dir}")
557 helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
560 convertBinaryExpectedLocation = imagemagick_convert
561 if (convertBinaryExpectedLocation.startsWith("~/")) {
562 convertBinaryExpectedLocation = System.getProperty("user.home") + convertBinaryExpectedLocation.substring(1)
564 if (file(convertBinaryExpectedLocation).exists()) {
565 convertBinary = convertBinaryExpectedLocation
568 relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
569 jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
570 jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
572 jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
574 jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
576 jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
577 jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
578 jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
579 jalviewjsJalviewCoreHtmlFile = string("")
580 jalviewjsJalviewCoreName = string(jalviewjs_core_name)
581 jalviewjsCoreClasslists = []
582 jalviewjsJalviewTemplateName = string(jalviewjs_name)
583 jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
584 jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
585 jalviewjsJ2sProps = null
586 jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
587 jalviewjsStderrLaunchFilename = "${jalviewjsSiteDir}/"+(file(jalviewjs_stderr_launch).getName())
589 eclipseWorkspace = null
590 eclipseBinary = string("")
591 eclipseVersion = string("")
594 jalviewjsChromiumUserDir = "${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}"
595 jalviewjsChromiumProfileDir = "${ext.jalviewjsChromiumUserDir}/${jalviewjs_chromium_profile_name}"
605 outputDir = file(classesDir)
609 srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
612 compileClasspath = files(sourceSets.main.java.outputDir)
613 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
615 runtimeClasspath = compileClasspath
616 runtimeClasspath += files(sourceSets.main.resources.srcDirs)
621 srcDirs cloverInstrDir
622 outputDir = cloverClassesDir
626 srcDirs = sourceSets.main.resources.srcDirs
629 compileClasspath = files( sourceSets.clover.java.outputDir )
630 //compileClasspath += files( testClassesDir )
631 compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
632 compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
633 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
635 runtimeClasspath = compileClasspath
640 srcDirs testSourceDir
641 outputDir = file(testClassesDir)
645 srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
648 compileClasspath = files( sourceSets.test.java.outputDir )
649 compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
650 compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
652 runtimeClasspath = compileClasspath
653 runtimeClasspath += files(sourceSets.test.resources.srcDirs)
659 // eclipse project and settings files creation, also used by buildship
662 name = eclipse_project_name
664 natures 'org.eclipse.jdt.core.javanature',
665 'org.eclipse.jdt.groovy.core.groovyNature',
666 'org.eclipse.buildship.core.gradleprojectnature'
668 buildCommand 'org.eclipse.jdt.core.javabuilder'
669 buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
673 //defaultOutputDir = sourceSets.main.java.outputDir
674 configurations.each{ c->
675 if (c.isCanBeResolved()) {
676 minusConfigurations += [c]
680 plusConfigurations = [ ]
684 def removeTheseToo = []
685 HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
686 cp.entries.each { entry ->
687 // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
688 // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
689 // we add the resources and help/help dirs in as libs afterwards (see below)
690 if (entry.kind == 'src') {
691 if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
692 removeTheseToo += entry
694 alreadyAddedSrcPath.putAt(entry.path, true)
699 cp.entries.removeAll(removeTheseToo)
701 //cp.entries += new Output("${eclipse_bin_dir}/main")
702 if (file(helpParentDir).isDirectory()) {
703 cp.entries += new Library(fileReference(helpParentDir))
705 if (file(resourceDir).isDirectory()) {
706 cp.entries += new Library(fileReference(resourceDir))
709 HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
711 sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
712 //don't want to add outputDir as eclipse is using its own output dir in bin/main
713 if (it.isDirectory() || ! it.exists()) {
714 // don't add dirs to classpath, especially if they don't exist
715 return false // groovy "continue" in .any closure
717 def itPath = it.toString()
718 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
719 // make relative path
720 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
722 if (alreadyAddedLibPath.get(itPath)) {
723 //println("Not adding duplicate entry "+itPath)
725 //println("Adding entry "+itPath)
726 cp.entries += new Library(fileReference(itPath))
727 alreadyAddedLibPath.put(itPath, true)
731 sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
732 //no longer want to add outputDir as eclipse is using its own output dir in bin/main
733 if (it.isDirectory() || ! it.exists()) {
734 // don't add dirs to classpath
735 return false // groovy "continue" in .any closure
738 def itPath = it.toString()
739 if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
740 itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
742 if (alreadyAddedLibPath.get(itPath)) {
745 def lib = new Library(fileReference(itPath))
746 lib.entryAttributes["test"] = "true"
748 alreadyAddedLibPath.put(itPath, true)
756 containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
761 // for the IDE, use java 11 compatibility
762 sourceCompatibility = compile_source_compatibility
763 targetCompatibility = compile_target_compatibility
764 javaRuntimeName = eclipseJavaRuntimeName
766 // add in jalview project specific properties/preferences into eclipse core preferences
768 withProperties { props ->
769 def jalview_prefs = new Properties()
770 def ins = new FileInputStream("${jalviewDirAbsolutePath}/${eclipse_extra_jdt_prefs_file}")
771 jalview_prefs.load(ins)
773 jalview_prefs.forEach { t, v ->
774 if (props.getAt(t) == null) {
778 // codestyle file -- overrides previous formatter prefs
779 def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
780 if (csFile.exists()) {
781 XmlParser parser = new XmlParser()
782 def profiles = parser.parse(csFile)
783 def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
784 if (profile != null) {
785 profile.'setting'.each { s ->
787 def value = s.'@value'
788 if (id != null && value != null) {
789 props.putAt(id, value)
800 // Don't want these to be activated if in headless build
801 synchronizationTasks "eclipseSynchronizationTask"
802 //autoBuildTasks "eclipseAutoBuildTask"
808 /* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
809 // Class to allow updating arbitrary properties files
810 class PropertiesFile extends PropertiesPersistableConfigurationObject {
811 public PropertiesFile(PropertiesTransformer t) { super(t); }
812 @Override protected void load(Properties properties) { }
813 @Override protected void store(Properties properties) { }
814 @Override protected String getDefaultResourceName() { return ""; }
815 // This is necessary, because PropertiesPersistableConfigurationObject fails
816 // if no default properties file exists.
817 @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
820 // Task to update arbitrary properties files (set outputFile)
821 class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
822 private final PropertiesFileContentMerger file;
823 public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
824 protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
825 protected void configure(PropertiesFile props) {
826 file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
828 public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
831 task eclipseUIPreferences(type: PropertiesFileTask) {
832 description = "Generate Eclipse additional settings"
833 def filename = "org.eclipse.jdt.ui.prefs"
834 outputFile = "$projectDir/.settings/${filename}" as File
837 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
842 task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
843 description = "Generate Eclipse additional settings"
844 def filename = "org.eclipse.jdt.groovy.core.prefs"
845 outputFile = "$projectDir/.settings/${filename}" as File
848 it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
853 task eclipseAllPreferences {
855 dependsOn eclipseUIPreferences
856 dependsOn eclipseGroovyCorePreferences
859 eclipseUIPreferences.mustRunAfter eclipseJdt
860 eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
862 /* end of eclipse preferences hack */
870 delete cloverBuildDir
871 delete cloverReportDir
876 task cloverInstrJava(type: JavaExec) {
877 group = "Verification"
878 description = "Create clover instrumented source java files"
880 dependsOn cleanClover
882 inputs.files(sourceSets.main.allJava)
883 outputs.dir(cloverInstrDir)
885 //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
886 classpath = sourceSets.clover.compileClasspath
887 main = "com.atlassian.clover.CloverInstr"
895 cloverInstrDir.getPath(),
897 def srcFiles = sourceSets.main.allJava.files
900 { file -> file.absolutePath }
903 args argsList.toArray()
906 delete cloverInstrDir
907 println("Clover: About to instrument "+srcFiles.size() +" files")
912 task cloverInstrTests(type: JavaExec) {
913 group = "Verification"
914 description = "Create clover instrumented source test files"
916 dependsOn cleanClover
918 inputs.files(testDir)
919 outputs.dir(cloverTestInstrDir)
921 classpath = sourceSets.clover.compileClasspath
922 main = "com.atlassian.clover.CloverInstr"
932 cloverTestInstrDir.getPath(),
934 args argsList.toArray()
937 delete cloverTestInstrDir
938 println("Clover: About to instrument test files")
944 group = "Verification"
945 description = "Create clover instrumented all source files"
947 dependsOn cloverInstrJava
948 dependsOn cloverInstrTests
952 cloverClasses.dependsOn cloverInstr
955 task cloverConsoleReport(type: JavaExec) {
956 group = "Verification"
957 description = "Creates clover console report"
960 file(cloverDb).exists()
963 inputs.dir cloverClassesDir
965 classpath = sourceSets.clover.runtimeClasspath
966 main = "com.atlassian.clover.reporters.console.ConsoleReporter"
968 if (cloverreport_mem.length() > 0) {
969 maxHeapSize = cloverreport_mem
971 if (cloverreport_jvmargs.length() > 0) {
972 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
982 args argsList.toArray()
986 task cloverHtmlReport(type: JavaExec) {
987 group = "Verification"
988 description = "Creates clover HTML report"
991 file(cloverDb).exists()
994 def cloverHtmlDir = cloverReportDir
995 inputs.dir cloverClassesDir
996 outputs.dir cloverHtmlDir
998 classpath = sourceSets.clover.runtimeClasspath
999 main = "com.atlassian.clover.reporters.html.HtmlReporter"
1001 if (cloverreport_mem.length() > 0) {
1002 maxHeapSize = cloverreport_mem
1004 if (cloverreport_jvmargs.length() > 0) {
1005 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1016 if (cloverreport_html_options.length() > 0) {
1017 argsList += cloverreport_html_options.split(" ")
1020 args argsList.toArray()
1024 task cloverXmlReport(type: JavaExec) {
1025 group = "Verification"
1026 description = "Creates clover XML report"
1029 file(cloverDb).exists()
1032 def cloverXmlFile = "${cloverReportDir}/clover.xml"
1033 inputs.dir cloverClassesDir
1034 outputs.file cloverXmlFile
1036 classpath = sourceSets.clover.runtimeClasspath
1037 main = "com.atlassian.clover.reporters.xml.XMLReporter"
1039 if (cloverreport_mem.length() > 0) {
1040 maxHeapSize = cloverreport_mem
1042 if (cloverreport_jvmargs.length() > 0) {
1043 jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
1054 if (cloverreport_xml_options.length() > 0) {
1055 argsList += cloverreport_xml_options.split(" ")
1058 args argsList.toArray()
1063 group = "Verification"
1064 description = "Creates clover reports"
1066 dependsOn cloverXmlReport
1067 dependsOn cloverHtmlReport
1074 sourceCompatibility = compile_source_compatibility
1075 targetCompatibility = compile_target_compatibility
1076 options.compilerArgs += additional_compiler_args
1077 print ("Setting target compatibility to "+targetCompatibility+"\n")
1079 //classpath += configurations.cloverRuntime
1085 // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
1086 sourceCompatibility = compile_source_compatibility
1087 targetCompatibility = compile_target_compatibility
1088 options.compilerArgs += additional_compiler_args
1089 options.encoding = "UTF-8"
1091 print ("Setting target compatibility to "+compile_target_compatibility+"\n")
1098 sourceCompatibility = compile_source_compatibility
1099 targetCompatibility = compile_target_compatibility
1100 options.compilerArgs += additional_compiler_args
1102 print ("Setting target compatibility to "+targetCompatibility+"\n")
1109 delete sourceSets.main.java.outputDir
1115 dependsOn cleanClover
1117 delete sourceSets.test.java.outputDir
1122 // format is a string like date.format("dd MMMM yyyy")
1123 def getDate(format) {
1124 return date.format(format)
1128 def convertMdToHtml (FileTree mdFiles, File cssFile) {
1129 MutableDataSet options = new MutableDataSet()
1131 def extensions = new ArrayList<>()
1132 extensions.add(AnchorLinkExtension.create())
1133 extensions.add(AutolinkExtension.create())
1134 extensions.add(StrikethroughExtension.create())
1135 extensions.add(TaskListExtension.create())
1136 extensions.add(TablesExtension.create())
1137 extensions.add(TocExtension.create())
1139 options.set(Parser.EXTENSIONS, extensions)
1141 // set GFM table parsing options
1142 options.set(TablesExtension.WITH_CAPTION, false)
1143 options.set(TablesExtension.COLUMN_SPANS, false)
1144 options.set(TablesExtension.MIN_HEADER_ROWS, 1)
1145 options.set(TablesExtension.MAX_HEADER_ROWS, 1)
1146 options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
1147 options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
1148 options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
1150 options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
1151 options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
1152 options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
1153 options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
1155 Parser parser = Parser.builder(options).build()
1156 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1158 mdFiles.each { mdFile ->
1159 // add table of contents
1160 def mdText = "[TOC]\n"+mdFile.text
1162 // grab the first top-level title
1164 def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
1165 def matcher = mdText =~ titleRegex
1166 if (matcher.size() > 0) {
1167 // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
1168 title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
1170 // or use the filename if none found
1171 if (title == null) {
1172 title = mdFile.getName()
1175 Node document = parser.parse(mdText)
1176 String htmlBody = renderer.render(document)
1177 def htmlText = '''<html>
1178 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1179 <html xmlns="http://www.w3.org/1999/xhtml">
1181 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1182 <meta http-equiv="Content-Style-Type" content="text/css" />
1183 <meta name="generator" content="flexmark" />
1185 htmlText += ((title != null) ? " <title>${title}</title>" : '' )
1187 <style type="text/css">code{white-space: pre;}</style>
1189 htmlText += ((cssFile != null) ? cssFile.text : '')
1190 htmlText += '''</head>
1193 htmlText += htmlBody
1199 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1200 def htmlFile = file(htmlFilePath)
1201 println("Creating ${htmlFilePath}")
1202 htmlFile.text = htmlText
1207 task copyDocs(type: Copy) {
1208 def inputDir = "${jalviewDir}/${doc_dir}"
1209 def outputDir = "${docBuildDir}/${doc_dir}"
1213 include('**/*.html')
1215 filter(ReplaceTokens,
1219 'Version-Rel': JALVIEW_VERSION,
1220 'Year-Rel': getDate("yyyy")
1227 exclude('**/*.html')
1232 inputs.dir(inputDir)
1233 outputs.dir(outputDir)
1237 task convertMdFiles {
1239 def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
1240 def cssFile = file("${jalviewDir}/${flexmark_css}")
1243 convertMdToHtml(mdFiles, cssFile)
1246 inputs.files(mdFiles)
1247 inputs.file(cssFile)
1250 mdFiles.each { mdFile ->
1251 def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
1252 htmlFiles.add(file(htmlFilePath))
1254 outputs.files(htmlFiles)
1258 def hugoTemplateSubstitutions(String input, Map extras=null) {
1259 def replacements = [
1260 DATE: getDate("yyyy-MM-dd"),
1261 CHANNEL: propertiesChannelName,
1262 APPLICATION_NAME: applicationName,
1264 GIT_BRANCH: gitBranch,
1265 VERSION: JALVIEW_VERSION,
1266 JAVA_VERSION: JAVA_VERSION,
1267 VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
1272 if (extras != null) {
1273 extras.each{ k, v ->
1274 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1277 replacements.each{ k, v ->
1278 output = output.replaceAll("__${k}__", ((v == null)?"":v))
1283 def mdFileComponents(File mdFile, def dateOnly=false) {
1286 if (mdFile.exists()) {
1287 def inFrontMatter = false
1288 def firstLine = true
1289 mdFile.eachLine { line ->
1290 if (line.matches("---")) {
1291 def prev = inFrontMatter
1292 inFrontMatter = firstLine
1293 if (inFrontMatter != prev)
1296 if (inFrontMatter) {
1298 if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
1299 map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
1300 } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
1301 map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
1302 } else if (m = line =~ /^channel:\s*(\S+)/) {
1303 map["channel"] = m[0][1]
1304 } else if (m = line =~ /^version:\s*(\S+)/) {
1305 map["version"] = m[0][1]
1306 } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
1307 map[ m[0][1] ] = m[0][2]
1309 if (dateOnly && map["date"] != null) {
1315 content += line+"\n"
1320 return dateOnly ? map["date"] : [map, content]
1323 task hugoTemplates {
1325 description "Create partially populated md pages for hugo website build"
1327 def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
1328 def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
1329 def templateFiles = fileTree(dir: hugoTemplatesDir)
1330 def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1331 def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1332 def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
1333 def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
1336 // specific release template for version archive
1339 def givenDate = null
1340 def givenChannel = null
1341 def givenVersion = null
1342 if (CHANNEL == "RELEASE") {
1343 def (map, content) = mdFileComponents(releaseMdFile)
1344 givenDate = map.date
1345 givenChannel = map.channel
1346 givenVersion = map.version
1348 if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
1349 throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
1352 if (whatsnewMdFile.exists())
1353 whatsnew = whatsnewMdFile.text
1356 def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
1357 def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
1359 def changesHugo = null
1360 if (changes != null) {
1361 changesHugo = '<div class="release_notes">\n\n'
1362 def inSection = false
1363 changes.eachLine { line ->
1365 if (m = line =~ /^##([^#].*)$/) {
1367 changesHugo += "</div>\n\n"
1369 def section = m[0][1].trim()
1370 section = section.toLowerCase()
1371 section = section.replaceAll(/ +/, "_")
1372 section = section.replaceAll(/[^a-z0-9_\-]/, "")
1373 changesHugo += "<div class=\"${section}\">\n\n"
1375 } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
1376 def comment = m[0][2].trim()
1377 if (comment != "") {
1378 comment = comment.replaceAll('"', """)
1380 comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
1381 def newline = m[0][1]
1382 if (comment.trim() != "")
1383 newline += "{{<comment>}}${comment}{{</comment>}} "
1384 newline += m[0][3].trim()
1385 if (issuekeys.size() > 0)
1386 newline += " {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
1387 if (m[0][4] != null)
1392 changesHugo += line+"\n"
1395 changesHugo += "\n</div>\n\n"
1397 changesHugo += '</div>'
1400 templateFiles.each{ templateFile ->
1401 def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
1402 def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
1403 def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
1405 def outPathName = string("${hugoBuildDir}/$newRelPathName")
1409 rename(templateFile.getName(), newFileName)
1413 def newFile = file("${outPathName}/${newFileName}".toString())
1414 def content = newFile.text
1415 newFile.text = hugoTemplateSubstitutions(content,
1418 CHANGES: changesHugo,
1419 DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
1420 DRAFT: givenDate == null ? "true" : "false",
1421 JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
1422 JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
1429 inputs.file(oldJvlFile)
1430 inputs.dir(hugoTemplatesDir)
1431 inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
1432 inputs.property("CHANNEL", { CHANNEL })
1435 def getMdDate(File mdFile) {
1436 return mdFileComponents(mdFile, true)
1439 def getMdSections(String content) {
1441 def sectionContent = ""
1442 def sectionName = null
1443 content.eachLine { line ->
1445 if (m = line =~ /^##([^#].*)$/) {
1446 if (sectionName != null) {
1447 sections[sectionName] = sectionContent
1451 sectionName = m[0][1].trim()
1452 sectionName = sectionName.toLowerCase()
1453 sectionName = sectionName.replaceAll(/ +/, "_")
1454 sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
1455 } else if (sectionName != null) {
1456 sectionContent += line+"\n"
1459 if (sectionContent != null) {
1460 sections[sectionName] = sectionContent
1466 task copyHelp(type: Copy) {
1467 def inputDir = helpSourceDir
1468 def outputDir = "${helpBuildDir}/${help_dir}"
1472 include('**/*.html')
1476 filter(ReplaceTokens,
1480 'Version-Rel': JALVIEW_VERSION,
1481 'Year-Rel': getDate("yyyy")
1488 exclude('**/*.html')
1495 inputs.dir(inputDir)
1496 outputs.files(helpFile)
1497 outputs.dir(outputDir)
1501 task releasesTemplates {
1503 description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
1507 def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
1508 def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
1509 def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
1510 def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
1511 def releasesMdDir = "${jalviewDir}/${releases_dir}"
1512 def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
1515 def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
1516 def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
1518 if (CHANNEL == "RELEASE") {
1519 if (!releaseMdFile.exists()) {
1520 throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
1522 if (!whatsnewMdFile.exists()) {
1523 throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
1527 def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
1528 def releaseFilesDates = releaseFiles.collectEntries {
1529 [(it): getMdDate(it)]
1531 releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
1533 def releasesTemplate = releasesTemplateFile.text
1534 def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
1535 def versionTemplate = m[0][1]
1537 MutableDataSet options = new MutableDataSet()
1539 def extensions = new ArrayList<>()
1540 options.set(Parser.EXTENSIONS, extensions)
1541 options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
1543 Parser parser = Parser.builder(options).build()
1544 HtmlRenderer renderer = HtmlRenderer.builder(options).build()
1546 def actualVersions = releaseFiles.collect { rf ->
1547 def (rfMap, rfContent) = mdFileComponents(rf)
1548 return rfMap.version
1550 def versionsHtml = ""
1551 def linkedVersions = []
1552 releaseFiles.reverse().each { rFile ->
1553 def (rMap, rContent) = mdFileComponents(rFile)
1555 def versionLink = ""
1556 def partialVersion = ""
1557 def firstPart = true
1558 rMap.version.split("\\.").each { part ->
1559 def displayPart = ( firstPart ? "" : "." ) + part
1560 partialVersion += displayPart
1562 linkedVersions.contains(partialVersion)
1563 || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
1565 versionLink += displayPart
1567 versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
1568 linkedVersions += partialVersion
1572 def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
1575 def rContentProcessed = ""
1576 rContent.eachLine { line ->
1577 if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
1578 line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
1579 } else if (lm = line =~ /^###([^#]+.*)$/) {
1580 line = "_${lm[0][1].trim()}_"
1582 rContentProcessed += line + "\n"
1585 def rContentSections = getMdSections(rContentProcessed)
1586 def rVersion = versionTemplate
1587 if (rVersion != "") {
1588 def rNewFeatures = rContentSections["new_features"]
1589 def rIssuesResolved = rContentSections["issues_resolved"]
1590 Node newFeaturesNode = parser.parse(rNewFeatures)
1591 String newFeaturesHtml = renderer.render(newFeaturesNode)
1592 Node issuesResolvedNode = parser.parse(rIssuesResolved)
1593 String issuesResolvedHtml = renderer.render(issuesResolvedNode)
1594 rVersion = hugoTemplateSubstitutions(rVersion,
1596 VERSION: rMap.version,
1597 VERSION_LINK: versionLink,
1598 DISPLAY_DATE: displayDate,
1599 NEW_FEATURES: newFeaturesHtml,
1600 ISSUES_RESOLVED: issuesResolvedHtml
1603 versionsHtml += rVersion
1607 releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
1608 releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
1609 releasesHtmlFile.text = releasesTemplate
1611 if (whatsnewMdFile.exists()) {
1612 def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
1613 def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
1614 Node whatsnewNode = parser.parse(whatsnewMd)
1615 String whatsnewHtml = renderer.render(whatsnewNode)
1616 whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
1617 whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
1619 VERSION: JALVIEW_VERSION,
1620 DISPLAY_DATE: wnDisplayDate
1623 } else if (gradle.taskGraph.hasTask(":linkCheck")) {
1624 whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
1629 inputs.file(releasesTemplateFile)
1630 inputs.file(whatsnewTemplateFile)
1631 inputs.dir(releasesMdDir)
1632 inputs.dir(whatsnewMdDir)
1633 outputs.file(releasesHtmlFile)
1634 outputs.file(whatsnewHtmlFile)
1638 task copyResources(type: Copy) {
1640 description = "Copy (and make text substitutions in) the resources dir to the build area"
1642 def inputDir = resourceDir
1643 def outputDir = resourcesBuildDir
1647 include('**/*.html')
1649 filter(ReplaceTokens,
1653 'Version-Rel': JALVIEW_VERSION,
1654 'Year-Rel': getDate("yyyy")
1661 exclude('**/*.html')
1666 inputs.dir(inputDir)
1667 outputs.dir(outputDir)
1670 task copyChannelResources(type: Copy) {
1671 dependsOn copyResources
1673 description = "Copy the channel resources dir to the build resources area"
1675 def inputDir = "${channelDir}/${resource_dir}"
1676 def outputDir = resourcesBuildDir
1678 include(channel_props)
1679 filter(ReplaceTokens,
1683 'SUFFIX': channelSuffix
1688 exclude(channel_props)
1692 inputs.dir(inputDir)
1693 outputs.dir(outputDir)
1696 task createBuildProperties(type: WriteProperties) {
1697 dependsOn copyResources
1699 description = "Create the ${buildProperties} file"
1701 inputs.dir(sourceDir)
1702 inputs.dir(resourcesBuildDir)
1703 outputFile (buildProperties)
1704 // taking time specific comment out to allow better incremental builds
1705 comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
1706 //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
1707 property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
1708 property "VERSION", JALVIEW_VERSION
1709 property "INSTALLATION", INSTALLATION+" git-commit:"+gitHash+" ["+gitBranch+"]"
1710 property "JAVA_COMPILE_VERSION", JAVA_INTEGER_VERSION
1711 if (getdownSetAppBaseProperty) {
1712 property "GETDOWNAPPBASE", getdownAppBase
1713 property "GETDOWNAPPDISTDIR", getdownAppDistDir
1715 outputs.file(outputFile)
1719 task buildIndices(type: JavaExec) {
1721 classpath = sourceSets.main.compileClasspath
1722 main = "com.sun.java.help.search.Indexer"
1723 workingDir = "${helpBuildDir}/${help_dir}"
1726 inputs.dir("${workingDir}/${argDir}")
1728 outputs.dir("${classesDir}/doc")
1729 outputs.dir("${classesDir}/help")
1730 outputs.file("${workingDir}/JavaHelpSearch/DOCS")
1731 outputs.file("${workingDir}/JavaHelpSearch/DOCS.TAB")
1732 outputs.file("${workingDir}/JavaHelpSearch/OFFSETS")
1733 outputs.file("${workingDir}/JavaHelpSearch/POSITIONS")
1734 outputs.file("${workingDir}/JavaHelpSearch/SCHEMA")
1735 outputs.file("${workingDir}/JavaHelpSearch/TMAP")
1738 task buildResources {
1739 dependsOn copyResources
1740 dependsOn copyChannelResources
1741 dependsOn createBuildProperties
1745 dependsOn buildResources
1748 dependsOn releasesTemplates
1749 dependsOn convertMdFiles
1750 dependsOn buildIndices
1754 compileJava.dependsOn prepare
1755 run.dependsOn compileJava
1756 compileTestJava.dependsOn compileJava
1761 group = "Verification"
1762 description = "Runs all testTaskN tasks)"
1765 dependsOn cloverClasses
1767 dependsOn testClasses
1770 // not running tests in this task
1773 /* testTask0 is the main test task */
1774 task testTask0(type: Test) {
1775 group = "Verification"
1776 description = "The main test task. Runs all non-testTaskN-labelled tests (unless excluded)"
1778 includeGroups testng_groups.split(",")
1779 excludeGroups testng_excluded_groups.split(",")
1780 tasks.withType(Test).matching {it.name.startsWith("testTask") && it.name != name}.all {t -> excludeGroups t.name}
1782 useDefaultListeners=true
1784 timeout = Duration.ofMinutes(15)
1787 /* separated tests */
1788 task testTask1(type: Test) {
1789 group = "Verification"
1790 description = "Tests that need to be isolated from the main test run"
1793 excludeGroups testng_excluded_groups.split(",")
1795 useDefaultListeners=true
1797 timeout = Duration.ofMinutes(5)
1800 task testTask2(type: Test) {
1801 group = "Verification"
1802 description = "Tests that need to be isolated from the main test run"
1805 excludeGroups testng_excluded_groups.split(",")
1807 useDefaultListeners=true
1809 timeout = Duration.ofMinutes(5)
1811 task testTask3(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
1820 timeout = Duration.ofMinutes(5)
1823 /* insert more testTaskNs here -- change N to next digit or other string */
1825 task testTaskN(type: Test) {
1826 group = "Verification"
1827 description = "Tests that need to be isolated from the main test run"
1830 excludeGroups testng_excluded_groups.split(",")
1832 useDefaultListeners=true
1839 * adapted from https://medium.com/@wasyl/pretty-tests-summary-in-gradle-744804dd676c
1840 * to summarise test results from all Test tasks
1842 /* START of test tasks results summary */
1843 import groovy.time.TimeCategory
1844 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1845 import org.gradle.api.tasks.testing.logging.TestLogEvent
1846 rootProject.ext.testsResults = [] // Container for tests summaries
1848 tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { testTask ->
1850 // from original test task
1852 dependsOn cloverClasses
1854 dependsOn testClasses //?
1857 // run main tests first
1858 if (!testTask.name.equals("testTask0"))
1859 testTask.mustRunAfter "testTask0"
1861 testTask.testLogging { logging ->
1862 events TestLogEvent.FAILED
1863 // TestLogEvent.SKIPPED,
1864 // TestLogEvent.STANDARD_OUT,
1865 // TestLogEvent.STANDARD_ERROR
1867 exceptionFormat TestExceptionFormat.FULL
1870 showStackTraces true
1872 showStandardStreams true
1874 info.events = [ TestLogEvent.FAILED ]
1877 if (OperatingSystem.current().isMacOsX()) {
1878 testTask.systemProperty "apple.awt.UIElement", "true"
1879 testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true"
1883 ignoreFailures = true // Always try to run all tests for all modules
1885 afterSuite { desc, result ->
1887 return // Only summarize results for whole modules
1889 def resultsInfo = [testTask.project.name, testTask.name, result, TimeCategory.minus(new Date(result.endTime), new Date(result.startTime)), testTask.reports.html.entryPoint]
1891 rootProject.ext.testsResults.add(resultsInfo)
1894 // from original test task
1895 maxHeapSize = "1024m"
1897 workingDir = jalviewDir
1898 def testLaf = project.findProperty("test_laf")
1899 if (testLaf != null) {
1900 println("Setting Test LaF to '${testLaf}'")
1901 systemProperty "laf", testLaf
1903 def testHiDPIScale = project.findProperty("test_HiDPIScale")
1904 if (testHiDPIScale != null) {
1905 println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
1906 systemProperty "sun.java2d.uiScale", testHiDPIScale
1908 sourceCompatibility = compile_source_compatibility
1909 targetCompatibility = compile_target_compatibility
1910 jvmArgs += additional_compiler_args
1913 // this is not perfect yet -- we should only add the commandLineIncludePatterns to the
1914 // testTasks that include the tests, and exclude all from the others.
1915 // get --test argument
1916 filter.commandLineIncludePatterns = test.filter.commandLineIncludePatterns
1917 // do something with testTask.getCandidateClassFiles() to see if the test should silently finish because of the
1918 // commandLineIncludePatterns not matching anything. Instead we are doing setFailOnNoMatchingTests(false) below
1922 println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
1927 /* don't fail on no matching tests (so --tests will run across all testTasks) */
1928 testTask.filter.setFailOnNoMatchingTests(false)
1930 /* ensure the "test" task dependsOn all the testTasks */
1931 test.dependsOn testTask
1934 gradle.buildFinished {
1935 def allResults = rootProject.ext.testsResults
1937 if (!allResults.isEmpty()) {
1938 printResults allResults
1939 allResults.each {r ->
1940 if (r[2].resultType == TestResult.ResultType.FAILURE)
1941 throw new GradleException("Failed tests!")
1946 private static String colString(styler, col, colour, text) {
1947 return col?"${styler[colour](text)}":text
1950 private static String getSummaryLine(s, pn, tn, rt, rc, rs, rf, rsk, t, col) {
1951 def colour = 'black'
1959 case TestResult.ResultType.SUCCESS:
1962 case TestResult.ResultType.FAILURE:
1970 StringBuilder sb = new StringBuilder()
1974 sb.append(" results: ")
1975 sb.append(colString(s, col && !nocol, colour, text))
1977 sb.append("${rc} tests, ")
1978 sb.append(colString(s, col && rs > 0, 'green', rs))
1979 sb.append(" successes, ")
1980 sb.append(colString(s, col && rf > 0, 'red', rf))
1981 sb.append(" failures, ")
1982 sb.append("${rsk} skipped) in ${t}")
1983 return sb.toString()
1986 private static void printResults(allResults) {
1988 // styler from https://stackoverflow.com/a/56139852
1989 def styler = 'black red green yellow blue magenta cyan white'.split().toList().withIndex(30).collectEntries { key, val -> [(key) : { "\033[${val}m${it}\033[0m" }] }
1992 def failedTests = false
1993 def summaryLines = []
1995 def totalsuccess = 0
1998 def totaltime = TimeCategory.getSeconds(0)
1999 // sort on project name then task name
2000 allResults.sort {a, b -> a[0] == b[0]? a[1]<=>b[1]:a[0] <=> b[0]}.each {
2001 def projectName = it[0]
2002 def taskName = it[1]
2006 def summaryCol = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, true)
2007 def summaryPlain = getSummaryLine(styler, projectName, taskName, result.resultType, result.testCount, result.successfulTestCount, result.failedTestCount, result.skippedTestCount, time, false)
2008 def reportLine = "Report file: ${report}"
2009 def ls = summaryPlain.length()
2010 def lr = reportLine.length()
2011 def m = [ls, lr].max()
2014 def info = [ls, summaryCol, reportLine]
2015 summaryLines.add(info)
2016 failedTests |= result.resultType == TestResult.ResultType.FAILURE
2017 totalcount += result.testCount
2018 totalsuccess += result.successfulTestCount
2019 totalfail += result.failedTestCount
2020 totalskip += result.skippedTestCount
2023 def totalSummaryCol = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, true)
2024 def totalSummaryPlain = getSummaryLine(styler, "OVERALL", "", failedTests?TestResult.ResultType.FAILURE:TestResult.ResultType.SUCCESS, totalcount, totalsuccess, totalfail, totalskip, totaltime, false)
2025 def tls = totalSummaryPlain.length()
2026 if (tls > maxLength)
2028 def info = [tls, totalSummaryCol, null]
2029 summaryLines.add(info)
2031 def allSummaries = []
2032 for(sInfo : summaryLines) {
2034 def summary = sInfo[1]
2035 def report = sInfo[2]
2037 StringBuilder sb = new StringBuilder()
2038 sb.append("│" + summary + " " * (maxLength - ls) + "│")
2039 if (report != null) {
2040 sb.append("\n│" + report + " " * (maxLength - report.length()) + "│")
2042 allSummaries += sb.toString()
2045 println "┌${"${"─" * maxLength}"}┐"
2046 println allSummaries.join("\n├${"${"─" * maxLength}"}┤\n")
2047 println "└${"${"─" * maxLength}"}┘"
2049 /* END of test tasks results summary */
2052 task compileLinkCheck(type: JavaCompile) {
2054 classpath = files("${jalviewDir}/${utils_dir}")
2055 destinationDir = file("${jalviewDir}/${utils_dir}")
2056 source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
2058 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2059 inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
2060 outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
2061 outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
2065 task linkCheck(type: JavaExec) {
2067 dependsOn compileLinkCheck
2069 def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
2070 classpath = files("${jalviewDir}/${utils_dir}")
2071 main = "HelpLinksChecker"
2072 workingDir = "${helpBuildDir}"
2073 args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
2075 def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
2076 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
2079 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
2083 inputs.dir(helpBuildDir)
2084 outputs.file(helpLinksCheckerOutFile)
2088 // import the pubhtmlhelp target
2089 ant.properties.basedir = "${jalviewDir}"
2090 ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
2091 ant.importBuild "${utils_dir}/publishHelp.xml"
2094 task cleanPackageDir(type: Delete) {
2096 delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
2106 attributes "Main-Class": main_class,
2107 "Permissions": "all-permissions",
2108 "Application-Name": applicationName,
2109 "Codebase": application_codebase,
2110 "Implementation-Version": JALVIEW_VERSION
2113 def outputDir = "${jalviewDir}/${package_dir}"
2114 destinationDirectory = file(outputDir)
2115 archiveFileName = rootProject.name+".jar"
2116 duplicatesStrategy "EXCLUDE"
2123 exclude "**/*.jar.*"
2125 inputs.dir(sourceSets.main.java.outputDir)
2126 sourceSets.main.resources.srcDirs.each{ dir ->
2129 outputs.file("${outputDir}/${archiveFileName}")
2133 task copyJars(type: Copy) {
2134 from fileTree(dir: classesDir, include: "**/*.jar").files
2135 into "${jalviewDir}/${package_dir}"
2139 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
2140 task syncJars(type: Sync) {
2142 from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
2143 into "${jalviewDir}/${package_dir}"
2145 include jar.archiveFileName.getOrNull()
2152 description = "Put all required libraries in dist"
2153 // order of "cleanPackageDir", "copyJars", "jar" important!
2154 jar.mustRunAfter cleanPackageDir
2155 syncJars.mustRunAfter cleanPackageDir
2156 dependsOn cleanPackageDir
2159 outputs.dir("${jalviewDir}/${package_dir}")
2164 dependsOn cleanPackageDir
2170 task launcherJar(type: Jar) {
2173 "Main-Class": shadow_jar_main_class,
2174 "Implementation-Version": JALVIEW_VERSION,
2175 "Application-Name": applicationName
2181 group = "distribution"
2182 description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
2187 def jarFiles = fileTree(dir: "${jalviewDir}/${libDistDir}", include: "*.jar", exclude: "regex.jar").getFiles()
2188 def groovyJars = jarFiles.findAll {it1 -> file(it1).getName().startsWith("groovy-swing")}
2189 def otherJars = jarFiles.findAll {it2 -> !file(it2).getName().startsWith("groovy-swing")}
2194 // shadowJar manifest must inheritFrom another Jar task. Can't set attributes here.
2195 inheritFrom(project.tasks.launcherJar.manifest)
2197 // we need to include the groovy-swing Include-Package for it to run in the shadowJar
2199 def jarFileManifests = []
2200 groovyJars.each { jarFile ->
2201 def mf = zipTree(jarFile).getFiles().find { it.getName().equals("MANIFEST.MF") }
2203 jarFileManifests += mf
2207 from (jarFileManifests) {
2208 eachEntry { details ->
2209 if (!details.key.equals("Import-Package")) {
2217 duplicatesStrategy "INCLUDE"
2219 // this mainClassName is mandatory but gets ignored due to manifest created in doFirst{}. Set the Main-Class as an attribute in launcherJar instead
2220 mainClassName = shadow_jar_main_class
2222 classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
2226 task getdownImagesCopy() {
2227 inputs.dir getdownImagesDir
2228 outputs.dir getdownImagesBuildDir
2232 from(getdownImagesDir) {
2233 include("*getdown*.png")
2235 into getdownImagesBuildDir
2240 task getdownImagesProcess() {
2241 dependsOn getdownImagesCopy
2244 if (backgroundImageText) {
2245 if (convertBinary == null) {
2246 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2248 if (!project.hasProperty("getdown_background_image_text_suffix_cmd")) {
2249 throw new StopExecutionException("No property 'getdown_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2251 fileTree(dir: getdownImagesBuildDir, include: "*background*.png").getFiles().each { file ->
2253 executable convertBinary
2256 '-font', getdown_background_image_text_font,
2257 '-fill', getdown_background_image_text_colour,
2258 '-draw', sprintf(getdown_background_image_text_suffix_cmd, channelSuffix),
2259 '-draw', sprintf(getdown_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2260 '-draw', sprintf(getdown_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2269 task getdownImages() {
2270 dependsOn getdownImagesProcess
2273 task getdownWebsiteBuild() {
2274 group = "distribution"
2275 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."
2277 dependsOn getdownImages
2282 def getdownWebsiteResourceFilenames = []
2283 def getdownResourceDir = getdownResourceDir
2284 def getdownResourceFilenames = []
2287 // clean the getdown website and files dir before creating getdown folders
2288 delete getdownAppBaseDir
2289 delete getdownFilesDir
2292 from buildProperties
2293 rename(file(buildProperties).getName(), getdown_build_properties)
2296 getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
2299 from channelPropsFile
2300 filter(ReplaceTokens,
2304 'SUFFIX': channelSuffix
2307 into getdownAppBaseDir
2309 getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
2311 // set some getdownTxt_ properties then go through all properties looking for getdownTxt_...
2312 def props = project.properties.sort { it.key }
2313 if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
2314 props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
2316 if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
2317 props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
2319 if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
2320 props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
2322 if (getdownImagesBuildDir != null && file(getdownImagesBuildDir).exists()) {
2323 props.put("getdown_txt_ui.background_image", "${getdownImagesBuildDir}/${getdown_background_image}")
2324 props.put("getdown_txt_ui.instant_background_image", "${getdownImagesBuildDir}/${getdown_instant_background_image}")
2325 props.put("getdown_txt_ui.error_background", "${getdownImagesBuildDir}/${getdown_error_background}")
2326 props.put("getdown_txt_ui.progress_image", "${getdownImagesBuildDir}/${getdown_progress_image}")
2327 props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
2328 props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
2331 props.put("getdown_txt_title", jalview_name)
2332 props.put("getdown_txt_ui.name", applicationName)
2334 // start with appbase
2335 getdownTextLines += "appbase = ${getdownAppBase}"
2336 props.each{ prop, val ->
2337 if (prop.startsWith("getdown_txt_") && val != null) {
2338 if (prop.startsWith("getdown_txt_multi_")) {
2339 def key = prop.substring(18)
2340 val.split(",").each{ v ->
2341 def line = "${key} = ${v}"
2342 getdownTextLines += line
2345 // file values rationalised
2346 if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
2348 if (val.indexOf('/') == 0) {
2351 } else if (val.indexOf('/') > 0) {
2352 // relative path (relative to jalviewDir)
2353 r = file( "${jalviewDir}/${val}" )
2356 val = "${getdown_resource_dir}/" + r.getName()
2357 getdownWebsiteResourceFilenames += val
2358 getdownResourceFilenames += r.getPath()
2361 if (! prop.startsWith("getdown_txt_resource")) {
2362 def line = prop.substring(12) + " = ${val}"
2363 getdownTextLines += line
2369 getdownWebsiteResourceFilenames.each{ filename ->
2370 getdownTextLines += "resource = ${filename}"
2372 getdownResourceFilenames.each{ filename ->
2375 into getdownResourceDir
2379 def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
2380 getdownWrapperScripts.each{ script ->
2381 def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
2385 into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2387 getdownTextLines += "xresource = ${getdown_wrapper_script_dir}/${script}"
2392 fileTree(file(package_dir)).each{ f ->
2393 if (f.isDirectory()) {
2394 def files = fileTree(dir: f, include: ["*"]).getFiles()
2396 } else if (f.exists()) {
2400 def jalviewJar = jar.archiveFileName.getOrNull()
2401 // put jalview.jar first for CLASSPATH and .properties files reasons
2402 codeFiles.sort{a, b -> ( a.getName() == jalviewJar ? -1 : ( b.getName() == jalviewJar ? 1 : a <=> b ) ) }.each{f ->
2403 def name = f.getName()
2404 def line = "code = ${getdownAppDistDir}/${name}"
2405 getdownTextLines += line
2412 // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
2414 if (JAVA_VERSION.equals("11")) {
2415 def j11libFiles = fileTree(dir: "${jalviewDir}/${j11libDir}", include: ["*.jar"]).getFiles()
2416 j11libFiles.sort().each{f ->
2417 def name = f.getName()
2418 def line = "code = ${getdown_j11lib_dir}/${name}"
2419 getdownTextLines += line
2422 into getdownJ11libDir
2428 // 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.
2429 //getdownTextLines += "class = " + file(getdownLauncher).getName()
2430 getdownTextLines += "resource = ${getdown_launcher_new}"
2431 getdownTextLines += "class = ${main_class}"
2432 // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
2433 if (getdownSetAppBaseProperty) {
2434 getdownTextLines += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}"
2435 getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
2438 def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
2439 getdownTxt.write(getdownTextLines.join("\n"))
2441 getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
2442 def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
2443 launchJvl.write("appbase=${getdownAppBase}")
2445 // files going into the getdown website dir: getdown-launcher.jar
2447 from getdownLauncher
2448 rename(file(getdownLauncher).getName(), getdown_launcher_new)
2449 into getdownAppBaseDir
2452 // files going into the getdown website dir: getdown-launcher(-local).jar
2454 from getdownLauncher
2455 if (file(getdownLauncher).getName() != getdown_launcher) {
2456 rename(file(getdownLauncher).getName(), getdown_launcher)
2458 into getdownAppBaseDir
2461 // files going into the getdown website dir: ./install dir and files
2462 if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
2465 from getdownLauncher
2466 from "${getdownAppDir}/${getdown_build_properties}"
2467 if (file(getdownLauncher).getName() != getdown_launcher) {
2468 rename(file(getdownLauncher).getName(), getdown_launcher)
2470 into getdownInstallDir
2473 // and make a copy in the getdown files dir (these are not downloaded by getdown)
2475 from getdownInstallDir
2476 into getdownFilesInstallDir
2480 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2484 from getdownLauncher
2485 from "${getdownAppBaseDir}/${getdown_build_properties}"
2486 from "${getdownAppBaseDir}/${channel_props}"
2487 if (file(getdownLauncher).getName() != getdown_launcher) {
2488 rename(file(getdownLauncher).getName(), getdown_launcher)
2490 into getdownFilesDir
2493 // and ./resource (not all downloaded by getdown)
2495 from getdownResourceDir
2496 into "${getdownFilesDir}/${getdown_resource_dir}"
2501 inputs.dir("${jalviewDir}/${package_dir}")
2503 outputs.dir(getdownAppBaseDir)
2504 outputs.dir(getdownFilesDir)
2508 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
2509 task getdownDigestDir(type: JavaExec) {
2511 description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
2513 def digestDirPropertyName = "DIGESTDIR"
2515 classpath = files(getdownLauncher)
2516 def digestDir = findProperty(digestDirPropertyName)
2517 if (digestDir == null) {
2518 throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
2522 main = "com.threerings.getdown.tools.Digester"
2526 task getdownDigest(type: JavaExec) {
2527 group = "distribution"
2528 description = "Digest the getdown website folder"
2530 dependsOn getdownWebsiteBuild
2533 classpath = files(getdownLauncher)
2535 main = "com.threerings.getdown.tools.Digester"
2536 args getdownAppBaseDir
2537 inputs.dir(getdownAppBaseDir)
2538 outputs.file("${getdownAppBaseDir}/digest2.txt")
2543 group = "distribution"
2544 description = "Create the minimal and full getdown app folder for installers and website and create digest file"
2545 dependsOn getdownDigest
2547 if (reportRsyncCommand) {
2548 def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
2549 def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
2550 println "LIKELY RSYNC COMMAND:"
2551 println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
2552 if (RUNRSYNC == "true") {
2554 commandLine "mkdir", "-p", toDir
2557 commandLine "rsync", "-avh", "--delete", fromDir, toDir
2564 task getdownWebsite {
2565 group = "distribution"
2566 description = "A task to create the whole getdown channel website dir including digest file"
2568 dependsOn getdownWebsiteBuild
2569 dependsOn getdownDigest
2572 task getdownArchiveBuild() {
2573 group = "distribution"
2574 description = "Put files in the archive dir to go on the website"
2576 dependsOn getdownWebsiteBuild
2578 def v = "v${JALVIEW_VERSION_UNDERSCORES}"
2579 def vDir = "${getdownArchiveDir}/${v}"
2580 getdownFullArchiveDir = "${vDir}/getdown"
2581 getdownVersionLaunchJvl = "${vDir}/jalview-${v}.jvl"
2583 def vAltDir = "alt_${v}"
2584 def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
2587 // cleanup old "old" dir
2588 delete getdownArchiveDir
2590 def getdownArchiveTxt = file("${getdownFullArchiveDir}/getdown.txt")
2591 getdownArchiveTxt.getParentFile().mkdirs()
2592 def getdownArchiveTextLines = []
2593 def getdownFullArchiveAppBase = "${getdownArchiveAppBase}${getdownArchiveAppBase.endsWith("/")?"":"/"}${v}/getdown/"
2597 from "${getdownAppBaseDir}/${getdownAppDistDir}"
2598 into "${getdownFullArchiveDir}/${vAltDir}"
2601 getdownTextLines.each { line ->
2602 line = line.replaceAll("^(?<s>appbase\\s*=\\s*).*", '${s}'+getdownFullArchiveAppBase)
2603 line = line.replaceAll("^(?<s>(resource|code)\\s*=\\s*)${getdownAppDistDir}/", '${s}'+vAltDir+"/")
2604 line = line.replaceAll("^(?<s>ui.background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background.png")
2605 line = line.replaceAll("^(?<s>ui.instant_background_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_initialising.png")
2606 line = line.replaceAll("^(?<s>ui.error_background\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_background_error.png")
2607 line = line.replaceAll("^(?<s>ui.progress_image\\s*=\\s*).*\\.png", '${s}'+"${getdown_resource_dir}/jalview_archive_getdown_progress_bar.png")
2608 // remove the existing resource = resource/ or bin/ lines
2609 if (! line.matches("resource\\s*=\\s*(resource|bin)/.*")) {
2610 getdownArchiveTextLines += line
2614 // the resource dir -- add these files as resource lines in getdown.txt
2616 from "${archiveImagesDir}"
2617 into "${getdownFullArchiveDir}/${getdown_resource_dir}"
2619 getdownArchiveTextLines += "resource = ${getdown_resource_dir}/${file.getName()}"
2623 // the wrapper scripts dir
2624 if ( file("${getdownAppBaseDir}/${getdown_wrapper_script_dir}").exists() ) {
2626 from "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
2627 into "${getdownFullArchiveDir}/${getdown_wrapper_script_dir}"
2631 getdownArchiveTxt.write(getdownArchiveTextLines.join("\n"))
2633 def vLaunchJvl = file(getdownVersionLaunchJvl)
2634 vLaunchJvl.getParentFile().mkdirs()
2635 vLaunchJvl.write("appbase=${getdownFullArchiveAppBase}\n")
2636 def vLaunchJvlPath = vLaunchJvl.toPath().toAbsolutePath()
2637 def jvlLinkPath = file("${vDir}/jalview.jvl").toPath().toAbsolutePath()
2638 // for some reason filepath.relativize(fileInSameDirPath) gives a path to "../" which is wrong
2639 //java.nio.file.Files.createSymbolicLink(jvlLinkPath, jvlLinkPath.relativize(vLaunchJvlPath));
2640 java.nio.file.Files.createSymbolicLink(jvlLinkPath, java.nio.file.Paths.get(".",vLaunchJvl.getName()));
2642 // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
2644 from getdownLauncher
2645 from "${getdownAppBaseDir}/${getdownLaunchJvl}"
2646 from "${getdownAppBaseDir}/${getdown_launcher_new}"
2647 from "${getdownAppBaseDir}/${channel_props}"
2648 if (file(getdownLauncher).getName() != getdown_launcher) {
2649 rename(file(getdownLauncher).getName(), getdown_launcher)
2651 into getdownFullArchiveDir
2657 task getdownArchiveDigest(type: JavaExec) {
2658 group = "distribution"
2659 description = "Digest the getdown archive folder"
2661 dependsOn getdownArchiveBuild
2664 classpath = files(getdownLauncher)
2665 args getdownFullArchiveDir
2667 main = "com.threerings.getdown.tools.Digester"
2668 inputs.dir(getdownFullArchiveDir)
2669 outputs.file("${getdownFullArchiveDir}/digest2.txt")
2672 task getdownArchive() {
2673 group = "distribution"
2674 description = "Build the website archive dir with getdown digest"
2676 dependsOn getdownArchiveBuild
2677 dependsOn getdownArchiveDigest
2680 tasks.withType(JavaCompile) {
2681 options.encoding = 'UTF-8'
2687 delete getdownAppBaseDir
2688 delete getdownFilesDir
2689 delete getdownArchiveDir
2695 if (file(install4jHomeDir).exists()) {
2697 } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
2698 install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
2699 } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
2700 install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
2702 installDir(file(install4jHomeDir))
2704 mediaTypes = Arrays.asList(install4j_media_types.split(","))
2708 task copyInstall4jTemplate {
2709 def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
2710 def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
2711 inputs.file(install4jTemplateFile)
2712 inputs.file(install4jFileAssociationsFile)
2713 inputs.property("CHANNEL", { CHANNEL })
2714 outputs.file(install4jConfFile)
2717 def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
2719 // turn off code signing if no OSX_KEYPASS
2720 if (OSX_KEYPASS == "") {
2721 install4jConfigXml.'**'.codeSigning.each { codeSigning ->
2722 codeSigning.'@macEnabled' = "false"
2724 install4jConfigXml.'**'.windows.each { windows ->
2725 windows.'@runPostProcessor' = "false"
2729 // disable install screen for OSX dmg (for 2.11.2.0)
2730 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2731 macosArchive.attributes().remove('executeSetupApp')
2732 macosArchive.attributes().remove('setupAppId')
2735 // turn off checksum creation for LOCAL channel
2736 def e = install4jConfigXml.application[0]
2737 e.'@createChecksums' = string(install4jCheckSums)
2739 // put file association actions where placeholder action is
2740 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2741 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2742 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2743 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2744 def parent = a.parent()
2746 fileAssociationActions.each { faa ->
2749 // don't need to continue in .any loop once replacements have been made
2754 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2755 // NB we're deleting the /other/ one!
2756 // Also remove the examples subdir from non-release versions
2757 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2758 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2759 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2760 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2762 // remove the examples subdir from Full File Set
2763 def files = install4jConfigXml.files[0]
2764 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2765 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2766 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2767 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2768 dirEntry.parent().remove(dirEntry)
2770 install4jConfigXml.'**'.action.any { a ->
2771 if (a.'@customizedId' == customizedIdToDelete) {
2772 def parent = a.parent()
2778 // write install4j file
2779 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2786 delete install4jConfFile
2790 task cleanInstallersDataFiles {
2791 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2792 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2793 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2795 delete installersOutputTxt
2796 delete installersSha256
2797 delete hugoDataJsonFile
2801 task install4jDMGBackgroundImageCopy {
2802 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2803 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2806 from(install4jDMGBackgroundImageDir) {
2807 include(install4jDMGBackgroundImageFile)
2809 into install4jDMGBackgroundImageBuildDir
2814 task install4jDMGBackgroundImageProcess {
2815 dependsOn install4jDMGBackgroundImageCopy
2818 if (backgroundImageText) {
2819 if (convertBinary == null) {
2820 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2822 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2823 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2825 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2827 executable convertBinary
2830 '-font', install4j_background_image_text_font,
2831 '-fill', install4j_background_image_text_colour,
2832 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2833 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2834 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2845 pip 'ds_store:1.3.1'
2848 task install4jCustomiseDS_StoreX64(type: PythonTask) {
2849 inputs.file(install4jDMGDSStore)
2850 inputs.file(install4jDMGDSStoreJSON)
2851 outputs.file(install4jDMGFixedDSStoreX64)
2852 def command_args = [ jalview_customise_ds_store, '--input', install4jDMGDSStore, '--output', install4jDMGFixedDSStoreX64, '--volumename', install4jmacOSArchiveX64Name, '--backgroundfile', install4j_dmg_background_filename, '--dmg', install4jmacOSArchiveX64DMGFilename + ".dmg" ]
2853 if (file(install4jDMGDSStoreJSON).exists()) {
2854 command_args += [ '--config', install4jDMGDSStoreJSON ]
2856 command = command_args
2858 println("Running command '${command_args.join(' ')}'")
2862 task install4jCustomiseDS_StoreAarch64(type: PythonTask) {
2863 inputs.file(install4jDMGDSStore)
2864 inputs.file(install4jDMGDSStoreJSON)
2865 outputs.file(install4jDMGFixedDSStoreAarch64)
2866 def command_args = [ jalview_customise_ds_store, '--input', install4jDMGDSStore, '--output', install4jDMGFixedDSStoreAarch64, '--volumename', install4jmacOSArchiveAarch64Name, '--backgroundfile', install4j_dmg_background_filename, '--dmg', install4jmacOSArchiveAarch64DMGFilename + ".dmg" ]
2867 if (file(install4jDMGDSStoreJSON).exists()) {
2868 command_args += [ '--config', install4jDMGDSStoreJSON ]
2870 command = command_args
2873 for (int i = 0; i < command_args.size(); i++) {
2874 def arg = command_args[i]
2875 print_args += (i > 0 && !arg.startsWith("-")) ? "\"${arg}\"" : arg
2877 println("Running command '${print_args.join(' ')}'")
2881 task install4jCustomiseDS_Store {
2882 dependsOn install4jCustomiseDS_StoreX64
2883 dependsOn install4jCustomiseDS_StoreAarch64
2886 task install4jDMGProcesses {
2887 dependsOn install4jDMGBackgroundImageProcess
2888 dependsOn install4jCustomiseDS_Store
2891 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2892 group = "distribution"
2893 description = "Create the install4j installers"
2895 dependsOn copyInstall4jTemplate
2896 dependsOn cleanInstallersDataFiles
2897 dependsOn install4jDMGProcesses
2899 projectFile = install4jConfFile
2901 // run install4j with 4g
2902 vmParameters = ["-Xmx4294967296"]
2904 // create an md5 for the input files to use as version for install4j conf file
2905 def digest = MessageDigest.getInstance("MD5")
2907 (file("${install4jDir}/${install4j_template}").text +
2908 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2909 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2910 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2911 if (filesMd5.length() >= 8) {
2912 filesMd5 = filesMd5.substring(0,8)
2914 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2917 'JALVIEW_NAME': jalview_name,
2918 'JALVIEW_APPLICATION_NAME': applicationName,
2919 'JALVIEW_DIR': "../..",
2920 'OSX_KEYSTORE': OSX_KEYSTORE,
2921 'OSX_APPLEID': OSX_APPLEID,
2922 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2923 'JSIGN_SH': JSIGN_SH,
2924 'JRE_DIR': getdown_app_dir_java,
2925 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2926 'JALVIEW_VERSION': JALVIEW_VERSION,
2927 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2928 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2929 'JAVA_VERSION': JAVA_VERSION,
2930 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2931 'VERSION': JALVIEW_VERSION,
2932 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2933 'BUNDLE_ID': install4jBundleId,
2934 'INTERNAL_ID': install4jInternalId,
2935 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2936 'MACOS_X64_DMG_DS_STORE': install4jDMGFixedDSStoreX64,
2937 'MACOS_AARCH64_DMG_DS_STORE': install4jDMGFixedDSStoreAarch64,
2938 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2939 'MACOS_DMG_BG_FILENAME': install4j_dmg_background_filename,
2940 'WRAPPER_LINK': getdownWrapperLink,
2941 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2942 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2943 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2944 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2945 'MACOSARCHIVE_X64_NAME': install4jmacOSArchiveX64Name,
2946 'MACOSARCHIVE_AARCH64_NAME': install4jmacOSArchiveAarch64Name,
2947 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2948 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2949 'GETDOWN_FILES_DIR': getdown_files_dir,
2950 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2951 'GETDOWN_DIST_DIR': getdownAppDistDir,
2952 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2953 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2954 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2955 'BUILD_DIR': install4jBuildDir,
2956 'APPLICATION_CATEGORIES': install4j_application_categories,
2957 'APPLICATION_FOLDER': install4jApplicationFolder,
2958 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2959 'EXECUTABLE_NAME': install4jExecutableName,
2960 'EXTRA_SCHEME': install4jExtraScheme,
2961 'MAC_ICONS_FILE': install4jMacIconsFile,
2962 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2963 'PNG_ICON_FILE': install4jPngIconFile,
2964 'BACKGROUND': install4jBackground,
2965 'MACOSARCHIVE_X64_DMG_FILENAME': install4jmacOSArchiveX64DMGFilename,
2966 'MACOSARCHIVE_AARCH64_DMG_FILENAME': install4jmacOSArchiveAarch64DMGFilename,
2967 'MACOSARCHIVE_VOLUMEICON': install4jDMGVolumeIcon,
2972 'windows': 'WINDOWS',
2976 // these are the bundled OS/architecture VMs needed by install4j
2979 [ "mac", "aarch64" ],
2980 [ "windows", "x64" ],
2982 [ "linux", "aarch64" ]
2984 osArch.forEach { os, arch ->
2985 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)
2986 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
2987 // otherwise running `gradle installers` generates a non-useful error:
2988 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
2989 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)
2992 //println("INSTALL4J VARIABLES:")
2993 //variables.each{k,v->println("${k}=${v}")}
2995 destination = "${jalviewDir}/${install4jBuildDir}"
2996 buildSelected = true
2998 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
3000 disableSigning = true
3001 disableNotarization = true
3005 macKeystorePassword = OSX_KEYPASS
3008 if (OSX_ALTOOLPASS) {
3009 appleIdPassword = OSX_ALTOOLPASS
3010 disableNotarization = false
3012 disableNotarization = true
3016 println("Using projectFile "+projectFile)
3017 if (!disableNotarization) { println("Will notarize OSX App DMG") }
3021 inputs.dir(getdownAppBaseDir)
3022 inputs.file(install4jConfFile)
3023 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
3024 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
3027 def getDataHash(File myFile) {
3028 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
3029 return myFile.exists()
3031 "file" : myFile.getName(),
3032 "filesize" : myFile.length(),
3033 "sha256" : hash.toString()
3038 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
3040 "channel" : getdownChannelName,
3041 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
3042 "git-commit" : "${gitHash} [${gitBranch}]",
3043 "version" : JALVIEW_VERSION
3045 // install4j installer files
3046 if (installersOutputTxt.exists()) {
3048 installersOutputTxt.readLines().each { def line ->
3049 if (line.startsWith("#")) {
3052 line.replaceAll("\n","")
3053 def vals = line.split("\t")
3054 def filename = vals[3]
3055 def filesize = file(filename).length()
3056 filename = filename.replaceAll(/^.*\//, "")
3057 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
3058 idHash."${filename}" = vals[0]
3060 if (install4jCheckSums && installersSha256.exists()) {
3061 installersSha256.readLines().each { def line ->
3062 if (line.startsWith("#")) {
3065 line.replaceAll("\n","")
3066 def vals = line.split(/\s+\*?/)
3067 def filename = vals[1]
3068 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
3074 "JAR": shadowJar.archiveFile, // executable JAR
3075 "JVL": getdownVersionLaunchJvl, // version JVL
3076 "SOURCE": sourceDist.archiveFile // source TGZ
3077 ].each { key, value ->
3078 def file = file(value)
3079 if (file.exists()) {
3080 def fileHash = getDataHash(file)
3081 if (fileHash != null) {
3082 hash."${key}" = fileHash;
3086 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
3089 task staticMakeInstallersJsonFile {
3091 def output = findProperty("i4j_output")
3092 def sha256 = findProperty("i4j_sha256")
3093 def json = findProperty("i4j_json")
3094 if (output == null || sha256 == null || json == null) {
3095 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3097 writeDataJsonFile(file(output), file(sha256), file(json))
3102 dependsOn installerFiles
3108 eclipse().configFile(eclipse_codestyle_file)
3112 task createSourceReleaseProperties(type: WriteProperties) {
3113 group = "distribution"
3114 description = "Create the source RELEASE properties file"
3116 def sourceTarBuildDir = "${buildDir}/sourceTar"
3117 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3118 outputFile (sourceReleasePropertiesFile)
3121 releaseProps.each{ key, val -> property key, val }
3122 property "git.branch", gitBranch
3123 property "git.hash", gitHash
3126 outputs.file(outputFile)
3129 task sourceDist(type: Tar) {
3130 group "distribution"
3131 description "Create a source .tar.gz file for distribution"
3133 dependsOn createBuildProperties
3134 dependsOn convertMdFiles
3135 dependsOn eclipseAllPreferences
3136 dependsOn createSourceReleaseProperties
3139 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3140 archiveFileName = outputFileName
3142 compression Compression.GZIP
3158 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3160 "utils/InstallAnywhere",
3175 "gradle.properties",
3187 ".settings/org.eclipse.buildship.core.prefs",
3188 ".settings/org.eclipse.jdt.core.prefs"
3192 exclude (EXCLUDE_FILES)
3193 include (PROCESS_FILES)
3194 filter(ReplaceTokens,
3198 'Version-Rel': JALVIEW_VERSION,
3199 'Year-Rel': getDate("yyyy")
3204 exclude (EXCLUDE_FILES)
3205 exclude (PROCESS_FILES)
3206 exclude ("appletlib")
3207 exclude ("**/*locales")
3208 exclude ("*locales/**")
3209 exclude ("utils/InstallAnywhere")
3211 exclude (getdown_files_dir)
3212 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3213 //exclude (getdown_website_dir)
3214 //exclude (getdown_archive_dir)
3216 // exluding these as not using jars as modules yet
3217 exclude ("${j11modDir}/**/*.jar")
3220 include(INCLUDE_FILES)
3222 // from (jalviewDir) {
3223 // // explicit includes for stuff that seemed to not get included
3224 // include(fileTree("test/**/*."))
3225 // exclude(EXCLUDE_FILES)
3226 // exclude(PROCESS_FILES)
3229 from(file(buildProperties).getParent()) {
3230 include(file(buildProperties).getName())
3231 rename(file(buildProperties).getName(), "build_properties")
3233 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3237 def sourceTarBuildDir = "${buildDir}/sourceTar"
3238 from(sourceTarBuildDir) {
3239 // this includes the appended RELEASE properties file
3243 task dataInstallersJson {
3245 description "Create the installers-VERSION.json data file for installer files created"
3247 mustRunAfter installers
3248 mustRunAfter shadowJar
3249 mustRunAfter sourceDist
3250 mustRunAfter getdownArchive
3252 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3253 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3255 if (installersOutputTxt.exists()) {
3256 inputs.file(installersOutputTxt)
3258 if (install4jCheckSums && installersSha256.exists()) {
3259 inputs.file(installersSha256)
3262 shadowJar.archiveFile, // executable JAR
3263 getdownVersionLaunchJvl, // version JVL
3264 sourceDist.archiveFile // source TGZ
3265 ].each { fileName ->
3266 if (file(fileName).exists()) {
3267 inputs.file(fileName)
3271 outputs.file(hugoDataJsonFile)
3274 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3280 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3283 dependsOn pubhtmlhelp
3285 inputs.dir("${helpBuildDir}/${help_dir}")
3286 outputs.dir("${buildDir}/distributions/${help_dir}")
3290 task j2sSetHeadlessBuild {
3297 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3299 description "Enable the alternative J2S Config file for headless build"
3301 outputFile = jalviewjsJ2sSettingsFileName
3302 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3303 def j2sProps = new Properties()
3304 if (j2sPropsFile.exists()) {
3306 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3307 j2sProps.load(j2sPropsFileFIS)
3308 j2sPropsFileFIS.close()
3310 j2sProps.each { prop, val ->
3313 } catch (Exception e) {
3314 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3318 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3319 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3324 task jalviewjsSetEclipseWorkspace {
3325 def propKey = "jalviewjs_eclipse_workspace"
3327 if (project.hasProperty(propKey)) {
3328 propVal = project.getProperty(propKey)
3329 if (propVal.startsWith("~/")) {
3330 propVal = System.getProperty("user.home") + propVal.substring(1)
3333 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3334 def propsFile = file(propsFileName)
3335 def eclipseWsDir = propVal
3336 def props = new Properties()
3338 def writeProps = true
3339 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3340 def ins = new FileInputStream(propsFileName)
3343 if (props.getProperty(propKey, null) != null) {
3344 eclipseWsDir = props.getProperty(propKey)
3349 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3350 def tempDir = File.createTempDir()
3351 eclipseWsDir = tempDir.getAbsolutePath()
3354 eclipseWorkspace = file(eclipseWsDir)
3357 // do not run a headless transpile when we claim to be in Eclipse
3359 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3360 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3362 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3366 props.setProperty(propKey, eclipseWsDir)
3367 propsFile.parentFile.mkdirs()
3368 def bytes = new ByteArrayOutputStream()
3369 props.store(bytes, null)
3370 def propertiesString = bytes.toString()
3371 propsFile.text = propertiesString
3377 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3380 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3381 outputs.file(propsFileName)
3382 outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
3386 task jalviewjsEclipsePaths {
3389 def eclipseRoot = jalviewjs_eclipse_root
3390 if (eclipseRoot.startsWith("~/")) {
3391 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3393 if (OperatingSystem.current().isMacOsX()) {
3394 eclipseRoot += "/Eclipse.app"
3395 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3396 eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3397 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3398 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3399 eclipseRoot += "/eclipse"
3401 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3402 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3403 } else { // linux or unix
3404 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3405 eclipseRoot += "/eclipse"
3406 println("eclipseDir exists")
3408 eclipseBinary = "${eclipseRoot}/eclipse"
3409 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3412 eclipseVersion = "4.13" // default
3413 def assumedVersion = true
3414 if (file(eclipseProduct).exists()) {
3415 def fis = new FileInputStream(eclipseProduct)
3416 def props = new Properties()
3418 eclipseVersion = props.getProperty("version")
3420 assumedVersion = false
3423 def propKey = "eclipse_debug"
3424 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3427 // do not run a headless transpile when we claim to be in Eclipse
3429 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3430 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3432 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3435 if (!assumedVersion) {
3436 println("ECLIPSE VERSION=${eclipseVersion}")
3442 task printProperties {
3444 description "Output to console all System.properties"
3446 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3452 dependsOn eclipseProject
3453 dependsOn eclipseClasspath
3454 dependsOn eclipseJdt
3458 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3459 task jalviewjsEclipseCopyDropins(type: Copy) {
3460 dependsOn jalviewjsEclipsePaths
3462 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3463 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3464 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3471 // this eclipse -clean doesn't actually work
3472 task jalviewjsCleanEclipse(type: Exec) {
3473 dependsOn eclipseSetup
3474 dependsOn jalviewjsEclipsePaths
3475 dependsOn jalviewjsEclipseCopyDropins
3477 executable(eclipseBinary)
3478 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3484 def inputString = """exit
3487 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3488 standardInput = inputByteStream
3491 /* not really working yet
3492 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3496 task jalviewjsTransferUnzipSwingJs {
3497 def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3501 from zipTree(file_zip)
3502 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3506 inputs.file file_zip
3507 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3511 task jalviewjsTransferUnzipLib {
3512 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
3515 zipFiles.each { file_zip ->
3517 from zipTree(file_zip)
3518 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3523 inputs.files zipFiles
3524 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3528 task jalviewjsTransferUnzipAllLibs {
3529 dependsOn jalviewjsTransferUnzipSwingJs
3530 dependsOn jalviewjsTransferUnzipLib
3534 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3536 description "Create the alternative j2s file from the j2s.* properties"
3538 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3539 def siteDirProperty = "j2s.site.directory"
3540 def setSiteDir = false
3541 jalviewjsJ2sProps.each { prop, val ->
3543 if (prop == siteDirProperty) {
3544 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3545 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3551 if (!setSiteDir) { // default site location, don't override specifically set property
3552 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3555 outputFile = jalviewjsJ2sAltSettingsFileName
3558 inputs.properties(jalviewjsJ2sProps)
3559 outputs.file(jalviewjsJ2sAltSettingsFileName)
3564 task jalviewjsEclipseSetup {
3565 dependsOn jalviewjsEclipseCopyDropins
3566 dependsOn jalviewjsSetEclipseWorkspace
3567 dependsOn jalviewjsCreateJ2sSettings
3571 task jalviewjsSyncAllLibs (type: Sync) {
3572 dependsOn jalviewjsTransferUnzipAllLibs
3573 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3574 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3575 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3579 def outputFiles = []
3580 rename { filename ->
3581 outputFiles += "${outputDir}/${filename}"
3588 // should this be exclude really ?
3589 duplicatesStrategy "INCLUDE"
3591 outputs.files outputFiles
3592 inputs.files inputFiles
3596 task jalviewjsSyncResources (type: Sync) {
3597 dependsOn buildResources
3599 def inputFiles = fileTree(dir: resourcesBuildDir)
3600 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3604 def outputFiles = []
3605 rename { filename ->
3606 outputFiles += "${outputDir}/${filename}"
3612 outputs.files outputFiles
3613 inputs.files inputFiles
3617 task jalviewjsSyncSiteResources (type: Sync) {
3618 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3619 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3623 def outputFiles = []
3624 rename { filename ->
3625 outputFiles += "${outputDir}/${filename}"
3631 outputs.files outputFiles
3632 inputs.files inputFiles
3636 task jalviewjsSyncBuildProperties (type: Sync) {
3637 dependsOn createBuildProperties
3638 def inputFiles = [file(buildProperties)]
3639 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3643 def outputFiles = []
3644 rename { filename ->
3645 outputFiles += "${outputDir}/${filename}"
3651 outputs.files outputFiles
3652 inputs.files inputFiles
3656 task jalviewjsProjectImport(type: Exec) {
3657 dependsOn eclipseSetup
3658 dependsOn jalviewjsEclipsePaths
3659 dependsOn jalviewjsEclipseSetup
3662 // do not run a headless import when we claim to be in Eclipse
3664 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3665 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3667 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3671 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3672 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3673 executable(eclipseBinary)
3674 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3678 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3680 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3681 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3684 inputs.file("${jalviewDir}/.project")
3685 outputs.upToDateWhen {
3686 file(projdir).exists()
3691 task jalviewjsTranspile(type: Exec) {
3692 dependsOn jalviewjsEclipseSetup
3693 dependsOn jalviewjsProjectImport
3694 dependsOn jalviewjsEclipsePaths
3696 dependsOn jalviewjsEnableAltFileProperty
3700 // do not run a headless transpile when we claim to be in Eclipse
3702 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3703 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3705 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3709 executable(eclipseBinary)
3710 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3714 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3716 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3717 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3723 stdout = new ByteArrayOutputStream()
3724 stderr = new ByteArrayOutputStream()
3726 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3727 def logOutFile = file(logOutFileName)
3728 logOutFile.createNewFile()
3729 logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3730 BINARY: ${eclipseBinary}
3731 VERSION: ${eclipseVersion}
3732 WORKSPACE: ${eclipseWorkspace}
3733 DEBUG: ${eclipseDebug}
3736 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3737 // combine stdout and stderr
3738 def logErrFOS = logOutFOS
3740 if (jalviewjs_j2s_to_console.equals("true")) {
3741 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3742 new org.apache.tools.ant.util.TeeOutputStream(
3746 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3747 new org.apache.tools.ant.util.TeeOutputStream(
3752 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3755 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3762 if (stdout.toString().contains("Error processing ")) {
3763 // j2s did not complete transpile
3764 //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3765 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3766 println("IGNORING TRANSPILE ERRORS")
3767 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3769 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3774 inputs.dir("${jalviewDir}/${sourceDir}")
3775 outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3776 outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3780 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3782 def stdout = new ByteArrayOutputStream()
3783 def stderr = new ByteArrayOutputStream()
3785 def coreFile = file(jsfile)
3787 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3789 logOutFile.createNewFile()
3790 logOutFile.append(msg+"\n")
3792 def coreTop = file(prefixFile)
3793 def coreBottom = file(suffixFile)
3794 coreFile.getParentFile().mkdirs()
3795 coreFile.createNewFile()
3796 coreFile.write( coreTop.getText("UTF-8") )
3800 def t = f.getText("UTF-8")
3801 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3802 coreFile.append( t )
3804 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3806 logOutFile.append(msg+"\n")
3809 coreFile.append( coreBottom.getText("UTF-8") )
3811 msg = "Generating ${zjsfile}"
3813 logOutFile.append(msg+"\n")
3814 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3815 def logErrFOS = logOutFOS
3818 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3819 main = "com.google.javascript.jscomp.CommandLineRunner"
3820 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3821 args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3824 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3826 logOutFile.append(msg+"\n")
3828 if (logOutConsole) {
3829 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3830 new org.apache.tools.ant.util.TeeOutputStream(
3834 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3835 new org.apache.tools.ant.util.TeeOutputStream(
3840 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3843 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3850 logOutFile.append(msg+"\n")
3854 task jalviewjsBuildAllCores {
3856 description "Build the core js lib closures listed in the classlists dir"
3857 dependsOn jalviewjsTranspile
3858 dependsOn jalviewjsTransferUnzipSwingJs
3860 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3861 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3862 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3863 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3864 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3865 def prefixFile = "${jsDir}/core/coretop2.js"
3866 def suffixFile = "${jsDir}/core/corebottom2.js"
3868 inputs.file prefixFile
3869 inputs.file suffixFile
3871 def classlistFiles = []
3872 // add the classlists found int the jalviewjs_classlists_dir
3873 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3875 def name = file.getName() - ".txt"
3882 // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3883 //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3884 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3886 jalviewjsCoreClasslists = []
3888 classlistFiles.each {
3891 def file = hash['file']
3892 if (! file.exists()) {
3893 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3894 return false // this is a "continue" in groovy .each closure
3896 def name = hash['name']
3898 name = file.getName() - ".txt"
3906 def list = fileTree(dir: j2sDir, includes: filelist)
3908 def jsfile = "${outputDir}/core${name}.js"
3909 def zjsfile = "${outputDir}/core${name}.z.js"
3911 jalviewjsCoreClasslists += [
3920 outputs.file(jsfile)
3921 outputs.file(zjsfile)
3924 // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3925 def stevesoftClasslistName = "_stevesoft"
3926 def stevesoftClasslist = [
3927 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3928 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3929 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3930 'name': stevesoftClasslistName
3932 jalviewjsCoreClasslists += stevesoftClasslist
3933 inputs.files(stevesoftClasslist['list'])
3934 outputs.file(stevesoftClasslist['jsfile'])
3935 outputs.file(stevesoftClasslist['zjsfile'])
3938 def allClasslistName = "_all"
3939 def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3940 allJsFiles += fileTree(
3944 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3945 "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3946 "**/org/jmol/export/JSExporter.js"
3949 allJsFiles += fileTree(
3953 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3954 "**/sun/misc/Unsafe.js",
3955 "**/swingjs/jquery/jquery-editable-select.js",
3956 "**/swingjs/jquery/j2sComboBox.js",
3957 "**/sun/misc/FloatingDecimal.js"
3960 def allClasslist = [
3961 'jsfile': "${outputDir}/core${allClasslistName}.js",
3962 'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3964 'name': allClasslistName
3966 // not including this version of "all" core at the moment
3967 //jalviewjsCoreClasslists += allClasslist
3968 inputs.files(allClasslist['list'])
3969 outputs.file(allClasslist['jsfile'])
3970 outputs.file(allClasslist['zjsfile'])
3973 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
3974 logOutFile.getParentFile().mkdirs()
3975 logOutFile.createNewFile()
3976 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
3978 jalviewjsCoreClasslists.each {
3979 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
3986 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
3989 into file(outputFile).getParentFile()
3990 rename { filename ->
3991 if (filename.equals(inputFile.getName())) {
3992 return file(outputFile).getName()
3996 filter(ReplaceTokens,
4000 'MAIN': '"'+main_class+'"',
4002 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
4003 'COREKEY': jalviewjs_core_key,
4004 'CORENAME': coreName
4011 task jalviewjsPublishCoreTemplates {
4012 dependsOn jalviewjsBuildAllCores
4013 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
4014 def inputFile = file(inputFileName)
4015 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4017 def outputFiles = []
4018 jalviewjsCoreClasslists.each { cl ->
4019 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
4020 cl['outputfile'] = outputFile
4021 outputFiles += outputFile
4025 jalviewjsCoreClasslists.each { cl ->
4026 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
4029 inputs.file(inputFile)
4030 outputs.files(outputFiles)
4034 task jalviewjsSyncCore (type: Sync) {
4035 dependsOn jalviewjsBuildAllCores
4036 dependsOn jalviewjsPublishCoreTemplates
4037 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
4038 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
4042 def outputFiles = []
4043 rename { filename ->
4044 outputFiles += "${outputDir}/${filename}"
4050 outputs.files outputFiles
4051 inputs.files inputFiles
4055 // this Copy version of TransferSiteJs will delete anything else in the target dir
4056 task jalviewjsCopyTransferSiteJs(type: Copy) {
4057 dependsOn jalviewjsTranspile
4058 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4059 into "${jalviewDir}/${jalviewjsSiteDir}"
4063 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
4064 task jalviewjsSyncTransferSiteJs(type: Sync) {
4065 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4067 into "${jalviewDir}/${jalviewjsSiteDir}"
4074 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
4075 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
4076 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
4077 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
4079 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
4080 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
4081 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
4082 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
4085 task jalviewjsPrepareSite {
4087 description "Prepares the website folder including unzipping files and copying resources"
4088 dependsOn jalviewjsSyncAllLibs
4089 dependsOn jalviewjsSyncResources
4090 dependsOn jalviewjsSyncSiteResources
4091 dependsOn jalviewjsSyncBuildProperties
4092 dependsOn jalviewjsSyncCore
4096 task jalviewjsBuildSite {
4098 description "Builds the whole website including transpiled code"
4099 dependsOn jalviewjsCopyTransferSiteJs
4100 dependsOn jalviewjsPrepareSite
4104 task cleanJalviewjsTransferSite {
4106 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4107 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4108 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4109 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4114 task cleanJalviewjsSite {
4115 dependsOn cleanJalviewjsTransferSite
4117 delete "${jalviewDir}/${jalviewjsSiteDir}"
4122 task jalviewjsSiteTar(type: Tar) {
4124 description "Creates a tar.gz file for the website"
4125 dependsOn jalviewjsBuildSite
4126 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4127 archiveFileName = outputFilename
4129 compression Compression.GZIP
4131 from "${jalviewDir}/${jalviewjsSiteDir}"
4132 into jalviewjs_site_dir // this is inside the tar file
4134 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4138 task jalviewjsServer {
4140 def filename = "jalviewjsTest.html"
4141 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4142 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4147 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4148 factory = f.newInstance()
4149 } catch (ClassNotFoundException e) {
4150 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4152 def port = Integer.valueOf(jalviewjs_server_port)
4157 while(port < start+1000 && !running) {
4159 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4160 jalviewjsServer = factory.start(doc_root, port)
4162 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4163 println("SERVER STARTED with document root ${doc_root}.")
4164 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4165 println("For debug: "+url+"?j2sdebug")
4166 println("For verbose: "+url+"?j2sverbose")
4167 } catch (Exception e) {
4172 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4173 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4174 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4176 jalviewjsCoreClasslists.each { cl ->
4177 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4179 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4181 println("For core ${cl.name}: "+urlcore)
4184 file(htmlFile).text = htmlText
4187 outputs.file(htmlFile)
4188 outputs.upToDateWhen({false})
4192 task cleanJalviewjsAll {
4194 description "Delete all configuration and build artifacts to do with JalviewJS build"
4195 dependsOn cleanJalviewjsSite
4196 dependsOn jalviewjsEclipsePaths
4199 delete "${jalviewDir}/${jalviewjsBuildDir}"
4200 delete "${jalviewDir}/${eclipse_bin_dir}"
4201 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4202 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4204 delete jalviewjsJ2sAltSettingsFileName
4207 outputs.upToDateWhen( { false } )
4211 task jalviewjsIDE_checkJ2sPlugin {
4212 group "00 JalviewJS in Eclipse"
4213 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4216 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4217 def j2sPluginFile = file(j2sPlugin)
4218 def eclipseHome = System.properties["eclipse.home.location"]
4219 if (eclipseHome == null || ! IN_ECLIPSE) {
4220 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4222 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4223 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4224 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4225 eclipseJ2sPluginDirs += altPluginsDir
4227 def foundPlugin = false
4228 def j2sPluginFileName = j2sPluginFile.getName()
4229 def eclipseJ2sPlugin
4230 def eclipseJ2sPluginFile
4231 eclipseJ2sPluginDirs.any { dir ->
4232 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4233 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4234 if (eclipseJ2sPluginFile.exists()) {
4240 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4241 System.err.println(msg)
4242 throw new StopExecutionException(msg)
4245 def digest = MessageDigest.getInstance("MD5")
4247 digest.update(j2sPluginFile.text.bytes)
4248 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4250 digest.update(eclipseJ2sPluginFile.text.bytes)
4251 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4253 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4254 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4255 System.err.println(msg)
4256 throw new StopExecutionException(msg)
4258 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4264 task jalviewjsIDE_copyJ2sPlugin {
4265 group "00 JalviewJS in Eclipse"
4266 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4269 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4270 def j2sPluginFile = file(j2sPlugin)
4271 def eclipseHome = System.properties["eclipse.home.location"]
4272 if (eclipseHome == null || ! IN_ECLIPSE) {
4273 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4275 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4276 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4277 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4278 System.err.println(msg)
4281 eclipseJ2sPluginFile.getParentFile().mkdirs()
4282 into eclipseJ2sPluginFile.getParent()
4288 task jalviewjsIDE_j2sFile {
4289 group "00 JalviewJS in Eclipse"
4290 description "Creates the .j2s file"
4291 dependsOn jalviewjsCreateJ2sSettings
4295 task jalviewjsIDE_SyncCore {
4296 group "00 JalviewJS in Eclipse"
4297 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4298 dependsOn jalviewjsSyncCore
4302 task jalviewjsIDE_SyncSiteAll {
4303 dependsOn jalviewjsSyncAllLibs
4304 dependsOn jalviewjsSyncResources
4305 dependsOn jalviewjsSyncSiteResources
4306 dependsOn jalviewjsSyncBuildProperties
4310 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4313 task jalviewjsIDE_PrepareSite {
4314 group "00 JalviewJS in Eclipse"
4315 description "Sync libs and resources to site dir, but not closure cores"
4317 dependsOn jalviewjsIDE_SyncSiteAll
4318 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4322 task jalviewjsIDE_AssembleSite {
4323 group "00 JalviewJS in Eclipse"
4324 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4325 dependsOn jalviewjsPrepareSite
4329 task jalviewjsIDE_SiteClean {
4330 group "00 JalviewJS in Eclipse"
4331 description "Deletes the Eclipse transpiled site"
4332 dependsOn cleanJalviewjsSite
4336 task jalviewjsIDE_Server {
4337 group "00 JalviewJS in Eclipse"
4338 description "Starts a webserver on localhost to test the website"
4339 dependsOn jalviewjsServer
4343 // buildship runs this at import or gradle refresh
4344 task eclipseSynchronizationTask {
4345 //dependsOn eclipseSetup
4346 dependsOn createBuildProperties
4348 dependsOn jalviewjsIDE_j2sFile
4349 dependsOn jalviewjsIDE_checkJ2sPlugin
4350 dependsOn jalviewjsIDE_PrepareSite
4355 // buildship runs this at build time or project refresh
4356 task eclipseAutoBuildTask {
4357 //dependsOn jalviewjsIDE_checkJ2sPlugin
4358 //dependsOn jalviewjsIDE_PrepareSite
4362 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4363 from file(jalviewjs_stderr_launch)
4364 into jalviewjsSiteDir
4366 inputs.file jalviewjs_stderr_launch
4367 outputs.file jalviewjsStderrLaunchFilename
4370 task cleanJalviewjsChromiumUserDir {
4372 delete jalviewjsChromiumUserDir
4374 outputs.dir jalviewjsChromiumUserDir
4375 // always run when depended on
4376 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4379 task jalviewjsChromiumProfile {
4380 dependsOn cleanJalviewjsChromiumUserDir
4381 mustRunAfter cleanJalviewjsChromiumUserDir
4383 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4386 mkdir jalviewjsChromiumProfileDir
4389 outputs.file firstRun
4392 task jalviewjsLaunchTest {
4394 description "Check JalviewJS opens in a browser"
4395 dependsOn jalviewjsBuildSite
4396 dependsOn jalviewjsCopyStderrLaunchFile
4397 dependsOn jalviewjsChromiumProfile
4399 def macOS = OperatingSystem.current().isMacOsX()
4400 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4401 if (chromiumBinary.startsWith("~/")) {
4402 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4408 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4410 def binary = file(chromiumBinary)
4411 if (!binary.exists()) {
4412 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4414 stdout = new ByteArrayOutputStream()
4415 stderr = new ByteArrayOutputStream()
4418 if (jalviewjs_j2s_to_console.equals("true")) {
4419 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4422 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4430 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4433 "--timeout=${timeoutms}",
4434 "--virtual-time-budget=${timeoutms}",
4435 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4436 "--profile-directory=${jalviewjs_chromium_profile_name}",
4437 "--allow-file-access-from-files",
4438 "--enable-logging=stderr",
4439 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4442 if (true || macOS) {
4443 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4444 Future f1 = executor.submit(
4447 standardOutput = execStdout
4448 errorOutput = execStderr
4449 executable(chromiumBinary)
4451 println "COMMAND: '"+commandLine.join(" ")+"'"
4453 executor.shutdownNow()
4457 def noChangeBytes = 0
4458 def noChangeIterations = 0
4459 executor.scheduleAtFixedRate(
4461 String stderrString = stderr.toString()
4462 // shutdown the task if we have a success string
4463 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4466 executor.shutdownNow()
4468 // if no change in stderr for 10s then also end
4469 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4470 executor.shutdownNow()
4472 if (stderrString.length() == noChangeBytes) {
4473 noChangeIterations++
4475 noChangeBytes = stderrString.length()
4476 noChangeIterations = 0
4479 1, 1, TimeUnit.SECONDS)
4481 executor.schedule(new Runnable(){
4484 executor.shutdownNow()
4486 }, timeoutms, TimeUnit.MILLISECONDS)
4488 executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
4489 executor.shutdownNow()
4496 stderr.toString().eachLine { line ->
4497 if (line.contains(jalviewjs_desktop_init_string)) {
4498 println("Found line '"+line+"'")
4504 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4512 description "Build the JalviewJS site and run the launch test"
4513 dependsOn jalviewjsBuildSite
4514 dependsOn jalviewjsLaunchTest