This is an automated email from the ASF dual-hosted git repository.

benw pushed a commit to branch gradle-improvements
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit a43d289522aab2b4aacc746773f4de67339631d4
Author: Ben Weidig <b...@netzgut.net>
AuthorDate: Sun Jun 15 13:06:31 2025 +0200

    TAP5-2809: split build.gradle further, modernize
---
 beanmodel/build.gradle                             |   3 +-
 buildSrc/build.gradle                              |   2 +
 .../main/groovy/t5build/GenerateChecksums.groovy   |  61 ++++++++++
 buildSrc/src/main/groovy/t5build/SSshExec.groovy   |  21 ++++
 buildSrc/src/main/groovy/t5build/Scp.groovy        |  36 ++++++
 buildSrc/src/main/groovy/t5build/SshTask.groovy    |  94 ++++++++++++++++
 .../main/groovy/t5build/TapestryBuildLogic.groovy  |  22 ++++
 .../src/main/groovy/tapestry.ssh-convention.gradle |  15 +++
 gradle/libs.versions.toml                          |   2 +
 md5.gradle                                         |  25 -----
 sha256.gradle                                      |  27 -----
 ssh.gradle                                         | 124 ---------------------
 12 files changed, 255 insertions(+), 177 deletions(-)

diff --git a/beanmodel/build.gradle b/beanmodel/build.gradle
index e8d491dc2..09b48b253 100644
--- a/beanmodel/build.gradle
+++ b/beanmodel/build.gradle
@@ -28,7 +28,8 @@ clean.delete generateGrammarSource.outputDirectory
 
 compileJava {
     dependsOn generateGrammarSource
-    options.fork(memoryMaximumSize: '512m')
+    options.fork = true
+    options.forkOptions.memoryMaximumSize = '512M'
 }
 
 tasks.named('sourcesJar') {
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 94cb7679d..34a0503f1 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -14,5 +14,7 @@ dependencies {
         exclude module: 'gmaven-runtime-1.7'
         exclude module: 'less4j'
     }
+    implementation 'org.apache.ant:ant-jsch:1.8.2'
+
     gradleApi()
 }
