Skip to content

CMake: Build man pages from markdown#7674

Merged
echoix merged 2 commits into
OSGeo:mainfrom
petrasovaa:cmake-man-md
Jul 14, 2026
Merged

CMake: Build man pages from markdown#7674
echoix merged 2 commits into
OSGeo:mainfrom
petrasovaa:cmake-man-md

Conversation

@petrasovaa

Copy link
Copy Markdown
Contributor

This PR adds markdown documentation generation to the CMake build and switches man page generation from HTML (g.html2man) to the generated markdown (g.md2man), mirroring what #7540 did for Autotools. The CMake and Autotools builds now produce man pages the same way.

Context: Follow-up to #7540. Man pages no longer depend on the HTML build in either build system, which unblocks removing the HTML documentation later.

What changed:

  • generate_docs.cmake runs each tool's --md-description, assembles the page with mkmarkdown.py into docs/mkdocs/source/, and converts the man page from it with g.md2man (all three doc paths: modules, GUI double
    pages, scripts). Images are copied to the markdown tree as well.
  • g.md2man.py/gmd.py are wired into the build and install, replacing g.html2man in the man pipeline (HTML pages themselves are still built).
  • New man/build_manpages.py: converts any markdown page without an up-to-date man page. This is man/Makefile's MANPAGES wildcard ported to CMake (which has no build-time glob) and it produces the index and dynamically named topic_* man pages — the CMake build previously shipped no index man pages at all.
  • The generated docs/mkdocs tree is now installed (previously it was not installed at all).
  • md variants added for build_class_graphical and parser_standard_options in man/CMakeLists.txt.

Fixes to pre-existing CMake doc bugs found along the way:

  • The doc generators ran from the build directory, so the SOURCE CODE footer of every CMake-built page linked to a nonexistent repository path (e.g. .../commits/main/build/display) and showed an "Accessed" fallback date instead of the last commit. The generators now run in the tool source directory like in the Autotools build; the .tmp. usage files land there
    too (gitignored, same as Autotools), and the copy-source-into-build-dir steps became unnecessary.
  • build_class.py was passed the documentation directory as its year argument.

Verification (AI-assisted): full CMake build compared against an Autotools-built tree. Man page sets match modulo environment differences (PDAL/LIBLAS/PostgreSQL availability) and intentional post-#7540 exclusions (index.1, keywords.1); converting the same generated markdown with g.md2man yields byte-identical man pages in both builds.

Out of scope (follow-ups):

  • MkDocs site statics (mkdocs.yml, overrides, index.md, ...) are not copied; building/serving the MkDocs site with CMake is a separate step.
  • build_addon.cmake still uses the HTML pipeline (belongs with the g.extension markdown work).
  • test.raster3d.lib is in NO_HTML_DESCR_TARGETS, so its page lacks the generated usage section (pre-existing, also affects its HTML page).

AI disclosure: implemented and verified by Claude (Fable), including the comparison against the Autotools documentation tree.

Generate the markdown documentation (--md-description + mkmarkdown.py)
in all three doc paths (modules, GUI, scripts) and convert man pages
from it with g.md2man instead of from HTML with g.html2man, mirroring
the Autotools build. Install g.md2man.py and gmd.py, and install the
generated docs/mkdocs tree, which was previously not installed at all.

Add man/build_manpages.py, a sweep run after the index scripts that
converts any markdown page without an up-to-date man page. It ports
man/Makefile's MANPAGES wildcard to CMake, which has no build-time
glob, and produces the index and dynamically named topic_* man pages
that the CMake build previously did not ship.

Run the doc generators in the tool source directory like Autotools
does. This fixes the SOURCE CODE footer in all CMake-built pages,
which previously pointed to a nonexistent build-directory path in the
repository and fell back to an Accessed date instead of the last
commit, and makes the copy-source-into-build-dir steps unnecessary.
Also fix build_class.py receiving the documentation directory as its
year argument.

Verified against an Autotools build: g.md2man output for the same
generated markdown is byte-identical.

Generated with AI assistance (Claude Fable), including the build
verification against the Autotools doc tree.

