Skip to content

Commit

Permalink
Set NSSupportsAutomaticGraphicsSwitching in native Mac distributions …
Browse files Browse the repository at this point in the history
…by default

Resolves #545
  • Loading branch information
AlexeyTsvetkov committed Apr 5, 2021
1 parent da8a6a4 commit 826f130
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,29 @@ internal fun Project.configurePackagingTasks(apps: Collection<Application>) {
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 packageFormat = tasks.composeTask<AbstractJPackageTask>(
taskName("package", app, targetFormat.name),
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)) {
Expand Down Expand Up @@ -130,13 +147,6 @@ internal fun Project.configurePackagingTasks(apps: Collection<Application>) {
configurePackageUberJarForCurrentOS(app)
}

val createDistributable = tasks.composeTask<AbstractJPackageTask>(
taskName("createDistributable", app),
args = listOf(TargetFormat.AppImage)
) {
configurePackagingTask(app, createRuntimeImage = createRuntimeImage)
}

val runDistributable = project.tasks.composeTask<AbstractRunDistributableTask>(
taskName("runDistributable", app),
args = listOf(createDistributable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,49 @@ abstract class AbstractJPackageTask @Inject constructor(

override fun checkResult(result: ExecResult) {
super.checkResult(result)
patchInfoPlistIfNeeded()
val outputFile = findOutputFileOrDir(destinationDir.ioFile, targetFormat)
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() {
val mappingFile = libsMappingFile.ioFile
if (mappingFile.exists()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.jetbrains.compose.test.*
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.Test
import java.nio.charset.Charset
import java.util.jar.JarFile

class DesktopApplicationTest : GradlePluginTestBase() {
Expand Down Expand Up @@ -150,6 +151,9 @@ class DesktopApplicationTest : GradlePluginTestBase() {
gradle(":runDistributable").build().checks { check ->
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
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/>")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,8 @@ internal class BuildResultChecks(private val result: BuildResult) {
}
}

internal fun BuildResult.checkOutputLogContains(substring: String) {
if (output.contains(substring)) return

println("Test output:")
output.lineSequence().forEach {
println(" > $it")
internal fun String.checkContains(substring: String) {
if (!contains(substring)) {
throw AssertionError("String '$substring' is not found in text:\n$this")
}
throw AssertionError("Test output does not contain the expected string: '$substring'")
}

0 comments on commit 826f130

Please sign in to comment.