diff --git a/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy 
b/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy
new file mode 100644
index 000000000..dd9f9997f
--- /dev/null
+++ b/buildSrc/src/main/groovy/t5build/GenerateChecksums.groovy
@@ -0,0 +1,61 @@
+package t5build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.SourceTask
+import org.gradle.api.file.ConfigurableFileCollection
+
+import java.security.MessageDigest
+
+class GenerateChecksums extends SourceTask {
+
+    enum Algorithm {
+        MD5('MD5', 32, 'md5'),
+        SHA256('SHA-256', 64, 'sha256')
+
+        final String name
+        final int padding
+        final String extension
+
+        Algorithm(String name, int padding, String extension) {
+            this.name = name
+            this.padding = padding
+            this.extension = extension
+        }
+    }
+
+    @OutputDirectory
+    File outputDir
+
+
+    @TaskAction
+    void generate() {
+        source.each { file ->
+            // Create a map of MessageDigest instances, one for each algorithm,
+            // so it's easier to update them and use later
+            def digests = Algorithm.values().collectEntries { alg ->
+                [(alg): MessageDigest.getInstance(alg.name)]
+            }
+
+            // use inputstream so to avoid loading whole file into memory
+            file.withInputStream { is ->
+                byte[] buffer = new byte[8192]
+                int bytesRead
+                while ((bytesRead = is.read(buffer)) != -1) {
+                    digests.values().each { digest ->
+                        digest.update(buffer, 0, bytesRead)
+                    }
+                }
+            }
+
+            // Write checksum files
+            digests.each { algo, digest ->
+                def checksum = new BigInteger(1, 
digest.digest()).toString(16).padLeft(algo.padding, '0')
+                new File(outputDir, "${file.name}.${algo.extension}").text = 
checksum
+            }
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/t5build/SSshExec.groovy 
b/buildSrc/src/main/groovy/t5build/SSshExec.groovy
new file mode 100644
index 000000000..e985b4591
--- /dev/null
+++ b/buildSrc/src/main/groovy/t5build/SSshExec.groovy
@@ -0,0 +1,21 @@
+package t5build
+
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.TaskAction
+
+class SshExec extends SshTask {
+
+    @Input
+    List<String[]> commandLines = []
+
+    void commandLine(String... commandLine) {
+        commandLines << commandLine
+    }
+
+    @TaskAction
+    void doActions() {
+        commandLines.each { commandLine ->
+            ssh(*commandLine)
+        }
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/t5build/Scp.groovy 
b/buildSrc/src/main/groovy/t5build/Scp.groovy
new file mode 100644
index 000000000..3280cfb9d
--- /dev/null
+++ b/buildSrc/src/main/groovy/t5build/Scp.groovy
@@ -0,0 +1,36 @@
+package t5build
+
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.SkipWhenEmpty
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+
+class Scp extends SshTask {
+
+    @InputFiles @SkipWhenEmpty
+    def source
+
+    @Input
+    String destination
+
+    @Input
+    boolean isDir = false
+
+    @TaskAction
+    void doActions() {
+        if (isDir) {
+            scpDir(source, destination)
+            return
+        }
+        project.files(source).each { doFile(it) }
+    }
+
+    private void doFile(File file) {
+        if (file.isDirectory()) {
+            file.eachFile { doFile(it) }
+        } else {
+            scpFile(file, destination)
+        }
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/t5build/SshTask.groovy 
b/buildSrc/src/main/groovy/t5build/SshTask.groovy
new file mode 100644
index 000000000..769b32adf
--- /dev/null
+++ b/buildSrc/src/main/groovy/t5build/SshTask.groovy
@@ -0,0 +1,94 @@
+package t5build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.FileCollection
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+
+abstract class SshTask extends DefaultTask {
+
+    @InputFiles
+    FileCollection sshAntClasspath
+
+    @Input
+    String host
+
+    @Input
+    String userName
+
+    // TODO: Passwords should not be plain @Input.
+    @Input
+    String password
+
+    @Input
+    boolean verbose = false
+
+    private boolean antInited = false
+
+    protected void initAnt() {
+        if (antInited) {
+            return
+        }
+        ant.taskdef(name: 'scp',
+                    classname: 
'org.apache.tools.ant.taskdefs.optional.ssh.Scp',
+                    classpath: sshAntClasspath.asPath,
+                    loaderref: 'ssh')
+
+        ant.taskdef(name: 'sshexec',
+                    classname: 
'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec',
+                    classpath: sshAntClasspath.asPath,
+                    loaderref: 'ssh')
+        antInited = true
+    }
+
+    protected void withInfoLogging(Closure action) {
+        def oldLogLevel = getLogging().getLevel()
+        getLogging().setLevel([LogLevel.INFO, oldLogLevel].min())
+        try {
+            action()
+        } finally {
+            if (oldLogLevel != null) {
+                getLogging().setLevel(oldLogLevel)
+            }
+        }
+    }
+
+    protected void scpFile(Object source, String destination) {
+        initAnt()
+        withInfoLogging {
+            // TODO: This keyfile is hardcoded and uses an old algorithm (dsa)
+            ant.scp(localFile: project.files(source).singleFile,
+                    remoteToFile: "${userName}@${host}:${destination}",
+                    keyfile: "${System.properties['user.home']}/.ssh/id_dsa",
+                    verbose: verbose)
+        }
+    }
+
+    protected void scpDir(Object source, String destination) {
+        initAnt()
+        withInfoLogging {
+            ant.sshexec(host: host,
+                        username: userName,
+                        password: password,
+                        command: "mkdir -p ${destination}")
+
+            ant.scp(remoteTodir: "${userName}@${host}:${destination}",
+                    keyfile: "${System.properties['user.home']}/.ssh/id_dsa",
+                    verbose: verbose) {
+                project.files(source).addToAntBuilder(ant, 'fileSet', 
FileCollection.AntType.FileSet)
+            }
+        }
+    }
+
+    protected void ssh(Object... commandLine) {
+        initAnt()
+        withInfoLogging {
+            ant.sshexec(host: host,
+                        username: userName,
+                        password: password,
+                        command: commandLine.join(' '))
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/t5build/TapestryBuildLogic.groovy 
b/buildSrc/src/main/groovy/t5build/TapestryBuildLogic.groovy
new file mode 100644
index 000000000..13f9b935a
--- /dev/null
+++ b/buildSrc/src/main/groovy/t5build/TapestryBuildLogic.groovy
@@ -0,0 +1,22 @@
+package t5build
+
+import org.gradle.api.Project
+
+class TapestryBuildLogic {
+
+    static boolean isSnapshot(Project project) {
+        return tapestryVersion(project).endsWith('SNAPSHOT')
+    }
+
+    static boolean isWindows() {
+        return System.properties['os.name'].toLowerCase().contains('windows')
+    }
+
+    static String tapestryVersion(Project project) {
+        String major = project.rootProject.ext.tapestryMajorVersion
+        String minor = project.rootProject.ext.tapestryMinorVersion
+      
+        boolean isCiBuild = 
project.rootProject.hasProperty('continuousIntegrationBuild') && 
project.rootProject.continuousIntegrationBuild
+        return isCiBuild ? major + '-SNAPSHOT' : major + minor
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/tapestry.ssh-convention.gradle 
b/buildSrc/src/main/groovy/tapestry.ssh-convention.gradle
new file mode 100644
index 000000000..0a66e2636
--- /dev/null
+++ b/buildSrc/src/main/groovy/tapestry.ssh-convention.gradle
@@ -0,0 +1,15 @@
+import t5build.SshTask
+
+// This configuration will hold the ant-jsch.jar at runtime
+configurations {
+    sshAntTask
+}
+
+dependencies {
+    sshAntTask libs.ant.jsch
+}
+
+// Configure all SshTask instances to use the files from the configuration
+tasks.withType(SshTask).configureEach {
+    it.sshAntClasspath = configurations.sshAntTask
+}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 0990af421..2153aade1 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -39,6 +39,7 @@ json = "20140107"
 xmlApis = "1.4.01"
 guice = "3.0"
 groovy = "3.0.13"
+ant-jsch = "1.8.2"
 
 # TESTING
 slf4j-simple = "2.0.17"
@@ -96,6 +97,7 @@ hamcrest-core = { module = "org.hamcrest:hamcrest-core", 
version.ref = "hamcrest
 json = { module = "org.json:json", version.ref = "json" }
 xml-apis = { module = "xml-apis:xml-apis", version.ref = "xmlApis" }
 guice = { module = "com.google.inject:guice", version.ref = "guice" }
+ant-jsch = { module = "org.apache.ant:ant-jsch", version.ref = "ant-jsch" }
 
 # GROOVY
 groovy-core = { group = "org.codehaus.groovy", name = "groovy", version.ref = 
"groovy" }
diff --git a/md5.gradle b/md5.gradle
deleted file mode 100644
index ab978c522..000000000
--- a/md5.gradle
+++ /dev/null
@@ -1,25 +0,0 @@
-import java.security.MessageDigest
-
-class GenMD5 extends SourceTask {
-
-  def outputDir
-
-  @OutputDirectory
-  File getOutputDir() { project.file(outputDir) }
-
-  @TaskAction
-  void writeMD5s() {
-
-    source.each { file ->
-      MessageDigest digest = MessageDigest.getInstance("MD5")
-
-      digest.update(file.bytes)
-
-      def checksum = new BigInteger(1, 
digest.digest()).toString(16).padLeft(32, "0")
-
-      new File(outputDir, file.name + ".md5").text = checksum
-    }
-  }
-}
-
-project.ext.GenMD5 = GenMD5.class
diff --git a/sha256.gradle b/sha256.gradle
deleted file mode 100644
index 5153fc8ce..000000000
--- a/sha256.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-import java.security.MessageDigest
-
-class GenSHA256 extends SourceTask {
-
-    def outputDir
-
-    @OutputDirectory
-    File getOutputDir() {
-        project.file(outputDir)
-    }
-
-    @TaskAction
-    void writeSHA256s() {
-
-        source.each { file ->
-            MessageDigest digest = MessageDigest.getInstance("SHA-256")
-
-            digest.update(file.bytes)
-
-            def checksum = new BigInteger(1, 
digest.digest()).toString(16).padLeft(32, "0")
-
-            new File(outputDir, file.name + ".sha256").text = checksum
-        }
-    }
-}
-
-project.ext.GenSHA256 = GenSHA256.class
\ No newline at end of file
diff --git a/ssh.gradle b/ssh.gradle
deleted file mode 100644
index fb5fe76df..000000000
--- a/ssh.gradle
+++ /dev/null
@@ -1,124 +0,0 @@
-project.ext.Scp = Scp.class
-project.ext.SshExec = SshExec.class
-project.ext.SshTask = SshTask.class
-
-configurations {
-    sshAntTask
-}
-
-dependencies {
-    sshAntTask "org.apache.ant:ant-jsch:1.8.2"
-}
-
-tasks.withType(SshTask) {
-    sshAntClasspath = configurations.sshAntTask
-}
-
-class SshTask extends DefaultTask {
-    @InputFiles
-    FileCollection sshAntClasspath
-
-    @Input
-    String host
-
-    @Input
-    String userName
-
-    @Input
-    String password
-
-    boolean verbose = false
-
-    private boolean antInited = false
-
-    protected initAnt() {
-        if (!antInited) {
-            ant.taskdef(name: 'scp',
-            classname: 'org.apache.tools.ant.taskdefs.optional.ssh.Scp',
-            classpath: sshAntClasspath.asPath,
-            loaderref: 'ssh')
-            ant.taskdef(name: 'sshexec',
-            classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec',
-            classpath: sshAntClasspath.asPath,
-            loaderref: 'ssh')
-            antInited = true
-        }
-    }
-
-    protected withInfoLogging(Closure action) {
-        def oldLogLevel = logging.level
-        logging.level = [LogLevel.INFO, oldLogLevel].min()
-        try {
-            action()
-        } finally {
-            if (oldLogLevel) {
-                logging.level = oldLogLevel
-            }
-        }
-    }
-
-    def scpFile(source, destination) {
-        initAnt()
-        withInfoLogging {
-            ant.scp(localFile: project.files(source).singleFile, remoteToFile: 
"${userName}@${host}:${destination}", keyfile : 
"${System.properties['user.home']}/.ssh/id_dsa", verbose: verbose)
-        }
-    }
-
-    def scpDir(source, destination) {
-        initAnt()
-        withInfoLogging {
-            ant.sshexec(host: host, username: userName, password: password, 
command: "mkdir -p ${destination}")
-            ant.scp(remoteTodir: "${userName}@${host}:${destination}", keyfile 
: "${System.properties['user.home']}/.ssh/id_dsa", verbose: verbose) {
-                project.files(source).addToAntBuilder(ant, "fileSet", 
FileCollection.AntType.FileSet)
-            }
-        }
-    }
-
-    def ssh(Object... commandLine) {
-        initAnt()
-        withInfoLogging {
-            ant.sshexec(host: host, username: userName, password: password, 
command: commandLine.join(' '))
-        }
-    }
-}
-
-class Scp extends SshTask {
-    @InputFiles @SkipWhenEmpty source
-    @Input destination
-    boolean isDir = false
-
-    @TaskAction
-    void doActions() {
-        if (isDir) {
-            scpDir(source, destination)
-            return
-        }
-
-        project.files(source).each { doFile(it) }
-    }
-
-    void doFile(File file) {
-
-        if (file.directory) {
-            file.eachFile { doFile(it) }
-        } else {
-            scpFile(file, destination)
-        }
-    }
-}
-
-class SshExec extends SshTask {
-    @Input
-    List<String[]> commandLines = []
-
-    void commandLine(String... commandLine) {
-        commandLines << commandLine
-    }
-
-    @TaskAction
-    void doActions() {
-        commandLines.each { commandLine ->
-            ssh(* commandLine)
-        }
-    }
-}
\ No newline at end of file

Reply via email to