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.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.YAMLGenerator
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
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.Nested
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.property
import java.io.File
import java.io.Serializable
open class GeneratePluginDescription : DefaultTask() {
@Input
var fileName: String? = null
val fileName: Property<String> = project.objects.property()
@Input
var pluginDescription: Serializable? = null
@Nested
val pluginDescription: Property<PluginDescription> = project.objects.property()
val outputFile: File
@OutputFile get() = File(temporaryDir, fileName)
val outputFile: Provider<File>
@OutputFile get() = fileName.map { File(temporaryDir, it) }
@TaskAction
fun generate() {
@ -54,11 +62,24 @@ open class GeneratePluginDescription : DefaultTask() {
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
.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)
.registerKotlinModule()
.registerModule(module)
.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.register
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
@ -46,11 +45,14 @@ abstract class PlatformPlugin<T : Serializable>(private val platformName: String
// Create task
val generateTask = tasks.register<GeneratePluginDescription>("generate${platformName}PluginDescription") {
fileName = this@PlatformPlugin.fileName
pluginDescription = description
fileName.set(this@PlatformPlugin.fileName)
pluginDescription.set(provider {
setDefaults(project, description)
description
})
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 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.JsonProperty
import groovy.lang.Closure
import net.minecrell.pluginyml.PluginDescription
import org.gradle.api.NamedDomainObjectContainer
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
var name: String? = null
var version: String? = null
var main: String? = null
var description: String? = null
var load: PluginLoadOrder? = null
var author: String? = null
var authors: List<String>? = null
var website: String? = null
var depend: List<String>? = null
@JsonProperty("softdepend") var softDepend: List<String>? = null
@JsonProperty("loadbefore") var loadBefore: List<String>? = null
var prefix: String? = null
@JsonProperty("default-permission") var defaultPermission: Permission.Default? = null
var provides: List<String>? = null
@Input @Optional @JsonProperty("api-version") var apiVersion: String? = null
@Input var name: String? = null
@Input var version: String? = null
@Input var main: String? = null
@Input @Optional var description: String? = null
@Input @Optional var load: PluginLoadOrder? = null
@Input @Optional var author: String? = null
@Input @Optional var authors: List<String>? = null
@Input @Optional var website: String? = null
@Input @Optional var depend: List<String>? = null
@Input @Optional @JsonProperty("softdepend") var softDepend: List<String>? = null
@Input @Optional @JsonProperty("loadbefore") var loadBefore: List<String>? = null
@Input @Optional var prefix: String? = null
@Input @Optional @JsonProperty("default-permission") var defaultPermission: Permission.Default? = null
@Input @Optional var provides: List<String>? = null
// DSL provider for commands and permissions (not serialized)
@Transient @JsonIgnore val commands: NamedDomainObjectContainer<Command> = project.container(Command::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 }
@Nested val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java)
@Nested val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java)
// For Groovy DSL
fun commands(closure: Closure<Unit>) = commands.configure(closure)
@ -68,23 +65,23 @@ class BukkitPluginDescription(project: Project) : Serializable {
POSTWORLD
}
data class Command(@Transient @JsonIgnore val name: String) : Serializable {
var description: String? = null
var aliases: List<String>? = null
var permission: String? = null
@JsonProperty("permission-message") var permissionMessage: String? = null
var usage: String? = null
data class Command(@Input @JsonIgnore val name: String) {
@Input @Optional var description: String? = null
@Input @Optional var aliases: List<String>? = null
@Input @Optional var permission: String? = null
@Input @Optional @JsonProperty("permission-message") var permissionMessage: String? = null
@Input @Optional var usage: String? = null
}
data class Permission(@Transient @JsonIgnore val name: String) : Serializable {
var description: String? = null
var default: Default? = null
data class Permission(@Input @JsonIgnore val name: String) {
@Input @Optional var description: String? = null
@Input @Optional var default: Default? = null
var children: List<String>?
@JsonIgnore get() = childrenMap?.filterValues { it }?.keys?.toList()
@Internal @JsonIgnore get() = childrenMap?.filterValues { it }?.keys?.toList()
set(value) {
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 {
@JsonProperty("true") TRUE,

View File

@ -24,14 +24,16 @@
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 {
var name: String? = null
var main: String? = null
var version: String? = null
var author: String? = null
var depends: Set<String>? = null
var softDepends: Set<String>? = null
var description: String? = null
class BungeePluginDescription : PluginDescription {
@Input var name: String? = null
@Input var main: String? = null
@Input @Optional var version: String? = null
@Input @Optional var author: String? = null
@Input @Optional var depends: Set<String>? = null
@Input @Optional var softDepends: Set<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.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) {
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.JsonProperty
import groovy.lang.Closure
import net.minecrell.pluginyml.PluginDescription
import org.gradle.api.NamedDomainObjectContainer
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
var main: String? = null
var version: String? = null
var api: List<String>? = null
var description: String? = null
var load: PluginLoadOrder? = null
var author: String? = null
var authors: List<String>? = null
var website: String? = null
var depend: List<String>? = null
@JsonProperty("softdepend") var softDepend: List<String>? = null
@JsonProperty("loadbefore") var loadBefore: List<String>? = null
var prefix: String? = null
@Input var name: String? = null
@Input var main: String? = null
@Input var version: String? = null
@Input var api: List<String>? = null
@Input @Optional var description: String? = null
@Input @Optional var load: PluginLoadOrder? = null
@Input @Optional var author: String? = null
@Input @Optional var authors: List<String>? = null
@Input @Optional var website: String? = null
@Input @Optional var depend: List<String>? = null
@Input @Optional @JsonProperty("softdepend") var softDepend: List<String>? = null
@Input @Optional @JsonProperty("loadbefore") var loadBefore: List<String>? = null
@Input @Optional var prefix: String? = null
// DSL provider for commands and permissions (not serialized)
@Transient @JsonIgnore
val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java)
@Transient @JsonIgnore
val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
@Nested val commands: NamedDomainObjectContainer<Command> = project.container(Command::class.java)
@Nested val permissions: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
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
fun commands(closure: Closure<Unit>) = commands.configure(closure)
fun permissions(closure: Closure<Unit>) = permissions.configure(closure)
@ -70,29 +65,23 @@ class NukkitPluginDescription(project: Project) : Serializable {
POSTWORLD
}
data class Command(@Transient @JsonIgnore val name: String) : Serializable {
var description: String? = null
var aliases: List<String>? = null
var permission: String? = null
@JsonProperty("permission-message") var permissionMessage: String? = null
var usage: String? = null
data class Command(@Input @JsonIgnore val name: String) {
@Input @Optional var description: String? = null
@Input @Optional var aliases: List<String>? = null
@Input @Optional var permission: String? = null
@Input @Optional @JsonProperty("permission-message") var permissionMessage: 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
var default: Default? = null
@Input @Optional var description: String? = null
@Input @Optional var default: Default? = null
// DSL provider for recursive children (not serialized)
@Transient @JsonIgnore
val children: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
@Nested val children: NamedDomainObjectContainer<Permission> = project.container(Permission::class.java) {
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
fun children(closure: Closure<Unit>) = children.configure(closure)