Cleanup and refactor the macOS build scripts. Fixes #5525

This commit is contained in:
Dave Page 2020-05-18 10:22:59 +01:00
parent 5337514c63
commit eac5fe5157
10 changed files with 439 additions and 448 deletions

21
README
View File

@ -255,7 +255,7 @@ icon and select the Configuration option to open the configuration dialogue.
On a Linux/Mac system, the Python Path will typically consist of a single path
to the virtual environment's site-packages directory, e.g.
/Users/<USERNAME>/.virtualenvs/pgadmin4/lib/python2.7/site-packages
/Users/<USERNAME>/.virtualenvs/pgadmin4/lib/python3.8/site-packages
On Windows, multiple paths are likely to be required, e.g.
@ -297,24 +297,7 @@ run:
(pgadmin4) $ make pip
On a Mac, build an application bundle in a disk image (DMG file) with:
(pgadmin4) $ make appbundle
Configure the framework.conf to match the QT and Python versions the app is
being built with:
$ cp $PGADMIN4_SRC/pkg/mac/framework.conf.in $PGADMIN4_SRC/pkg/mac/framework.conf
$ vi $PGADMIN4_SRC/pkg/mac/framework.conf
If you have an Apple code signing certificate, both the app bundle and disk
image can be automatically signed by configuring signing:
$ cp $PGADMIN4_SRC/pkg/mac/codesign.conf.in $PGADMIN4_SRC/pkg/mac/codesign.conf
$ vi $PGADMIN4_SRC/pkg/mac/codesign.conf
Edit the file as appropriate, ensuring the various version numbers are correct
and that the appropriate developer ID is specified.
To build the macOS AppBundle, please see pkg/mac/README.
On Windows, the InnoSetup tool is required to create an installer. Download the
Unicode version from:

View File

@ -22,6 +22,7 @@ Housekeeping
| `Issue #5444 <https://redmine.postgresql.org/issues/5444>`_ - Cleanup Python detection in the runtime project file.
| `Issue #5455 <https://redmine.postgresql.org/issues/5455>`_ - Refactor pgAdmin4.py so it can be imported and is a lot more readable.
| `Issue #5493 <https://redmine.postgresql.org/issues/5493>`_ - Search object UI improvements.
| `Issue #5525 <https://redmine.postgresql.org/issues/5525>`_ - Cleanup and refactor the macOS build scripts.
Bug fixes
*********

View File

@ -15,20 +15,23 @@ similar):
Building:
1. Set the PYTHON_HOME environment variable to the Python root installation
1. If a value different from the default of /usr/local/python is required, set
the PGADMIN_PYTHON_DIR environment variable to the Python root installation
directory, e.g.
export PYTHON_HOME=/opt/local
export PGADMIN_PYTHON_DIR=/opt/local
2. Set the QTDIR environment variable to the QT root installation directory,
e.g.
2. If a value different from the default of ~/Qt/5.13.2/clang_64, is required,
set the PGADMIN_QT_DIR environment variable to the QT root installation
directory, e.g.
export QTDIR=~/Qt/5.14.2/clang_64
export PGADMIN_QT_DIR=~/Qt/5.14.2/clang_64
3. Set the PGDIR environment variable to the PostgreSQL installation directory,
e.g.
3. If a value different from the default of /usr/local/pgsql is required, set
the PGADMIN_POSTGRES_DIR environment variable to the PostgreSQL installation
directory, e.g.
export PGDIR=/usr/local/pgsql
export PGADMIN_POSTGRES_DIR=/opt/local/pgsql
4. Copy framework.conf.in to framework.conf, and edit the values accordingly.

380
pkg/mac/build-functions.sh Normal file
View File

