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 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. 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. On Windows, multiple paths are likely to be required, e.g.
@@ -297,24 +297,7 @@ run:
(pgadmin4) $ make pip (pgadmin4) $ make pip
On a Mac, build an application bundle in a disk image (DMG file) with: To build the macOS AppBundle, please see pkg/mac/README.
(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.
On Windows, the InnoSetup tool is required to create an installer. Download the On Windows, the InnoSetup tool is required to create an installer. Download the
Unicode version from: 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 #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 #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 #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 Bug fixes
********* *********

View File

@@ -15,20 +15,23 @@ similar):
Building: 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. 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, 2. If a value different from the default of ~/Qt/5.13.2/clang_64, is required,
e.g. 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, 3. If a value different from the default of /usr/local/pgsql is required, set
e.g. 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. 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 # Build script to create Mac App Bundle and DMG for pgAdmin4 runtime
export WD=$(cd `dirname $0` && pwd) # Exit when any command fails
export SOURCEDIR=${WD}/../.. set -e -E
export BUILDROOT=${WD}/../../mac-build
export DISTROOT=${WD}/../../dist
export VIRTUALENV=venv
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
echo "Error: pkg/mac/framework.conf not found!" 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." 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 exit 1
fi fi
if [ "x${PYTHON_HOME}" == "x" ]; then CODESIGN=1
echo "PYTHON_HOME not set. It must be set, and pointing to a Python 3 installation." if [ ! -f ${SCRIPT_DIR}/codesign.conf ]; then
exit 1 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 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 # Check if Python is working and calculate PYTHON_VERSION
if ${PYTHON_HOME}/bin/python3 -V > /dev/null 2>&1; then if ${PYTHON_EXE} -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/\.//'` PYTHON_VERSION=`${PYTHON_EXE} -V 2>&1 | awk '{print $2}' | cut -d"." -f1-2 | sed 's/\.//'`
else else
echo "Error: Python installation missing!" echo "Error: Python installation missing!"
exit 1 exit 1
fi 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." echo "Python version not supported."
exit 1 exit 1
fi fi
export PYTHON=${PYTHON_HOME}/bin/python3 if [ "x${PGADMIN_QT_DIR}" == "x" ]; then
export PIP=pip3 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
if [ "x${QTDIR}" == "x" ]; then
echo "QTDIR not set. Setting it to default."
export QTDIR=~/Qt/5.8/clang_64
fi fi
export QMAKE=${QTDIR}/bin/qmake
QMAKE=${PGADMIN_QT_DIR}/bin/qmake
if ! ${QMAKE} --version > /dev/null 2>&1; then if ! ${QMAKE} --version > /dev/null 2>&1; then
echo "Error: qmake not found. QT installation is not present or incomplete." echo "Error: qmake not found. QT installation is not present or incomplete."
exit 1 exit 1
fi fi
if [ "x${PGDIR}" == "x" ]; then if [ "x${PGADMIN_POSTGRES_DIR}" == "x" ]; then
echo "PGDIR not set. Setting it to default." echo "PGADMIN_POSTGRES_DIR not set. Setting it to the default: /usr/local/pgsql"
export PGDIR=/usr/local/pgsql export PGADMIN_POSTGRES_DIR=/usr/local/pgsql
fi fi
_get_version() { source ${SCRIPT_DIR}/build-functions.sh
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() { _setup_env
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; }
_cleanup _cleanup
_create_venv || { echo venv creation failed; exit 1; } _create_venv
_build_runtime || { echo Runtime build failed; exit 1; } _build_runtime
_build_doc _build_docs
_complete_bundle _complete_bundle
_framework_config _framework_config
_codesign_binaries _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