From 9ee89c181a97365c880dd3d6ba7375605d86aa78 Mon Sep 17 00:00:00 2001 From: everoddandeven Date: Fri, 23 Jan 2026 19:04:23 +0100 Subject: [PATCH] CI windows build * Add windows CI build * Update README with windows build instructions * Update build script --- .github/workflows/build-windows.yml | 115 ++++++++++++++++ .github/workflows/codacy.yml | 42 +++++- .github/workflows/test.yml | 197 ++++++++++++++++++++++------ CMakeLists.txt | 37 +++++- README.md | 50 ++++++- bin/build_libmonero_python.sh | 25 +++- 6 files changed, 416 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/build-windows.yml diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml new file mode 100644 index 0000000..6927224 --- /dev/null +++ b/.github/workflows/build-windows.yml @@ -0,0 +1,115 @@ +name: Build Windows Package + +on: + push: + branches: [ main ] + workflow_dispatch: + +jobs: + msys2-mingw64: + name: MSYS2 MinGW64 + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: '3.13' + architecture: 'x64' + + - name: Setup MSYS2 MINGW64 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + mingw-w64-x86_64-openssl + mingw-w64-x86_64-zeromq + mingw-w64-x86_64-libsodium + mingw-w64-x86_64-hidapi + mingw-w64-x86_64-unbound + mingw-w64-x86_64-protobuf + mingw-w64-x86_64-libusb + mingw-w64-x86_64-ntldd + git + make + gettext + base-devel + wget + + - name: Install ICU v75.1.1 + shell: msys2 {0} + run: | + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-icu-75.1-1-any.pkg.tar.zst + pacman -U --noconfirm mingw-w64-x86_64-icu-75.1-1-any.pkg.tar.zst + + - name: Install boost v1.85.0 + shell: msys2 {0} + run: | + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-boost-1.85.0-4-any.pkg.tar.zst + pacman -U --noconfirm mingw-w64-x86_64-boost-1.85.0-4-any.pkg.tar.zst + + - name: Install pybind11 v2.11.1 + shell: msys2 {0} + run: | + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-pybind11-2.11.1-1-any.pkg.tar.zst + pacman -U --noconfirm mingw-w64-x86_64-pybind11-2.11.1-1-any.pkg.tar.zst + + - name: Build monero + shell: msys2 {0} + run: | + cd external/monero-cpp/external/monero-project + mkdir -p build/release + cd build/release + cmake -G "MSYS Makefiles" \ + -D STATIC=ON \ + -D ARCH="x86-64" \ + -D BUILD_64=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_TAG="win-x64" \ + -D CMAKE_TOOLCHAIN_FILE="../../cmake/64-bit-toolchain.cmake" \ + -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) \ + ../../ + make wallet cryptonote_protocol + + - name: Build monero-cpp + shell: msys2 {0} + run: | + cd external/monero-cpp + mkdir -p build + cd build + cmake .. + cmake --build . + + - name: Build monero-python + shell: msys2 {0} + run: | + mkdir -p build + cd build + export WIN_PYTHON_EXE=$(cygpath -u "$PYTHON") + cmake .. -DPython3_EXECUTABLE="$WIN_PYTHON_EXE" \ + -DPython3_FIND_STRATEGY=LOCATION \ + -DPython3_FIND_REGISTRY=NEVER + cmake --build . + mkdir -p ../dist + cp *.pyd ../dist/monero.pyd + ntldd -R *.pyd | grep mingw64 | awk '{print $3}' | while read -r line; do + cp "$(cygpath -u "$line")" ../dist/ + done + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: monero-python-win-amd64 + path: dist/ diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index c7d6e95..f4b0abb 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -12,7 +12,7 @@ permissions: contents: read jobs: - report-coverage: + report-coverage-linux: if: github.repository == 'everoddandeven/monero-python' runs-on: ubuntu-latest steps: @@ -24,7 +24,7 @@ jobs: - name: Download coverage report uses: actions/download-artifact@v4 with: - name: coverage-reports + name: coverage-reports-linux github-token: ${{ secrets.API_GITHUB }} run-id: ${{ github.event.workflow_run.id }} @@ -48,3 +48,41 @@ jobs: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} language: cpp coverage-reports: coverage.info + +# TODO build docker monero image for windows +# report-coverage-windows: +# if: github.repository == 'everoddandeven/monero-python' +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 +# with: +# fetch-depth: 0 + +# - name: Download coverage report +# uses: actions/download-artifact@v4 +# with: +# name: coverage-reports-windows +# github-token: ${{ secrets.API_GITHUB }} +# run-id: ${{ github.event.workflow_run.id }} + +# - name: Report python coverage +# uses: codacy/codacy-coverage-reporter-action@v1 +# with: +# project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} +# language: python +# coverage-reports: coverage.xml + +# - name: Report c coverage +# uses: codacy/codacy-coverage-reporter-action@v1 +# with: +# project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} +# language: c +# coverage-reports: coverage.info + +# - name: Report c++ coverage +# uses: codacy/codacy-coverage-reporter-action@v1 +# with: +# project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} +# language: cpp +# coverage-reports: coverage.info diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ad1eda..f41058b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ on: workflow_dispatch: jobs: - noble: + linux: runs-on: ubuntu-24.04 name: linux (pytest) @@ -50,58 +50,32 @@ jobs: sudo make install cd ../ -# TODO re-enable this after https://github.com/woodser/monero-cpp/pull/95 and remove custom regtest build -# - name: Update submodules -# run: | -# git submodule update --init --recursive - -# - name: Build monero -# run: | -# mkdir build -# cd external/monero-cpp/external/monero-project -# mkdir -p build/release -# cd build/release -# cmake -DSTATIC=ON -DBUILD_64=ON -DCMAKE_BUILD_TYPE=Release ../../ -# make -j3 wallet cryptonote_protocol -# cd ../../../../../../ - -# - name: Build monero-cpp -# run: | -# cd external/monero-cpp -# mkdir -p build -# cd build -# cmake .. -# cmake --build . -# make -j3 -# sudo cp libmonero-cpp.so /usr/lib/ -# cd ../../../ - -# TODO remove regtest build - name: Clone monero-cpp (regtest) run: | + cd external + rm -rf monero-cpp git clone -b regtest-env --single-branch --recurse-submodules https://github.com/everoddandeven/monero-cpp.git + cd .. - - name: Build monero (regtest) + - name: Build monero run: | - cd monero-cpp/external/monero-project + cd external/monero-cpp/external/monero-project mkdir -p build/release cd build/release cmake -DSTATIC=ON -DBUILD_64=ON -DCMAKE_BUILD_TYPE=Release ../../ make -j3 wallet cryptonote_protocol - cd ../../../../../ + cd ../../../../../../ - - name: Install monero-cpp (regtest) + - name: Install monero-cpp run: | - cd monero-cpp + cd external/monero-cpp mkdir -p build cd build cmake .. cmake --build . make -j3 sudo cp libmonero-cpp.so /usr/lib/ - cd ../../ - mkdir -p external - mv monero-cpp external/ + cd ../../../ - name: Install monero-python run: | @@ -138,7 +112,156 @@ jobs: - name: Upload coverage report uses: actions/upload-artifact@v4 with: - name: coverage-reports + name: coverage-reports-linux path: | coverage.info coverage.xml + + windows: + name: windows (pytest) + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: '3.13' + architecture: 'x64' + + - name: Install pytest + shell: bash + run: | + python -m pip install pytest pytest-rerunfailures pytest-cov gcovr + + - name: Setup MSYS2 MINGW64 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + mingw-w64-x86_64-openssl + mingw-w64-x86_64-zeromq + mingw-w64-x86_64-libsodium + mingw-w64-x86_64-hidapi + mingw-w64-x86_64-unbound + mingw-w64-x86_64-protobuf + mingw-w64-x86_64-libusb + mingw-w64-x86_64-ntldd + git + make + gettext + base-devel + wget + + - name: Install ICU v75.1.1 + shell: msys2 {0} + run: | + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-icu-75.1-1-any.pkg.tar.zst + pacman -U --noconfirm mingw-w64-x86_64-icu-75.1-1-any.pkg.tar.zst + + - name: Install boost v1.85.0 + shell: msys2 {0} + run: | + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-boost-1.85.0-4-any.pkg.tar.zst + pacman -U --noconfirm mingw-w64-x86_64-boost-1.85.0-4-any.pkg.tar.zst + + - name: Install pybind11 v2.11.1 + shell: msys2 {0} + run: | + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-pybind11-2.11.1-1-any.pkg.tar.zst + pacman -U --noconfirm mingw-w64-x86_64-pybind11-2.11.1-1-any.pkg.tar.zst + + - name: Clone monero-cpp (regtest) + shell: msys2 {0} + run: | + cd external + rm -rf monero-cpp + git clone -b regtest-env --single-branch --recurse-submodules https://github.com/everoddandeven/monero-cpp.git + cd .. + + - name: Build monero + shell: msys2 {0} + run: | + cd external/monero-cpp/external/monero-project + mkdir -p build/release + cd build/release + cmake -G "MSYS Makefiles" \ + -D STATIC=ON \ + -D ARCH="x86-64" \ + -D BUILD_64=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_TAG="win-x64" \ + -D CMAKE_TOOLCHAIN_FILE="../../cmake/64-bit-toolchain.cmake" \ + -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) \ + ../../ + make wallet cryptonote_protocol + + - name: Build monero-cpp + shell: msys2 {0} + run: | + cd external/monero-cpp + mkdir -p build + cd build + cmake .. + cmake --build . + + - name: Build monero-python + shell: msys2 {0} + run: | + mkdir -p build + cd build + export WIN_PYTHON_EXE=$(cygpath -u "$PYTHON") + cmake .. -DPython3_EXECUTABLE="$WIN_PYTHON_EXE" \ + -DPython3_FIND_STRATEGY=LOCATION \ + -DPython3_FIND_REGISTRY=NEVER + cmake --build . + mkdir -p ../dist + cp *.pyd ../dist/monero.pyd + ntldd -R *.pyd | grep mingw64 | awk '{print $3}' | while read -r line; do + cp "$(cygpath -u "$line")" ../dist/ + done + cd .. + +# TODO build monero docker image for windows +# - name: Setup test environment +# shell: bash +# run: | +# docker compose -f tests/docker-compose.yml up -d node_1 node_2 xmr_wallet_1 xmr_wallet_2 + +# - name: Run tests +# shell: bash +# env: +# IN_CONTAINER: "true" +# PYTHONPATH: ${{ github.workspace }}/dist +# PATH: "${{ github.workspace }}/dist:/usr/bin:$PATH" +# run: | +# python -m pytest --cov=tests --cov-report=xml + +# - name: Cleanup test environment +# if: always() +# shell: bash +# run: docker compose -f tests/docker-compose.yml down -v + +# - name: Generate coverage report +# shell: bash +# run: | +# gcovr -r . --filter src/ --lcov coverage.info + +# - name: Upload coverage report +# uses: actions/upload-artifact@v4 +# with: +# name: coverage-reports-windows +# path: | +# coverage.info +# coverage.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b5979c..cd92815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,23 @@ cmake_minimum_required(VERSION 3.14) + +if (WIN32) + add_definitions( "-D_GLIBCXX_USE_NANOSLEEP=1" ) # "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8 + add_definitions( "-DWIN32_LEAN_AND_MEAN" ) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -O2 -fPIC -F/Library/Frameworks -pthread -lcrypto -lcrypt32 -lbcrypt") +else() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -F/Library/Frameworks -pthread") +endif() + project(monero LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module) find_package(pybind11 REQUIRED) +set(Boost_NO_BOOST_CMAKE 1) +set(Boost_USE_MULTITHREADED ON) +find_package(Boost REQUIRED COMPONENTS system thread serialization filesystem) set(SOURCES src/cpp/common/py_monero_common.cpp @@ -36,10 +51,22 @@ link_directories( pybind11_add_module(monero ${SOURCES}) -target_link_libraries(monero PRIVATE monero-cpp) +target_link_libraries(monero PRIVATE + monero-cpp -if (MSVC) - target_compile_options(monero PRIVATE /std:c++17) -else() - target_compile_options(monero PRIVATE -std=c++17) + Boost::system + Boost::thread + Boost::serialization + Boost::filesystem + + ssl + crypto +) + +if (WIN32) + target_link_libraries(monero PRIVATE + ws2_32 + crypt32 + bcrypt + ) endif() diff --git a/README.md b/README.md index db0f1ef..42a7e37 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # Monero Python Library -[![Build](https://github.com/everoddandeven/monero-python/actions/workflows/build-deb.yml/badge.svg)](https://github.com/everoddandeven/monero-python/actions/workflows/build-deb.yml) -[![Tests](https://github.com/everoddandeven/monero-python/actions/workflows/test.yml/badge.svg)](https://github.com/everoddandeven/monero-python/actions/workflows/test.yml) + [![Codacy Badge](https://app.codacy.com/project/badge/Grade/aeff91a5b1d543ddb400f88ffce150a8)](https://app.codacy.com/gh/everoddandeven/monero-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/aeff91a5b1d543ddb400f88ffce150a8)](https://app.codacy.com/gh/everoddandeven/monero-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage) +[![Tests](https://github.com/everoddandeven/monero-python/actions/workflows/test.yml/badge.svg)](https://github.com/everoddandeven/monero-python/actions/workflows/test.yml) + +[![Build Debian Packages](https://github.com/everoddandeven/monero-python/actions/workflows/build-deb.yml/badge.svg)](https://github.com/everoddandeven/monero-python/actions/workflows/build-deb.yml) +[![Build Windows Package](https://github.com/everoddandeven/monero-python/actions/workflows/build-windows.yml/badge.svg?branch=main)](https://github.com/everoddandeven/monero-python/actions/workflows/build-windows.yml) > [!WARNING] > @@ -122,6 +125,7 @@ wallet_full.close(true) 6. Or build and install monero-python with pip: `pip3 install . --break-system-packages` ### Linux Docker Build + 1. Install [Docker](https://docs.docker.com/engine/install/) 2. Clone the repository ```sh @@ -147,6 +151,41 @@ wallet_full.close(true) 6. Library build will be placed in `monero-python/build` directory +### Windows + +1. Download and install [MSYS2](https://www.msys2.org/). +2. Press the Windows button and launch `MSYS2 MINGW64`. +3. Update packages: `pacman -Syu` and confirm at prompts. +4. Relaunch MSYS2 (if necessary) and install dependencies: + + ``` + pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-unbound mingw-w64-x86_64-protobuf git mingw-w64-x86_64-libusb gettext base-devel + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-icu-75.1-1-any.pkg.tar.zst + pacman -U mingw-w64-x86_64-icu-75.1-1-any.pkg.tar.zst + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-boost-1.85.0-4-any.pkg.tar.zst + pacman -U mingw-w64-x86_64-boost-1.85.0-4-any.pkg.tar.zst + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-pybind11-2.11.1-1-any.pkg.tar.zst + pacman -U mingw-w64-x86_64-pybind11-2.11.1-1-any.pkg.tar.zst + ``` + +5. Clone repo: `git clone --recurse-submodules https://github.com/everoddandeven/monero-python.git` +6. Build monero-cpp, located as a submodule at ./external/monero-cpp: + + ``` + ./bin/build_libmonero_cpp.sh + ``` + +7. Build monero-python: + + ``` + mkdir -p build && + cd build && + cmake .. + cmake --build . + ``` + +8. Or run build script: `./bin/build_libmonero_python.sh` + ## Use RPC servers in your project: 1. Download and install [Monero CLI](https://web.getmonero.org/downloads/). @@ -202,12 +241,15 @@ For example: `export LD_PRELOAD=/path/to/libjemalloc.a` then run your app. ```bash pytest ``` - +7. Cleanup docker test environment + ```bash + docker compose -f tests/docker-compose.yml down -v + ``` ## Related projects * [monero-cpp](https://github.com/woodser/monero-cpp) -* [monero-java](https://github.com/woodser/monero-cpp) +* [monero-java](https://github.com/woodser/monero-java) * [monero-ts](https://github.com/woodser/monero-ts) ## License diff --git a/bin/build_libmonero_python.sh b/bin/build_libmonero_python.sh index 3104e6f..246585b 100755 --- a/bin/build_libmonero_python.sh +++ b/bin/build_libmonero_python.sh @@ -6,10 +6,31 @@ git submodule update --init --force || exit 1 HOST_NCORES=$(nproc 2>/dev/null || shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1) if [[ $(uname -s) == "MINGW64_NT"* || $(uname -s) == "MSYS"* ]]; then bit=$(getconf LONG_BIT) + FOLDER=$(cd ${MINGW_PREFIX}/.. && pwd -W) if [ "$bit" == "64" ]; then - make release-static-win64 -j$HOST_NCORES || exit 1 + cmake -G "MSYS Makefiles" \ + -D STATIC=ON \ + -D ARCH="x86-64" \ + -D BUILD_64=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_TAG="win-x64" \ + -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake \ + -D MSYS2_FOLDER="$FOLDER" \ + ../../ + + make -j"$HOST_NCORES" wallet cryptonote_protocol || exit 1 else - make release-static-win32 -j$HOST_NCORES || exit 1 + cmake -G "MSYS Makefiles" \ + -D STATIC=ON \ + -D ARCH="i686" \ + -D BUILD_64=OFF \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_TAG="win-x32" \ + -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake \ + -D MSYS2_FOLDER="$FOLDER" \ + ../../ + + make -j"$HOST_NCORES" wallet cryptonote_protocol || exit 1 fi else # OS is not windows