From 06a5de972983e294bf37c17d97d8e4b7e08074cc Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Mon, 19 Oct 2026 21:38:18 +0300
Subject: [PATCH 33/33] Add new MacOS appbundle building script

New homebrew-appbundle-gtk4-dmg.sh replaces
old homebrew-appbundle.sh

Script by janior with minor changes by me

See RM #2388

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 Makefile.am                                   |   2 +-
 .../macos/homebrew-appbundle-gtk4-dmg.sh      | 320 ++++++++++++++++++
 platforms/macos/homebrew-appbundle.sh         | 118 -------
 3 files changed, 321 insertions(+), 119 deletions(-)
 create mode 100755 platforms/macos/homebrew-appbundle-gtk4-dmg.sh
 delete mode 100755 platforms/macos/homebrew-appbundle.sh

diff --git a/Makefile.am b/Makefile.am
index c4afdc174f..0440fe3c9a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -99,7 +99,7 @@ EXTRA_DIST =	autogen.sh 			\
 		platforms/flatpak/org.freeciv.sdl2.yml	\
 		platforms/flatpak/org.freeciv.sdl3.yml	\
 		platforms/flatpak/org.freeciv.ruledit.yml	\
-		platforms/macos/homebrew-appbundle.sh \
+		platforms/macos/homebrew-appbundle-gtk4-dmg.sh \
 		scripts/build_tolua.sh		\
 		scripts/generate_doc.sh		\
 		scripts/mapimg2anim		\
