diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0ef5edd6..ebe3345d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -14,6 +14,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ g++ \ gdb \ just \ + lcov \ ninja-build \ python3 \ openmpi-bin \ diff --git a/.devcontainer/postStartCommand.sh b/.devcontainer/postStartCommand.sh index fdb6ddbd..576974ea 100755 --- a/.devcontainer/postStartCommand.sh +++ b/.devcontainer/postStartCommand.sh @@ -8,4 +8,4 @@ echo "Set up development environment in $WORKSPACE" uv sync --all-groups --all-extras -v # install prek hooks -uv run prek install --install-hooks +uv run prek install --install-hooks --overwrite diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index d40f399b..74da2ab9 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -14,6 +14,9 @@ jobs: copilot-setup-steps: runs-on: ubuntu-26.04 + env: + CPM_USE_LOCAL_PACKAGES: "TRUE" + permissions: contents: read @@ -36,5 +39,4 @@ jobs: - name: Install package run: | - export CPM_USE_LOCAL_PACKAGES=TRUE - uv sync --all-extras + uv sync --all-groups --all-extras -v diff --git a/.github/workflows/docpages.yml b/.github/workflows/docpages.yml index a030c522..09cb071e 100644 --- a/.github/workflows/docpages.yml +++ b/.github/workflows/docpages.yml @@ -39,6 +39,9 @@ jobs: build: runs-on: ubuntu-26.04 + env: + SKBUILD_CMAKE_DEFINE: monoprop_ENABLE_CXX_UNIT_TESTS=OFF + steps: - name: Checkout uses: actions/checkout@v7.0.1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7342cfca..c7e6000a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,32 +20,44 @@ concurrency: env: FORCE_COLOR: 3 - # Directory where build-wrapper output will be placed - BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory COLUMNS: 120 CPM_USE_LOCAL_PACKAGES: "TRUE" jobs: - python-checks: + package-tests: # in combination with the PR types selection, this top-level if statement # skips running the workflow for Draft PRs if: ${{ github.event_name == 'push' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'test-in-draft') }} - name: Check bindings for ${{ matrix.python-version }} on ${{ matrix.runner }} + name: Check project ${{ matrix.runner }}/Python ${{ matrix.python-version }}/Compiler ${{ matrix.compiler}}/Wide term index ${{ matrix.wide}} strategy: fail-fast: false matrix: - python-version: - - "3.11" - - "3.14" - runner: - - ubuntu-26.04 - - ubuntu-26.04-arm - - macos-15 + python-version: ["3.11", "3.14"] + runner: [ubuntu-26.04, ubuntu-26.04-arm, macos-15] + compiler: [default, g++-14, clang++-18] + wide: ["off"] + exclude: + - compiler: g++-14 + python-version: "3.14" + - compiler: clang++-18 + python-version: "3.14" + - compiler: g++-14 + runner: macos-15 + - compiler: clang++-18 + runner: macos-15 + include: + - python-version: "3.11" + runner: ubuntu-26.04 + compiler: default + wide: "on" runs-on: ${{ matrix.runner}} + env: + SKBUILD_CMAKE_DEFINE: "monoprop_WIDE_TERM_INDEX=${{ matrix.wide }}" + steps: - uses: actions/checkout@v7.0.1 with: @@ -54,16 +66,24 @@ jobs: - name: Install dependencies run: | if [[ "${{ matrix.runner }}" == "macos-15" ]]; then - brew install boost open-mpi + brew install boost open-mpi msgpack-cxx else sudo apt-get update - sudo apt-get install -y libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev + packages="libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev" + if [[ "${{ matrix.compiler }}" == "clang++-18" ]]; then + packages="$packages clang-18" + fi + sudo apt-get install -y $packages fi - name: Set build environment variables - if: ${{ matrix.runner == 'macos-15' }} run: | - echo "MACOSX_DEPLOYMENT_TARGET=15.0" >> "$GITHUB_ENV" + if [[ "${{ matrix.runner }}" == "macos-15" ]]; then + echo "MACOSX_DEPLOYMENT_TARGET=15.0" >> "$GITHUB_ENV" + fi + if [[ "${{ matrix.compiler }}" != "default" ]]; then + echo "CXX=${{ matrix.compiler }}" >> "$GITHUB_ENV" + fi - name: Install the latest version of uv uses: astral-sh/setup-uv@v9.0.0 @@ -73,7 +93,7 @@ jobs: - name: Install package run: | - uv sync --no-progress --group dev --group test --all-extras -v + uv sync --no-progress --group test --all-extras -v - name: Python environment information run: | @@ -83,26 +103,50 @@ jobs: run: | uv run python -c "import monoprop as mp; print(mp.__version__)" + - name: Verify that find_package(monoprop) works + run: | + mkdir -p find-package-smoke + + cat > find-package-smoke/CMakeLists.txt <<'EOF' + cmake_minimum_required(VERSION 3.28) + project(monoprop_find_package_smoke LANGUAGES CXX) + + find_package(monoprop CONFIG REQUIRED) + + add_executable(smoke main.cpp) + target_link_libraries(smoke PRIVATE monoprop::monoprop) + EOF + + cat > find-package-smoke/main.cpp <<'EOF' + auto main() -> int { return 0; } + EOF + + cmake -S find-package-smoke -B find-package-smoke/build -Dmonoprop_DIR="$(uv run --no-sync python -c "import sysconfig; print(sysconfig.get_path('purelib'))")"/monoprop/cmake + + cmake --build find-package-smoke/build + - name: Get test group label id: test-group-label run: | runner="${{ matrix.runner }}" pyver="${{ matrix.python-version }}" - echo "label=$runner-$pyver" >> $GITHUB_OUTPUT + compiler="${{ matrix.compiler }}" + wide="${{ matrix.wide }}" + echo "label=$runner-$pyver-$compiler-$wide" >> $GITHUB_OUTPUT - - name: Run tests + - name: Run Python tests run: | - export COVERAGE_FILE="coverage-${{ steps.test-group-label.outputs.label }}" - uv run pytest \ -r aR \ - --cov=src/monoprop \ - --cov-report=xml:coverage-"${{ steps.test-group-label.outputs.label }}".xml \ --durations=50 \ --durations-min=5.0 \ --junit-xml=pytest-"${{ steps.test-group-label.outputs.label }}".xml \ -o junit_family=legacy + - name: Run C++ unit tests + run: | + ctest --test-dir build/editable/Release --output-on-failure --output-junit ctest-"${{ steps.test-group-label.outputs.label }}".xml + - name: Run Python tests under MPI env: OMPI_ALLOW_RUN_AS_ROOT: "1" @@ -124,34 +168,24 @@ jobs: uv run pytest tests --with-mpi -m mpi -v done - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v7 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: coverage-${{ steps.test-group-label.outputs.label }}.xml - flags: python - disable_search: true - fail_ci_if_error: true - - name: Upload test results to Codecov - uses: codecov/codecov-action@v7 + uses: codecov/codecov-action@v7.0.0 with: token: ${{ secrets.CODECOV_TOKEN }} - files: pytest-${{ steps.test-group-label.outputs.label }}.xml + files: pytest-${{ steps.test-group-label.outputs.label }}.xml, ctest-${{ steps.test-group-label.outputs.label }}.xml report_type: test_results disable_search: true fail_ci_if_error: true - cpp-checks: + code-coverage: # in combination with the PR types selection, this top-level if statement # skips running the workflow for Draft PRs if: ${{ github.event_name == 'push' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'test-in-draft') }} - name: Check C++ on ubuntu-26.04 (coverage) + name: Check code coverage runs-on: ubuntu-26.04 - env: - BUILD_DIR: build/coverage-gcc + SKBUILD_CMAKE_BUILD_TYPE: "Coverage" steps: - uses: actions/checkout@v7.0.1 @@ -161,152 +195,62 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y ninja-build libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev - - - name: Configure - run: cmake --preset coverage-gcc + sudo apt-get install -y libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev - - name: Build - run: cmake --build --preset coverage-gcc + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v9.0.0 + with: + enable-cache: true + python-version: "3.11" - # --output-junit resolves relative to the test directory (build/coverage-gcc), so ../../ - # puts the report at the workspace root where the Codecov step looks for it. - - name: Test + - name: Install package run: | - ctest --preset coverage-gcc --output-junit ../../ctest-ubuntu_latest.xml + uv sync --no-progress --group test --all-extras -v - - name: Verify CMake install and find_package(monoprop) + - name: Run Python tests run: | - cmake --install "$BUILD_DIR" --prefix "$PWD/monoprop-install" - - mkdir -p find-package-smoke - - cat > find-package-smoke/CMakeLists.txt <<'EOF' - cmake_minimum_required(VERSION 3.28) - project(monoprop_find_package_smoke LANGUAGES CXX) - - find_package(monoprop CONFIG REQUIRED) - - add_executable(smoke main.cpp) - target_link_libraries(smoke PRIVATE monoprop::monoprop) - EOF - - cat > find-package-smoke/main.cpp <<'EOF' - auto main() -> int { return 0; } - EOF - - cmake -S find-package-smoke -B find-package-smoke/build \ - -DCMAKE_CXX_COMPILER=g++ \ - -DCMAKE_PREFIX_PATH="$PWD/monoprop-install" - - cmake --build find-package-smoke/build + uv run pytest \ + -r aR \ + --cov=src/monoprop \ + --cov-report=xml:python-coverage.xml \ + --durations=50 \ + --durations-min=5.0 - - name: Collect coverage into a gcovr tracefile + - name: Collect coverage (C++ through Python bindings) run: | - pipx run gcovr \ + uvx gcovr \ --gcov-executable gcov \ --gcov-ignore-parse-errors \ --exclude-throw-branches \ --exclude-unreachable-branches \ - --root . \ --filter '^(src|include)/' \ --exclude '^tests/' \ - "$BUILD_DIR" \ - --json cpp-coverage-native.json - - - name: Convert C++ tracefile to Cobertura XML for Codecov - run: | - - pipx run gcovr \ - --root . \ --merge-lines \ - --add-tracefile cpp-coverage-native.json \ - --cobertura cpp-coverage-native.xml - - - name: Upload C++ coverage to Codecov - uses: codecov/codecov-action@v7 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: cpp-coverage-native.xml - flags: cpp - disable_search: true - fail_ci_if_error: true - - - name: Upload test results to Codecov - uses: codecov/codecov-action@v7 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ctest-ubuntu_latest.xml - report_type: test_results - disable_search: true - fail_ci_if_error: true - - python-bindings-coverage: - # in combination with the PR types selection, this top-level if statement - # skips running the workflow for Draft PRs - if: ${{ github.event_name == 'push' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'test-in-draft') }} - - name: Collect C++ coverage through the Python bindings - runs-on: ubuntu-26.04 - permissions: - contents: read - - env: - # Instruments the C++ reached through the Python bindings for gcov. - SKBUILD_CMAKE_BUILD_TYPE: Coverage - # A persistent build dir, so gcov can still find the .gcno/.gcda files after the tests run. - SKBUILD_BUILD_DIR: ${{ github.workspace }}/build-python-coverage - - steps: - - uses: actions/checkout@v7.0.1 - with: - fetch-depth: 0 + "build/editable/Coverage" \ + --cobertura cpp-coverage-through-python-bindings.xml - - name: Install dependencies + - name: Run C++ unit tests run: | - sudo apt-get update - sudo apt-get install -y libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev - - - name: Install the latest version of uv - uses: astral-sh/setup-uv@v9.0.0 - with: - enable-cache: true - python-version: "3.11" + ctest --test-dir build/editable/Coverage --output-on-failure - - name: Install package - run: | - uv sync --no-progress --group test --all-extras -v - - - name: Run tests to exercise the bindings - run: | - uv run pytest tests -r aR - - - name: Collect coverage into a gcovr tracefile + - name: Collect coverage (C++ unit tests) run: | - pipx run gcovr \ + uvx gcovr \ --gcov-executable gcov \ --gcov-ignore-parse-errors \ --exclude-throw-branches \ --exclude-unreachable-branches \ - --root . \ --filter '^(src|include)/' \ --exclude '^tests/' \ - "$SKBUILD_BUILD_DIR" \ - --json cpp-coverage-python.json - - - name: Convert C++ tracefile to Cobertura XML for Codecov - run: | - pipx run gcovr \ - --root . \ --merge-lines \ - --add-tracefile cpp-coverage-python.json \ - --cobertura cpp-coverage-python.xml + "build/editable/Coverage" \ + --cobertura cpp-coverage.xml - name: Upload C++ coverage to Codecov uses: codecov/codecov-action@v7.0.0 with: token: ${{ secrets.CODECOV_TOKEN }} - files: cpp-coverage-python.xml + files: python-coverage.xml, cpp-coverage-through-python-bindings.xml, cpp-coverage.xml flags: cpp disable_search: true fail_ci_if_error: true @@ -318,6 +262,9 @@ jobs: name: SonarQube analysis runs-on: ubuntu-26.04 + env: + SKBUILD_CMAKE_DEFINE: "monoprop_ENABLE_MPI=ON" + steps: - uses: actions/checkout@v7.0.1 with: @@ -328,24 +275,15 @@ jobs: sudo apt-get update sudo apt-get install -y ninja-build libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev - - name: Install matching Build Wrapper - run: | - curl --location --output build-wrapper-linux-x86.zip \ - https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip - unzip -q -o build-wrapper-linux-x86.zip - - # MPI is ON here so the analyzer sees detail/mpi/*: HybridComm.h includes - # unconditionally and every includer guards it behind #ifdef monoprop_ENABLE_MPI, so an - # MPI-off configure keeps that whole layer out of compile_commands.json and reports - # #ifdef'd-away members as smells (e.g. Exchange.h's Ticket losing its request_). - - name: Configure for SonarQube CFamily analysis - run: cmake --preset release-gcc-mpi - - - name: Build with Build Wrapper + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v9.0.0 + with: + enable-cache: true + python-version: "3.11" + + - name: Install package run: | - ./build-wrapper-linux-x86/build-wrapper-linux-x86-64 \ - --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} \ - cmake --build --preset release-gcc-mpi + uv sync --no-progress --all-extras -v - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@v8.2.1 @@ -353,140 +291,4 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} with: args: > - -Dsonar.cfamily.compile-commands=${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json - - cpp-checks-other: - # in combination with the PR types selection, this top-level if statement - # skips running the workflow for Draft PRs - if: ${{ github.event_name == 'push' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'test-in-draft') }} - - name: Check C++ on ${{ matrix.runner }} (${{ matrix.compiler }}) - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - # `preset` picks the compiler: the update-alternatives steps below point the plain - # g++/clang++ the presets name at the matrix's toolchain version. - include: - - runner: ubuntu-26.04-arm - compiler: gcc-14 - preset: release-gcc - - runner: ubuntu-26.04-arm - compiler: clang-18 - preset: release-clang - - runner: ubuntu-26.04 - compiler: clang-18 - preset: release-clang - - runner: macos-15 - compiler: apple-clang - preset: release-clang - - steps: - - uses: actions/checkout@v7.0.1 - with: - fetch-depth: 0 - - - name: Set gcc-14/g++-14 as default compilers - if: ${{ matrix.compiler == 'gcc-14' }} - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 140 - sudo update-alternatives --set gcc /usr/bin/gcc-14 - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 140 - sudo update-alternatives --set cc /usr/bin/gcc-14 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 140 - sudo update-alternatives --set g++ /usr/bin/g++-14 - sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-14 140 - sudo update-alternatives --set c++ /usr/bin/g++-14 - - - name: Set clang-18/clang++-18 as default compilers - if: ${{ matrix.compiler == 'clang-18' }} - run: | - if ! command -v clang-18 >/dev/null; then - sudo apt-get update - sudo apt-get install -y clang-18 - fi - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 180 - sudo update-alternatives --set clang /usr/bin/clang-18 - sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-18 180 - sudo update-alternatives --set cc /usr/bin/clang-18 - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 180 - sudo update-alternatives --set clang++ /usr/bin/clang++-18 - sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-18 180 - sudo update-alternatives --set c++ /usr/bin/clang++-18 - - - name: Install dependencies - if: ${{ matrix.runner != 'macos-15' }} - run: | - sudo apt-get update - sudo apt-get install ninja-build libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev - - - name: Install dependencies - if: ${{ matrix.runner == 'macos-15' }} - run: | - brew install ninja boost msgpack-cxx open-mpi - - - name: Configure - run: | - if [[ "${{ matrix.runner }}" == "macos-15" ]]; then - export MACOSX_DEPLOYMENT_TARGET=15.0 - fi - cmake --preset ${{ matrix.preset }} - - - name: Build - run: cmake --build --preset ${{ matrix.preset }} - - # --output-junit resolves relative to the test directory (build/), so ../../ puts the - # report at the workspace root where the Codecov step looks for it. - - name: Test - run: | - ctest --preset ${{ matrix.preset }} --output-junit ../../ctest-${{ matrix.runner }}-${{ matrix.compiler }}.xml - - - name: Upload test results to Codecov - uses: codecov/codecov-action@v7 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ctest-${{ matrix.runner }}-${{ matrix.compiler }}.xml - report_type: test_results - disable_search: true - fail_ci_if_error: true - - cpp-wide: - # in combination with the PR types selection, this top-level if statement - # skips running the workflow for Draft PRs - if: ${{ github.event_name == 'push' || !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'test-in-draft') }} - - name: Check C++ on ubuntu-26.04 (wide TermIndex) - runs-on: ubuntu-26.04 - - steps: - - uses: actions/checkout@v7.0.1 - with: - fetch-depth: 0 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y ninja-build libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev - - # Builds with a 64-bit TermIndex so the monoprop_WIDE_TERM_INDEX #if branches are actually - # compiled and run; serial coverage is enough to exercise them. No C++ test job here enables - # monoprop_ENABLE_MPI, so the mpi-labelled variants only run locally (`just test-cpp-mpi`); - # CI's MPI coverage comes from the Python suite under mpiexec in python-checks. - - name: Configure (wide TermIndex) - run: cmake --preset release-gcc-wide - - - name: Build - run: cmake --build --preset release-gcc-wide - - # --output-junit resolves relative to the test directory (build/release-gcc-wide). - - name: Test (serial) - run: ctest --preset release-gcc-wide -L serial --output-junit ../../ctest-ubuntu_26_04-wide.xml - - - name: Upload test results to Codecov - uses: codecov/codecov-action@v7 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ctest-ubuntu_26_04-wide.xml - report_type: test_results - disable_search: true - fail_ci_if_error: true + -Dsonar.cfamily.compile-commands=build/editable/Release/compile_commands.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a8b2aae..ac4719ba 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -181,9 +181,16 @@ ], "cmake.cmakePath": "cmake", "cmake.allowCommentsInPresetsFile": true, - "cmake.configurePreset": "release-gcc-mpi", - "cmake.buildPreset": "release-gcc-mpi", - "cmake.testPreset": "release-gcc-mpi", + // This project only configures correctly through scikit-build-core (invoked + // by `uv sync`), which injects the SKBUILD / Python / nanobind cache + // variables from an ephemeral build-isolation environment. Never let CMake + // Tools reconfigure the adopted build tree: the cached Python interpreter + // path is deleted after the build and a reconfigure would fail. + "cmake.configureOnEdit": false, + "cmake.configureOnOpen": false, + "cmake.automaticReconfigure": false, + "cmake.ctest.testExplorerIntegrationEnabled": true, + "cmake.buildBeforeRun": false, "cmake.options.statusBarVisibility": "hidden", "cmake.options.advanced": { "build": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 08707ba9..89af1ddf 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "label": "Install in Release mode", "type": "shell", - "command": "uv sync --group dev --group test --group interactive --all-extras -v", + "command": "uv sync --all-groups --all-extras -v", "problemMatcher": [], "group": { "kind": "build", @@ -16,7 +16,17 @@ { "label": "Install in Debug mode", "type": "shell", - "command": "uv sync --group dev --group test --group interactive --all-extras -v --config-settings=cmake.build-type='Debug'", + "command": "uv sync --all-groups --all-extras -v --config-settings-package='monoprop:cmake.build-type=Debug'", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": false + } + }, + { + "label": "Install in Coverage mode", + "type": "shell", + "command": "uv sync --all-groups --all-extras -v --config-settings-package='monoprop:cmake.build-type=Coverage'", "problemMatcher": [], "group": { "kind": "build", diff --git a/CMakeLists.txt b/CMakeLists.txt index 6caeeb48..0a461507 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,34 @@ cmake_minimum_required(VERSION 3.28...4.0) -include(cmake/CPM.cmake) - -if(SKBUILD) - # this value doesn't really matter, as it will be set by scikit-build-core - set(PROJECT_VERSION "0.1.0") -else() - cpmaddpackage("gh:LecrisUT/CMakeExtraUtils@0.4.1") - include(${CMakeExtraUtils_BINARY_DIR}/DynamicVersion.cmake) - dynamic_version(PROJECT_PREFIX monoprop_) +project(monoprop LANGUAGES CXX) + +# warn if the user invokes CMake directly +if(NOT SKBUILD) + message( + WARNING + "\ + This CMake file is meant to be executed using 'scikit-build-core'. + Running it directly will almost certainly not produce the desired + result. If you are a user trying to install this package, use the + command below, which will install all necessary build dependencies, + compile the package in an isolated environment, and then install it. + ===================================================================== + $ pip install . + ===================================================================== + If you are a software developer, and this is your own package, then + it is usually much more efficient to install the build dependencies + in your environment once and use the following command that avoids + a costly creation of a new virtual environment at every compilation: + ===================================================================== + $ pip install nanobind scikit-build-core + $ pip install --no-build-isolation -ve . + ===================================================================== + You may optionally add -Ceditable.rebuild=true to auto-rebuild when + the package is imported. Otherwise, you need to rerun the above + after editing C++ files." + ) endif() -project(monoprop LANGUAGES CXX VERSION ${PROJECT_VERSION}) - if(APPLE) set(THREADS_FOUND TRUE CACHE BOOL "") set(CMAKE_THREAD_LIBS_INIT "-lpthread" CACHE STRING "") @@ -38,9 +54,19 @@ option( "Use 64-bit term indices (support > 2^32 terms per partition)" OFF ) -if(monoprop_WIDE_TERM_INDEX) - add_compile_definitions(monoprop_WIDE_TERM_INDEX) -endif() + +option(monoprop_ENABLE_CXX_UNIT_TESTS "Enable C++ unit test suite" ON) + +set(Python_FIND_VIRTUALENV FIRST) +find_package( + Python + 3.11 + REQUIRED + COMPONENTS + Interpreter + Development.Module + ${SKBUILD_SABI_COMPONENT} +) find_package(Threads REQUIRED) @@ -50,11 +76,6 @@ endif() include(${PROJECT_SOURCE_DIR}/cmake/compiler_flags/CXXFlags.cmake) -# PROJECT_VERSION_FULL comes from dynamic_version(), which is skipped under scikit-build-core. -if(NOT DEFINED SKBUILD) - message(STATUS "${PROJECT_NAME} version: ${PROJECT_VERSION_FULL}") -endif() - # report on compiler flags in use message(STATUS "Configuring a ${CMAKE_BUILD_TYPE} build") string(TOUPPER ${CMAKE_BUILD_TYPE} _cmake_build_type_upper) @@ -75,9 +96,11 @@ message( " Project defaults : ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} ${monoprop_CXX_FLAGS}" ) message(STATUS " User-appended : ${EXTRA_CXXFLAGS}") + message(STATUS " MPI parallelization : ${monoprop_ENABLE_MPI}") message(STATUS " Wide term index : ${monoprop_WIDE_TERM_INDEX}") message(STATUS " Max simulable modes : ${monoprop_MAX_NUM_MODES}") +message(STATUS " C++ unit tests : ${monoprop_ENABLE_CXX_UNIT_TESTS}") include(GNUInstallDirs) @@ -91,7 +114,7 @@ configure_file( add_subdirectory(src) -if(NOT DEFINED SKBUILD) +if(monoprop_ENABLE_CXX_UNIT_TESTS) enable_testing() include(CTest) diff --git a/CMakePresets.json b/CMakePresets.json index 617dca6b..115f246e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -2,206 +2,62 @@ "version": 8, "configurePresets": [ { - "name": "base", - "hidden": true, - "description": "Shared layout for every preset: Ninja into build/. CMAKE_EXPORT_COMPILE_COMMANDS is set by cmake/compiler_flags/CXXFlags.cmake, so it is not repeated here.", - "generator": "Ninja", - "binaryDir": "${sourceDir}/build/${presetName}" - }, - { - "name": "release-gcc", - "inherits": "base", - "displayName": "Release - Configure with GCC Compiler Kit", - "description": "Configure project with Release flags using g++", - "cacheVariables": { - "CMAKE_CXX_COMPILER": "g++", - "CMAKE_BUILD_TYPE": "Release" - } - }, - { - "name": "release-gcc-mpi", - "inherits": "release-gcc", - "displayName": "Release - Configure with GCC Compiler Kit (MPI)", - "description": "Configure Release build with g++ and monoprop_ENABLE_MPI=ON", - "cacheVariables": { - "monoprop_ENABLE_MPI": "ON" - } - }, - { - "name": "release-gcc-wide", - "inherits": "release-gcc", - "displayName": "Release - Configure with GCC Compiler Kit (wide TermIndex)", - "description": "Configure Release build with g++ and monoprop_WIDE_TERM_INDEX=ON: the only configuration that compiles the 64-bit TermIndex branches", - "cacheVariables": { - "monoprop_WIDE_TERM_INDEX": "ON" - } - }, - { - "name": "release-clang", - "inherits": "base", - "displayName": "Release - Configure with Clang Compiler Kit", - "description": "Configure project with Release flags using clang++ (also covers Apple clang)", - "cacheVariables": { - "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_BUILD_TYPE": "Release" - } - }, - { - "name": "debug-gcc", - "inherits": "base", - "displayName": "Debug - Configure with GCC Compiler Kit", - "description": "Configure project with Debug flags using g++", - "cacheVariables": { - "CMAKE_CXX_COMPILER": "g++", - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "debug-gcc-mpi", - "inherits": "debug-gcc", - "displayName": "Debug - Configure with GCC Compiler Kit (MPI)", - "description": "Configure Debug build with g++ and monoprop_ENABLE_MPI=ON", - "cacheVariables": { - "monoprop_ENABLE_MPI": "ON" - } - }, - { - "name": "coverage-gcc", - "inherits": "base", - "displayName": "Coverage - Configure with GCC Compiler Kit", - "description": "Configure project with Coverage flags using g++", - "cacheVariables": { - "CMAKE_CXX_COMPILER": "g++", - "CMAKE_BUILD_TYPE": "Coverage" - } + "name": "skbuild-release", + "displayName": "scikit-build-core (Release, adopt existing tree)", + "description": "Adopt the Release build tree generated by 'uv sync' / scikit-build-core. This project only configures correctly through scikit-build-core (which injects SKBUILD, Python and nanobind cache variables from an ephemeral build-isolation environment). Do NOT reconfigure this preset from CMake Tools; run 'uv sync --all-extras' to (re)generate the tree, then use this preset only to build and run the C++ CTest suite.", + "binaryDir": "${sourceDir}/build/editable/Release", + "generator": "Ninja" + }, + { + "name": "skbuild-debug", + "displayName": "scikit-build-core (Debug, adopt existing tree)", + "description": "Adopt the Debug build tree generated by 'uv sync --config-settings=cmake.build-type=Debug' (the 'Install in Debug mode' task). Do NOT reconfigure this preset from CMake Tools; regenerate the tree with uv/scikit-build-core, then use this preset only to build and run the C++ CTest suite.", + "binaryDir": "${sourceDir}/build/editable/Debug", + "generator": "Ninja" } ], "buildPresets": [ { - "name": "build-base", - "hidden": true, - "jobs": 4, - "verbose": true, - "inheritConfigureEnvironment": true - }, - { - "name": "release-gcc", - "inherits": "build-base", - "displayName": "Release - Build with GCC Compiler Kit", - "description": "Build project with Release flags using g++", - "configurePreset": "release-gcc" - }, - { - "name": "release-gcc-mpi", - "inherits": "build-base", - "displayName": "Release - Build with GCC Compiler Kit (MPI)", - "description": "Build Release project with g++ and MPI enabled", - "configurePreset": "release-gcc-mpi" - }, - { - "name": "release-gcc-wide", - "inherits": "build-base", - "displayName": "Release - Build with GCC Compiler Kit (wide TermIndex)", - "description": "Build Release project with g++ and a 64-bit TermIndex", - "configurePreset": "release-gcc-wide" - }, - { - "name": "release-clang", - "inherits": "build-base", - "displayName": "Release - Build with Clang Compiler Kit", - "description": "Build project with Release flags using clang++", - "configurePreset": "release-clang" - }, - { - "name": "debug-gcc", - "inherits": "build-base", - "displayName": "Debug - Build with GCC Compiler Kit", - "description": "Build project with Debug flags using g++", - "configurePreset": "debug-gcc" - }, - { - "name": "debug-gcc-mpi", - "inherits": "build-base", - "displayName": "Debug - Build with GCC Compiler Kit (MPI)", - "description": "Build Debug project with g++ and MPI enabled", - "configurePreset": "debug-gcc-mpi" - }, - { - "name": "coverage-gcc", - "inherits": "build-base", - "displayName": "Coverage - Build with GCC Compiler Kit", - "description": "Build project with Coverage flags using g++", - "configurePreset": "coverage-gcc" + "name": "skbuild-release", + "displayName": "scikit-build-core (Release) - Build C++ unit tests", + "description": "Build the C++ unit test executable in the scikit-build-core Release tree. Requires the tree to already exist (run 'uv sync --all-extras').", + "configurePreset": "skbuild-release", + "targets": [ + "monoprop_unit_tests.x" + ], + "jobs": 4 + }, + { + "name": "skbuild-debug", + "displayName": "scikit-build-core (Debug) - Build C++ unit tests", + "description": "Build the C++ unit test executable in the scikit-build-core Debug tree. Requires the tree to already exist (run the 'Install in Debug mode' task).", + "configurePreset": "skbuild-debug", + "targets": [ + "monoprop_unit_tests.x" + ], + "jobs": 4 } ], "testPresets": [ { - "name": "test-base", - "hidden": true, + "name": "skbuild-release", + "displayName": "scikit-build-core (Release) - Run C++ unit tests", + "description": "Run the C++ CTest suite from the scikit-build-core Release tree.", + "configurePreset": "skbuild-release", "output": { "outputOnFailure": true }, "inheritConfigureEnvironment": true }, { - "name": "test-mpi-base", - "hidden": true, - "inherits": "test-base", - "description": "Environment the mpi-labelled variants need: run-as-root consent and oversubscription, so a 4-rank variant still runs on a 2-core box.", - "environment": { - "OMPI_ALLOW_RUN_AS_ROOT": "1", - "OMPI_ALLOW_RUN_AS_ROOT_CONFIRM": "1", - "OMPI_MCA_rmaps_base_oversubscribe": "1" - } - }, - { - "name": "release-gcc", - "inherits": "test-base", - "displayName": "Test - Release configuration with GCC Compiler Kit", - "description": "Test project built with Release flags using g++", - "configurePreset": "release-gcc" - }, - { - "name": "release-gcc-mpi", - "inherits": "test-mpi-base", - "displayName": "Test - Release configuration with GCC Compiler Kit (MPI)", - "description": "Test Release build with g++ and MPI enabled", - "configurePreset": "release-gcc-mpi" - }, - { - "name": "release-gcc-wide", - "inherits": "test-base", - "displayName": "Test - Release configuration with GCC Compiler Kit (wide TermIndex)", - "description": "Test Release build with g++ and a 64-bit TermIndex", - "configurePreset": "release-gcc-wide" - }, - { - "name": "release-clang", - "inherits": "test-base", - "displayName": "Test - Release configuration with Clang Compiler Kit", - "description": "Test project built with Release flags using clang++", - "configurePreset": "release-clang" - }, - { - "name": "debug-gcc", - "inherits": "test-base", - "displayName": "Test - Debug configuration with GCC Compiler Kit", - "description": "Test project built with Debug flags using g++", - "configurePreset": "debug-gcc" - }, - { - "name": "debug-gcc-mpi", - "inherits": "test-mpi-base", - "displayName": "Test - Debug configuration with GCC Compiler Kit (MPI)", - "description": "Test Debug build with g++ and MPI enabled", - "configurePreset": "debug-gcc-mpi" - }, - { - "name": "coverage-gcc", - "inherits": "test-base", - "displayName": "Test - Coverage configuration with GCC Compiler Kit", - "description": "Test project built with Coverage flags using g++", - "configurePreset": "coverage-gcc" + "name": "skbuild-debug", + "displayName": "scikit-build-core (Debug) - Run C++ unit tests", + "description": "Run the C++ CTest suite from the scikit-build-core Debug tree.", + "configurePreset": "skbuild-debug", + "output": { + "outputOnFailure": true + }, + "inheritConfigureEnvironment": true } ] } diff --git a/docs/content/docs/testing.mdx b/docs/content/docs/testing.mdx index 1707fffb..3a287d3c 100644 --- a/docs/content/docs/testing.mdx +++ b/docs/content/docs/testing.mdx @@ -15,7 +15,7 @@ the tools needed to run the tests. The Python tests run with `pytest`. Without MPI: ```bash -uv run python -m pytest -m "not mpi" # or: just test-py +uv run python -m pytest -m "not mpi" # or: just test ``` The MPI tests need an MPI-enabled build, which the default source build does not @@ -23,8 +23,8 @@ produce. The `just` recipes build an MPI-enabled extension and launch the suite under `mpiexec`: ```bash -just test-py-mpi # full suite under MPI -just test-py-mpi-matrix # MPI-marked tests across a rank matrix +just test-mpi # full suite under MPI +just test-mpi-matrix # MPI-marked tests across a rank matrix ``` To do it by hand, build with MPI on, then run under `mpiexec` with `--no-sync` @@ -39,22 +39,27 @@ mpiexec --allow-run-as-root -n 2 uv run --no-sync python -m pytest tests --with- ### C++ unit tests -The C++ tests run through CTest, against whichever preset you built. Without MPI: +The C++ tests run through CTest. The build tree is produced by `uv sync` (via +scikit-build-core); do not invoke `cmake --preset` to configure directly, as the +project requires scikit-build-core's build environment to configure correctly. +Once the tree exists, run the tests with: ```bash -cmake --preset release-gcc -cmake --build --preset release-gcc -ctest --preset release-gcc +uv sync --all-extras +ctest --test-dir build/editable/Release --output-on-failure ``` -With MPI, use the `-mpi` preset, which also runs the multi-rank tests: +With MPI, reuse the MPI-enabled `uv sync` from the Python MPI section above, +then run CTest against the same tree: ```bash -cmake --preset release-gcc-mpi -cmake --build --preset release-gcc-mpi -ctest --preset release-gcc-mpi +SKBUILD_CMAKE_ARGS="-Dmonoprop_ENABLE_MPI=ON" \ + uv sync --all-extras --reinstall-package monoprop --no-cache +ctest --test-dir build/editable/Release --output-on-failure ``` +Or simply `just test-mpi`. + The full MPI rank matrix (several rank counts) is driven by a helper script: ```bash @@ -62,10 +67,10 @@ ctest -S tools/ctest-mpi-matrix.cmake -VV ``` A 64-bit `TermIndex` build is the only configuration that compiles the -`monoprop_WIDE_TERM_INDEX` branches, so it has its own preset and recipe: +`monoprop_WIDE_TERM_INDEX` branches, so it has its own recipe: ```bash -just test-cpp-wide # cmake/build/ctest with the release-gcc-wide preset, -L serial +just test-wide # rebuilds via uv sync with monoprop_WIDE_TERM_INDEX=ON, then runs CTest ``` CTest registers every Boost case individually as a `serial` variant. When the @@ -73,7 +78,7 @@ build has MPI enabled and a launcher is found, it also registers the whole suite once per rank count in `monoprop_MPI_TEST_PROCS`, labelled `mpi` and `mpi-` — one entry per rank count rather than per case, because the ranks have to reach the same collectives. Select either group with -`ctest --preset release-gcc-mpi -L serial` (or `-L mpi-2`). +`ctest --test-dir build/editable/Release -L serial` (or `-L mpi-2`). ## Adding tests diff --git a/include/monoprop/Info.h.in b/include/monoprop/Info.h.in index 21e6ee9e..8c50e5c6 100644 --- a/include/monoprop/Info.h.in +++ b/include/monoprop/Info.h.in @@ -19,14 +19,6 @@ #include namespace monoprop { -static constexpr auto version() noexcept -> std::string_view { - return "@PROJECT_VERSION_FULL@"; -} - -static constexpr auto git_commit() noexcept -> std::string_view { - return "@GIT_COMMIT@"; -} - static constexpr auto build_type() noexcept -> std::string_view { return "@CMAKE_BUILD_TYPE@"; } diff --git a/justfile b/justfile index b128e446..0120d0e8 100644 --- a/justfile +++ b/justfile @@ -29,54 +29,90 @@ fumapy := "--with " + site + "/node_modules/fumadocs-python --with 'griffe<2' -- default: build-docs -test-py: +test: uv run python -m pytest -m "not mpi" + ctest --test-dir build/editable/Release --output-on-failure # MPI is off by default in source builds, so build an MPI-enabled editable install - # first, then run the suite under mpiexec with --no-sync (avoids a per-rank resync). -test-py-mpi: - uv sync --all-extras --group test --reinstall-package monoprop --no-cache --config-settings-package="monoprop:cmake.define.monoprop_ENABLE_MPI=ON" -v; \ - ranks="${monoprop_MPI_TEST_PROCS:-2}"; \ +# Pass RANKS as either a single integer or a semicolon-separated list (e.g. "1;2;4"). + +test-mpi RANKS='': + export OMPI_MCA_rmaps_base_oversubscribe="1" + uv sync --all-extras --group test --reinstall-package monoprop --no-cache --config-settings-package="monoprop:cmake.define.monoprop_ENABLE_MPI=ON" -v + ranks="${1:-${monoprop_MPI_TEST_PROCS:-2}}" for r in ${ranks//;/ }; \ do echo "Running full Python test suite with ${r} MPI rank(s)"; \ - mpiexec --allow-run-as-root -n "$r" uv run --no-sync python -m pytest tests --with-mpi -v; \ + mpiexec -n "$r" uv run --no-sync python -m pytest tests --with-mpi -v; \ + echo "Running C++ unit tests with ${r} MPI rank(s)"; \ + ctest --test-dir build/editable/Release --output-on-failure; \ done -test-py-mpi-matrix: - uv sync --all-extras --group test --reinstall-package monoprop --no-cache --config-settings-package="monoprop:cmake.define.monoprop_ENABLE_MPI=ON" -v; \ - ranks="${monoprop_MPI_TEST_PROCS:-1;2;4}"; for r in ${ranks//;/ }; do echo "Running MPI-marked Python tests with ${r} rank(s)"; mpiexec --allow-run-as-root -n "$r" uv run --no-sync python -m pytest tests --with-mpi -m mpi -v; done - # Build and run the C++ suite with a 64-bit TermIndex (monoprop_WIDE_TERM_INDEX=ON). # This is the only configuration that compiles the wide `#if defined(monoprop_WIDE_TERM_INDEX)` # branches (operator_index_tests, large_cosine_storage_tests, graph_encoding_tests), so it - # guards them from bit-rotting. Serial is enough to exercise those branches. -test-cpp-wide: - cmake --preset release-gcc-wide - cmake --build --preset release-gcc-wide - ctest --preset release-gcc-wide -L serial - -# Build and run the C++ suite with MPI enabled. `monoprop_MPI_TEST_PROCS` (default 2, a - -# semicolon list) picks the rank counts the mpi-labelled tests are registered for. -test-cpp-mpi: - cmake --preset release-gcc-mpi - cmake --build --preset release-gcc-mpi - ctest --preset release-gcc-mpi - -# Run the C++ suite under coverage and report with gcovr. - -# Approximates the CI cpp-checks gcovr invocation; set $GCOV to pick another gcov binary. -coverage-cpp: - cmake --preset coverage-gcc - cmake --build --preset coverage-gcc - ctest --preset coverage-gcc - uvx gcovr --gcov-executable "${GCOV:-gcov}" --gcov-ignore-parse-errors \ - --exclude-throw-branches --exclude-unreachable-branches \ - --root . --filter '^(src|include)/' --exclude '^tests/' build/coverage-gcc --txt + +test-wide: + uv sync --all-extras --group test --reinstall-package monoprop --no-cache --config-settings-package="monoprop:cmake.define.monoprop_WIDE_TERM_INDEX=ON" + uv run --no-sync python -m pytest -m "not mpi" + ctest --test-dir build/editable/Release --output-on-failure + +# Report code coverage. + +code-coverage: + uv sync --no-progress --group test --all-extras --reinstall-package monoprop --no-cache --config-settings-package="monoprop:cmake.build-type=Coverage" -v + + # run python tests + uv run --no-sync python -m pytest --cov=src/monoprop --cov-report=lcov:python-coverage.info + + # coverage.py emits repo-relative SF: paths while gcovr emits absolute ones; + # genhtml's common-prefix stripping would abort with "duplicate merge record" without this step + sed -i 's|^SF:\([^/]\)|SF:{{ project_source_dir }}/\1|' python-coverage.info + + # collect coverage (C++ through Python bindings) + uvx gcovr \ + --gcov-executable gcov \ + --gcov-ignore-parse-errors \ + --exclude-throw-branches \ + --exclude-unreachable-branches \ + --filter '^(src|include)/' \ + --exclude '^tests/' \ + --merge-lines \ + "build/editable/Coverage" \ + --lcov cpp-coverage-through-python-bindings.info + + # run C++ unit tests + ctest --test-dir build/editable/Coverage --output-on-failure + + # collect coverage (C++ unit tests) + uvx gcovr \ + --gcov-executable gcov \ + --gcov-ignore-parse-errors \ + --exclude-throw-branches \ + --exclude-unreachable-branches \ + --filter '^(src|include)/' \ + --exclude '^tests/' \ + --merge-lines \ + "build/editable/Coverage" \ + --lcov cpp-coverage.info + + # merge + lcov \ + --ignore-errors inconsistent,corrupt \ + -a python-coverage.info \ + -a cpp-coverage-through-python-bindings.info \ + -a cpp-coverage.info \ + -o merged.info + + # output to HTML + genhtml merged.info -o monoprop-coverage --legend --title "monoprop coverage" \ + --prefix {{ project_source_dir }} \ + --ignore-errors inconsistent \ + --verbose # Install the documentation site's JavaScript dependencies. + docs-install: cd {{ site }} && npm ci diff --git a/pyproject.toml b/pyproject.toml index 1593c7b0..00019efd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ mpi = ["mpi4py>=4.1.0"] qiskit = ["qiskit>=2.0.0"] [dependency-groups] -dev = ["prek>=0.4", "ruff==0.15.20", "gcovr"] +dev = ["prek>=0.4", "ruff==0.15.20"] # The documentation site is built with Fumadocs (Next.js); see `docs/`. # Python's only roles are converting the tutorial notebooks to Markdown # (`nbconvert`) and generating the API reference from docstrings (`griffe`, @@ -350,11 +350,11 @@ test-extras = ["qiskit", "openfermion", "pyscf"] [tool.cibuildwheel.linux] before-all = ["./tools/install-deps.sh --skip-boost-test --skip-msgpack"] -environment = { SKBUILD_CMAKE_ARGS = "-Dmonoprop_ENABLE_ARCH_FLAGS=OFF;-Dmonoprop_ENABLE_MPI=OFF;-DCPM_USE_LOCAL_PACKAGES=TRUE" } +environment = { SKBUILD_CMAKE_ARGS = "-Dmonoprop_ENABLE_ARCH_FLAGS=OFF;-Dmonoprop_ENABLE_CXX_UNIT_TESTS=OFF;-Dmonoprop_ENABLE_MPI=OFF;-DCPM_USE_LOCAL_PACKAGES=TRUE" } [tool.cibuildwheel.macos] before-all = "brew install boost" -environment = { MACOSX_DEPLOYMENT_TARGET = "15.0", SKBUILD_CMAKE_ARGS = "-Dmonoprop_ENABLE_ARCH_FLAGS=OFF;-Dmonoprop_ENABLE_MPI=OFF;-DCPM_USE_LOCAL_PACKAGES=TRUE" } +environment = { MACOSX_DEPLOYMENT_TARGET = "15.0", SKBUILD_CMAKE_ARGS = "-Dmonoprop_ENABLE_ARCH_FLAGS=OFF;-Dmonoprop_ENABLE_CXX_UNIT_TESTS=OFF;-Dmonoprop_ENABLE_MPI=OFF;-DCPM_USE_LOCAL_PACKAGES=TRUE" } [[tool.cibuildwheel.overrides]] # override on x86_64 to keep the gcc-toolset runtime and /usr/local pkg-config paths visible diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed4fa62e..0cb6381c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,7 +20,7 @@ add_subdirectory(monoprop) include(CMakePackageConfigHelpers) -set(monoprop_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) +set(monoprop_INSTALL_CMAKEDIR ${PROJECT_NAME}/cmake) configure_package_config_file( ${PROJECT_SOURCE_DIR}/cmake/monopropConfig.cmake.in @@ -30,7 +30,7 @@ configure_package_config_file( write_basic_package_version_file( ${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - VERSION ${PROJECT_VERSION} + VERSION ${SKBUILD_PROJECT_VERSION_FULL} COMPATIBILITY SameMajorVersion ) diff --git a/src/monoprop/CMakeLists.txt b/src/monoprop/CMakeLists.txt index 111543dd..c048e24e 100644 --- a/src/monoprop/CMakeLists.txt +++ b/src/monoprop/CMakeLists.txt @@ -2,127 +2,127 @@ find_package(Boost 1.85 CONFIG REQUIRED) message(STATUS "Using Boost: ${Boost_DIR} (version ${Boost_VERSION})") add_library( - monoprop - SHARED + monoprop-objs + OBJECT Evolution.cpp MPFunctions.cpp - detail/pare/PareGraph.cpp MPGraph.cpp Validation.cpp ) -include(GenerateExportHeader) - -generate_export_header( - monoprop - BASE_NAME "${PROJECT_NAME}" - EXPORT_FILE_NAME - "${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}Export.h" - EXPORT_MACRO_NAME "${PROJECT_NAME}_EXPORT" - DEPRECATED_MACRO_NAME "${PROJECT_NAME}_DEPRECATED" - NO_EXPORT_MACRO_NAME "${PROJECT_NAME}_NO_EXPORT" - STATIC_DEFINE "${PROJECT_NAME}_STATIC_DEFINE" - NO_DEPRECATED_MACRO_NAME "${PROJECT_NAME}_NO_DEPRECATED" - DEFINE_NO_DEPRECATED +target_compile_definitions( + monoprop-objs + PUBLIC + $<$:monoprop_ENABLE_MPI> + $<$:monoprop_WIDE_TERM_INDEX> ) -file( - GLOB_RECURSE monoprop_PUBLIC_HEADERS - CONFIGURE_DEPENDS - "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/*.h" - "${PROJECT_SOURCE_DIR}/src/${PROJECT_NAME}/*.h" - "${PROJECT_SOURCE_DIR}/src/${PROJECT_NAME}/detail/*.h" - "${PROJECT_SOURCE_DIR}/src/${PROJECT_NAME}/detail/**/*.h" +# flags to prepend +target_compile_options( + monoprop-objs + BEFORE + PUBLIC + "${monoprop_CXX_FLAGS}" + "${ARCH_FLAG}" ) -list( - APPEND monoprop_PUBLIC_HEADERS - ${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/Info.h - ${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}Export.h +# flags to append +target_compile_options(monoprop-objs PUBLIC "${EXTRA_CXXFLAGS}") + +target_include_directories( + monoprop-objs + PUBLIC + $ + $ + $ ) -set(monoprop_PRIVATE_HEADERS) +target_link_libraries( + monoprop-objs + PUBLIC + Boost::boost + Threads::Threads + $<$:MPI::MPI_CXX> +) set_target_properties( - monoprop + monoprop-objs PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES - PUBLIC_HEADER - "${monoprop_PUBLIC_HEADERS}" - PRIVATE_HEADER - "${monoprop_PRIVATE_HEADERS}" ) -target_include_directories( +include(GenerateExportHeader) + +generate_export_header( + monoprop-objs + BASE_NAME "${PROJECT_NAME}" + EXPORT_FILE_NAME + "${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}Export.h" + EXPORT_MACRO_NAME "${PROJECT_NAME}_EXPORT" + DEPRECATED_MACRO_NAME "${PROJECT_NAME}_DEPRECATED" + NO_EXPORT_MACRO_NAME "${PROJECT_NAME}_NO_EXPORT" + STATIC_DEFINE "${PROJECT_NAME}_STATIC_DEFINE" + NO_DEPRECATED_MACRO_NAME "${PROJECT_NAME}_NO_DEPRECATED" + DEFINE_NO_DEPRECATED +) + +add_library(monoprop SHARED $) + +target_compile_definitions( monoprop - PUBLIC - $ - $ - $ - $ + INTERFACE + $<$:monoprop_ENABLE_MPI> + $<$:monoprop_WIDE_TERM_INDEX> ) target_link_libraries( monoprop - PUBLIC + INTERFACE Boost::boost Threads::Threads $<$:MPI::MPI_CXX> ) -target_compile_definitions( +target_sources( monoprop PUBLIC - $<$:monoprop_ENABLE_MPI> + FILE_SET headers + TYPE HEADERS + BASE_DIRS + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/include + ${PROJECT_SOURCE_DIR}/src + FILES + "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/Evolution.h" + "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/MPFunctions.h" + "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/MPGraph.h" + "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/MonomialPropagator.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Bitset.h" + "${CMAKE_CURRENT_SOURCE_DIR}/MPGraphEncoding.h" + "${CMAKE_CURRENT_SOURCE_DIR}/TypeAliases.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Utilities.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Validation.h" + "${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/${PROJECT_NAME}Export.h" + "${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}/Info.h" ) -# flags to prepend -target_compile_options( - monoprop - BEFORE - PRIVATE - "${monoprop_CXX_FLAGS}" - "${ARCH_FLAG}" -) - -# flags to append -target_compile_options(monoprop PRIVATE "${EXTRA_CXXFLAGS}") +add_subdirectory(algebra) +add_subdirectory(core) +add_subdirectory(detail) install( TARGETS monoprop EXPORT monopropTargets LIBRARY - DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE - DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME - DESTINATION ${CMAKE_INSTALL_BINDIR} - PUBLIC_HEADER - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - PRIVATE_HEADER - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/monoprop/private -) - -install( - DIRECTORY - ${PROJECT_SOURCE_DIR}/include/monoprop - ${PROJECT_SOURCE_DIR}/src/monoprop - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.h" -) - -install( - FILES - ${PROJECT_BINARY_DIR}/include/monoprop/Info.h - ${PROJECT_BINARY_DIR}/include/monoprop/monopropExport.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/monoprop + DESTINATION ${PROJECT_NAME}/${CMAKE_INSTALL_LIBDIR} + COMPONENT lib + FILE_SET headers + DESTINATION ${PROJECT_NAME}/${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT dev ) -if(DEFINED SKBUILD) - add_subdirectory(bindings) -endif() +add_subdirectory(bindings) diff --git a/src/monoprop/algebra/CMakeLists.txt b/src/monoprop/algebra/CMakeLists.txt new file mode 100644 index 00000000..ddc05bd5 --- /dev/null +++ b/src/monoprop/algebra/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "Algebra.h" + "AlgebraCommon.h" + "MajoranaAlgebra.h" + "PauliAlgebra.h" +) diff --git a/src/monoprop/bindings/CMakeLists.txt b/src/monoprop/bindings/CMakeLists.txt index b724c511..e8f2a4c1 100644 --- a/src/monoprop/bindings/CMakeLists.txt +++ b/src/monoprop/bindings/CMakeLists.txt @@ -1,14 +1,3 @@ -set(Python_FIND_VIRTUALENV FIRST) -find_package( - Python - 3.11 - REQUIRED - COMPONENTS - Interpreter - Development.Module - ${SKBUILD_SABI_COMPONENT} -) - if(monoprop_ENABLE_MPI) include(${PROJECT_SOURCE_DIR}/cmake/custom/FindPythonModule.cmake) find_python_module(mpi4py REQUIRED) @@ -132,44 +121,7 @@ target_include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries(_core PRIVATE monoprop) - -# The hot MonomialPropagator kernels are header templates instantiated in this -# target's TUs, so ARCH_FLAG must be applied here too: on the library alone it never -# reached them and they compiled to scalar x86-64. -target_compile_options(_core BEFORE PRIVATE "${ARCH_FLAG}") - -file( - RELATIVE_PATH - _rel - ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR} - ${CMAKE_INSTALL_PREFIX} -) -if(APPLE) - set(_rpath "@loader_path/${_rel}") -else() - set(_rpath "\$ORIGIN/${_rel}") -endif() -file( - TO_NATIVE_PATH - "${_rpath}/${CMAKE_INSTALL_LIBDIR}" - monoprop_RPATH -) - -set_target_properties( - _core - PROPERTIES - MACOSX_RPATH - ON - SKIP_BUILD_RPATH - OFF - BUILD_WITH_INSTALL_RPATH - OFF - INSTALL_RPATH - "${monoprop_RPATH}" - INSTALL_RPATH_USE_LINK_PATH - ON -) +target_link_libraries(_core PRIVATE monoprop-objs) # generate dispatch function module install( diff --git a/src/monoprop/core/CMakeLists.txt b/src/monoprop/core/CMakeLists.txt new file mode 100644 index 00000000..d9faa730 --- /dev/null +++ b/src/monoprop/core/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "Monomial.h" +) diff --git a/src/monoprop/detail/CMakeLists.txt b/src/monoprop/detail/CMakeLists.txt new file mode 100644 index 00000000..f75f51c5 --- /dev/null +++ b/src/monoprop/detail/CMakeLists.txt @@ -0,0 +1,17 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "EnvConfig.h" +) + +add_subdirectory(evolution) +add_subdirectory(graph) +add_subdirectory(graph_encoding) +add_subdirectory(monomial_propagator) +add_subdirectory(mpi) +add_subdirectory(operator) +add_subdirectory(pare) +add_subdirectory(partition) diff --git a/src/monoprop/detail/evolution/CMakeLists.txt b/src/monoprop/detail/evolution/CMakeLists.txt new file mode 100644 index 00000000..00971ef2 --- /dev/null +++ b/src/monoprop/detail/evolution/CMakeLists.txt @@ -0,0 +1,13 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "CosineRecompute.h" + "CosineRecomputeCallbacks.h" + "EvolutionHelpers.h" + "LayerBuilder.h" +) + +add_subdirectory(layer_build) diff --git a/src/monoprop/detail/evolution/layer_build/CMakeLists.txt b/src/monoprop/detail/evolution/layer_build/CMakeLists.txt new file mode 100644 index 00000000..eb4c9853 --- /dev/null +++ b/src/monoprop/detail/evolution/layer_build/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "Common.h" + "Engine.h" + "FusedApply.h" + "Resolve.h" + "Scan.h" +) diff --git a/src/monoprop/detail/graph/CMakeLists.txt b/src/monoprop/detail/graph/CMakeLists.txt new file mode 100644 index 00000000..ace02b1f --- /dev/null +++ b/src/monoprop/detail/graph/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "MPGraphLayers.h" + "MPGraphViews.h" +) diff --git a/src/monoprop/detail/graph_encoding/CMakeLists.txt b/src/monoprop/detail/graph_encoding/CMakeLists.txt new file mode 100644 index 00000000..378759f9 --- /dev/null +++ b/src/monoprop/detail/graph_encoding/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "MPGraphEncodingStorage.h" + "MPGraphEncodingTypes.h" +) diff --git a/src/monoprop/detail/monomial_propagator/CMakeLists.txt b/src/monoprop/detail/monomial_propagator/CMakeLists.txt new file mode 100644 index 00000000..ded6cbbe --- /dev/null +++ b/src/monoprop/detail/monomial_propagator/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "MonomialPropagatorHelpers.h" + "MonomialPropagatorCommon.h" + "MonomialPropagatorImpl.h" +) diff --git a/src/monoprop/detail/mpi/CMakeLists.txt b/src/monoprop/detail/mpi/CMakeLists.txt new file mode 100644 index 00000000..b4f5a136 --- /dev/null +++ b/src/monoprop/detail/mpi/CMakeLists.txt @@ -0,0 +1,17 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "CheckedCount.h" + "Comm.h" + "CpuRelax.h" + "Exchange.h" + "HybridComm.h" + "MPICompat.h" + "MPIUtils.h" + "PartitionBarrier.h" + "RecvLayout.h" + "ShmComm.h" +) diff --git a/src/monoprop/detail/operator/CMakeLists.txt b/src/monoprop/detail/operator/CMakeLists.txt new file mode 100644 index 00000000..223511d7 --- /dev/null +++ b/src/monoprop/detail/operator/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "InvertedIndex.h" + "MPOperator.h" + "OperatorIndex.h" +) diff --git a/src/monoprop/detail/pare/CMakeLists.txt b/src/monoprop/detail/pare/CMakeLists.txt new file mode 100644 index 00000000..a3233909 --- /dev/null +++ b/src/monoprop/detail/pare/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "PareGraph.h" +) + +target_sources(monoprop-objs PRIVATE PareGraph.cpp) diff --git a/src/monoprop/detail/partition/CMakeLists.txt b/src/monoprop/detail/partition/CMakeLists.txt new file mode 100644 index 00000000..d50fcfbb --- /dev/null +++ b/src/monoprop/detail/partition/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources( + monoprop + PUBLIC + FILE_SET headers + TYPE HEADERS + FILES + "CpuTopology.h" + "PartitionGroup.h" +) diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index 2513ffe7..d91ac792 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -1,14 +1,14 @@ -# -# Compile monoprop C++ test suite -# find_package(Boost 1.85 COMPONENTS unit_test_framework REQUIRED) +include(${PROJECT_SOURCE_DIR}/cmake/CPM.cmake) + cpmaddpackage( NAME "msgpack-cxx" - VERSION "7.0.0" GIT_REPOSITORY "https://github.com/msgpack/msgpack-c" GIT_TAG "cpp-7.0.0" OPTIONS "MSGPACK_USE_BOOST OFF" "MSGPACK_CXX20 ON" + SYSTEM YES + EXCLUDE_FROM_ALL YES ) file( @@ -30,7 +30,7 @@ target_compile_definitions( target_link_libraries( monoprop_unit_tests.x PRIVATE - monoprop + monoprop-objs Boost::unit_test_framework msgpack-cxx ) diff --git a/tests/cpp/boostAddTests.cmake b/tests/cpp/boostAddTests.cmake index 8b46aeae..f0fd71b2 100644 --- a/tests/cpp/boostAddTests.cmake +++ b/tests/cpp/boostAddTests.cmake @@ -74,6 +74,7 @@ while(_common_prop_idx LESS _common_prop_len) math(EXPR _common_prop_idx "${_common_prop_idx} + 2") endwhile() +list(APPEND common_labels_list cxx) list(REMOVE_DUPLICATES common_labels_list) list(REMOVE_DUPLICATES common_env_list) diff --git a/uv.lock b/uv.lock index 061ea3cb..0e875173 100644 --- a/uv.lock +++ b/uv.lock @@ -146,18 +146,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "colorlog" -version = "6.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8c/55/ba79756cb90c8d69d599d57785398ac87bba7b19c80e87f4e8a562197c93/colorlog-6.12.0.tar.gz", hash = "sha256:2a7924c1dadf18b22a0eb8b06d1c7b01d5341707ec1641eb6fcc4fde0c3e8e5f", size = 18151, upload-time = "2026-07-23T13:40:40.71Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", hash = "sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", size = 12239, upload-time = "2026-07-23T13:40:39.562Z" }, -] - [[package]] name = "comm" version = "0.2.3" @@ -479,21 +467,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, ] -[[package]] -name = "gcovr" -version = "8.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorlog" }, - { name = "jinja2" }, - { name = "lxml" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/07/37/b4a87dff166dc0a5002e9d03fcb6ca8eeff048247b011b67f047e31122c9/gcovr-8.6.tar.gz", hash = "sha256:b2e7042abca9321cadbab8a06eb34d19f801b831557b28cdc30a029313de8b9e", size = 199997, upload-time = "2026-01-13T20:04:30.019Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/be/f722c843e7875c7cf92cf0e0c1604cddda55a70278c768c6327a78fdba79/gcovr-8.6-py3-none-any.whl", hash = "sha256:dbf9d87c38042752ad6f530aa8210427e22b526611bb7b7bfed0e81977d1f1ef", size = 254618, upload-time = "2026-01-13T20:04:28.15Z" }, -] - [[package]] name = "h5py" version = "3.16.0" @@ -810,108 +783,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, ] -[[package]] -name = "lxml" -version = "6.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/3b/aab6728cae887456f409b4d75e8a01856e4f04bd510de38052a47768b680/lxml-6.1.1.tar.gz", hash = "sha256:ba96ae44888e0185281e937633a743ea90d5a196c6000f82565ebb0580012d40", size = 4197430, upload-time = "2026-05-18T19:19:06.424Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/b0/83f481780d1548750b8ce2ec824073deef2f452d9cd1a6faff8507e3d16d/lxml-6.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:53b7d2b7a10b1c35c0a5e21e9224accf60c1bbfba523990732e521b2b73adef2", size = 8526461, upload-time = "2026-05-18T19:17:25.862Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d5/30fa0f808002c7329397bfbb24e306789c0b29f04aa5842c07b174b4216f/lxml-6.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3f333630ab480244a1bff72043e511a91eb22e7595dead8653ee5612dd8f3d", size = 4595375, upload-time = "2026-05-18T19:17:34.555Z" }, - { url = "https://files.pythonhosted.org/packages/4f/d2/edb71cf0e561581a7c5eb2626244320eb04e9f8ce6d563184fd668b45073/lxml-6.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a4bbea04c97f6d78a48e3fbc1cb9116d2780b1b39e03a23f6eb9b603fd61f510", size = 4923654, upload-time = "2026-05-18T19:17:42.917Z" }, - { url = "https://files.pythonhosted.org/packages/4c/77/1bc7eeb0de4577d783fb625aa092cc9357883bba35845a3666bf1259f3dc/lxml-6.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db1d75f6617a49c1c01bc7023713e0ff59ab32c9579ae62a7674c0e34f3b0b0a", size = 5067921, upload-time = "2026-05-18T19:17:49.175Z" }, - { url = "https://files.pythonhosted.org/packages/1b/3c/c0690d74bd2bc17bc03b5b0d093569ead597dd0bfa088bf99eef8c24e19c/lxml-6.1.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a12689be69a28ddaa0ab99a5a1137da2afd5f8f16df7b5680b66f616d3eda1d", size = 5002456, upload-time = "2026-05-18T19:17:59.715Z" }, - { url = "https://files.pythonhosted.org/packages/66/8d/d1b3271af0c0f1e27e8472a849e4d2c65bc7766884b9ad2da9e76e145c88/lxml-6.1.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b73c339ae29b90fd2d06e58ebd555a751bde9cd6bbd36cc0281b9a2c94e9d8", size = 5202776, upload-time = "2026-05-18T19:18:08.924Z" }, - { url = "https://files.pythonhosted.org/packages/7a/45/689824ffb237fd10125ad273f32b28ff04dc6203c2822c85ff65a93df65e/lxml-6.1.1-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:752d3bbfe874715ccd0aec7f88d7fc623c0f1fd7aa7b3238a084e017bad2a009", size = 5329945, upload-time = "2026-05-18T19:18:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c0/ef73af53767e958fd87d437c170f272e2f6e6c0f854939f133a895f1e711/lxml-6.1.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:6b1761fbf9ec984e2e9d9c589ef5f5fd684b7c19f92aadd567a26c5224958db6", size = 4659237, upload-time = "2026-05-18T19:18:18.657Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5e/e1158e40397585e91cb0472374a1f63d0926a1ddeaa92f13d1a1ffe306d5/lxml-6.1.1-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d680fbcb768404c601ecb43519ecd8461f6954cb11c06a78962f666832ccfca8", size = 5265904, upload-time = "2026-05-18T19:18:24.883Z" }, - { url = "https://files.pythonhosted.org/packages/a0/16/8687e5d1400ed1c0bc41dace232ebb7553952b618ea1f2e5fb6e2cfbbe23/lxml-6.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:162af1091cd785f2f27e62d3547ae9bc58ec5c86dd314d67021fd02463708d83", size = 5045225, upload-time = "2026-05-18T19:17:20.073Z" }, - { url = "https://files.pythonhosted.org/packages/ca/18/d877bd1ae2e5ffdfd4836565aba350db31feb2f2656d6ce70316ed66a05e/lxml-6.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e9308ff8241c532df3f3e570f9a5aeed6c853f888512ba4b75638d7c11c95ef6", size = 4712721, upload-time = "2026-05-18T19:17:40.512Z" }, - { url = "https://files.pythonhosted.org/packages/44/4d/1f44fd1d770b10dacbf6b5c6e520f4d6e0708744930f719dc04e67cab981/lxml-6.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f6994074ebae6ffb04447268e37dc16edc304f9859cf91acb86e0af6c1b395c", size = 5252549, upload-time = "2026-05-18T19:17:51.236Z" }, - { url = "https://files.pythonhosted.org/packages/64/5d/1d66b84f850089254c230ef6ea6b267a5a54e2e179a5d960036a05d501d7/lxml-6.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:80c2dfadb855da477cf73373ad29a333535dedb9b12bad02c9814c8e2b43bf08", size = 5226877, upload-time = "2026-05-18T19:18:00.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/00/84c4b5302d42a2d0184f38d538c8a197f33b52a50bd4f7bcfe990bce3036/lxml-6.1.1-cp311-cp311-win32.whl", hash = "sha256:30a89d3ac8faec007453fb541f3f46807eeec88edd5826f6e3fe001752a2c621", size = 3594072, upload-time = "2026-05-18T19:17:12.714Z" }, - { url = "https://files.pythonhosted.org/packages/61/9d/2e2f7d876349f45e0f3e29f72da311668853d59b58d473a2dea4f0160135/lxml-6.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:abbefa31eee84842140f67acef1c828e28bba8bbf0c3bc6e5492a9af88152c28", size = 4025469, upload-time = "2026-05-18T19:17:50.566Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d5/570e6390e4110331e6208b2ba83d1482cc9146808ee118b22824a34c1070/lxml-6.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:dcb292aa7fe485ceff7af4f92e46c5af397daec5dff64871a528f0fc47a3cc5b", size = 3667640, upload-time = "2026-05-19T19:22:48.293Z" }, - { url = "https://files.pythonhosted.org/packages/6a/6e/c4add832b6fc1e887125b96f880d7b9b70aae5248718e046b1704bcac4b9/lxml-6.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:104c09bda8d2a562824c0e319d0768ce26a779b7601e0931d33b09b53c392ef7", size = 8570821, upload-time = "2026-05-18T19:17:42.068Z" }, - { url = "https://files.pythonhosted.org/packages/22/00/ff3009c88e65de8011630acf8ab5a09cb2becd2aaf47fba2f3449f6224e9/lxml-6.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25c6997a9a534e016695a0ba06b2f07945de682731ff01065b6d5a4474179da1", size = 4624252, upload-time = "2026-05-18T19:17:47.897Z" }, - { url = "https://files.pythonhosted.org/packages/42/95/bb63f0fd62e554fe078e1fb3c8fe9083c14ddc7ad7fa178d10e57e071ac7/lxml-6.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c921ba5c51e4e9f63b8b00267d06566e1f63407408a0496da2d1d0bfc819c7fc", size = 4930746, upload-time = "2026-05-18T19:18:29.637Z" }, - { url = "https://files.pythonhosted.org/packages/eb/99/0013e8d9b5960f4f041cf0b73e2f80c23eb5205b1f7bfb20203243651359/lxml-6.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:54a7f95e4de5fb94e2f9f4b9055c6ba33bf3d628fd77a1d647c5923caa2cdcdc", size = 5093723, upload-time = "2026-05-18T19:18:34.168Z" }, - { url = "https://files.pythonhosted.org/packages/29/91/317b332636bfc7bddcff828d41b3307f50043f4b237e40849c333d80fa1a/lxml-6.1.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f2ec43df44b1f76249ee0a615334f9b5b060e1c8bd90e706dad2d14d02f383", size = 5005557, upload-time = "2026-05-18T19:18:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/42/2f/cc9bf06afe70f9c9093ae60855d9759da9db601ec4080f7473319666ffd7/lxml-6.1.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70ef8a7e102a1508f8121aae5b0867abd663f72c14f0a9c937e6554cb4587b7b", size = 5631036, upload-time = "2026-05-18T19:18:44.858Z" }, - { url = "https://files.pythonhosted.org/packages/08/f6/af32e23e563971ffb0fb86be52bc5be5c2c118858ffc119bf6a9039b173d/lxml-6.1.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebe6af670449830d6d9b752c256a983291c766a1365ba5d5460048f9e33a7818", size = 5240367, upload-time = "2026-05-18T19:18:49.217Z" }, - { url = "https://files.pythonhosted.org/packages/78/83/8555d40948b09ce86f1bd0c68a7ac31d07b1929f92cc1b074006c97ef2d2/lxml-6.1.1-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:27acc820660aaffa4f7c087f29120e12980f7779d56d8492d263170111284740", size = 5350171, upload-time = "2026-05-18T19:18:52.779Z" }, - { url = "https://files.pythonhosted.org/packages/63/75/5d92da93729b7bad783689e6496049fa40927b45bec7bf183c981de3ca70/lxml-6.1.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:1db753c9115ec7100d073b744d17e25e88a8f90f5c39b2f5dd878149af59671f", size = 4694874, upload-time = "2026-05-18T19:18:55.139Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b5/3aad415a9a25b822e783f15deeb4dffccf5113030f1afa2222dd929313d9/lxml-6.1.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4f469aebd783bb741c2ecb2a681008fd26bfe5c16a9a72ed5467f834e810df2", size = 5244492, upload-time = "2026-05-18T19:19:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a1/5fcf7eb9904b80086aa47dcf0027de07b1bb990afad2e6823144c368ae04/lxml-6.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:766b010012d59470072c1816b5b6c69f1d243e5db36ea5968e94accf430a4635", size = 5048232, upload-time = "2026-05-18T19:18:12.67Z" }, - { url = "https://files.pythonhosted.org/packages/77/74/1f601b63c7a69fcdf10fa9b148c81da8442204194f6c55509cc485c786b9/lxml-6.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b8d812c6011c08b8111a15e54dd990b8923692d80adf35488bee34026c35accf", size = 4777023, upload-time = "2026-05-18T19:18:15.928Z" }, - { url = "https://files.pythonhosted.org/packages/a2/b9/7a78f51aec95b1bf780d78e12705a9f6533284f8693dc5c0e6724fa53d3f/lxml-6.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe0306bd29505a9177aac19f1877174b0e7422c222a59f70b2cd41633448c3dc", size = 5645773, upload-time = "2026-05-18T19:18:23.223Z" }, - { url = "https://files.pythonhosted.org/packages/a5/6e/98a7b7ad54e4e74fa1f20fff776913980619d0ebe5558232d7da6580bdd8/lxml-6.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5ba186ad207446c65d3bb3d3e0412b032b1d9f595e59861e2354798c5703d955", size = 5233088, upload-time = "2026-05-18T19:18:31.433Z" }, - { url = "https://files.pythonhosted.org/packages/65/d1/bc0ed2427bf609f2ee10da303a6a226f9c8bce94f945dc29a32ce55de6e4/lxml-6.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa366a1e55b8ebfe8ca8ddc3cfe75c8ebade181aeb0f661d0cb05986b647f72a", size = 5260995, upload-time = "2026-05-18T19:18:37.091Z" }, - { url = "https://files.pythonhosted.org/packages/69/8b/6772e1a4b513fc50a8d931f19edde0e13ae6918510a1e13ff67864f3e5ed/lxml-6.1.1-cp312-cp312-win32.whl", hash = "sha256:126c93f7f56f0eda92f6d8c619edc463a4f23d9252f1c9d0405a76f25fa9f11a", size = 3596382, upload-time = "2026-05-18T19:17:18.37Z" }, - { url = "https://files.pythonhosted.org/packages/1b/89/45198e9624762af2dfd2cb8782598477ceb29f6e59caab560388ae1f4ec1/lxml-6.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:26e6eda8d38c1fcab1090dd196ee87cbd13788e531937610e2589085de074e77", size = 3997255, upload-time = "2026-05-18T19:17:56.781Z" }, - { url = "https://files.pythonhosted.org/packages/90/a9/7a54b6834088d9ae528a7b780584ba6a39a9457b0ac330479f20ffbc9449/lxml-6.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:6540377fbd53fe1b629172288c464fb18db11ce1fa7dc15891da10aa9dcc3e7f", size = 3659610, upload-time = "2026-05-19T19:22:50.843Z" }, - { url = "https://files.pythonhosted.org/packages/a5/eb/7e6f37c5584ccbb2ff267f56fd0339016938c1c8684cfefab9b33ffc2f36/lxml-6.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:68a9198d0fc122d14bb76837de9aa80cf84caed990b5b237f532ed87d3706736", size = 8559780, upload-time = "2026-05-18T19:17:57.661Z" }, - { url = "https://files.pythonhosted.org/packages/a1/36/587c2521cf23a2cd6c9c22108aa7528f683a1f195ed7ccd23a4b1786ad36/lxml-6.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d47866cb32fb503450b6edc9df355d10dc49836af2e89901bd6ac6b0896d9d9", size = 4618006, upload-time = "2026-05-18T19:18:04.452Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ca/ab7bfe2bf4c972af5e7878262845ead3a24a929a9b04bc11c7c1ece6c82a/lxml-6.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb7c9811bfaa8b1ed5ed319f5d370dfbcaa59d52ea64be2a5a85e18195930354", size = 4924139, upload-time = "2026-05-18T19:19:04.873Z" }, - { url = "https://files.pythonhosted.org/packages/6b/55/a0c72851dfee5ecc689f949723a73dea457758912542cb955b108eaf0d8f/lxml-6.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:762ff394d5bd56da0cf034a23dcce4e13923f15321a2adfa2ac00201dc6d3fca", size = 5082329, upload-time = "2026-05-18T19:19:09.728Z" }, - { url = "https://files.pythonhosted.org/packages/f0/b6/0608f7d61a3b96cc67e5648a3d906e31a5082093e10e7be65b3886289938/lxml-6.1.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a088f287f7d8275a33c07f2cac6c50b9319309a0200a39e7e75d80c707723099", size = 4993564, upload-time = "2026-05-18T19:19:13.608Z" }, - { url = "https://files.pythonhosted.org/packages/4c/66/ae227524b066d29d55bf0b453d93d2d793c40218657d643dcbbca13b8faf/lxml-6.1.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e902da4b04e6b52e5893900d4b8ab46068f75f3561f01bf1080957f9fd932ed6", size = 5613467, upload-time = "2026-05-18T19:19:16.228Z" }, - { url = "https://files.pythonhosted.org/packages/a6/76/dbe4a00b50385e40194231dcfe5a12c059de7cf90e89c83407d2b085b719/lxml-6.1.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d4962d4c66bf830a7e59ed6cfc17d148149898a3aefa8ec6e59763e6e3ed085", size = 5228304, upload-time = "2026-05-18T19:19:19.354Z" }, - { url = "https://files.pythonhosted.org/packages/1c/01/00b1b8442ed2041793336868ba0b9ea4b13d7da7c085c6404c207a63bf79/lxml-6.1.1-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:581d4c8ae690a6609e64862dd6b7c2489635c2d13907fc2b20f2bc200ff1d21e", size = 5341607, upload-time = "2026-05-18T19:19:22.297Z" }, - { url = "https://files.pythonhosted.org/packages/63/36/1ad29931e9a4638bb707869f01d423a6c815f82152138d1a40dfcfde2b95/lxml-6.1.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:876e1ff5930ed8bf295ec5ef9a8155e9b6b1876bbf1deed8b3a8069311875a8f", size = 4700168, upload-time = "2026-05-18T19:19:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/3c/d1/a9536cecf9be18a0dc72d32bead283a2332d1ffebd2dd3ac70ce444686e5/lxml-6.1.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9eb9b5a968f6e0f6d640092a567e14529ff8cea2e29d00da6f78a79fa49f013c", size = 5232487, upload-time = "2026-05-18T19:19:28.603Z" }, - { url = "https://files.pythonhosted.org/packages/0e/77/b4fb1e03bf5d130e879214d3100092e386418807fb74dd0adc4b0a48f351/lxml-6.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:aa49e06d94aba782c6a02eecb7e507969e7e7a41b267f1b359bb35585f295d5b", size = 5044231, upload-time = "2026-05-18T19:18:42.246Z" }, - { url = "https://files.pythonhosted.org/packages/26/4c/d00daeeb0a5530c4028a9232aa1b93db3ef4ed2158c116ea73c79a9765b3/lxml-6.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:70cdfd80589d59e43e18005dd7244e8895e93db8ab6a620b7e23df5445a4e3d2", size = 4769450, upload-time = "2026-05-18T19:18:48.013Z" }, - { url = "https://files.pythonhosted.org/packages/ed/6a/715a3a8d156ce42f29cf014706f5410c2ff3b02267774110fc23266409fe/lxml-6.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:aad9aa39483ed8ec44d6d2e59e5b98a0d80676ef0d92f44bfc374836111f62f5", size = 5635874, upload-time = "2026-05-18T19:18:51.914Z" }, - { url = "https://files.pythonhosted.org/packages/45/37/0544bc21dde2a88f3a17b504e6fc79c0e01d25a33c2f6079724e9e72b9c7/lxml-6.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d49514be2f28d895c38cf9d2b72d7b9a07d00314519f456c0b50b53cfcf4c785", size = 5223987, upload-time = "2026-05-18T19:18:59.715Z" }, - { url = "https://files.pythonhosted.org/packages/4d/f8/f6a5e8185bcb28c2befae3d31f8e3df3b811cb0f47746517a81279fcafe1/lxml-6.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:47402e62c52ff5988c1e8c6c63177f5708bccf48e366dea4e3dcf1e645e04947", size = 5250276, upload-time = "2026-05-18T19:19:03.834Z" }, - { url = "https://files.pythonhosted.org/packages/c7/f2/1a2b9f1b7a49d45495369be7ef9ad05b262930f2eab3e3145706fca8083f/lxml-6.1.1-cp313-cp313-win32.whl", hash = "sha256:3483644525531e1d5762b0c44a8e18b6efba321b6dcf8a8952de10b037618bca", size = 3596903, upload-time = "2026-05-18T19:17:29.863Z" }, - { url = "https://files.pythonhosted.org/packages/e6/99/f4ffb024f238eec2131aaa09f3278fb6129cf892741bf68e1fc1afb8c100/lxml-6.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:a10bd2fd62e8ce916ececb342f348f190724a098c1faa056fdfb2a22ad5e8660", size = 3995869, upload-time = "2026-05-18T19:18:02.596Z" }, - { url = "https://files.pythonhosted.org/packages/d1/53/70eb8c5c6037f27448f1e3c54ebede9545a801ae63f0a7254afca4fe8e45/lxml-6.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:424aa57aca0897eb922aef34395bd1289b3b6f04e6bae20ea123c0c7e333cffc", size = 3658490, upload-time = "2026-05-19T19:22:53.846Z" }, - { url = "https://files.pythonhosted.org/packages/13/e2/2e325795566de01d0d7c3bb57d3c370616b2d07b01214e84eec5d3b10963/lxml-6.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:19b7ab10b210b0b3ad7985d9ac4eb66ab09a90b20fe6e2f7ba55d01a234345d0", size = 8577146, upload-time = "2026-05-18T19:18:17.765Z" }, - { url = "https://files.pythonhosted.org/packages/93/cf/5630b5e4be7d2e6bee8efe83865c925221103cf0221303b104ce134b01e2/lxml-6.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c08e5c694306507275f2290073350c4f32e383db15213b2c69e7ff39c1193840", size = 4623866, upload-time = "2026-05-18T19:18:30.669Z" }, - { url = "https://files.pythonhosted.org/packages/d2/51/3904907c063451cf8d4a5c9fe0cad95fa1f4ec57f4e3884fa0731bd7a305/lxml-6.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:74a9717fd0d82effef5c2854f0d917231d5324b5a3eb7275c43ac9fa32f97a14", size = 4950022, upload-time = "2026-05-18T19:19:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/94/cd/9c7611a51c37a2830928405817cc5d56a97f64fab83cc3f628748b135749/lxml-6.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efe0374196335f93b53269acd811b944f2e6bdc88e8894f214bd636455484909", size = 5086695, upload-time = "2026-05-18T19:19:34.764Z" }, - { url = "https://files.pythonhosted.org/packages/da/d6/24e3b5906abb0b674ff2ae195bc3ce59708df2bcd17cf17703b2d7dd643a/lxml-6.1.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac931cdc9442c1763b8a8f6cd62c0c938737eafc5be75eff88df55fc73bc0d00", size = 5031642, upload-time = "2026-05-18T19:19:37.771Z" }, - { url = "https://files.pythonhosted.org/packages/2d/db/6ec54f99019838bff54785c51da07f189eb4676861c5f2730962b0d8d665/lxml-6.1.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aee395f5d0927f947758b4ec119fd5fc8ec71f07a1c5c52077b30b04c0fa6955", size = 5647338, upload-time = "2026-05-18T19:19:40.553Z" }, - { url = "https://files.pythonhosted.org/packages/42/3d/ef4dcfffd22d27a61805d8ed9f7fb888495bc6aa88648fa07c1eaa5586b6/lxml-6.1.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9395002973c827b3ed67db77e6ec09f092919a587022174554096a269378fb13", size = 5239528, upload-time = "2026-05-18T19:19:43.657Z" }, - { url = "https://files.pythonhosted.org/packages/62/bb/37fb3f0dff146bdcfa78eec47879273820b2a0bf350ec236ce14bd0b1c26/lxml-6.1.1-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:73bc2086f141224ebddb7fc5c6a36ca58b31b94b561e1dfe8e073e3270fad1e7", size = 5350730, upload-time = "2026-05-18T19:19:46.307Z" }, - { url = "https://files.pythonhosted.org/packages/90/42/43253f168388df4fae1f38c01df36ddb9bee39e2048167b54cdcbae85ea3/lxml-6.1.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3779def59032b81e44a5f70096ef6bf2082f8d901937dca354474ba09782e245", size = 4697530, upload-time = "2026-05-18T19:19:49.889Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a8/c5a8504f81bbdfc8e7094c2c850cdb4ed6777fc4d5ddd9e5ab819f3b0d54/lxml-6.1.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:86c89b9d55ebf820ad7c90bc533410f0d098054f293351f10603c0c46ff598f5", size = 5250670, upload-time = "2026-05-18T19:19:53.199Z" }, - { url = "https://files.pythonhosted.org/packages/77/b7/c7e76ab18744d75e21f320ebf9ff9d1ceae2b54dd431ea5a64caf26c9672/lxml-6.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19607c6bbff2a44cf3fe8250abccd20942d3462473e0a721d01d379ed017e462", size = 5084485, upload-time = "2026-05-18T19:19:08.422Z" }, - { url = "https://files.pythonhosted.org/packages/31/31/b35c53f8ef7b7c31cacd23d3638652fff7bcd1deb6eedb709ab43b685908/lxml-6.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c6ed5141a5c7507cf3ee76bd363b0d6f801e3321adc35b5d825a23115faa5465", size = 4737635, upload-time = "2026-05-18T19:19:12.321Z" }, - { url = "https://files.pythonhosted.org/packages/d9/06/31f23c813a7fe8e0cb1b175e915b08c9bf4e86d225b210feadbdbe519667/lxml-6.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:62aeb7e85b5d60320b9d77eef2e773994e2c0ce10121b277e0a19804e1654a5a", size = 5670681, upload-time = "2026-05-18T19:19:15.001Z" }, - { url = "https://files.pythonhosted.org/packages/1a/bc/ce619bccc89b1fd9ad8a8e1330ee3f3beff9f2ff95b712d7bbcdd6e22fc3/lxml-6.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b1b963fd8f5caa68e99dfae060d54de1fe9cba899b8718b44a00cdca53c3e590", size = 5238229, upload-time = "2026-05-18T19:19:18.131Z" }, - { url = "https://files.pythonhosted.org/packages/2f/5d/b329acbbedc0b619ebc2be6cf7ee9ed07e80892c88d4dfd612c33805789a/lxml-6.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63876be28efefa04a1df615b46770e82042cce445cfdce55160522f57b231ccb", size = 5264191, upload-time = "2026-05-18T19:19:21.118Z" }, - { url = "https://files.pythonhosted.org/packages/d6/85/be36fb1425b30db3c3f9df75fe86343ebffb79e6320bd7f588e25bfeac39/lxml-6.1.1-cp314-cp314-win32.whl", hash = "sha256:7f7a92e8583f06b1fd49d01158143b8461cfcd135dcb10ec807270a3051bd603", size = 3657202, upload-time = "2026-05-18T19:17:39.509Z" }, - { url = "https://files.pythonhosted.org/packages/b8/ce/3cf9a827342269f54d405a6202397de63f07c69cbd6ce7d183a3f0cba1e9/lxml-6.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:b2d444f2e66624d68e9c6b211e28a76e22fff5fcabcfff4deac18b529b7d4137", size = 4064497, upload-time = "2026-05-18T19:18:14.662Z" }, - { url = "https://files.pythonhosted.org/packages/d9/3e/1a957bde8f0760039e627f94699f82caa782c9d838d86c3d28245ee67212/lxml-6.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:3fd9728a2735fda14f4e8235830c86b539e9661e849665bf926d3f867943b4bf", size = 3741991, upload-time = "2026-05-19T19:22:59.111Z" }, - { url = "https://files.pythonhosted.org/packages/78/b2/00ed55b3a2efa4658fb795c38d1090ec9b3e8a6c3683d4441fa517f09c3b/lxml-6.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:787b2496d0dbe8cd180984e8d29e3a6f76e7ea34db781cb3bd55e4ba1ef8b4ee", size = 8827545, upload-time = "2026-05-18T19:18:41.193Z" }, - { url = "https://files.pythonhosted.org/packages/c0/73/74573db19baa618d5f266f2407898b087ff6927115b00b71e5fc1b700847/lxml-6.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2c8daa471358dc2d6fcf02165e80ec68f77871a286df95bc5cc3816153b0fd2c", size = 4735736, upload-time = "2026-05-18T19:18:46.761Z" }, - { url = "https://files.pythonhosted.org/packages/16/02/6f7061f4f95f51e545d48e87647c54791d204a4e881be4156e7a26ba5338/lxml-6.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:acd7d70b64c0aae0c7922cca83d288a16f5f6da523637697872253415269baef", size = 4970291, upload-time = "2026-05-18T19:19:56.215Z" }, - { url = "https://files.pythonhosted.org/packages/b0/02/55fc057d8283427dea7d6edb102e7a840239c77a64a983d92f62a304c0e9/lxml-6.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4f0dd2f01f9f8a89f565d000e03abcf0a13d692a346c8d22f628d49af098777a", size = 5102822, upload-time = "2026-05-18T19:19:59.223Z" }, - { url = "https://files.pythonhosted.org/packages/e4/48/8e1cf78d89d66850121d9255a2a24414c98f775da93b90cf976956c24b14/lxml-6.1.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b7e8a14c8634bf6f7a568634cb395305a6d964aeb5b7ee32248094bed3a7e2c", size = 5027923, upload-time = "2026-05-18T19:20:01.549Z" }, - { url = "https://files.pythonhosted.org/packages/ed/00/0632a0647612c8af24d26997b3b961397daa9d5b2581444805933629a4cb/lxml-6.1.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:86281fbdd6a8162756f8d603f37e3435bfa38043adb79c6dc6a2dfee065e7525", size = 5595843, upload-time = "2026-05-18T19:20:03.93Z" }, - { url = "https://files.pythonhosted.org/packages/bc/86/ab008a7dc360711b66858d61c80a5979a70a09f2aa2b05d9698df80b803d/lxml-6.1.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5d7152ec39ca7c402d8fb9bad86140a15b9503bd0c54484e3f1bbe3dd37ceca", size = 5224515, upload-time = "2026-05-18T19:20:06.381Z" }, - { url = "https://files.pythonhosted.org/packages/75/c6/2702ff375e728e34f56d9a45339a9cf7e4427e917f542225242d63a05afa/lxml-6.1.1-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:88d8cb75b9d82858497a5393e3c63cfbf03035225e4b35a49ed7ccb151e4dc0e", size = 5312511, upload-time = "2026-05-18T19:20:09.308Z" }, - { url = "https://files.pythonhosted.org/packages/b7/57/a5807c98f87a86f10ef9ffab35516df7c0f0c4b6d5d33e9f608ab9c04a31/lxml-6.1.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f64ec5397ea6a41fc1b4af0380d79b44a755b5531dcaccd9940fb260dca93038", size = 4639206, upload-time = "2026-05-18T19:20:11.704Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e1/8a0a2c35734812395f4da4eaf33748a7e5705bfb2a58b128da764339d5ec/lxml-6.1.1-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d34bbf07dbc7ca5970671b1512e928991fb5e9d95365636c9b2d8b4f53af405e", size = 5232404, upload-time = "2026-05-18T19:20:14.064Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/0e6a4dd5ad84d01d99aa7bae7cfefd4a760a0e0f8176818241de17d9b6c0/lxml-6.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:17e0e18d4ad8adbd0399291bc44845b69d9dd68439a3cdebdf35ff902ec05072", size = 5083769, upload-time = "2026-05-18T19:19:23.758Z" }, - { url = "https://files.pythonhosted.org/packages/a0/7e/161f33d463f6ffc1c7679104b65086dea120080d49dde4d238f015aaee2f/lxml-6.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:3ab541146f1f6968c462d6c2ac495148e8cdba2f8347700b2141b6ec5a75bf52", size = 4758936, upload-time = "2026-05-18T19:19:27.256Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fb/2369825e3f6ca99305bf9f7b7085fda91c8b0922a89e54d900974aa3ef85/lxml-6.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2a0217714657e023ef4293500f65aa20fce6164c8fd6b08fa5bd4a859fb14b9b", size = 5620296, upload-time = "2026-05-18T19:19:29.993Z" }, - { url = "https://files.pythonhosted.org/packages/30/90/d61e383146f74c5ab683947ea14dc7b82778838ab9b95ea73a23b60d0191/lxml-6.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:05a82eb6e1530a64f26225b55cbd178113bd0b5af1c2b625f25e5296742c26d2", size = 5228598, upload-time = "2026-05-18T19:19:33.523Z" }, - { url = "https://files.pythonhosted.org/packages/76/2d/2dafd8149e94b05bb070690efd5bb2680720681e03ff03fc57d2b70a1105/lxml-6.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9e36f163528fc50cbef305f02a5fd66d404edf7049cdaff211dbc2cba5a7013e", size = 5247845, upload-time = "2026-05-18T19:19:36.649Z" }, - { url = "https://files.pythonhosted.org/packages/ce/68/b30e913340c380ddac9580c6e6230991fc37240ec4f64704833e4f3e2769/lxml-6.1.1-cp314-cp314t-win32.whl", hash = "sha256:649dda677cf3bd6ac9ae14007ba0c824ded8ce5808b53fc7431d9140399118c1", size = 3897345, upload-time = "2026-05-18T19:17:33.562Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4e/9eb2af5335545f9fbcd7af57bcf87c6025d31eaa31b14ec184a6c8675328/lxml-6.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:793033d6c5cdf33a573f910d9bea14ef8f5771820411d118da8e1182edb53d5e", size = 4393350, upload-time = "2026-05-18T19:18:10.076Z" }, - { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223, upload-time = "2026-05-19T19:22:56.823Z" }, - { url = "https://files.pythonhosted.org/packages/b5/32/86a3f0f724a3a402d4627937a7fc27b160e45e7012b4adf47f6e1e844511/lxml-6.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:31033dc34636ea6b7d5cc11b1ddbda78a14de858ba9d3e1ed4b69a3085bc521e", size = 3930127, upload-time = "2026-05-18T19:19:02.27Z" }, - { url = "https://files.pythonhosted.org/packages/40/44/d832e82af08723761556d004b1d04d281c09f9a8cecd7d3148548c9941a3/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3893c14c4b6ac5b2d54ba8cf03e99fe5104e592de491f19bd6b82756c09f8004", size = 4210769, upload-time = "2026-05-18T19:20:41.427Z" }, - { url = "https://files.pythonhosted.org/packages/6d/39/0dc5949f759ed7d951e0bb8c2f2d9d7aca1908d22352fa84a8afd2ea54af/lxml-6.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c07da4cebf6889f03ebac8d238f62318e29f495de0aa18a51ea14e61ae907e2e", size = 4318163, upload-time = "2026-05-18T19:20:44.702Z" }, - { url = "https://files.pythonhosted.org/packages/e6/fb/8ab3845fe046ba4cbf74536bcf6801a774b7caf4350de1c5d37f1f0a9e90/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6f0ce10945fab9c4c06ce14e22af9059d1a87493a9af4501a5b0b9187e21cf2", size = 4250945, upload-time = "2026-05-18T19:20:47.385Z" }, - { url = "https://files.pythonhosted.org/packages/68/1b/7553ab136894374ffae8851ec06f98f511cd8e66246e41b6be059d0a7289/lxml-6.1.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f8844cd288697c6425c9beba919302241e3278871dc6519515e72b04e987abcf", size = 4401664, upload-time = "2026-05-18T19:20:50.489Z" }, - { url = "https://files.pythonhosted.org/packages/db/a4/441aee36c6f6b249823d20fd91f9be9ab89d7c5a8ae542a4a4ca6d342d56/lxml-6.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ed21202aec73cda4d55d1ce57b389aadb90ffb044e6cd1080b8347efe1b1ec84", size = 3508989, upload-time = "2026-05-18T19:18:38.158Z" }, -] - [[package]] name = "makefun" version = "1.16.0" @@ -1131,7 +1002,6 @@ bench = [ { name = "tabulate" }, ] dev = [ - { name = "gcovr" }, { name = "prek" }, { name = "ruff" }, ] @@ -1182,7 +1052,6 @@ bench = [ { name = "tabulate", specifier = ">=0.9" }, ] dev = [ - { name = "gcovr" }, { name = "prek", specifier = ">=0.4" }, { name = "ruff", specifier = "==0.15.20" }, ]