From 0b62c742a7c7ffcc4a64265752222f1a8dea7420 Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Mon, 20 Jul 2026 05:34:48 +0000
Subject: [PATCH 63/71] feat(macos): lite app bundle (dock icon + launch)
 separate from dylib packaging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Split bundling into two levels so a plain "dock icon + standalone launch" app
does not pull in the heavy dylib-copying meant for distribution:

- CONTOUR_MACOS_BUNDLE now builds Contour.app with the icon and Info.plist —
  a real app that launches from Finder/dock with no parent terminal — and
  resolves runtime deps via MacPorts, exactly like the plain binary. This is
  all that is needed to run it as a normal app on one's own machine.
- CONTOUR_MACOS_BUNDLE_STANDALONE (new, implies BUNDLE) additionally runs
  fixup_bundle to copy every non-system dylib into Contents/Frameworks, so the
  .app runs with MacPorts deactivated. For distribution; slower install.

Previously CONTOUR_MACOS_BUNDLE always did the fixup_bundle step, coupling
"I want an app icon" to "package all of MacPorts". Configure summary and the
option help updated to describe both levels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeG2jwYMX5gdvvtUnx3PJa
---
 CMakeLists.txt                   |  6 +++--
 src/contour_macos/CMakeLists.txt | 38 +++++++++++++++++++++-----------
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f758aaa2..b1a85f69 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -102,7 +102,8 @@ option(CONTOUR_TESTING "Enables building of unittests for libterminal [default:
 option(CONTOUR_VERIFY_STATE "Enables expensive internal terminal-state invariant checks (Grid/Terminal verifyState) [default: ${MAINTAINER_MODE}]" ${MAINTAINER_MODE})
 option(CONTOUR_FRONTEND_GUI "Enables the Qt6 GUI frontend." ON)
 option(CONTOUR_FRONTEND_MACOS "Enables the native AppKit/CoreGraphics macOS frontend (no Qt)." OFF)
-option(CONTOUR_MACOS_BUNDLE "Build a standalone Contour.app that bundles its non-system dylibs, so it runs independently of MacPorts [default: OFF]." OFF)
+option(CONTOUR_MACOS_BUNDLE "Build Contour.app (dock icon + Finder launch; runtime deps via MacPorts) [default: OFF]. Enable CONTOUR_MACOS_BUNDLE_STANDALONE to also bundle non-system dylibs for MacPorts-independent distribution." OFF)
+option(CONTOUR_MACOS_BUNDLE_STANDALONE "Bundle non-system dylibs into Contour.app for MacPorts-independent distribution (implies CONTOUR_MACOS_BUNDLE) [default: OFF]." OFF)
 option(CONTOUR_COVERAGE "Builds with codecov [default: OFF]" OFF)
 option(CONTOUR_SANITIZE "Builds with Address sanitizer enabled [default: OFF]" "OFF")
 option(CONTOUR_STACKTRACE_ADDR2LINE "Uses addr2line to pretty-print SEGV stacktrace." ${ADDR2LINE_DEFAULT})
@@ -303,7 +304,8 @@ macro(ContourConfigurationSummary)
             else()
                 message(STATUS "  Frontend ObjC++ compiler:                         default OBJCXX (-std=${CONTOUR_MACOS_OBJCXX_STD})  [WARNING: modern GCC miscompiles this; set CONTOUR_MACOS_OBJCXX_COMPILER=/usr/bin/g++-4.2]")
             endif()
-            message(STATUS "  Bundle dependencies (standalone .app):            ${CONTOUR_MACOS_BUNDLE}")
+            message(STATUS "  Build Contour.app (dock icon):                     ${CONTOUR_MACOS_BUNDLE}")
+            message(STATUS "  Bundle non-system dylibs (standalone .app):       ${CONTOUR_MACOS_BUNDLE_STANDALONE}")
         endif()
     endif()
     message(STATUS "Build contour using mimalloc:                       ${CONTOUR_BUILD_WITH_MIMALLOC}")
diff --git a/src/contour_macos/CMakeLists.txt b/src/contour_macos/CMakeLists.txt
index 0682bc16..6b74c09c 100644
--- a/src/contour_macos/CMakeLists.txt
+++ b/src/contour_macos/CMakeLists.txt
@@ -169,14 +169,21 @@ Empty = use the modern default OBJCXX compiler.")
 
     include(GNUInstallDirs)
 
-    if(CONTOUR_MACOS_BUNDLE)
-        # --- Standalone Contour.app that carries its own non-system dylibs. ---
-        # Make the target a bundle with a minimal Info.plist, then at install time copy every
-        # non-system dylib the binary depends on (MacPorts libstdc++/libgcc_s, freetype,
-        # harfbuzz, fontconfig, libunicode, yaml-cpp, ...) into Contents/Frameworks and rewrite
-        # load paths to @executable_path-relative, so the app runs with MacPorts deactivated.
-        # System frameworks (AppKit/Foundation/ApplicationServices) are left as-is.
+    # Two levels of bundling:
+    #   CONTOUR_MACOS_BUNDLE            -> a real Contour.app (dock icon + Finder/`open` launch, no
+    #                                     parent terminal). Runtime deps resolved via MacPorts, as
+    #                                     for the plain binary. This is the "lite" bundle: everything
+    #                                     you need to run it as a normal app on your own machine.
+    #   CONTOUR_MACOS_BUNDLE_STANDALONE -> additionally copy every non-system dylib into
+    #                                     Contents/Frameworks (fixup_bundle) so the .app runs with
+    #                                     MacPorts deactivated. For distribution; slower install.
+    # STANDALONE (declared as an option in the top-level CMakeLists) implies BUNDLE.
+    if(CONTOUR_MACOS_BUNDLE_STANDALONE)
+        set(CONTOUR_MACOS_BUNDLE ON)
+    endif()
 
+    if(CONTOUR_MACOS_BUNDLE)
+        # --- Contour.app with a Finder/dock icon. ---
         # Dock/Finder icon: reuse contour's existing multi-resolution .icns. Adding it as a bundle
         # resource with the MACOSX_PACKAGE_LOCATION Resources property makes CMake copy it into
         # Contents/Resources; MACOSX_BUNDLE_ICON_FILE names it in Info.plist (CFBundleIconFile).
@@ -195,12 +202,17 @@ Empty = use the modern default OBJCXX compiler.")
 
         install(TARGETS contour BUNDLE DESTINATION ".")
 
-        # fixup_bundle runs otool/install_name_tool on the installed .app and pulls in deps.
-        set(_bundle_app "\${CMAKE_INSTALL_PREFIX}/contour.app")
-        install(CODE "
-            include(BundleUtilities)
-            fixup_bundle(\"${_bundle_app}\" \"\" \"\")
-        " COMPONENT Runtime)
+        if(CONTOUR_MACOS_BUNDLE_STANDALONE)
+            # fixup_bundle copies non-system dylibs (MacPorts libstdc++/libgcc_s, freetype, harfbuzz,
+            # fontconfig, libunicode, yaml-cpp, ...) into Contents/Frameworks and rewrites load paths
+            # to @executable_path-relative, so the app runs with MacPorts deactivated. System
+            # frameworks (AppKit/Foundation/ApplicationServices) are left as-is.
+            set(_bundle_app "\${CMAKE_INSTALL_PREFIX}/contour.app")
+            install(CODE "
+                include(BundleUtilities)
+                fixup_bundle(\"${_bundle_app}\" \"\" \"\")
+            " COMPONENT Runtime)
+        endif()
     else()
         # Plain binary in bin/ (the MacPorts default; runtime deps resolved via MacPorts).
         install(TARGETS contour RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
