Fixup incremental builds

Get rid of all the Serializable mess and instead use @ Nested to
tell Gradle to look at the plugin descriptions directly.

This means that we need some annotation mess inside the plugin
description classes though. :D
This commit is contained in:
Minecrell 2021-04-30 18:43:17 +02:00
parent 5fff9cf695
commit 886351c1ae
7 changed files with 139 additions and 106 deletions

View file

@ -26,26 +26,34 @@ package net.minecrell.pluginyml
import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer
import com.fasterxml.jackson.databind.util.Converter
import com.fasterxml.jackson.databind.util.StdConverter
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
import com.fasterxml.jackson.module.kotlin.registerKotlinModule import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
import java.io.File import java.io.File
import java.io.Serializable
open class GeneratePluginDescription : DefaultTask() { open class GeneratePluginDescription : DefaultTask() {
@Input @Input
var fileName: String? = null val fileName: Property<String> = project.objects.property()
@Input @Nested
var pluginDescription: Serializable? = null val pluginDescription: Property<PluginDescription> = project.objects.property()
val outputFile: File val outputFile: Provider<File>
@OutputFile get() = File(temporaryDir, fileName) @OutputFile get() = fileName.map { File(temporaryDir, it) }
@TaskAction @TaskAction
fun generate() { fun generate() {
@ -54,11 +62,24 @@ open class GeneratePluginDescription : DefaultTask() {
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES) .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
.enable(YAMLGenerator.Feature.INDENT_ARRAYS) .enable(YAMLGenerator.Feature.INDENT_ARRAYS)
val module = SimpleModule()
@Suppress("UNCHECKED_CAST") // Too stupid to figure out the generics here...
module.addSerializer(StdDelegatingSerializer(NamedDomainObjectCollection::class.java,
NamedDomainObjectCollectionConverter as Converter<NamedDomainObjectCollection<*>, *>))
val mapper = ObjectMapper(factory) val mapper = ObjectMapper(factory)
.registerKotlinModule() .registerKotlinModule()
.registerModule(module)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY) .setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
mapper.writeValue(outputFile, pluginDescription) mapper.writeValue(outputFile.get(), pluginDescription.get())
}
object NamedDomainObjectCollectionConverter : StdConverter<NamedDomainObjectCollection<Any>, Map<String, Any>>() {
override fun convert(value: NamedDomainObjectCollection<Any>): Map<String, Any> {
val namer = value.namer
return value.associateBy { namer.determineName(it) }
}
} }
} }

View file

@ -31,9 +31,8 @@ import org.gradle.api.tasks.AbstractCopyTask
import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.withType
import java.io.Serializable
abstract class PlatformPlugin<T : Serializable>(private val platformName: String, private val fileName: String) : Plugin<Project> { abstract class PlatformPlugin<T : PluginDescription>(private val platformName: String, private val fileName: String) : Plugin<Project> {
protected abstract fun createExtension(project: Project): T protected abstract fun createExtension(project: Project): T
@ -46,11 +45,14 @@ abstract class PlatformPlugin<T : Serializable>(private val platformName: String
// Create task // Create task
val generateTask = tasks.register<GeneratePluginDescription>("generate${platformName}PluginDescription") { val generateTask = tasks.register<GeneratePluginDescription>("generate${platformName}PluginDescription") {
fileName = this@PlatformPlugin.fileName fileName.set(this@PlatformPlugin.fileName)
pluginDescription = description pluginDescription.set(provider {
setDefaults(project, description)
description
})
doFirst { doFirst {
prepare(project, description) validate(description)
} }
} }
@ -62,11 +64,6 @@ abstract class PlatformPlugin<T : Serializable>(private val platformName: String
} }
} }
private fun prepare(project: Project, description: T) {
setDefaults(project, description)
validate(description)
}
protected abstract fun setDefaults(project: Project, description: T) protected abstract fun setDefaults(project: Project, description: T)
protected abstract fun validate(description: T) protected abstract fun validate(description: T)

View file

@ -0,0 +1,27 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Minecrell <https://github.com/Minecrell>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package net.minecrell.pluginyml
interface PluginDescription

View file

