Skip to content

Commit 93a44c8

Browse files
Support linking static dependencies when building with MSVC (#73)
* 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 ``` * Support Debug build and upload debug binaries * Fix $ is missing * Fix apt-get install failure * Remove default LINK_STATIC build * Use upload-artifact for Linux packages
1 parent 42a425f commit 93a44c8

File tree

6 files changed

+219
-70
lines changed

6 files changed

+219
-70
lines changed

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

+41-36
Original file line numberDiff line numberDiff line change
@@ -75,36 +75,32 @@ jobs:
7575
- name: Zip artifact
7676
run: zip -r ${{matrix.pkg.type}}-${{matrix.cpu.platform}}.zip ${{matrix.pkg.path}}
7777

78-
- name: Upload binaries to release
79-
uses: svenstaro/upload-release-action@v2
78+
- name: Upload artifacts
79+
uses: actions/upload-artifact@v3
8080
with:
81-
repo_token: ${{ secrets.GITHUB_TOKEN }}
82-
file: ${{matrix.pkg.type}}-${{matrix.cpu.platform}}.zip
83-
asset_name: ${{matrix.pkg.type}}-${{matrix.cpu.platform}}.zip
84-
tag: ${{ github.ref }}
85-
overwrite: true
81+
name: ${{matrix.pkg.type}}-${{matrix.cpu.platform}}
82+
path: ${{matrix.pkg.path}}
8683

8784
package-windows:
8885
timeout-minutes: 120
8986
name: Build CPP Client on ${{ matrix.name }}
9087
runs-on: ${{ matrix.os }}
9188
env:
9289
VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
90+
INSTALL_DIR: 'C:\\pulsar-cpp'
9391
strategy:
9492
fail-fast: false
9593
matrix:
9694
include:
9795
- name: 'Windows x64'
9896
os: windows-2022
99-
triplet: x64-windows
100-
vcpkg_dir: 'C:\vcpkg'
97+
triplet: x64-windows-static
10198
suffix: 'windows-win64'
10299
generator: 'Visual Studio 17 2022'
103100
arch: '-A x64'
104101
- name: 'Windows x86'
105102
os: windows-2022
106-
triplet: x86-windows
107-
vcpkg_dir: 'C:\vcpkg'
103+
triplet: x86-windows-static
108104
suffix: 'windows-win32'
109105
generator: 'Visual Studio 17 2022'
110106
arch: '-A Win32'
@@ -145,7 +141,7 @@ jobs:
145141
run: |
146142
${{ env.VCPKG_ROOT }}\vcpkg.exe install --triplet ${{ matrix.triplet }} > dependencies.txt
147143
148-
- name: Configure and build
144+
- name: Build and package
149145
shell: bash
150146
run: |
151147
BUILD_DIR=./build
@@ -154,36 +150,45 @@ jobs:
154150
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
155151
-DBUILD_TESTS=OFF \
156152
-DVCPKG_TRIPLET=${{ matrix.triplet }} \
153+
-DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} \
157154
-S .
158155
cmake --build $BUILD_DIR --parallel --config Release
156+
cmake --install $BUILD_DIR
157+
cp dependencies.txt ${{ env.INSTALL_DIR }}
159158
160-
- name: Package
159+
- name: Zip artifact
160+
shell: bash
161+
run: 7z a -tzip pulsar-client-cpp-${{ matrix.triplet }}.zip ${{ env.INSTALL_DIR }}/*
162+
163+
- name: Upload artifacts
164+
uses: actions/upload-artifact@v3
165+
with:
166+
name: ${{ matrix.triplet }}
167+
path: ${{ env.INSTALL_DIR }}
168+
169+
- name: Build and package (Debug)
161170
shell: bash
162171
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 }}
172+
BUILD_DIR=./build-debug
173+
INSTALL_DIR_DEBUG=${{ env.INSTALL_DIR }}-Debug
174+
mkdir -p $BUILD_DIR
175+
cmake -B $BUILD_DIR \
176+
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
177+
-DBUILD_TESTS=OFF \
178+
-DVCPKG_TRIPLET=${{ matrix.triplet }} \
179+
-DCMAKE_INSTALL_PREFIX=$INSTALL_DIR_DEBUG \
180+
-DCMAKE_BUILD_TYPE=Debug \
181+
-S .
182+
cmake --build $BUILD_DIR --parallel --config Debug
183+
cmake --install $BUILD_DIR --config Debug
184+
cp dependencies.txt $INSTALL_DIR_DEBUG
177185
178-
- name: Zip artifact
186+
- name: Zip artifact (Debug)
179187
shell: bash
180-
run: 7z a -tzip Windows-${{ matrix.triplet }}.zip ./package
188+
run: 7z a -tzip pulsar-client-cpp-${{ matrix.triplet }}-Debug.zip ${{ env.INSTALL_DIR }}-Debug/*
181189

182-
- name: Upload binaries to release
183-
uses: svenstaro/upload-release-action@v2
190+
- name: Upload artifacts (Debug)
191+
uses: actions/upload-artifact@v3
184192
with:
185-
repo_token: ${{ secrets.GITHUB_TOKEN }}
186-
file: Windows-${{ matrix.triplet }}.zip
187-
asset_name: Windows-${{ matrix.triplet }}.zip
188-
tag: ${{ github.ref }}
189-
overwrite: true
193+
name: ${{ matrix.triplet }}-Debug
194+
path: ${{ env.INSTALL_DIR }}-Debug

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

+33-18
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,20 @@ jobs:
8282
runs-on: ${{ matrix.os }}
8383
env:
8484
VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
85+
INSTALL_DIR: 'C:\\pulsar-cpp'
8586
strategy:
8687
fail-fast: false
8788
matrix:
8889
include:
8990
- name: 'Windows x64'
9091
os: windows-2022
91-
triplet: x64-windows
92-
vcpkg_dir: 'C:\vcpkg'
92+
triplet: x64-windows-static
9393
suffix: 'windows-win64'
9494
generator: 'Visual Studio 17 2022'
9595
arch: '-A x64'
9696
- name: 'Windows x86'
9797
os: windows-2022
98-
triplet: x86-windows
99-
vcpkg_dir: 'C:\vcpkg'
98+
triplet: x86-windows-static
10099
suffix: 'windows-win32'
101100
generator: 'Visual Studio 17 2022'
102101
arch: '-A Win32'
@@ -137,44 +136,60 @@ jobs:
137136
run: |
138137
${{ env.VCPKG_ROOT }}\vcpkg.exe install --triplet ${{ matrix.triplet }}
139138
140-
- name: Configure (default)
141-
139+
- name: Configure
142140
shell: bash
143141
run: |
144142
if [ "$RUNNER_OS" == "Windows" ]; then
145143
cmake \
146-
-B ./build-0 \
144+
-B ./build-1 \
147145
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
148146
-DBUILD_TESTS=OFF \
149-
-DVCPKG_TRIPLET=${{ matrix.triplet }} \
147+
-DVCPKG_TRIPLET="${{ matrix.triplet }}" \
148+
-DCMAKE_INSTALL_PREFIX="${{ env.INSTALL_DIR }}" \
150149
-S .
151150
fi
152151
153-
- name: Compile
152+
- name: Install
154153
shell: bash
155154
run: |
156155
if [ "$RUNNER_OS" == "Windows" ]; then
157-
cmake --build ./build-0 --parallel --config Release
156+
cmake --build ./build-1 --parallel --config Release
157+
cmake --install ./build-1
158158
fi
159159
160-
- name: Configure (dynamic library only)
160+
- name: Test examples
161161
shell: bash
162162
run: |
163163
if [ "$RUNNER_OS" == "Windows" ]; then
164+
cd win-examples
164165
cmake \
165-
-B ./build-1 \
166166
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
167-
-DBUILD_TESTS=OFF \
168-
-DVCPKG_TRIPLET=${{ matrix.triplet }} \
169-
-DBUILD_STATIC_LIB=OFF \
170-
-S .
167+
-DLINK_STATIC=OFF \
168+
-DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} \
169+
-B build-dynamic
170+
cmake --build build-dynamic --config Release
171+
cmake \
172+
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
173+
-DLINK_STATIC=ON \
174+
-DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} \
175+
-B build-static
176+
cmake --build build-static --config Release
177+
./build-static/Release/win-example.exe
171178
fi
172179
173-
- name: Compile
180+
- name: Build (Debug)
174181
shell: bash
175182
run: |
176183
if [ "$RUNNER_OS" == "Windows" ]; then
177-
cmake --build ./build-1 --parallel --config Release
184+
cmake \
185+
-B ./build-2 \
186+
-G "${{ matrix.generator }}" ${{ matrix.arch }} \
187+
-DBUILD_TESTS=OFF \
188+
-DVCPKG_TRIPLET="${{ matrix.triplet }}" \
189+
-DCMAKE_INSTALL_PREFIX="${{ env.INSTALL_DIR }}" \
190+
-DCMAKE_BUILD_TYPE=Debug \
191+
-S .
192+
cmake --build ./build-2 --parallel --config Debug
178193
fi
179194
180195
package:

CMakeLists.txt

+63-15
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}")
@@ -43,8 +44,15 @@ if (VCPKG_TRIPLET)
4344
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
4445
set(ZLIB_ROOT ${VCPKG_DEBUG_ROOT})
4546
set(OPENSSL_ROOT_DIR ${VCPKG_DEBUG_ROOT})
47+
set(CMAKE_PREFIX_PATH ${VCPKG_DEBUG_ROOT} ${CMAKE_PREFIX_PATH})
48+
endif ()
49+
if (VCPKG_TRIPLET MATCHES ".*-static")
50+
set(LINK_STATIC ON)
51+
else ()
52+
set(LINK_STATIC OFF)
4653
endif ()
4754
endif()
55+
MESSAGE(STATUS "LINK_STATIC: " ${LINK_STATIC})
4856

4957
find_program(CCACHE_PROGRAM ccache)
5058
if(CCACHE_PROGRAM)
@@ -69,9 +77,6 @@ MESSAGE(STATUS "BUILD_WIRESHARK: " ${BUILD_WIRESHARK})
6977
option(BUILD_PERF_TOOLS "Build Pulsar CLI perf producer/consumer" OFF)
7078
MESSAGE(STATUS "BUILD_PERF_TOOLS: " ${BUILD_PERF_TOOLS})
7179

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

@@ -93,7 +98,7 @@ set(CMAKE_C_STANDARD 11)
9398
# https://stackoverflow.com/questions/10046114/in-cmake-how-can-i-test-if-the-compiler-is-clang
9499
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
95100
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)
101+
add_compile_options(/wd4244 /wd4267 /wd4018 /wd4715 /wd4251 /wd4275 /wd4819)
97102
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
98103
# ?? Don't have this to test with
99104
else() # GCC or Clang are mostly compatible:
@@ -168,7 +173,7 @@ if (NOT ZLIB_INCLUDE_DIRS OR NOT ZLIB_LIBRARIES)
168173
message(FATAL_ERROR "Could not find zlib")
169174
endif ()
170175

171-
if (LINK_STATIC)
176+
if (LINK_STATIC AND NOT VCPKG_TRIPLET)
172177
find_library(LIB_ZSTD NAMES libzstd.a)
173178
message(STATUS "ZStd: ${LIB_ZSTD}")
174179
find_library(LIB_SNAPPY NAMES libsnappy.a)
@@ -195,6 +200,37 @@ if (LINK_STATIC)
195200
if (MSVC)
196201
add_definitions(-DCURL_STATICLIB)
197202
endif()
203+
elseif (LINK_STATIC AND VCPKG_TRIPLET)
204+
find_package(protobuf REQUIRED)
205+
message(STATUS "Found protobuf static library: " ${Protobuf_LIBRARIES})
206+
if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
207+
find_library(ZLIB_LIBRARIES NAMES zlibd)
208+
else ()
209+
find_library(ZLIB_LIBRARIES NAMES zlib z)
210+
endif ()
211+
if (ZLIB_LIBRARIES)
212+
message(STATUS "Found zlib static library: " ${ZLIB_LIBRARIES})
213+
else ()
214+
message(FATAL_ERROR "Failed to find zlib static library")
215+
endif ()
216+
if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
217+
find_library(CURL_LIBRARIES NAMES libcurl-d)
218+
else ()
219+
find_library(CURL_LIBRARIES NAMES libcurl)
220+
endif ()
221+
if (CURL_LIBRARIES)
222+
message(STATUS "Found libcurl: ${CURL_LIBRARIES}")
223+
else ()
224+
message(FATAL_ERROR "Cannot find libcurl")
225+
endif ()
226+
find_library(LIB_ZSTD zstd)
227+
if (LIB_ZSTD)
228+
message(STATUS "Found ZSTD library: ${LIB_ZSTD}")
229+
endif ()
230+
find_library(LIB_SNAPPY NAMES snappy)
231+
if (LIB_SNAPPY)
232+
message(STATUS "Found Snappy library: ${LIB_SNAPPY}")
233+
endif ()
198234
else()
199235
if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
200236
find_library(LIB_ZSTD zstdd HINTS "${VCPKG_DEBUG_ROOT}/lib")
@@ -211,7 +247,7 @@ else()
211247
find_library(LOG4CXX_LIBRARY_PATH log4cxx)
212248
find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
213249
endif (USE_LOG4CXX)
214-
endif (LINK_STATIC)
250+
endif ()
215251

216252
if (Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69)
217253
# Boost System does not require linking since 1.69
@@ -220,15 +256,15 @@ if (Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69)
220256
endif()
221257

222258
if (MSVC)
223-
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time)
259+
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time)
224260
endif()
225261

226262
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
227263
# GCC 4.8.2 implementation of std::regex is buggy
228264
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex)
229265
set(CMAKE_CXX_FLAGS " -DPULSAR_USE_BOOST_REGEX")
230266
MESSAGE(STATUS "Using Boost::Regex")
231-
else()
267+
elseif (CMAKE_COMPILER_IS_GNUCC)
232268
MESSAGE(STATUS "Using std::regex")
233269
# Turn on color error messages and show additional help with errors (only available in GCC v4.9+):
234270
add_compile_options(-fdiagnostics-show-option -fdiagnostics-color)
@@ -295,7 +331,7 @@ include_directories(
295331

296332
set(COMMON_LIBS
297333
${COMMON_LIBS}
298-
Threads::Threads
334+
${CMAKE_THREAD_LIBS_INIT}
299335
${Boost_REGEX_LIBRARY}
300336
${Boost_SYSTEM_LIBRARY}
301337
${Boost_DATE_TIME_LIBRARY}
@@ -307,13 +343,25 @@ set(COMMON_LIBS
307343
${CMAKE_DL_LIBS}
308344
)
309345

310-
if (NOT MSVC)
311-
set(COMMON_LIBS ${COMMON_LIBS} m)
312-
else()
346+
if (MSVC)
313347
set(COMMON_LIBS
314-
${COMMON_LIBS}
315-
wldap32.lib
316-
Normaliz.lib)
348+
${COMMON_LIBS}
349+
${Boost_DATE_TIME_LIBRARY}
350+
wldap32.lib
351+
Normaliz.lib)
352+
if (LINK_STATIC)
353+
# add external dependencies of libcurl
354+
set(COMMON_LIBS ${COMMON_LIBS} ws2_32.lib crypt32.lib)
355+
# the default compile options have /MD, which cannot be used to build DLLs that link static libraries
356+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
357+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
358+
string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
359+
message(STATUS "CMAKE_CXX_FLAGS_DEBUG: " ${CMAKE_CXX_FLAGS_DEBUG})
360+
message(STATUS "CMAKE_CXX_FLAGS_RELEASE: " ${CMAKE_CXX_FLAGS_RELEASE})
361+
message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: " ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
362+
endif ()
363+
else()
364+
set(COMMON_LIBS ${COMMON_LIBS} m)
317365
endif()
318366

319367
if (USE_LOG4CXX)

0 commit comments

Comments
 (0)