Skip to content

ci(macos): Avoid linking against Homebrew #1755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 6, 2025
16 changes: 9 additions & 7 deletions .github/workflows/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:

env:
PIP_BREAK_SYSTEM_PACKAGES: "1"
LIBPQ_VERSION: "16.0"
OPENSSL_VERSION: "1.1.1w"

jobs:
sdist: # {{{
Expand Down Expand Up @@ -59,10 +61,6 @@ jobs:
linux: # {{{
if: true

env:
LIBPQ_VERSION: "16.0"
OPENSSL_VERSION: "1.1.1w"

strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -162,6 +160,12 @@ jobs:
- name: Checkout repos
uses: actions/checkout@v4

- name: Cache libpq build
uses: actions/cache@v4
with:
path: /tmp/libpq.build
key: libpq-${{ env.LIBPQ_VERSION }}-macos-${{ matrix.arch }}

- name: Build wheels
uses: pypa/[email protected]
env:
Expand All @@ -172,12 +176,10 @@ jobs:
export PYTHONPATH={project} &&
python -c "import tests; tests.unittest.main(defaultTest='tests.test_suite')"
CIBW_ENVIRONMENT: >-
MACOSX_DEPLOYMENT_TARGET=${{ matrix.macver }}.0
PG_VERSION=16
PACKAGE_NAME=psycopg2-binary
PSYCOPG2_TESTDB=postgres
PSYCOPG2_TEST_FAST=1
PATH="/usr/local/opt/postgresql@${PG_VERSION}/bin:$PATH"
PATH="/tmp/libpq.build/bin:$PATH"

- name: Upload artifacts
uses: actions/upload-artifact@v4
Expand Down
127 changes: 108 additions & 19 deletions scripts/build/build_libpq.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

# Build a modern version of libpq and depending libs from source on Centos 5
# Build a modern version of libpq and depending libs from source on Centos 5, Alpine or macOS

set -euo pipefail
set -x
Expand All @@ -12,30 +12,69 @@ postgres_version="${LIBPQ_VERSION}"
# last release: https://www.openssl.org/source/
openssl_version="${OPENSSL_VERSION}"

# last release: https://kerberos.org/dist/
krb5_version="1.21.3"

# last release: https://www.gnu.org/software/gettext/
gettext_version="0.22.5"

# last release: https://openldap.org/software/download/
ldap_version="2.6.3"
ldap_version="2.6.8"

# last release: https://github.com/cyrusimap/cyrus-sasl/releases
sasl_version="2.1.28"

export LIBPQ_BUILD_PREFIX=${LIBPQ_BUILD_PREFIX:-/tmp/libpq.build}

if [[ -f "${LIBPQ_BUILD_PREFIX}/lib/libpq.so" ]]; then
case "$(uname)" in
Darwin)
ID=macos
library_suffix=dylib
;;

Linux)
source /etc/os-release
library_suffix=so
;;

*)
echo "$0: unexpected Operating system: '$(uname)'" >&2
exit 1
;;
esac

if [[ -f "${LIBPQ_BUILD_PREFIX}/lib/libpq.${library_suffix}" ]]; then
echo "libpq already available: build skipped" >&2
exit 0
fi

source /etc/os-release

case "$ID" in
centos)
yum update -y
yum install -y zlib-devel krb5-devel pam-devel
curl="$(which curl)"
;;

alpine)
apk upgrade
apk add --no-cache zlib-dev krb5-dev linux-pam-dev openldap-dev openssl-dev
curl="$(which curl)"
;;

macos)
brew install automake m4 libtool
# If available, libpq seemingly insists on linking against homebrew's
# openssl no matter what so remove it. Since homebrew's curl depends on
# it, force use of system curl.
brew uninstall --force --ignore-dependencies openssl gettext
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't suppose you know a better way to force which copy of a library to link against? The build system seems to add homebrew to its search path even if I env -i away all the brew related environment variables.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe with -L/-l options in the compiler? Not sure.

curl="/usr/bin/curl"
# The deployment target should be <= to that of the oldest supported Python version.
# e.g. https://www.python.org/downloads/release/python-380/
if [ "$(uname -m)" == "x86_64" ]; then
export MACOSX_DEPLOYMENT_TARGET=10.9
else
export MACOSX_DEPLOYMENT_TARGET=11.0
fi
;;

*)
Expand All @@ -44,12 +83,12 @@ case "$ID" in
;;
esac

if [ "$ID" == "centos" ]; then
if [ "$ID" == "centos" ] || [ "$ID" == "macos" ]; then

# Build openssl if needed
openssl_tag="OpenSSL_${openssl_version//./_}"
openssl_dir="openssl-${openssl_tag}"
if [ ! -d "${openssl_dir}" ]; then curl -sL \
if [ ! -d "${openssl_dir}" ]; then "$curl" -sL \
https://github.com/openssl/openssl/archive/${openssl_tag}.tar.gz \
| tar xzf -

Expand All @@ -70,7 +109,55 @@ if [ "$ID" == "centos" ]; then
fi


if [ "$ID" == "centos" ]; then
if [ "$ID" == "macos" ]; then

# Build kerberos if needed
krb5_dir="krb5-${krb5_version}/src"
if [ ! -d "${krb5_dir}" ]; then
"$curl" -sL \
curl -sL "https://kerberos.org/dist/krb5/$(echo 1.21.3 | grep -oE '\d+\.\d+')/krb5-${krb5_version}.tar.gz" \
| tar xzf -

