diff --git a/.gitignore b/.gitignore index 8af103bec8..1d968cac66 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ .gradle/ build/ +# gradle build scans +/gradle.tos.agree + # fleet .fleet/ diff --git a/build-extensions/build.gradle.kts b/build-extensions/build.gradle.kts index db7d532e7f..ec09c718fe 100644 --- a/build-extensions/build.gradle.kts +++ b/build-extensions/build.gradle.kts @@ -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" + } + } } diff --git a/build-extensions/settings.gradle.kts b/build-extensions/settings.gradle.kts new file mode 100644 index 0000000000..46034abe95 --- /dev/null +++ b/build-extensions/settings.gradle.kts @@ -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" diff --git a/build-extensions/src/main/kotlin/extensions.kt b/build-extensions/src/main/kotlin/extensions.kt index 1a8a3c05c3..0620a12e30 100644 --- a/build-extensions/src/main/kotlin/extensions.kt +++ b/build-extensions/src/main/kotlin/extensions.kt @@ -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 @@ -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) @@ -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() { + 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().sourceSets -fun ProjectDependency.sourceSets(): SourceSetContainer = dependencyProject.sourceSets() - fun Project.mavenRepositories(): Iterable = repositories.filterIsInstance() fun releasesOnly(repository: MavenArtifactRepository) { @@ -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 = 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? { - 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 - } - } -} diff --git a/build-extensions/src/main/kotlin/modules-configuration.kt b/build-extensions/src/main/kotlin/modules-configuration.kt new file mode 100644 index 0000000000..ec4dc50e74 --- /dev/null +++ b/build-extensions/src/main/kotlin/modules-configuration.kt @@ -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 { + 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("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("compileJava") { + dependsOn(tasks.getByName("processSources")) + source = tasks.getByName("processSources").outputs.files.asFileTree + } + } + } +} diff --git a/build-extensions/src/main/kotlin/plugins-configuration.kt b/build-extensions/src/main/kotlin/plugins-configuration.kt new file mode 100644 index 0000000000..ed29cbf8ca --- /dev/null +++ b/build-extensions/src/main/kotlin/plugins-configuration.kt @@ -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 { + 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("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("compileJava") { + dependsOn(tasks.getByName("processSources")) + source = tasks.getByName("processSources").outputs.files.asFileTree + } + } + } +} diff --git a/build-extensions/src/main/kotlin/publishing-extensions.kt b/build-extensions/src/main/kotlin/publishing-extensions.kt index 4c8f491dd4..eae400e924 100644 --- a/build-extensions/src/main/kotlin/publishing-extensions.kt +++ b/build-extensions/src/main/kotlin/publishing-extensions.kt @@ -20,23 +20,17 @@ import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication import org.gradle.external.javadoc.JavadocMemberLevel import org.gradle.external.javadoc.StandardJavadocDocletOptions -import org.gradle.internal.impldep.com.amazonaws.util.XpathUtils.asNode import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType import org.gradle.plugins.signing.Sign import org.gradle.plugins.signing.SigningExtension -fun Project.configurePublishing(publishedComponent: String, withJavadocAndSource: Boolean = false) { +fun Project.configurePublishing(publishedComponent: String) { extensions.configure { publications.apply { create("maven", MavenPublication::class.java).apply { from(components.getByName(publishedComponent)) - if (withJavadocAndSource) { - artifact(tasks.getByName("sourcesJar")) - artifact(tasks.getByName("javadocJar")) - } - pom.apply { name.set(project.name) description.set(project.description) @@ -65,19 +59,19 @@ fun Project.configurePublishing(publishedComponent: String, withJavadocAndSource scm { tag.set("HEAD") - url.set("git@github.com:CloudNetService/CloudNet-v3.git") - connection.set("scm:git:git@github.com:CloudNetService/CloudNet-v3.git") - developerConnection.set("scm:git:git@github.com:CloudNetService/CloudNet-v3.git") + url.set("git@github.com:CloudNetService/CloudNet.git") + connection.set("scm:git:git@github.com:CloudNetService/CloudNet.git") + developerConnection.set("scm:git:git@github.com:CloudNetService/CloudNet.git") } issueManagement { system.set("GitHub Issues") - url.set("https://github.com/CloudNetService/CloudNet-v3/issues") + url.set("https://github.com/CloudNetService/CloudNet/issues") } ciManagement { system.set("GitHub Actions") - url.set("https://github.com/CloudNetService/CloudNet-v3/actions") + url.set("https://github.com/CloudNetService/CloudNet/actions") } withXml { @@ -109,7 +103,7 @@ fun Project.configurePublishing(publishedComponent: String, withJavadocAndSource sign(extensions.getByType(PublishingExtension::class.java).publications.getByName("maven")) } - tasks.withType { + tasks.withType().configureEach { onlyIf { !rootProject.version.toString().endsWith("-SNAPSHOT") } diff --git a/build-extensions/src/main/kotlin/settings-plugin.kt b/build-extensions/src/main/kotlin/settings-plugin.kt new file mode 100644 index 0000000000..ada1e0afa2 --- /dev/null +++ b/build-extensions/src/main/kotlin/settings-plugin.kt @@ -0,0 +1,255 @@ +/* + * 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 com.diffplug.gradle.spotless.SpotlessExtension +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin +import org.gradle.api.JavaVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.ExternalModuleDependencyBundle +import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.initialization.Settings +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.plugins.quality.Checkstyle +import org.gradle.api.plugins.quality.CheckstyleExtension +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.api.tasks.javadoc.Javadoc +import org.gradle.api.tasks.testing.Test +import org.gradle.external.javadoc.StandardJavadocDocletOptions +import org.gradle.kotlin.dsl.* +import org.gradle.language.jvm.tasks.ProcessResources + +class SettingsPlugin : Plugin { + override fun apply(settings: Settings) { + settings.gradle.lifecycle.beforeProject { + println("configuring ${this.path}") // TODO remove, this is temporary to better debug any configuration done (configure-on-demand) + plugins.apply(AllProjects::class) + if (this != this.rootProject) { + plugins.apply(JavaProjects::class) + } + } + } +} + +fun isJavaConfiguredProject(name: String): Boolean { + if (isHelperProject(name)) return false + return name != "bom" +} + +fun isHelperProject(name: String): Boolean { + return name == "modules" || name == "plugins" || name == "ext" || name == "launcher" +} + +object CustomConfigurations { + const val GLOBAL_JAVADOC_SOURCES = "globalJavadocSources" + const val GLOBAL_JAVADOC_CLASSPATH = "globalJavadocClasspath" +} + +internal fun Provider.library(name: String): Provider { + return flatMap { + it.findLibrary(name) + .orElseThrow { NoSuchElementException("Failed to find library named $name. Valid names: ${it.libraryAliases}") } + } +} + +internal fun Provider.bundle(name: String): Provider { + // We can't use flatMap here, because Gradle uses internal magic + return get().let { + it.findBundle(name) + .orElseThrow { NoSuchElementException("Failed to find bundle named $name. Valid names: ${it.bundleAliases}") } + } +} + +internal val Project.libs: Provider + get() = provider { extensions.getByName("versionCatalogs").named("libs") } + +class JavaProjects : Plugin { + override fun apply(project: Project) { + project.run { + + + // these are top level projects which are configured separately + if (isHelperProject(name)) { + return@run + } + + // these are the plugins which we need to apply to all projects + apply(plugin = "signing") + apply(plugin = "maven-publish") + + // skip further applying to bom - this project is a bit special as we're not allowed to + // apply the java plugin to it (that's why we need to stop here, but we need to publish + // at well (that's why we're applying the publish plugin) + if (!isJavaConfiguredProject(name)) { + return@run + } + + apply(plugin = "checkstyle") + apply(plugin = "java-library") + apply(plugin = "com.diffplug.spotless") + apply(plugin = "net.kyori.indra.git") + + if (path.startsWith(":plugins:")) { + apply() + } + if (path.startsWith(":modules:")) { + apply() + } + + val libs = this.libs + + afterEvaluate { + dependencies { + // the 'rootProject.libs.' prefix is needed here - see https://github.com/gradle/gradle/issues/16634 + // lombok + "compileOnly"(libs.library("lombok")) + "annotationProcessor"(libs.library("lombok")) + // annotations + "compileOnly"(libs.library("annotations")) + // testing + "testImplementation"(libs.library("mockito")) + "testRuntimeOnly"(libs.library("junitLauncher")) + "testImplementation"(libs.bundle("junit")) + "testImplementation"(libs.bundle("testContainers")) + } + } + + configurations.all { + // unsure why but every project loves them, and they literally have an import for every letter I type - beware + exclude("org.checkerframework", "checker-qual") + } + + tasks.withType().configureEach { + from(rootProject.file("LICENSE")) + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + + tasks.withType().configureEach { + useJUnitPlatform() + testLogging { + events("started", "passed", "skipped", "failed") + } + + // allow dynamic agent loading for mockito + jvmArgs( + "--enable-preview", + "-XX:+EnableDynamicAgentLoading", + "--enable-native-access=ALL-UNNAMED", + "--add-opens=java.base/java.lang.invoke=ALL-UNNAMED" + ) + + // always pass down all given system properties + systemProperties(System.getProperties().mapKeys { it.key.toString() }) + } + + tasks.withType().configureEach { + val javaVersion = if (project.path.contains("api")) JavaVersion.VERSION_17 else JavaVersion.VERSION_24 + sourceCompatibility = javaVersion.toString() + targetCompatibility = javaVersion.toString() + + options.encoding = "UTF-8" + options.isIncremental = true + + if (project.path != ":launcher:java8" && project.path != ":launcher:patcher" && !project.path.contains("api")) { + options.compilerArgs.add("--enable-preview") + options.compilerArgs.add("-Xlint:-deprecation,-unchecked,-preview") + options.compilerArgs.add("-proc:full") + } + } + + project.afterEvaluate { + if (project.plugins.hasPlugin(ShadowJavaPlugin::class)) { + tasks.named("assemble").configure { + dependsOn(project.tasks.named("shadowJar")) + } + tasks.named("jar").configure { + // we use the shadow jar task, so move the jar into the task's temporary dir to avoid clutter + destinationDirectory = temporaryDir + } + } + } + + tasks.withType().configureEach { + maxErrors = 0 + maxWarnings = 0 + configFile = rootProject.file("checkstyle.xml") + } + + afterEvaluate { + extensions.configure { + toolVersion = libs.map { it.findVersion("checkstyleTools").orElseThrow().requiredVersion }.get() + } + } + + extensions.configure { + java { + licenseHeaderFile(rootProject.file("LICENSE_HEADER")) + } + } + + val java = project.extensions.getByType() + java.withSourcesJar() + java.withJavadocJar() + + tasks.withType().configureEach { + val options = options as? StandardJavadocDocletOptions ?: return@configureEach + applyDefaultJavadocOptions(options) + } + + tasks.withType().configureEach { + dependsOn(tasks.withType()) + } + + // all these projects are publishing their java artifacts + configurePublishing("java") + + // create consumable artifacts for global javadoc + configurations.consumable(CustomConfigurations.GLOBAL_JAVADOC_SOURCES) { + outgoing.artifacts(sourceSets().named("main").map { it.allJava.srcDirs }) + } + configurations.consumable(CustomConfigurations.GLOBAL_JAVADOC_CLASSPATH) { + outgoing.artifacts(sourceSets().named("main").map { it.compileClasspath }) + } + } + } +} + +class AllProjects : Plugin { + override fun apply(project: Project) { + project.run { + this.version = Versions.cloudNet + this.group = "eu.cloudnetservice.cloudnet" + this.description = "A modern application that can dynamically and easily deliver Minecraft oriented software" + + this.repositories { + releasesOnly(mavenCentral()) + snapshotsOnly(maven("https://central.sonatype.com/repository/maven-snapshots/")) + + // ensure that we use these repositories for snapshots/releases only (improves lookup times) + releasesOnly(maven("https://repository.derklaro.dev/releases/")) + snapshotsOnly(maven("https://repository.derklaro.dev/snapshots/")) + + // must be after sonatype as sponge mirrors sonatype which leads to outdated dependencies + maven("https://repo.spongepowered.org/maven/") + } + } + } +} diff --git a/build-extensions/src/main/kotlin/tasks.kt b/build-extensions/src/main/kotlin/tasks.kt new file mode 100644 index 0000000000..c3a13a5a55 --- /dev/null +++ b/build-extensions/src/main/kotlin/tasks.kt @@ -0,0 +1,180 @@ +/* + * 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. + */ + +@file:Suppress("LeakingThis") + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.repositories.MavenArtifactRepository +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.internal.artifacts.repositories.resolver.MavenUniqueSnapshotComponentIdentifier +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.setProperty +import java.io.File +import java.net.HttpURLConnection +import java.net.URI +import javax.inject.Inject + +@CacheableTask +abstract class ExportCnlFile : DefaultTask() { + @get:Inject + abstract val objects: ObjectFactory + + @get:Input + abstract val fileName: Property + + @Input + val ignoredDependencyGroups: SetProperty = objects.setProperty() + + @OutputFile + val outputFile: RegularFileProperty = objects.fileProperty() + + @Nested + val mavenRepositories: ListProperty = objects.listProperty() + + @Nested + val resolvedArtifacts: SetProperty = objects.setProperty() + + @get: Inject + internal abstract val layout: ProjectLayout + + init { + outputFile.convention(layout.file(fileName.map { temporaryDir.resolve(it) })) + mavenRepositories.convention(project.mavenRepositories().map { CacheableMavenRepository(it) }.toList()) + } + + fun setResolvedArtifacts(runtimeClasspath: Configuration) { + val artifacts = runtimeClasspath.resolvedConfiguration.resolvedArtifacts.map { + val group = it.moduleVersion.id.group + val name = it.moduleVersion.id.name + val version = it.moduleVersion.id.version + val timestampedVersion = + if (version.endsWith("-SNAPSHOT") && it.id.componentIdentifier is MavenUniqueSnapshotComponentIdentifier) { + // little hack to get the timestamped ("snapshot") version of the identifier + (it.id.componentIdentifier as MavenUniqueSnapshotComponentIdentifier).timestampedVersion + } else version + + val classifier = it.classifier + val file = it.file + CacheableResolvedArtifact(group, name, version, timestampedVersion, classifier, file) + }.filter { + it.group != project.group + }.toSet() + this.resolvedArtifacts.set(artifacts) + } + + @TaskAction + fun run() { + val ignoredDependencyGroups = this.ignoredDependencyGroups.get() + val mavenRepositories = this.mavenRepositories.get() + + 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.dropLastWhile { it == '/' }}\n") + } + + // add all dependencies + stringBuilder.append("\n\n# dependencies\n") + resolvedArtifacts.get().forEach { + // get the module version from the artifact, stop if the dependency is ignored + if (ignoredDependencyGroups.contains(it.group)) { + return@forEach + } + + // try to find the repository associated with the module + val path = "${it.group.replace('.', '/')}/${it.name}/${it.version}/${it.name}-${it.timestampedVersion}.jar" + val repository = resolveRepository( + path, mavenRepositories + ) ?: throw IllegalStateException( + "Unable to resolve repository for $it.\nSearched in ${ + mavenRepositories.joinToString( + separator = "\n" + ) { r -> r.url + path } + }") + + // add the repository + val cs = ChecksumHelper.fileShaSum(it.file) + stringBuilder.append("include ${repository.name} ${it.group} ${it.name} ${it.version} ${it.timestampedVersion} $cs ${it.classifier ?: ""}\n") + } + + // write to the output file + outputFile.get().asFile.writeText(stringBuilder.toString()) + } +} + +data class CacheableResolvedArtifact( + @Input val group: String, + @Input val name: String, + @Input val version: String, + @Input val timestampedVersion: String, + @Optional @Input val classifier: String?, + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) val file: File +) + +data class CacheableMavenRepository(@Input val name: String, @Input val url: String) { + constructor(repository: MavenArtifactRepository) : this(repository.name, repository.url.toString()) +} + +private fun resolveRepository( + testUrlPath: String, repositories: Iterable +): CacheableMavenRepository? { + return repositories.firstOrNull { + val url = URI.create(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 + } + } +} + +@CacheableTask +abstract class ExportLanguageFileInformation : DefaultTask() { + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val languageFiles: ConfigurableFileCollection + + @get:OutputFile + abstract val outputFile: RegularFileProperty + + init { + outputFile.convention { temporaryDir.resolve("languages.txt") } + } + + @TaskAction + fun run() { + outputFile.asFile.get().writeText(languageFiles.files.joinToString(separator = "\n") { it.name }) + } +} diff --git a/build.gradle.kts b/build.gradle.kts index fc49e630e2..9f8c885bc8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,152 +14,18 @@ * limitations under the License. */ -import com.diffplug.gradle.spotless.SpotlessExtension - plugins { id("cloudnet.parent-build-logic") - alias(libs.plugins.spotless) alias(libs.plugins.nexusPublish) + alias(libs.plugins.spotless) apply false + alias(libs.plugins.shadow) apply false alias(libs.plugins.fabricLoom) apply false } -defaultTasks("build", "test", "shadowJar") - -allprojects { - version = Versions.cloudNet - group = "eu.cloudnetservice.cloudnet" - description = "A modern application that can dynamically and easily deliver Minecraft oriented software" - - repositories { - releasesOnly(mavenCentral()) - snapshotsOnly(maven("https://central.sonatype.com/repository/maven-snapshots/")) - - // ensure that we use these repositories for snapshots/releases only (improves lookup times) - releasesOnly(maven("https://repository.derklaro.dev/releases/")) - snapshotsOnly(maven("https://repository.derklaro.dev/snapshots/")) - - // must be after sonatype as sponge mirrors sonatype which leads to outdated dependencies - maven("https://repo.spongepowered.org/maven/") - } -} - -subprojects { - // these are top level projects which are configured separately - if (name == "modules" || name == "plugins" || name == "ext" || name == "launcher") { - return@subprojects - } - - // these are the plugins which we need to apply to all projects - apply(plugin = "signing") - apply(plugin = "maven-publish") - - // skip further applying to bom - this project is a bit special as we're not allowed to - // apply the java plugin to it (that's why we need to stop here, but we need to publish - // at well (that's why we're applying the publish plugin) - if (name == "bom") { - return@subprojects - } - - apply(plugin = "checkstyle") - apply(plugin = "java-library") - apply(plugin = "com.diffplug.spotless") - - dependencies { - // the 'rootProject.libs.' prefix is needed here - see https://github.com/gradle/gradle/issues/16634 - // lombok - "compileOnly"(rootProject.libs.lombok) - "annotationProcessor"(rootProject.libs.lombok) - // annotations - "compileOnly"(rootProject.libs.annotations) - // testing - "testImplementation"(rootProject.libs.mockito) - "testRuntimeOnly"(rootProject.libs.junitLauncher) - "testImplementation"(rootProject.libs.bundles.junit) - "testImplementation"(rootProject.libs.bundles.testContainers) - } - - configurations.all { - // unsure why but every project loves them, and they literally have an import for every letter I type - beware - exclude("org.checkerframework", "checker-qual") - } - - tasks.withType { - from(rootProject.file("LICENSE")) - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } - - tasks.withType { - useJUnitPlatform() - testLogging { - events("started", "passed", "skipped", "failed") - } - - // allow dynamic agent loading for mockito - jvmArgs( - "--enable-preview", - "-XX:+EnableDynamicAgentLoading", - "--enable-native-access=ALL-UNNAMED", - "--add-opens=java.base/java.lang.invoke=ALL-UNNAMED" - ) - - // always pass down all given system properties - systemProperties(System.getProperties().mapKeys { it.key.toString() }) - systemProperty("io.netty5.noUnsafe", "true") - } - - tasks.withType().configureEach { - val javaVersion = if (project.path.contains("api")) JavaVersion.VERSION_17 else JavaVersion.VERSION_24 - sourceCompatibility = javaVersion.toString() - targetCompatibility = javaVersion.toString() +defaultTasks("build") - options.encoding = "UTF-8" - options.isIncremental = true - - if (project.path != ":launcher:java8" && project.path != ":launcher:patcher" && !project.path.contains("api")) { - options.compilerArgs.add("--enable-preview") - options.compilerArgs.add("-Xlint:-deprecation,-unchecked,-preview") - options.compilerArgs.add("-proc:full") - } - } - - tasks.withType { - maxErrors = 0 - maxWarnings = 0 - configFile = rootProject.file("checkstyle.xml") - } - - extensions.configure { - toolVersion = rootProject.libs.versions.checkstyleTools.get() - } - - extensions.configure { - java { - licenseHeaderFile(rootProject.file("LICENSE_HEADER")) - } - } - - tasks.register("javadocJar") { - archiveClassifier.set("javadoc") - from(tasks.getByName("javadoc")) - } - - tasks.register("sourcesJar") { - archiveClassifier.set("sources") - from(project.sourceSets()["main"].allJava) - } - - tasks.withType { - val options = options as? StandardJavadocDocletOptions ?: return@withType - applyDefaultJavadocOptions(options) - } - - tasks.withType { - dependsOn(tasks.withType()) - } - - // all these projects are publishing their java artifacts - configurePublishing("java", true) -} +val globalJavadocSources = configurations.register("globalJavadocSources") +val globalJavadocClasspath = configurations.register("globalJavadocClasspath") tasks.register("globalJavaDoc", Javadoc::class) { val options = options as? StandardJavadocDocletOptions ?: return@register @@ -170,9 +36,8 @@ tasks.register("globalJavaDoc", Javadoc::class) { applyDefaultJavadocOptions(options) options.windowTitle = "CloudNet JavaDocs" // set the sources - val sources = subprojects.filter { it.plugins.hasPlugin("java") }.map { it.path } - source(files(sources.flatMap { project(it).sourceSets()["main"].allJava })) - classpath = files(sources.flatMap { project(it).sourceSets()["main"].compileClasspath }) + source(globalJavadocSources) + classpath = globalJavadocClasspath.get() } nexusPublishing { @@ -189,10 +54,23 @@ nexusPublishing { useStaging.set(!project.version.toString().endsWith("-SNAPSHOT")) } +dependencies { + subprojects.map { it.isolated }.forEach { project -> + if (!isJavaConfiguredProject(project.name)) return@forEach + globalJavadocSources(this.project(project.path)) { + targetConfiguration = CustomConfigurations.GLOBAL_JAVADOC_SOURCES + } + globalJavadocClasspath(this.project(project.path)) { + targetConfiguration = CustomConfigurations.GLOBAL_JAVADOC_CLASSPATH + } + } +} + gradle.projectsEvaluated { tasks.register("genUpdaterInformation") { subprojects.forEach { // check if we need to depend on the plugin + // TODO this breaks isolated projects if (!it.plugins.hasPlugin("java")) return@forEach // depend this task on the build output of each subproject dependsOn("${it.path}:build") diff --git a/driver/impl/build.gradle.kts b/driver/impl/build.gradle.kts index e688b1a4ab..4dfded9b06 100644 --- a/driver/impl/build.gradle.kts +++ b/driver/impl/build.gradle.kts @@ -34,6 +34,6 @@ dependencies { "annotationProcessor"(projects.driver.driverAp) } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/driver.aero") } diff --git a/ext/platform-inject-support/loader/build.gradle.kts b/ext/platform-inject-support/loader/build.gradle.kts index 72394726e9..b48781dd05 100644 --- a/ext/platform-inject-support/loader/build.gradle.kts +++ b/ext/platform-inject-support/loader/build.gradle.kts @@ -19,13 +19,16 @@ dependencies { "implementation"(projects.utils.utilsBase) } -tasks.withType { - // depend on the output of the jar task - val jarTask = projects.ext.platformInjectSupport.platformInjectRuntime.dependencyProject.tasks.getByName("jar") - dependsOn(jarTask) +val includeInJar = configurations.register("includeInJar") { + isCanBeConsumed = false; + isTransitive = false +} +tasks.withType().configureEach { // copy over the final output file - from("../runtime/build/libs") { - include(Files.injectSupport) - } + from(includeInJar) +} + +dependencies { + includeInJar(projects.ext.platformInjectSupport.platformInjectRuntime) } diff --git a/ext/platform-inject-support/runtime/build.gradle.kts b/ext/platform-inject-support/runtime/build.gradle.kts index 73561c96c1..4b58e37717 100644 --- a/ext/platform-inject-support/runtime/build.gradle.kts +++ b/ext/platform-inject-support/runtime/build.gradle.kts @@ -31,6 +31,6 @@ dependencies { "compileOnly"(projects.ext.platformInjectSupport.platformInjectApi) } -tasks.withType { +tasks.jar.configure { archiveFileName.set(Files.injectSupport) } diff --git a/gradle.properties b/gradle.properties index 1da63925a8..e4bf8fb30e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,10 @@ # limitations under the License. # org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.configureondemand=true org.gradle.parallel=true +#org.gradle.unsafe.isolated-projects=true org.gradle.warning.mode=all -org.gradle.logging.level=info +org.gradle.logging.level=warn org.gradle.jvmargs=-Xmx5G -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4bfe10e13c..0d9b375ff6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] # plugins -shadow = "8.3.8" -juppiter = "0.4.0" +shadow = "9.0.0-rc1" +juppiter = "0.5.0-SNAPSHOT" spotless = "7.1.0" fabricLoom = "1.10.5" nexusPublish = "2.0.0" @@ -227,7 +227,7 @@ serverPlatform = ["spigot", "sponge", "nukkitX", "minestom", "minestomExtensions [plugins] fabricLoom = { id = "fabric-loom", version.ref = "fabricLoom" } -spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } -shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } +spotless = { id = "com.diffplug.spotless" } # version on classpath from build-extensions +shadow = { id = "com.gradleup.shadow" } # version on classpath from build-extensions juppiter = { id = "eu.cloudnetservice.juppiter", version.ref = "juppiter" } nexusPublish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexusPublish" } diff --git a/launcher/java22/build.gradle.kts b/launcher/java22/build.gradle.kts index 566645b08e..e3e3ca7277 100644 --- a/launcher/java22/build.gradle.kts +++ b/launcher/java22/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.launcher) } diff --git a/launcher/java8/build.gradle.kts b/launcher/java8/build.gradle.kts index 1c412be718..090dcda321 100644 --- a/launcher/java8/build.gradle.kts +++ b/launcher/java8/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ -tasks.withType { +tasks.withType().configureEach { sourceCompatibility = JavaVersion.VERSION_1_8.toString() targetCompatibility = JavaVersion.VERSION_1_8.toString() } diff --git a/launcher/patcher/build.gradle.kts b/launcher/patcher/build.gradle.kts index b722bd1b67..3a05c63947 100644 --- a/launcher/patcher/build.gradle.kts +++ b/launcher/patcher/build.gradle.kts @@ -14,11 +14,11 @@ * limitations under the License. */ -tasks.withType { +tasks.jar.configure { archiveFileName.set(Files.launcherPatcher) } -tasks.withType { +tasks.withType().configureEach { sourceCompatibility = JavaVersion.VERSION_17.toString() targetCompatibility = JavaVersion.VERSION_17.toString() } diff --git a/modules/bridge/impl/build.gradle.kts b/modules/bridge/impl/build.gradle.kts index 4ba6439167..fa72b54ee0 100644 --- a/modules/bridge/impl/build.gradle.kts +++ b/modules/bridge/impl/build.gradle.kts @@ -22,12 +22,12 @@ plugins { configurations { // custom configuration for later dependency resolution - create("runtimeImpl") { + register("runtimeImpl") { configurations.getByName("api").extendsFrom(this) } } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/bridge.aero") } @@ -56,7 +56,7 @@ dependencies { "mappings"(loom.officialMojangMappings()) } -tasks.withType { +tasks.jar.configure { manifest { attributes["paperweight-mappings-namespace"] = "mojang" } @@ -72,7 +72,7 @@ tasks.withType { } } -tasks.withType { +tasks.remapJar.configure { archiveFileName.set(Files.bridge) } diff --git a/modules/build.gradle.kts b/modules/build.gradle.kts index e50c6de7bf..d2d9f18709 100644 --- a/modules/build.gradle.kts +++ b/modules/build.gradle.kts @@ -13,55 +13,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import org.apache.tools.ant.filters.ReplaceTokens - -plugins { - alias(libs.plugins.juppiter) apply false -} - -subprojects { - 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"(rootProject.projects.node.nodeApi) - "testImplementation"(rootProject.projects.node.nodeApi) - - // generation for platform main classes - "compileOnly"(rootProject.projects.ext.platformInjectSupport.platformInjectApi) - "annotationProcessor"(rootProject.projects.ext.platformInjectSupport.platformInjectProcessor) - - // internal dependencies - "compileOnly"(rootProject.libs.guava) - } - - tasks.create("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("compileJava") { - dependsOn(tasks.getByName("processSources")) - source = tasks.getByName("processSources").outputs.files.asFileTree - } -} diff --git a/modules/cloudflare/impl/build.gradle.kts b/modules/cloudflare/impl/build.gradle.kts index 0bb49fb5f8..9217375d21 100644 --- a/modules/cloudflare/impl/build.gradle.kts +++ b/modules/cloudflare/impl/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.cloudflare) } diff --git a/modules/database-mongodb/impl/build.gradle.kts b/modules/database-mongodb/impl/build.gradle.kts index 7a6c1a8e99..be32eda410 100644 --- a/modules/database-mongodb/impl/build.gradle.kts +++ b/modules/database-mongodb/impl/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.databaseMongo) } diff --git a/modules/database-mysql/impl/build.gradle.kts b/modules/database-mysql/impl/build.gradle.kts index 1367d226b5..d3889cc7a9 100644 --- a/modules/database-mysql/impl/build.gradle.kts +++ b/modules/database-mysql/impl/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.* + /* * Copyright 2019-2024 CloudNetService team & contributors * @@ -18,7 +20,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.databaseMysql) } diff --git a/modules/dockerized-services/impl/build.gradle.kts b/modules/dockerized-services/impl/build.gradle.kts index af1351406d..f882d8149f 100644 --- a/modules/dockerized-services/impl/build.gradle.kts +++ b/modules/dockerized-services/impl/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.dockerizedServices) } diff --git a/modules/influx/impl/build.gradle.kts b/modules/influx/impl/build.gradle.kts index fc0fdb072b..7eb4a86dc3 100644 --- a/modules/influx/impl/build.gradle.kts +++ b/modules/influx/impl/build.gradle.kts @@ -20,7 +20,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.influx) } diff --git a/modules/labymod/impl/build.gradle.kts b/modules/labymod/impl/build.gradle.kts index d449543322..83cfcb4749 100644 --- a/modules/labymod/impl/build.gradle.kts +++ b/modules/labymod/impl/build.gradle.kts @@ -19,7 +19,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.labymod) } diff --git a/modules/npcs/impl/build.gradle.kts b/modules/npcs/impl/build.gradle.kts index 0b9fef880f..3656a275e5 100644 --- a/modules/npcs/impl/build.gradle.kts +++ b/modules/npcs/impl/build.gradle.kts @@ -21,7 +21,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.npcs) manifest { @@ -29,11 +29,11 @@ tasks.withType { } } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/npcs.aero") } -tasks.withType { +tasks.withType().configureEach { relocate("net.kyori", "eu.cloudnetservice.modules.npc.relocate.net.kyori") relocate("io.papermc.lib", "eu.cloudnetservice.modules.npc.relocate.paperlib") relocate("io.leangen.geantyref", "eu.cloudnetservice.modules.npc.relocate.geantyref") diff --git a/modules/report/impl/build.gradle.kts b/modules/report/impl/build.gradle.kts index 6b64c4490f..295befefb2 100644 --- a/modules/report/impl/build.gradle.kts +++ b/modules/report/impl/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.report) } diff --git a/modules/signs/impl/build.gradle.kts b/modules/signs/impl/build.gradle.kts index 5913bcee5e..c2c175dcc7 100644 --- a/modules/signs/impl/build.gradle.kts +++ b/modules/signs/impl/build.gradle.kts @@ -20,7 +20,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.signs) manifest { @@ -28,7 +28,7 @@ tasks.withType { } } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/signs.aero") } diff --git a/modules/smart/impl/build.gradle.kts b/modules/smart/impl/build.gradle.kts index 5ef8a7fa49..b50b179ecb 100644 --- a/modules/smart/impl/build.gradle.kts +++ b/modules/smart/impl/build.gradle.kts @@ -20,7 +20,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.smart) } diff --git a/modules/storage-s3/impl/build.gradle.kts b/modules/storage-s3/impl/build.gradle.kts index d2c5d3dffe..b13ddc8f3b 100644 --- a/modules/storage-s3/impl/build.gradle.kts +++ b/modules/storage-s3/impl/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.storageS3) } diff --git a/modules/storage-sftp/impl/build.gradle.kts b/modules/storage-sftp/impl/build.gradle.kts index e26b05086c..cc26e6a14e 100644 --- a/modules/storage-sftp/impl/build.gradle.kts +++ b/modules/storage-sftp/impl/build.gradle.kts @@ -18,7 +18,7 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.storageSftp) } diff --git a/modules/syncproxy/impl/build.gradle.kts b/modules/syncproxy/impl/build.gradle.kts index b4933162c4..7744946621 100644 --- a/modules/syncproxy/impl/build.gradle.kts +++ b/modules/syncproxy/impl/build.gradle.kts @@ -20,11 +20,11 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +tasks.shadowJar.configure { archiveFileName.set(Files.syncproxy) } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/syncproxy.aero") } diff --git a/node/impl/build.gradle.kts b/node/impl/build.gradle.kts index d505d75829..289e7d5294 100644 --- a/node/impl/build.gradle.kts +++ b/node/impl/build.gradle.kts @@ -14,26 +14,35 @@ * limitations under the License. */ -tasks.withType { - archiveFileName.set(Files.node) - from(projects.node.nodeApi.sourceSets()["main"].output) - from(projects.utils.utilsBase.sourceSets()["main"].output) - from(projects.driver.driverApi.sourceSets()["main"].output) - from(projects.driver.driverImpl.sourceSets()["main"].output) +plugins { + alias(libs.plugins.shadow) +} - dependsOn(":wrapper-jvm:wrapper-jvm-impl:shadowJar") +val exportCnlFile = tasks.register("exportCnlFile") { + fileName = Files.nodeCnl + setResolvedArtifacts(configurations.runtimeClasspath.get()) +} +val exportLanguageFileInformation = tasks.register("exportLanguageFileInformation") { + languageFiles.from(project.projectDir.resolve("src/main/resources/lang").listFiles()) +} +val includeInJar = configurations.register("includeInJar") { isTransitive = false } +val wrapperJar = configurations.register("wrapperJar") { isTransitive = false } - from("../../wrapper-jvm/impl/build/libs") { - include(Files.wrapper) - } +tasks.shadowJar { + archiveFileName.set(Files.node) + configurations = listOf(includeInJar.get()) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE - doFirst { - from(exportCnlFile(Files.nodeCnl)) - from(exportLanguageFileInformation()) + from(wrapperJar) { + // Rename the file to make sure 100% it is correctly named + rename { Files.wrapper } } + + from(exportCnlFile) + from(exportLanguageFileInformation) } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/node.aero") } @@ -72,6 +81,13 @@ dependencies { "implementation"(libs.logbackClassic) "compileOnly"(libs.bundles.netty) + + includeInJar(projects.node.nodeApi) + includeInJar(projects.utils.utilsBase) + includeInJar(projects.driver.driverApi) + includeInJar(projects.driver.driverImpl) + + wrapperJar(projects.wrapperJvm.wrapperJvmImpl) { targetConfiguration = "shadow" } } applyJarMetadata("eu.cloudnetservice.node.impl.boot.Bootstrap", "eu.cloudnetservice.node") diff --git a/plugins/build.gradle.kts b/plugins/build.gradle.kts index 8eae5de66c..d2d9f18709 100644 --- a/plugins/build.gradle.kts +++ b/plugins/build.gradle.kts @@ -13,38 +13,3 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import org.apache.tools.ant.filters.ReplaceTokens - -subprojects { - 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"(rootProject.libs.guava) - - // generation for platform main classes - "compileOnly"(rootProject.projects.ext.platformInjectSupport.platformInjectApi) - "annotationProcessor"(rootProject.projects.ext.platformInjectSupport.platformInjectProcessor) - } - - tasks.create("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("compileJava") { - dependsOn(tasks.getByName("processSources")) - source = tasks.getByName("processSources").outputs.files.asFileTree - } -} diff --git a/plugins/luckperms/build.gradle.kts b/plugins/luckperms/build.gradle.kts index 2cd4b073b5..60908e3634 100644 --- a/plugins/luckperms/build.gradle.kts +++ b/plugins/luckperms/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { "mappings"(loom.officialMojangMappings()) } -tasks.withType { +tasks.remapJar.configure { // base setup archiveFileName.set(Files.luckPermsPlugin) } diff --git a/plugins/papi-expansion/build.gradle.kts b/plugins/papi-expansion/build.gradle.kts index 65cae4941e..9df39b2368 100644 --- a/plugins/papi-expansion/build.gradle.kts +++ b/plugins/papi-expansion/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ -tasks.withType { +tasks.jar.configure { archiveFileName.set(Files.papiExpansion) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 0a4e5ce95f..228fbac935 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,12 +21,29 @@ pluginManagement { includeBuild("build-extensions") repositories { gradlePluginPortal() +// maven("https://repo.stellardrift.ca/repository/snapshots/") { + mavenLocal { + content { + includeGroup("net.kyori") + } + } + mavenLocal { + content { + includeGroup("eu.cloudnetservice.juppiter") + includeGroup("eu.cloudnetservice.gradle") + } + } maven { name = "Fabric" url = uri("https://maven.fabricmc.net/") } } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" + id("com.gradle.develocity") version ("4.1") + id("settings-plugin") +} rootProject.name = "cloudnet-root" @@ -89,9 +106,7 @@ initializePrefixedSubProjects("modules:dockerized-services", "dockerized-service fun initializeSubProjects(rootProject: String, vararg names: String) { names.forEach { include("$rootProject:$it") - // update the project properties project(":$rootProject:$it").name = it - project(":$rootProject:$it").projectDir = file(rootProject).resolve(it) } } @@ -101,3 +116,31 @@ fun initializePrefixedSubProjects(rootProject: String, prefix: String, vararg na project(":$rootProject:$it").name = "$prefix-$it" } } + +val isCI = System.getenv("CI") != null + +develocity { + buildScan { + // auto publishing build scans can be enabled by creating a file + // named "gradle.tos.agree" in the root directory with the ToS URL + // as content. + val file = rootDir.resolve("gradle.tos.agree") + val agree = if (file.exists() && file.readText() == "https://gradle.com/help/legal-terms-of-use") "yes" else "" + termsOfUseAgree = agree + termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" + gradle.settingsEvaluated { + // IntelliJ overrides this in windows using an init script %TEMP%/ejNoScan1.gradle + // We agree with the file, so we override IntelliJ again. + termsOfUseAgree = agree + } + publishing.onlyIf { termsOfUseAgree.get() == "yes" } + + uploadInBackground = !isCI + obfuscation { + ipAddresses { listOf("0.0.0.0") } + hostname { "removed" } + externalProcessName { "hidden-process-name" } + username { "hidden-username" } + } + } +} diff --git a/wrapper-jvm/impl/build.gradle.kts b/wrapper-jvm/impl/build.gradle.kts index c595aec4dd..37fa08ec26 100644 --- a/wrapper-jvm/impl/build.gradle.kts +++ b/wrapper-jvm/impl/build.gradle.kts @@ -4,17 +4,26 @@ plugins { alias(libs.plugins.shadow) } -tasks.withType { +val ignoredGroupIds = listOf("com.google.guava", "com.google.code.gson") +val exportCnlFile = tasks.register("exportCnlFile") { + fileName = "wrapper.cnl" + ignoredDependencyGroups = ignoredGroupIds + setResolvedArtifacts(configurations.runtimeClasspath.get()) +} +val exportLanguageFileInformation = tasks.register("exportLanguageFileInformation") { + languageFiles.from(project.projectDir.resolve("src/main/resources/lang").listFiles()) +} +tasks.shadowJar.configure { archiveFileName.set(Files.wrapper) - archiveVersion.set(null as String?) // do not shade dependencies which we don't need to shade - val ignoredGroupIds = arrayOf("com.google.guava", "com.google.code.gson") + dependencies { exclude { it.moduleGroup != rootProject.group && !ignoredGroupIds.contains(it.moduleGroup) } } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // google lib relocation relocate("com.google.gson", "eu.cloudnetservice.relocate.gson") @@ -23,14 +32,11 @@ tasks.withType { // drop unused classes which are making the jar bigger minimize() - doFirst { - // Note: included dependencies will not be resolved, they must be available from the node resolution already - from(exportLanguageFileInformation()) - from(exportCnlFile("wrapper.cnl", ignoredGroupIds)) - } + from(exportLanguageFileInformation) + from(exportCnlFile) } -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("-AaerogelAutoFileName=autoconfigure/wrapper.aero") }