diff --git a/platforms/macos/homebrew-appbundle-gtk4-dmg.sh b/platforms/macos/homebrew-appbundle-gtk4-dmg.sh
new file mode 100755
index 0000000000..b727ad3cde
--- /dev/null
+++ b/platforms/macos/homebrew-appbundle-gtk4-dmg.sh
@@ -0,0 +1,320 @@
+#!/usr/bin/env bash
+
+# Freeciv - Copyright (C) 2022-2023 - Freeciv team
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2, or (at your option)
+#  any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+
+# Variant of homebrew-appbundle.sh: GTK4 client only, libraries bundled
+# inside the package. Output: dist/Freeciv.app, dist/Freeciv Server.app and
+# dist/Freeciv-<version>.dmg
+#
+# Required:
+#   brew install dylibbundler meson ninja pkg-config gettext icu4c \
+#     readline sqlite curl xz zstd gtk4 librsvg adwaita-icon-theme lua@5.4 \
+#     sdl3 sdl3_mixer gdk-pixbuf
+#
+# Work from the top-level source directory, regardless of where the
+# script was started from
+cd "$(dirname "$0")/../.." || exit 1
+
+BREW="$(brew --prefix)"
+
+export CXXFLAGS=-std=c++17
+export CPPFLAGS="-I$(brew --prefix readline)/include -I$(brew --prefix gettext)/include -I${BREW}/include"
+export LDFLAGS="-L$(brew --prefix icu4c)/lib -L$(brew --prefix readline)/lib -L$(brew --prefix gettext)/lib"
+export PKG_CONFIG_PATH="$(brew --prefix icu4c)/lib/pkgconfig:$(brew --prefix lua@5.4)/lib/pkgconfig"
+export PATH="$(brew --prefix gettext)/bin:$PATH"
+
+if ! command -v dylibbundler >/dev/null ; then
+  echo "dylibbundler not found. Install it with: brew install dylibbundler" >&2
+  exit 1
+fi
+
+DISTDIR="${PWD}/dist"
+APPDIR="${DISTDIR}/Freeciv.app"
+CONTENTSDIR="${APPDIR}/Contents"
+
+# Clean start (changing the configuration requires a fresh build directory)
+rm -rf "${DISTDIR}" build
+
+if ! mkdir -p "${CONTENTSDIR}/Resources" "${CONTENTSDIR}/MacOS" ; then
+  echo "Failed to create \"${CONTENTSDIR}\"" >&2
+  exit 1
+fi
+
+if ! cp data/freeciv-client.icns "${CONTENTSDIR}/Resources/" ; then
+  echo "Failed to copy file \"freeciv-client.icns\"" >&2
+  exit 1
+fi
+
+VSTRING=$(./fc_version)
+
+# Info.plist - the executable is a launcher script that sets up the environment
+cat > "${CONTENTSDIR}/Info.plist" <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleName</key>
+	<string>freeciv</string>
+	<key>CFBundleIdentifier</key>
+	<string>freeciv</string>
+	<key>CFBundleVersion</key>
+	<string>${VSTRING}</string>
+	<key>CFBundleShortVersionString</key>
+	<string>${VSTRING}</string>
+	<key>CFBundleExecutable</key>
+	<string>Freeciv</string>
+	<key>CFBundleIconFile</key>
+	<string>freeciv-client.icns</string>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>NSHighResolutionCapable</key>
+	<true/>
+</dict>
+</plist>
+EOF
+
+if ! echo -n "APPL????" > "${CONTENTSDIR}/PkgInfo" ; then
+  echo "Failed to create file \"${CONTENTSDIR}/PkgInfo\"" >&2
+  exit 1
+fi
+
+# Launcher: points the game to data, icons, schemas and loaders inside the bundle
+cat > "${CONTENTSDIR}/MacOS/Freeciv" <<'EOF'
+#!/bin/bash
+CONTENTS="$(cd "$(dirname "$0")/.." && pwd)"
+
+export FREECIV_DATA_PATH="$CONTENTS/share/freeciv"
+export FREECIV_LOCALE_PATH="$CONTENTS/share/locale"
+export XDG_DATA_DIRS="$CONTENTS/share"
+export GSETTINGS_SCHEMA_DIR="$CONTENTS/share/glib-2.0/schemas"
+export PATH="$CONTENTS/bin:$PATH"
+
+# The gdk-pixbuf loaders cache needs absolute paths, so it is generated
+# at startup (the bundle may live on a read-only disk image)
+CACHE_IN="$CONTENTS/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache.in"
+if [ -f "$CACHE_IN" ]; then
+  CACHE_OUT="${TMPDIR:-/tmp}/freeciv-pixbuf-loaders.cache"
+  sed "s|@CONTENTS@|$CONTENTS|g" "$CACHE_IN" > "$CACHE_OUT"
+  export GDK_PIXBUF_MODULE_FILE="$CACHE_OUT"
+fi
+
+exec "$CONTENTS/bin/freeciv-gtk4" "$@"
+EOF
+chmod +x "${CONTENTSDIR}/MacOS/Freeciv"
+
+mkdir build || { echo "Failed to create build directory" >&2; exit 1; }
+
+cd build || exit 1
+
+if ! meson setup .. \
+       -Dtools=manual,ruleup \
+       -Dsyslua=true \
+       -Ddebug=false \
+       -Dclients=gtk4 \
+       -Dfcmp=gtk4 \
+       -Daudio=sdl3 \
+       -Dfollowtag=macos \
+       -Ddefault_library=static \
+       -Dprefix="${CONTENTSDIR}" ||
+   ! ninja ||
+   ! ninja install
+then
+  echo "Build failed!" >&2
+  exit 1
+fi
+
+cd .. || exit 1
+
+# --- GTK4 resources -------------------------------------------------------
+
+# GSettings schemas (needed e.g. by the file chooser dialog)
+mkdir -p "${CONTENTSDIR}/share/glib-2.0/schemas"
+cp "${BREW}"/share/glib-2.0/schemas/*.xml "${CONTENTSDIR}/share/glib-2.0/schemas/"
+glib-compile-schemas "${CONTENTSDIR}/share/glib-2.0/schemas"
+
+# Icon themes
+mkdir -p "${CONTENTSDIR}/share/icons"
+for T in Adwaita hicolor ; do
+  if [ -d "${BREW}/share/icons/${T}" ] ; then
+    cp -R "${BREW}/share/icons/${T}" "${CONTENTSDIR}/share/icons/"
+  fi
+done
+
+# gdk-pixbuf loaders
+PIXDIR="lib/gdk-pixbuf-2.0/2.10.0"
+PIXSRC="$(brew --prefix gdk-pixbuf)/${PIXDIR}/loaders"
+if [ -d "${PIXSRC}" ] ; then
+  mkdir -p "${CONTENTSDIR}/${PIXDIR}"
+  cp -R "${PIXSRC}" "${CONTENTSDIR}/${PIXDIR}/"
+  "$(brew --prefix gdk-pixbuf)/bin/gdk-pixbuf-query-loaders" \
+      "${CONTENTSDIR}/${PIXDIR}"/loaders/*.so |
+    sed "s|${CONTENTSDIR}|@CONTENTS@|g" > "${CONTENTSDIR}/${PIXDIR}/loaders.cache.in"
+else
+  echo "Warning: gdk-pixbuf loaders directory not found, skipping" >&2
+fi
+
+chmod -R u+w "${CONTENTSDIR}"
+
+# --- Bundling libraries ---------------------------------------------------
+
+# Collect all Mach-O binaries (executables, plugins, loaders)
+MACHO=()
+while IFS= read -r F ; do
+  if file -b "$F" | grep -q 'Mach-O' ; then
+    MACHO+=("$F")
+  fi
+done < <(find "${CONTENTSDIR}/bin" "${CONTENTSDIR}/lib" -type f 2>/dev/null)
+
+ARGS=()
+for F in "${MACHO[@]}" ; do
+  ARGS+=(-x "$F")
+done
+
+if ! dylibbundler -od -b -cd "${ARGS[@]}" \
+       -d "${CONTENTSDIR}/Frameworks/" \
+       -p "@executable_path/../Frameworks/" \
+       -s "${BREW}/lib" ; then
+  echo "dylibbundler failed!" >&2
+  exit 1
+fi
+
+# --- Ad-hoc code signing (required on Apple Silicon) ----------------------
+
+while IFS= read -r F ; do
+  if file -b "$F" | grep -q 'Mach-O' ; then
+    codesign --force -s - "$F"
+  fi
+done < <(find "${CONTENTSDIR}/Frameworks" "${CONTENTSDIR}/bin" "${CONTENTSDIR}/lib" -type f)
+
+codesign --force --deep -s - "${APPDIR}"
+
+# Sanity check: nothing should reference /opt/homebrew any more
+LEFT=$(find "${CONTENTSDIR}/bin" "${CONTENTSDIR}/Frameworks" "${CONTENTSDIR}/lib" -type f -exec sh -c \
+  'file -b "$1" | grep -q Mach-O && otool -L "$1" | grep -H --label="$1" "/opt/homebrew"' _ {} \;)
+if [ -n "${LEFT}" ] ; then
+  echo "Warning: references to /opt/homebrew remain:" >&2
+  echo "${LEFT}" >&2
+fi
+
+# --- Freeciv Server.app -----------------------------------------------------
+# A separate small bundle, so that the server gets its own icon in Launchpad.
+# It contains no binaries: it opens freeciv-server from Freeciv.app (next to
+# it, or in /Applications) in a Terminal window.
+
+SERVERAPP="${DISTDIR}/Freeciv Server.app"
+SERVERCONTENTS="${SERVERAPP}/Contents"
+
+if ! mkdir -p "${SERVERCONTENTS}/MacOS" "${SERVERCONTENTS}/Resources" ; then
+  echo "Failed to create \"${SERVERCONTENTS}\"" >&2
+  exit 1
+fi
+
+if ! cp data/freeciv-server.icns "${SERVERCONTENTS}/Resources/" ; then
+  echo "Failed to copy file \"freeciv-server.icns\"" >&2
+  exit 1
+fi
+
+cat > "${SERVERCONTENTS}/Info.plist" <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleName</key>
+	<string>Freeciv Server</string>
+	<key>CFBundleIdentifier</key>
+	<string>freeciv.server</string>
+	<key>CFBundleVersion</key>
+	<string>${VSTRING}</string>
+	<key>CFBundleShortVersionString</key>
+	<string>${VSTRING}</string>
+	<key>CFBundleExecutable</key>
+	<string>FreecivServer</string>
+	<key>CFBundleIconFile</key>
+	<string>freeciv-server.icns</string>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>NSHighResolutionCapable</key>
+	<true/>
+</dict>
+</plist>
+EOF
+
+# Bundle executable: opens the .command file below in a Terminal window
+cat > "${SERVERCONTENTS}/MacOS/FreecivServer" <<'EOF'
+#!/bin/bash
+CONTENTS="$(cd "$(dirname "$0")/.." && pwd)"
+exec open -a Terminal "$CONTENTS/Resources/freeciv-server.command"
+EOF
+chmod +x "${SERVERCONTENTS}/MacOS/FreecivServer"
+
+# Script run inside Terminal: finds Freeciv.app and starts the server
+cat > "${SERVERCONTENTS}/Resources/freeciv-server.command" <<'EOF'
+#!/bin/bash
+# Locate Freeciv.app: next to this Freeciv Server.app first, then in /Applications
+SELF="$(cd "$(dirname "$0")/../../.." && pwd)"
+APP=""
+for CAND in "$SELF/Freeciv.app" "/Applications/Freeciv.app" ; do
+  if [ -x "$CAND/Contents/bin/freeciv-server" ] ; then
+    APP="$CAND"
+    break
+  fi
+done
+
+if [ -z "$APP" ] ; then
+  echo "Freeciv.app not found next to Freeciv Server.app or in /Applications."
+  echo "Press Enter to close."
+  read -r
+  exit 1
+fi
+
+export FREECIV_DATA_PATH="$APP/Contents/share/freeciv"
+export FREECIV_LOCALE_PATH="$APP/Contents/share/locale"
+exec "$APP/Contents/bin/freeciv-server" "$@"
+EOF
+chmod +x "${SERVERCONTENTS}/Resources/freeciv-server.command"
+
+codesign --force --deep -s - "${SERVERAPP}"
+
+# --- DMG ------------------------------------------------------------------
+
+STAGE="${DISTDIR}/dmg-stage"
+mkdir -p "${STAGE}"
+cp -R "${APPDIR}" "${STAGE}/"
+cp -R "${SERVERAPP}" "${STAGE}/"
+ln -s /Applications "${STAGE}/Applications"
+
+DMG="${DISTDIR}/Freeciv-${VSTRING}.dmg"
+rm -f "${DMG}"
+# hdiutil create is deprecated in macOS 27 in favour of diskutil image;
+# fall back to hdiutil on older systems that lack "diskutil image"
+if ! diskutil image create from --format UDZO \
+       --volumeName "Freeciv ${VSTRING}" "${STAGE}" "${DMG}" &&
+   ! hdiutil create -volname "Freeciv ${VSTRING}" -srcfolder "${STAGE}" \
+       -ov -format UDZO "${DMG}" ; then
+  echo "Failed to create DMG" >&2
+  exit 1
+fi
+rm -rf "${STAGE}"
+
+echo "Done: ${DMG}"
diff --git a/platforms/macos/homebrew-appbundle.sh b/platforms/macos/homebrew-appbundle.sh
deleted file mode 100755
index 53890358b3..0000000000
--- a/platforms/macos/homebrew-appbundle.sh
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/env bash
-
-# Freeciv - Copyright (C) 2022-2023 - Freeciv team
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2, or (at your option)
-#  any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-
-# TODO: Remove this if not needed here, and if it IS needed,
-#       investigate why meson.build isn't using proper definition
-#       by itself.
-export CXXFLAGS=-std=c++17
-export CPPFLAGS="-I$(brew --prefix readline)/include"
-export LDFLAGS="-L$(brew --prefix icu4c)/lib -L$(brew --prefix readline)/lib"
-export PKG_CONFIG_PATH="$(brew --prefix icu4c)/lib/pkgconfig"
-
-CONTENTSDIR="/Applications/Freeciv.app/Contents/"
-
-if ! [ -e "${CONTENTSDIR}" ] ; then
-
-	if ! mkdir -p "${CONTENTSDIR}" ; then
-	  echo "Failed to create \"${CONTENTSDIR}" >&2
-	  exit 1
-	fi
-
-	if ! mkdir -p "${CONTENTSDIR}Resources" ; then
-	  echo "Failed to create directory \"${CONTENTSDIR}Resources\"" >&2
-	  exit 1
-	fi
-
-	if ! cp data/freeciv-client.icns "${CONTENTSDIR}Resources" ; then
-	  echo "Failed to copy file \"freeciv-client.icns\"" >&2
-	  exit 1
-	fi
-
-	if ! mkdir -p "${CONTENTSDIR}MacOS" ; then
-	  echo "Failed to create directory \"${CONTENTSDIR}MacOS\"" >&2
-	  exit 1
-	fi
-
-	if ! echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
-<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\"
-<plist version=\"0.9\">
-<dict>
-	<key>LSEnvironment</key>
-	<dict>
-		<key>LD_LIBRARY_PATH</key>
-		<string>/Applications/Freeciv.app/Contents/lib</string>
-	</dict>
-	<key>CFBundleName</key>
-	<string>freeciv</string>
-	<key>CFBundleIdentifier</key>
-	<string>freeciv</string>
-	<key>CFBundleVersion</key>
-	<string>VER</string>
-	<key>CFBundleShortVersionString</key>
-	<string>VER</string>
-	<key>CFBundleExecutable</key>
-	<string>../bin/freeciv-gtk4</string>
-	<key>CFBundleIconFile</key>
-	<string>freeciv-client.icns</string>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>English</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleSignature</key>
-	<string>????</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-</dict>
-</plist>" > "${CONTENTSDIR}Info.plist" ; then
-	  echo "Failed to create file \"${CONTENTSDIR}Info.plist\"" >&2
-	  exit 1
-	fi
-
-	VSTRING=`./fc_version`
-
-	# Substitute VSTRING into Info.plist
-	sed -i '' -e "s/VER/${VSTRING}/g" "${CONTENTSDIR}Info.plist"
-
-	if ! echo -n "APPL????" > "${CONTENTSDIR}PkgInfo" ; then
-	  echo "Failed to create file \"${CONTENTSDIR}PkgInfo\"" >&2
-	  exit 1
-	fi
-
-fi
-
-if ! [ -e build ] ; then
-
-	if ! mkdir build ; then
-	  echo "Failed to create build directory" >&2
-	  exit 1
-	fi
-
-fi
-
-cd build || exit 1
-
-if ! meson setup .. \
-       -Dtools=manual,ruleup \
-       -Dsyslua=true \
-       -Ddebug=false \
-       -Dclients=gtk4,qt \
-       -Dfcmp=qt \
-       -Dfollowtag=macos \
-       -Ddefault_library=static \
-       -Dprefix="${CONTENTSDIR}" ||
-   ! ninja ||
-   ! ninja install
-then
-  echo "Build failed!" >&2
-  exit 1
-fi
-- 
2.53.0

