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-python.git


The following commit(s) were added to refs/heads/main by this push:
     new 325f852  Support building with MSVC (#23)
325f852 is described below

commit 325f852df87dc15d68dd87fd540cf71e61d93281
Author: Yunze Xu <[email protected]>
AuthorDate: Wed Nov 2 11:41:40 2022 +0800

    Support building with MSVC (#23)
    
    Fixes https://github.com/apache/pulsar-client-python/issues/7
    
    The Python client cannot be built with MSVC.
    
    CMakeLists.txt:
    1. Boost.Python cannot be found on Windows. The component of Boost
       cannot be `python3`. It should be a specific version like
       `python310`. Therefore, find all possible components from 3.10 to 3.7
       until the first component is available.
    2. For MSVC, link to `pulsarWithDeps.lib` when `LINK_STATIC` is `ON` and
       set the `MSVC_RUNTIME_LIBRARY` target property to `MultiThreaded`.
    3. Change the suffix from `.so` to `.pyd` because Python on Windows
       recognizes `*.pyd` as the dynamic library.
    4. Link to Python library with MSVC, otherwise the symbos cannot be
       found when linking `boost-python`.
    
    README: tell users how to build Python client on Windows.
    
    Add a workflow to build Python wheels of versions 3.7 to 3.10 on Windows
    and verify the build will succeed.
    
    With this PR, Windows users still need to put the related
    `boost_python*.dll` under the system path. In future, the `boost-python`
    dependency will be removed. See
    https://github.com/apache/pulsar-client-python/issues/24.
---
 .github/workflows/ci-pr-validation.yaml | 85 ++++++++++++++++++++++++++++++++-
 .gitignore                              |  5 +-
 .gitmodules                             |  3 ++
 CMakeLists.txt                          | 62 +++++++++++++++++++-----
 README.md                               | 32 ++++++++++++-
 setup.py                                |  9 +++-
 vcpkg                                   |  1 +
 vcpkg-3.10.json                         | 12 +++++
 vcpkg-3.7.json                          | 18 +++++++
 vcpkg-3.8.json                          | 18 +++++++
 vcpkg-3.9.json                          | 12 +++++
 vcpkg.json                              |  6 +++
 12 files changed, 247 insertions(+), 16 deletions(-)

diff --git a/.github/workflows/ci-pr-validation.yaml 
b/.github/workflows/ci-pr-validation.yaml
index e1b7434..6f3f894 100644
--- a/.github/workflows/ci-pr-validation.yaml
+++ b/.github/workflows/ci-pr-validation.yaml
@@ -67,6 +67,7 @@ jobs:
 
   linux-wheel:
     name: Wheel ${{matrix.image.name}} - Py ${{matrix.python.version}} - 
${{matrix.cpu.platform}}
+    needs: unit-tests
     runs-on: ubuntu-22.04
     timeout-minutes: 300
 
@@ -118,6 +119,7 @@ jobs:
 
   mac-wheels:
     name: Wheel MacOS Universal2 - Py ${{matrix.py.version}}
+    needs: unit-tests
     runs-on: macos-12
     timeout-minutes: 300
 
@@ -155,11 +157,92 @@ jobs:
       - name: Build and test Mac wheels
         run: pkg/mac/build-mac-wheels.sh ${{matrix.py.version}}
 
+  windows-wheels:
+    name: "Python ${{ matrix.python.version }} Wheel on ${{ 
matrix.windows.name }}"
+    needs: unit-tests
+    runs-on: ${{ matrix.windows.os }}
+    timeout-minutes: 120
+
+    env:
+      PULSAR_CPP_DIR: 'C:\\pulsar-cpp'
+    strategy:
+      fail-fast: false
+      matrix:
+        windows:
+          - name: 'Windows x64'
+            os: windows-2022
+            arch: '-A x64'
+            triplet: 'x64-windows'
+        python:
+          - version: '3.7'
+          - version: '3.8'
+          - version: '3.9'
+          - version: '3.10'
+
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          submodules: true
+
+      - uses: actions/setup-python@v4
+        with:
+          python-version: ${{ matrix.python.version }}
+
+      - name: Prepare vcpkg.json
+        shell: bash
+        run: |
+          python --version
+          cp -f vcpkg-${{ matrix.python.version }}.json vcpkg.json
+          cat vcpkg.json
+
+      - name: Download Pulsar C++ client on Windows
+        shell: bash
+        run: |
+          mkdir -p ${{ env.PULSAR_CPP_DIR }}
+          cd ${{ env.PULSAR_CPP_DIR }}
+          # TODO: switch to official releases
+          curl -O -L 
https://github.com/BewareMyPower/pulsar-client-cpp/releases/download/v3.1.0-rc-20221028/${{
 matrix.windows.triplet }}-static.zip
