Skip to content

Commit

Permalink
Jars flattening task now flattens to the single jar instead of direct…
Browse files Browse the repository at this point in the history
…ory.
  • Loading branch information
badmannersteam committed Mar 12, 2024
1 parent 3da362a commit 7fe647f
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -458,22 +458,22 @@ private fun JvmApplicationContext.configureFlattenJars(
) {
if (runProguard != null) {
flattenJars.dependsOn(runProguard)
flattenJars.inputFiles.from(project.fileTree(runProguard.flatMap { it.destinationDir }))
flattenJars.inputFiles.from(runProguard.flatMap { it.destinationDir })
} else {
flattenJars.useAppRuntimeFiles { (runtimeJars, _) ->
inputFiles.from(runtimeJars)
}
}

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

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

app.mainClass?.let { jar.manifest.attributes["Main-Class"] = it }
jar.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,81 @@
package org.jetbrains.compose.desktop.tasks

import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.compose.internal.utils.clearDirs
import org.jetbrains.compose.desktop.application.internal.files.copyZipEntry
import org.jetbrains.compose.desktop.application.internal.files.isJarFile
import org.jetbrains.compose.internal.utils.delete
import org.jetbrains.compose.internal.utils.ioFile
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream


/**
* This task flattens all jars from the input directory into the single one,
* which is used later as a single source for uberjar.
*
* This task is necessary because the standard Jar/Zip task evaluates own `from()` args eagerly
* [in the configuration phase](https://discuss.gradle.org/t/why-is-the-closure-in-from-method-of-copy-task-evaluated-in-config-phase/23469/4)
* and snapshots an empty list of files in the Proguard destination directory,
* instead of a list of real jars after Proguard task execution.
*
* Also, we use output to the single jar instead of flattening to the directory in the filesystem because:
* - Windows filesystem is case-sensitive and not every jar can be unzipped without losing files
* - it's just faster
*/
abstract class AbstractJarsFlattenTask : AbstractComposeDesktopTask() {

@get:InputFiles
val inputFiles: ConfigurableFileCollection = objects.fileCollection()

@get:OutputDirectory
val destinationDir: DirectoryProperty = objects.directoryProperty()
@get:OutputFile
val flattenedJar: RegularFileProperty = objects.fileProperty()

@get:Internal
val entries = hashSetOf<String>()

@TaskAction
fun execute() {
fileOperations.clearDirs(destinationDir)
entries.clear()
fileOperations.delete(flattenedJar)

fileOperations.copy {
it.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
it.from(flattenJars(inputFiles))
it.into(destinationDir)
ZipOutputStream(FileOutputStream(flattenedJar.ioFile).buffered()).use { outputStream ->
inputFiles.asFileTree.visit {
when {
!it.isDirectory && it.file.isJarFile -> outputStream.writeJarContent(it.file)
!it.isDirectory -> outputStream.writeFile(it.file)
}
}
}
}

private fun flattenJars(files: FileCollection) = files.map {
when {
it.isZipOrJar() -> this.archiveOperations.zipTree(it)
else -> it
private fun ZipOutputStream.writeJarContent(jarFile: File) =
ZipInputStream(FileInputStream(jarFile)).use { inputStream ->
var inputEntry: ZipEntry? = inputStream.nextEntry
while (inputEntry != null) {
writeEntryIfNotSeen(inputEntry, inputStream)
inputEntry = inputStream.nextEntry
}
}
}

private fun File.isZipOrJar() = name.endsWith(".jar", ignoreCase = true) || name.endsWith(".zip", ignoreCase = true)
private fun ZipOutputStream.writeFile(file: File) =
FileInputStream(file).use { inputStream ->
writeEntryIfNotSeen(ZipEntry(file.name), inputStream)
}

private fun ZipOutputStream.writeEntryIfNotSeen(entry: ZipEntry, inputStream: InputStream) {
if (entry.name !in entries) {
copyZipEntry(entry, inputStream, this)
entries += entry.name
}
}
}

0 comments on commit 7fe647f

Please sign in to comment.