Skip to content

Update gradle build to support new gradle features + use configuration cache #1611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: nightly
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
.gradle/
build/

# gradle build scans
/gradle.tos.agree

# fleet
.fleet/

Expand Down
30 changes: 29 additions & 1 deletion build-extensions/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,41 @@

plugins {
`kotlin-dsl`
id("java-gradle-plugin")
}

// fabric requires jvm 21, so we can do so too
kotlin.jvmToolchain(21)

repositories {
gradlePluginPortal()
mavenCentral()
maven("https://repo.stellardrift.ca/repository/snapshots/") {
content {
includeGroup("net.kyori")
}
}
mavenLocal {
content {
includeModule("eu.cloudnetservice.gradle", "juppiter")
}
}
}

dependencies {
implementation("net.kyori", "indra-common", "3.1.3")
implementation("net.kyori", "indra-git", "4.0.0-SNAPSHOT")
implementation("com.google.code.gson", "gson", "2.13.1")

implementation("com.diffplug.spotless", "spotless-plugin-gradle", libs.versions.spotless.get())
implementation("com.gradleup.shadow", "shadow-gradle-plugin", libs.versions.shadow.get())
implementation("eu.cloudnetservice.gradle", "juppiter", libs.versions.juppiter.get())
}

gradlePlugin {
plugins {
register("settings-plugin") {
id = "settings-plugin"
implementationClass = "SettingsPlugin"
}
}
}
25 changes: 25 additions & 0 deletions build-extensions/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2019-2025 CloudNetService team & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

dependencyResolutionManagement {
versionCatalogs {
register("libs") {
from(files("./../gradle/libs.versions.toml"))
}
}
}

rootProject.name = "build-extensions"
111 changes: 32 additions & 79 deletions build-extensions/src/main/kotlin/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
*/

import net.kyori.indra.git.IndraGitExtension
import net.kyori.indra.git.RepositoryValueSource
import net.kyori.indra.git.internal.IndraGitExtensionImpl
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Constants
import org.gradle.api.Project
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.internal.artifacts.repositories.resolver.MavenUniqueSnapshotComponentIdentifier
import org.gradle.api.plugins.JavaPluginExtension
Expand All @@ -27,8 +30,10 @@ import org.gradle.kotlin.dsl.attributes
import org.gradle.kotlin.dsl.findByType
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.the
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.util.logging.Level
import java.util.logging.Logger

fun Project.applyJarMetadata(mainClass: String, module: String) {
applyJarMetadata(mainClass, module, null)
Expand All @@ -48,21 +53,41 @@ fun Project.applyJarMetadata(mainClass: String, module: String, preMain: String?
manifest.attributes("Premain-Class" to preMain)
}
// apply git information to manifest
git()?.applyVcsInformationToManifest(manifest)
git()?.let { git ->
val commit = git.commit().map { it.name.substring(0, 8) }
val branchName = git.repositoryValue(BranchName::class.java)
if (commit.isPresent) manifest.attributes(IndraGitExtension.MANIFEST_ATTRIBUTE_GIT_COMMIT to commit.get())
if (branchName.isPresent) manifest.attributes(IndraGitExtension.MANIFEST_ATTRIBUTE_GIT_BRANCH to branchName.get())
}
}
}
}

/**
* Indra does not properly support configuration caching for #branchName yet
*/
private abstract class BranchName : RepositoryValueSource.Parameterless<String>() {
override fun obtain(repository: Git): String? {
try {
val ref = repository.repository.exactRef(Constants.HEAD)
if (ref == null || !ref.isSymbolic) return null // no HEAD, or detached HEAD

return ref.target.name
} catch (ex: IOException) {
Logger.getGlobal().log(Level.SEVERE, "Failed to query current branch name from git:", ex)
return null
}
}
}

fun Project.shortCommitHash(): String {
return git()?.commit()?.name?.substring(0, 8) ?: "unknown"
return git()?.commit()?.get()?.name()?.substring(0, 8) ?: "unknown"
}

fun Project.git(): IndraGitExtension? = rootProject.extensions.findByType()
fun Project.git(): IndraGitExtension? = extensions.findByType()

fun Project.sourceSets(): SourceSetContainer = the<JavaPluginExtension>().sourceSets

fun ProjectDependency.sourceSets(): SourceSetContainer = dependencyProject.sourceSets()

fun Project.mavenRepositories(): Iterable<MavenArtifactRepository> = repositories.filterIsInstance<MavenArtifactRepository>()

fun releasesOnly(repository: MavenArtifactRepository) {
Expand All @@ -76,75 +101,3 @@ fun snapshotsOnly(repository: MavenArtifactRepository) {
snapshotsOnly()
}
}

fun Project.exportLanguageFileInformation(): String {
val file = project.layout.buildDirectory.file("languages.txt").get().asFile
file.writeText(project.projectDir.resolve("src/main/resources/lang").listFiles()?.joinToString(separator = "\n") { it.name }!!)

return file.absolutePath
}

