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 // delete .VolumeIcon.icns in macos DMG if there isn't one
2730 if (!file(install4jDMGVolumeIcon).exists()) {
2731 println("No '.VolumeIcon.icns' file found. Removing from install4j file.")
2732 install4jConfigXml.'**'.macosArchive.topLevelFiles.each { topLevelFiles ->
2733 topLevelFiles.file.each() { file ->
2734 if (file.attribute("name") && file.attribute("name").equals(".VolumeIcon.icns")) {
2735 println("Removing "+file.toString())
2736 topLevelFiles.remove(file)
2741 println("Using '.VolumeIcon.icns' file '${install4jDMGVolumeIcon}'")
2744 // disable install screen for OSX dmg (for 2.11.2.0)
2745 install4jConfigXml.'**'.macosArchive.each { macosArchive ->
2746 macosArchive.attributes().remove('executeSetupApp')
2747 macosArchive.attributes().remove('setupAppId')
2750 // turn off checksum creation for LOCAL channel
2751 def e = install4jConfigXml.application[0]
2752 e.'@createChecksums' = string(install4jCheckSums)
2754 // put file association actions where placeholder action is
2755 def install4jFileAssociationsText = install4jFileAssociationsFile.text
2756 def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
2757 install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
2758 if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
2759 def parent = a.parent()
2761 fileAssociationActions.each { faa ->
2764 // don't need to continue in .any loop once replacements have been made
2769 // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
2770 // NB we're deleting the /other/ one!
2771 // Also remove the examples subdir from non-release versions
2772 def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
2773 // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
2774 if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
2775 customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
2777 // remove the examples subdir from Full File Set
2778 def files = install4jConfigXml.files[0]
2779 def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
2780 def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
2781 def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
2782 def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
2783 dirEntry.parent().remove(dirEntry)
2785 install4jConfigXml.'**'.action.any { a ->
2786 if (a.'@customizedId' == customizedIdToDelete) {
2787 def parent = a.parent()
2793 // write install4j file
2794 install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
2801 delete install4jConfFile
2805 task cleanInstallersDataFiles {
2806 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
2807 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
2808 def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
2810 delete installersOutputTxt
2811 delete installersSha256
2812 delete hugoDataJsonFile
2816 task install4jDMGBackgroundImageCopy {
2817 inputs.file "${install4jDMGBackgroundImageDir}/${install4jDMGBackgroundImageFile}"
2818 outputs.dir "${install4jDMGBackgroundImageBuildDir}"
2821 from(install4jDMGBackgroundImageDir) {
2822 include(install4jDMGBackgroundImageFile)
2824 into install4jDMGBackgroundImageBuildDir
2829 task install4jDMGBackgroundImageProcess {
2830 dependsOn install4jDMGBackgroundImageCopy
2833 if (backgroundImageText) {
2834 if (convertBinary == null) {
2835 throw new StopExecutionException("No ImageMagick convert binary installed at '${convertBinaryExpectedLocation}'")
2837 if (!project.hasProperty("install4j_background_image_text_suffix_cmd")) {
2838 throw new StopExecutionException("No property 'install4j_background_image_text_suffix_cmd' defined. See channel_gradle.properties for channel ${CHANNEL}")
2840 fileTree(dir: install4jDMGBackgroundImageBuildDir, include: "*.png").getFiles().each { file ->
2842 executable convertBinary
2845 '-font', install4j_background_image_text_font,
2846 '-fill', install4j_background_image_text_colour,
2847 '-draw', sprintf(install4j_background_image_text_suffix_cmd, channelSuffix),
2848 '-draw', sprintf(install4j_background_image_text_commit_cmd, "git-commit: ${gitHash}"),
2849 '-draw', sprintf(install4j_background_image_text_date_cmd, getDate("yyyy-MM-dd HH:mm:ss")),
2860 pip 'ds_store:1.3.1'
2863 task install4jCustomiseDS_StoreX64(type: PythonTask) {
2864 inputs.file(install4jDMGDSStore)
2865 outputs.file(install4jDMGFixedDSStoreX64)
2866 def command_args = [ jalview_customise_ds_store,
2867 '--input', install4jDMGDSStore,
2868 '--output', install4jDMGFixedDSStoreX64,
2869 '--volumename', install4jmacOSArchiveX64Name,
2870 '--backgroundfile', install4j_dmg_background_filename,
2871 '--dmg', install4jmacOSArchiveX64DMGFilename + ".dmg",
2872 '--appname', "${applicationName}.app",
2874 if (file(install4jDMGDSStoreJSON).exists()) {
2875 command_args += [ '--config', install4jDMGDSStoreJSON ]
2876 inputs.file(install4jDMGDSStoreJSON)
2878 command = command_args
2880 println("Running command '${command_args.join(' ')}'")
2884 task install4jCustomiseDS_StoreAarch64(type: PythonTask) {
2885 inputs.file(install4jDMGDSStore)
2886 outputs.file(install4jDMGFixedDSStoreAarch64)
2887 def command_args = [ jalview_customise_ds_store,
2888 '--input', install4jDMGDSStore,
2889 '--output', install4jDMGFixedDSStoreAarch64,
2890 '--volumename', install4jmacOSArchiveAarch64Name,
2891 '--backgroundfile', install4j_dmg_background_filename,
2892 '--dmg', install4jmacOSArchiveAarch64DMGFilename + ".dmg",
2893 '--appname', "${applicationName}.app",
2895 if (file(install4jDMGDSStoreJSON).exists()) {
2896 command_args += [ '--config', install4jDMGDSStoreJSON ]
2897 inputs.file(install4jDMGDSStoreJSON)
2899 command = command_args
2902 for (int i = 0; i < command_args.size(); i++) {
2903 def arg = command_args[i]
2904 print_args += (i > 0 && !arg.startsWith("-")) ? "\"${arg}\"" : arg
2906 println("Running command '${print_args.join(' ')}'")
2910 task install4jCustomiseDS_Store {
2911 dependsOn install4jCustomiseDS_StoreX64
2912 dependsOn install4jCustomiseDS_StoreAarch64
2915 task install4jDMGProcesses {
2916 dependsOn install4jDMGBackgroundImageProcess
2917 dependsOn install4jCustomiseDS_Store
2920 task installerFiles(type: com.install4j.gradle.Install4jTask) {
2921 group = "distribution"
2922 description = "Create the install4j installers"
2924 dependsOn copyInstall4jTemplate
2925 dependsOn cleanInstallersDataFiles
2926 dependsOn install4jDMGProcesses
2928 projectFile = install4jConfFile
2930 // run install4j with 4g
2931 vmParameters = ["-Xmx4294967296"]
2933 // create an md5 for the input files to use as version for install4j conf file
2934 def digest = MessageDigest.getInstance("MD5")
2936 (file("${install4jDir}/${install4j_template}").text +
2937 file("${install4jDir}/${install4j_info_plist_file_associations}").text +
2938 file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
2939 def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
2940 if (filesMd5.length() >= 8) {
2941 filesMd5 = filesMd5.substring(0,8)
2943 def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
2946 'JALVIEW_NAME': jalview_name,
2947 'JALVIEW_APPLICATION_NAME': applicationName,
2948 'JALVIEW_DIR': "../..",
2949 'OSX_KEYSTORE': OSX_KEYSTORE,
2950 'OSX_APPLEID': OSX_APPLEID,
2951 'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
2952 'JSIGN_SH': JSIGN_SH,
2953 'JRE_DIR': getdown_app_dir_java,
2954 'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
2955 'JALVIEW_VERSION': JALVIEW_VERSION,
2956 'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
2957 'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
2958 'JAVA_VERSION': JAVA_VERSION,
2959 'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
2960 'VERSION': JALVIEW_VERSION,
2961 'COPYRIGHT_MESSAGE': install4j_copyright_message,
2962 'BUNDLE_ID': install4jBundleId,
2963 'INTERNAL_ID': install4jInternalId,
2964 'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
2965 'MACOS_X64_DMG_DS_STORE': install4jDMGFixedDSStoreX64,
2966 'MACOS_AARCH64_DMG_DS_STORE': install4jDMGFixedDSStoreAarch64,
2967 'MACOS_DMG_BG_IMAGE': "${install4jDMGBackgroundImageBuildDir}/${install4jDMGBackgroundImageFile}",
2968 'MACOS_DMG_BG_FILENAME': install4j_dmg_background_filename,
2969 'WRAPPER_LINK': getdownWrapperLink,
2970 'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
2971 'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
2972 'BATCH_WRAPPER_SCRIPT': getdown_batch_wrapper_script,
2973 'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
2974 'MACOSARCHIVE_X64_NAME': install4jmacOSArchiveX64Name,
2975 'MACOSARCHIVE_AARCH64_NAME': install4jmacOSArchiveAarch64Name,
2976 'INSTALL4J_UTILS_DIR': install4j_utils_dir,
2977 'GETDOWN_CHANNEL_DIR': getdownChannelDir,
2978 'GETDOWN_FILES_DIR': getdown_files_dir,
2979 'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
2980 'GETDOWN_DIST_DIR': getdownAppDistDir,
2981 'GETDOWN_ALT_DIR': getdown_app_dir_alt,
2982 'GETDOWN_INSTALL_DIR': getdown_install_dir,
2983 'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
2984 'BUILD_DIR': install4jBuildDir,
2985 'APPLICATION_CATEGORIES': install4j_application_categories,
2986 'APPLICATION_FOLDER': install4jApplicationFolder,
2987 'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
2988 'EXECUTABLE_NAME': install4jExecutableName,
2989 'EXTRA_SCHEME': install4jExtraScheme,
2990 'MAC_ICONS_FILE': install4jMacIconsFile,
2991 'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
2992 'PNG_ICON_FILE': install4jPngIconFile,
2993 'BACKGROUND': install4jBackground,
2994 'MACOSARCHIVE_X64_DMG_FILENAME': install4jmacOSArchiveX64DMGFilename,
2995 'MACOSARCHIVE_AARCH64_DMG_FILENAME': install4jmacOSArchiveAarch64DMGFilename,
2996 'MACOSARCHIVE_VOLUMEICON': install4jDMGVolumeIcon,
3001 'windows': 'WINDOWS',
3005 // these are the bundled OS/architecture VMs needed by install4j
3008 [ "mac", "aarch64" ],
3009 [ "windows", "x64" ],
3011 [ "linux", "aarch64" ]
3013 osArch.forEach { os, arch ->
3014 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)
3015 // N.B. For some reason install4j requires the below filename to have underscores and not hyphens
3016 // otherwise running `gradle installers` generates a non-useful error:
3017 // `install4j: compilation failed. Reason: java.lang.NumberFormatException: For input string: "windows"`
3018 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)
3021 //println("INSTALL4J VARIABLES:")
3022 //variables.each{k,v->println("${k}=${v}")}
3024 destination = "${jalviewDir}/${install4jBuildDir}"
3025 buildSelected = true
3027 if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
3029 disableSigning = true
3030 disableNotarization = true
3034 macKeystorePassword = OSX_KEYPASS
3037 if (OSX_ALTOOLPASS) {
3038 appleIdPassword = OSX_ALTOOLPASS
3039 disableNotarization = false
3041 disableNotarization = true
3045 println("Using projectFile "+projectFile)
3046 if (!disableNotarization) { println("Will notarize OSX App DMG") }
3050 inputs.dir(getdownAppBaseDir)
3051 inputs.file(install4jConfFile)
3052 inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
3053 outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
3056 def getDataHash(File myFile) {
3057 HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
3058 return myFile.exists()
3060 "file" : myFile.getName(),
3061 "filesize" : myFile.length(),
3062 "sha256" : hash.toString()
3067 def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
3069 "channel" : getdownChannelName,
3070 "date" : getDate("yyyy-MM-dd HH:mm:ss"),
3071 "git-commit" : "${gitHash} [${gitBranch}]",
3072 "version" : JALVIEW_VERSION
3074 // install4j installer files
3075 if (installersOutputTxt.exists()) {
3077 installersOutputTxt.readLines().each { def line ->
3078 if (line.startsWith("#")) {
3081 line.replaceAll("\n","")
3082 def vals = line.split("\t")
3083 def filename = vals[3]
3084 def filesize = file(filename).length()
3085 filename = filename.replaceAll(/^.*\//, "")
3086 hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
3087 idHash."${filename}" = vals[0]
3089 if (install4jCheckSums && installersSha256.exists()) {
3090 installersSha256.readLines().each { def line ->
3091 if (line.startsWith("#")) {
3094 line.replaceAll("\n","")
3095 def vals = line.split(/\s+\*?/)
3096 def filename = vals[1]
3097 def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
3103 "JAR": shadowJar.archiveFile, // executable JAR
3104 "JVL": getdownVersionLaunchJvl, // version JVL
3105 "SOURCE": sourceDist.archiveFile // source TGZ
3106 ].each { key, value ->
3107 def file = file(value)
3108 if (file.exists()) {
3109 def fileHash = getDataHash(file)
3110 if (fileHash != null) {
3111 hash."${key}" = fileHash;
3115 return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
3118 task staticMakeInstallersJsonFile {
3120 def output = findProperty("i4j_output")
3121 def sha256 = findProperty("i4j_sha256")
3122 def json = findProperty("i4j_json")
3123 if (output == null || sha256 == null || json == null) {
3124 throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
3126 writeDataJsonFile(file(output), file(sha256), file(json))
3131 dependsOn installerFiles
3137 eclipse().configFile(eclipse_codestyle_file)
3141 task createSourceReleaseProperties(type: WriteProperties) {
3142 group = "distribution"
3143 description = "Create the source RELEASE properties file"
3145 def sourceTarBuildDir = "${buildDir}/sourceTar"
3146 def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
3147 outputFile (sourceReleasePropertiesFile)
3150 releaseProps.each{ key, val -> property key, val }
3151 property "git.branch", gitBranch
3152 property "git.hash", gitHash
3155 outputs.file(outputFile)
3158 task sourceDist(type: Tar) {
3159 group "distribution"
3160 description "Create a source .tar.gz file for distribution"
3162 dependsOn createBuildProperties
3163 dependsOn convertMdFiles
3164 dependsOn eclipseAllPreferences
3165 dependsOn createSourceReleaseProperties
3168 def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
3169 archiveFileName = outputFileName
3171 compression Compression.GZIP
3187 "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
3189 "utils/InstallAnywhere",
3204 "gradle.properties",
3216 ".settings/org.eclipse.buildship.core.prefs",
3217 ".settings/org.eclipse.jdt.core.prefs"
3221 exclude (EXCLUDE_FILES)
3222 include (PROCESS_FILES)
3223 filter(ReplaceTokens,
3227 'Version-Rel': JALVIEW_VERSION,
3228 'Year-Rel': getDate("yyyy")
3233 exclude (EXCLUDE_FILES)
3234 exclude (PROCESS_FILES)
3235 exclude ("appletlib")
3236 exclude ("**/*locales")
3237 exclude ("*locales/**")
3238 exclude ("utils/InstallAnywhere")
3240 exclude (getdown_files_dir)
3241 // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
3242 //exclude (getdown_website_dir)
3243 //exclude (getdown_archive_dir)
3245 // exluding these as not using jars as modules yet
3246 exclude ("${j11modDir}/**/*.jar")
3249 include(INCLUDE_FILES)
3251 // from (jalviewDir) {
3252 // // explicit includes for stuff that seemed to not get included
3253 // include(fileTree("test/**/*."))
3254 // exclude(EXCLUDE_FILES)
3255 // exclude(PROCESS_FILES)
3258 from(file(buildProperties).getParent()) {
3259 include(file(buildProperties).getName())
3260 rename(file(buildProperties).getName(), "build_properties")
3262 line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
3266 def sourceTarBuildDir = "${buildDir}/sourceTar"
3267 from(sourceTarBuildDir) {
3268 // this includes the appended RELEASE properties file
3272 task dataInstallersJson {
3274 description "Create the installers-VERSION.json data file for installer files created"
3276 mustRunAfter installers
3277 mustRunAfter shadowJar
3278 mustRunAfter sourceDist
3279 mustRunAfter getdownArchive
3281 def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
3282 def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
3284 if (installersOutputTxt.exists()) {
3285 inputs.file(installersOutputTxt)
3287 if (install4jCheckSums && installersSha256.exists()) {
3288 inputs.file(installersSha256)
3291 shadowJar.archiveFile, // executable JAR
3292 getdownVersionLaunchJvl, // version JVL
3293 sourceDist.archiveFile // source TGZ
3294 ].each { fileName ->
3295 if (file(fileName).exists()) {
3296 inputs.file(fileName)
3300 outputs.file(hugoDataJsonFile)
3303 writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
3309 description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
3312 dependsOn pubhtmlhelp
3314 inputs.dir("${helpBuildDir}/${help_dir}")
3315 outputs.dir("${buildDir}/distributions/${help_dir}")
3319 task j2sSetHeadlessBuild {
3326 task jalviewjsEnableAltFileProperty(type: WriteProperties) {
3328 description "Enable the alternative J2S Config file for headless build"
3330 outputFile = jalviewjsJ2sSettingsFileName
3331 def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
3332 def j2sProps = new Properties()
3333 if (j2sPropsFile.exists()) {
3335 def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
3336 j2sProps.load(j2sPropsFileFIS)
3337 j2sPropsFileFIS.close()
3339 j2sProps.each { prop, val ->
3342 } catch (Exception e) {
3343 println("Exception reading ${jalviewjsJ2sSettingsFileName}")
3347 if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
3348 property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
3353 task jalviewjsSetEclipseWorkspace {
3354 def propKey = "jalviewjs_eclipse_workspace"
3356 if (project.hasProperty(propKey)) {
3357 propVal = project.getProperty(propKey)
3358 if (propVal.startsWith("~/")) {
3359 propVal = System.getProperty("user.home") + propVal.substring(1)
3362 def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
3363 def propsFile = file(propsFileName)
3364 def eclipseWsDir = propVal
3365 def props = new Properties()
3367 def writeProps = true
3368 if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
3369 def ins = new FileInputStream(propsFileName)
3372 if (props.getProperty(propKey, null) != null) {
3373 eclipseWsDir = props.getProperty(propKey)
3378 if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
3379 def tempDir = File.createTempDir()
3380 eclipseWsDir = tempDir.getAbsolutePath()
3383 eclipseWorkspace = file(eclipseWsDir)
3386 // do not run a headless transpile when we claim to be in Eclipse
3388 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3389 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3391 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3395 props.setProperty(propKey, eclipseWsDir)
3396 propsFile.parentFile.mkdirs()
3397 def bytes = new ByteArrayOutputStream()
3398 props.store(bytes, null)
3399 def propertiesString = bytes.toString()
3400 propsFile.text = propertiesString
3406 println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
3409 //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
3410 outputs.file(propsFileName)
3411 outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
3415 task jalviewjsEclipsePaths {
3418 def eclipseRoot = jalviewjs_eclipse_root
3419 if (eclipseRoot.startsWith("~/")) {
3420 eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
3422 if (OperatingSystem.current().isMacOsX()) {
3423 eclipseRoot += "/Eclipse.app"
3424 eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
3425 eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
3426 } else if (OperatingSystem.current().isWindows()) { // check these paths!!
3427 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3428 eclipseRoot += "/eclipse"
3430 eclipseBinary = "${eclipseRoot}/eclipse.exe"
3431 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3432 } else { // linux or unix
3433 if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
3434 eclipseRoot += "/eclipse"
3435 println("eclipseDir exists")
3437 eclipseBinary = "${eclipseRoot}/eclipse"
3438 eclipseProduct = "${eclipseRoot}/.eclipseproduct"
3441 eclipseVersion = "4.13" // default
3442 def assumedVersion = true
3443 if (file(eclipseProduct).exists()) {
3444 def fis = new FileInputStream(eclipseProduct)
3445 def props = new Properties()
3447 eclipseVersion = props.getProperty("version")
3449 assumedVersion = false
3452 def propKey = "eclipse_debug"
3453 eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
3456 // do not run a headless transpile when we claim to be in Eclipse
3458 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3459 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3461 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3464 if (!assumedVersion) {
3465 println("ECLIPSE VERSION=${eclipseVersion}")
3471 task printProperties {
3473 description "Output to console all System.properties"
3475 System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
3481 dependsOn eclipseProject
3482 dependsOn eclipseClasspath
3483 dependsOn eclipseJdt
3487 // this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
3488 task jalviewjsEclipseCopyDropins(type: Copy) {
3489 dependsOn jalviewjsEclipsePaths
3491 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
3492 inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
3493 def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
3500 // this eclipse -clean doesn't actually work
3501 task jalviewjsCleanEclipse(type: Exec) {
3502 dependsOn eclipseSetup
3503 dependsOn jalviewjsEclipsePaths
3504 dependsOn jalviewjsEclipseCopyDropins
3506 executable(eclipseBinary)
3507 args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
3513 def inputString = """exit
3516 def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
3517 standardInput = inputByteStream
3520 /* not really working yet
3521 jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
3525 task jalviewjsTransferUnzipSwingJs {
3526 def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
3530 from zipTree(file_zip)
3531 into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3535 inputs.file file_zip
3536 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
3540 task jalviewjsTransferUnzipLib {
3541 def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
3544 zipFiles.each { file_zip ->
3546 from zipTree(file_zip)
3547 into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3552 inputs.files zipFiles
3553 outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
3557 task jalviewjsTransferUnzipAllLibs {
3558 dependsOn jalviewjsTransferUnzipSwingJs
3559 dependsOn jalviewjsTransferUnzipLib
3563 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
3565 description "Create the alternative j2s file from the j2s.* properties"
3567 jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
3568 def siteDirProperty = "j2s.site.directory"
3569 def setSiteDir = false
3570 jalviewjsJ2sProps.each { prop, val ->
3572 if (prop == siteDirProperty) {
3573 if (!(val.startsWith('/') || val.startsWith("file://") )) {
3574 val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
3580 if (!setSiteDir) { // default site location, don't override specifically set property
3581 property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
3584 outputFile = jalviewjsJ2sAltSettingsFileName
3587 inputs.properties(jalviewjsJ2sProps)
3588 outputs.file(jalviewjsJ2sAltSettingsFileName)
3593 task jalviewjsEclipseSetup {
3594 dependsOn jalviewjsEclipseCopyDropins
3595 dependsOn jalviewjsSetEclipseWorkspace
3596 dependsOn jalviewjsCreateJ2sSettings
3600 task jalviewjsSyncAllLibs (type: Sync) {
3601 dependsOn jalviewjsTransferUnzipAllLibs
3602 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
3603 inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
3604 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3608 def outputFiles = []
3609 rename { filename ->
3610 outputFiles += "${outputDir}/${filename}"
3617 // should this be exclude really ?
3618 duplicatesStrategy "INCLUDE"
3620 outputs.files outputFiles
3621 inputs.files inputFiles
3625 task jalviewjsSyncResources (type: Sync) {
3626 dependsOn buildResources
3628 def inputFiles = fileTree(dir: resourcesBuildDir)
3629 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3633 def outputFiles = []
3634 rename { filename ->
3635 outputFiles += "${outputDir}/${filename}"
3641 outputs.files outputFiles
3642 inputs.files inputFiles
3646 task jalviewjsSyncSiteResources (type: Sync) {
3647 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
3648 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
3652 def outputFiles = []
3653 rename { filename ->
3654 outputFiles += "${outputDir}/${filename}"
3660 outputs.files outputFiles
3661 inputs.files inputFiles
3665 task jalviewjsSyncBuildProperties (type: Sync) {
3666 dependsOn createBuildProperties
3667 def inputFiles = [file(buildProperties)]
3668 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
3672 def outputFiles = []
3673 rename { filename ->
3674 outputFiles += "${outputDir}/${filename}"
3680 outputs.files outputFiles
3681 inputs.files inputFiles
3685 task jalviewjsProjectImport(type: Exec) {
3686 dependsOn eclipseSetup
3687 dependsOn jalviewjsEclipsePaths
3688 dependsOn jalviewjsEclipseSetup
3691 // do not run a headless import when we claim to be in Eclipse
3693 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3694 throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3696 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3700 //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
3701 def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
3702 executable(eclipseBinary)
3703 args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
3707 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3709 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3710 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3713 inputs.file("${jalviewDir}/.project")
3714 outputs.upToDateWhen {
3715 file(projdir).exists()
3720 task jalviewjsTranspile(type: Exec) {
3721 dependsOn jalviewjsEclipseSetup
3722 dependsOn jalviewjsProjectImport
3723 dependsOn jalviewjsEclipsePaths
3725 dependsOn jalviewjsEnableAltFileProperty
3729 // do not run a headless transpile when we claim to be in Eclipse
3731 println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3732 throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
3734 println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
3738 executable(eclipseBinary)
3739 args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
3743 args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
3745 args += [ "-D${j2sHeadlessBuildProperty}=true" ]
3746 args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
3752 stdout = new ByteArrayOutputStream()
3753 stderr = new ByteArrayOutputStream()
3755 def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
3756 def logOutFile = file(logOutFileName)
3757 logOutFile.createNewFile()
3758 logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
3759 BINARY: ${eclipseBinary}
3760 VERSION: ${eclipseVersion}
3761 WORKSPACE: ${eclipseWorkspace}
3762 DEBUG: ${eclipseDebug}
3765 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3766 // combine stdout and stderr
3767 def logErrFOS = logOutFOS
3769 if (jalviewjs_j2s_to_console.equals("true")) {
3770 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3771 new org.apache.tools.ant.util.TeeOutputStream(
3775 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3776 new org.apache.tools.ant.util.TeeOutputStream(
3781 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3784 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3791 if (stdout.toString().contains("Error processing ")) {
3792 // j2s did not complete transpile
3793 //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3794 if (jalviewjs_ignore_transpile_errors.equals("true")) {
3795 println("IGNORING TRANSPILE ERRORS")
3796 println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3798 throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
3803 inputs.dir("${jalviewDir}/${sourceDir}")
3804 outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
3805 outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
3809 def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
3811 def stdout = new ByteArrayOutputStream()
3812 def stderr = new ByteArrayOutputStream()
3814 def coreFile = file(jsfile)
3816 msg = "Creating core for ${name}...\nGenerating ${jsfile}"
3818 logOutFile.createNewFile()
3819 logOutFile.append(msg+"\n")
3821 def coreTop = file(prefixFile)
3822 def coreBottom = file(suffixFile)
3823 coreFile.getParentFile().mkdirs()
3824 coreFile.createNewFile()
3825 coreFile.write( coreTop.getText("UTF-8") )
3829 def t = f.getText("UTF-8")
3830 t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
3831 coreFile.append( t )
3833 msg = "...file '"+f.getPath()+"' does not exist, skipping"
3835 logOutFile.append(msg+"\n")
3838 coreFile.append( coreBottom.getText("UTF-8") )
3840 msg = "Generating ${zjsfile}"
3842 logOutFile.append(msg+"\n")
3843 def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
3844 def logErrFOS = logOutFOS
3847 classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
3848 main = "com.google.javascript.jscomp.CommandLineRunner"
3849 jvmArgs = [ "-Dfile.encoding=UTF-8" ]
3850 args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
3853 msg = "\nRunning '"+commandLine.join(' ')+"'\n"
3855 logOutFile.append(msg+"\n")
3857 if (logOutConsole) {
3858 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3859 new org.apache.tools.ant.util.TeeOutputStream(
3863 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3864 new org.apache.tools.ant.util.TeeOutputStream(
3869 standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
3872 errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
3879 logOutFile.append(msg+"\n")
3883 task jalviewjsBuildAllCores {
3885 description "Build the core js lib closures listed in the classlists dir"
3886 dependsOn jalviewjsTranspile
3887 dependsOn jalviewjsTransferUnzipSwingJs
3889 def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
3890 def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
3891 def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
3892 def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
3893 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
3894 def prefixFile = "${jsDir}/core/coretop2.js"
3895 def suffixFile = "${jsDir}/core/corebottom2.js"
3897 inputs.file prefixFile
3898 inputs.file suffixFile
3900 def classlistFiles = []
3901 // add the classlists found int the jalviewjs_classlists_dir
3902 fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
3904 def name = file.getName() - ".txt"
3911 // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
3912 //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
3913 classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
3915 jalviewjsCoreClasslists = []
3917 classlistFiles.each {
3920 def file = hash['file']
3921 if (! file.exists()) {
3922 //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
3923 return false // this is a "continue" in groovy .each closure
3925 def name = hash['name']
3927 name = file.getName() - ".txt"
3935 def list = fileTree(dir: j2sDir, includes: filelist)
3937 def jsfile = "${outputDir}/core${name}.js"
3938 def zjsfile = "${outputDir}/core${name}.z.js"
3940 jalviewjsCoreClasslists += [
3949 outputs.file(jsfile)
3950 outputs.file(zjsfile)
3953 // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
3954 def stevesoftClasslistName = "_stevesoft"
3955 def stevesoftClasslist = [
3956 'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
3957 'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
3958 'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
3959 'name': stevesoftClasslistName
3961 jalviewjsCoreClasslists += stevesoftClasslist
3962 inputs.files(stevesoftClasslist['list'])
3963 outputs.file(stevesoftClasslist['jsfile'])
3964 outputs.file(stevesoftClasslist['zjsfile'])
3967 def allClasslistName = "_all"
3968 def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
3969 allJsFiles += fileTree(
3973 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3974 "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
3975 "**/org/jmol/export/JSExporter.js"
3978 allJsFiles += fileTree(
3982 // these exlusions are files that the closure-compiler produces errors for. Should fix them
3983 "**/sun/misc/Unsafe.js",
3984 "**/swingjs/jquery/jquery-editable-select.js",
3985 "**/swingjs/jquery/j2sComboBox.js",
3986 "**/sun/misc/FloatingDecimal.js"
3989 def allClasslist = [
3990 'jsfile': "${outputDir}/core${allClasslistName}.js",
3991 'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
3993 'name': allClasslistName
3995 // not including this version of "all" core at the moment
3996 //jalviewjsCoreClasslists += allClasslist
3997 inputs.files(allClasslist['list'])
3998 outputs.file(allClasslist['jsfile'])
3999 outputs.file(allClasslist['zjsfile'])
4002 def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
4003 logOutFile.getParentFile().mkdirs()
4004 logOutFile.createNewFile()
4005 logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
4007 jalviewjsCoreClasslists.each {
4008 jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
4015 def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
4018 into file(outputFile).getParentFile()
4019 rename { filename ->
4020 if (filename.equals(inputFile.getName())) {
4021 return file(outputFile).getName()
4025 filter(ReplaceTokens,
4029 'MAIN': '"'+main_class+'"',
4031 'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
4032 'COREKEY': jalviewjs_core_key,
4033 'CORENAME': coreName
4040 task jalviewjsPublishCoreTemplates {
4041 dependsOn jalviewjsBuildAllCores
4042 def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
4043 def inputFile = file(inputFileName)
4044 def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4046 def outputFiles = []
4047 jalviewjsCoreClasslists.each { cl ->
4048 def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
4049 cl['outputfile'] = outputFile
4050 outputFiles += outputFile
4054 jalviewjsCoreClasslists.each { cl ->
4055 jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
4058 inputs.file(inputFile)
4059 outputs.files(outputFiles)
4063 task jalviewjsSyncCore (type: Sync) {
4064 dependsOn jalviewjsBuildAllCores
4065 dependsOn jalviewjsPublishCoreTemplates
4066 def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
4067 def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
4071 def outputFiles = []
4072 rename { filename ->
4073 outputFiles += "${outputDir}/${filename}"
4079 outputs.files outputFiles
4080 inputs.files inputFiles
4084 // this Copy version of TransferSiteJs will delete anything else in the target dir
4085 task jalviewjsCopyTransferSiteJs(type: Copy) {
4086 dependsOn jalviewjsTranspile
4087 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4088 into "${jalviewDir}/${jalviewjsSiteDir}"
4092 // this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
4093 task jalviewjsSyncTransferSiteJs(type: Sync) {
4094 from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4096 into "${jalviewDir}/${jalviewjsSiteDir}"
4103 jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
4104 jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
4105 jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
4106 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
4108 jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
4109 jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
4110 jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
4111 jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
4114 task jalviewjsPrepareSite {
4116 description "Prepares the website folder including unzipping files and copying resources"
4117 dependsOn jalviewjsSyncAllLibs
4118 dependsOn jalviewjsSyncResources
4119 dependsOn jalviewjsSyncSiteResources
4120 dependsOn jalviewjsSyncBuildProperties
4121 dependsOn jalviewjsSyncCore
4125 task jalviewjsBuildSite {
4127 description "Builds the whole website including transpiled code"
4128 dependsOn jalviewjsCopyTransferSiteJs
4129 dependsOn jalviewjsPrepareSite
4133 task cleanJalviewjsTransferSite {
4135 delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
4136 delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
4137 delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
4138 delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
4143 task cleanJalviewjsSite {
4144 dependsOn cleanJalviewjsTransferSite
4146 delete "${jalviewDir}/${jalviewjsSiteDir}"
4151 task jalviewjsSiteTar(type: Tar) {
4153 description "Creates a tar.gz file for the website"
4154 dependsOn jalviewjsBuildSite
4155 def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
4156 archiveFileName = outputFilename
4158 compression Compression.GZIP
4160 from "${jalviewDir}/${jalviewjsSiteDir}"
4161 into jalviewjs_site_dir // this is inside the tar file
4163 inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
4167 task jalviewjsServer {
4169 def filename = "jalviewjsTest.html"
4170 description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
4171 def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
4176 def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
4177 factory = f.newInstance()
4178 } catch (ClassNotFoundException e) {
4179 throw new GradleException("Unable to create SimpleHttpFileServerFactory")
4181 def port = Integer.valueOf(jalviewjs_server_port)
4186 while(port < start+1000 && !running) {
4188 def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
4189 jalviewjsServer = factory.start(doc_root, port)
4191 url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
4192 println("SERVER STARTED with document root ${doc_root}.")
4193 println("Go to "+url+" . Run gradle --stop to stop (kills all gradle daemons).")
4194 println("For debug: "+url+"?j2sdebug")
4195 println("For verbose: "+url+"?j2sverbose")
4196 } catch (Exception e) {
4201 <p><a href="${url}">JalviewJS Test. <${url}></a></p>
4202 <p><a href="${url}?j2sdebug">JalviewJS Test with debug. <${url}?j2sdebug></a></p>
4203 <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. <${url}?j2sdebug></a></p>
4205 jalviewjsCoreClasslists.each { cl ->
4206 def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
4208 <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. <${urlcore}></a></p>
4210 println("For core ${cl.name}: "+urlcore)
4213 file(htmlFile).text = htmlText
4216 outputs.file(htmlFile)
4217 outputs.upToDateWhen({false})
4221 task cleanJalviewjsAll {
4223 description "Delete all configuration and build artifacts to do with JalviewJS build"
4224 dependsOn cleanJalviewjsSite
4225 dependsOn jalviewjsEclipsePaths
4228 delete "${jalviewDir}/${jalviewjsBuildDir}"
4229 delete "${jalviewDir}/${eclipse_bin_dir}"
4230 if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
4231 delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
4233 delete jalviewjsJ2sAltSettingsFileName
4236 outputs.upToDateWhen( { false } )
4240 task jalviewjsIDE_checkJ2sPlugin {
4241 group "00 JalviewJS in Eclipse"
4242 description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
4245 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4246 def j2sPluginFile = file(j2sPlugin)
4247 def eclipseHome = System.properties["eclipse.home.location"]
4248 if (eclipseHome == null || ! IN_ECLIPSE) {
4249 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
4251 def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
4252 def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
4253 if (altPluginsDir != null && file(altPluginsDir).exists()) {
4254 eclipseJ2sPluginDirs += altPluginsDir
4256 def foundPlugin = false
4257 def j2sPluginFileName = j2sPluginFile.getName()
4258 def eclipseJ2sPlugin
4259 def eclipseJ2sPluginFile
4260 eclipseJ2sPluginDirs.any { dir ->
4261 eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
4262 eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4263 if (eclipseJ2sPluginFile.exists()) {
4269 def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
4270 System.err.println(msg)
4271 throw new StopExecutionException(msg)
4274 def digest = MessageDigest.getInstance("MD5")
4276 digest.update(j2sPluginFile.text.bytes)
4277 def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4279 digest.update(eclipseJ2sPluginFile.text.bytes)
4280 def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
4282 if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
4283 def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
4284 System.err.println(msg)
4285 throw new StopExecutionException(msg)
4287 def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
4293 task jalviewjsIDE_copyJ2sPlugin {
4294 group "00 JalviewJS in Eclipse"
4295 description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
4298 def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
4299 def j2sPluginFile = file(j2sPlugin)
4300 def eclipseHome = System.properties["eclipse.home.location"]
4301 if (eclipseHome == null || ! IN_ECLIPSE) {
4302 throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
4304 def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
4305 def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
4306 def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
4307 System.err.println(msg)
4310 eclipseJ2sPluginFile.getParentFile().mkdirs()
4311 into eclipseJ2sPluginFile.getParent()
4317 task jalviewjsIDE_j2sFile {
4318 group "00 JalviewJS in Eclipse"
4319 description "Creates the .j2s file"
4320 dependsOn jalviewjsCreateJ2sSettings
4324 task jalviewjsIDE_SyncCore {
4325 group "00 JalviewJS in Eclipse"
4326 description "Build the core js lib closures listed in the classlists dir and publish core html from template"
4327 dependsOn jalviewjsSyncCore
4331 task jalviewjsIDE_SyncSiteAll {
4332 dependsOn jalviewjsSyncAllLibs
4333 dependsOn jalviewjsSyncResources
4334 dependsOn jalviewjsSyncSiteResources
4335 dependsOn jalviewjsSyncBuildProperties
4339 cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
4342 task jalviewjsIDE_PrepareSite {
4343 group "00 JalviewJS in Eclipse"
4344 description "Sync libs and resources to site dir, but not closure cores"
4346 dependsOn jalviewjsIDE_SyncSiteAll
4347 //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
4351 task jalviewjsIDE_AssembleSite {
4352 group "00 JalviewJS in Eclipse"
4353 description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
4354 dependsOn jalviewjsPrepareSite
4358 task jalviewjsIDE_SiteClean {
4359 group "00 JalviewJS in Eclipse"
4360 description "Deletes the Eclipse transpiled site"
4361 dependsOn cleanJalviewjsSite
4365 task jalviewjsIDE_Server {
4366 group "00 JalviewJS in Eclipse"
4367 description "Starts a webserver on localhost to test the website"
4368 dependsOn jalviewjsServer
4372 // buildship runs this at import or gradle refresh
4373 task eclipseSynchronizationTask {
4374 //dependsOn eclipseSetup
4375 dependsOn createBuildProperties
4377 dependsOn jalviewjsIDE_j2sFile
4378 dependsOn jalviewjsIDE_checkJ2sPlugin
4379 dependsOn jalviewjsIDE_PrepareSite
4384 // buildship runs this at build time or project refresh
4385 task eclipseAutoBuildTask {
4386 //dependsOn jalviewjsIDE_checkJ2sPlugin
4387 //dependsOn jalviewjsIDE_PrepareSite
4391 task jalviewjsCopyStderrLaunchFile(type: Copy) {
4392 from file(jalviewjs_stderr_launch)
4393 into jalviewjsSiteDir
4395 inputs.file jalviewjs_stderr_launch
4396 outputs.file jalviewjsStderrLaunchFilename
4399 task cleanJalviewjsChromiumUserDir {
4401 delete jalviewjsChromiumUserDir
4403 outputs.dir jalviewjsChromiumUserDir
4404 // always run when depended on
4405 outputs.upToDateWhen { !file(jalviewjsChromiumUserDir).exists() }
4408 task jalviewjsChromiumProfile {
4409 dependsOn cleanJalviewjsChromiumUserDir
4410 mustRunAfter cleanJalviewjsChromiumUserDir
4412 def firstRun = file("${jalviewjsChromiumUserDir}/First Run")
4415 mkdir jalviewjsChromiumProfileDir
4418 outputs.file firstRun
4421 task jalviewjsLaunchTest {
4423 description "Check JalviewJS opens in a browser"
4424 dependsOn jalviewjsBuildSite
4425 dependsOn jalviewjsCopyStderrLaunchFile
4426 dependsOn jalviewjsChromiumProfile
4428 def macOS = OperatingSystem.current().isMacOsX()
4429 def chromiumBinary = macOS ? jalviewjs_macos_chromium_binary : jalviewjs_chromium_binary
4430 if (chromiumBinary.startsWith("~/")) {
4431 chromiumBinary = System.getProperty("user.home") + chromiumBinary.substring(1)
4437 def timeoutms = Integer.valueOf(jalviewjs_chromium_overall_timeout) * 1000
4439 def binary = file(chromiumBinary)
4440 if (!binary.exists()) {
4441 throw new StopExecutionException("Could not find chromium binary '${chromiumBinary}'. Cannot run task ${name}.")
4443 stdout = new ByteArrayOutputStream()
4444 stderr = new ByteArrayOutputStream()
4447 if (jalviewjs_j2s_to_console.equals("true")) {
4448 execStdout = new org.apache.tools.ant.util.TeeOutputStream(
4451 execStderr = new org.apache.tools.ant.util.TeeOutputStream(
4459 "--no-sandbox", // --no-sandbox IS USED BY THE THORIUM APPIMAGE ON THE BUILDSERVER
4462 "--timeout=${timeoutms}",
4463 "--virtual-time-budget=${timeoutms}",
4464 "--user-data-dir=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_chromium_user_dir}",
4465 "--profile-directory=${jalviewjs_chromium_profile_name}",
4466 "--allow-file-access-from-files",
4467 "--enable-logging=stderr",
4468 "file://${jalviewDirAbsolutePath}/${jalviewjsStderrLaunchFilename}"
4471 if (true || macOS) {
4472 ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
4473 Future f1 = executor.submit(
4476 standardOutput = execStdout
4477 errorOutput = execStderr
4478 executable(chromiumBinary)
4480 println "COMMAND: '"+commandLine.join(" ")+"'"
4482 executor.shutdownNow()
4486 def noChangeBytes = 0
4487 def noChangeIterations = 0
4488 executor.scheduleAtFixedRate(
4490 String stderrString = stderr.toString()
4491 // shutdown the task if we have a success string
4492 if (stderrString.contains(jalviewjs_desktop_init_string)) {
4495 executor.shutdownNow()
4497 // if no change in stderr for 10s then also end
4498 if (noChangeIterations >= jalviewjs_chromium_idle_timeout) {
4499 executor.shutdownNow()
4501 if (stderrString.length() == noChangeBytes) {
4502 noChangeIterations++
4504 noChangeBytes = stderrString.length()
4505 noChangeIterations = 0
4508 1, 1, TimeUnit.SECONDS)
4510 executor.schedule(new Runnable(){
4513 executor.shutdownNow()
4515 }, timeoutms, TimeUnit.MILLISECONDS)
4517 executor.awaitTermination(timeoutms+10000, TimeUnit.MILLISECONDS)
4518 executor.shutdownNow()
4525 stderr.toString().eachLine { line ->
4526 if (line.contains(jalviewjs_desktop_init_string)) {
4527 println("Found line '"+line+"'")
4533 throw new GradleException("Could not find evidence of Desktop launch in JalviewJS.")
4541 description "Build the JalviewJS site and run the launch test"
4542 dependsOn jalviewjsBuildSite
4543 dependsOn jalviewjsLaunchTest