Skip to content

Commit bd82c3d

Browse files
committed
Add support for "fixing" Xcode 15.
1 parent 9203503 commit bd82c3d

File tree

17 files changed

+426
-153
lines changed

17 files changed

+426
-153
lines changed

.run/install.run.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<component name="ProjectRunConfigurationManager">
2-
<configuration default="false" name="install" type="KonanRunConfiguration" factoryName="KonanApp" PROGRAM_PARAMS="install" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" WORKING_DIR="file://$PROJECT_DIR$" PASS_PARENT_ENVS_2="true">
2+
<configuration default="false" name="install" type="KonanRunConfiguration" factoryName="KonanApp" PROGRAM_PARAMS="install" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$PROJECT_DIR$" PASS_PARENT_ENVS_2="true">
33
<executable TARGET="macos_arm64" TARGET_NAME="macosArm64" EXECUTABLE_NAME="xcode-kotlin" PROJECT_PREFIX="xcode-kotlin:" IS_TEST="false">
4-
<variant NAME="Debug" GRADLE_TASK=":linkDebugExecutableMacosArm64" FILE="$PROJECT_DIR$/build/bin/macosArm64/debugExecutable/xcode-kotlin.kexe" WORKING_DIR="" PROGRAM_PARAMS="">
4+
<variant NAME="Debug" GRADLE_TASK=":linkDebugExecutableMacosArm64" FILE="$PROJECT_DIR$/build/bin/macosArm64/debugExecutable/xcode-kotlin.kexe" WORKING_DIR="$PROJECT_DIR$" PROGRAM_PARAMS="&quot;&quot;">
55
<envs />
66
</variant>
7-
<variant NAME="Release" GRADLE_TASK=":linkReleaseExecutableMacosArm64" FILE="$PROJECT_DIR$/build/bin/macosArm64/releaseExecutable/xcode-kotlin.kexe" WORKING_DIR="" PROGRAM_PARAMS="">
7+
<variant NAME="Release" GRADLE_TASK=":linkReleaseExecutableMacosArm64" FILE="$PROJECT_DIR$/build/bin/macosArm64/releaseExecutable/xcode-kotlin.kexe" WORKING_DIR="$PROJECT_DIR$" PROGRAM_PARAMS="&quot;&quot;">
88
<envs />
99
</variant>
1010
</executable>
11-
<variant NAME="Debug" GRADLE_TASK=":linkDebugExecutableMacosArm64" FILE="$PROJECT_DIR$/build/bin/macosArm64/debugExecutable/xcode-kotlin.kexe" WORKING_DIR="" PROGRAM_PARAMS="">
11+
<variant NAME="Debug" GRADLE_TASK=":linkDebugExecutableMacosArm64" FILE="$PROJECT_DIR$/build/bin/macosArm64/debugExecutable/xcode-kotlin.kexe" WORKING_DIR="$PROJECT_DIR$" PROGRAM_PARAMS="&quot;&quot;">
1212
<envs />
1313
</variant>
1414
<method v="2">

README.md

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,35 @@ Let us know how you're using (or will use) the xcode-kotlin plugin by taking our
1414
> [Open Touchlab Xcode Plugin User Survey](https://touchlabwaitlist.typeform.com/xcodepluginuser)
1515
*************************************************************************
1616

17-
18-
## Beta Version!!!
19-
20-
The CLI installer is a significant improvement over our original install process, but is also more complex. We are considering this version to be a beta release. Please let us know if you have issues! If there is a crash using the tool, it will ask if you want to upload a report. Please do. For other problems, [please file an issue in Github](https://github.com/touchlab/xcode-kotlin/issues).
21-
22-
We aren't anticipating any major problems, but If you cannot get the plugin to install properly, you can follow the [MANUAL_INSTALL](MANUAL_INSTALL.md) instructions as a workaround.
17+
## 💥 Xcode 15+ support 💥
18+
19+
Xcode 15 introduced a bug where it crashes if you have any non-Apple Xcode plugin installed.
20+
Until the bug is fixed, we have found a workaround that's built into the `xcode-kotlin` CLI.
21+
All your Xcode 15 installations will have the workaround applied to them during `install`,
22+
`sync` and a new `fix-xcode15` commands.
23+
24+
The workaround works like this:
25+
1. Disabling Xcode Kotlin plugin (if it's installed)
26+
2. Enabling `IDEPerformanceDebugger` plugin that's in Xcode
27+
3. Running each Xcode 15 installation you have (15.0, 15.0.1, etc.)
28+
4. Disabling `IDEPerformanceDebugger` plugin
29+
5. Re-enabling Xcode Kotlin plugin (if it's installed)
30+
31+
This lets Xcode create a valid plugin cache and use it the next time it runs.
32+
When the plugin cache isn't used,
33+
Xcode tries to scan all plugins and due to a bug freezes extension points that are used by custom plugins,
34+
like Xcode Kotlin.
35+
When the plugin cache is used, the execution goes through a different path so those extension points are not frozen,
36+
allowing Xcode Kotlin to load properly.
37+
38+
The reason Xcode doesn't use the cache otherwise is
39+
that it expects to find an entry for `IDEPerformanceDebugger.framework`.
40+
But for some reason,
41+
Xcode doesn't add the `IDEPerformanceDebugger` entry to the plugin cache unless the plugin is enabled.
42+
So essentially, performing these steps should also lead to faster Xcode startup time, what a bargain!
43+
44+
In case your Xcode starts crashing again, run `xcode-kotlin fix-xcode15` (or `xcode-kotlin sync`).
45+
This will reapply the workaround and should make your Xcode work again.
2346

2447
## Getting Help
2548

@@ -63,7 +86,14 @@ This will install the plugin with support for all of your currently installed Xc
6386

6487
## Manual Install
6588

66-
If needed, you can install manually. See [MANUAL_INSTALL](MANUAL_INSTALL.md).
89+
The CLI installer is a significant improvement over our original install process, but is also more complex.
90+
Please let us know if you encounter any issues.
91+
If there is a crash using the tool, it will ask if you want to upload a report.
92+
Please do.
93+
For other problems, [please file an issue in Github](https://github.com/touchlab/xcode-kotlin/issues).
94+
95+
We aren't anticipating any major problems, but If you cannot get the plugin to install properly,
96+
you can follow the [MANUAL_INSTALL](MANUAL_INSTALL.md) instructions as a workaround.
6797

6898
## Sync
6999

build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ kotlin {
2020
runTask?.run {
2121
val args = providers.gradleProperty("runArgs")
2222
args(args.getOrElse("").split(' '))
23+
24+
standardOutput = System.out
25+
errorOutput = System.err
2326
}
2427
}
2528
}
@@ -61,6 +64,8 @@ kotlin {
6164

6265
all {
6366
languageSettings.optIn("kotlinx.cli.ExperimentalCli")
67+
languageSettings.optIn("kotlinx.cinterop.BetaInteropApi")
68+
languageSettings.optIn("kotlin.experimental.ExperimentalNativeApi")
6469
}
6570
}
6671
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package co.touchlab.xcode.cli
2+
3+
import co.touchlab.kermit.LogWriter
4+
import co.touchlab.kermit.Severity
5+
import kotlinx.cinterop.ExperimentalForeignApi
6+
import platform.posix.fflush
7+
import platform.posix.fprintf
8+
import platform.posix.stderr
9+
10+
class EchoWriter: LogWriter() {
11+
override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {
12+
val printString: (String) -> Unit = when (severity) {
13+
Severity.Verbose, Severity.Debug -> return
14+
Severity.Info -> { string -> println(string) }
15+
Severity.Warn -> { string -> println("WARN: $string") }
16+
Severity.Error, Severity.Assert -> @OptIn(ExperimentalForeignApi::class) { string ->
17+
fprintf(stderr, string)
18+
fflush(stderr)
19+
}
20+
}
21+
22+
printString(message)
23+
throwable?.let {
24+
printString(it.stackTraceToString())
25+
}
26+
}
27+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package co.touchlab.xcode.cli
2+
3+
import co.touchlab.kermit.Logger
4+
import co.touchlab.xcode.cli.command.Install
5+
import co.touchlab.xcode.cli.util.Console
6+
7+
object InstallationFacade {
8+
private val logger = Logger.withTag("InstallationFacade")
9+
10+
fun installAll(xcodeInstallations: List<XcodeHelper.XcodeInstallation>, fixXcode15: Boolean) {
11+
XcodeHelper.ensureXcodeNotRunning()
12+
13+
val bundledVersion = PluginManager.bundledVersion
14+
logger.v { "Bundled plugin version = $bundledVersion" }
15+
val installedVersion = PluginManager.installedVersion
16+
logger.v { "Installed plugin version = ${installedVersion ?: "N/A"}" }
17+
18+
if (installedVersion != null) {
19+
val (confirmation, notification) = when {
20+
bundledVersion > installedVersion -> {
21+
"Do you want to update from $installedVersion to $bundledVersion? y/n: " to "Updating to $bundledVersion"
22+
}
23+
bundledVersion == installedVersion -> {
24+
"Do you want to reinstall version $installedVersion? y/n: " to "Reinstalling $installedVersion"
25+
}
26+
bundledVersion < installedVersion -> {
27+
"Do you want to downgrade from $installedVersion to $bundledVersion? y/n: " to "Downgrading to $bundledVersion"
28+
}
29+
else -> error("Unhandled comparison possibility!")
30+
}
31+
32+
if (!Console.confirm(confirmation)) {
33+
return
34+
}
35+
36+
logger.v { "Installation confirmed." }
37+
logger.i { notification }
38+
uninstallAll()
39+
} else {
40+
logger.i { "Installing $bundledVersion." }
41+
}
42+
43+
PluginManager.install()
44+
PluginManager.disable(bundledVersion, xcodeInstallations)
45+
if (fixXcode15) {
46+
PluginManager.fixXcode15(xcodeInstallations)
47+
}
48+
PluginManager.sync(xcodeInstallations)
49+
LangSpecManager.install()
50+
LLDBInitManager.install()
51+
PluginManager.enable(bundledVersion, xcodeInstallations)
52+
53+
logger.i { "Installation complete." }
54+
}
55+
56+
fun enable(xcodeInstallations: List<XcodeHelper.XcodeInstallation>) {
57+
XcodeHelper.ensureXcodeNotRunning()
58+
59+
val installedVersion = PluginManager.installedVersion ?: run {
60+
Console.echo("Plugin not installed, nothing to enable.")
61+
return
62+
}
63+
64+
PluginManager.enable(installedVersion, xcodeInstallations)
65+
66+
logger.i { "Plugin enabled." }
67+
}
68+
69+
fun disable(xcodeInstallations: List<XcodeHelper.XcodeInstallation>) {
70+
XcodeHelper.ensureXcodeNotRunning()
71+
72+
val installedVersion = PluginManager.installedVersion ?: run {
73+
Console.echo("Plugin not installed, nothing to disable.")
74+
return
75+
}
76+
77+
PluginManager.disable(installedVersion, xcodeInstallations)
78+
79+
logger.i { "Plugin disabled." }
80+
}
81+
82+
fun fixXcode15(xcodeInstallations: List<XcodeHelper.XcodeInstallation>) {
83+
XcodeHelper.ensureXcodeNotRunning()
84+
85+
val installedVersion = PluginManager.installedVersion
86+
try {
87+
if (installedVersion != null) {
88+
PluginManager.disable(installedVersion, xcodeInstallations)
89+
}
90+
91+
PluginManager.fixXcode15(xcodeInstallations)
92+
} finally {
93+
if (installedVersion != null) {
94+
PluginManager.enable(installedVersion, xcodeInstallations)
95+
}
96+
}
97+
98+
logger.i { "Xcode 15 fix applied." }
99+
}
100+
101+
fun sync(xcodeInstallations: List<XcodeHelper.XcodeInstallation>, fixXcode15: Boolean) {
102+
XcodeHelper.ensureXcodeNotRunning()
103+
104+
val installedVersion = PluginManager.installedVersion ?: run {
105+
Console.echo("Plugin not installed, nothing to synchronize.")
106+
return
107+
}
108+
109+
PluginManager.disable(installedVersion, xcodeInstallations)
110+
PluginManager.sync(xcodeInstallations)
111+
if (fixXcode15) {
112+
PluginManager.fixXcode15(xcodeInstallations)
113+
}
114+
PluginManager.enable(installedVersion, xcodeInstallations)
115+
116+
logger.i { "Synchronization complete." }
117+
}
118+
119+
fun uninstallAll() {
120+
logger.v { "Will uninstall all plugin components." }
121+
XcodeHelper.ensureXcodeNotRunning()
122+
PluginManager.uninstall()
123+
LangSpecManager.uninstall()
124+
LLDBInitManager.uninstall()
125+
126+
logger.i { "Uninstallation complete." }
127+
}
128+
}

src/macosMain/kotlin/co/touchlab/xcode/cli/Installer.kt

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)