# Building pybind11-rdp from Source with CMake: Step-by-Step Guide

> Learn to build pybind11-rdp from source with CMake. This guide covers C++14 compiler, Python headers, and CMake 3.15+ for compiling the core extension module.

- Repository: [cubao/pybind11-rdp](https://github.com/cubao/pybind11-rdp)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Building pybind11-rdp from source with CMake requires a C++14 compiler, Python development headers, and CMake 3.15+ to compile the native `_core` extension module that exposes the Ramer-Douglas-Peucker algorithm to Python.**

The `cubao/pybind11-rdp` repository provides a high-performance C++ implementation of the RDP line-simplification algorithm, wrapped for Python using pybind11. While pre-built wheels are available, compiling from source with CMake gives you control over compiler optimizations and debug symbols. This guide walks through the complete build process, from cloning submodules to importing the compiled `rdp` and `rdp_mask` functions.

## Prerequisites for Building from Source

Before compiling pybind11-rdp, verify your environment meets these requirements:

- **CMake 3.15 or higher** (required by scikit-build-core)
- **C++14 compatible compiler** (GCC, Clang, or MSVC)
- **Python 3.7+** with development headers (python3-dev or python-devel packages)
- **Git** to clone recursive submodules

The project uses **scikit-build-core** as its PEP 517 build backend, which automatically locates pybind11 and Eigen headers during the CMake configuration phase defined in [`CMakeLists.txt`](https://github.com/cubao/pybind11-rdp/blob/main/CMakeLists.txt).

## Step-by-Step CMake Build Instructions

Follow these commands to manually compile the native extension using CMake.

### 1. Clone the Repository with Submodules

The project depends on pybind11 and Eigen as git submodules. Clone recursively to fetch all headers required by [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp):

```bash
git clone --recursive https://github.com/cubao/pybind11-rdp.git
cd pybind11-rdp

```

### 2. Create a Build Directory

CMake recommends out-of-source builds to isolate generated files from the source tree:

```bash
mkdir build && cd build

```

### 3. Configure the Project

Run CMake to generate build files. The configuration step locates your Python interpreter, finds pybind11 headers, and prepares the `VERSION_INFO` definition (line 52 of [`CMakeLists.txt`](https://github.com/cubao/pybind11-rdp/blob/main/CMakeLists.txt)):

```bash
cmake .. -DCMAKE_BUILD_TYPE=Release

```

The [`CMakeLists.txt`](https://github.com/cubao/pybind11-rdp/blob/main/CMakeLists.txt) defines the `_core` target as a Python extension module linking against `pybind11::headers`. It also injects the package version string into the compiled binary via the `VERSION_INFO` preprocessor definition.

### 4. Compile the Extension

Build the shared object (or `.pyd` on Windows) containing the RDP algorithm implementation:

```bash
cmake --build . --parallel

```

This compiles [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp), which contains the core logic: the `LineSegment` helper class (for point-to-segment distance calculations), the recursive `douglas_simplify` function, and the iterative `douglas_simplify_iter` variant that fills an `Eigen::VectorXi` mask.

### 5. Install the Package

Install the compiled module into your Python environment:

```bash
cd ..
pip install .

```

The `pip install .` command triggers scikit-build-core to move the compiled `_core` module (located at `src/pybind11_rdp/_core.so` on Linux) into your site-packages. The [`src/pybind11_rdp/__init__.py`](https://github.com/cubao/pybind11-rdp/blob/main/src/pybind11_rdp/__init__.py) wrapper then re-exports the compiled symbols—`rdp`, `rdp_mask`, `LineSegment`, `mask2indexes`, and `select_by_mask`—to the Python namespace.

## Architecture of the Compiled Module

Understanding the build output helps when debugging or extending the library:

- **[`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp)**: Implements the RDP algorithm using **Eigen** for vector operations. The `LineSegment` class pre-computes segment lengths and provides `distance()` methods for efficient point-to-line calculations. The algorithm functions accept 2-D or 3-D NumPy arrays via `Eigen::Ref` for zero-copy data access.
- **[`CMakeLists.txt`](https://github.com/cubao/pybind11-rdp/blob/main/CMakeLists.txt)**: Orchestrates compilation by finding Python development libraries, including pybind11 headers, and setting C++14 standards. It creates the `pybind11_rdp/_core` shared library target.
- **[`src/pybind11_rdp/__init__.py`](https://github.com/cubao/pybind11-rdp/blob/main/src/pybind11_rdp/__init__.py)**: Provides the Pythonic interface by importing functions from the compiled `_core` module and exposing them at the package level.

## Alternative: Installing via pip (PEP 517)

If you prefer not to run CMake manually, the project supports modern Python packaging standards. The [`pyproject.toml`](https://github.com/cubao/pybind11-rdp/blob/main/pyproject.toml) file declares **scikit-build-core** as the build system, allowing pip to handle the CMake invocation automatically:

```bash
pip install git+https://github.com/cubao/pybind11-rdp.git

```

This single command clones the repository, runs the CMake configuration and build steps described above, and installs the resulting wheel without requiring manual intervention.

## Verifying the Installation

Test your build by running the RDP algorithm on sample coordinate data:

```python
import numpy as np
from pybind11_rdp import rdp, rdp_mask

# 3-D polyline example

points = np.array([[0, 0, 0],
                   [1, 0.1, 0],
                   [2, 0, 0],
                   [10, 0.2, 0]])

# Simplify with epsilon=0.5

simplified = rdp(points, epsilon=0.5)
print(f"Original: {len(points)} points, Simplified: {len(simplified)} points")

# Get boolean mask (1 = keep, 0 = discard)

mask = rdp_mask(points, epsilon=0.5)
print(f"Retention mask: {mask}")

```

The `rdp` function returns a reduced coordinate matrix, while `rdp_mask` returns the boolean mask used internally by the `mask2indexes` and `select_by_mask` utilities implemented in [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp).

## Summary

- **pybind11-rdp** compiles into a native Python extension module (`_core`) using CMake and scikit-build-core.
- The build requires **CMake 3.15+**, a **C++14 compiler**, and **Python development headers**.
- Clone with `--recursive` to fetch pybind11 and Eigen submodules required by [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp).
- Use **out-of-source builds** (`mkdir build`) to keep the source tree clean.
- The [`CMakeLists.txt`](https://github.com/cubao/pybind11-rdp/blob/main/CMakeLists.txt) defines `VERSION_INFO` and links against `pybind11::headers` to create the extension.
- For automated builds, `pip install .` triggers PEP 517 compliant compilation via scikit-build-core.

## Frequently Asked Questions

### What CMake version is required to build pybind11-rdp?

CMake 3.15 or higher is required. This version ensures compatibility with scikit-build-core, which drives the build process defined in [`pyproject.toml`](https://github.com/cubao/pybind11-rdp/blob/main/pyproject.toml). Older versions may fail to locate Python components correctly or handle the `pybind11::headers` target properly.

### Do I need to install Eigen or pybind11 separately before building?

No. Both dependencies are included as git submodules. You must clone the repository using `git clone --recursive` so that CMake can find the headers in the `pybind11` and `eigen` directories. If you forget `--recursive`, run `git submodule update --init --recursive` before running CMake.

### How do I build a debug version of the extension for development?

Pass `-DCMAKE_BUILD_TYPE=Debug` during the configuration step instead of `Release`. This disables compiler optimizations and includes debug symbols, allowing you to step through the `douglas_simplify` implementation in [`src/main.cpp`](https://github.com/cubao/pybind11-rdp/blob/main/src/main.cpp) using GDB or LLDB to inspect the `LineSegment` distance calculations.

### Can I build pybind11-rdp against a specific Python version?

Yes. During the CMake configuration step, specify the Python executable explicitly: `cmake .. -DPython_EXECUTABLE=/usr/bin/python3.9`. The [`CMakeLists.txt`](https://github.com/cubao/pybind11-rdp/blob/main/CMakeLists.txt) uses CMake's FindPython module to locate headers and libraries matching that interpreter, ensuring the compiled `_core` module links against the correct Python version.