@wenzeslaus wenzeslaus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aim for parity between the two builds, but this seems to have some gaps. Either one can change, and changing Autotools to make the CMake side more sensible would be okay. With that in mind, I prepared the following points with AI:

  1. build_manpages.py has no web-only exclusions. Autotools man/Makefile filters WEB_ONLY_MD (index, keywords, development_intro, command_line_intro, and the doc/*.md guides except grass_database/projectionintro) out of MANPAGES; the new script converts every *.md it finds. Today the results are equivalent because the CMake build never puts those files into the mkdocs source tree — but the statics follow-up listed in the PR description will silently start producing index.1, keywords.1, etc., regressing the #7540 exclusions. The cleanest fix may be to put the exclusion list into build_manpages.py itself and have the Autotools build call the same script instead of the Makefile MANPAGES/WEB_ONLY_MD machinery — one list, one code path, and the parity holds by construction. If that is left for the statics follow-up, a docstring sentence stating the assumption would do for now.

  2. Generated .md differs from Autotools for pages without --md-description. For non-HTML_DESCR pages (grass_database, projectionintro), md_descr_command is cmake -E echo, which writes a newline into the tmp file; mkmarkdown.py treats "\n" as real content and prepends blank lines. Autotools generates these pages with no tmp file at all (read_file() returns ""), so the installed mkdocs trees are not byte-identical. Two ways to close the gap: cmake -E true > file (empty file) on the CMake side, or make mkmarkdown.py treat whitespace-only generated content as empty — the latter fixes the shared tooling so neither build depends on how the tmp file happens to be created. The same quirk pre-exists on the HTML side; fixing it there too (or leaving HTML alone since it is on the way out) is fine either way.

  3. >= in the mtime check can leave a stale man page. man_file.stat().st_mtime >= md_file.stat().st_mtime skips on equal timestamps, so on filesystems with coarse mtime granularity an .md regenerated in the same second as an earlier conversion is skipped. Using > only costs an occasional redundant conversion, which is the safer direction for a build.

  4. Undeclared byproducts. The custom commands declare only the HTML file as OUTPUT while also producing the .md and .1 files. Consistent with how the man file was (not) handled before, but BYPRODUCTS would help Ninja dependency tracking and clean. Fine as a follow-up.

  5. Redundant per-file installs. install(FILES ... DESTINATION ${GRASS_INSTALL_MKDOCSDIR}/source) for images duplicates the new top-level install(DIRECTORY ${OUTDIR}/${GRASS_INSTALL_MKDOCSDIR}/ ...), since the images are already copied into the OUTDIR tree at build time. Mirrors the existing DOCDIR redundancy, so keeping it for symmetry is defensible — noting it as future cleanup.

  6. Source-tree .tmp. writes. Running the generators in the source directory forecloses read-only-source CMake builds. The in-code comment justifies it well (footer link derivation, gitignored, Autotools does the same) and it fixes genuinely wrong footer links, so I agree with the choice. Long term, teaching the footer machinery to take an explicit source path would let both builds stop writing into the source tree — another case where changing the shared tooling (and Autotools) to fit CMake would be the better end state.

  7. Nit: build_manpages.py has no argv validation, so a missing argument dies with an IndexError traceback. Matches the sibling scripts in man/, but a one-line usage check would be friendlier.

@petrasovaa

Copy link
Copy Markdown
Contributor Author

Thanks — addressed all points:

  1. Added a docstring note in build_manpages.py stating the no-web-only-pages assumption; the exclusion mechanism comes with the MkDocs-statics follow-up (already prepared, using a list derived from the doc/ source dirs like WEB_ONLY_MD). Moving the list into the script and unifying with Autotools is worth considering in that follow-up.
  2. Not reproducible — extract_yaml_header() in mkmarkdown.py strips the extracted content, so a newline-only tmp file is already treated as empty. Verified byte-identical output for grass_database with and without the tmp file, and the installed mkdocs trees match the Autotools build for these pages.
  3. Changed to strict > with a comment.
  4. Added BYPRODUCTS for the md and man files in all three command blocks.
  5. Kept for symmetry with the existing DOCDIR per-file installs; noted as cleanup for the HTML-removal work.
  6. Agreed — noted the explicit-source-path idea for the shared tooling as a future improvement.
  7. Switched to argparse; missing arguments now print usage instead of a traceback.

@wenzeslaus wenzeslaus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@echoix echoix merged commit 01ac43d into OSGeo:main Jul 14, 2026
27 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in GRASS Markdown Documentation Jul 14, 2026
@github-actions github-actions Bot added this to the 8.6.0 milestone Jul 14, 2026
@petrasovaa petrasovaa deleted the cmake-man-md branch July 14, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CMake docs Python Related code is in Python

Projects

Development

Successfully merging this pull request may close these issues.

3 participants