From 0b62c742a7c7ffcc4a64265752222f1a8dea7420 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov 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) 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}")