Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions docs/template-prompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ The bundled C++ template lives in `src/repoman/cpp_template/` and is selected
with `--preset cpp` or `repoman create cpp cpp`. In addition to project, author,
repository, and license metadata, it prompts for:

| Prompt | Description | Default |
| -------------------- | -------------------------------------- | ------------------------- |
| `cpp_namespace` | C++ namespace and public include dir | project name with `_` |
| `cpp_library_name` | Meson library target name | project name with `_` |
| `cpp_standard` | C++ language standard | `c++17` |
| `cpp_build_cli` | Build the example CLI executable | `true` |
| `cpp_build_examples` | Build example programs | `true` |
| `cpp_build_tests` | Build test executables | `true` |
| Prompt | Description | Default |
| --------------------------- | -------------------------------------- | --------------------- |
| `cpp_namespace` | C++ namespace and public include dir | project name with `_` |
| `cpp_library_name` | Meson library target name | project name with `_` |
| `cpp_standard` | C++ language standard | `c++17` |
| `cpp_build_cli` | Build the example CLI executable | `true` |
| `cpp_build_examples` | Build example programs | `true` |
| `cpp_build_tests` | Build test executables | `true` |
| `cpp_build_python_bindings` | Build Python bindings with pybind11 | `true` |
5 changes: 4 additions & 1 deletion docs/template-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
- **Docs-only:** If `docs_only` is true, source package and test files are omitted.
- **Notebooks:** If `python_notebooks` is true, the template generates a `notebooks/` folder.
- **Paper:** If `latex_paper` is true, the template generates a `paper/` folder with a LaTeX manuscript scaffold and Make targets.
- **C++:** The bundled `cpp_template` is a separate Meson-based template with `include/`, `src/`, `tests/`, `examples/`, Doxygen hooks, and the same repoman Make target names used by CI.
- **C++:** The bundled `cpp_template` is a separate Meson-based template with `include/`, `src/`, `tests/`, `examples/`, optional pybind11 Python bindings, Doxygen hooks, and the same repoman Make target names used by CI.

## High-level generated layout

Expand Down Expand Up @@ -58,5 +58,8 @@ my_cpp_library/
│ └── <namespace>/
├── scripts/
├── src/
│ ├── <namespace>.cpp
│ ├── cli.cpp
│ └── python_bindings.cpp
└── tests/
```
1 change: 1 addition & 0 deletions src/repoman/copier/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def build_preset_data(preset_name: str, project_name: str) -> dict:
overrides.setdefault("cpp_build_cli", True)
overrides.setdefault("cpp_build_examples", True)
overrides.setdefault("cpp_build_tests", True)
overrides.setdefault("cpp_build_python_bindings", True)
return {**base, **overrides}


Expand Down
5 changes: 5 additions & 0 deletions src/repoman/cpp_template/copier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,8 @@ cpp_build_tests:
type: bool
help: Build test executables
default: true

cpp_build_python_bindings:
type: bool
help: Build Python bindings
default: true
9 changes: 8 additions & 1 deletion src/repoman/cpp_template/template/README.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,18 @@ make format
make check
```

Python bindings are built by default with pybind11 and expose a module named
`{{ cpp_namespace }}`. Disable them with:

```bash
uv run meson configure build -Dbuild_python_bindings=false
```

## Layout

```text
include/{{ cpp_namespace }}/ Public headers
src/ Library and CLI sources
src/ Library, CLI, and Python binding sources
tests/ Meson test executables
examples/ Small example programs
docs/ ProperDocs documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ Build the project:
make build
```

The default build includes pybind11 Python bindings. You can disable them in an
existing build directory with:

```bash
uv run meson configure build -Dbuild_python_bindings=false
```

Run tests:

```bash
Expand Down
3 changes: 2 additions & 1 deletion src/repoman/cpp_template/template/docs/index.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ make build
make test
```

The public C++ API lives in `include/{{ cpp_namespace }}/`.
The public C++ API lives in `include/{{ cpp_namespace }}/`. Python bindings are
built by default as the `{{ cpp_namespace }}` module.
15 changes: 15 additions & 0 deletions src/repoman/cpp_template/template/meson.build.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ if get_option('build_cli')
)
endif

if get_option('build_python_bindings')
python = import('python').find_installation()
pybind11_dep = dependency('pybind11')

python.extension_module(
'{{ cpp_namespace }}',
'src/python_bindings.cpp',
dependencies: [
{{ cpp_library_name }}_dep,
pybind11_dep,
],
install: true,
)
endif

