mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2026-03-13 09:24:25 +08:00
327 lines
9.5 KiB
Bash
Executable File
327 lines
9.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
DO_NOTARIZE=false
|
||
# parse args
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--notarize) DO_NOTARIZE=true ;;
|
||
esac
|
||
done
|
||
|
||
|
||
### CONFIGURATION
|
||
|
||
APP_NAME="heidisql"
|
||
BUNDLE_NAME="${APP_NAME}.app"
|
||
APP_DIR="$(pwd)/${BUNDLE_NAME}"
|
||
|
||
# Path to the already built Lazarus executable
|
||
EXECUTABLE_SRC="$(pwd)/out/heidisql"
|
||
EXECUTABLE_TRG="${APP_DIR}/Contents/MacOS/${APP_NAME}"
|
||
|
||
# Homebrew prefix (auto-detected; override if needed)
|
||
BREW_PREFIX="$(brew --prefix 2>/dev/null || echo "/opt/homebrew")"
|
||
|
||
# Your Developer ID identity, as shown by: security find-identity -v -p codesigning
|
||
TEAM_ID="QBD4CC6FH3"
|
||
CODESIGN_IDENTITY="Developer ID Application: Ansgar Becker (${TEAM_ID})"
|
||
|
||
# Name for notarytool keychain profile (store once with notarytool store-credentials)
|
||
NOTARY_PROFILE="notarytool-profile"
|
||
|
||
LOCALES_ZIP_URL="https://www.heidisql.com/downloads/locale/HeidiSQL-locale.zip"
|
||
|
||
### INSTALL REQUIRED LIBRARIES VIA HOMEBREW
|
||
|
||
# MySQL client (libmysqlclient.dylib)
|
||
brew list mysql-client >/dev/null 2>&1 || brew install mysql-client
|
||
|
||
# PostgreSQL client (libpq.dylib)
|
||
brew list libpq >/dev/null 2>&1 || brew install libpq
|
||
|
||
# SQLite (libsqlite3.dylib; comes with macOS, but install via brew for consistency)
|
||
brew list sqlite >/dev/null 2>&1 || brew install sqlite
|
||
|
||
# MariaDB Connector/C (libmariadb*.dylib)
|
||
brew list mariadb-connector-c >/dev/null 2>&1 || brew install mariadb-connector-c
|
||
|
||
# OpenSSL 1.1 for TFPHTTPClient
|
||
brew list openssl@1.1 >/dev/null 2>&1 || brew install openssl@1.1
|
||
|
||
MYSQL_LIB_DIR="${BREW_PREFIX}/opt/mysql-client/lib"
|
||
PG_LIB_DIR="${BREW_PREFIX}/opt/libpq/lib"
|
||
SQLITE_LIB_DIR="${BREW_PREFIX}/opt/sqlite/lib"
|
||
MARIADB_LIB_DIR="${BREW_PREFIX}/opt/mariadb-connector-c/lib"
|
||
MYSQL_PLUGIN_DIR="${MYSQL_LIB_DIR}/plugin"
|
||
MARIADB_PLUGIN_DIR="${MARIADB_LIB_DIR}/mariadb/plugin"
|
||
OPENSSL11_LIB_DIR="${BREW_PREFIX}/opt/openssl@1.1/lib"
|
||
|
||
### PREPARE APP BUNDLE STRUCTURE
|
||
|
||
rm -rf "${APP_DIR}"
|
||
mkdir -p "${APP_DIR}/Contents/MacOS"
|
||
mkdir -p "${APP_DIR}/Contents/Resources"
|
||
mkdir -p "${APP_DIR}/Contents/Frameworks" # where we will put .dylib and .so files
|
||
|
||
# Copy main executable
|
||
cp "${EXECUTABLE_SRC}" "${EXECUTABLE_TRG}"
|
||
chmod +x "${EXECUTABLE_TRG}"
|
||
codesign --force --options runtime --timestamp --sign "${CODESIGN_IDENTITY}" "${EXECUTABLE_TRG}"
|
||
|
||
# Minimal Info.plist (adjust identifiers/versions as needed)
|
||
cat > "${APP_DIR}/Contents/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>${APP_NAME}</string>
|
||
<key>CFBundleDisplayName</key>
|
||
<string>${APP_NAME}</string>
|
||
<key>CFBundleIdentifier</key>
|
||
<string>com.example.${APP_NAME}</string>
|
||
<key>CFBundleVersion</key>
|
||
<string>1.0</string>
|
||
<key>CFBundleShortVersionString</key>
|
||
<string>1.0</string>
|
||
<key>CFBundleExecutable</key>
|
||
<string>${APP_NAME}</string>
|
||
<key>CFBundlePackageType</key>
|
||
<string>APPL</string>
|
||
<key>LSMinimumSystemVersion</key>
|
||
<string>15.0</string>
|
||
<key>NSHighResolutionCapable</key>
|
||
<true/>
|
||
<key>CFBundleIconFile</key>
|
||
<string>heidisql</string>
|
||
</dict>
|
||
</plist>
|
||
EOF
|
||
|
||
|
||
### COPY INI FILES INTO RESOURCES
|
||
cp extra/ini/*.ini "${APP_DIR}/Contents/Resources/"
|
||
cp res/heidisql.icns "${APP_DIR}/Contents/Resources/"
|
||
|
||
|
||
### FUNCTION: COPY A DYLIB AND ITS DEPENDENCIES
|
||
|
||
copy_and_rewrite_dylib() {
|
||
local src_dylib="$1"
|
||
local dest_dir="${APP_DIR}/Contents/Frameworks"
|
||
mkdir -p "${dest_dir}"
|
||
|
||
local base
|
||
base="$(basename "${src_dylib}")"
|
||
local dest_dylib="${dest_dir}/${base}"
|
||
|
||
# If already copied, skip
|
||
if [[ -f "${dest_dylib}" ]]; then
|
||
return
|
||
fi
|
||
|
||
echo "Copying ${src_dylib} -> ${dest_dylib}"
|
||
cp "${src_dylib}" "${dest_dylib}"
|
||
|
||
# Make it writable for install_name_tool
|
||
chmod u+w "${dest_dylib}"
|
||
|
||
# Change its own install name to @rpath/@loader_path-style inside the app
|
||
install_name_tool -id "@rpath/${base}" "${dest_dylib}"
|
||
|
||
# Find direct dependencies
|
||
local deps
|
||
# otool -L output: first line is the file itself, subsequent lines are dependencies
|
||
deps=$(otool -L "${dest_dylib}" | tail -n +2 | awk '{print $1}')
|
||
|
||
for dep in ${deps}; do
|
||
case "${dep}" in
|
||
/usr/lib/*|/System/*)
|
||
# System libraries: keep them as-is, do not copy
|
||
continue
|
||
;;
|
||
esac
|
||
|
||
# If dependency already points into @rpath/@loader_path, just rewrite to Frameworks
|
||
local dep_base
|
||
dep_base="$(basename "${dep}")"
|
||
local new_dep_path="@loader_path/../Frameworks/${dep_base}"
|
||
|
||
echo " Rewriting dep ${dep} -> ${new_dep_path}"
|
||
install_name_tool -change "${dep}" "${new_dep_path}" "${dest_dylib}"
|
||
|
||
# If dep is an absolute path, copy that dylib too
|
||
if [[ -f "${dep}" ]]; then
|
||
copy_and_rewrite_dylib "${dep}"
|
||
fi
|
||
done
|
||
}
|
||
|
||
|
||
### COPY CLIENT LIBRARIES AND DEPENDENCIES
|
||
|
||
# libmysqlclient.dylib
|
||
if ls "${MYSQL_LIB_DIR}"/libmysqlclient*.dylib >/dev/null 2>&1; then
|
||
for f in "${MYSQL_LIB_DIR}"/libmysqlclient*.dylib; do
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: No libmysqlclient*.dylib found in ${MYSQL_LIB_DIR}" >&2
|
||
fi
|
||
|
||
# libpq.dylib
|
||
if [[ -f "${PG_LIB_DIR}/libpq.dylib" ]]; then
|
||
copy_and_rewrite_dylib "${PG_LIB_DIR}/libpq.dylib"
|
||
else
|
||
# libpq often has versioned names
|
||
if ls "${PG_LIB_DIR}"/libpq*.dylib >/dev/null 2>&1; then
|
||
for f in "${PG_LIB_DIR}"/libpq*.dylib; do
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: No libpq*.dylib found in ${PG_LIB_DIR}" >&2
|
||
fi
|
||
fi
|
||
|
||
# libsqlite3.dylib
|
||
if [[ -f "${SQLITE_LIB_DIR}/libsqlite3.dylib" ]]; then
|
||
copy_and_rewrite_dylib "${SQLITE_LIB_DIR}/libsqlite3.dylib"
|
||
elif ls "${SQLITE_LIB_DIR}"/libsqlite3*.dylib >/dev/null 2>&1; then
|
||
for f in "${SQLITE_LIB_DIR}"/libsqlite3*.dylib; do
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: No libsqlite3*.dylib found in ${SQLITE_LIB_DIR}" >&2
|
||
fi
|
||
|
||
# libmariadb*.dylib (MariaDB Connector/C)
|
||
if ls "${MARIADB_LIB_DIR}"/libmariadb*.dylib >/dev/null 2>&1; then
|
||
for f in "${MARIADB_LIB_DIR}"/libmariadb*.dylib; do
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: No libmariadb*.dylib found in ${MARIADB_LIB_DIR}" >&2
|
||
fi
|
||
|
||
# MARIADB PLUGIN .so FILES
|
||
if ls "${MARIADB_PLUGIN_DIR}"/*.so >/dev/null 2>&1; then
|
||
for f in "${MARIADB_PLUGIN_DIR}"/*.so; do
|
||
[[ -f "${f}" ]] || continue
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: MariaDB plugin directory not found: ${MARIADB_PLUGIN_DIR}" >&2
|
||
fi
|
||
|
||
# MYSQL PLUGIN .so FILES
|
||
if ls "${MYSQL_PLUGIN_DIR}"/*.so >/dev/null 2>&1; then
|
||
for f in "${MYSQL_PLUGIN_DIR}"/*.so; do
|
||
[[ -f "${f}" ]] || continue
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: MySQL plugin directory not found: ${MYSQL_PLUGIN_DIR}" >&2
|
||
fi
|
||
|
||
# libssl.1.1.dylib and libcrypto.1.1.dylib (names may vary slightly)
|
||
if ls "${OPENSSL11_LIB_DIR}"/libssl*.1.1*.dylib >/dev/null 2>&1; then
|
||
for f in "${OPENSSL11_LIB_DIR}"/libssl*.1.1*.dylib; do
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: No libssl*.1.1*.dylib found in ${OPENSSL11_LIB_DIR}" >&2
|
||
fi
|
||
|
||
if ls "${OPENSSL11_LIB_DIR}"/libcrypto*.1.1*.dylib >/dev/null 2>&1; then
|
||
for f in "${OPENSSL11_LIB_DIR}"/libcrypto*.1.1*.dylib; do
|
||
copy_and_rewrite_dylib "${f}"
|
||
done
|
||
else
|
||
echo "WARNING: No libcrypto*.1.1*.dylib found in ${OPENSSL11_LIB_DIR}" >&2
|
||
fi
|
||
|
||
|
||
### FIX MAIN EXECUTABLE’S REFERENCES TO CLIENT LIBS
|
||
|
||
# Helper: rewrite dependency of the main executable to bundled Frameworks
|
||
rewrite_exe_dep () {
|
||
local pattern="$1" # e.g. libmysqlclient
|
||
local dep
|
||
dep=$(otool -L "${EXECUTABLE_TRG}" | awk "/${pattern}.*dylib/ {print \$1}" | head -n1 || true)
|
||
if [[ -n "${dep}" ]]; then
|
||
local base
|
||
base="$(basename "${dep}")"
|
||
local new="@loader_path/../Frameworks/${base}"
|
||
echo "Rewriting ${EXECUTABLE_TRG} dep ${dep} -> ${new}"
|
||
chmod u+w "${EXECUTABLE_TRG}"
|
||
install_name_tool -change "${dep}" "${new}" "${EXECUTABLE_TRG}"
|
||
fi
|
||
}
|
||
|
||
rewrite_exe_dep "libmysqlclient"
|
||
rewrite_exe_dep "libpq"
|
||
rewrite_exe_dep "libsqlite3"
|
||
rewrite_exe_dep "libmariadb"
|
||
rewrite_exe_dep "libssl.3"
|
||
rewrite_exe_dep "libssl" # OpenSSL 1.1
|
||
rewrite_exe_dep "libcrypto"
|
||
|
||
### DOWNLOAD AND EXTRACT LOCALE FILES
|
||
|
||
if [[ -n "${LOCALES_ZIP_URL}" ]]; then
|
||
LOCALE_DEST="${APP_DIR}/Contents/Resources/locale"
|
||
mkdir -p "${LOCALE_DEST}"
|
||
|
||
TMP_ZIP="$(mktemp -t heidisql_locales.XXXXXX).zip"
|
||
echo "Downloading locales from ${LOCALES_ZIP_URL}..."
|
||
curl -L --fail --show-error "${LOCALES_ZIP_URL}" -o "${TMP_ZIP}"
|
||
|
||
echo "Extracting locales to ${LOCALE_DEST}..."
|
||
/usr/bin/unzip -o "${TMP_ZIP}" -d "${LOCALE_DEST}" >/dev/null
|
||
|
||
rm -f "${TMP_ZIP}"
|
||
fi
|
||
|
||
|
||
echo "Done. Bundled app is at: ${APP_DIR}"
|
||
|
||
|
||
### SIGN ALL DYLIBS AND THE APP BUNDLE
|
||
|
||
echo "Signing embedded libraries..."
|
||
find "${APP_DIR}/Contents" -type f \( -name "*.dylib" -o -name "*.so" \) | while read -r f; do
|
||
echo " Signing ${f}"
|
||
codesign --force --options runtime --timestamp --sign "${CODESIGN_IDENTITY}" "${f}"
|
||
done
|
||
|
||
echo "Signing main app bundle..."
|
||
codesign --force --options runtime --timestamp --deep --sign "${CODESIGN_IDENTITY}" "${APP_DIR}"
|
||
|
||
echo "Verifying code signature..."
|
||
codesign --verify --deep --strict --verbose=2 "${APP_DIR}"
|
||
|
||
|
||
### ZIP, NOTARIZE, AND STAPLE
|
||
|
||
if ! $DO_NOTARIZE; then
|
||
echo "Skip notarization (--notarize not given)"
|
||
exit 0
|
||
fi
|
||
|
||
ZIP_PATH="${APP_DIR}.zip"
|
||
rm -f "${ZIP_PATH}"
|
||
echo "Zipping app for notarization..."
|
||
/usr/bin/zip -r -y "${ZIP_PATH}" "${BUNDLE_NAME}"
|
||
|
||
echo "Submitting to Apple notary service..."
|
||
xcrun notarytool submit "${ZIP_PATH}" \
|
||
--keychain-profile "${NOTARY_PROFILE}" \
|
||
--team-id "${TEAM_ID}" \
|
||
--wait
|
||
|
||
echo "Stapling notarization ticket..."
|
||
xcrun stapler staple "${APP_DIR}"
|
||
|
||
echo "Notarization complete. Distribute: ${APP_DIR}"
|