fun Project.exportCnlFile(fileName: String, ignoredDependencyGroups: Array<String> = arrayOf()): String {
val stringBuilder = StringBuilder("# CloudNet ${Versions.cloudNetCodeName} ${Versions.cloudNet}\n\n")
.append("# repositories\n")
// add all repositories
mavenRepositories().forEach { repo ->
stringBuilder.append("repo ${repo.name} ${repo.url.toString().dropLastWhile { it == '/' }}\n")
}

// add all dependencies
stringBuilder.append("\n\n# dependencies\n")
configurations.getByName("runtimeClasspath").resolvedConfiguration.resolvedArtifacts.forEach {
// get the module version from the artifact, stop if the dependency is ignored
val id = it.moduleVersion.id
if (id.group.equals(group) || ignoredDependencyGroups.contains(id.group)) {
return@forEach
}

// check if the dependency is a snapshot version - in this case we need to use another artifact url
var version = id.version
if (id.version.endsWith("-SNAPSHOT") && it.id.componentIdentifier is MavenUniqueSnapshotComponentIdentifier) {
// little hack to get the timestamped ("snapshot") version of the identifier
version = (it.id.componentIdentifier as MavenUniqueSnapshotComponentIdentifier).timestampedVersion
}

// try to find the repository associated with the module
val repository = resolveRepository(
"${id.group.replace('.', '/')}/${id.name}/${id.version}/${id.name}-$version.jar",
mavenRepositories()
) ?: throw IllegalStateException("Unable to resolve repository for $id")

// add the repository
val cs = ChecksumHelper.fileShaSum(it.file)
stringBuilder.append("include ${repository.name} ${id.group} ${id.name} ${id.version} $version $cs ${it.classifier ?: ""}\n")
}

// write to the output file
val target = project.layout.buildDirectory.file(fileName).get().asFile
target.writeText(stringBuilder.toString())

return target.absolutePath
}

private fun resolveRepository(
testUrlPath: String,
repositories: Iterable<MavenArtifactRepository>
): MavenArtifactRepository? {
return repositories.firstOrNull {
val url = it.url.resolve(testUrlPath).toURL()
with(url.openConnection() as HttpURLConnection) {
useCaches = false
readTimeout = 30000
connectTimeout = 30000
instanceFollowRedirects = true

setRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"
)

connect()
responseCode == 200
}
}
}
71 changes: 71 additions & 0 deletions build-extensions/src/main/kotlin/modules-configuration.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2019-2025 CloudNetService team & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.*

class ModuleGradlePlugin : Plugin<Project> {
override fun apply(project: Project) {
project.run {
if (name.endsWith("impl")) {
apply(plugin = "eu.cloudnetservice.juppiter")

configurations {
getByName("testImplementation").extendsFrom(getByName("moduleLibrary"))
}
}

repositories {
maven("https://repo.waterdog.dev/releases/")
maven("https://repo.waterdog.dev/snapshots/")
maven("https://repo.loohpjames.com/repository")
maven("https://repo.md-5.net/repository/releases/")
maven("https://repo.md-5.net/repository/snapshots/")
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}

dependencies {
"compileOnly"(project(":node:node-api"))
"testImplementation"(project(":node:node-api"))

// generation for platform main classes
"compileOnly"(project(":ext:platform-inject-support:platform-inject-api"))
"annotationProcessor"(project(":ext:platform-inject-support:platform-inject-processor"))

"compileOnly"(libs.library("guava"))
}

tasks.register<Sync>("processSources") {
inputs.property("version", project.version)
from(sourceSets().getByName("main").java)
into(layout.buildDirectory.dir("src"))
filter(ReplaceTokens::class, mapOf("tokens" to mapOf("version" to rootProject.version)))
}

tasks.named<JavaCompile>("compileJava") {
dependsOn(tasks.getByName("processSources"))
source = tasks.getByName("processSources").outputs.files.asFileTree
}
}
}
}
59 changes: 59 additions & 0 deletions build-extensions/src/main/kotlin/plugins-configuration.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2019-2025 CloudNetService team & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.*

class PluginGradlePlugin : Plugin<Project> {
override fun apply(project: Project) {
project.run {
repositories {
maven("https://repo.waterdog.dev/releases/")
maven("https://repo.waterdog.dev/snapshots/")
maven("https://repo.spongepowered.org/maven/")
maven("https://repo.loohpjames.com/repository")
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}

dependencies {
"implementation"(libs.library("guava"))

// generation for platform main classes
"compileOnly"(project(":ext:platform-inject-support:platform-inject-api"))
"annotationProcessor"(project(":ext:platform-inject-support:platform-inject-processor"))
}

tasks.register<Sync>("processSources") {
inputs.property("version", project.version)
from(sourceSets().getByName("main").java)
into(layout.buildDirectory.dir("src"))
filter(ReplaceTokens::class, mapOf("tokens" to mapOf("version" to rootProject.version)))
}

tasks.named<JavaCompile>("compileJava") {
dependsOn(tasks.getByName("processSources"))
source = tasks.getByName("processSources").outputs.files.asFileTree
}
}
}
}
Loading