+          unzip -q ${{ matrix.windows.triplet }}-static.zip
+          ls -l ${{ env.PULSAR_CPP_DIR }}
+
+      - name: Cache Vcpkg
+        uses: actions/cache@v3
+        id: cache-vcpkg
+        with:
+          path: build/vcpkg_installed
+          key: ${{ matrix.python.version }}-${{ 
hashFiles(format('vcpkg-{0}.json', matrix.python.version)) }}
+
+      - name: Install dependencies and configure CMake
+        shell: bash
+        run: |
+          COMMIT_ID=$(grep baseline vcpkg.json | sed 's/[",]//g' | awk '{print 
$2}')
+          cd vcpkg
+          echo "git fetch origin $COMMIT_ID"
+          git fetch origin $COMMIT_ID
+          cd -
+          cmake -B build ${{ matrix.windows.arch }} \
+            -DCMAKE_PREFIX_PATH=${{ env.PULSAR_CPP_DIR }} \
+            -DUSE_VCPKG=ON \
+            -DLINK_STATIC=ON
+
+      - name: Build Python wheel
+        shell: bash
+        run: |
+          cmake --build build --config Release --target install
+          python -m pip install wheel
+          python setup.py bdist_wheel
+          python -m pip install ./dist/*.whl
+          cp ./build/Release/boost_python*.dll .
+          echo "The extra DLLs:"
+          ls -l *.dll
+          python -c 'import pulsar; c = 
pulsar.Client("pulsar://localhost:6650"); c.close()'
+
+
   # Job that will be required to complete and depends on all the other jobs
   check-completion:
     name: Check Completion
     runs-on: ubuntu-latest
-    needs: [unit-tests, linux-wheel, mac-wheels]
+    needs: [unit-tests, linux-wheel, mac-wheels, windows-wheels]
 
     steps:
       - run: true
diff --git a/.gitignore b/.gitignore
index ee9119f..72f931b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,7 @@ __pycache__
 .pulsar-mac-wheels-cache
 .DS_Store
 wheelhouse
-.pulsar-mac-build
\ No newline at end of file
+.pulsar-mac-build
+vcpkg_installed/
+*.pyd
+*.lib
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..a0a57f3
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vcpkg"]
+       path = vcpkg
+       url = https://github.com/microsoft/vcpkg.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 10a70ec..a60e1c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,10 +17,15 @@
 # under the License.
 #
 
-project (pulsar-client-python)
 cmake_minimum_required(VERSION 3.18)
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules")
 
+option(USE_VCPKG "Use Vcpkg to install dependencies" OFF)
+if (USE_VCPKG)
+    set(CMAKE_TOOLCHAIN_FILE 
"${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
+        CACHE STRING "Vcpkg toolchain file")
+endif ()
+project (pulsar-client-python)
 option(LINK_STATIC "Link against static libraries" OFF)
 MESSAGE(STATUS "LINK_STATIC:  " ${LINK_STATIC})
 
@@ -29,10 +34,19 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE)
 find_package(Threads REQUIRED)
 MESSAGE(STATUS "Threads library: " ${CMAKE_THREAD_LIBS_INIT})
 
+if (MSVC)
+    add_compile_options(/wd4819)
+endif ()
+
 if (LINK_STATIC)
-    find_library(PULSAR_LIBRARY NAMES libpulsar.a)
+    if (MSVC)
+        find_library(PULSAR_LIBRARY NAMES pulsarWithDeps.lib)
+    else ()
+        find_library(PULSAR_LIBRARY NAMES libpulsar.a)
+    endif ()
+    add_definitions("-DPULSAR_STATIC")
 else()
-    find_library(PULSAR_LIBRARY NAMES libpulsar.so  libpulsar.dylib)
+    find_library(PULSAR_LIBRARY NAMES pulsar libpulsar)
 endif()
 message(STATUS "PULSAR_LIBRARY: ${PULSAR_LIBRARY}")
 
@@ -44,10 +58,23 @@ SET(CMAKE_CXX_STANDARD 11)
 find_package (Python3 REQUIRED COMPONENTS Development.Module)
 MESSAGE(STATUS "PYTHON: " ${Python3_VERSION} " - " ${Python3_INCLUDE_DIRS})
 
+find_package(Boost REQUIRED ${Boost_INCLUDE_DIRS})
+message(STATUS "Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}")
+
 SET(Boost_USE_STATIC_LIBS   ${LINK_STATIC})
-find_package(Boost REQUIRED COMPONENTS python3)
-MESSAGE(STATUS "Boost Python3: " ${Boost_PYTHON3_LIBRARY})
-MESSAGE(STATUS "Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}")
+
+set(BOOST_PYTHON_NAME_LIST python3 python310 python39 python38 python37)
+foreach (BOOST_PYTHON_NAME IN LISTS BOOST_PYTHON_NAME_LIST)
+    find_package(Boost QUIET COMPONENTS ${BOOST_PYTHON_NAME})
+    if (${Boost_FOUND})
+        set(BOOST_PYTHON_COMPONENT_FOUND ${BOOST_PYTHON_NAME})
+        message(STATUS "Found Boost COMPONENTS " 
${BOOST_PYTHON_COMPONENT_FOUND})
+        break ()
+    endif ()
+endforeach ()
+if (NOT BOOST_PYTHON_COMPONENT_FOUND)
+    message(FATAL_ERROR "Could not find Boost Python library")
+endif ()
 
 
########################################################################################################################
 
@@ -68,8 +95,11 @@ ADD_LIBRARY(_pulsar SHARED src/pulsar.cc
                            src/utils.cc
         )
 
-SET(CMAKE_SHARED_LIBRARY_PREFIX )
-SET(CMAKE_SHARED_LIBRARY_SUFFIX .so)
+if (MSVC)
+    set(CMAKE_SHARED_LIBRARY_SUFFIX .pyd)
+else ()
+    set(CMAKE_SHARED_LIBRARY_SUFFIX .so)
+endif ()
 
 if (NOT APPLE AND NOT MSVC)
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_PYTHON}")
@@ -80,12 +110,17 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
 endif()
 
 # Try all possible boost-python variable namings
-set(PYTHON_WRAPPER_LIBS ${PULSAR_LIBRARY}
-                        ${Boost_PYTHON3_LIBRARY})
+set(PYTHON_WRAPPER_LIBS
+    ${PULSAR_LIBRARY}
+    Boost::${BOOST_PYTHON_COMPONENT_FOUND}
+)
+if (MSVC)
+    set(PYTHON_WRAPPER_LIBS ${PYTHON_WRAPPER_LIBS} Python3::Module)
+endif ()
 
 message(STATUS "All libraries: ${PYTHON_WRAPPER_LIBS}")
 
-if (LINK_STATIC)
+if (LINK_STATIC AND NOT MSVC)
     set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
 
     # We need to include all the static libs individually because we cannot 
easily create a universal2 libpulsar.a
@@ -125,9 +160,14 @@ if (LINK_STATIC)
         endif()
         target_link_libraries(_pulsar ${PYTHON_WRAPPER_LIBS})
     endif ()
+elseif (LINK_STATIC) # MSVC
+    set_property(TARGET _pulsar PROPERTY
+        MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+    target_link_libraries(_pulsar ${PYTHON_WRAPPER_LIBS})
 else()
     target_link_libraries(_pulsar ${PYTHON_WRAPPER_LIBS})
 endif ()
+install(TARGETS _pulsar DESTINATION ${CMAKE_SOURCE_DIR})
 
 find_package(ClangTools)
 set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build-support")
diff --git a/README.md b/README.md
index 0d403cd..b0857a0 100644
--- a/README.md
+++ b/README.md
@@ -31,13 +31,39 @@
 
 ## Install the Python wheel
 
+### Windows (with Vcpkg)
+
+First, install the dependencies via 
[Vcpkg](https://github.com/microsoft/vcpkg).
+
+```PowerShell
+vcpkg install --feature-flags=manifests --triplet x64-windows
+```
+
+> NOTE: For Windows 32-bit library, change `x64-windows` to `x86-windows`, see 
[here](https://github.com/microsoft/vcpkg/tree/master/triplets) for all 
available triplets.
+
+Then, build and install the Python wheel.
+
+```PowerShell
+# Assuming the Pulsar C++ client has been installed under the `PULSAR_CPP` 
directory.
+cmake -B build -DUSE_VCPKG=ON -DCMAKE_PREFIX_PATH="$env:PULSAR_CPP" 
-DLINK_STATIC=ON
+cmake --build build --config Release
+cmake --install build
+py setup.py bdist_wheel
+py -m pip install ./dist/pulsar_client-*.whl
+```
+
+Since the Python client links to Boost.Python dynamically, you have to copy 
the dll (e.g. `boost_python310-vc142-mt-x64-1_80.dll`) into the system path 
(the `PATH` environment variable). If the `-DLINK_STATIC=ON` option is not 
specified, you have to copy the `pulsar.dll` into the system path as well.
+
+### Linux or macOS
+
+Assuming the Pulsar C++ client and Boost.Python have been installed under the 
system path.
+
 ```bash
 cmake -B build
 cmake --build build -j8
-cp build/_pulsar.so .
+cmake --install build
 ./setup.py bdist_wheel
 pip3 install dist/pulsar_client-*.whl --force-reinstall
-rm _pulsar.so
 ```
 
 > **NOTE**
@@ -45,6 +71,8 @@ rm _pulsar.so
 > 1. Here a separate `build` directory is created to store all CMake temporary 
 > files. However, the `setup.py` requires the `_pulsar.so` is under the 
 > project directory.
 > 2. Add the `--force-reinstall` option to overwrite the existing Python wheel 
 > in case your system has already installed a wheel before.
 
+## Running examples
+
 You can run `python3 -c 'import pulsar'` to see whether the wheel has been 
installed successfully. If it failed, check whether dependencies (e.g. 
`libpulsar.so`) are in the system path. If not, make sure the dependencies are 
in `LD_LIBRARY_PATH` (on Linux) or `DYLD_LIBRARY_PATH` (on macOS).
 
 Then you can run examples as a simple end-to-end test.
diff --git a/setup.py b/setup.py
index f20446f..046b2bb 100755
--- a/setup.py
+++ b/setup.py
@@ -21,6 +21,7 @@
 from setuptools import setup
 from distutils.core import Extension
 from os import environ, path
+import platform
 
 from distutils.command import build_ext
 
@@ -58,7 +59,13 @@ class my_build_ext(build_ext.build_ext):
         except OSError as e:
             if e.errno != 17:  # already exists
                 raise
-        shutil.copyfile('_pulsar.so', self.get_ext_fullpath(ext.name))
+        if 'Windows' in platform.platform():
+            shutil.copyfile('_pulsar.pyd', self.get_ext_fullpath(ext.name))
+        else:
+            try:
+                shutil.copyfile('_pulsar.so', self.get_ext_fullpath(ext.name))
+            except FileNotFoundError:
+                shutil.copyfile('lib_pulsar.so', 
self.get_ext_fullpath(ext.name))
 
 
 # Core Client dependencies
diff --git a/vcpkg b/vcpkg
new file mode 160000
index 0000000..2537044
--- /dev/null
+++ b/vcpkg
@@ -0,0 +1 @@
+Subproject commit 253704407ae68efa37bf8f5b59b3e06dd40d3d3f
diff --git a/vcpkg-3.10.json b/vcpkg-3.10.json
new file mode 100644
index 0000000..fdb1128
--- /dev/null
+++ b/vcpkg-3.10.json
@@ -0,0 +1,12 @@
+{
+  "name": "pulsar-python",
+  "version": "3.0.0",
+  "description": "Pulsar Python SDK (Python 3.10)",
+  "dependencies": [
+     {
+       "name": "boost-python",
+       "version>=": "1.79.0"
+     }
+  ],
+  "builtin-baseline": "c266859544a3cdcfd952d218039c55a268863740"
+}
diff --git a/vcpkg-3.7.json b/vcpkg-3.7.json
new file mode 100644
index 0000000..1b7b846
--- /dev/null
+++ b/vcpkg-3.7.json
@@ -0,0 +1,18 @@
+{
+  "name": "pulsar-python",
+  "version": "3.0.0",
+  "description": "Pulsar Python SDK (Python 3.7)",
+  "dependencies": [
+     {
+       "name": "boost-python",
+       "version>=": "1.76.0"
+     }
+  ],
+  "builtin-baseline": "35312384e7701760ed7855961eff41a63f9cc379",
+  "overrides": [
+     {
+       "name": "python3",
+       "version": "3.7.3"
+     }
+  ]
+}
diff --git a/vcpkg-3.8.json b/vcpkg-3.8.json
new file mode 100644
index 0000000..b210e87
--- /dev/null
+++ b/vcpkg-3.8.json
@@ -0,0 +1,18 @@
+{
+  "name": "pulsar-python",
+  "version": "3.0.0",
+  "description": "Pulsar Python SDK (Python 3.8)",
+  "dependencies": [
+     {
+       "name": "boost-python",
+       "version>=": "1.76.0"
+     }
+  ],
+  "builtin-baseline": "35312384e7701760ed7855961eff41a63f9cc379",
+  "overrides": [
+     {
+       "name": "python3",
+       "version": "3.8.3"
+     }
+  ]
+}
diff --git a/vcpkg-3.9.json b/vcpkg-3.9.json
new file mode 100644
index 0000000..250a1ee
--- /dev/null
+++ b/vcpkg-3.9.json
@@ -0,0 +1,12 @@
+{
+  "name": "pulsar-python",
+  "version": "3.0.0",
+  "description": "Pulsar Python SDK (Python 3.9)",
+  "dependencies": [
+     {
+       "name": "boost-python",
+       "version>=": "1.76.0"
+     }
+  ],
+  "builtin-baseline": "35312384e7701760ed7855961eff41a63f9cc379"
+}
diff --git a/vcpkg.json b/vcpkg.json
new file mode 100644
index 0000000..ef5c7c2
--- /dev/null
+++ b/vcpkg.json
@@ -0,0 +1,6 @@
+{
+  "name": "pulsar-python",
+  "version": "3.0.0",
+  "description": "Pulsar Python SDK",
+  "dependencies": ["boost-python"]
+}

Reply via email to