Skip to content

Commit 8822eba

Browse files
CloudyDinoclaude
andcommitted
Add packagingResourcesRootDir to expose jpackage --resource-dir
jpackage's --resource-dir is the documented override mechanism for packaging resources (DMG volume icon via <name>-volume.icns, DMG background, Linux preinst/postinst scripts, WiX templates, ...), but Compose fully owns that directory: AbstractJPackageTask clears it and repopulates it with the generated Info.plist on every run, so users have no supported way to contribute their own files to it. Add a packagingResourcesRootDir DSL property, mirroring appResourcesRootDir (with common/<os>/<os>-<arch> subdirectories). Its files are copied into the jpackage resource dir after it is cleared but before the generated Info.plist/product-def.plist are written, so the Compose-managed plists always take precedence over user-supplied files. This revives the approach a maintainer suggested on JetBrains#1843 and supersedes the abandoned JetBrains#3647. Unlike JetBrains#1843 -- which repointed jpackageResources at a Sync output and so had its files removed by clearDirs -- the files are copied in after the clear, so nothing is wiped. Addresses CMP-2042, CMP-6411, CMP-1972, CMP-9837. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ff1c314 commit 8822eba

11 files changed

Lines changed: 119 additions & 0 deletions

File tree

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/AbstractDistributions.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ abstract class AbstractDistributions {
3030
var description: String? = null
3131
var vendor: String? = null
3232
val appResourcesRootDir: DirectoryProperty = objects.directoryProperty()
33+
34+
/**
35+
* Root directory for files passed to the underlying packaging tool (jpackage)
36+
* via `--resource-dir`, used to override the tool's own packaging resources (e.g.
37+
* a `<package-name>-volume.icns` DMG volume icon, a DMG background, Linux
38+
* `preinst`/`postinst` scripts, or WiX templates). See
39+
* https://docs.oracle.com/en/java/javase/21/jpackage/override-jpackage-resources.html
40+
*
41+
* Files are resolved from `common`, `<os>` and `<os>-<arch>` subdirectories, the
42+
* same layout as [appResourcesRootDir]. The generated `Info.plist` and
43+
* `product-def.plist` are managed by Compose and always take precedence.
44+
*/
45+
val packagingResourcesRootDir: DirectoryProperty = objects.directoryProperty()
46+
3347
val licenseFile: RegularFileProperty = objects.fileProperty()
3448

3549
var targetFormats: Set<TargetFormat> = EnumSet.noneOf(TargetFormat::class.java)

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureJvmApplication.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ private fun JvmApplicationContext.configurePackageTask(
318318
packageTask.packageVendor.set(packageTask.provider { executables.vendor })
319319
packageTask.packageVersion.set(packageVersionFor(packageTask.targetFormat))
320320
packageTask.licenseFile.set(executables.licenseFile)
321+
packageTask.packagingResourcesRootDir.set(executables.packagingResourcesRootDir)
321322
}
322323

323324
packageTask.destinationDir.set(app.nativeDistributions.outputBaseDir.map {

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ abstract class AbstractJPackageTask @Inject constructor(
377377
@get:Internal
378378
val appResourcesDir: DirectoryProperty = objects.directoryProperty()
379379

380+
@get:InputDirectory
381+
@get:Optional
382+
@get:PathSensitive(PathSensitivity.RELATIVE)
383+
val packagingResourcesRootDir: DirectoryProperty = objects.directoryProperty()
384+
380385
@get:Internal
381386
private val libsMappingFile: Provider<RegularFile> = workingDir.map {
382387
it.file("libs-mapping.txt")
@@ -607,6 +612,7 @@ abstract class AbstractJPackageTask @Inject constructor(
607612
}
608613

609614
fileOperations.clearDirs(jpackageResources)
615+
copyUserPackagingResources()
610616
if (currentOS == OS.MacOS) {
611617
InfoPlistBuilder(macExtraPlistKeysRawXml.orNull)
612618
.also { setInfoPlistValues(it) }
@@ -626,6 +632,33 @@ abstract class AbstractJPackageTask @Inject constructor(
626632
}
627633
}
628634

635+
/**
636+
* Copies user-supplied packaging override resources into [jpackageResources]
637+
* (the `--resource-dir` passed to jpackage). Files are taken from the `common`,
638+
* `<os>` and `<os>-<arch>` subdirectories of [packagingResourcesRootDir], mirroring
639+
* the layout of `appResourcesRootDir`. Called after [jpackageResources] is cleared
640+
* but before the generated `Info.plist`/`product-def.plist` are written, so those
641+
* Compose-managed files always win over anything the user drops in.
642+
*/
643+
private fun copyUserPackagingResources() {
644+
val rootDir = packagingResourcesRootDir.ioFileOrNull ?: return
645+
val destDir = jpackageResources.ioFile
646+
for (subDirName in listOf("common", currentOS.id, currentTarget.id)) {
647+
val sourceDir = rootDir.resolve(subDirName)
648+
if (!sourceDir.isDirectory) continue
649+
for (file in sourceDir.walk()) {
650+
val relPath = file.relativeTo(sourceDir).path
651+
if (relPath.isEmpty()) continue
652+
val destFile = destDir.resolve(relPath)
653+
if (file.isDirectory) {
654+
fileOperations.mkdirs(destFile)
655+
} else {
656+
file.copyTo(destFile, overwrite = true)
657+
}
658+
}
659+
}
660+
}
661+
629662
override fun jvmToolEnvironment(): MutableMap<String, String> =
630663
super.jvmToolEnvironment().apply {
631664
if (currentOS == OS.Windows) {

gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,19 @@ class DesktopApplicationTest : GradlePluginTestBase() {
593593
}
594594
}
595595

596+
@Test
597+
fun packagingResources() = with(testProject("application/packagingResources")) {
598+
gradle(":createDistributable").checks {
599+
check.taskSuccessful(":createDistributable")
600+
601+
// Files from packagingResourcesRootDir must be copied into the
602+
// directory passed to jpackage via --resource-dir, for both the
603+
// `common` and the current-OS subdirectories.
604+
file("build/compose/tmp/resources/common-packaging-resource.txt").checkExists()
605+
file("build/compose/tmp/resources/os-specific-packaging-resource.txt").checkExists()
606+
}
607+
}
608+
596609
@Test
597610
fun testWixUnzip() {
598611
Assumptions.assumeTrue(currentOS == OS.Windows) { "The test is only relevant for Windows" }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
2+
3+
plugins {
4+
id "org.jetbrains.kotlin.jvm"
5+
id "org.jetbrains.kotlin.plugin.compose"
6+
id "org.jetbrains.compose"
7+
}
8+
9+
dependencies {
10+
implementation "org.jetbrains.kotlin:kotlin-stdlib"
11+
implementation compose.desktop.currentOs
12+
}
13+
14+
compose.desktop {
15+
application {
16+
mainClass = "MainKt"
17+
nativeDistributions {
18+
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
19+
packageVersion = "1.0.0"
20+
21+
packagingResourcesRootDir.set(project.layout.projectDirectory.dir("packaging-resources"))
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
common packaging resource
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
linux only packaging resource
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
macos only packaging resource
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
windows only packaging resource
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
pluginManagement {
2+
plugins {
3+
id 'org.jetbrains.kotlin.jvm' version 'KOTLIN_VERSION_PLACEHOLDER'
4+
id 'org.jetbrains.kotlin.plugin.compose' version 'KOTLIN_VERSION_PLACEHOLDER'
5+
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER'
6+
}
7+
repositories {
8+
mavenLocal()
9+
gradlePluginPortal()
10+
mavenCentral()
11+
google()
12+
maven {
13+
url 'https://packages.jetbrains.team/maven/p/cmp/dev'
14+
}
15+
}
16+
}
17+
dependencyResolutionManagement {
18+
repositories {
19+
mavenCentral()
20+
google()
21+
maven {
22+
url 'https://packages.jetbrains.team/maven/p/cmp/dev'
23+
}
24+
mavenLocal()
25+
}
26+
}
27+
rootProject.name = "packagingResources"

0 commit comments

Comments
 (0)