Skip to content

Commit e3eefce

Browse files
committed
Support linking static dependencies when building with MSVC
### Motivation Currently it's impossible to build Pulsar C++ client on Windows with `LINK_STATIC=ON`. It means users have to package all 3rd-party DLLs as well as `pulsar.dll`, which harms the experience. ### Modifications Enable `LINK_STATIC` when the Vcpkg triplet is `xxx-static`. In this case, find the 3rd party libraries with correct names on Windows. And replace `Threads::Threads` with `CMAKE_THREAD_LIB_INIT`. The most important change is replacing the `/MD` compile option with `/MT`. It should have been done by setting the [`MSVC_RUNTIME_LIBRARY`](https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html) property, but it seems not work. So this PR just modifies the `CMAKE_CXX_FLAGS_<CONFIG>` variables. For `pulsarWithAllDeps.lib`, add the actual library (`*.lib`) to `COMMON_LIBS` instead of the target name (`dlfcn-win32::dl`). Some warnings on Windows caused by incorrect compile options are fixed as well. A workflow is added to verify the static build for x64 and x86 Windows. And a simple example is added as `win-examples` to show the `pulsarWithAllDeps.lib` can be linked without any other dependency to run an executable. Change the existing release workflow to release two `*.zip` files: - pulsar-client-cpp-x64-windows-static.zip - pulsar-client-cpp-x86-windows-static.zip Each zip file consists of: ``` bin/pulsar.dll - The dynamic library that links statically to dependencies include/pulsar/ - Headers lib/ pulsar.lib - The import library of pulsar.dll pulsar-static.lib - The static library pulsarWithDeps.lib - The static library with all dependnecies included dependencies.txt - The vcpkg outputs, which contains the dependency versions ``` (cherry picked from commit 1c07678)
1 parent 5ef4c11 commit e3eefce

File tree

6 files changed

+172
-48
lines changed

6 files changed

+172
-48
lines changed

