Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Atlas texture generation for *.atlas folder/files, fixes catalog generation and resource processing on all the targets & adds mechanism to add a custom resource processors in the build.gradle #1565

Merged
merged 11 commits into from
May 2, 2023
11 changes: 8 additions & 3 deletions buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import korlibs.korge.gradle.util.*
import org.gradle.api.*
import java.io.*
import groovy.text.*
import korlibs.korge.gradle.processor.*
import org.gradle.api.artifacts.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import java.net.*
Expand Down Expand Up @@ -297,6 +298,12 @@ open class KorgeExtension(
androidGradleDependency("org.jetbrains.kotlinx:kotlinx-serialization-json:${BuildVersions.KOTLIN_SERIALIZATION}")
}

val resourceProcessors = arrayListOf<KorgeResourceProcessor>()

fun addResourceProcessor(processor: KorgeResourceProcessor) {
resourceProcessors += processor
}

//val bundles = KorgeBundles(project)

//@JvmOverloads
Expand Down Expand Up @@ -376,8 +383,6 @@ open class KorgeExtension(
val newJsEnabled get() = project.findProperty("korge.enable.js") == "true" || System.getenv("KORGE_ENABLE_JS") == "true"

var searchResourceProcessorsInMainSourceSet: Boolean = false
var enableKorgeResourceProcessing: Boolean = true
//var enableKorgeResourceProcessing: Boolean = false

var icon: File? = project.projectDir["icon.png"]
var banner: File? = project.projectDir["banner.png"]
Expand Down Expand Up @@ -466,7 +471,7 @@ open class KorgeExtension(

internal val defaultPluginsClassLoader by lazy { plugins.classLoader }

var androidReleaseSignStoreFile: String = "korge.keystore"
var androidReleaseSignStoreFile: String = "build/korge.keystore"
var androidReleaseSignStorePassword: String = "password"
var androidReleaseSignKeyAlias: String = "korge"
var androidReleaseSignKeyPassword: String = "password"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ class KorgeGradleApply(val project: Project, val projectType: ProjectType) {

project.afterEvaluate {
project.configureDependencies()
if (korge.enableKorgeResourceProcessing) {
project.addGenResourcesTasks()
}
project.addGenResourcesTasks()
project.enableFeaturesOnAllTargets()
}
}
Expand Down
133 changes: 105 additions & 28 deletions buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeProcessResources.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package korlibs.korge.gradle

import korlibs.korge.gradle.processor.*
import korlibs.korge.gradle.targets.*
import korlibs.korge.gradle.targets.jvm.*
import korlibs.korge.gradle.util.*
Expand All @@ -23,6 +24,36 @@ fun Project.getCompilationKorgeProcessedResourcesFolder(
fun getKorgeProcessResourcesTaskName(targetName: String, compilationName: String): String =
"korgeProcessedResources${targetName.capitalize()}${compilationName.capitalize()}"

fun getProcessResourcesTaskName(targetName: String, compilationName: String): String =
"${targetName.decapitalize()}${if (compilationName == "main") "" else compilationName.capitalize()}ProcessResources"

fun Project.generateKorgeProcessedFromTask(task: ProcessResources?, taskName: String) {
val targetNameRaw = taskName.removeSuffix("ProcessResources")
val isTest = targetNameRaw.endsWith("Test")
val targetName = targetNameRaw.removeSuffix("Test")
val target = kotlin.targets.findByName(targetName) ?: return
val isJvm = targetName == "jvm"
val compilationName = if (isTest) "test" else "main"
val korgeGeneratedTaskName = getKorgeProcessResourcesTaskName(target.name, compilationName)
val korgeGeneratedTask = tasks.createThis<KorgeGenerateResourcesTask>(korgeGeneratedTaskName)
val korgeGeneratedFolder = getCompilationKorgeProcessedResourcesFolder(targetName, compilationName)
val compilation = target.compilations.findByName(compilationName)
val folders: List<FileCollection> = when {
compilation != null -> compilation.allKotlinSourceSets.map { it.resources.sourceDirectories }
else -> listOf(project.files(file("src/commonMain/resources"), file("src/${targetNameRaw}${compilationName.capitalize()}/resources")))
}

korgeGeneratedTask.korgeGeneratedFolder = korgeGeneratedFolder
korgeGeneratedTask.inputFolders = folders
korgeGeneratedTask.resourceProcessors = korge.resourceProcessors

if (task != null) {
task.from(korgeGeneratedFolder)
task.dependsOn(korgeGeneratedTask)
korgeGeneratedTask.addToCopySpec(task)
}
}

fun Project.addGenResourcesTasks() {
if (project.extensions.findByType(KotlinMultiplatformExtension::class.java) == null) return

Expand All @@ -42,6 +73,18 @@ fun Project.addGenResourcesTasks() {
}
}

tasks.createThis<Task>("listKorgePlugins") {
group = GROUP_KORGE_LIST
doLast {
//URLClassLoader(prepareResourceProcessingClasses.outputs.files.toList().map { it.toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader()).use { classLoader ->
println("KorgePlugins:")
for (item in (korge.resourceProcessors + KorgeResourceProcessor.getAll()).distinct()) {
println("- $item")
}
}
}

/*
tasks.createThis<Task>("listKorgePlugins") {
group = GROUP_KORGE_LIST
if (korge.searchResourceProcessorsInMainSourceSet) {
Expand All @@ -57,6 +100,7 @@ fun Project.addGenResourcesTasks() {
) { listOf(it) }
}
}
*/

afterEvaluate {
//for (target in kotlin.targets) {
Expand All @@ -66,25 +110,9 @@ fun Project.addGenResourcesTasks() {
// }
//}


for (task in tasks.withType(ProcessResources::class.java).toList()) {
val taskName = task.name
val targetNameRaw = taskName.removeSuffix("ProcessResources")
val isTest = targetNameRaw.endsWith("Test")
val targetName = targetNameRaw.removeSuffix("Test")
val target = kotlin.targets.findByName(targetName) ?: continue
val isJvm = targetName == "jvm"
val compilationName = if (isTest) "test" else "main"
val compilation = target.compilations[compilationName]
val korgeGeneratedTaskName = getKorgeProcessResourcesTaskName(target.name, compilation.name)
val korgeGeneratedTask = tasks.createThis<KorgeCatalogJsonTask>(korgeGeneratedTaskName)
val korgeGeneratedFolder = getCompilationKorgeProcessedResourcesFolder(targetName, compilationName)
val folders = compilation.allKotlinSourceSets.map { it.resources.sourceDirectories }

korgeGeneratedTask.korgeGeneratedFolder = korgeGeneratedFolder
korgeGeneratedTask.inputFolders = folders

task.from(korgeGeneratedFolder)
task.dependsOn(korgeGeneratedTask)
generateKorgeProcessedFromTask(task, task.name)
}
}

Expand Down Expand Up @@ -191,7 +219,7 @@ fun Project.addGenResourcesTasks() {
*/
}

open class KorgeCatalogJsonTask @Inject constructor(
open class KorgeGenerateResourcesTask @Inject constructor(
//private val fs: FileSystemOperations,
) : DefaultTask() {
@get:OutputDirectory
Expand All @@ -200,21 +228,69 @@ open class KorgeCatalogJsonTask @Inject constructor(
@get:InputFiles
lateinit var inputFolders: List<FileCollection>

//@get:Input
@Internal
lateinit var resourceProcessors: List<KorgeResourceProcessor>

@get:OutputDirectories
var skippedFiles: Set<String> = setOf()

fun addToCopySpec(copy: CopySpec, addFrom: Boolean = true) {
if (addFrom) copy.from(korgeGeneratedFolder)

copy.exclude {
val relativeFile = File(it.relativePath.toString())
if (it.relativePath.startsWith('.')) return@exclude true
for (skippedFile in skippedFiles) {
//println("addExcludeToCopyTask: relativeFile=$relativeFile, skippedFile=$skippedFile")
if (relativeFile.startsWith(skippedFile)) {
//println("!! EXCLUDED")
return@exclude true
}
}
//val rfile = it.file.absolutePath
//for (skippedFile in skippedFiles) {
// println("it.file=${it.file}, rpath=${it.relativePath}, rfile=$rfile, skippedFile=${skippedFile}")
// if (rfile.startsWith(skippedFile.absolutePath)) {
// println("!! EXCLUDED")
// return@exclude true
// }
//}
false
}
}

@TaskAction
fun run() {
korgeGeneratedFolder.mkdirs()
val files = inputFolders.flatMap { it.toList() }.flatMap { it.listFiles()?.toList() ?: emptyList() }.distinct()
val map = LinkedHashMap<String, Any?>()
for (file in files) {
val fileName = if (file.isDirectory) "${file.name}/" else file.name
map[fileName] = listOf(file.length(), file.lastModified())
val resourcesFolders = inputFolders.flatMap { it.toList() }
val resourcesSubfolders = resourcesFolders.flatMap { base -> base.walk().filter { it.isDirectory }.map { it.relativeTo(base) } }.distinct()

for (folder in resourcesSubfolders) {
val korgeGeneratedSubFolder = korgeGeneratedFolder.resolve(folder)
korgeGeneratedSubFolder.mkdirs()
processFolder(korgeGeneratedSubFolder, resourcesFolders.mapNotNull { it.resolve(folder).takeIf { it.isDirectory } })
}
}

fun processFolder(generatedFolder: File, resourceFolders: List<File>) {
val context = KorgeResourceProcessorContext(logger, generatedFolder, resourceFolders)
try {
for (processor in (resourceProcessors + KorgeResourceProcessor.getAll()).distinct()) {
try {
processor.processFolder(context)
} catch (e: Throwable) {
e.printStackTrace()
}
}
} catch (e: Throwable) {
e.printStackTrace()
}
//println("-------- $folders")
//println("++++++++ $files")
korgeGeneratedFolder["\$catalog.json"].writeText(Json.stringify(map))
skippedFiles += context.skippedFiles
//println("processFolder.skippedFiles=$skippedFiles")
}
}

/*
data class KorgeProcessedResourcesTaskConfig(
val isJvm: Boolean,
val targetName: String,
Expand Down Expand Up @@ -262,3 +338,4 @@ open class KorgeProcessedResourcesTask @Inject constructor(
}
}
}
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package korlibs.korge.gradle.processor

import korlibs.korge.gradle.util.*

open class CatalogGenerator : KorgeResourceProcessor {
override fun processFolder(context: KorgeResourceProcessorContext) {
val map = LinkedHashMap<String, Any?>()
for (folder in (context.resourceFolders + context.generatedFolder)) {
for (file in (folder.listFiles()?.toList() ?: emptyList())) {
if (file.name == "\$catalog.json") continue
if (file.name.startsWith(".")) continue
val fileName = if (file.isDirectory) "${file.name}/" else file.name
map[fileName] = listOf(file.length(), file.lastModified())
}
}
//println("-------- $folders")
//println("++++++++ $files")
context.generatedFolder["\$catalog.json"].writeText(Json.stringify(map))
//println("generatedFolder: $generatedFolder")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package korlibs.korge.gradle.processor

import korlibs.korge.gradle.util.*
import org.gradle.api.file.RelativePath
import java.io.*
import java.util.*
import kotlin.collections.LinkedHashSet

data class KorgeResourceProcessorContext(
val logger: org.slf4j.Logger,
val generatedFolder: File,
val resourceFolders: List<File>,
) {
val skippedFiles = LinkedHashSet<String>()

/**
* Prevents copying that [files] or folder to the final executable
*/
fun skipFiles(vararg files: File) {
for (file in files) {
for (folder in resourceFolders) {
if (file.isDescendantOf(folder)) {
skippedFiles += file.relativeTo(folder).path + (if (file.isDirectory) "/" else "")
break
}
}
}
}
}

fun interface KorgeResourceProcessor {
fun processFolder(context: KorgeResourceProcessorContext)

//override fun toString(): String = "${this::class.qualifiedName}"

companion object {
fun getAll(): List<KorgeResourceProcessor> {
return (ServiceLoader.load(KorgeResourceProcessor::class.java).toList() + CatalogGenerator())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package korlibs.korge.gradle.processor

import korlibs.korge.gradle.texpacker.*
import korlibs.korge.gradle.util.*
import java.io.*
import kotlin.system.*

open class KorgeTexturePacker : KorgeResourceProcessor {
override fun processFolder(context: KorgeResourceProcessorContext) {
for (folder in context.resourceFolders) {
val files = folder.listFiles()?.toList() ?: emptyList()
for (file in files) {
if (file.name.endsWith(".atlas")) {
val atlasJsonFile = File(context.generatedFolder, file.nameWithoutExtension + ".atlas.json")
when {
file.isDirectory -> {
context.skipFiles(file)
generate(context.logger, atlasJsonFile, arrayOf(file))
}
file.isFile -> {
val sources = file.readLines().filter { it.isNotBlank() }.map { File(folder, it) }.toTypedArray()
context.skipFiles(file, *sources)
generate(context.logger, atlasJsonFile, sources)
}
}
}
}
}
}

fun generate(logger: org.slf4j.Logger, outputFile: File, imageFolders: Array<File>) {
val involvedFiles = NewTexturePacker.getAllFiles(*imageFolders)
//val maxLastModifiedTime = involvedFiles.maxOfOrNull { it.file.lastModified() } ?: System.currentTimeMillis()
val involvedString = involvedFiles.map { it.relative.name + ":" + it.file.length() + ":" + it.file.lastModified() }.sorted().joinToString("\n")
val involvedFile = File(outputFile.parentFile, "." + outputFile.name + ".info")

//if (!outputFile.exists() || involvedFile.takeIfExists()?.readText() != involvedString) {
if (involvedFile.takeIfExists()?.readText() != involvedString) {
val time = measureTimeMillis {
val results = NewTexturePacker.packImages(*imageFolders, enableRotation = true, enableTrimming = true)
for (result in results) {
val imageOut = result.write(outputFile)
}
involvedFile.writeText(involvedString)
}
//outputFile.setLastModified(maxLastModifiedTime)
//imageOut.setLastModified(maxLastModifiedTime)
logger.info("KorgeTexturePacker.GENERATED in ${time}ms: $involvedFile")
} else {
logger.info("KorgeTexturePacker.CACHED: $involvedFile")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ fun Project.configureAndroidDirect(projectType: ProjectType, isKorge: Boolean) {
"${project.projectDir}/src/androidMain/resources",
"${project.projectDir}/src/main/resources",
"${project.projectDir}/build/commonMain/korgeProcessedResources/metadata/main",
"${project.projectDir}/build/korgeProcessedResources/android/main",
)
//assets.srcDirs(*resourcesSrcDirs.map { it.absoluteFile }.toTypedArray())
//java.srcDirs(*kotlinSrcDirs.map { it.absoluteFile }.toTypedArray())
Expand Down
Loading