From 079633858a34ca4342f2715f3edd432d806711f5 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sun, 19 Oct 2025 18:02:14 +0200 Subject: [PATCH 01/17] Gradle: modernize plugin configuration --- build.gradle | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 20ee360e81f..41f004c3115 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,8 @@ import java.security.MessageDigest -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:0.9.+' - } +plugins { + id 'java' + id 'maven-publish' } configurations { @@ -14,9 +10,6 @@ configurations { compile } -apply plugin: 'java' -apply plugin: 'maven-publish' - repositories { flatDir { dirs uri("${projectDir}/lib") } maven { From adfdd26bb67593df23f2b12ecbdef1c94095b961 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sun, 19 Oct 2025 18:25:43 +0200 Subject: [PATCH 02/17] Gradle: remove unused configurations --- build.gradle | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build.gradle b/build.gradle index 41f004c3115..b668c37a90c 100644 --- a/build.gradle +++ b/build.gradle @@ -5,11 +5,6 @@ plugins { id 'maven-publish' } -configurations { - provided - compile -} - repositories { flatDir { dirs uri("${projectDir}/lib") } maven { @@ -21,11 +16,6 @@ repositories { mavenCentral() } -sourceSets { - main.compileClasspath += configurations.provided - test.compileClasspath += configurations.provided -} - def version_buildir = file("$projectDir/build/tmp/compileVersion/") def version_src = 'freenet/node/Version.java' From 4f07878a9c362c48c566d04774544c2655c02ad4 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 18:34:51 +0200 Subject: [PATCH 03/17] Gradle: move dependencies & publishing to top --- build.gradle | 73 ++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/build.gradle b/build.gradle index b668c37a90c..84bd77e32dd 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,42 @@ repositories { mavenCentral() } +dependencies { + implementation 'org.bouncycastle:bcprov-jdk15on:1.59' + implementation 'net.java.dev.jna:jna:4.5.2' + implementation 'net.java.dev.jna:jna-platform:4.5.2' + implementation 'org.freenetproject:freenet-ext:29' + implementation 'io.pebbletemplates:pebble:3.1.5' + // dependencies of pebble + implementation 'org.unbescape:unbescape:1.1.6.RELEASE' + implementation 'org.slf4j:slf4j-api:1.7.25' + + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:1.9.5' + testImplementation 'org.hamcrest:hamcrest:3.0' + testImplementation 'org.objenesis:objenesis:1.0' +} + +publishing { + publications { + mavenJava(MavenPublication) { + groupId 'org.freenetproject' + artifactId 'fred' + version gitrev + from components.java + } + } + repositories { + maven { + url 's3://mvn.freenetproject.org/' + credentials(AwsCredentials) { + accessKey System.getenv('AWS_ACCESS_KEY_ID') + secretKey System.getenv('AWS_SECRET_ACCESS_KEY') + } + } + } +} + def version_buildir = file("$projectDir/build/tmp/compileVersion/") def version_src = 'freenet/node/Version.java' @@ -208,26 +244,6 @@ tasks.withType(Test) { enableAssertions = false } -publishing { - publications { - mavenJava(MavenPublication) { - groupId 'org.freenetproject' - artifactId "fred" - version gitrev - from components.java - } - } - repositories { - maven { - url "s3://mvn.freenetproject.org/" - credentials(AwsCredentials) { - accessKey System.getenv('AWS_ACCESS_KEY_ID') - secretKey System.getenv('AWS_SECRET_ACCESS_KEY') - } - } - } -} - task copyRuntimeLibs(type: Copy) { into "${buildDir}/output/" from configurations.runtimeClasspath @@ -235,23 +251,6 @@ task copyRuntimeLibs(type: Copy) { } copyRuntimeLibs.dependsOn jar -// In this section you declare the dependencies for your production and test code -dependencies { - implementation "org.bouncycastle:bcprov-jdk15on:1.59" - implementation "net.java.dev.jna:jna:4.5.2" - implementation "net.java.dev.jna:jna-platform:4.5.2" - implementation "org.freenetproject:freenet-ext:29" - implementation "io.pebbletemplates:pebble:3.1.5" - // dependencies of pebble - implementation "org.unbescape:unbescape:1.1.6.RELEASE" - implementation "org.slf4j:slf4j-api:1.7.25" - - testImplementation 'junit:junit:4.13.2' - testImplementation "org.mockito:mockito-core:1.9.5" - testImplementation "org.hamcrest:hamcrest:3.0" - testImplementation "org.objenesis:objenesis:1.0" -} - task tar(type: Tar) { description = "Build a source release, specifically excluding the build directories and gradle wrapper files" compression = Compression.BZIP2 From d5721c9ff8237393412b2ab983e17a033ac83591 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 18 Oct 2025 15:36:43 +0200 Subject: [PATCH 04/17] Gradle: replace deprecated `task` with `tasks.register` --- build.gradle | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 84bd77e32dd..6d551ff112c 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ sourceSets { } def gitrev -task buildInfo { +tasks.register('buildInfo') { try { def cmd = "git describe --always --abbrev=4 --dirty" def proc = cmd.execute([], project.rootDir) @@ -92,7 +92,7 @@ tasks.withType(Javadoc) { options.encoding = "UTF-8" } -task generateVersionSource(type: Copy) { +tasks.register('generateVersionSource', Copy) { dependsOn buildInfo from(sourceSets.main.java.srcDirs) { include version_src @@ -103,7 +103,7 @@ task generateVersionSource(type: Copy) { into version_buildir } -task compileVersion (type: JavaCompile) { +tasks.register('compileVersion', JavaCompile) { dependsOn generateVersionSource, compileJava sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -113,7 +113,7 @@ task compileVersion (type: JavaCompile) { destinationDirectory.set(layout.buildDirectory.dir("java/version/").get()) } -task buildJar(type: Jar) { +tasks.register('buildJar', Jar) { dependsOn compileVersion, processResources from(processResources) from(compileJava.destinationDirectory) { @@ -145,7 +145,7 @@ task buildJar(type: Jar) { jar.enabled = false jar.dependsOn buildJar -task sourcesJar(type: Jar) { +tasks.register('sourcesJar', Jar) { dependsOn generateVersionSource archiveClassifier = 'sources' preserveFileTimestamps = false @@ -180,7 +180,7 @@ gradle.addBuildListener(new BuildAdapter() { } }) -task copyResourcesToClasses2 { +tasks.register('copyResourcesToClasses2') { inputs.files sourceSets.main.allSource outputs.dir layout.buildDirectory.dir("classes/java/main/") doLast { @@ -203,7 +203,7 @@ task copyResourcesToClasses2 { processResources.dependsOn copyResourcesToClasses2 compileVersion.dependsOn copyResourcesToClasses2 -task copyTestResourcesToClasses2 { +tasks.register('copyTestResourcesToClasses2') { inputs.files sourceSets.test.allSource outputs.dir layout.buildDirectory.dir("classes/java/test/") doLast { @@ -244,14 +244,14 @@ tasks.withType(Test) { enableAssertions = false } -task copyRuntimeLibs(type: Copy) { +tasks.register('copyRuntimeLibs', Copy) { into "${buildDir}/output/" from configurations.runtimeClasspath from jar } copyRuntimeLibs.dependsOn jar -task tar(type: Tar) { +tasks.register('tar', Tar) { description = "Build a source release, specifically excluding the build directories and gradle wrapper files" compression = Compression.BZIP2 From c15b6a57cc2a93977c523b0013c9925e17ff92db Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 18 Oct 2025 15:46:08 +0200 Subject: [PATCH 05/17] Gradle: assign properties using the 'propName = value' syntax Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated. --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 6d551ff112c..ab929a4d8ca 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { repositories { flatDir { dirs uri("${projectDir}/lib") } maven { - url 'https://mvn.freenetproject.org' + url = 'https://mvn.freenetproject.org' metadataSources { artifact() } @@ -35,18 +35,18 @@ dependencies { publishing { publications { mavenJava(MavenPublication) { - groupId 'org.freenetproject' - artifactId 'fred' - version gitrev + groupId = 'org.freenetproject' + artifactId = 'fred' + version = gitrev from components.java } } repositories { maven { - url 's3://mvn.freenetproject.org/' + url = 's3://mvn.freenetproject.org/' credentials(AwsCredentials) { - accessKey System.getenv('AWS_ACCESS_KEY_ID') - secretKey System.getenv('AWS_SECRET_ACCESS_KEY') + accessKey = System.getenv('AWS_ACCESS_KEY_ID') + secretKey = System.getenv('AWS_SECRET_ACCESS_KEY') } } } From 8ee8f843f9f32ac2914c7a85cfd3773f65bf7228 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sun, 19 Oct 2025 17:55:02 +0200 Subject: [PATCH 06/17] Gradle: replace deprecated project.buildDir usage --- build.gradle | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index ab929a4d8ca..8ae91eff3be 100644 --- a/build.gradle +++ b/build.gradle @@ -245,36 +245,29 @@ tasks.withType(Test) { } tasks.register('copyRuntimeLibs', Copy) { - into "${buildDir}/output/" + into layout.buildDirectory.dir('output') from configurations.runtimeClasspath from jar } copyRuntimeLibs.dependsOn jar tasks.register('tar', Tar) { - description = "Build a source release, specifically excluding the build directories and gradle wrapper files" - compression = Compression.BZIP2 + description = "Build a source release, specifically excluding the build directories and gradle wrapper files" - archiveBaseName = "freenet-sources" - - from(project.rootDir) { - exclude '**/build' - exclude 'build' - exclude '.gradle' - } - - into(archiveBaseName) - - preserveFileTimestamps = true - reproducibleFileOrder = true + from(project.rootDir) { + exclude '**/build' + exclude 'build' + exclude '.gradle' + } + into 'freenet-sources' - // Set destination directory. - destinationDirectory = file("${project.buildDir}") + compression = Compression.BZIP2 + destinationDirectory = layout.buildDirectory + archiveFileName = 'freenet-sources.tgz' - archiveFileName = "${archiveBaseName}.tgz" - doLast { // generate md5 checksum - ant.checksum file:"$destinationDirectory/$archiveFileName" - } + doLast { // generate md5 checksum + ant.checksum file: file(archiveFile) + } } javadoc { From d46605c600f1973dd5b1a53a724d413c85420012 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 18 Oct 2025 17:31:33 +0200 Subject: [PATCH 07/17] Gradle: refactor use of deprecated buildFinished(...) The event was used to output archive digests after build completion. Replace this with a task `printArchiveDigests` and schedule it whenever an archive task is used, preferring execution after all other tasks. --- build.gradle | 56 ++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/build.gradle b/build.gradle index 8ae91eff3be..dd2b833890d 100644 --- a/build.gradle +++ b/build.gradle @@ -156,30 +156,6 @@ tasks.register('sourcesJar', Jar) { from(compileVersion.source) } -def jars = [] -gradle.addListener(new TaskExecutionListener() { -void afterExecute(Task task, TaskState state) { - if(task in AbstractArchiveTask && task.enabled) { - jars << task.outputs.files.singleFile - } -} - -void beforeExecute(Task task) { } -}) -gradle.addBuildListener(new BuildAdapter() { - void buildFinished(BuildResult result) { - if(jars) { - def hash = { - File file -> def sha256 = MessageDigest.getInstance('SHA-256') - file.eachByte(1024 * 4) { buffer, len -> sha256.update(buffer, 0, len) } - println "SHA-256 of ${file.name}: ${sha256.digest().encodeHex().toString()}" - } - - jars.each { hash(it) } - } - } -}) - tasks.register('copyResourcesToClasses2') { inputs.files sourceSets.main.allSource outputs.dir layout.buildDirectory.dir("classes/java/main/") @@ -208,8 +184,8 @@ tasks.register('copyTestResourcesToClasses2') { outputs.dir layout.buildDirectory.dir("classes/java/test/") doLast { copy { - from sourceSets.test.allSource - into layout.buildDirectory.dir("classes/java/test/") + from sourceSets.test.allSource + into layout.buildDirectory.dir("classes/java/test/") include 'freenet/client/filter/*/**' include 'freenet/crypt/ciphers/rijndael-gladman-test-data/**' include 'freenet/l10n/*properties' @@ -275,3 +251,31 @@ javadoc { failOnError false } } + +tasks.register('archiveDigests') { + description = 'Display SHA-256 digests of all archives that were created.' + group = 'Help' + + // Do not declare dependencies, we'll print whatever is available + def artifacts = configurations.archives.artifacts + + // Run this task at the end of the build + shouldRunAfter tasks + + // Print SHA256 digests of all available archive artifacts + doLast { + def sha256 = MessageDigest.getInstance('SHA-256') + artifacts.files.files.findAll { + it.canRead() + }.each { file -> + file.eachByte(1024 * 4) { buffer, len -> sha256.update(buffer, 0, len) } + logger.lifecycle('SHA-256 of {}: {}', file.name, sha256.digest().encodeHex()) + } + } +} + +tasks.withType(AbstractArchiveTask).configureEach { + // Register the archive for displaying digests at the end of the build + artifacts.archives archiveFile + finalizedBy archiveDigests +} From 7bfdf9bf9bd6bbea341e50ced2c9a5fa6667afc7 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 18:58:13 +0200 Subject: [PATCH 08/17] Gradle: regenerate Version.java when the version changes The Version.java source file should always be regenerated whenever the version changes. Always compute the version and define it as an input so that the file is considered stale when the version changes. --- build.gradle | 71 ++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/build.gradle b/build.gradle index dd2b833890d..0b65edba25c 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,11 @@ plugins { id 'maven-publish' } +// Use version from Git if no version is provided +if (version == DEFAULT_VERSION) { + version = versionFromGit +} + repositories { flatDir { dirs uri("${projectDir}/lib") } maven { @@ -37,7 +42,6 @@ publishing { mavenJava(MavenPublication) { groupId = 'org.freenetproject' artifactId = 'fred' - version = gitrev from components.java } } @@ -52,8 +56,7 @@ publishing { } } -def version_buildir = file("$projectDir/build/tmp/compileVersion/") -def version_src = 'freenet/node/Version.java' +def versionJavaFile = 'freenet/node/Version.java' sourceSets { main { @@ -68,17 +71,6 @@ sourceSets { } } -def gitrev -tasks.register('buildInfo') { - try { - def cmd = "git describe --always --abbrev=4 --dirty" - def proc = cmd.execute([], project.rootDir) - gitrev = proc.text.trim() - } catch (IOException ignored) { - gitrev = "@unknown@" - } -} - java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -92,35 +84,30 @@ tasks.withType(Javadoc) { options.encoding = "UTF-8" } -tasks.register('generateVersionSource', Copy) { - dependsOn buildInfo +tasks.register('versionSource', Copy) { + inputs.property('version', version) from(sourceSets.main.java.srcDirs) { - include version_src - filter { - String line -> line.replaceAll('@custom@', gitrev) - } + include versionJavaFile } - into version_buildir + filteringCharset = 'UTF-8' + filter { + line -> line.replaceAll('@custom@', inputs.properties.version as String) + } + into temporaryDir } -tasks.register('compileVersion', JavaCompile) { - dependsOn generateVersionSource, compileJava - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - source = version_buildir - include version_src - classpath = files(sourceSets.main.compileClasspath, sourceSets.main.output.classesDirs) - destinationDirectory.set(layout.buildDirectory.dir("java/version/").get()) +compileJava { + // Include generated Version.java in compilation + source versionSource } tasks.register('buildJar', Jar) { - dependsOn compileVersion, processResources + dependsOn processResources from(processResources) from(compileJava.destinationDirectory) { exclude 'freenet/node/Version.class' exclude 'freenet/node/Version$1.class' } - from(compileVersion.destinationDirectory) preserveFileTimestamps = false reproducibleFileOrder = true duplicatesStrategy = DuplicatesStrategy.FAIL @@ -136,7 +123,7 @@ tasks.register('buildJar', Jar) { "Specification-Version": "0.7.5", "Specification-Vendor": "freenetproject.org", "Implementation-Title": "Freenet", - "Implementation-Version": "0.7.5 ${gitrev}", + "Implementation-Version": "0.7.5 ${version}", "Implementation-Vendor": "freenetproject.org", ], "common") } @@ -146,14 +133,13 @@ jar.enabled = false jar.dependsOn buildJar tasks.register('sourcesJar', Jar) { - dependsOn generateVersionSource archiveClassifier = 'sources' preserveFileTimestamps = false reproducibleFileOrder = true from(sourceSets.main.allSource) { exclude 'freenet/node/Version.java' } - from(compileVersion.source) + from(versionSource) } tasks.register('copyResourcesToClasses2') { @@ -177,7 +163,6 @@ tasks.register('copyResourcesToClasses2') { } } processResources.dependsOn copyResourcesToClasses2 -compileVersion.dependsOn copyResourcesToClasses2 tasks.register('copyTestResourcesToClasses2') { inputs.files sourceSets.test.allSource @@ -279,3 +264,19 @@ tasks.withType(AbstractArchiveTask).configureEach { artifacts.archives archiveFile finalizedBy archiveDigests } + +/** + * Try to obtain the version description from `git describe`. + */ +String getVersionFromGit() { + try { + def process = 'git describe --always --abbrev=4 --dirty'.execute([], project.rootDir) + if (process.waitFor() != 0) { + throw new IOException("exit code ${process.exitValue()}") + } + return process.text.trim() + } catch (IOException e) { + logger.warn('Cannot determine version from git: {}', e.message) + return '@unknown@' + } +} From a8df5ac32265246be49a296dc12db6bae96d5515 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 18:59:55 +0200 Subject: [PATCH 09/17] Gradle: refactor sourceSets to leverage built-in functionality If the sourceSets express our intention for compiling the source, Gradle can do pretty much all the heavy lifting without custom tasks. Refactor the sourceSets so that the custom tasks providing standard functionality can be removed: * `buildJar` is replaced by the java-plugin provided `jar` task * `compileVersion` is no longer a separate step, `compileJava` comsumes the sources (without the version file) and the generated version file * `sourcesJar` is replaced by the java-plugin provided `sourcesJar` task (enabled by specifying `withSourcesJar()`) * `copyResourcesToClasses2` and `copyTestResourcesToClasses2` are no longer needed as the resources are now declared in the sourceSets and automatically copied by Gradle * `copyRuntimeLibs` dependency on `jar` is implicit (from `from jar`) and does not need to be specified. * Reproducible build settings are now applied across all archival tasks --- build.gradle | 156 ++++++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 84 deletions(-) diff --git a/build.gradle b/build.gradle index 0b65edba25c..30f5615fcca 100644 --- a/build.gradle +++ b/build.gradle @@ -61,12 +61,29 @@ def versionJavaFile = 'freenet/node/Version.java' sourceSets { main { java { - srcDir 'src/' + srcDirs 'src' + include '**/*.java' + exclude versionJavaFile + } + resources { + srcDirs 'src' + include 'freenet/l10n/*properties' + include 'freenet/l10n/iso-*.tab' + include 'freenet/clients/http/staticfiles/**' + include 'freenet/clients/http/templates/**' } } test { java { - srcDir 'test/' + srcDirs 'test' + include '**/*.java' + } + resources { + srcDirs 'test' + include 'freenet/client/filter/*/**' + include 'freenet/crypt/ciphers/rijndael-gladman-test-data/**' + include 'freenet/l10n/*properties' + include 'freenet/clients/http/templates/**' } } } @@ -74,10 +91,7 @@ sourceSets { java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 -} - -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" + withSourcesJar() } tasks.withType(Javadoc) { @@ -96,90 +110,25 @@ tasks.register('versionSource', Copy) { into temporaryDir } -compileJava { - // Include generated Version.java in compilation - source versionSource -} - -tasks.register('buildJar', Jar) { - dependsOn processResources - from(processResources) - from(compileJava.destinationDirectory) { - exclude 'freenet/node/Version.class' - exclude 'freenet/node/Version$1.class' - } - preserveFileTimestamps = false - reproducibleFileOrder = true - duplicatesStrategy = DuplicatesStrategy.FAIL - archivesBaseName = "freenet" - manifest { - attributes("Permissions": "all-permissions") - attributes("Application-Name": "Freenet REference Daemon") - attributes("Required-Ext-Version": 29) - attributes("Recommended-Ext-Version": 29) - attributes("Compiled-With": "${System.getProperty('java.version')} (${System.getProperty('java.vendor')})") - attributes([ - "Specification-Title": "Freenet", - "Specification-Version": "0.7.5", - "Specification-Vendor": "freenetproject.org", - "Implementation-Title": "Freenet", - "Implementation-Version": "0.7.5 ${version}", - "Implementation-Vendor": "freenetproject.org", - ], "common") - } +extendSourceDirectorySet(sourceSets.main.java, 'version') { + // Add the generated version file + srcDirs versionSource + include versionJavaFile } -jar.enabled = false -jar.dependsOn buildJar - -tasks.register('sourcesJar', Jar) { - archiveClassifier = 'sources' - preserveFileTimestamps = false - reproducibleFileOrder = true - from(sourceSets.main.allSource) { - exclude 'freenet/node/Version.java' - } - from(versionSource) +extendSourceDirectorySet(sourceSets.main.resources, 'dependencies') { + // Add `dependencies.properties` from the project root + srcDirs projectDir + include 'dependencies.properties' } -tasks.register('copyResourcesToClasses2') { - inputs.files sourceSets.main.allSource - outputs.dir layout.buildDirectory.dir("classes/java/main/") - doLast { - copy { - from sourceSets.main.allSource - into layout.buildDirectory.dir("classes/java/main/") - include 'freenet/l10n/*properties' - include 'freenet/l10n/iso-*.tab' - include 'freenet/clients/http/staticfiles/**' - include 'freenet/clients/http/templates/**' - include '../dependencies.properties' - } - copy { - from "${projectDir}/" - into layout.buildDirectory.dir("classes/java/main/") - include 'dependencies.properties' - } - } +compileJava { + options.encoding = 'UTF-8' } -processResources.dependsOn copyResourcesToClasses2 -tasks.register('copyTestResourcesToClasses2') { - inputs.files sourceSets.test.allSource - outputs.dir layout.buildDirectory.dir("classes/java/test/") - doLast { - copy { - from sourceSets.test.allSource - into layout.buildDirectory.dir("classes/java/test/") - include 'freenet/client/filter/*/**' - include 'freenet/crypt/ciphers/rijndael-gladman-test-data/**' - include 'freenet/l10n/*properties' - include 'freenet/clients/http/templates/**' - } - } +compileTestJava { + options.encoding = 'UTF-8' } -compileTestJava.dependsOn copyResourcesToClasses2 -compileTestJava.dependsOn copyTestResourcesToClasses2 test { if(JavaVersion.current() >= JavaVersion.VERSION_1_9) { @@ -205,12 +154,34 @@ tasks.withType(Test) { enableAssertions = false } +jar { + archiveFileName = 'freenet.jar' + manifest { + attributes 'Permissions': 'all-permissions' + attributes 'Application-Name': 'Freenet REference Daemon' + attributes 'Required-Ext-Version': 29 + attributes 'Recommended-Ext-Version': 29 + attributes 'Compiled-With': "${System.getProperty('java.version')} (${System.getProperty('java.vendor')})" + attributes([ + 'Specification-Title' : 'Freenet', + 'Specification-Version' : '0.7.5', + 'Specification-Vendor' : 'freenetproject.org', + 'Implementation-Title' : 'Freenet', + 'Implementation-Version': "0.7.5 ${version}", + 'Implementation-Vendor' : 'freenetproject.org', + ], 'common') + } +} + +sourcesJar { + archiveFileName = 'freenet-sources.jar' +} + tasks.register('copyRuntimeLibs', Copy) { into layout.buildDirectory.dir('output') from configurations.runtimeClasspath from jar } -copyRuntimeLibs.dependsOn jar tasks.register('tar', Tar) { description = "Build a source release, specifically excluding the build directories and gradle wrapper files" @@ -260,6 +231,13 @@ tasks.register('archiveDigests') { } tasks.withType(AbstractArchiveTask).configureEach { + // Ensure reproducible archives + preserveFileTimestamps = false + reproducibleFileOrder = true + + // Fail the build if any file would be overwritten due to duplicates + duplicatesStrategy = DuplicatesStrategy.FAIL + // Register the archive for displaying digests at the end of the build artifacts.archives archiveFile finalizedBy archiveDigests @@ -280,3 +258,13 @@ String getVersionFromGit() { return '@unknown@' } } + +/** + * Add additional sources to a existing source directory. The additional sources can specify their own inclusion/ + * exclusion patterns independently from the existing sources. + */ +void extendSourceDirectorySet(SourceDirectorySet parent, String name, @DelegatesTo(value = SourceDirectorySet) Closure cl) { + def child = objects.sourceDirectorySet(name, parent.name) + cl.rehydrate(child, cl.owner, cl.thisObject).call() + parent.source child +} From 1fd9d66e1d2e66001194ceacb1977a09768e4712 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 18:02:08 +0200 Subject: [PATCH 10/17] Gradle: add descriptions for custom tasks This includes the tasks in the listing generated by :tasks. --- build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 30f5615fcca..aa45915dd4d 100644 --- a/build.gradle +++ b/build.gradle @@ -178,13 +178,17 @@ sourcesJar { } tasks.register('copyRuntimeLibs', Copy) { + description = 'Collect all runtime artifacts and dependencies.' + group = 'Build' + into layout.buildDirectory.dir('output') from configurations.runtimeClasspath from jar } tasks.register('tar', Tar) { - description = "Build a source release, specifically excluding the build directories and gradle wrapper files" + description = 'Build a source release, specifically excluding the build directories and gradle wrapper files.' + group = 'Build' from(project.rootDir) { exclude '**/build' From e59f2a53d62aed4cdf004c179d22044e19822e28 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 19:39:24 +0200 Subject: [PATCH 11/17] Gradle: collect all build artifacts in :assemble --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index aa45915dd4d..210cf1d96be 100644 --- a/build.gradle +++ b/build.gradle @@ -186,6 +186,12 @@ tasks.register('copyRuntimeLibs', Copy) { from jar } +assemble { + // Add copyRuntimeLibs so `gradlew assemble` collects all build artifacts (except tar). + // Note: jar, sourcesJar, javadocJar are added as dependency by default. + dependsOn copyRuntimeLibs +} + tasks.register('tar', Tar) { description = 'Build a source release, specifically excluding the build directories and gradle wrapper files.' group = 'Build' From 8a22b0dcf1166f32a406e5e98177774d18b729c5 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 23:00:43 +0200 Subject: [PATCH 12/17] Gradle: generate Javadoc jar --- build.gradle | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 210cf1d96be..da62f0c02a1 100644 --- a/build.gradle +++ b/build.gradle @@ -92,10 +92,7 @@ java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 withSourcesJar() -} - -tasks.withType(Javadoc) { - options.encoding = "UTF-8" + withJavadocJar() } tasks.register('versionSource', Copy) { @@ -154,6 +151,16 @@ tasks.withType(Test) { enableAssertions = false } +javadoc { + title = "Freenet REference Daemon ${version}" + options { + encoding = 'UTF-8' + source = java.sourceCompatibility + // Ignore Javadoc linting errors + addBooleanOption('Xdoclint:none', true) + } +} + jar { archiveFileName = 'freenet.jar' manifest { @@ -177,6 +184,10 @@ sourcesJar { archiveFileName = 'freenet-sources.jar' } +javadocJar { + archiveFileName = 'freenet-javadoc.jar' +} + tasks.register('copyRuntimeLibs', Copy) { description = 'Collect all runtime artifacts and dependencies.' group = 'Build' @@ -212,12 +223,6 @@ tasks.register('tar', Tar) { } } -javadoc { - doLast { - failOnError false - } -} - tasks.register('archiveDigests') { description = 'Display SHA-256 digests of all archives that were created.' group = 'Help' From 4fa8a8e0343b55e40b00e8252d9ae2ab517a9025 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sun, 26 Oct 2025 13:38:34 +0100 Subject: [PATCH 13/17] Gradle: use standard JUnit test configuration --- build.gradle | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index da62f0c02a1..b4d1cbac10d 100644 --- a/build.gradle +++ b/build.gradle @@ -128,27 +128,19 @@ compileTestJava { } test { - if(JavaVersion.current() >= JavaVersion.VERSION_1_9) { + if (JavaVersion.current() >= JavaVersion.VERSION_1_9) { jvmArgs '--add-opens=java.base/java.lang=ALL-UNNAMED' jvmArgs '--add-opens=java.base/java.util=ALL-UNNAMED' jvmArgs '--add-opens=java.base/java.io=ALL-UNNAMED' jvmArgs '--add-opens=java.base/java.util.zip=ALL-UNNAMED' } - minHeapSize = "128m" - maxHeapSize = "512m" - // no inner class + useJUnit() + minHeapSize = '128m' + maxHeapSize = '512m' + // Do not run inner classes, they are registered as part of a Suite include 'freenet/**/*Test.class' exclude 'freenet/**/*$*Test.class' scanForTestClasses = false - systemProperties += [ -// "test.extensive": -// "test.verbose": -// "test.benchmark": - ] -} - -tasks.withType(Test) { - enableAssertions = false } javadoc { From 3596527f8d947ccd802be4d45a871af1bfc0c2f4 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sun, 26 Oct 2025 01:14:21 +0200 Subject: [PATCH 14/17] Gradle: ensure incremental compilation is disabled on release builds --- build.gradle | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b4d1cbac10d..d5b928d572d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'maven-publish' } -// Use version from Git if no version is provided +// Use version from Git if no version is provided (using `-Pversion=buildXXXXX`) if (version == DEFAULT_VERSION) { version = versionFromGit } @@ -121,6 +121,11 @@ extendSourceDirectorySet(sourceSets.main.resources, 'dependencies') { compileJava { options.encoding = 'UTF-8' + if (!version.endsWith('-dirty')) { + // Make sure incremental compilation is disabled on release builds due to reproducibility issues. + logger.lifecycle('Potential release build (project version not dirty). Incremental compilation disabled.') + options.incremental = false + } } compileTestJava { From 298ecb876f9e14068c3d3f2ae7eb676121fcbba9 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sat, 25 Oct 2025 19:57:38 +0200 Subject: [PATCH 15/17] Debian: invoke Gradle only once during build :copyRuntimeLibs depends on :jar, there is no need to invoke :jar first. --- debian/rules | 1 - 1 file changed, 1 deletion(-) diff --git a/debian/rules b/debian/rules index 60026f8d707..9c487a9dfbe 100755 --- a/debian/rules +++ b/debian/rules @@ -32,7 +32,6 @@ clean: rm -rf .gradle gradle/wrapper/dists debian/freenet override_dh_auto_build: build_seed_file - ./gradlew jar; \ ./gradlew copyRuntimeLibs sed -e 's/@version@/$(FREENET_VERSION)/' -e 's/@build@/$(FREENET_BUILD)/' $(CURDIR)/debian/freenet.service.params | \ From 967ad6f9367f13b79432e43f376b868693efb071 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Sun, 26 Oct 2025 13:34:57 +0100 Subject: [PATCH 16/17] Gradle: simplify source set patterns --- build.gradle | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index d5b928d572d..6fbec059b61 100644 --- a/build.gradle +++ b/build.gradle @@ -67,10 +67,7 @@ sourceSets { } resources { srcDirs 'src' - include 'freenet/l10n/*properties' - include 'freenet/l10n/iso-*.tab' - include 'freenet/clients/http/staticfiles/**' - include 'freenet/clients/http/templates/**' + exclude '**/*.java' } } test { @@ -80,10 +77,7 @@ sourceSets { } resources { srcDirs 'test' - include 'freenet/client/filter/*/**' - include 'freenet/crypt/ciphers/rijndael-gladman-test-data/**' - include 'freenet/l10n/*properties' - include 'freenet/clients/http/templates/**' + exclude '**/*.java' } } } From fdfc24a8133eed21f9134df9bc73fac80bd2d634 Mon Sep 17 00:00:00 2001 From: Bert Massop Date: Tue, 6 Jan 2026 19:22:10 +0100 Subject: [PATCH 17/17] Gradle: remove :tar task This task is never used in the release process. The archives it would create can be created more reliably using `git archive`. --- build.gradle | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index 6fbec059b61..41514ca2040 100644 --- a/build.gradle +++ b/build.gradle @@ -189,31 +189,11 @@ tasks.register('copyRuntimeLibs', Copy) { } assemble { - // Add copyRuntimeLibs so `gradlew assemble` collects all build artifacts (except tar). + // Add copyRuntimeLibs so `gradlew assemble` collects all build artifacts. // Note: jar, sourcesJar, javadocJar are added as dependency by default. dependsOn copyRuntimeLibs } -tasks.register('tar', Tar) { - description = 'Build a source release, specifically excluding the build directories and gradle wrapper files.' - group = 'Build' - - from(project.rootDir) { - exclude '**/build' - exclude 'build' - exclude '.gradle' - } - into 'freenet-sources' - - compression = Compression.BZIP2 - destinationDirectory = layout.buildDirectory - archiveFileName = 'freenet-sources.tgz' - - doLast { // generate md5 checksum - ant.checksum file: file(archiveFile) - } -} - tasks.register('archiveDigests') { description = 'Display SHA-256 digests of all archives that were created.' group = 'Help'