Repository: JetBrains/compose-multiplatform
File: gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt (Kotlin)

Code before:
        add("-D$APP_RESOURCES_DIR=${appResourcesDir.absolutePath}")
    }
    exec.args = app.args

    if (runProguard != null) {
        exec.dependsOn(runProguard)
        exec.classpath = project.fileTree(runProguard.flatMap { it.destinationDir })
    } else {
        exec.useAppRuntimeFiles { (runtimeJars, _) ->
            classpath = runtimeJars
        }
    }
}

private fun JvmApplicationContext.configurePackageUberJarForCurrentOS(
    jar: Jar,
    runProguard: Provider<AbstractProguardTask>?
) {
    fun flattenJars(files: FileCollection): FileCollection =
        jar.project.files({
            files.map { if (it.isZipOrJar()) jar.project.zipTree(it) else it }
        })

    if (runProguard != null) {
        jar.dependsOn(runProguard)
        jar.from(flattenJars(project.fileTree(runProguard.flatMap { it.destinationDir })))
    } else {
        jar.useAppRuntimeFiles { (runtimeJars, _) ->
            from(flattenJars(runtimeJars))
        }
    }

    app.mainClass?.let { jar.manifest.attributes["Main-Class"] = it }
    jar.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    jar.archiveAppendix.set(currentTarget.id)
    jar.archiveBaseName.set(packageNameProvider)
    jar.archiveVersion.set(packageVersionFor(TargetFormat.AppImage))
    jar.archiveClassifier.set(buildType.classifier)
    jar.destinationDirectory.set(jar.project.layout.buildDirectory.dir("compose/jars"))

    jar.doLast {
        jar.logger.lifecycle("The jar is written to ${jar.archiveFile.ioFile.canonicalPath}")
    }
}

private fun File.isZipOrJar() =
    name.endsWith(".jar", ignoreCase = true)
        || name.endsWith(".zip", ignoreCase = true)

Review comment (performance):
After some tests i've found that value, passed to the `from()` method, evaluates eagerly before the execution phase when there are no proguard processed files yet, so it remembers the empty files list.

I've tried to play with `fileTree()`, `zipTree()`, `files()` and closures for creating the lazy evaluated file tree of zip trees from files from `destinationDir`, but with no luck.

The only working solution that I found was this:
```kotlin
val fs: FileSystemOperations
val archive: ArchiveOperations

fun flattenJars(files: FileCollection) = files.map { if (it.isZipOrJar()) archive.zipTree(it) else it }

val proguardDestinationDir = jar.project.fileTree(runProguard.flatMap { it.destinationDir })
val uberjarFlattenedSourcesDir = appTmpDir.dir("uberjar")
jar.doFirst {
    fs.clearDirs(uberjarFlattenedSourcesDir)
    fs.copy {
        it.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        it.from(flattenJars(proguardDestinationDir))
        it.into(uberjarFlattenedSourcesDir)
    }
}
jar.from(uberjarFlattenedSourcesDir)
```
It works fine, but has some disadvantages:
- Uses temp directory for proguard processed jars unpacking and flattening instead of `zipTree`
- I don't know where to place and inject `FileSystemOperations` and `ArchiveOperations` better.
Usage of corresponding `Project` methods is forbidden because of gradle configuration cache.

Maybe you can suggest something?

To reproduce this you can check `packageReleaseUberJarForCurrentOSJvm` test.

Code after:
        if (currentOS == OS.MacOS) {
            val file = app.nativeDistributions.macOS.iconFile.ioFileOrNull
            if (file != null) add("-Xdock:icon=$file")
        }

        addAll(app.jvmArgs)
        val appResourcesDir = prepareAppResources.get().destinationDir
        add("-D$APP_RESOURCES_DIR=${appResourcesDir.absolutePath}")
    }
    exec.args = app.args

    if (runProguard != null) {
        exec.dependsOn(runProguard)
        exec.classpath = project.fileTree(runProguard.flatMap { it.destinationDir })
    } else {
        exec.useAppRuntimeFiles { (runtimeJars, _) ->
            classpath = runtimeJars
        }
    }
}

private fun JvmApplicationContext.configureFlattenJars(
    flattenJars: AbstractJarsFlattenTask,
    runProguard: Provider<AbstractProguardTask>?
) {
    if (runProguard != null) {
        flattenJars.dependsOn(runProguard)
        flattenJars.inputFiles.from(runProguard.flatMap { it.destinationDir })
    } else {
        flattenJars.useAppRuntimeFiles { (runtimeJars, _) ->
            inputFiles.from(runtimeJars)
        }
    }

    flattenJars.flattenedJar.set(appTmpDir.file("flattenJars/flattened.jar"))
}

private fun JvmApplicationContext.configurePackageUberJarForCurrentOS(
    jar: Jar,
    flattenJars: Provider<AbstractJarsFlattenTask>
) {
    jar.dependsOn(flattenJars)
    jar.from(project.zipTree(flattenJars.flatMap { it.flattenedJar }))

    app.mainClass?.let { jar.manifest.attributes["Main-Class"] = it }
    jar.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    jar.archiveAppendix.set(currentTarget.id)
    jar.archiveBaseName.set(packageNameProvider)
    jar.archiveVersion.set(packageVersionFor(TargetFormat.AppImage))
    jar.archiveClassifier.set(buildType.classifier)
    jar.destinationDirectory.set(jar.project.layout.buildDirectory.dir("compose/jars"))