Skip to content
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

ci(macos): Avoid linking against Homebrew #1755

Merged
merged 10 commits into from
Jan 6, 2025
37 changes: 13 additions & 24 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 @@ -134,55 +132,46 @@ jobs:
# }}}

macos: # {{{
runs-on: macos-${{ matrix.macver }}
runs-on: macos-latest
if: true

strategy:
fail-fast: false
matrix:
# These archs require an Apple M1 runner: [arm64, universal2]
arch: [x86_64]
arch: [x86_64, arm64]
pyver: [cp39, cp310, cp311, cp312, cp313]
macver: ["13"]
include:
- arch: arm64
pyver: cp310
macver: "14"
- arch: arm64
pyver: cp311
macver: "14"
- arch: arm64
pyver: cp312
macver: "14"
- arch: arm64
pyver: cp313
macver: "14"

steps:
- 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:
CIBW_BUILD: ${{matrix.pyver}}-macosx_${{matrix.arch}}
CIBW_ARCHS_MACOS: ${{matrix.arch}}
MACOSX_ARCHITECTURE: ${{matrix.arch}}
CIBW_BEFORE_ALL_MACOS: ./scripts/build/wheel_macos_before_all.sh
CIBW_TEST_COMMAND: >-
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
with:
name: macos-${{matrix.pyver}}-macos-${{matrix.macver}}_${{matrix.arch}}
name: macos-${{matrix.pyver}}-macos-${{matrix.arch}}
path: ./wheelhouse/*.whl

# }}}
Expand Down
151 changes: 115 additions & 36 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,21 +12,39 @@ 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://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
Expand All @@ -38,39 +56,101 @@ case "$ID" in
apk add --no-cache zlib-dev krb5-dev linux-pam-dev openldap-dev openssl-dev
;;

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.
dvarrazzo marked this conversation as resolved.
Show resolved Hide resolved
brew uninstall --force --ignore-dependencies openssl gettext curl
if [ -z "$MACOSX_ARCHITECTURE" ]; then
dvarrazzo marked this conversation as resolved.
Show resolved Hide resolved
MACOSX_ARCHITECTURE="$(uname -m)"
fi
# Set the deployment target to be <= to that of the oldest supported Python version.
# e.g. https://www.python.org/downloads/release/python-380/
if [ "$MACOSX_ARCHITECTURE" == "x86_64" ]; then
export MACOSX_DEPLOYMENT_TARGET=10.9
else
export MACOSX_DEPLOYMENT_TARGET=11.0
fi
;;

*)
echo "$0: unexpected Linux distribution: '$ID'" >&2
exit 1
;;
esac

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

if [ "$ID" == "macos" ]; then
make_configure_standard_flags=( \
--prefix=${LIBPQ_BUILD_PREFIX} \
"CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ -arch $MACOSX_ARCHITECTURE" \
"LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib -arch $MACOSX_ARCHITECTURE" \
)
else
make_configure_standard_flags=( \
--prefix=${LIBPQ_BUILD_PREFIX} \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ \
LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib \
)
fi


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 -

cd "${openssl_dir}"
pushd "${openssl_dir}"

options=(--prefix=${LIBPQ_BUILD_PREFIX} --openssldir=${LIBPQ_BUILD_PREFIX} \
zlib -fPIC shared)
if [ -z "$MACOSX_ARCHITECTURE" ]; then
./config $options
else
./configure "darwin64-$MACOSX_ARCHITECTURE-cc" $options
fi

./config --prefix=${LIBPQ_BUILD_PREFIX} --openssldir=${LIBPQ_BUILD_PREFIX} \
zlib -fPIC shared
make depend
make
else
cd "${openssl_dir}"
pushd "${openssl_dir}"
fi

# Install openssl
make install_sw
cd ..
popd

fi


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

# Build kerberos if needed
krb5_dir="krb5-${krb5_version}/src"
if [ ! -d "${krb5_dir}" ]; then
curl -sL "https://kerberos.org/dist/krb5/${krb5_version%.*}/krb5-${krb5_version}.tar.gz" \
| tar xzf -

pushd "${krb5_dir}"
./configure "${make_configure_standard_flags[@]}"
make
else
pushd "${krb5_dir}"
fi

make install
popd

fi


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

# Build libsasl2 if needed
# The system package (cyrus-sasl-devel) causes an amazing error on i686:
Expand All @@ -83,26 +163,25 @@ if [ "$ID" == "centos" ]; then
https://github.com/cyrusimap/cyrus-sasl/archive/${sasl_tag}.tar.gz \
| tar xzf -

cd "${sasl_dir}"
pushd "${sasl_dir}"

autoreconf -i
./configure --prefix=${LIBPQ_BUILD_PREFIX} \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib
./configure "${make_configure_standard_flags[@]}" --disable-macos-framework
make
else
cd "${sasl_dir}"
pushd "${sasl_dir}"
fi

# Install libsasl2
# requires missing nroff to build
touch saslauthd/saslauthd.8
make install
cd ..
popd

fi


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

# Build openldap if needed
ldap_tag="${ldap_version}"
Expand All @@ -112,25 +191,24 @@ if [ "$ID" == "centos" ]; then
https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${ldap_tag}.tgz \
| tar xzf -

cd "${ldap_dir}"
pushd "${ldap_dir}"

./configure --prefix=${LIBPQ_BUILD_PREFIX} --enable-backends=no --enable-null \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib
./configure "${make_configure_standard_flags[@]}" --enable-backends=no --enable-null

make depend
make -C libraries/liblutil/
make -C libraries/liblber/
make -C libraries/libldap/
else
cd "${ldap_dir}"
pushd "${ldap_dir}"
fi

# Install openldap
make -C libraries/liblber/ install
make -C libraries/libldap/ install
make -C include/ install
chmod +x ${LIBPQ_BUILD_PREFIX}/lib/{libldap,liblber}*.so*
cd ..
chmod +x ${LIBPQ_BUILD_PREFIX}/lib/{libldap,liblber}*.${library_suffix}*
popd

fi

Expand All @@ -143,32 +221,33 @@ if [ ! -d "${postgres_dir}" ]; then
https://github.com/postgres/postgres/archive/${postgres_tag}.tar.gz \
| tar xzf -

cd "${postgres_dir}"
pushd "${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"

./configure --prefix=${LIBPQ_BUILD_PREFIX} --sysconfdir=/etc/postgresql-common \
./configure "${make_configure_standard_flags[@]}" --sysconfdir=/etc/postgresql-common \
--with-gssapi --with-openssl --with-pam --with-ldap \
--without-readline --without-icu \
CPPFLAGS=-I${LIBPQ_BUILD_PREFIX}/include/ LDFLAGS=-L${LIBPQ_BUILD_PREFIX}/lib
--without-readline --without-icu
make -C src/interfaces/libpq
make -C src/bin/pg_config
make -C src/include
else
cd "${postgres_dir}"
pushd "${postgres_dir}"
fi

# Install libpq
make -C src/interfaces/libpq install
make -C src/bin/pg_config install
make -C src/include install
cd ..
popd

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