.github/workflows/ci-build-binary-artifacts.yaml

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,20 @@ jobs:
9090
runs-on: ${{ matrix.os }}
9191
env:
9292
VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
93+
INSTALL_DIR: 'C:\\pulsar-cpp'
9394
strategy:
9495
fail-fast: false
9596
matrix:
9697
include:
9798
- name: 'Windows x64'
9899
os: windows-2022
99-
triplet: x64-windows
100-
vcpkg_dir: 'C:\vcpkg'
100+
triplet: x64-windows-static
101101
suffix: 'windows-win64'
102102
generator: 'Visual Studio 17 2022'
103103
arch: '-A x64'
104104
- name: 'Windows x86'
105105
os: windows-2022
106-
triplet: x86-windows
107-
vcpkg_dir: 'C:\vcpkg'
106+
triplet: x86-windows-static
108107
suffix: 'windows-win32'
109108
generator: 'Visual Studio 17 2022'
110109
arch: '-A Win32'
@@ -145,7 +144,7 @@ jobs:
145144
run: |
146145
${{ env.VCPKG_ROOT }}\vcpkg.exe install --triplet ${{ matrix.triplet }} > dependencies.txt
147146
148-
- name: Configure and build
147+
- name: Build and package
149148
shell: bash
150149
run: |
151150
BUILD_DIR=./build
@@ -154,36 +153,21 @@ jobs:
154153
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
155154
-DBUILD_TESTS=OFF \
156155
-DVCPKG_TRIPLET=${{ matrix.triplet }} \
156+
-DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} \
157157
-S .
158158
cmake --build $BUILD_DIR --parallel --config Release
159-
160-
- name: Package
161-
shell: bash
162-
run: |
163-
BUILD_DIR=./build
164-
PACKAGE_DIR=./package
165-
LIB_DIR=$PACKAGE_DIR/lib/Release
166-
VCPKG_INSTALLED_DIR=$PACKAGE_DIR/vcpkg_installed
167-
mkdir -p $PACKAGE_DIR
168-
mkdir -p $LIB_DIR
169-
mkdir -p $VCPKG_INSTALLED_DIR/${{ matrix.triplet }}
170-
171-
cp dependencies.txt $PACKAGE_DIR
172-
cp -r ./include $PACKAGE_DIR
173-
cp -r $BUILD_DIR/include/ $PACKAGE_DIR
174-
cp -r $BUILD_DIR/lib/Release/*.lib $LIB_DIR
175-
cp -r $BUILD_DIR/lib/Release/*.dll $LIB_DIR
176-
cp -r ./vcpkg_installed/${{ matrix.triplet }}/* $VCPKG_INSTALLED_DIR/${{ matrix.triplet }}
159+
cmake --install $BUILD_DIR
160+
cp dependencies.txt ${{ env.INSTALL_DIR }}
177161
178162
- name: Zip artifact
179163
shell: bash
180-
run: 7z a -tzip Windows-${{ matrix.triplet }}.zip ./package
164+
run: 7z a -tzip pulsar-client-cpp-${{ matrix.triplet }}.zip ${{ env.INSTALL_DIR }}/*
181165

182166
- name: Upload binaries to release
183167
uses: svenstaro/upload-release-action@v2
184168
with:
185169
repo_token: ${{ secrets.GITHUB_TOKEN }}
186-
file: Windows-${{ matrix.triplet }}.zip
187-
asset_name: Windows-${{ matrix.triplet }}.zip
170+
file: pulsar-client-cpp-${{ matrix.triplet }}.zip
171+
asset_name: pulsar-client-cpp-${{ matrix.triplet }}.zip
188172
tag: ${{ github.ref }}
189173
overwrite: true

.github/workflows/ci-pr-validation.yaml

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,20 @@ jobs:
8181
runs-on: ${{ matrix.os }}
8282
env:
8383
VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
84+
INSTALL_DIR: 'C:\\pulsar-cpp'
8485
strategy:
8586
fail-fast: false
8687
matrix:
8788
include:
8889
- name: 'Windows x64'
8990
os: windows-2022
9091
triplet: x64-windows
91-
vcpkg_dir: 'C:\vcpkg'
9292
suffix: 'windows-win64'
9393
generator: 'Visual Studio 17 2022'
9494
arch: '-A x64'
9595
- name: 'Windows x86'
9696
os: windows-2022
9797
triplet: x86-windows
98-
vcpkg_dir: 'C:\vcpkg'
9998
suffix: 'windows-win32'
10099
generator: 'Visual Studio 17 2022'
101100
arch: '-A Win32'
@@ -156,24 +155,49 @@ jobs:
156155
cmake --build ./build-0 --parallel --config Release
157156
fi
158157
159-
- name: Configure (dynamic library only)
158+
- name: Install static vcpkg packages
159+
run: |
160+
${{ env.VCPKG_ROOT }}\vcpkg.exe install --triplet "${{ matrix.triplet }}-static"
161+
162+
- name: Configure (LINK_STATIC=ON)
160163
shell: bash
161164
run: |
162165
if [ "$RUNNER_OS" == "Windows" ]; then
163166
cmake \
164167
-B ./build-1 \
165168
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
166169
-DBUILD_TESTS=OFF \
167-
-DVCPKG_TRIPLET=${{ matrix.triplet }} \
168-
-DBUILD_STATIC_LIB=OFF \
170+
-DVCPKG_TRIPLET="${{ matrix.triplet }}-static" \
171+
-DCMAKE_INSTALL_PREFIX="${{ env.INSTALL_DIR }}" \
169172
-S .
170173
fi
171174
172-
- name: Compile
175+
- name: Install
173176
shell: bash
174177
run: |
175178
if [ "$RUNNER_OS" == "Windows" ]; then
176179
cmake --build ./build-1 --parallel --config Release
180+
cmake --install ./build-1
181+
fi
182+
183+
- name: Test examples
184+
shell: bash
185+
run: |
186+
if [ "$RUNNER_OS" == "Windows" ]; then
187+
cd win-examples
188+
cmake \
189+
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
190+
-DLINK_STATIC=OFF \
191+
-DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} \
192+
-B build-dynamic
193+
cmake --build build-dynamic --config Release
194+
cmake \
195+
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
196+
-DLINK_STATIC=ON \
197+
-DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} \
198+
-B build-static
199+
cmake --build build-static --config Release
200+
./build-static/Release/win-example.exe
177201
fi
178202
179203
package:

CMakeLists.txt

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ message(STATUS "Pulsar Client version macro: ${PULSAR_CLIENT_VERSION_MACRO}")
3333
set(PVM_COMMENT "This is generated from Version.h.in by CMAKE. DO NOT EDIT DIRECTLY")
3434
configure_file(templates/Version.h.in include/pulsar/Version.h @ONLY)
3535

36+
option(LINK_STATIC "Link against static libraries" OFF)
3637
if (VCPKG_TRIPLET)
3738
message(STATUS "Use vcpkg, triplet is ${VCPKG_TRIPLET}")
3839
set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/vcpkg_installed/${VCPKG_TRIPLET}")
@@ -44,7 +45,13 @@ if (VCPKG_TRIPLET)
4445
set(ZLIB_ROOT ${VCPKG_DEBUG_ROOT})
4546
set(OPENSSL_ROOT_DIR ${VCPKG_DEBUG_ROOT})
4647
endif ()
48+
if (VCPKG_TRIPLET MATCHES ".*-static")
49+
set(LINK_STATIC ON)
50+
else ()
51+
set(LINK_STATIC OFF)
52+
endif ()
4753
endif()
54+
MESSAGE(STATUS "LINK_STATIC: " ${LINK_STATIC})
4855

4956
find_program(CCACHE_PROGRAM ccache)
5057
if(CCACHE_PROGRAM)
@@ -69,9 +76,6 @@ MESSAGE(STATUS "BUILD_WIRESHARK: " ${BUILD_WIRESHARK})
6976
option(BUILD_PERF_TOOLS "Build Pulsar CLI perf producer/consumer" OFF)
7077
MESSAGE(STATUS "BUILD_PERF_TOOLS: " ${BUILD_PERF_TOOLS})
7178

72-
option(LINK_STATIC "Link against static libraries" OFF)
73-
MESSAGE(STATUS "LINK_STATIC: " ${LINK_STATIC})
74-
7579
option(USE_LOG4CXX "Build with Log4cxx support" OFF)
7680
MESSAGE(STATUS "USE_LOG4CXX: " ${USE_LOG4CXX})
7781

@@ -93,7 +97,7 @@ set(CMAKE_C_STANDARD 11)
9397
# https://stackoverflow.com/questions/10046114/in-cmake-how-can-i-test-if-the-compiler-is-clang
9498
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
9599
add_definitions(-DWIN32_LEAN_AND_MEAN -DNOGDI -D_WIN32_WINNT=0x0501 -D_CRT_SECURE_NO_WARNINGS)
96-
add_compile_options(/wd4244 /wd4267 /wd4018 /wd4715 /wd4251 /wd4275)
100+
add_compile_options(/wd4244 /wd4267 /wd4018 /wd4715 /wd4251 /wd4275 /wd4819)
97101
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
98102
# ?? Don't have this to test with
99103
else() # GCC or Clang are mostly compatible:
@@ -168,7 +172,7 @@ if (NOT ZLIB_INCLUDE_DIRS OR NOT ZLIB_LIBRARIES)
168172
message(FATAL_ERROR "Could not find zlib")
169173
endif ()
170174

171-
if (LINK_STATIC)
175+
if (LINK_STATIC AND NOT VCPKG_TRIPLET)
172176
find_library(LIB_ZSTD NAMES libzstd.a)
173177
message(STATUS "ZStd: ${LIB_ZSTD}")
174178
find_library(LIB_SNAPPY NAMES libsnappy.a)
@@ -195,6 +199,29 @@ if (LINK_STATIC)
195199
if (MSVC)
196200
add_definitions(-DCURL_STATICLIB)
197201
endif()
202+
elseif (LINK_STATIC AND VCPKG_TRIPLET)
203+
find_package(protobuf REQUIRED)
204+
message(STATUS "Found protobuf static library: " ${Protobuf_LIBRARIES})
205+
find_library(ZLIB_LIBRARIES NAMES zlib z)
206+
if (ZLIB_LIBRARIES)
207+
message(STATUS "Found zlib static library: " ${ZLIB_LIBRARIES})
208+
else ()
209+
message(FATAL_ERROR "Failed to find zlib static library")
210+
endif ()
211+
find_library(CURL_LIBRARIES NAMES libcurl)
212+
if (CURL_LIBRARIES)
213+
message(STATUS "Found libcurl: ${CURL_LIBRARIES}")
214+
else ()
215+
message(FATAL_ERROR "Cannot find libcurl")
216+
endif ()
217+
find_library(LIB_ZSTD zstd)
218+
if (LIB_ZSTD)
219+
message(STATUS "Found ZSTD library: ${LIB_ZSTD}")
220+
endif ()
221+
find_library(LIB_SNAPPY NAMES snappy)
222+
if (LIB_SNAPPY)
223+
message(STATUS "Found Snappy library: ${LIB_SNAPPY}")
224+
endif ()
198225
else()
199226
if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
200227
find_library(LIB_ZSTD zstdd HINTS "${VCPKG_DEBUG_ROOT}/lib")
@@ -211,7 +238,7 @@ else()
211238
find_library(LOG4CXX_LIBRARY_PATH log4cxx)
212239
find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
213240
endif (USE_LOG4CXX)
214-
endif (LINK_STATIC)
241+
endif ()
215242

216243
if (Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69)
217244
# Boost System does not require linking since 1.69
@@ -220,15 +247,15 @@ if (Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69)
220247
endif()
221248

222249
if (MSVC)
223-
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time)
250+
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time)
224251
endif()
225252

226253
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
227254
# GCC 4.8.2 implementation of std::regex is buggy
228255
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex)
229256
set(CMAKE_CXX_FLAGS " -DPULSAR_USE_BOOST_REGEX")
230257
MESSAGE(STATUS "Using Boost::Regex")
231-
else()
258+
elseif (CMAKE_COMPILER_IS_GNUCC)
232259
MESSAGE(STATUS "Using std::regex")
233260
# Turn on color error messages and show additional help with errors (only available in GCC v4.9+):
234261
add_compile_options(-fdiagnostics-show-option -fdiagnostics-color)
@@ -295,7 +322,7 @@ include_directories(
295322

296323
set(COMMON_LIBS
297324
${COMMON_LIBS}
298-
Threads::Threads
325+
${CMAKE_THREAD_LIBS_INIT}
299326
${Boost_REGEX_LIBRARY}
300327
${Boost_SYSTEM_LIBRARY}
301328
${Boost_DATE_TIME_LIBRARY}
@@ -307,13 +334,25 @@ set(COMMON_LIBS
307334
${CMAKE_DL_LIBS}
308335
)
309336

310-
if (NOT MSVC)
311-
set(COMMON_LIBS ${COMMON_LIBS} m)
312-
else()
337+
if (MSVC)
313338
set(COMMON_LIBS
314-
${COMMON_LIBS}
315-
wldap32.lib
316-
Normaliz.lib)
339+
${COMMON_LIBS}
340+
${Boost_DATE_TIME_LIBRARY}
341+
wldap32.lib
342+
Normaliz.lib)
343+
if (LINK_STATIC)
344+
# add external dependencies of libcurl
345+
set(COMMON_LIBS ${COMMON_LIBS} ws2_32.lib crypt32.lib)
346+
# the default compile options have /MD, which cannot be used to build DLLs that link static libraries
347+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
348+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
349+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
350+
message(STATUS "CMAKE_CXX_FLAGS_DEBUG: " ${CMAKE_CXX_FLAGS_DEBUG})
351+
message(STATUS "CMAKE_CXX_FLAGS_RELEASE: " ${CMAKE_CXX_FLAGS_RELEASE})
352+
message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: " ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
353+
endif ()
354+
else()
355+
set(COMMON_LIBS ${COMMON_LIBS} m)
317356
endif()
318357

319358
if (USE_LOG4CXX)

lib/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ endif(NOT LIBRARY_VERSION)
4646
if (MSVC)
4747
find_package(dlfcn-win32 REQUIRED)
4848
set(CMAKE_DL_LIBS dlfcn-win32::dl psapi.lib)
49+
get_target_property(dlfcn-win32_LIBRARY dlfcn-win32::dl IMPORTED_LOCATION_RELEASE)
50+
message(STATUS "dlfcn-win32_LIBRARY: " ${dlfcn-win32_LIBRARY})
4951
endif(MSVC)
5052

5153

@@ -88,7 +90,7 @@ endif()
8890
# required dependencies except ssl
8991
if (LINK_STATIC AND BUILD_STATIC_LIB)
9092
if (MSVC)
91-
93+
set(COMMON_LIBS ${COMMON_LIBS} ${dlfcn-win32_LIBRARY})
9294
# This function is to remove either "debug" or "optimized" library names
9395
# out of the COMMON_LIBS list and return the sanitized list of libraries
9496
function(remove_libtype LIBLIST LIBTYPE OUTLIST)

win-examples/CMakeLists.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
#
19+
20+
cmake_minimum_required(VERSION 3.4)
21+
project(pulsar-cpp-win-examples)
22+
23+
find_path(PULSAR_INCLUDES NAMES "pulsar/Client.h")
24+
if (PULSAR_INCLUDES)
25+
message(STATUS "PULSAR_INCLUDES: " ${PULSAR_INCLUDES})
26+
else ()
27+
message(FATAL_ERROR "Failed to find PULSAR_INCLUDES")
28+
endif ()
29+
option(LINK_STATIC "Link statically to pulsar" ON)
30+
if (LINK_STATIC)
31+
find_library(PULSAR_LIBRARIES NAMES "pulsarWithDeps")
32+
else ()
33+
find_library(PULSAR_LIBRARIES NAMES "pulsar")
34+
endif ()
35+
if (PULSAR_LIBRARIES)
36+
message(STATUS "PULSAR_LIBRARIES: " ${PULSAR_LIBRARIES})
37+
else ()
38+
message(FATAL_ERROR "Failed to find PULSAR_LIBRARIES")
39+
endif ()
40+
41+
if (LINK_STATIC)
42+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
43+
add_definitions(-DPULSAR_STATIC)
44+
endif ()
45+
message(STATUS "CMAKE_CXX_FLAGS_RELEASE: " ${CMAKE_CXX_FLAGS_RELEASE})
46+
47+
add_executable(win-example "example.cc")
48+
include_directories(${PULSAR_INCLUDES})
49+
target_link_libraries(win-example PRIVATE ${PULSAR_LIBRARIES})

win-examples/example.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
#include <pulsar/Client.h>
20+
using namespace pulsar;
21+
22+
int main() {
23+
Client client("pulsar://localhost:6650");
24+
client.close();
25+
return 0;
26+
}

0 commit comments

Comments
 (0)