Skip to content

Commit

Permalink
Add support for adding custom resources for jpackage
Browse files Browse the repository at this point in the history
  • Loading branch information
sebkur committed Sep 20, 2022
1 parent 685f59f commit d6836d1
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ abstract class AbstractDistributions {
var description: String? = null
var vendor: String? = null
val appResourcesRootDir: DirectoryProperty = objects.directoryProperty()
val jpackageResourcesRootDir: DirectoryProperty = objects.directoryProperty()
val licenseFile: RegularFileProperty = objects.fileProperty()

var targetFormats: Set<TargetFormat> = EnumSet.noneOf(TargetFormat::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.desktop.application.internal.validation.validatePackageVersions
import org.jetbrains.compose.desktop.application.tasks.*
import org.jetbrains.compose.desktop.tasks.AbstractUnpackDefaultComposeApplicationResourcesTask
import org.jetbrains.compose.internal.joinDashLowercaseNonEmpty
import java.io.File

private val defaultJvmArgs = listOf("-D$CONFIGURE_SWING_GLOBALS=true")
Expand Down Expand Up @@ -45,6 +44,7 @@ internal class CommonJvmDesktopTasks(
val checkRuntime: TaskProvider<AbstractCheckNativeDistributionRuntime>,
val suggestRuntimeModules: TaskProvider<AbstractSuggestModulesTask>,
val prepareAppResources: TaskProvider<Sync>,
val prepareJPackageResources: TaskProvider<Sync>,
val createRuntimeImage: TaskProvider<AbstractJLinkTask>
)

Expand Down Expand Up @@ -89,6 +89,19 @@ private fun JvmApplicationContext.configureCommonJvmDesktopTasks(): CommonJvmDes
into(jvmTmpDirForTask())
}

val prepareJPackageResources = tasks.register<Sync>(
taskNameAction = "prepare",
taskNameObject = "JPackageResources"
) {
val jpackageResourcesRootDir = app.nativeDistributions.jpackageResourcesRootDir
if (jpackageResourcesRootDir.isPresent) {
from(jpackageResourcesRootDir.dir("common"))
from(jpackageResourcesRootDir.dir(currentOS.id))
from(jpackageResourcesRootDir.dir(currentTarget.id))
}
into(jvmTmpDirForTask())
}

val createRuntimeImage = tasks.register<AbstractJLinkTask>(
taskNameAction = "create",
taskNameObject = "runtimeImage"
Expand All @@ -106,6 +119,7 @@ private fun JvmApplicationContext.configureCommonJvmDesktopTasks(): CommonJvmDes
checkRuntime,
suggestRuntimeModules,
prepareAppResources,
prepareJPackageResources,
createRuntimeImage
)
}
Expand All @@ -131,6 +145,7 @@ private fun JvmApplicationContext.configurePackagingTasks(
this,
createRuntimeImage = commonTasks.createRuntimeImage,
prepareAppResources = commonTasks.prepareAppResources,
prepareJPackageResources = commonTasks.prepareJPackageResources,
checkRuntime = commonTasks.checkRuntime,
unpackDefaultResources = commonTasks.unpackDefaultResources,
runProguard = runProguard
Expand All @@ -154,6 +169,7 @@ private fun JvmApplicationContext.configurePackagingTasks(
this,
createRuntimeImage = commonTasks.createRuntimeImage,
prepareAppResources = commonTasks.prepareAppResources,
prepareJPackageResources = commonTasks.prepareJPackageResources,
checkRuntime = commonTasks.checkRuntime,
unpackDefaultResources = commonTasks.unpackDefaultResources,
runProguard = runProguard
Expand Down Expand Up @@ -217,7 +233,7 @@ private fun JvmApplicationContext.configurePackagingTasks(
)

val run = tasks.register<JavaExec>(taskNameAction = "run") {
configureRunTask(this, commonTasks.prepareAppResources)
configureRunTask(this, commonTasks.prepareAppResources, commonTasks.prepareJPackageResources)
}
}

Expand Down Expand Up @@ -248,6 +264,7 @@ private fun JvmApplicationContext.configurePackageTask(
createAppImage: TaskProvider<AbstractJPackageTask>? = null,
createRuntimeImage: TaskProvider<AbstractJLinkTask>? = null,
prepareAppResources: TaskProvider<Sync>? = null,
prepareJPackageResources: TaskProvider<Sync>? = null,
checkRuntime: TaskProvider<AbstractCheckNativeDistributionRuntime>? = null,
unpackDefaultResources: TaskProvider<AbstractUnpackDefaultComposeApplicationResourcesTask>,
runProguard: Provider<AbstractProguardTask>? = null
Expand All @@ -264,12 +281,18 @@ private fun JvmApplicationContext.configurePackageTask(
packageTask.runtimeImage.set(createRuntimeImage.flatMap { it.destinationDir })
}

prepareAppResources?.let { prepareResources ->
packageTask.dependsOn(prepareResources)
val resourcesDir = packageTask.project.layout.dir(prepareResources.map { it.destinationDir })
prepareAppResources?.let { prepareAppResources ->
packageTask.dependsOn(prepareAppResources)
val resourcesDir = packageTask.project.layout.dir(prepareAppResources.map { it.destinationDir })
packageTask.appResourcesDir.set(resourcesDir)
}

prepareJPackageResources?.let { prepareJPackageResources ->
packageTask.dependsOn(prepareJPackageResources)
val resourcesDir = packageTask.project.layout.dir(prepareJPackageResources.map { it.destinationDir })
packageTask.jpackageResourcesDir.set(resourcesDir)
}

checkRuntime?.let { checkRuntime ->
packageTask.dependsOn(checkRuntime)
packageTask.javaRuntimePropertiesFile.set(checkRuntime.flatMap { it.javaRuntimePropertiesFile })
Expand Down Expand Up @@ -376,9 +399,11 @@ internal fun JvmApplicationContext.configurePlatformSettings(

private fun JvmApplicationContext.configureRunTask(
exec: JavaExec,
prepareAppResources: TaskProvider<Sync>
prepareAppResources: TaskProvider<Sync>,
prepareJPackageResources: TaskProvider<Sync>
) {
exec.dependsOn(prepareAppResources)
exec.dependsOn(prepareJPackageResources)

exec.mainClass.set(exec.provider { app.mainClass })
exec.executable(javaExecutable(app.javaHome))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,26 @@ abstract class AbstractJPackageTask @Inject constructor(
internal val appResourcesDirInputDirHackForVerification: Provider<Directory>
get() = appResourcesDir.map { it.takeIf { it.asFile.exists() } }

@get:Internal
val jpackageResourcesDir: DirectoryProperty = objects.directoryProperty()

/**
* Gradle runtime verification fails,
* if InputDirectory is not null, but a directory does not exist.
* The directory might not exist, because prepareJPackageResources task
* does not create output directory if there are no resources.
*
* To work around this, jpackageResourcesDir is used as a real property,
* but it is annotated as @Internal, so it ignored during inputs checking.
* This property is used only for inputs checking.
* It returns appResourcesDir value if the underlying directory exists.
*/
@Suppress("unused")
@get:InputDirectory
@get:Optional
internal val jpackageResourcesDirInputDirHackForVerification: Provider<Directory>
get() = jpackageResourcesDir.map { it.takeIf { it.asFile.exists() } }

@get:Internal
private val libsMappingFile: Provider<RegularFile> = workingDir.map {
it.file("libs-mapping.txt")
Expand Down Expand Up @@ -477,24 +497,14 @@ abstract class AbstractJPackageTask @Inject constructor(
// todo: incremental copy
cleanDirs(packagedResourcesDir)
val destResourcesDir = packagedResourcesDir.ioFile
val appResourcesDir = appResourcesDir.ioFileOrNull
if (appResourcesDir != null) {
for (file in appResourcesDir.walk()) {
val relPath = file.relativeTo(appResourcesDir).path
val destFile = destResourcesDir.resolve(relPath)
if (file.isDirectory) {
fileOperations.mkdir(destFile)
} else {
file.copyTo(destFile)
}
}
}
appResourcesDir.ioFileOrNull?.copyContentsTo(destResourcesDir)

cleanDirs(jpackageResources)
val jpackageResources = jpackageResources.ioFile
if (currentOS == OS.MacOS) {
InfoPlistBuilder(macExtraPlistKeysRawXml.orNull)
.also { setInfoPlistValues(it) }
.writeToFile(jpackageResources.ioFile.resolve("Info.plist"))
.writeToFile(jpackageResources.resolve("Info.plist"))

if (macAppStore.orNull == true) {
val productDefPlistXml = """
Expand All @@ -504,7 +514,20 @@ abstract class AbstractJPackageTask @Inject constructor(
</array>
""".trimIndent()
InfoPlistBuilder(productDefPlistXml)
.writeToFile(jpackageResources.ioFile.resolve("product-def.plist"))
.writeToFile(jpackageResources.resolve("product-def.plist"))
}
}
jpackageResourcesDir.ioFileOrNull?.copyContentsTo(jpackageResources)
}

private fun File.copyContentsTo(destination: File) {
for (file in walk()) {
val relPath = file.relativeTo(this).path
val destFile = destination.resolve(relPath)
if (file.isDirectory) {
fileOperations.mkdir(destFile)
} else {
file.copyTo(destFile)
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions tutorials/Native_distributions_and_local_execution/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,38 @@ fun main() {
3. Run `./gradlew runDistributable`.
4. Links like `compose://foo/bar` are now redirected from a browser to your application.

## Customizing data passed to `jpackage` via `--resource-dir`

In some cases it can be useful to pass files to `jpackage` via the `--resource-dir`
option. For example on Windows, it is possible to customize the generated install
wizard that way as the Wix toolchain used to build the installer uses that directory
for looking for custom configuration files.

To do so, specify a root resource directory via DSL:
```
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageVersion = "1.0.0"
jpackageResourcesRootDir.set(project.layout.projectDirectory.dir("jpackage-resources"))
}
}
}
```
In the example above a root resource directory is set to `<PROJECT_DIR>/jpackage-resources`.

Compose Gradle plugin will include all files under the following subdirectories:
1. Files from `<RESOURCES_ROOT_DIR>/common` will be included into all packages.
2. Files from `<RESOURCES_ROOT_DIR>/<OS_NAME>` will be included only into packages for
a specific OS. Possible values for `<OS_NAME>` are: `windows`, `macos`, `linux`.
3. Files from `<RESOURCES_ROOT_DIR>/<OS_NAME>-<ARCH_NAME>` will be included only into packages for
a specific combination of OS and CPU architecture. Possible values for `<ARCH_NAME>` are: `x64` and `arm64`.
For example, files from `<RESOURCES_ROOT_DIR>/macos-arm64` will be included only into packages built for Apple Silicon
Macs.

## Obfuscation

To obfuscate Compose Multiplatform JVM applications the standard approach for JVM applications works.
Expand Down

0 comments on commit d6836d1

Please sign in to comment.