ogdf-py 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,190 @@
1
+ name: Build and Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ pull_request:
8
+ branches: [main, master]
9
+ workflow_dispatch:
10
+ inputs:
11
+ publish:
12
+ description: "Publish to PyPI"
13
+ required: false
14
+ default: false
15
+ type: boolean
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ concurrency:
21
+ group: build-${{ github.workflow }}-${{ github.ref }}
22
+ cancel-in-progress: true
23
+
24
+ env:
25
+ # Build CPython 3.10-3.13; skip musllinux (OGDF is bootstrapped on
26
+ # manylinux/glibc) and PyPy.
27
+ CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-*"
28
+ CIBW_SKIP: "*-musllinux_* pp*"
29
+ # Clone + build OGDF once per platform before building any wheels. On Linux
30
+ # this runs inside the manylinux container (git present; install cmake).
31
+ CIBW_BEFORE_ALL_LINUX: >-
32
+ (command -v cmake >/dev/null 2>&1 || pipx install cmake) &&
33
+ bash scripts/bootstrap_ogdf.sh
34
+ CIBW_BEFORE_ALL_MACOS: bash scripts/bootstrap_ogdf.sh
35
+ # Exercise each wheel end-to-end (import + layouts + I/O).
36
+ CIBW_TEST_REQUIRES: pytest
37
+ CIBW_TEST_COMMAND: pytest {project}/tests -v
38
+
39
+ jobs:
40
+ build-sdist:
41
+ name: Build source distribution
42
+ runs-on: ubuntu-latest
43
+ steps:
44
+ - uses: actions/checkout@v7
45
+
46
+ - name: Install uv
47
+ uses: astral-sh/setup-uv@v8.3.0
48
+
49
+ - name: Build sdist
50
+ run: uv build --sdist
51
+
52
+ - name: Upload sdist
53
+ uses: actions/upload-artifact@v7
54
+ with:
55
+ name: sdist
56
+ path: dist/*.tar.gz
57
+ retention-days: 14
58
+
59
+ build-wheels:
60
+ name: Wheels (${{ matrix.os }} / ${{ matrix.arch }})
61
+ runs-on: ${{ matrix.os }}
62
+ strategy:
63
+ fail-fast: false
64
+ matrix:
65
+ include:
66
+ # Native runners only -- no QEMU, no cross-compilation, so the OGDF
67
+ # static library always matches the wheel's architecture.
68
+ - os: ubuntu-latest
69
+ arch: x86_64
70
+ - os: ubuntu-24.04-arm
71
+ arch: aarch64
72
+ - os: macos-15-intel
73
+ arch: x86_64
74
+ macos_target: "10.15" # C++17 libc++ (<filesystem>) needs 10.15+
75
+ - os: macos-14
76
+ arch: arm64
77
+ macos_target: "11.0"
78
+ env:
79
+ MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macos_target }}
80
+ steps:
81
+ - uses: actions/checkout@v7
82
+
83
+ # Cache the cloned+built OGDF per platform/arch so reruns skip the build.
84
+ - name: Cache OGDF
85
+ uses: actions/cache@v4
86
+ with:
87
+ path: thirdparty/ogdf
88
+ key: ogdf-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('scripts/bootstrap_ogdf.sh') }}
89
+
90
+ - name: Build wheels
91
+ uses: pypa/cibuildwheel@v4.1.0
92
+ env:
93
+ CIBW_ARCHS: ${{ matrix.arch }}
94
+
95
+ - name: Upload wheels
96
+ uses: actions/upload-artifact@v7
97
+ with:
98
+ name: wheels-${{ matrix.os }}-${{ matrix.arch }}
99
+ path: wheelhouse/*.whl
100
+ retention-days: 14
101
+
102
+ collect-artifacts:
103
+ name: Collect all build artifacts
104
+ needs: [build-sdist, build-wheels]
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - name: Download all artifacts
108
+ uses: actions/download-artifact@v8
109
+ with:
110
+ path: dist
111
+ pattern: "*"
112
+ merge-multiple: true
113
+
114
+ - name: List all artifacts
115
+ run: |
116
+ echo "=== All build artifacts ==="
117
+ ls -la dist/
118
+ wheels=$(find dist -name '*.whl' | wc -l)
119
+ sdists=$(find dist -name '*.tar.gz' | wc -l)
120
+ echo "=== Wheels: ${wheels} | Sdists: ${sdists} ==="
121
+
122
+ - name: Upload combined artifacts
123
+ uses: actions/upload-artifact@v7
124
+ with:
125
+ name: all-dist
126
+ path: dist/
127
+ retention-days: 30
128
+
129
+ publish-testpypi:
130
+ name: Publish to TestPyPI
131
+ needs: collect-artifacts
132
+ runs-on: ubuntu-latest
133
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
134
+ environment:
135
+ name: testpypi
136
+ url: https://test.pypi.org/p/ogdf-py
137
+ permissions:
138
+ id-token: write
139
+ steps:
140
+ - name: Download all artifacts
141
+ uses: actions/download-artifact@v8
142
+ with:
143
+ name: all-dist
144
+ path: dist/
145
+
146
+ - name: Publish to TestPyPI
147
+ uses: pypa/gh-action-pypi-publish@release/v1
148
+ with:
149
+ repository-url: https://test.pypi.org/legacy/
150
+ skip-existing: true
151
+
152
+ publish-pypi:
153
+ name: Publish to PyPI
154
+ needs: [collect-artifacts, publish-testpypi]
155
+ runs-on: ubuntu-latest
156
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
157
+ environment:
158
+ name: pypi
159
+ url: https://pypi.org/p/ogdf-py
160
+ permissions:
161
+ id-token: write
162
+ steps:
163
+ - name: Download all artifacts
164
+ uses: actions/download-artifact@v8
165
+ with:
166
+ name: all-dist
167
+ path: dist/
168
+
169
+ - name: Publish to PyPI
170
+ uses: pypa/gh-action-pypi-publish@release/v1
171
+
172
+ publish-manual:
173
+ name: Manual publish to PyPI
174
+ needs: collect-artifacts
175
+ runs-on: ubuntu-latest
176
+ if: github.event_name == 'workflow_dispatch' && inputs.publish
177
+ environment:
178
+ name: pypi
179
+ url: https://pypi.org/p/ogdf-py
180
+ permissions:
181
+ id-token: write
182
+ steps:
183
+ - name: Download all artifacts
184
+ uses: actions/download-artifact@v8
185
+ with:
186
+ name: all-dist
187
+ path: dist/
188
+
189
+ - name: Publish to PyPI
190
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,63 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ci-${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ qa:
19
+ name: QA (lint, typecheck, test)
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+
24
+ # Cache the cloned+built OGDF so most runs skip the ~2 min build.
25
+ # Keyed on the bootstrap script (which pins the OGDF tag).
26
+ - name: Cache OGDF
27
+ uses: actions/cache@v4
28
+ with:
29
+ path: thirdparty/ogdf
30
+ key: ogdf-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('scripts/bootstrap_ogdf.sh') }}
31
+
32
+ - name: Bootstrap OGDF
33
+ run: ./scripts/bootstrap_ogdf.sh
34
+
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v8.3.0
37
+ with:
38
+ enable-cache: true
39
+
40
+ - name: Set up Python
41
+ run: uv python install 3.13
42
+
43
+ - name: Install dependencies (builds the extension)
44
+ run: uv sync --dev
45
+
46
+ - name: Lint with ruff
47
+ run: uv run ruff check src/ tests/
48
+
49
+ - name: Check formatting with ruff
50
+ run: uv run ruff format --check src/ tests/
51
+
52
+ - name: Type check with mypy
53
+ run: uv run mypy src/ogdf tests/
54
+
55
+ - name: Run tests
56
+ run: uv run pytest tests/ -v --cov=src/ogdf --cov-report=xml
57
+
58
+ - name: Upload coverage
59
+ uses: codecov/codecov-action@v7
60
+ with:
61
+ files: coverage.xml
62
+ fail_ci_if_error: false
63
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,46 @@
1
+ # Build
2
+ build/
3
+ dist/
4
+ *.egg-info/
5
+ *.so
6
+ *.pyd
7
+
8
+ # folders
9
+ thirdparty/
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+ .uv/
15
+
16
+ # Cache
17
+ __pycache__/
18
+ *.pyc
19
+ *.pyo
20
+ .pytest_cache/
21
+ .mypy_cache/
22
+ .ruff_cache/
23
+
24
+ # Coverage
25
+ .coverage
26
+ coverage.xml
27
+ htmlcov/
28
+
29
+ # CMake
30
+ CMakeCache.txt
31
+ CMakeFiles/
32
+ cmake_install.cmake
33
+
34
+ # IDE
35
+ .idea/
36
+ .vscode/
37
+ *.swp
38
+ *~
39
+
40
+ # OS
41
+ .DS_Store
42
+
43
+ # agentic
44
+ .claude
45
+ CLAUDE.md
46
+
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.1]
10
+
11
+ ### Added
12
+
13
+ - OGDF bindings (curated subset): `Graph`/`Node`/`Edge` with iteration; `NodeArray`/`EdgeArray` in int/double/bool; `GraphAttributes` with coordinates, size, labels, and styling (colors, shapes, fill patterns, stroke, edge arrows, bends).
14
+
15
+ - Layout algorithms: `SugiyamaLayout`, `FMMMLayout`, `GEMLayout`, `SpringEmbedderKK`, `StressMinimization`, `PivotMDS`, `PlanarizationLayout` (with optional orthogonal routing), `SchnyderLayout`, `TreeLayout`, `CircularLayout`.
16
+
17
+ - Core graph algorithms: connectivity/structure predicates, connected / strong / biconnected components, topological numbering, Dijkstra shortest paths, minimum spanning tree, maximum flow, global minimum cut, matching, node coloring, and planar embedding.
18
+
19
+ - Graph generators: complete, complete-bipartite, wheel, cube, grid, Petersen, regular tree, plus random graphs, trees, digraphs, and regular / biconnected / planar variants.
20
+
21
+ - File I/O: interchange formats GML, GraphML, DOT, GEXF, GDF, TLP (with extension-based `read`/`write`) and drawing output as SVG and TikZ.
22
+
23
+ - `demos/` folder with illustrative scripts (layout gallery, styling showcase, algorithm visualizations, generator zoo, I/O round-trips) writing to `build/demo-output`; `make demos` also builds a self-contained HTML gallery (`index.html`) of every drawing.
24
+
25
+ - Bindings split into modular translation units (`bind_graph`, `bind_layouts`, `bind_algorithms`, `bind_io`, `bind_generators`).
26
+
27
+ - Auto-generated `_core.pyi` type stub for typed usage and IDE completion.
28
+
29
+ - `cibuildwheel` release workflow building CPython 3.10-3.13 wheels for Linux (x86_64, aarch64) and macOS (x86_64, arm64) on native runners, bootstrapping OGDF once per platform; trusted-publishing jobs for TestPyPI/PyPI on tags.
30
+
31
+ - CMake auto-bootstraps OGDF if missing, so `pip install` from an sdist works without a manual step (Unix only).
32
+
33
+ ### Changed
34
+
35
+ - Reproducible OGDF dependency: `scripts/bootstrap_ogdf.sh` shallow-clones OGDF at a pinned tag (`foxglove-202510`) and builds its static libraries from source; the extension links them. `make build`/`make sync` auto-bootstrap if needed. Replaces reliance on a manually placed OGDF checkout.
36
+
37
+ - Persist the CMake build directory so only the bindings recompile on edits.
38
+
39
+ ### Fixed
40
+
41
+ - `make build` targeted the wrong package name (`ogdf` vs `ogdf-py`), making it a silent no-op; it now rebuilds correctly.
42
+
43
+ - The generic `write` now raises `ValueError` for attribute-incapable formats (e.g. LEDA, Chaco) instead of crashing the interpreter.
44
+
45
+ ## [0.1.0] - 2026-07-06
46
+
47
+ ### Added
48
+
49
+ - Initial project structure
50
+
51
+ - Core module with example functions
52
+
53
+ - Test suite with pytest
54
+
55
+ - Build system using scikit-build-core
@@ -0,0 +1,88 @@
1
+ cmake_minimum_required(VERSION 3.15...3.31)
2
+ project(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION} LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
7
+
8
+ find_package(Python REQUIRED COMPONENTS Interpreter Development.Module)
9
+ find_package(nanobind CONFIG REQUIRED)
10
+ find_package(Threads REQUIRED)
11
+
12
+ # --- OGDF (prebuilt static libraries) ---------------------------------------
13
+ # OGDF is cloned at a pinned tag and built from source by
14
+ # scripts/bootstrap_ogdf.sh, which produces libOGDF.a / libCOIN.a and the
15
+ # generated config header. We link those static libs into the extension.
16
+ set(OGDF_ROOT "${CMAKE_SOURCE_DIR}/thirdparty/ogdf")
17
+ set(OGDF_BUILD "${OGDF_ROOT}/build")
18
+
19
+ # If OGDF has not been built yet, try to bootstrap it automatically (clone the
20
+ # pinned tag + build from source). This lets `pip install` from an sdist work
21
+ # without a separate manual step. Windows is not supported by the bash script.
22
+ if(NOT EXISTS "${OGDF_BUILD}/libOGDF.a"
23
+ AND EXISTS "${CMAKE_SOURCE_DIR}/scripts/bootstrap_ogdf.sh"
24
+ AND NOT WIN32)
25
+ message(STATUS "OGDF not built; running scripts/bootstrap_ogdf.sh ...")
26
+ execute_process(
27
+ COMMAND bash "${CMAKE_SOURCE_DIR}/scripts/bootstrap_ogdf.sh"
28
+ RESULT_VARIABLE _ogdf_bootstrap_result)
29
+ if(NOT _ogdf_bootstrap_result EQUAL 0)
30
+ message(FATAL_ERROR
31
+ "OGDF bootstrap failed (exit ${_ogdf_bootstrap_result}).")
32
+ endif()
33
+ endif()
34
+
35
+ if(NOT EXISTS "${OGDF_BUILD}/libOGDF.a")
36
+ message(FATAL_ERROR
37
+ "Prebuilt OGDF not found at ${OGDF_BUILD}/libOGDF.a.\n"
38
+ "Run the bootstrap script first:\n"
39
+ " scripts/bootstrap_ogdf.sh\n"
40
+ "(or `make bootstrap`), which clones OGDF at a pinned tag and builds it.")
41
+ endif()
42
+
43
+ # Public headers plus the build-time generated config header
44
+ # (config_autogen.h lives under build/include/ogdf-release).
45
+ set(OGDF_INCLUDE_DIRS
46
+ "${OGDF_ROOT}/include"
47
+ "${OGDF_BUILD}/include/ogdf-release")
48
+
49
+ set(OGDF_LIBRARIES
50
+ "${OGDF_BUILD}/libOGDF.a"
51
+ "${OGDF_BUILD}/libCOIN.a"
52
+ Threads::Threads)
53
+
54
+ nanobind_add_module(_core
55
+ src/ogdf/_core.cpp
56
+ src/ogdf/bind_graph.cpp
57
+ src/ogdf/bind_layouts.cpp
58
+ src/ogdf/bind_algorithms.cpp
59
+ src/ogdf/bind_io.cpp
60
+ src/ogdf/bind_generators.cpp)
61
+
62
+ target_include_directories(_core PRIVATE ${OGDF_INCLUDE_DIRS})
63
+ target_link_libraries(_core PRIVATE ${OGDF_LIBRARIES})
64
+
65
+ if(MSVC)
66
+ target_compile_options(_core PRIVATE /W4)
67
+ else()
68
+ target_compile_options(_core PRIVATE -Wall -Wextra)
69
+ endif()
70
+
71
+ install(TARGETS _core DESTINATION ogdf)
72
+
73
+ # Generate a type stub (_core.pyi) so the compiled module is typed for mypy and
74
+ # IDEs. nanobind imports the built module and introspects it at build time.
75
+ nanobind_add_stub(
76
+ _core_stub
77
+ MODULE _core
78
+ OUTPUT _core.pyi
79
+ PYTHON_PATH $<TARGET_FILE_DIR:_core>
80
+ DEPENDS _core
81
+ )
82
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/_core.pyi DESTINATION ogdf)
83
+
84
+ # Ship py.typed alongside the compiled module + stub. In an editable install the
85
+ # pure-Python files (including src/ogdf/py.typed) stay in the source tree, so
86
+ # without this the site-packages ogdf/ dir has _core.pyi but no py.typed marker,
87
+ # and mypy refuses to use the stub.
88
+ install(FILES ${CMAKE_SOURCE_DIR}/src/ogdf/py.typed DESTINATION ogdf)
ogdf_py-0.1.1/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2026 Shakeeb Alireza
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
ogdf_py-0.1.1/Makefile ADDED
@@ -0,0 +1,157 @@
1
+ # Makefile frontend for scikit-build-core project
2
+ # Generated by buildgen for: ogdf
3
+ #
4
+ # This Makefile wraps common build commands for convenience.
5
+ # The actual build is handled by scikit-build-core via pyproject.toml
6
+
7
+ .PHONY: all bootstrap sync build rebuild test demos lint lint-check format format-check typecheck qa clean distclean wheel sdist dist check publish-test publish upgrade coverage coverage-html docs release help
8
+
9
+ # Prebuilt OGDF static library; its presence marks a completed bootstrap.
10
+ OGDF_LIB := thirdparty/ogdf/build/libOGDF.a
11
+
12
+ # Default target
13
+ all: build
14
+
15
+ # Clone OGDF at the pinned tag and build its static libraries from source.
16
+ bootstrap:
17
+ @./scripts/bootstrap_ogdf.sh
18
+
19
+ # Auto-bootstrap: build OGDF only if it has not been built yet.
20
+ $(OGDF_LIB):
21
+ @./scripts/bootstrap_ogdf.sh
22
+
23
+ # Sync environment (initial setup, installs dependencies + package)
24
+ sync: $(OGDF_LIB)
25
+ @uv sync
26
+
27
+ # Build/rebuild the extension after code changes.
28
+ # The distribution is named "ogdf-py" (see pyproject.toml); --refresh-package
29
+ # busts uv's build cache so C++ source edits are actually recompiled.
30
+ build: $(OGDF_LIB)
31
+ @uv sync --reinstall-package ogdf-py --refresh-package ogdf-py
32
+
33
+ # Alias for build
34
+ rebuild: build
35
+
36
+ # Run tests
37
+ test:
38
+ @uv run pytest tests/ -v
39
+
40
+ # Run the illustrative demos (writes SVGs and data files to build/demo-output)
41
+ demos:
42
+ @uv run python demos/run_all.py
43
+
44
+ # Lint with ruff (applies fixes)
45
+ lint:
46
+ @uv run ruff check --fix src/ tests/
47
+
48
+ # Lint with ruff (check only, no fixes)
49
+ lint-check:
50
+ @uv run ruff check src/ tests/
51
+
52
+ # Format with ruff
53
+ format:
54
+ @uv run ruff format src/ tests/
55
+
56
+ # Check formatting without modifying files
57
+ format-check:
58
+ @uv run ruff format --check src/ tests/
59
+
60
+ # Type check with mypy
61
+ typecheck:
62
+ @uv run mypy src/ogdf
63
+
64
+ # Run a full quality assurance check (non-mutating; mirrors CI)
65
+ qa: lint-check format-check typecheck test
66
+
67
+ # Build wheel
68
+ wheel:
69
+ @uv build --wheel
70
+
71
+ # Build source distribution
72
+ sdist:
73
+ @uv build --sdist
74
+
75
+ # Check distributions with twine
76
+ check:
77
+ @uv run twine check dist/*
78
+
79
+ # Build both wheel and sdist
80
+ dist: wheel sdist check
81
+
82
+ # Publish to TestPyPI
83
+ publish-test: check
84
+ @uv run twine upload --repository testpypi dist/*
85
+
86
+ # Publish to PyPI
87
+ publish: check
88
+ @uv run twine upload dist/*
89
+
90
+ # Upgrade all dependencies
91
+ upgrade:
92
+ @uv lock --upgrade
93
+ @uv sync
94
+
95
+ # Run tests with coverage
96
+ coverage:
97
+ @uv run pytest tests/ -v --cov=src/ogdf --cov-report=term-missing
98
+
99
+ # Generate HTML coverage report
100
+ coverage-html:
101
+ @uv run pytest tests/ -v --cov=src/ogdf --cov-report=html
102
+ @echo "Coverage report: htmlcov/index.html"
103
+
104
+ # Build documentation (requires sphinx in dev dependencies)
105
+ docs:
106
+ @uv run sphinx-build -b html docs/ docs/_build/html
107
+
108
+ # Create a release (bump version, tag, push)
109
+ release:
110
+ @echo "Current version: $$(grep '^version' pyproject.toml | head -1)"
111
+ @read -p "New version: " version; sed "s/^version = .*/version = \"$$version\"/" pyproject.toml > pyproject.toml.tmp && mv pyproject.toml.tmp pyproject.toml; git add pyproject.toml; git commit -m "Bump version to $$version"; git tag -a "v$$version" -m "Release $$version"; echo "Tagged v$$version. Run 'git push && git push --tags' to publish."
112
+
113
+ # Clean build artifacts
114
+ clean:
115
+ @rm -rf build/
116
+ @rm -rf dist/
117
+ @rm -rf *.egg-info/
118
+ @rm -rf src/*.egg-info/
119
+ @rm -rf .pytest_cache/
120
+ @find . -name "*.so" -delete
121
+ @find . -name "*.pyd" -delete
122
+ @find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
123
+
124
+ # Clean everything including CMake cache
125
+ distclean: clean
126
+ @rm -rf CMakeCache.txt CMakeFiles/
127
+
128
+ # Show help
129
+ help:
130
+ @echo "Available targets:"
131
+ @echo " all - Build/rebuild the extension (default)"
132
+ @echo " bootstrap - Clone OGDF at the pinned tag and build it from source"
133
+ @echo " sync - Sync environment (initial setup)"
134
+ @echo " build - Rebuild extension after code changes"
135
+ @echo " rebuild - Alias for build"
136
+ @echo " test - Run tests"
137
+ @echo " demos - Run demos (output to build/demo-output)"
138
+ @echo " lint - Lint with ruff (applies fixes)"
139
+ @echo " lint-check - Lint with ruff (check only)"
140
+ @echo " format - Format with ruff"
141
+ @echo " format-check - Check formatting without modifying files"
142
+ @echo " typecheck - Type check with mypy"
143
+ @echo " qa - Run full quality assurance (non-mutating: lint-check, format-check, typecheck, test)"
144
+ @echo " wheel - Build wheel distribution"
145
+ @echo " sdist - Build source distribution"
146
+ @echo " dist - Build both wheel and sdist"
147
+ @echo " check - Check distributions with twine"
148
+ @echo " publish-test - Publish to TestPyPI"
149
+ @echo " publish - Publish to PyPI"
150
+ @echo " upgrade - Upgrade all dependencies"
151
+ @echo " coverage - Run tests with coverage"
152
+ @echo " coverage-html- Generate HTML coverage report"
153
+ @echo " docs - Build documentation with Sphinx"
154
+ @echo " release - Bump version, tag, and prepare release"
155
+ @echo " clean - Remove build artifacts"
156
+ @echo " distclean - Remove all generated files"
157
+ @echo " help - Show this help message"