Browse Source
## Proposed changes 1. Added support to join JARs to the uber JAR with ProGuard, disabled by default: ``` compose.desktop { application { buildTypes.release.proguard { joinOutputJars.set(true) } } } ``` 2. All 'release' tasks now really depend on ProGuard, as stated in [tutorial](https://github.com/JetBrains/compose-multiplatform/tree/master/tutorials/Native_distributions_and_local_execution#minification--obfuscation). ## Testing - A new auto test - Manual: 1. Test on Windows/macOs/Linux 2. Test the new Gradle parameter `joinOutputJars`: ``` compose.desktop { application { buildTypes.release.proguard { joinOutputJars.set(true) } } } ``` `false` (by default) should generate multiple jars (except for `package*UberJarForCurrentOS`) `true` should generate a single jar in a result distribution 3. Test debug tasks: ``` run runDistributable createDistributable packageUberJarForCurrentOS ``` 4. Test release tasks: ``` runRelease runReleaseDistributable createReleaseDistributable packageReleaseUberJarForCurrentOS ``` The jars should be reduced in size (because Proguard is enabled in the release mode) This should be test by QA. ## Issues fixed Fixes https://github.com/JetBrains/compose-multiplatform/issues/4129 --------- Co-authored-by: Igor Demin <igordmn@users.noreply.github.com>pull/4628/head v1.6.10-dev1580
badmannersteam
7 months ago
committed by
GitHub
7 changed files with 275 additions and 47 deletions
@ -0,0 +1,86 @@
|
||||
/* |
||||
* Copyright 2020-2024 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
package org.jetbrains.compose.desktop.tasks |
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection |
||||
import org.gradle.api.file.RegularFileProperty |
||||
import org.gradle.api.tasks.InputFiles |
||||
import org.gradle.api.tasks.Internal |
||||
import org.gradle.api.tasks.OutputFile |
||||
import org.gradle.api.tasks.TaskAction |
||||
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-insensitive 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:OutputFile |
||||
val flattenedJar: RegularFileProperty = objects.fileProperty() |
||||
|
||||
@get:Internal |
||||
val seenEntryNames = hashSetOf<String>() |
||||
|
||||
@TaskAction |
||||
fun execute() { |
||||
seenEntryNames.clear() |
||||
fileOperations.delete(flattenedJar) |
||||
|
||||
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 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 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 seenEntryNames) { |
||||
copyZipEntry(entry, inputStream, this) |
||||
seenEntryNames += entry.name |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue