From ce69b31309687e7aaf660bc321ef01cf76bcd9e2 Mon Sep 17 00:00:00 2001 From: Slava Vishnyakov Date: Tue, 15 Jul 2025 00:14:06 +0300 Subject: [PATCH] Create Mac app bundle for GUI apps on macOS when --app:gui is used (#25042) Fixes https://github.com/nim-lang/Nim/issues/25041 Basically it creates a "real" console-less app when --app:gui is used. Otherwise a console window opens, see the bug. --------- Co-authored-by: Andreas Rumpf (cherry picked from commit 30d4f7791dc796d05d7ff963c698ca3525c3c08e) --- compiler/extccomp.nim | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 4bae400dc0..0f34d77df0 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -810,6 +810,59 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body (ose.msg & " " & $ose.errorCode)) raise +proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) = + let (dir, name, _) = splitFile(exefile.string) + let appBundleName = name & ".app" + let appBundlePath = dir / appBundleName + let contentsPath = appBundlePath / "Contents" + let macosPath = contentsPath / "MacOS" + + createDir(macosPath) + + let bundleExePath = macosPath / name + copyFileWithPermissions(exefile.string, bundleExePath) + + let infoPlistPath = contentsPath / "Info.plist" + + proc xmlEscape(s: string): string = + result = newStringOfCap(s.len) + for c in items(s): + case c: + of '<': result.add("<") + of '>': result.add(">") + of '&': result.add("&") + of '"': result.add(""") + of '\'': result.add("'") + else: + if ord(c) < 32: + result.add("&#" & $ord(c) & ';') + else: + result.add(c) + + let escapedName = xmlEscape(name) + let infoPlistContent = """ + + + + CFBundleExecutable + $1 + CFBundleIdentifier + com.nim.$1 + CFBundleName + $1 + CFBundlePackageType + APPL + LSUIElement + 1 + +""" % [escapedName] + + writeFile(infoPlistPath, infoPlistContent) + + removeFile(exefile.string) + + rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath) + proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] = result = @[] when defined(macosx): @@ -994,6 +1047,10 @@ proc callCCompiler*(conf: ConfigRef) = preventLinkCmdMaxCmdLen(conf, linkCmd) for cmd in extraCmds: execExternalProgram(conf, cmd, hintExecuting) + # create Mac app bundle for GUI apps on macOS + when defined(macosx): + if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}: + createMacAppBundle(conf, mainOutput) else: linkCmd = "" if optGenScript in conf.globalOptions: