From 3074af101bd118772b6fe98553bed7b2096c40bb Mon Sep 17 00:00:00 2001 From: Sandeep Thakkar Date: Thu, 2 Jun 2016 13:56:56 +0100 Subject: [PATCH] Add Mac Appbundle generation support. --- .gitignore | 1 + Makefile | 20 ++++- pkg/mac/.gitignore | 3 + pkg/mac/PkgInfo | 1 + pkg/mac/README.txt | 32 +++++++ pkg/mac/build.sh | 153 ++++++++++++++++++++++++++++++++++ pkg/mac/complete-bundle.sh | 140 +++++++++++++++++++++++++++++++ pkg/mac/create-dmg.sh | 38 +++++++++ pkg/mac/licence.r | 42 ++++++++++ pkg/mac/pgadmin.Info.plist.in | 28 +++++++ runtime/Server.cpp | 27 +++++- 11 files changed, 482 insertions(+), 3 deletions(-) create mode 100644 pkg/mac/.gitignore create mode 100644 pkg/mac/PkgInfo create mode 100644 pkg/mac/README.txt create mode 100755 pkg/mac/build.sh create mode 100755 pkg/mac/complete-bundle.sh create mode 100755 pkg/mac/create-dmg.sh create mode 100644 pkg/mac/licence.r create mode 100644 pkg/mac/pgadmin.Info.plist.in diff --git a/.gitignore b/.gitignore index 5d84dd23b..562fee62f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ pgadmin4.log /pgadmin4.egg-info /MANIFEST.in /build +/mac-build /dist diff --git a/Makefile b/Makefile index adae41c44..3f4e5fc97 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,9 @@ SHELL = /bin/sh # High-level targets ######################################################################### -all: install-pip-requirements pip +all: docs install-pip-requirements pip appbundle -clean: clean-pip +clean: clean-pip clean-docs clean-appbundle ######################################################################### # Python PIP package @@ -34,6 +34,7 @@ PIP_CHECK_CMD = which pip &> /dev/null && pip show pip | grep Metadata-Version 2 PGADMIN_SRC_DIR = pgadmin4 PGADMIN_EGG = ${PGADMIN_SRC_DIR}.egg-info PGADMIN_BUILD = build +PGADMIN_MACBUILD = mac-build PGADMIN_DIST = dist PGADMIN_MANIFEST = MANIFEST.in PGADMIN_INSTALL_CMD = pip install --use-wheel --find-links=${PGADMIN_DIST} ${PGADMIN_SRC_DIR} @@ -83,9 +84,24 @@ endif install-pip: ${PGADMIN_INSTALL_CMD} +appbundle: docs + ./pkg/mac/build.sh + +docs: + LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $(MAKE) -C docs/en_US -f Makefile.sphinx html + +clean-docs: + LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $(MAKE) -C docs/en_US -f Makefile.sphinx clean + clean-pip: rm -rf ${PGADMIN_SRC_DIR} rm -rf ${PGADMIN_EGG} rm -rf ${PGADMIN_BUILD} rm -rf ${PGADMIN_DIST} rm -f ${PGADMIN_MANIFEST} + +clean-appbundle: + rm -rf ${PGADMIN_MACBUILD} + rm -rf ${PGADMIN_DIST}/pgadmin4*.dmg* + +.PHONY: docs diff --git a/pkg/mac/.gitignore b/pkg/mac/.gitignore new file mode 100644 index 000000000..7d29cb06c --- /dev/null +++ b/pkg/mac/.gitignore @@ -0,0 +1,3 @@ +# Global excludes across all subdirectories +debug.pgadmin.Info.plist +pgadmin.Info.plist diff --git a/pkg/mac/PkgInfo b/pkg/mac/PkgInfo new file mode 100644 index 000000000..bd04210fb --- /dev/null +++ b/pkg/mac/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/pkg/mac/README.txt b/pkg/mac/README.txt new file mode 100644 index 000000000..987bf2492 --- /dev/null +++ b/pkg/mac/README.txt @@ -0,0 +1,32 @@ +Building pgAdmin4.dmg on Mac OS X +================================= + +Required Packages (Either build the sources or get them from macports or similar): + +1. Python installation + - Python 2.6 or above from https://www.python.org/ + +2. QT installation + - Qt 4 or 5 from http://www.qt.io/ + +3. PostgreSQL installation + - PostgreSQL 9.1 or above from http://www.postgresql.org/ + +Building: + +1. Set the PYTHON_HOME environment variable to the Python root installation directory, e.g. + + export PYTHON_HOME=/System/Library/Frameworks/Python.framework/Versions/2.7 + +2. Set the QTDIR environment variable to the QT root installation directory, e.g. + + export QTDIR=~/Qt/5.5/clang_64 + +3. Set the PGDIR environment variable to the PostgreSQL installation directory, e.g. + + export PGDIR=/usr/local/pgsql + +4. To build, go to pgAdmin4 source root directory and execute "make appbundle". This will + create the python virtual environment and install all the required python modules mentioned in the + requirements file using pip, build the runtime code and finally create the app bundle and the DMG + in ./dist directory diff --git a/pkg/mac/build.sh b/pkg/mac/build.sh new file mode 100755 index 000000000..d39a01bc0 --- /dev/null +++ b/pkg/mac/build.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# Build script to create Mac App Bundle and DMG for pgAdmin4 runtime + +export WD=$(cd `dirname $0` && pwd) +export SOURCEDIR=$WD/../.. +export BUILDROOT=$WD/../../mac-build +export DISTROOT=$WD/../../dist +export VIRTUALENV=venv + +if [ "x$PYTHON_HOME" == "x" ]; then + echo "PYTHON_HOME not set. Setting it to default" + export PYTHON_HOME=/System/Library/Frameworks/Python.framework/Versions/2.7 + export PYTHON_VERSION=27 +fi + +# Check if Python is working and calculate PYTHON_VERSION +if $PYTHON_HOME/bin/python2 -V > /dev/null 2>&1; then + export PYTHON_VERSION=`$PYTHON_HOME/bin/python2 -V 2>&1 | awk '{print $2}' | cut -d"." -f1-2 | sed 's/\.//'` +elif $PYTHON_HOME/bin/python3 -V > /dev/null 2>&1; then + export PYTHON_VERSION=`$PYTHON_HOME/bin/python3 -V 2>&1 | awk '{print $2}' | cut -d"." -f1-2 | sed 's/\.//'` +else + echo "Error: Python installation missing!" + exit 1 +fi + +if [ "$PYTHON_VERSION" -gt "34" -a "$PYTHON_VERSION" -lt "26" ]; then + echo "Python version not supported" + exit 1 +fi + +if [ "$PYTHON_VERSION" -ge "30" ]; then + export PYTHON=$PYTHON_HOME/bin/python3 + export PIP=pip3 + export REQUIREMENTS=requirements_py3.txt +else + export PYTHON=$PYTHON_HOME/bin/python2 + export PIP=pip + export REQUIREMENTS=requirements_py2.txt +fi + +if [ "x$QTDIR" == "x" ]; then + echo "QTDIR not set. Setting it to default" + export QTDIR=~/Qt/5.5/clang_64 +fi +export QMAKE=$QTDIR/bin/qmake +if ! $QMAKE --version > /dev/null 2>&1; then + echo "Error: qmake not found. QT installation is not present or incomplete." + exit 1 +fi + +if [ "x$PGDIR" == "x" ]; then + echo "PGDIR not set. Setting it to default" + export PGDIR=/usr/local/pgsql +fi + +_get_version() { + export APP_RELEASE=`grep "^APP_RELEASE" web/config.py | cut -d"=" -f2 | sed 's/ //g'` + export APP_REVISION=`grep "^APP_REVISION" web/config.py | cut -d"=" -f2 | sed 's/ //g'` + export APP_NAME=`grep "^APP_NAME" web/config.py | cut -d"=" -f2 | sed "s/'//g" | sed 's/^ //'` + export APP_BUNDLE_NAME=$APP_NAME.app + export APP_LONG_VERSION=$APP_RELEASE.$APP_REVISION + export APP_SHORT_VERSION=`echo $APP_LONG_VERSION | cut -d . -f1,2` + export APP_SUFFIX=`grep "^APP_SUFFIX" web/config.py | cut -d"=" -f2 | sed 's/ //g' | sed "s/'//g"` + if [ ! -z $APP_SUFFIX ]; then + export APP_LONG_VERSION=$APP_LONG_VERSION-$APP_SUFFIX + fi +} + +_cleanup() { + echo "Cleaning up the old environment and app bundle" + rm -rf $SOURCEDIR/runtime/pgAdmin4.app + rm -rf $BUILDROOT + rm -f $DISTROOT/pgadmin4*.dmg +} + +_create_python_virtualenv() { + export PATH=$PGDIR/bin:$PATH + export LD_LIBRARY_PATH=$PGDIR/lib:$_LD_LIBRARY_PATH + test -d $BUILDROOT || mkdir $BUILDROOT || exit 1 + cd $BUILDROOT + test -d $VIRTUALENV || virtualenv -p $PYTHON $VIRTUALENV || exit 1 + source $VIRTUALENV/bin/activate + $PIP install -r $SOURCEDIR/$REQUIREMENTS || { echo PIP install failed. Please resolve the issue and rerun the script; exit 1; } + + # Move the python directory to python so that the private environment path is found by the application. + export PYMODULES_PATH=`python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"` + export DIR_PYMODULES_PATH=`dirname $PYMODULES_PATH` + if test -d $DIR_PYMODULES_PATH; then + mv $DIR_PYMODULES_PATH $DIR_PYMODULES_PATH/../python + fi +} + +_build_runtime() { + _create_python_virtualenv || exit 1 + cd $SOURCEDIR/runtime + $QMAKE || { echo qmake failed; exit 1; } + make || { echo make failed; exit 1; } + cp -r pgAdmin4.app "$BUILDROOT/$APP_BUNDLE_NAME" +} + +_build_doc() { + cd $SOURCEDIR/docs/en_US + # Commenting the build as it is taken care by Makefile + #LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 make -f Makefile.sphinx html || exit 1 + test -d "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources" || "mkdir -p $BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources" + test -d "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/docs/en_US" || mkdir -p "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/docs/en_US" + cp -r _build/html "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/docs/en_US/" || exit 1 +} + +_complete_bundle() { + cd $SOURCEDIR/pkg/mac + + # Replace the place holders with the current version + sed -e "s/PGADMIN_LONG_VERSION/$APP_LONG_VERSION/g" -e "s/PGADMIN_SHORT_VERSION/$APP_SHORT_VERSION/g" pgadmin.Info.plist.in > pgadmin.Info.plist + + # copy Python private environment to app bundle + cp -PR $BUILDROOT/$VIRTUALENV "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/" || exit 1 + + # remove the python bin and include from app bundle as it is not needed + rm -rf "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/$VIRTUALENV/bin" "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/$VIRTUALENV/include" + rm -rf "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/$VIRTUALENV/.Python" + + # run complete-bundle to copy the dependent libraries and frameworks and fix the rpaths + ./complete-bundle.sh "$BUILDROOT/$APP_BUNDLE_NAME" || { echo complete-bundle.sh failed; exit 1; } + + # copy the web directory to the bundle as it is required by runtime + cp -r $SOURCEDIR/web "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/" || exit 1 + cd "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/web" + rm -f pgadmin4.db config_local.* + echo "SERVER_MODE = False" > config_local.py + echo "MINIFY_HTML = False" >> config_local.py + echo "HELP_PATH = '../../../docs/en_US/html/'" >> config_local.py + + # Remove the .pyc files if any + cd "$BUILDROOT/$APP_BUNDLE_NAME" + find . -name *.pyc | xargs rm -f + +} + +_create_dmg() { + cd $SOURCEDIR + ./pkg/mac/create-dmg.sh || { echo create-dmg.sh failed; exit 1; } + # Clean the mac-build/ on successful build + rm -rf $BUILDROOT/* +} + +_get_version || { echo Could not get versioning; exit 1; } +_cleanup +_build_runtime || { echo Runtime build failed; exit 1; } +_build_doc +_complete_bundle +_create_dmg diff --git a/pkg/mac/complete-bundle.sh b/pkg/mac/complete-bundle.sh new file mode 100755 index 000000000..f954d1154 --- /dev/null +++ b/pkg/mac/complete-bundle.sh @@ -0,0 +1,140 @@ +#!/bin/sh + +bundle="$1" + +if ! test -d "$bundle" ; then + echo "$bundle is no bundle!" >&2 + exit 1 +fi + +if test -z $QTDIR ; then + echo "QTDIR environment variable not set" + exit 1 +else + echo "QTDIR=$QTDIR" +fi + +test -d "$bundle/Contents/Resources" || mkdir -p "$bundle/Contents/Resources" || exit 1 +# Create qt.conf so that app knows where the Plugins are present +cat >> "$bundle/Contents/Resources/qt.conf" << EOF +[Paths] +Plugins = PlugIns +EOF + +test -d "$bundle/Contents/Frameworks" || mkdir -p "$bundle/Contents/Frameworks" || exit 1 +test -d "$bundle/Contents/PlugIns/platforms" || mkdir -p "$bundle/Contents/PlugIns/platforms" || exit 1 +cp -f $QTDIR/plugins/platforms/libqcocoa.dylib "$bundle/Contents/PlugIns/platforms" || { echo libqcocoa.dylib not found in $QTDIR/plugins/platforms; exit 1; } +cp -f $PGDIR/lib/libpq.5.dylib "$bundle/Contents/Frameworks" || { echo libpq.5.dylib not found in $PGDIR; exit 1; } + +function CompleteSingleApp() { + local bundle=$1 tag=$(basename "$1") todo todo_old fw_relpath lib lib_bn nested_app na_relpath + + echo "Completing app: $bundle" + pushd "$bundle" > /dev/null + + #We skip nested apps here - those are treated specially + todo=$(file `find ./ -perm +0111 ! -type d ! -path "*.app/*" ! -name "*.app"` | grep -E "Mach-O 64-bit" | awk -F ':| ' '{ORS=" "; print $1}' | uniq) + + echo "App: $tag: Found executables: $todo" + while test "$todo" != ""; do + todo_old=$todo ; + todo="" ; + for todo_obj in $todo_old; do + echo "App: $tag: Post-processing: $todo_obj" + + #Figure out the relative path from todo_obj to Contents/Frameworks + fw_relpath=$(echo "$todo_obj" |\ + sed -n 's|^\(\.//*\)\(\([^/][^/]*/\)*\)[^/][^/]*$|\2|gp' | \ + sed -n 's|[^/][^/]*/|../|gp' \ + )"Contents/Frameworks" + fw_relpath_old=$fw_relpath + + fw_loc="Contents/Frameworks" + + #Find all libraries $todo_obj depends on, but skip system libraries + for lib in $( + otool -L $todo_obj | \ + grep "Qt\|dylib\|Frameworks\|PlugIns" | grep -v ":" | sed 's/(.*//' | egrep -v '(/usr/lib)|(/System)|@executable_path@' \ + ) $(otool -L $todo_obj | grep "Python" | grep -v ":" | sed 's/(.*//' \ + ); do + if echo $lib | grep "PlugIns\|libqcocoa" > /dev/null; then + lib_loc="Contents/PlugIns/platforms" + elif echo $lib | grep "Qt" > /dev/null; then + qtfw_path="$(dirname $lib | sed 's|.*\(Qt.*framework\)|\1|')" + lib_loc="Contents/Frameworks/$qtfw_path" + if [ "$(basename $todo_obj)" = "$lib" ]; then + lib_loc="$(dirname $todo_obj)" + qtfw_path=$(echo $lib_loc | sed 's/Contents\/Frameworks\///') + fi + elif echo $lib | grep "Python" > /dev/null; then + pyfw_path="$(dirname $lib | sed 's|.*\(Python.*framework\)|\1|')" + lib_loc="Contents/Frameworks/$pyfw_path" + if [ "$(basename $todo_obj)" = "$lib" ]; then + lib_loc="$(dirname $todo_obj)" + pyfw_path=$(echo $lib_loc | sed 's/Contents\/Frameworks\///') + fi + else + lib_loc="Contents/Frameworks" + fi + lib_bn="$(basename "$lib")" ; + if ! test -f "$lib_loc/$lib_bn"; then + target_file="" + target_path="" + echo "App: $tag: Adding symlink: $lib_bn (because of: $todo_obj)" + # Copy the QT and Python framework + if echo $lib | grep Qt > /dev/null ; then + test -d $lib_loc || mkdir -p $lib_loc + cp $QTDIR/lib/$qtfw_path/$lib_bn $lib_loc/ + elif echo $lib | grep Python > /dev/null ; then + test -d $lib_loc || mkdir -p $lib_loc + cp -R "$lib" "$lib_loc/$lib_bn" + else + cp -R "$lib" "$lib_loc/$lib_bn" + fi + if ! test -L "$lib_loc/$lib_bn"; then + chmod 755 "$lib_loc/$lib_bn" + else + target_file=$(readlink "$lib") + target_path=$(dirname "$lib")/$target_file + echo "App: $tag: Adding symlink target: $target_path" + cp "$target_path" "$lib_loc/$target_file" + chmod 755 "$lib_loc/$target_file" + fi + echo "Rewriting ID in $lib_loc/$lib_bn to $lib_bn" + echo install_name_tool -id "$lib_bn" "$lib_loc/$lib_bn" + install_name_tool \ + -id "$lib_bn" \ + "$lib_loc/$lib_bn" || exit 1 + todo="$todo ./$lib_loc/$lib_bn" + fi + if echo $lib | grep Qt > /dev/null ; then + fw_relpath="$fw_relpath/$qtfw_path" + fi + if echo $lib | grep Python > /dev/null ; then + fw_relpath="$fw_relpath/$pyfw_path" + fi + echo "Rewriting library $lib to @loader_path/$fw_relpath/$lib_bn in $todo_obj" + echo install_name_tool -change "$lib" "@loader_path/$fw_relpath/$lib_bn" "$todo_obj" + install_name_tool -change \ + "$lib" \ + "@loader_path/$fw_relpath/$lib_bn" \ + "$todo_obj" || exit 1 + install_name_tool -change \ + "$target_path" \ + "@loader_path/$fw_relpath/$target_file" \ + "$todo_obj" || exit 1 + fw_relpath="$fw_relpath_old" + done + done + done + + # Fix the rpaths for psycopg module + find "$bundle/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libpq.5.dylib @loader_path/../../../../../../Frameworks/libpq.5.dylib + find "$bundle/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libssl.1.0.0.dylib @loader_path/../../../../../../Frameworks/libssl.1.0.0.dylib + find "$bundle/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libcrypto.1.0.0.dylib @loader_path/../../../../../../Frameworks/libcrypto.1.0.0.dylib + + echo "App completed: $bundle" + popd > /dev/null +} + +CompleteSingleApp "$bundle" diff --git a/pkg/mac/create-dmg.sh b/pkg/mac/create-dmg.sh new file mode 100755 index 000000000..1d290abb6 --- /dev/null +++ b/pkg/mac/create-dmg.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# move to the directory where we want to create the DMG +test -d $DISTROOT || mkdir $DISTROOT +cd $DISTROOT + +DMG_SOURCES="./../mac-build/$APP_BUNDLE_NAME" +DMG_LICENCE=./../pkg/mac/licence.r +DMG_VOLUME_NAME=$APP_NAME +DMG_NAME=`echo $DMG_VOLUME_NAME | sed 's/ //g' | awk '{print tolower($0)}'` +DMG_IMAGE=$DMG_NAME-$APP_LONG_VERSION.dmg +HDIUTIL=/usr/bin/hdiutil +REZ="/usr/bin/Rez /System/Library/Frameworks/Carbon.framework/Versions/A/Headers/*.r" + +DMG_DIR=./$DMG_IMAGE.src + +if test -e "$DMG_DIR"; then + echo "Directory $DMG_DIR already exists. Please delete it manually." >&2 + exit 1 +fi + +echo "Cleaning up" +rm -f "$DMG_IMAGE" || exit 1 +mkdir "$DMG_DIR" || exit 1 + +echo "Copying data into temporary directory" +for src in "$DMG_SOURCES"; do + cp -R "$src" "$DMG_DIR" || exit 1 +done + +echo "Creating image" +$HDIUTIL create -quiet -srcfolder "$DMG_DIR" -format UDZO -volname "$DMG_VOLUME_NAME" -ov "$DMG_IMAGE" || exit 1 +rm -rf "$DMG_DIR" || exit 1 + +echo "Attaching License to image" +$HDIUTIL unflatten -quiet "$DMG_IMAGE" || exit 1 +$REZ "$DMG_LICENCE" -a -o "$DMG_IMAGE" || exit 1 +$HDIUTIL flatten -quiet "$DMG_IMAGE" || exit 1 diff --git a/pkg/mac/licence.r b/pkg/mac/licence.r new file mode 100644 index 000000000..88a89afc7 --- /dev/null +++ b/pkg/mac/licence.r @@ -0,0 +1,42 @@ +data 'LPic' (5000) { + $"0000 0001 0000 0000 0000" +}; + +resource 'STR#' (5000, "English buttons") { + { /* array StringArray: 9 elements */ + /* [1] */ + "English", + /* [2] */ + "Agree", + /* [3] */ + "Disagree", + /* [4] */ + "Print", + /* [5] */ + "Save...", + /* [6] */ + "IMPORTANT - Read this License Agreement carefully before clicking on " + "the \"Agree\" button. By clicking on the \"Agree\" button, you agree " + "to be bound by the terms of the License Agreement.", + /* [7] */ + "Software License Agreement", + /* [8] */ + "This text cannot be saved. This disk may be full or locked, or the file " + "may be locked.", + /* [9] */ + "Unable to print. Make sure you've selected a printer." + } +}; + +data 'TEXT' (5000, "English") { + "pgAdmin 4\n" + "\n" + "Copyright (C) 2013 - 2016, The pgAdmin Development Team\n" + "\n" + "Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.\n" + "\n" + "IN NO EVENT SHALL THE PGADMIN DEVELOPMENT TEAM BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE PGADMIN DEVELOPMENT TEAM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + "\n" + "THE PGADMIN DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN \"AS IS\" BASIS, AND THE PGADMIN DEVELOPMENT TEAM HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n" +}; + diff --git a/pkg/mac/pgadmin.Info.plist.in b/pkg/mac/pgadmin.Info.plist.in new file mode 100644 index 000000000..fb57ac894 --- /dev/null +++ b/pkg/mac/pgadmin.Info.plist.in @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + pgAdmin4 + CFBundleGetInfoString + pgAdmin4 PGADMIN_LONG_VERSION + CFBundleIconFile + pgAdmin4.icns + CFBundleIdentifier + org.postgresql.pgadmin + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + PGADMIN_SHORT_VERSION + CFBundleSignature + ???? + CFBundleVersion + PGADMIN_LONG_VERSION + CSResourcesFileMapped + + + diff --git a/runtime/Server.cpp b/runtime/Server.cpp index 8691224c6..fd930f6dd 100644 --- a/runtime/Server.cpp +++ b/runtime/Server.cpp @@ -23,7 +23,7 @@ #include "Server.h" Server::Server(quint16 port) -{ +{ // Appserver port m_port = port; m_wcAppName = NULL; @@ -48,6 +48,26 @@ Server::Server(quint16 port) QSettings settings; QString python_path = settings.value("PythonPath").toString(); +#ifdef Q_OS_MAC + + // In the case we're running in a release appbundle, we need to ensure the + // bundled virtual env is included in the Python path. We include it at the + // end, so expert users can override the path, but we do not save it, because + // if users move the app bundle, we'll end up with dead entries + + // Get the application directory + QString app_dir = qApp->applicationDirPath(); + + // Build (and canonicalise) the virtual environment path + QString get_pymodules_path = (app_dir + "/../Resources/venv/lib/python/site-packages"); + QFileInfo fi(get_pymodules_path); + QString pymodules_path = fi.canonicalFilePath(); + + // Append the path, if it's not already there + if (!python_path.contains(pymodules_path)) + python_path.append(pymodules_path); +#endif + if (python_path.length() > 0) { // Split the path setting into individual entries @@ -66,6 +86,8 @@ Server::Server(quint16 port) #endif } } + python_path = settings.value("PythonPath").toString(); + qDebug() << "Python path: " << python_path; } Server::~Server() @@ -86,7 +108,9 @@ bool Server::Init() paths.append("../web/"); // Linux source tree paths.append("../../web/"); // Windows source tree paths.append("../../../../web/"); // Mac source tree (in a dev env) +#ifdef Q_OS_MAC paths.append("../Resources/web/"); // Mac source tree (in a release app bundle) +#endif paths.append(settings.value("ApplicationPath").toString()); // System configured value paths.append(""); // Should be last! @@ -124,6 +148,7 @@ void Server::run() // Set the port number PyRun_SimpleString(QString("PGADMIN_PORT = %1").arg(m_port).toLatin1()); + // Run the app! #ifdef PYTHON2 PyObject* PyFileObject = PyFile_FromString(m_appfile.toUtf8().data(), (char *)"r"); if (PyRun_SimpleFile(PyFile_AsFile(PyFileObject), m_appfile.toUtf8().data()) != 0)