@ -27,37 +27,34 @@ package net.minecrell.pluginyml.bukkit
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import groovy.lang.Closure import groovy.lang.Closure
import net.minecrell.pluginyml.PluginDescription
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import java.io.Serializable import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.Optional
class BukkitPluginDescription(project: Project) : Serializable { class BukkitPluginDescription(project: Project) : PluginDescription {
@JsonProperty("api-version") var apiVersion: String? = null @Input @Optional @JsonProperty("api-version") var apiVersion: String? = null
var name: String? = null @Input var name: String? = null
var version: String? = null @Input var version: String? = null
var main: String? = null @Input var main: String? = null
var description: String? = null @Input @Optional var description: String? = null
var load: PluginLoadOrder? = null @Input @Optional var load: PluginLoadOrder? = null
var author: String? = null @Input @Optional var author: String? = null
var authors: List<String>? = null @Input @Optional var authors: List<String>? = null
var website: String? = null @Input @Optional var website: String? = null
var depend: List<String>? = null @Input @Optional var depend: List<String>? = null
@JsonProperty("softdepend") var softDepend: List<String>? = null @Input @Optional @JsonProperty("softdepend") var softDepend: List<String>? = null
@JsonProperty("loadbefore") var loadBefore: List<String>? = null @Input @Optional @JsonProperty("loadbefore") var loadBefore: List<String>? = null
var prefix: String? = null @Input @Optional var prefix: String? = null
@JsonProperty("default-permission") var defaultPermission: Permission.Default? = null @Input @Optional @JsonProperty("default-permission") var defaultPermission: Permission.Default? = null
var provides: List<String>? = null @Input @Optional var provides: List<String>? = null
// DSL provider for commands and permissions (not serialized) @Nested val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java)
@Transient @JsonIgnore val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java) @Nested val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java)
@Transient @JsonIgnore val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java)
// Java/Jackson serialization for commands and permissions
internal val commandMap: Map<String, Command>
@JsonProperty("commands") get() = commands.associateBy { it.name }
internal val permissionMap: Map<String, Permission>
@JsonProperty("permissions") get() = permissions.associateBy { it.name }
// For Groovy DSL // For Groovy DSL
fun commands(closure: Closure<Unit>) = commands.configure(closure) fun commands(closure: Closure<Unit>) = commands.configure(closure)
@ -68,23 +65,23 @@ class BukkitPluginDescription(project: Project) : Serializable {
POSTWORLD POSTWORLD
} }
data class Command(@Transient @JsonIgnore val name: String) : Serializable { data class Command(@Input @JsonIgnore val name: String) {
var description: String? = null @Input @Optional var description: String? = null
var aliases: List<String>? = null @Input @Optional var aliases: List<String>? = null
var permission: String? = null @Input @Optional var permission: String? = null
@JsonProperty("permission-message") var permissionMessage: String? = null @Input @Optional @JsonProperty("permission-message") var permissionMessage: String? = null
var usage: String? = null @Input @Optional var usage: String? = null
} }
data class Permission(@Transient @JsonIgnore val name: String) : Serializable { data class Permission(@Input @JsonIgnore val name: String) {
var description: String? = null @Input @Optional var description: String? = null
var default: Default? = null @Input @Optional var default: Default? = null
var children: List<String>? var children: List<String>?
@JsonIgnore get() = childrenMap?.filterValues { it }?.keys?.toList() @Internal @JsonIgnore get() = childrenMap?.filterValues { it }?.keys?.toList()
set(value) { set(value) {
childrenMap = value?.associateWith { true } childrenMap = value?.associateWith { true }
} }
@JsonProperty("children") var childrenMap: Map<String, Boolean>? = null @Input @Optional @JsonProperty("children") var childrenMap: Map<String, Boolean>? = null
enum class Default { enum class Default {
@JsonProperty("true") TRUE, @JsonProperty("true") TRUE,

View file

@ -24,14 +24,16 @@
package net.minecrell.pluginyml.bungee package net.minecrell.pluginyml.bungee
import java.io.Serializable import net.minecrell.pluginyml.PluginDescription
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
class BungeePluginDescription : Serializable { class BungeePluginDescription : PluginDescription {
var name: String? = null @Input var name: String? = null
var main: String? = null @Input var main: String? = null
var version: String? = null @Input @Optional var version: String? = null
var author: String? = null @Input @Optional var author: String? = null
var depends: Set<String>? = null @Input @Optional var depends: Set<String>? = null
var softDepends: Set<String>? = null @Input @Optional var softDepends: Set<String>? = null
var description: String? = null @Input @Optional var description: String? = null
} }

View file

@ -47,7 +47,7 @@ class NukkitPlugin : PlatformPlugin<NukkitPluginDescription>("Nukkit", "nukkit.y
if (main.isEmpty()) throw InvalidPluginDescriptionException("Main class cannot be empty") if (main.isEmpty()) throw InvalidPluginDescriptionException("Main class cannot be empty")
if (main.startsWith("cn.nukkit.")) throw InvalidPluginDescriptionException("Main class cannot be within cn.nukkit. package") if (main.startsWith("cn.nukkit.")) throw InvalidPluginDescriptionException("Main class cannot be within cn.nukkit. package")
if (description.api?.isEmpty() != false) throw InvalidPluginDescriptionException("Nukkit API version is not set") if (description.api.isNullOrEmpty()) throw InvalidPluginDescriptionException("Nukkit API version is not set")
for (command in description.commands) { for (command in description.commands) {
if (command.name.contains(':')) throw InvalidPluginDescriptionException("Command '${command.name}' cannot contain ':'") if (command.name.contains(':')) throw InvalidPluginDescriptionException("Command '${command.name}' cannot contain ':'")

View file

@ -27,40 +27,35 @@ package net.minecrell.pluginyml.nukkit
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import groovy.lang.Closure import groovy.lang.Closure
import net.minecrell.pluginyml.PluginDescription
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import java.io.Serializable import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.Optional
class NukkitPluginDescription(project: Project) : Serializable { class NukkitPluginDescription(project: Project) : PluginDescription {
var name: String? = null @Input var name: String? = null
var main: String? = null @Input var main: String? = null
var version: String? = null @Input var version: String? = null
var api: List<String>? = null @Input var api: List<String>? = null
var description: String? = null @Input @Optional var description: String? = null
var load: PluginLoadOrder? = null @Input @Optional var load: PluginLoadOrder? = null
var author: String? = null @Input @Optional var author: String? = null
var authors: List<String>? = null @Input @Optional var authors: List<String>? = null
var website: String? = null @Input @Optional var website: String? = null
var depend: List<String>? = null @Input @Optional var depend: List<String>? = null
@JsonProperty("softdepend") var softDepend: List<String>? = null @Input @Optional @JsonProperty("softdepend") var softDepend: List<String>? = null
@JsonProperty("loadbefore") var loadBefore: List<String>? = null @Input @Optional @JsonProperty("loadbefore") var loadBefore: List<String>? = null
var prefix: String? = null @Input @Optional var prefix: String? = null
// DSL provider for commands and permissions (not serialized) @Nested val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java)
@Transient @JsonIgnore @Nested val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java)
@Transient @JsonIgnore
val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
Permission(project, it) Permission(project, it)
} }
// Java/Jackson serialization for commands and permissions
internal val commandMap: Map<String, Command>
@JsonProperty("commands") get() = commands.associateBy { it.name }
internal val permissionMap: Map<String, Permission>
@JsonProperty("permissions") get() = permissions.associateBy { it.name }
// For Groovy DSL // For Groovy DSL
fun commands(closure: Closure<Unit>) = commands.configure(closure) fun commands(closure: Closure<Unit>) = commands.configure(closure)
fun permissions(closure: Closure<Unit>) = permissions.configure(closure) fun permissions(closure: Closure<Unit>) = permissions.configure(closure)
@ -70,29 +65,23 @@ class NukkitPluginDescription(project: Project) : Serializable {
POSTWORLD POSTWORLD
} }
data class Command(@Transient @JsonIgnore val name: String) : Serializable { data class Command(@Input @JsonIgnore val name: String) {
var description: String? = null @Input @Optional var description: String? = null
var aliases: List<String>? = null @Input @Optional var aliases: List<String>? = null
var permission: String? = null @Input @Optional var permission: String? = null
@JsonProperty("permission-message") var permissionMessage: String? = null @Input @Optional @JsonProperty("permission-message") var permissionMessage: String? = null
var usage: String? = null @Input @Optional var usage: String? = null
} }
data class Permission(@Transient @JsonIgnore val project: Project, @Transient @JsonIgnore val name: String) : Serializable { data class Permission(@Internal @JsonIgnore val project: Project, @Input @JsonIgnore val name: String) {
var description: String? = null @Input @Optional var description: String? = null
var default: Default? = null @Input @Optional var default: Default? = null
// DSL provider for recursive children (not serialized) @Nested val children: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
@Transient @JsonIgnore
val children: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
Permission(project, it) Permission(project, it)
} }
// Java/Jackson serialization for commands and permissions
internal val childrenMap: Map<String, Permission>
@JsonProperty("children") get() = children.associateBy { it.name }
// For Groovy DSL // For Groovy DSL
fun children(closure: Closure<Unit>) = children.configure(closure) fun children(closure: Closure<Unit>) = children.configure(closure)