cd "${krb5_dir}"

./configure --prefix=${LIBPQ_BUILD_PREFIX} \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib
make
else
cd "${krb5_dir}"
fi

make install
cd ../..

fi


if [ "$ID" == "macos" ]; then

# Build gettext if needed
gettext_dir="gettext-${gettext_version}"
if [ ! -d "${gettext_dir}" ]; then
"$curl" -sL \
curl -sL "https://ftp.gnu.org/pub/gnu/gettext/gettext-${gettext_version}.tar.gz" \
| tar xzf -

cd "${gettext_dir}"

./configure --prefix=${LIBPQ_BUILD_PREFIX} --disable-java \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib
make -C gettext-runtime all
else
cd "${gettext_dir}"
fi

make -C gettext-runtime install
cd ..

fi


if [ "$ID" == "centos" ] || [ "$ID" == "macos" ]; then

# Build libsasl2 if needed
# The system package (cyrus-sasl-devel) causes an amazing error on i686:
Expand All @@ -79,14 +166,14 @@ if [ "$ID" == "centos" ]; then
sasl_tag="cyrus-sasl-${sasl_version}"
sasl_dir="cyrus-sasl-${sasl_tag}"
if [ ! -d "${sasl_dir}" ]; then
curl -sL \
"$curl" -sL \
https://github.com/cyrusimap/cyrus-sasl/archive/${sasl_tag}.tar.gz \
| tar xzf -

cd "${sasl_dir}"

autoreconf -i
./configure --prefix=${LIBPQ_BUILD_PREFIX} \
./configure --prefix=${LIBPQ_BUILD_PREFIX} --disable-macos-framework \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib
make
else
Expand All @@ -102,13 +189,13 @@ if [ "$ID" == "centos" ]; then
fi


if [ "$ID" == "centos" ]; then
if [ "$ID" == "centos" ] || [ "$ID" == "macos" ]; then

# Build openldap if needed
ldap_tag="${ldap_version}"
ldap_dir="openldap-${ldap_tag}"
if [ ! -d "${ldap_dir}" ]; then
curl -sL \
"$curl" -sL \
https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${ldap_tag}.tgz \
| tar xzf -

Expand All @@ -129,7 +216,7 @@ if [ "$ID" == "centos" ]; then
make -C libraries/liblber/ install
make -C libraries/libldap/ install
make -C include/ install
chmod +x ${LIBPQ_BUILD_PREFIX}/lib/{libldap,liblber}*.so*
chmod +x ${LIBPQ_BUILD_PREFIX}/lib/{libldap,liblber}*.${library_suffix}*
cd ..

fi
Expand All @@ -139,17 +226,19 @@ fi
postgres_tag="REL_${postgres_version//./_}"
postgres_dir="postgres-${postgres_tag}"
if [ ! -d "${postgres_dir}" ]; then
curl -sL \
"$curl" -sL \
https://github.com/postgres/postgres/archive/${postgres_tag}.tar.gz \
| tar xzf -

cd "${postgres_dir}"

# Match the default unix socket dir default with what defined on Ubuntu and
# Red Hat, which seems the most common location
sed -i 's|#define DEFAULT_PGSOCKET_DIR .*'\
if [ "$ID" != "macos" ]; then
# Match the default unix socket dir default with what defined on Ubuntu and
# Red Hat, which seems the most common location
sed -i 's|#define DEFAULT_PGSOCKET_DIR .*'\
'|#define DEFAULT_PGSOCKET_DIR "/var/run/postgresql"|' \
src/include/pg_config_manual.h
src/include/pg_config_manual.h
fi

# Often needed, but currently set by the workflow
# export LD_LIBRARY_PATH="${LIBPQ_BUILD_PREFIX}/lib"
Expand All @@ -171,4 +260,4 @@ make -C src/bin/pg_config install
make -C src/include install
cd ..

find ${LIBPQ_BUILD_PREFIX} -name \*.so.\* -type f -exec strip --strip-unneeded {} \;
find ${LIBPQ_BUILD_PREFIX} -name \*.${library_suffix}.\* -type f -exec strip --strip-unneeded {} \;
6 changes: 6 additions & 0 deletions scripts/build/wheel_macos_before_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ set -x
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
prjdir="$( cd "${dir}/../.." && pwd )"

# Build dependency libraries
"${prjdir}/scripts/build/build_libpq.sh"

# Show dependency tree
# otool -L /tmp/libpq.build/lib/*.dylib

brew install gnu-sed postgresql@${PG_VERSION}
brew link --overwrite postgresql@${PG_VERSION}

Expand Down
2 changes: 1 addition & 1 deletion tests/test_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ def test_non_block_after_notification(self):
raise Exception("Unexpected result from poll: %r", state)
polls += 1

self.assert_(polls >= 8, polls)
self.assert_(polls >= 5, polls)

def test_poll_noop(self):
self.conn.poll()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_green.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def wait(conn):
""")

polls = stub.polls.count(POLL_READ)
self.assert_(polls > 8, polls)
self.assert_(polls > 6, polls)


class CallbackErrorTestCase(ConnectingTestCase):
Expand Down
Loading