You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by mm...@apache.org on 2022/10/29 17:59:20 UTC

[pulsar-client-cpp] branch main updated: Support linking static dependencies when building with MSVC (#73)

This is an automated email from the ASF dual-hosted git repository.

mmerli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-client-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 93a44c8  Support linking static dependencies when building with MSVC (#73)
93a44c8 is described below

commit 93a44c856805e7f9b0b5ad342770c42495bd543d
Author: Yunze Xu <xy...@163.com>
AuthorDate: Sun Oct 30 01:59:15 2022 +0800

    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
---
 .github/workflows/ci-build-binary-artifacts.yaml | 77 ++++++++++++-----------
 .github/workflows/ci-pr-validation.yaml          | 51 ++++++++++------
 CMakeLists.txt                                   | 78 +++++++++++++++++++-----
 lib/CMakeLists.txt                               |  8 ++-
 win-examples/CMakeLists.txt                      | 49 +++++++++++++++
 win-examples/example.cc                          | 26 ++++++++
 6 files changed, 219 insertions(+), 70 deletions(-)

diff --git a/.github/workflows/ci-build-binary-artifacts.yaml b/.github/workflows/ci-build-binary-artifacts.yaml
index f24dd7a..8e2a6ea 100644
--- a/.github/workflows/ci-build-binary-artifacts.yaml
+++ b/.github/workflows/ci-build-binary-artifacts.yaml
@@ -75,14 +75,11 @@ jobs:
       - name: Zip artifact
         run: zip -r ${{matrix.pkg.type}}-${{matrix.cpu.platform}}.zip ${{matrix.pkg.path}}
 
-      - name: Upload binaries to release
-        uses: svenstaro/upload-release-action@v2
+      - name: Upload artifacts
+        uses: actions/upload-artifact@v3
         with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: ${{matrix.pkg.type}}-${{matrix.cpu.platform}}.zip
-          asset_name: ${{matrix.pkg.type}}-${{matrix.cpu.platform}}.zip
-          tag: ${{ github.ref }}
-          overwrite: true
+          name: ${{matrix.pkg.type}}-${{matrix.cpu.platform}}
+          path: ${{matrix.pkg.path}}
 
   package-windows:
     timeout-minutes: 120
@@ -90,21 +87,20 @@ jobs:
     runs-on: ${{ matrix.os }}
     env:
       VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
+      INSTALL_DIR: 'C:\\pulsar-cpp'
     strategy:
       fail-fast: false
       matrix:
         include:
           - name: 'Windows x64'
             os: windows-2022
-            triplet: x64-windows
-            vcpkg_dir: 'C:\vcpkg'
+            triplet: x64-windows-static
             suffix: 'windows-win64'
             generator: 'Visual Studio 17 2022'
             arch: '-A x64'
           - name: 'Windows x86'
             os: windows-2022
-            triplet: x86-windows
-            vcpkg_dir: 'C:\vcpkg'
+            triplet: x86-windows-static
             suffix: 'windows-win32'
             generator: 'Visual Studio 17 2022'
             arch: '-A Win32'
@@ -145,7 +141,7 @@ jobs:
         run: |
           ${{ env.VCPKG_ROOT }}\vcpkg.exe install --triplet ${{ matrix.triplet }} > dependencies.txt
 
-      - name: Configure and build
+      - name: Build and package
         shell: bash
         run: |
           BUILD_DIR=./build
@@ -154,36 +150,45 @@ jobs:
             -G "${{ matrix.generator }}" ${{ matrix.arch }} \
             -DBUILD_TESTS=OFF \
             -DVCPKG_TRIPLET=${{ matrix.triplet }} \
+            -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} \
             -S .
           cmake --build $BUILD_DIR --parallel --config Release
+          cmake --install $BUILD_DIR
+          cp dependencies.txt ${{ env.INSTALL_DIR }}
 
-      - name: Package
+      - name: Zip artifact
+        shell: bash
+        run: 7z a -tzip pulsar-client-cpp-${{ matrix.triplet }}.zip ${{ env.INSTALL_DIR }}/*
+
+      - name: Upload artifacts
+        uses: actions/upload-artifact@v3
+        with:
+          name: ${{ matrix.triplet }}
+          path: ${{ env.INSTALL_DIR }}
+
+      - name: Build and package (Debug)
         shell: bash
         run: |
-          BUILD_DIR=./build
-          PACKAGE_DIR=./package
-          LIB_DIR=$PACKAGE_DIR/lib/Release
-          VCPKG_INSTALLED_DIR=$PACKAGE_DIR/vcpkg_installed
-          mkdir -p $PACKAGE_DIR
-          mkdir -p $LIB_DIR
-          mkdir -p $VCPKG_INSTALLED_DIR/${{ matrix.triplet }}
-          
-          cp dependencies.txt $PACKAGE_DIR
-          cp -r ./include $PACKAGE_DIR
-          cp -r $BUILD_DIR/include/ $PACKAGE_DIR
-          cp -r $BUILD_DIR/lib/Release/*.lib $LIB_DIR
-          cp -r $BUILD_DIR/lib/Release/*.dll $LIB_DIR
-          cp -r ./vcpkg_installed/${{ matrix.triplet }}/* $VCPKG_INSTALLED_DIR/${{ matrix.triplet }}
+          BUILD_DIR=./build-debug
+          INSTALL_DIR_DEBUG=${{ env.INSTALL_DIR }}-Debug
+          mkdir -p $BUILD_DIR
+          cmake -B $BUILD_DIR \
+            -G "${{ matrix.generator }}" ${{ matrix.arch }} \
+            -DBUILD_TESTS=OFF \
+            -DVCPKG_TRIPLET=${{ matrix.triplet }} \
+            -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR_DEBUG \
+            -DCMAKE_BUILD_TYPE=Debug \
+            -S .
+          cmake --build $BUILD_DIR --parallel --config Debug
+          cmake --install $BUILD_DIR --config Debug
+          cp dependencies.txt $INSTALL_DIR_DEBUG
 
-      - name: Zip artifact
+      - name: Zip artifact (Debug)
         shell: bash
-        run: 7z a -tzip Windows-${{ matrix.triplet }}.zip ./package
+        run: 7z a -tzip pulsar-client-cpp-${{ matrix.triplet }}-Debug.zip ${{ env.INSTALL_DIR }}-Debug/*
 
-      - name: Upload binaries to release
-        uses: svenstaro/upload-release-action@v2
+      - name: Upload artifacts (Debug)
+        uses: actions/upload-artifact@v3
         with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: Windows-${{ matrix.triplet }}.zip
-          asset_name: Windows-${{ matrix.triplet }}.zip
-          tag: ${{ github.ref }}
-          overwrite: true
+          name: ${{ matrix.triplet }}-Debug
+          path: ${{ env.INSTALL_DIR }}-Debug
diff --git a/.github/workflows/ci-pr-validation.yaml b/.github/workflows/ci-pr-validation.yaml
index 1607997..64a39d0 100644
--- a/.github/workflows/ci-pr-validation.yaml
+++ b/.github/workflows/ci-pr-validation.yaml
@@ -82,21 +82,20 @@ jobs:
     runs-on: ${{ matrix.os }}
     env:
       VCPKG_ROOT: '${{ github.workspace }}/vcpkg'
+      INSTALL_DIR: 'C:\\pulsar-cpp'
     strategy:
       fail-fast: false
       matrix:
         include:
           - name: 'Windows x64'
             os: windows-2022
-            triplet: x64-windows
-            vcpkg_dir: 'C:\vcpkg'
+            triplet: x64-windows-static
             suffix: 'windows-win64'
             generator: 'Visual Studio 17 2022'
             arch: '-A x64'
           - name: 'Windows x86'
             os: windows-2022
-            triplet: x86-windows
-            vcpkg_dir: 'C:\vcpkg'
+            triplet: x86-windows-static
             suffix: 'windows-win32'
             generator: 'Visual Studio 17 2022'
             arch: '-A Win32'
@@ -137,44 +136,60 @@ jobs:
         run: |
           ${{ env.VCPKG_ROOT }}\vcpkg.exe install --triplet ${{ matrix.triplet }}
 
-      - name: Configure (default)
-
+      - name: Configure
         shell: bash
         run: |
           if [ "$RUNNER_OS" == "Windows" ]; then
             cmake \
-              -B ./build-0 \
+              -B ./build-1 \
               -G "${{ matrix.generator }}" ${{ matrix.arch }} \
               -DBUILD_TESTS=OFF \
-              -DVCPKG_TRIPLET=${{ matrix.triplet }} \
+              -DVCPKG_TRIPLET="${{ matrix.triplet }}" \
+              -DCMAKE_INSTALL_PREFIX="${{ env.INSTALL_DIR }}" \
               -S .
           fi
 
-      - name: Compile
+      - name: Install
         shell: bash
         run: |
           if [ "$RUNNER_OS" == "Windows" ]; then
-            cmake --build ./build-0 --parallel --config Release
+            cmake --build ./build-1 --parallel --config Release
+            cmake --install ./build-1
           fi
 
-      - name: Configure (dynamic library only)
+      - name: Test examples
         shell: bash
         run: |
           if [ "$RUNNER_OS" == "Windows" ]; then
+            cd win-examples
             cmake \
-              -B ./build-1 \
               -G "${{ matrix.generator }}" ${{ matrix.arch }} \
-              -DBUILD_TESTS=OFF \
-              -DVCPKG_TRIPLET=${{ matrix.triplet }} \
-              -DBUILD_STATIC_LIB=OFF \
-              -S .
+              -DLINK_STATIC=OFF \
+              -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} \
+              -B build-dynamic
+            cmake --build build-dynamic --config Release
+            cmake \
+              -G "${{ matrix.generator }}" ${{ matrix.arch }} \
+              -DLINK_STATIC=ON \
+              -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} \
+              -B build-static
+            cmake --build build-static --config Release
+            ./build-static/Release/win-example.exe
           fi
 
-      - name: Compile
+      - name: Build (Debug)
         shell: bash
         run: |
           if [ "$RUNNER_OS" == "Windows" ]; then
-            cmake --build ./build-1 --parallel --config Release
+            cmake \
+              -B ./build-2 \
+              -G "${{ matrix.generator }}" ${{ matrix.arch }} \
+              -DBUILD_TESTS=OFF \
+              -DVCPKG_TRIPLET="${{ matrix.triplet }}" \
+              -DCMAKE_INSTALL_PREFIX="${{ env.INSTALL_DIR }}" \
+              -DCMAKE_BUILD_TYPE=Debug \
+              -S .
+            cmake --build ./build-2 --parallel --config Debug
           fi
 
   package:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6e4d7f5..28be829 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,7 @@ message(STATUS "Pulsar Client version macro: ${PULSAR_CLIENT_VERSION_MACRO}")
 set(PVM_COMMENT "This is generated from Version.h.in by CMAKE. DO NOT EDIT DIRECTLY")
 configure_file(templates/Version.h.in include/pulsar/Version.h @ONLY)
 
+option(LINK_STATIC "Link against static libraries" OFF)
 if (VCPKG_TRIPLET)
     message(STATUS "Use vcpkg, triplet is ${VCPKG_TRIPLET}")
     set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/vcpkg_installed/${VCPKG_TRIPLET}")
@@ -43,8 +44,15 @@ if (VCPKG_TRIPLET)
     if (CMAKE_BUILD_TYPE STREQUAL "Debug")
         set(ZLIB_ROOT ${VCPKG_DEBUG_ROOT})
         set(OPENSSL_ROOT_DIR ${VCPKG_DEBUG_ROOT})
+        set(CMAKE_PREFIX_PATH ${VCPKG_DEBUG_ROOT} ${CMAKE_PREFIX_PATH})
+    endif ()
+    if (VCPKG_TRIPLET MATCHES ".*-static")
+        set(LINK_STATIC ON)
+    else ()
+        set(LINK_STATIC OFF)
     endif ()
 endif()
+MESSAGE(STATUS "LINK_STATIC:  " ${LINK_STATIC})
 
 find_program(CCACHE_PROGRAM ccache)
 if(CCACHE_PROGRAM)
@@ -69,9 +77,6 @@ MESSAGE(STATUS "BUILD_WIRESHARK:  " ${BUILD_WIRESHARK})
 option(BUILD_PERF_TOOLS "Build Pulsar CLI perf producer/consumer" OFF)
 MESSAGE(STATUS "BUILD_PERF_TOOLS:  " ${BUILD_PERF_TOOLS})
 
-option(LINK_STATIC "Link against static libraries" OFF)
-MESSAGE(STATUS "LINK_STATIC:  " ${LINK_STATIC})
-
 option(USE_LOG4CXX "Build with Log4cxx support" OFF)
 MESSAGE(STATUS "USE_LOG4CXX:  " ${USE_LOG4CXX})
 
@@ -93,7 +98,7 @@ set(CMAKE_C_STANDARD 11)
 # https://stackoverflow.com/questions/10046114/in-cmake-how-can-i-test-if-the-compiler-is-clang
 if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
     add_definitions(-DWIN32_LEAN_AND_MEAN -DNOGDI -D_WIN32_WINNT=0x0501 -D_CRT_SECURE_NO_WARNINGS)
-    add_compile_options(/wd4244 /wd4267 /wd4018 /wd4715 /wd4251 /wd4275)
+    add_compile_options(/wd4244 /wd4267 /wd4018 /wd4715 /wd4251 /wd4275 /wd4819)
 elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
     # ?? Don't have this to test with
 else() # GCC or Clang are mostly compatible:
@@ -168,7 +173,7 @@ if (NOT ZLIB_INCLUDE_DIRS OR NOT ZLIB_LIBRARIES)
     message(FATAL_ERROR "Could not find zlib")
 endif ()
 
-if (LINK_STATIC)
+if (LINK_STATIC AND NOT VCPKG_TRIPLET)
     find_library(LIB_ZSTD NAMES libzstd.a)
     message(STATUS "ZStd: ${LIB_ZSTD}")
     find_library(LIB_SNAPPY NAMES libsnappy.a)
@@ -195,6 +200,37 @@ if (LINK_STATIC)
     if (MSVC)
         add_definitions(-DCURL_STATICLIB)
     endif()
+elseif (LINK_STATIC AND VCPKG_TRIPLET)
+    find_package(protobuf REQUIRED)
+    message(STATUS "Found protobuf static library: " ${Protobuf_LIBRARIES})
+    if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
+        find_library(ZLIB_LIBRARIES NAMES zlibd)
+    else ()
+        find_library(ZLIB_LIBRARIES NAMES zlib z)
+    endif ()
+    if (ZLIB_LIBRARIES)
+        message(STATUS "Found zlib static library: " ${ZLIB_LIBRARIES})
+    else ()
+        message(FATAL_ERROR "Failed to find zlib static library")
+    endif ()
+    if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
+        find_library(CURL_LIBRARIES NAMES libcurl-d)
+    else ()
+        find_library(CURL_LIBRARIES NAMES libcurl)
+    endif ()
+    if (CURL_LIBRARIES)
+        message(STATUS "Found libcurl: ${CURL_LIBRARIES}")
+    else ()
+        message(FATAL_ERROR "Cannot find libcurl")
+    endif ()
+    find_library(LIB_ZSTD zstd)
+    if (LIB_ZSTD)
+        message(STATUS "Found ZSTD library: ${LIB_ZSTD}")
+    endif ()
+    find_library(LIB_SNAPPY NAMES snappy)
+    if (LIB_SNAPPY)
+        message(STATUS "Found Snappy library: ${LIB_SNAPPY}")
+    endif ()
 else()
     if (MSVC AND (${CMAKE_BUILD_TYPE} STREQUAL Debug))
         find_library(LIB_ZSTD zstdd HINTS "${VCPKG_DEBUG_ROOT}/lib")
@@ -211,7 +247,7 @@ else()
         find_library(LOG4CXX_LIBRARY_PATH log4cxx)
         find_path(LOG4CXX_INCLUDE_PATH log4cxx/logger.h)
     endif (USE_LOG4CXX)
-endif (LINK_STATIC)
+endif ()
 
 if (Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69)
     # Boost System does not require linking since 1.69
@@ -220,7 +256,7 @@ if (Boost_MAJOR_VERSION EQUAL 1 AND Boost_MINOR_VERSION LESS 69)
 endif()
 
 if (MSVC)
-  set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time)
+    set(BOOST_COMPONENTS ${BOOST_COMPONENTS} date_time)
 endif()
 
 if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
@@ -228,7 +264,7 @@ if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
     set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex)
     set(CMAKE_CXX_FLAGS " -DPULSAR_USE_BOOST_REGEX")
     MESSAGE(STATUS "Using Boost::Regex")
-else()
+elseif (CMAKE_COMPILER_IS_GNUCC)
     MESSAGE(STATUS "Using std::regex")
     # Turn on color error messages and show additional help with errors (only available in GCC v4.9+):
     add_compile_options(-fdiagnostics-show-option -fdiagnostics-color)
@@ -295,7 +331,7 @@ include_directories(
 
 set(COMMON_LIBS
   ${COMMON_LIBS}
-  Threads::Threads
+  ${CMAKE_THREAD_LIBS_INIT}
   ${Boost_REGEX_LIBRARY}
   ${Boost_SYSTEM_LIBRARY}
   ${Boost_DATE_TIME_LIBRARY}
@@ -307,13 +343,25 @@ set(COMMON_LIBS
   ${CMAKE_DL_LIBS}
 )
 
-if (NOT MSVC)
-    set(COMMON_LIBS ${COMMON_LIBS} m)
-else()
+if (MSVC)
     set(COMMON_LIBS
-    ${COMMON_LIBS}
-    wldap32.lib
-    Normaliz.lib)
+        ${COMMON_LIBS}
+        ${Boost_DATE_TIME_LIBRARY}
+        wldap32.lib
+        Normaliz.lib)
+    if (LINK_STATIC)
+        # add external dependencies of libcurl
+        set(COMMON_LIBS ${COMMON_LIBS} ws2_32.lib crypt32.lib)
+        # the default compile options have /MD, which cannot be used to build DLLs that link static libraries
+        string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
+        string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
+        string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
+        message(STATUS "CMAKE_CXX_FLAGS_DEBUG: " ${CMAKE_CXX_FLAGS_DEBUG})
+        message(STATUS "CMAKE_CXX_FLAGS_RELEASE: " ${CMAKE_CXX_FLAGS_RELEASE})
+        message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: " ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
+    endif ()
+else()
+    set(COMMON_LIBS ${COMMON_LIBS} m)
 endif()
 
 if (USE_LOG4CXX)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index fb89a23..ae2c8ac 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -46,6 +46,12 @@ endif(NOT LIBRARY_VERSION)
 if (MSVC)
     find_package(dlfcn-win32 REQUIRED)
     set(CMAKE_DL_LIBS dlfcn-win32::dl psapi.lib)
+    if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+        get_target_property(dlfcn-win32_LIBRARY dlfcn-win32::dl IMPORTED_LOCATION_DEBUG)
+    else ()
+        get_target_property(dlfcn-win32_LIBRARY dlfcn-win32::dl IMPORTED_LOCATION_RELEASE)
+    endif ()
+    message(STATUS "dlfcn-win32_LIBRARY: " ${dlfcn-win32_LIBRARY})
 endif(MSVC)
 
 
@@ -88,7 +94,7 @@ endif()
 # required dependencies except ssl
 if (LINK_STATIC AND BUILD_STATIC_LIB)
     if (MSVC)
-
+        set(COMMON_LIBS ${COMMON_LIBS} ${dlfcn-win32_LIBRARY})
         # This function is to remove either "debug" or "optimized" library names
         # out of the COMMON_LIBS list and return the sanitized list of libraries
         function(remove_libtype LIBLIST LIBTYPE OUTLIST)
diff --git a/win-examples/CMakeLists.txt b/win-examples/CMakeLists.txt
new file mode 100644
index 0000000..3998c43
--- /dev/null
+++ b/win-examples/CMakeLists.txt
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+cmake_minimum_required(VERSION 3.4)
+project(pulsar-cpp-win-examples)
+
+find_path(PULSAR_INCLUDES NAMES "pulsar/Client.h")
+if (PULSAR_INCLUDES)
+    message(STATUS "PULSAR_INCLUDES: " ${PULSAR_INCLUDES})
+else ()
+    message(FATAL_ERROR "Failed to find PULSAR_INCLUDES")
+endif ()
+option(LINK_STATIC "Link statically to pulsar" ON)
+if (LINK_STATIC)
+    find_library(PULSAR_LIBRARIES NAMES "pulsarWithDeps")
+else ()
+    find_library(PULSAR_LIBRARIES NAMES "pulsar")
+endif ()
+if (PULSAR_LIBRARIES)
+    message(STATUS "PULSAR_LIBRARIES: " ${PULSAR_LIBRARIES})
+else ()
+    message(FATAL_ERROR "Failed to find PULSAR_LIBRARIES")
+endif ()
+
+if (LINK_STATIC)
+    string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
+    add_definitions(-DPULSAR_STATIC)
+endif ()
+message(STATUS "CMAKE_CXX_FLAGS_RELEASE: " ${CMAKE_CXX_FLAGS_RELEASE})
+
+add_executable(win-example "example.cc")
+include_directories(${PULSAR_INCLUDES})
+target_link_libraries(win-example PRIVATE ${PULSAR_LIBRARIES})
diff --git a/win-examples/example.cc b/win-examples/example.cc
new file mode 100644
index 0000000..ddab3d2
--- /dev/null
+++ b/win-examples/example.cc
@@ -0,0 +1,26 @@
+/**
+ * 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.
+ */
+#include <pulsar/Client.h>
+using namespace pulsar;
+
+int main() {
+    Client client("pulsar://localhost:6650");
+    client.close();
+    return 0;
+}