Browse Source

Set NSSupportsAutomaticGraphicsSwitching in native Mac distributions by default

Resolves #545
pull/547/head
Alexey Tsvetkov 4 years ago committed by Alexey Tsvetkov
parent
commit
826f130b99
  1. 26
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/configureApplication.kt
  2. 39
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractJPackageTask.kt
  3. 4
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/gradle/DesktopApplicationTest.kt
  4. 10
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/assertUtils.kt

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

@ -91,12 +91,29 @@ internal fun Project.configurePackagingTasks(apps: Collection<Application>) {
destinationDir.set(project.layout.buildDirectory.dir("compose/tmp/${app.name}/runtime")) destinationDir.set(project.layout.buildDirectory.dir("compose/tmp/${app.name}/runtime"))
} }
val createDistributable = tasks.composeTask<AbstractJPackageTask>(
taskName("createDistributable", app),
args = listOf(TargetFormat.AppImage)
) {
configurePackagingTask(app, createRuntimeImage = createRuntimeImage)
}
val packageFormats = app.nativeDistributions.targetFormats.map { targetFormat -> val packageFormats = app.nativeDistributions.targetFormats.map { targetFormat ->
val packageFormat = tasks.composeTask<AbstractJPackageTask>( val packageFormat = tasks.composeTask<AbstractJPackageTask>(
taskName("package", app, targetFormat.name), taskName("package", app, targetFormat.name),
args = listOf(targetFormat) args = listOf(targetFormat)
) { ) {
configurePackagingTask(app, createRuntimeImage = createRuntimeImage) // On Mac we want to patch bundled Info.plist file,
// so we create an app image, change its Info.plist,
// then create an installer based on the app image.
// We could create an installer the same way on other platforms, but
// in some cases there are failures with JDK 15.
// See [AbstractJPackageTask.patchInfoPlistIfNeeded]
if (currentOS != OS.MacOS) {
configurePackagingTask(app, createRuntimeImage = createRuntimeImage)
} else {
configurePackagingTask(app, createAppImage = createDistributable)
}
} }
if (targetFormat.isCompatibleWith(OS.MacOS)) { if (targetFormat.isCompatibleWith(OS.MacOS)) {
@ -130,13 +147,6 @@ internal fun Project.configurePackagingTasks(apps: Collection<Application>) {
configurePackageUberJarForCurrentOS(app) configurePackageUberJarForCurrentOS(app)
} }
val createDistributable = tasks.composeTask<AbstractJPackageTask>(
taskName("createDistributable", app),
args = listOf(TargetFormat.AppImage)
) {
configurePackagingTask(app, createRuntimeImage = createRuntimeImage)
}
val runDistributable = project.tasks.composeTask<AbstractRunDistributableTask>( val runDistributable = project.tasks.composeTask<AbstractRunDistributableTask>(
taskName("runDistributable", app), taskName("runDistributable", app),
args = listOf(createDistributable) args = listOf(createDistributable)

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

@ -356,10 +356,49 @@ abstract class AbstractJPackageTask @Inject constructor(
override fun checkResult(result: ExecResult) { override fun checkResult(result: ExecResult) {
super.checkResult(result) super.checkResult(result)
patchInfoPlistIfNeeded()
val outputFile = findOutputFileOrDir(destinationDir.ioFile, targetFormat) val outputFile = findOutputFileOrDir(destinationDir.ioFile, targetFormat)
logger.lifecycle("The distribution is written to ${outputFile.canonicalPath}") logger.lifecycle("The distribution is written to ${outputFile.canonicalPath}")
} }
/**
* https://github.com/JetBrains/compose-jb/issues/545
*
* Patching Info.plist is necessary to avoid duplicating and supporting
* properties set by jpackage.
*
* Info.plist is patched only on macOS for app image.
* Packaged installers receive patched Info.plist through
* prebuilt [appImage].
*/
private fun patchInfoPlistIfNeeded() {
if (currentOS != OS.MacOS || targetFormat != TargetFormat.AppImage) return
val infoPlist = destinationDir.ioFile.resolve("${packageName.get()}.app/Contents/Info.plist")
if (!infoPlist.exists()) return
val content = infoPlist.readText()
val nsSupportsAutomaticGraphicsSwitching = "<key>NSSupportsAutomaticGraphicsSwitching</key>"
val stringToAppend = "$nsSupportsAutomaticGraphicsSwitching<true/>"
if (content.indexOf(nsSupportsAutomaticGraphicsSwitching) >= 0) return
/**
* Dirty hack: to avoid parsing plist file, let's find known expected key substring,
* and insert the necessary keys before it.
*/
val knownExpectedKey = "<key>NSHighResolutionCapable</key>"
val i = content.indexOf(knownExpectedKey)
if (i >= 0) {
val newContent = buildString {
append(content.substring(0, i))
appendln(stringToAppend)
append(" ")
appendln(content.substring(i, content.length))
}
infoPlist.writeText(newContent)
}
}
override fun initState() { override fun initState() {
val mappingFile = libsMappingFile.ioFile val mappingFile = libsMappingFile.ioFile
if (mappingFile.exists()) { if (mappingFile.exists()) {

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

@ -14,6 +14,7 @@ import org.jetbrains.compose.test.*
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.nio.charset.Charset
import java.util.jar.JarFile import java.util.jar.JarFile
class DesktopApplicationTest : GradlePluginTestBase() { class DesktopApplicationTest : GradlePluginTestBase() {
@ -150,6 +151,9 @@ class DesktopApplicationTest : GradlePluginTestBase() {
gradle(":runDistributable").build().checks { check -> gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS) check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
check.logContains("Hello, from Mac OS!") check.logContains("Hello, from Mac OS!")
val appDir = testWorkDir.resolve("build/compose/binaries/main/app/TestPackage.app/Contents/")
val infoPlist = appDir.resolve("Info.plist").checkExists().checkExists()
infoPlist.readText().checkContains("<key>NSSupportsAutomaticGraphicsSwitching</key><true/>")
} }
} }
} }

10
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/assertUtils.kt

@ -39,12 +39,8 @@ internal class BuildResultChecks(private val result: BuildResult) {
} }
} }
internal fun BuildResult.checkOutputLogContains(substring: String) { internal fun String.checkContains(substring: String) {
if (output.contains(substring)) return if (!contains(substring)) {
throw AssertionError("String '$substring' is not found in text:\n$this")
println("Test output:")
output.lineSequence().forEach {
println(" > $it")
} }
throw AssertionError("Test output does not contain the expected string: '$substring'")
} }
Loading…
Cancel
Save