install_headers(
'include/{{ cpp_namespace }}/{{ cpp_namespace }}.hpp',
subdir: '{{ cpp_namespace }}',
Expand Down
1 change: 1 addition & 0 deletions src/repoman/cpp_template/template/meson_options.txt.jinja
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
option('build_cli', type: 'boolean', value: {{ "true" if cpp_build_cli else "false" }}, description: 'Build the example command-line executable')
option('build_examples', type: 'boolean', value: {{ "true" if cpp_build_examples else "false" }}, description: 'Build example programs')
option('build_tests', type: 'boolean', value: {{ "true" if cpp_build_tests else "false" }}, description: 'Build test executables')
option('build_python_bindings', type: 'boolean', value: {{ "true" if cpp_build_python_bindings else "false" }}, description: 'Build Python bindings')
1 change: 1 addition & 0 deletions src/repoman/cpp_template/template/pyproject.toml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default-groups = ["ci", "docs"]
ci = [
"meson>=1.5.0",
"ninja>=1.11.1",
"pybind11>=2.13.6",
]
docs = [
"markdown-callouts>=0.4.0",
Expand Down
13 changes: 13 additions & 0 deletions src/repoman/cpp_template/template/src/python_bindings.cpp.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "{{ cpp_namespace }}/{{ cpp_namespace }}.hpp"

#include <pybind11/pybind11.h>

namespace py = pybind11;

PYBIND11_MODULE({{ cpp_namespace }}, module) {
module.doc() = "Python bindings for this C++ library.";

module.def("version", &{{ cpp_namespace }}::version, "Return the library version.");
module.def("add", &{{ cpp_namespace }}::add, py::arg("lhs"), py::arg("rhs"),
"Add two integers.");
}
12 changes: 12 additions & 0 deletions src/repoman/cpp_template/template/tests/meson.build.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,15 @@ test_{{ cpp_library_name }} = executable(
)

test('{{ cpp_library_name }}', test_{{ cpp_library_name }})

if get_option('build_python_bindings')
python_binding_env = environment()
python_binding_env.prepend('PYTHONPATH', meson.project_build_root())

test(
'{{ cpp_library_name }}_python',
python,
args: files('test_python_bindings.py'),
env: python_binding_env,
)
endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Smoke tests for the generated Python bindings."""

from __future__ import annotations

import importlib


def main() -> None:
module = importlib.import_module("{{ cpp_namespace }}")
assert module.add(2, 3) == 5
assert module.version() == "0.1.0"


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions tests/test_copier.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def test_build_preset_data_cpp_preset() -> None:
assert data["cpp_build_cli"] is True
assert data["cpp_build_examples"] is True
assert data["cpp_build_tests"] is True
assert data["cpp_build_python_bindings"] is True


def test_build_copier_options_without_data() -> None:
Expand Down
9 changes: 9 additions & 0 deletions tests/test_template/test_cpp_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,33 @@ def test_cpp_template_renders_spinach_style_structure(tmp_path: Path) -> None:
"cpp_build_cli": True,
"cpp_build_examples": True,
"cpp_build_tests": True,
"cpp_build_python_bindings": True,
},
)

assert (project_dir / "include" / "sample_cpp" / "sample_cpp.hpp").exists()
assert (project_dir / "include" / "sample_cpp" / "version.hpp.in").exists()
assert (project_dir / "src" / "sample_cpp.cpp").exists()
assert (project_dir / "src" / "python_bindings.cpp").exists()
assert (project_dir / "src" / "cli.cpp").exists()
assert (project_dir / "tests" / "test_sample_cpp.cpp").exists()
assert (project_dir / "tests" / "test_python_bindings.py").exists()
assert (project_dir / "examples" / "basic.cpp").exists()
assert (project_dir / "Doxyfile").exists()

meson = _read_text(project_dir / "meson.build")
meson_options = _read_text(project_dir / "meson_options.txt")
makefile = _read_text(project_dir / "Makefile")
mkdocs = _read_text(project_dir / "config" / "mkdocs.yml")
pyproject = _read_text(project_dir / "pyproject.toml")
github_ci = _read_text(project_dir / ".github" / "workflows" / "ci.yml")

assert "subdir('include/sample_cpp')" in meson
assert "'cpp_std=c++17'" in meson
assert "python.extension_module(" in meson
assert "dependency('pybind11')" in meson
assert "option('build_python_bindings'" in meson_options
assert '"pybind11>=2.13.6"' in pyproject
assert "make check-docs" in github_ci
assert "make check" in github_ci
assert "make test" in github_ci
Expand Down
Loading