@ -0,0 +1,380 @@
_setup_env() {
APP_RELEASE=`grep "^APP_RELEASE" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
APP_REVISION=`grep "^APP_REVISION" web/config.py | cut -d"=" -f2 | sed 's/ //g'`
APP_NAME=`grep "^APP_NAME" web/config.py | cut -d"=" -f2 | sed "s/'//g" | sed 's/^ //'`
APP_LONG_VERSION=${APP_RELEASE}.${APP_REVISION}
APP_SHORT_VERSION=`echo ${APP_LONG_VERSION} | cut -d . -f1,2`
APP_SUFFIX=`grep "^APP_SUFFIX" web/config.py | cut -d"=" -f2 | sed 's/ //g' | sed "s/'//g"`
if [ ! -z ${APP_SUFFIX} ]; then
APP_LONG_VERSION=${APP_LONG_VERSION}-${APP_SUFFIX}
fi
BUNDLE_DIR="${BUILD_ROOT}/${APP_NAME}.app"
}
_cleanup() {
echo Cleaning up the old environment and app bundle...
rm -rf ${SOURCE_DIR}/runtime/*.app
rm -rf ${BUILD_ROOT}
rm -f ${DIST_ROOT}/*.dmg
}
_create_venv() {
PATH=${PGADMIN_POSTGRES_DIR}/bin:${PATH}
LD_LIBRARY_PATH=${PGADMIN_POSTGRES_DIR}/lib:${LD_LIBRARY_PATH}
test -d ${BUILD_ROOT} || mkdir ${BUILD_ROOT}
cd ${BUILD_ROOT}
${PYTHON_EXE} -m venv --copies venv
source venv/bin/activate
pip install --no-cache-dir --no-binary psycopg2 -r ${SOURCE_DIR}/requirements.txt
# Figure the source path for use when completing the venv
SOURCE_PYMODULES_PATH=$(dirname $("${PYTHON_EXE}" -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"))
# Figure the target path for use when completing the venv
# Use "python" here as we want the venv path
TARGET_PYMODULES_PATH=$(dirname $(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"))
# Copy in the additional system python modules
cp -R ${SOURCE_PYMODULES_PATH}/* "${TARGET_PYMODULES_PATH}/"
# Link the python<version> directory to python so that the private environment path is found by the application.
ln -s "$(basename ${TARGET_PYMODULES_PATH})" "${TARGET_PYMODULES_PATH}/../python"
# Remove tests
find venv -name "test" -type d -print0 | xargs -0 rm -rf
find venv -name "tests" -type d -print0 | xargs -0 rm -rf
}
_build_runtime() {
cd ${SOURCE_DIR}/runtime
make clean
${QMAKE}
make
cp -r pgAdmin4.app "${BUNDLE_DIR}"
}
_build_docs() {
cd ${SOURCE_DIR}/docs/en_US
test -d "${BUNDLE_DIR}/Contents/Resources/docs/en_US" || mkdir -p "${BUNDLE_DIR}/Contents/Resources/docs/en_US"
cp -r _build/html "${BUNDLE_DIR}/Contents/Resources/docs/en_US/"
}
_complete_bundle() {
cd ${SCRIPT_DIR}
# Copy the binary utilities into place
mkdir -p "${BUNDLE_DIR}/Contents/SharedSupport/"
cp "${PGADMIN_POSTGRES_DIR}/bin/pg_dump" "${BUNDLE_DIR}/Contents/SharedSupport/"
cp "${PGADMIN_POSTGRES_DIR}/bin/pg_dumpall" "${BUNDLE_DIR}/Contents/SharedSupport/"
cp "${PGADMIN_POSTGRES_DIR}/bin/pg_restore" "${BUNDLE_DIR}/Contents/SharedSupport/"
cp "${PGADMIN_POSTGRES_DIR}/bin/psql" "${BUNDLE_DIR}/Contents/SharedSupport/"
# 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 ${BUILD_ROOT}/venv "${BUNDLE_DIR}/Contents/Resources/"
# Remove any TCL-related files that may cause us problems
find "${BUNDLE_DIR}/Contents/Resources/venv/" -name "_tkinter*" -print0 | xargs -0 rm -f
test -d "${BUNDLE_DIR}/Contents/Resources" || mkdir -p "${BUNDLE_DIR}/Contents/Resources"
# Create qt.conf so that app knows where the Plugins are present
cat >> "${BUNDLE_DIR}/Contents/Resources/qt.conf" << EOF
[Paths]
Plugins = PlugIns
EOF
test -d "${BUNDLE_DIR}/Contents/Frameworks" || mkdir -p "${BUNDLE_DIR}/Contents/Frameworks"
test -d "${BUNDLE_DIR}/Contents/PlugIns/platforms" || mkdir -p "${BUNDLE_DIR}/Contents/PlugIns/platforms"
test -d "${BUNDLE_DIR}/Contents/PlugIns/imageformats" || mkdir -p "${BUNDLE_DIR}/Contents/PlugIns/imageformats"
cp -f ${PGADMIN_QT_DIR}/plugins/platforms/libqcocoa.dylib "${BUNDLE_DIR}/Contents/PlugIns/platforms"
cp -f ${PGADMIN_QT_DIR}/plugins/imageformats/libqsvg.dylib "${BUNDLE_DIR}/Contents/PlugIns/imageformats"
cp -f ${PGADMIN_POSTGRES_DIR}/lib/libpq.5.dylib "${BUNDLE_DIR}/Contents/Frameworks"
local todo todo_old fw_relpath lib lib_bn
pushd "${BUNDLE_DIR}" > /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}')
echo "Found executables: ${todo}"
while test "${todo}" != ""; do
todo_old=${todo} ;
todo="" ;
for todo_obj in ${todo_old}; do
echo "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}
# 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@'); do
if echo ${lib} | grep "PlugIns\|libqcocoa" > /dev/null; then
lib_loc="Contents/PlugIns/platforms"
elif echo ${lib} | grep "PlugIns\|libqsvg" > /dev/null; then
lib_loc="Contents/PlugIns/imageformats"
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 "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}
echo Copying -R ${PGADMIN_QT_DIR}/lib/${qtfw_path}/${lib_bn} to ${lib_loc}/
cp ${PGADMIN_QT_DIR}/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 "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}"
install_name_tool -id "${lib_bn}" "${lib_loc}/${lib_bn}"
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
chmod +w ${todo_obj}
echo "Rewriting library ${lib} to @loader_path/${fw_relpath}/${lib_bn} in ${todo_obj}"
install_name_tool -change "${lib}" "@loader_path/${fw_relpath}/${lib_bn}" "${todo_obj}"
install_name_tool -change "${target_path}" "@loader_path/${fw_relpath}/${target_file}" "${todo_obj}"
fw_relpath="${fw_relpath_old}"
done
done
done
# Fix the rpaths for psycopg module
find "${BUNDLE_DIR}/Contents/Resources/venv/" -name _psycopg.so -print0 | xargs -0 install_name_tool -change libpq.5.dylib @loader_path/../../../../../../Frameworks/libpq.5.dylib
find "${BUNDLE_DIR}/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_DIR}/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_DIR}"
popd > /dev/null
pushd ${SOURCE_DIR}/web > /dev/null
yarn install
yarn run bundle
curl https://curl.haxx.se/ca/cacert.pem -o cacert.pem -s
popd > /dev/null
# copy the web directory to the bundle as it is required by runtime
cp -r ${SOURCE_DIR}/web "${BUNDLE_DIR}/Contents/Resources/"
cd "${BUNDLE_DIR}/Contents/Resources/web"
rm -f pgadmin4.db config_local.*
rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
find . -name "tests" -type d -print0 | xargs -0 rm -rf
find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
find . -name ".DS_Store" -print0 | xargs -0 rm -f
echo "SERVER_MODE = False" > config_distro.py
echo "HELP_PATH = '../../../docs/en_US/html/'" >> config_distro.py
echo "DEFAULT_BINARY_PATHS = {" >> config_distro.py
echo " 'pg': '\$DIR/../../SharedSupport'," >> config_distro.py
echo " 'ppas': ''" >> config_distro.py
echo "}" >> config_distro.py
# Remove the .pyc files if any
find "${BUNDLE_DIR}" -name "*.pyc" -print0 | xargs -0 rm -f
}
_framework_config() {
# Get the config
source ${SCRIPT_DIR}/framework.conf
echo Reorganising the framework structure...
# Create "Current" and "Current/Resources" inside each of the framework dirs
find "${BUNDLE_DIR}/Contents/Frameworks"/*framework -type d -name "Versions" | while read -r framework_dir; do
pushd "${framework_dir}" > /dev/null
# Create framework 'Current' soft link
VERSION_NUMBER=`ls -1`
ln -s ${VERSION_NUMBER} Current
# Create "Resources" subdirectory
if [ ! -d Current/Resources ]; then
mkdir Current/Resources
fi
popd > /dev/null
done
# Stuff for Qt framework files only
find "${BUNDLE_DIR}/Contents/Frameworks" -type d -name "Qt*framework" | while read -r framework_dir; do
pushd "${framework_dir}" > /dev/null
# Create soft link to the framework binary
ln -s Versions/Current/Qt*
# Create soft link to the framework Resources dir
ln -s Versions/Current/Resources
# Create the Info.plist files
MYNAME=`ls -1 Qt*`
if [ -f Resources/Info.plist ]; then
chmod +w Resources/Info.plist
fi
sed 's/__SHORT_VERSION__/${QT_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Qt5" | sed 's/__FULL_VERSION__/${QT_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist"
popd > /dev/null
done
# Same thing, but specific to the Python framework dir
find "${BUNDLE_DIR}/Contents/Frameworks" -type d -name "P*framework" | while read -r framework_dir; do
pushd "${framework_dir}" > /dev/null
# Create soft link to the framework binary
ln -s Versions/Current/Py*
# Create soft link to the framework Resources dir
ln -s Versions/Current/Resources
# Create the Info.plist file
MYNAME=`ls -1 Py*`
sed 's/__SHORT_VERSION__/${PYTHON_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Python" | sed 's/__FULL_VERSION__/${PYTHON_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist"
popd > /dev/null
done
}
_codesign_binaries() {
if [ ${CODESIGN} -eq 0 ]; then
return
fi
if [ -z "${DEVELOPER_ID}" ] ; then
echo "Developer ID Application not found in codesign.conf" >&2
exit 1
fi
if [ -z "${DEVELOPER_BUNDLE_ID}" ]; then
echo "Developer Bundle Identifier not found in codesign.conf" >&2
fi
echo Signing ${BUNDLE_DIR} binaries...
IFS=$'\n'
for i in $(find "${BUNDLE_DIR}" -type f -perm +111 -exec file "{}" \; | grep -E "Mach-O executable|Mach-O 64-bit executable|Mach-O 64-bit bundle" | awk -F":| \\\(" '{print $1}' | uniq)
do
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
done
echo Signing ${BUNDLE_DIR} libraries...
for i in $(find "${BUNDLE_DIR}" -type f -name "*.dylib*")
do
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
done
}
_codesign_bundle() {
if [ ${CODESIGN} -eq 0 ]; then
return
fi
# Sign the .app
echo Signing ${BUNDLE_DIR}...
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${BUNDLE_DIR}"
# Verify it worked
echo Verifying the signature...
codesign --verify --verbose --deep --force "${BUNDLE_DIR}"
echo ${BUNDLE_DIR} successfully signed.
}
_create_dmg() {
# move to the directory where we want to create the DMG
test -d ${DIST_ROOT} || mkdir ${DIST_ROOT}
cd ${DIST_ROOT}
DMG_LICENCE=./../pkg/mac/licence.rtf
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
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}"
mkdir "${DMG_DIR}"
echo "Copying data into temporary directory"
cp -R "${BUNDLE_DIR}" "${DMG_DIR}"
echo "Creating image"
hdiutil create -quiet -srcfolder "$DMG_DIR" -fs HFS+ -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}"
rm -rf "${DMG_DIR}"
echo Attaching License to image...
python ${SCRIPT_DIR}/dmg-license.py "${DMG_IMAGE}" "${DMG_LICENCE}" -c bz2
}
_codesign_dmg() {
if [ ${CODESIGN} -eq 0 ]; then
return
fi
DMG_VOLUME_NAME=${APP_NAME}
DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
DMG_IMAGE=${DIST_ROOT}/${DMG_NAME}-${APP_LONG_VERSION}.dmg
if ! test -f "${DMG_IMAGE}" ; then
echo "${DMG_IMAGE} is no disk image!" >&2
exit 1
fi
# Sign the .app
echo Signing ${DMG_IMAGE}...
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${DMG_IMAGE}"
# Verify it worked
echo Verifying the signature...
codesign --verify --verbose --force "${DMG_IMAGE}"
echo ${DMG_IMAGE} successfully signed.
}

View File

@ -2,13 +2,19 @@
# 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
# Exit when any command fails
set -e -E
if [ ! -f ${SOURCEDIR}/pkg/mac/framework.conf ]; then
# Debugging shizz
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
trap 'if [ $? -ne 0 ]; then echo "\"${last_command}\" command filed with exit code $?."; fi' EXIT
SCRIPT_DIR=$(cd `dirname $0` && pwd)
SOURCE_DIR=$(realpath ${SCRIPT_DIR}/../..)
BUILD_ROOT=$(realpath ${SCRIPT_DIR}/../..)/mac-build
DIST_ROOT=$(realpath ${SCRIPT_DIR}/../..)/dist
if [ ! -f ${SCRIPT_DIR}/framework.conf ]; then
echo
echo "Error: pkg/mac/framework.conf not found!"
echo "Copy pkg/mac/framework.conf.in to pkg/mac/framework.conf and edit as required for the current system."
@ -16,244 +22,61 @@ if [ ! -f ${SOURCEDIR}/pkg/mac/framework.conf ]; then
exit 1
fi
if [ "x${PYTHON_HOME}" == "x" ]; then
echo "PYTHON_HOME not set. It must be set, and pointing to a Python 3 installation."
exit 1
CODESIGN=1
if [ ! -f ${SCRIPT_DIR}/codesign.conf ]; then
echo
echo "******************************************************************"
echo "* ${SCRIPT_DIR}/codesign.conf not found. NOT signing the binaries."
echo "******************************************************************"
echo
CODESIGN=0
sleep 5
else
source ${SCRIPT_DIR}/codesign.conf
fi
if [ "x${PGADMIN_PYTHON_DIR}" == "x" ]; then
echo "PGADMIN_PYTHON_DIR not set. Setting it to the default: /usr/local/python"
export PGADMIN_PYTHON_DIR=/usr/local/python
fi
PYTHON_EXE=${PGADMIN_PYTHON_DIR}/bin/python3
# Check if Python is working and calculate PYTHON_VERSION
if ${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/\.//'`
if ${PYTHON_EXE} -V > /dev/null 2>&1; then
PYTHON_VERSION=`${PYTHON_EXE} -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 "38" -a "${PYTHON_VERSION}" -lt "34" ]; then
if [ "${PYTHON_VERSION}" -gt "38" ] && [ "${PYTHON_VERSION}" -lt "34" ]; then
echo "Python version not supported."
exit 1
fi
export PYTHON=${PYTHON_HOME}/bin/python3
export PIP=pip3
if [ "x${QTDIR}" == "x" ]; then
echo "QTDIR not set. Setting it to default."
export QTDIR=~/Qt/5.8/clang_64
if [ "x${PGADMIN_QT_DIR}" == "x" ]; then
echo "PGADMIN_QT_DIR not set. Setting it to the default: ~/Qt/5.13.2/clang_64"
export PGADMIN_QT_DIR=~/Qt/5.13.2/clang_64
fi
export QMAKE=${QTDIR}/bin/qmake
QMAKE=${PGADMIN_QT_DIR}/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
if [ "x${PGADMIN_POSTGRES_DIR}" == "x" ]; then
echo "PGADMIN_POSTGRES_DIR not set. Setting it to the default: /usr/local/pgsql"
export PGADMIN_POSTGRES_DIR=/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
}
source ${SCRIPT_DIR}/build-functions.sh
_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_venv() {
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} --always-copy ${VIRTUALENV} || exit 1
source ${VIRTUALENV}/bin/activate
${PIP} install --no-cache-dir --no-binary psycopg2 -r ${SOURCEDIR}/requirements.txt || { echo PIP install failed. Please resolve the issue and rerun the script; exit 1; }
# Figure out some paths for use when completing the venv
# Use "python" here as we want the venv path
export PYMODULES_PATH=`python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"`
export DIR_PYMODULES_PATH=`dirname ${PYMODULES_PATH}`
# Use $PYTHON here as we want the system path
export PYSYSLIB_PATH=`${PYTHON} -c "import sys; print('%s/lib/python%d.%.d' % (sys.prefix, sys.version_info.major, sys.version_info.minor))"`
# Symlink in the rest of the Python libs. This is required because the runtime
# will clear PYTHONHOME for safety, which has the side-effect of preventing
# it from finding modules that are not explicitly included in the venv
cd ${DIR_PYMODULES_PATH}
# Files
for FULLPATH in ${PYSYSLIB_PATH}/*.py; do
FILE=${FULLPATH##*/}
if [ ! -e ${FILE} ]; then
cp ${FULLPATH} ${FILE}
fi
done
# Paths
for FULLPATH in ${PYSYSLIB_PATH}/*/; do
FULLPATH=${FULLPATH%*/}
FILE=${FULLPATH##*/}
if [ ! -e ${FILE} ]; then
cp -R ${FULLPATH} ${FILE}
fi
done
# Remove tests
cd site-packages
find . -name "test" -type d -print0 | xargs -0 rm -rf
find . -name "tests" -type d -print0 | xargs -0 rm -rf
# Link the python<version> directory to python so that the private environment path is found by the application.
if test -d ${DIR_PYMODULES_PATH}; then
ln -s $(basename ${DIR_PYMODULES_PATH}) ${DIR_PYMODULES_PATH}/../python
fi
}
_build_runtime() {
cd ${SOURCEDIR}/runtime
make clean
PGADMIN_PYTHON_DIR=${PYTHON_HOME} ${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
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
# Copy the binary utilities into place
mkdir -p "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
cp "${PGDIR}/bin/pg_dump" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
cp "${PGDIR}/bin/pg_dumpall" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
cp "${PGDIR}/bin/pg_restore" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
cp "${PGDIR}/bin/psql" "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/SharedSupport/" || exit 1
# 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 any TCL-related files that may cause us problems
find "${BUILDROOT}/${APP_BUNDLE_NAME}/Contents/Resources/${VIRTUALENV}/" -name "_tkinter*" -print0 | xargs -0 rm -f
# 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; }
pushd ${SOURCEDIR}/web
yarn install || exit 1
yarn run bundle || exit 1
curl https://curl.haxx.se/ca/cacert.pem -o cacert.pem -s || { echo Failed to download cacert.pem; exit 1; }
popd
# 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.*
rm -rf karma.conf.js package.json node_modules/ regression/ tools/ pgadmin/static/js/generated/.cache
find . -name "tests" -type d -print0 | xargs -0 rm -rf
find . -name "feature_tests" -type d -print0 | xargs -0 rm -rf
find . -name ".DS_Store" -print0 | xargs -0 rm -f
echo "SERVER_MODE = False" > config_distro.py
echo "HELP_PATH = '../../../docs/en_US/html/'" >> config_distro.py
echo "DEFAULT_BINARY_PATHS = {" >> config_distro.py
echo " 'pg': '\$DIR/../../SharedSupport'," >> config_distro.py
echo " 'ppas': ''" >> config_distro.py
echo "}" >> config_distro.py
# Remove the .pyc files if any
cd "${BUILDROOT}/${APP_BUNDLE_NAME}"
find . -name *.pyc -print0 | xargs -0 rm -f
}
_framework_config() {
cd ${SOURCEDIR}/pkg/mac
./framework-config.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo "framework-config.sh failed"; exit 1; }
}
_codesign_binaries() {
cd ${SOURCEDIR}/pkg/mac
if [ ! -f codesign.conf ]; then
echo
echo "******************************************************************"
echo "* codesign.conf not found. NOT signing the binaries."
echo "******************************************************************"
echo
sleep 5
return
fi
./codesign-binaries.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo codesign-binaries.sh failed; exit 1; }
}
_codesign_bundle() {
cd ${SOURCEDIR}/pkg/mac
if [ ! -f codesign.conf ]; then
echo
echo "******************************************************************"
echo "* codesign.conf not found. NOT signing the bundle."
echo "******************************************************************"
echo
sleep 5
return
fi
./codesign-bundle.sh "${BUILDROOT}/${APP_BUNDLE_NAME}" || { echo codesign-bundle.sh failed; exit 1; }
}
_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}/*
}
_codesign_dmg() {
cd ${SOURCEDIR}/pkg/mac
if [ ! -f codesign.conf ]; then
echo
echo "******************************************************************"
echo "* codesign.conf not found. NOT signing the disk image."
echo "******************************************************************"
echo
sleep 5
return
fi
./codesign-dmg.sh || { echo codesign-bundle.sh failed; exit 1; }
}
_get_version || { echo Could not get versioning; exit 1; }
_setup_env
_cleanup
_create_venv || { echo venv creation failed; exit 1; }
_build_runtime || { echo Runtime build failed; exit 1; }
_build_doc
_create_venv
_build_runtime
_build_docs
_complete_bundle
_framework_config
_codesign_binaries

View File

@ -1,36 +0,0 @@
#!/bin/sh
BUNDLE="$1"
if ! test -d "${BUNDLE}" ; then
echo "${BUNDLE} is no bundle!" >&2
exit 1
fi
# Get the config
source codesign.conf
if [ -z "${DEVELOPER_ID}" ] ; then
echo "Developer ID Application not found in codesign.conf" >&2
exit 1
fi
if [ -z "${DEVELOPER_BUNDLE_ID}" ]; then
echo "Developer Bundle Identifier not found in codesign.conf" >&2
fi
echo Signing ${BUNDLE} binaries
IFS=$'\n'
for i in $(find "${BUNDLE}" -type f)
do
file "${i}" | grep -E "Mach-O executable|Mach-O 64-bit executable|Mach-O 64-bit bundle"
if [ $? -eq 0 ] ; then
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
fi
done
echo Signing ${BUNDLE} libraries
for i in $(find "${BUNDLE}" -type f -name "*.dylib*")
do
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "$i"
done

View File

@ -1,26 +0,0 @@
#!/bin/sh
BUNDLE="$1"
if ! test -d "${BUNDLE}" ; then
echo "${BUNDLE} is no bundle!" >&2
exit 1
fi
# Get the config
source codesign.conf
# Sign the .app
echo Signing ${BUNDLE}
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${BUNDLE}"
# Verify it worked
echo Verifying the signature
codesign --verify --verbose --deep --force "${BUNDLE}"
RETURN_STATUS=$?
if [ ${RETURN_STATUS} -ne 0 ]; then
echo Code signing did not work, check the log
exit 1
else
echo ${BUNDLE} successfully signed
fi

View File

@ -1,30 +0,0 @@
#!/bin/bash
DMG_VOLUME_NAME=${APP_NAME}
DMG_NAME=`echo ${DMG_VOLUME_NAME} | sed 's/ //g' | awk '{print tolower($0)}'`
DMG_IMAGE=${DISTROOT}/${DMG_NAME}-${APP_LONG_VERSION}.dmg
if ! test -f "${DMG_IMAGE}" ; then
echo "${DMG_IMAGE} is no disk image!" >&2
exit 1
fi
# Get the config
source codesign.conf
SCRIPT_DIR=`pwd`
# Sign the .app
echo Signing ${DMG_IMAGE}
codesign --deep --force --verify --verbose --timestamp --options runtime -i "${DEVELOPER_BUNDLE_ID}" --sign "${DEVELOPER_ID}" "${DMG_IMAGE}"
# Verify it worked
echo Verifying the signature
codesign --verify --verbose --force "${DMG_IMAGE}"
RETURN_STATUS=$?
if [ ${RETURN_STATUS} -ne 0 ]; then
echo ERROR: Code signing did not work
exit 1
else
echo ${DMG_IMAGE} successfully signed
fi

View File

@ -1,36 +0,0 @@
#!/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.rtf
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"
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" -fs HFS+ -format UDZO -volname "${DMG_VOLUME_NAME}" -ov "${DMG_IMAGE}" || exit 1
rm -rf "${DMG_DIR}" || exit 1
echo "Attaching License to image"
python ./../pkg/mac/dmg-license.py "${DMG_IMAGE}" "${DMG_LICENCE}" -c bz2

View File

@ -1,71 +0,0 @@
#!/bin/sh
BUNDLE="$1"
if ! test -d "${BUNDLE}" ; then
echo "${BUNDLE} is no bundle!" >&2
exit 1
fi
# Get the config
source framework.conf
SCRIPT_DIR=`pwd`
echo Reorganising the framework structure
# Create "Current" and "Current/Resources" inside each of the framework dirs
MYDIR=`pwd`
find "${BUNDLE}/Contents/Frameworks"/*framework -type d -name "Versions" | while read -r framework_dir; do
cd "${framework_dir}"
# Create framework 'Current' soft link
VERSION_NUMBER=`ls -1`
ln -s ${VERSION_NUMBER} Current || { echo "link creation in framework-config.sh failed"; exit 1; }
# Create "Resources" subdirectory
if [ ! -d Current/Resources ]; then
mkdir Current/Resources
fi
cd "${MYDIR}"
done
# Stuff for Qt framework files only
find "${BUNDLE}/Contents/Frameworks" -type d -name "Qt*framework" | while read -r framework_dir; do
cd "${framework_dir}"
# Create soft link to the framework binary
ln -s Versions/Current/Qt* || { echo "link creation in framework-config.sh failed"; exit 1; }
# Create soft link to the framework Resources dir
ln -s Versions/Current/Resources || { echo "link creation in framework-config.sh failed"; exit 1; }
# Create the Info.plist files
MYNAME=`ls -1 Qt*`
if [ -f Resources/Info.plist ]; then
chmod +w Resources/Info.plist
fi
sed 's/__SHORT_VERSION__/${QT_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Qt5" | sed 's/__FULL_VERSION__/${QT_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist" || { echo "sed replacement in framework-config.sh failed"; exit 1; }
cd "${MYDIR}"
done
# Same thing, but specific to the Python framework dir
find "${BUNDLE}/Contents/Frameworks" -type d -name "P*framework" | while read -r framework_dir; do
cd "${framework_dir}"
# Create soft link to the framework binary
ln -s Versions/Current/Py* || { echo "link creation in framework-config.sh failed"; exit 1; }
# Create soft link to the framework Resources dir
ln -s Versions/Current/Resources || { echo "link creation in framework-config.sh failed"; exit 1; }
# Create the Info.plist file
MYNAME=`ls -1 Py*`
sed 's/__SHORT_VERSION__/${PYTHON_SHORT_VERSION}/' "${SCRIPT_DIR}/Info.plist-template_Python" | sed 's/__FULL_VERSION__/${PYTHON_FULL_VERSION}/' | sed "s/__FRAMEWORK_NAME__/${MYNAME}/" > "Resources/Info.plist" || { echo "sed replacement in framework-config.sh failed"; exit 1; }
cd "${MYDIR}"
done
echo ${BUNDLE} framework config finished