Fix Protobuf symbols not found in libpulsarwithdeps.a when building on macOS (#354)
### Motivation
https://github.com/apache/pulsar-client-cpp/pull/290 brings a regression
that on macOS, Protobuf is always found with CMake Config mode, which
does not set the `Protobuf_LIBRARIES` variable so that the
libpulsarwithdeps.a misses the symbols of Protobuf.
### Modifications
When `LINK_STATIC` is ON, use CMake Module mode to find the Protobuf.
Add `build-static-library.sh` to build libraries with static
dependencies and verify these libraries in PR workflow. Upload the
pre-built binaries in the build workflow.
(cherry picked from commit f75b39bcbaad63ed96268fe7e09993e0e6a8a959)
diff --git a/.github/workflows/ci-build-binary-artifacts.yaml b/.github/workflows/ci-build-binary-artifacts.yaml
index 586458d..a2b7be8 100644
--- a/.github/workflows/ci-build-binary-artifacts.yaml
+++ b/.github/workflows/ci-build-binary-artifacts.yaml
@@ -192,3 +192,34 @@
with:
name: ${{ matrix.triplet }}-Debug
path: ${{ env.INSTALL_DIR }}-Debug
+
+ package-macos:
+ name: Build macOS libraries
+ runs-on: macos-latest
+ timeout-minutes: 500
+
+ strategy:
+ fail-fast: false
+ matrix:
+ arch: [x86_64, arm64]
+
+ steps:
+ - name: checkout
+ uses: actions/checkout@v3
+
+ - name: Install dependencies
+ run: |
+ export ARCH=${{ matrix.arch }}
+ ./pkg/mac/build-static-library.sh
+
+ - name: Zip artifact
+ run: |
+ cd ./pkg/mac/.install
+ zip -r macos-${{ matrix.arch }}.zip ./include/pulsar/* ./lib/*
+ cp macos-${{ matrix.arch }}.zip ../../../
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: macos-${{ matrix.arch }}.zip
+ path: macos-${{ matrix.arch }}.zip
diff --git a/.github/workflows/ci-pr-validation.yaml b/.github/workflows/ci-pr-validation.yaml
index 03d417f..80e1a1d 100644
--- a/.github/workflows/ci-pr-validation.yaml
+++ b/.github/workflows/ci-pr-validation.yaml
@@ -302,6 +302,28 @@
run: |
cmake --build ./build-macos --parallel --config Release
+ cpp-build-macos-static:
+ timeout-minutes: 120
+ name: Build CPP Client on macOS with static dependencies
+ runs-on: macos-12
+ needs: unit-tests
+ steps:
+ - name: checkout
+ uses: actions/checkout@v3
+
+ - name: Build libraries
+ run: ./pkg/mac/build-static-library.sh
+
+ - name: Test static libraries
+ run: |
+ export PULSAR_DIR=$PWD/pkg/mac/.install
+ echo "Build with static library"
+ clang++ win-examples/example.cc -o static.out -std=c++11 -I $PULSAR_DIR/include $PULSAR_DIR/lib/libpulsarwithdeps.a
+ ./static.out
+ echo "Build with dynamic library"
+ clang++ win-examples/example.cc -o dynamic.out -std=c++11 -I $PULSAR_DIR/include -L $PULSAR_DIR/lib -Wl,-rpath $PULSAR_DIR/lib -lpulsar
+ ./dynamic.out
+
# Job that will be required to complete and depends on all the other jobs
check-completion:
name: Check Completion
diff --git a/.gitignore b/.gitignore
index a569bda..a1e5224 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,3 +104,5 @@
.tests-container-id.txt
Testing
.test-token.txt
+pkg/mac/.build
+pkg/mac/.install
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8d6a00..fb4f1b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -88,9 +88,17 @@
MESSAGE(STATUS "Threads library: " ${CMAKE_THREAD_LIBS_INIT})
set(Boost_NO_BOOST_CMAKE ON)
+
+if (APPLE AND NOT LINK_STATIC)
+ # The latest Protobuf dependency on macOS requires the C++17 support and
+ # it could only be found by the CONFIG mode
+ set(LATEST_PROTOBUF TRUE)
+else ()
+ set(LATEST_PROTOBUF FALSE)
+endif ()
+
if (NOT CMAKE_CXX_STANDARD)
- if (APPLE)
- # The latest Protobuf dependency on macOS requires the C++17 support
+ if (LATEST_PROTOBUF)
set(CMAKE_CXX_STANDARD 17)
else ()
set(CMAKE_CXX_STANDARD 11)
@@ -143,7 +151,7 @@
message("OPENSSL_INCLUDE_DIR: " ${OPENSSL_INCLUDE_DIR})
message("OPENSSL_LIBRARIES: " ${OPENSSL_LIBRARIES})
-if (APPLE)
+if (LATEST_PROTOBUF)
# See https://github.com/apache/arrow/issues/35987
add_definitions(-DPROTOBUF_USE_DLLS)
# Use Config mode to avoid FindProtobuf.cmake does not find the Abseil library
@@ -318,7 +326,7 @@
${CMAKE_DL_LIBS}
)
-if (APPLE)
+if (LATEST_PROTOBUF)
# Protobuf_LIBRARIES is empty when finding Protobuf in Config mode
set(COMMON_LIBS ${COMMON_LIBS} protobuf::libprotobuf)
else ()
diff --git a/dependencies.yaml b/dependencies.yaml
index f206ccc..18be438 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -23,5 +23,5 @@
zlib: 1.2.12
zstd: 1.5.2
snappy: 1.1.9
-openssl: 1.1.1q
+openssl: 1.1.1v
curl: 8.4.0
diff --git a/pkg/mac/build-static-library.sh b/pkg/mac/build-static-library.sh
new file mode 100755
index 0000000..f0253fb
--- /dev/null
+++ b/pkg/mac/build-static-library.sh
@@ -0,0 +1,190 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set -ex
+cd `dirname $0`
+
+pip3 install pyyaml
+
+MACOSX_DEPLOYMENT_TARGET=10.15
+if [[ -z ${ARCH} ]]; then
+ ARCH=`uname -m`
+fi
+
+BUILD_DIR=$PWD/.build
+INSTALL_DIR=$PWD/.install
+PREFIX=$BUILD_DIR/install
+mkdir -p $BUILD_DIR
+cp -f ../../build-support/dep-version.py $BUILD_DIR/
+cp -f ../../dependencies.yaml $BUILD_DIR/
+
+pushd $BUILD_DIR
+
+BOOST_VERSION=$(./dep-version.py boost)
+ZLIB_VERSION=$(./dep-version.py zlib)
+OPENSSL_VERSION=$(./dep-version.py openssl)
+PROTOBUF_VERSION=$(./dep-version.py protobuf)
+ZSTD_VERSION=$(./dep-version.py zstd)
+SNAPPY_VERSION=$(./dep-version.py snappy)
+CURL_VERSION=$(./dep-version.py curl)
+
+BOOST_VERSION_=${BOOST_VERSION//./_}
+if [ ! -f boost/.done ]; then
+ echo "Building Boost $BOOST_VERSION"
+ curl -O -L https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_}.tar.gz
+ tar zxf boost_${BOOST_VERSION_}.tar.gz
+ mkdir -p $PREFIX/include
+ cp -rf boost_${BOOST_VERSION_}/boost $PREFIX/include/
+ mkdir -p boost
+ touch boost/.done
+else
+ echo "Using cached Boost $BOOST_VERSION"
+fi
+
+if [ ! -f zlib-${ZLIB_VERSION}/.done ]; then
+ echo "Building ZLib $ZLIB_VERSION"
+ curl -O -L https://zlib.net/fossils/zlib-${ZLIB_VERSION}.tar.gz
+ tar zxf zlib-${ZLIB_VERSION}.tar.gz
+ pushd zlib-$ZLIB_VERSION
+ CFLAGS="-fPIC -O3 -arch ${ARCH} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" ./configure --prefix=$PREFIX
+ make -j16
+ make install
+ touch .done
+ popd
+else
+ echo "Using cached ZLib $ZLIB_VERSION"
+fi
+
+OPENSSL_VERSION_UNDERSCORE=$(echo $OPENSSL_VERSION | sed 's/\./_/g')
+if [ ! -f openssl-OpenSSL_${OPENSSL_VERSION_UNDERSCORE}.done ]; then
+ echo "Building OpenSSL $OPENSSL_VERSION"
+ curl -O -L https://github.com/openssl/openssl/archive/OpenSSL_$OPENSSL_VERSION_UNDERSCORE.tar.gz
+ tar zxf OpenSSL_$OPENSSL_VERSION_UNDERSCORE.tar.gz
+
+ pushd openssl-OpenSSL_${OPENSSL_VERSION_UNDERSCORE}
+ if [[ $ARCH = 'arm64' ]]; then
+ PLATFORM=darwin64-arm64-cc
+ else
+ PLATFORM=darwin64-x86_64-cc
+ fi
+ CFLAGS="-fPIC -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" \
+ ./Configure --prefix=$PREFIX no-shared no-unit-test $PLATFORM
+ make -j8 >/dev/null
+ make install_sw >/dev/null
+ popd
+
+ touch openssl-OpenSSL_${OPENSSL_VERSION_UNDERSCORE}.done
+else
+ echo "Using cached OpenSSL $OPENSSL_VERSION"
+fi
+
+if [ ! -f protobuf-${PROTOBUF_VERSION}/.done ]; then
+ echo "Building Protobuf $PROTOBUF_VERSION"
+ curl -O -L https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protobuf-cpp-${PROTOBUF_VERSION}.tar.gz
+ tar zxf protobuf-cpp-${PROTOBUF_VERSION}.tar.gz
+ pushd protobuf-${PROTOBUF_VERSION}
+ pushd cmake/
+ # Build protoc that can run on both x86 and arm architectures
+ cmake -B build -DCMAKE_CXX_FLAGS="-fPIC -arch x86_64 -arch arm64 -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" \
+ -Dprotobuf_BUILD_TESTS=OFF \
+ -DCMAKE_INSTALL_PREFIX=$PREFIX
+ cmake --build build -j16 --target install
+ popd
+
+ # Retain the library for one architecture so that `ar` can work on the library
+ pushd $PREFIX/lib
+ mv libprotobuf.a libprotobuf_universal.a
+ lipo libprotobuf_universal.a -thin ${ARCH} -output libprotobuf.a
+ popd
+ touch .done
+ popd
+else
+ echo "Using cached Protobuf $PROTOBUF_VERSION"
+fi
+
+if [ ! -f zstd-${ZSTD_VERSION}/.done ]; then
+ echo "Building ZStd $ZSTD_VERSION"
+ curl -O -L https://github.com/facebook/zstd/releases/download/v${ZSTD_VERSION}/zstd-${ZSTD_VERSION}.tar.gz
+ tar zxf zstd-${ZSTD_VERSION}.tar.gz
+ pushd zstd-${ZSTD_VERSION}
+ CFLAGS="-fPIC -O3 -arch ${ARCH} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" PREFIX=$PREFIX \
+ make -j16 -C lib install-static install-includes
+ touch .done
+ popd
+else
+ echo "Using cached ZStd $ZSTD_VERSION"
+fi
+
+if [ ! -f snappy-${SNAPPY_VERSION}/.done ]; then
+ echo "Building Snappy $SNAPPY_VERSION"
+ curl -O -L https://github.com/google/snappy/archive/refs/tags/${SNAPPY_VERSION}.tar.gz
+ tar zxf ${SNAPPY_VERSION}.tar.gz
+ pushd snappy-${SNAPPY_VERSION}
+ CXXFLAGS="-fPIC -O3 -arch ${ARCH} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" \
+ cmake . -DCMAKE_INSTALL_PREFIX=$PREFIX -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
+ make -j16
+ make install
+ touch .done
+ popd
+else
+ echo "Using cached Snappy $SNAPPY_VERSION"
+fi
+
+if [ ! -f curl-${CURL_VERSION}/.done ]; then
+ echo "Building LibCurl $CURL_VERSION"
+ CURL_VERSION_=${CURL_VERSION//./_}
+ curl -O -L https://github.com/curl/curl/releases/download/curl-${CURL_VERSION_}/curl-${CURL_VERSION}.tar.gz
+ tar zxf curl-${CURL_VERSION}.tar.gz
+ pushd curl-${CURL_VERSION}
+ # Force the compiler to find the OpenSSL headers instead of the headers in the system path like /usr/local/include/openssl.
+ cp -rf $PREFIX/include/openssl include/
+ CFLAGS="-I$PREFIX/include -fPIC -arch ${ARCH} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}" \
+ ./configure --with-ssl=$PREFIX \
+ --without-nghttp2 \
+ --without-libidn2 \
+ --disable-ldap \
+ --without-brotli \
+ --without-secure-transport \
+ --without-librtmp \
+ --disable-ipv6 \
+ --host=$ARCH-apple-darwin \
+ --prefix=$PREFIX
+ make -j16 install
+ touch .done
+ popd
+else
+ echo "Using cached LibCurl $CURL_VERSION"
+fi
+
+popd # pkg/mac
+cd ../../ # project root
+
+cmake -B build-static -DCMAKE_OSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET \
+ -DLINK_STATIC=ON \
+ -DBUILD_TESTS=OFF \
+ -DBUILD_DYNAMIC_LIB=ON \
+ -DBUILD_STATIC_LIB=ON \
+ -DCMAKE_OSX_ARCHITECTURES=${ARCH} \
+ -DCMAKE_PREFIX_PATH=$PREFIX \
+ -DOPENSSL_ROOT_DIR=$PREFIX \
+ -DPROTOC_PATH=$PREFIX/bin/protoc \
+ -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR \
+ -DCMAKE_BUILD_TYPE=Release
+cmake --build build-static -j16 --target install