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
86 changes: 86 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Docs

# Publishes the GitHub Pages site: the README landing page at / and the
# generated API reference at /api/. Nothing generated is committed - both
# halves are built here on every push to the default branch.
#
# This requires the repository's Pages source to be "GitHub Actions"
# (Settings -> Pages -> Build and deployment -> Source).

on:
push:
branches: [ main, master ]
# A doc comment change lands in the header, so watch that too.
paths:
- 'src/libcmutils.h'
- 'doc/**'
- 'README.md'
- '_config.yml'
- '.github/workflows/docs.yml'
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# One deployment at a time, and never cancel one halfway - a cancelled
# deploy leaves the site in whatever state it reached.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
name: Build the site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install doxygen
run: |
sudo apt-get update
sudo apt-get install -y doxygen

# Runs first, because it creates _site from scratch.
- name: Build the landing page
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site

- name: Build the API reference
working-directory: doc
run: LIBCMUTILS_VERSION=$(cat ../VERSION) doxygen Doxyfile

# The Doxyfile turns every documentation warning on and writes them
# here, so an empty log is the contract. Publishing a reference with
# known gaps in it defeats the point of the setting.
- name: Fail on documentation warnings
run: |
if [ -s doc/doxygen.log ]; then
echo "::error::doxygen reported documentation problems:"
cat doc/doxygen.log
exit 1
fi
echo "doxygen reported no documentation problems"

- name: Place the reference under /api
run: |
mkdir -p _site/api
cp -r doc/html/. _site/api/

- uses: actions/upload-pages-artifact@v3
with:
path: ./_site

deploy:
name: Deploy to Pages
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ build*

# CLion
.idea

# Generated API reference (cmake --build build --target docs)
doc/html/
doc/doxygen.log
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,6 @@ IF ( BUILD_SAMPLES )
MESSAGE ( STATUS "Samples enabled building samples..." )
ADD_SUBDIRECTORY ( samples )
ENDIF ( BUILD_SAMPLES )

# Adds the "docs" target when doxygen is present. It is not part of "all".
ADD_SUBDIRECTORY ( doc )
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,17 @@ Like the tests, every sample returns a failure status when `CMUTIL_Clear()` repo
[`samples/README.md`](samples/README.md) for the full list, the data files each one reads, and which
of them bind ports or reach the network.

## API reference

The sections above are prose. The generated reference is the same API organized for lookup — grouped
by subject, built from the doc comments in `src/libcmutils.h`:

```bash
cd doc && doxygen # or: cmake --build build --target docs
```

Open `doc/html/index.html`. See [doc/README.md](doc/README.md) for what the topics contain.

## Project structure

```
Expand Down Expand Up @@ -1147,6 +1158,9 @@ samples/ One annotated program per feature area
sample_plugin.c Tiny shared library, loaded by sample_15_library
conf/ Configurations, documents and keys the samples read
cmutil_log.jsonc Annotated reference logging configuration
doc/ API reference generated from the header's doc comments
Doxyfile Doxygen configuration
README.md What the reference contains and how to build it
CMakeLists.txt Build definition
vcpkg.json Dependency manifest
VERSION Version string, read at configure time
Expand All @@ -1164,7 +1178,9 @@ Issues and pull requests are welcome at
3. Follow the surrounding style: object types are structs of function pointers, constructors are
`CMUTIL_XxxCreate[Ex]`, and every allocation goes through the `CMAlloc`/`CMFree` family.
4. Keep the public surface in `src/libcmutils.h`, documented with the same doxygen comment style as
its neighbours. Internal helpers belong in `src/functions.h`.
its neighbours — a `@brief`, a `@param` per argument and a `@return` unless it returns `void`.
Internal helpers belong in `src/functions.h`. `cd doc && doxygen` must leave `doc/doxygen.log`
empty; see [doc/README.md](doc/README.md).

Participation is governed by the [Code of Conduct](CODE_OF_CONDUCT.md).

Expand Down
22 changes: 21 additions & 1 deletion _config.yml
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
theme: jekyll-theme-cayman
theme: jekyll-theme-cayman

title: libcmutils
description: Multi-platform C99 utility library

# Jekyll copies everything it is not told to skip, which otherwise mirrors
# the whole source tree onto the site. Only README.md needs rendering; the
# API reference is generated separately and dropped into /api by the Docs
# workflow.
exclude:
- src
- test
- samples
- doc
- build
- CMakeLists.txt
- vcpkg.json
- VERSION
- AUTHORS
- ChangeLog
- NEWS
25 changes: 25 additions & 0 deletions doc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#
# API reference for libcmutils.
#
# Adds a "docs" target when doxygen is available. It is never part of "all":
# the reference is built on request, not on every compile.
#

FIND_PACKAGE ( Doxygen )

IF ( NOT DOXYGEN_FOUND )
MESSAGE ( STATUS "Doxygen not found, the docs target is unavailable" )
RETURN ()
ENDIF ()

# The Doxyfile reads the version out of the environment so that a bare
# "doxygen" run in this directory works too.
ADD_CUSTOM_TARGET ( docs
COMMAND ${CMAKE_COMMAND} -E env
LIBCMUTILS_VERSION=${LIB_VERSION_STRING}
${DOXYGEN_EXECUTABLE} Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating the API reference into doc/html"
VERBATIM )

MESSAGE ( STATUS "Doxygen found, \"docs\" target available" )
100 changes: 100 additions & 0 deletions doc/Doxyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Doxygen configuration for the libcmutils API reference.
#
# Run it from this directory:
#
# cd doc && doxygen
#
# or through the build:
#
# cmake -S . -B build -DBUILD_DOCS=ON && cmake --build build --target docs
#
# Only the settings that matter are listed; everything else keeps its
# default. See doc/README.md for what the output contains.

#---------------------------------------------------------------------------
# Project
#---------------------------------------------------------------------------
PROJECT_NAME = libcmutils
PROJECT_BRIEF = "Multi-platform C99 utility library"
# The build passes the contents of VERSION in through this variable; a bare
# "doxygen" run simply leaves the version blank.
PROJECT_NUMBER = $(LIBCMUTILS_VERSION)

OUTPUT_DIRECTORY =
HTML_OUTPUT = html
GENERATE_LATEX = NO
GENERATE_HTML = YES

#---------------------------------------------------------------------------
# Input
#---------------------------------------------------------------------------
# libcmutils.h is the whole public API - the .c files are implementation and
# are deliberately left out.
INPUT = ../src/libcmutils.h
FILE_PATTERNS = *.h
RECURSIVE = NO
EXAMPLE_PATH = ../samples
EXAMPLE_PATTERNS = *.c
EXAMPLE_RECURSIVE = NO

#---------------------------------------------------------------------------
# What to extract
#---------------------------------------------------------------------------
# This is a C API: no classes, no namespaces, and "typedef struct X X;"
# should read as X rather than as an anonymous struct.
OPTIMIZE_OUTPUT_FOR_C = YES
TYPEDEF_HIDES_STRUCT = YES

# Undocumented entities are left out rather than listed empty, and the
# warnings below say which they were - so the reference never silently
# grows a blank page.
EXTRACT_ALL = NO
EXTRACT_STATIC = NO
HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES

# Declaration order carries meaning in this header: related methods sit
# together and the constructor follows its type.
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_GROUP_NAMES = NO
ALPHABETICAL_INDEX = YES

#---------------------------------------------------------------------------
# Preprocessing
#---------------------------------------------------------------------------
# CMUTIL_API is an export decoration and would otherwise show up in every
# signature. The platform macros are all defined so that the shims each one
# guards are documented too, whichever platform the docs are built on.
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
SKIP_FUNCTION_MACROS = NO
PREDEFINED = CMUTIL_API= \
CMUTIL_STATIC=static \
DOXYGEN=1

#---------------------------------------------------------------------------
# Warnings - these are how documentation gaps get noticed
#---------------------------------------------------------------------------
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = YES
WARN_AS_ERROR = NO
WARN_LOGFILE = doxygen.log

#---------------------------------------------------------------------------
# HTML output
#---------------------------------------------------------------------------
GENERATE_TREEVIEW = YES
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_COLORSTYLE = TOGGLE
SEARCHENGINE = YES
HTML_DYNAMIC_SECTIONS = NO

# graphviz is not required to build these docs.
HAVE_DOT = NO
82 changes: 82 additions & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# API reference

The reference is generated from the doc comments in
[`src/libcmutils.h`](../src/libcmutils.h), which is the whole public API — the
`.c` files are implementation and are deliberately left out.

## Building it

```bash
cd doc && doxygen
```

or through the build, which also fills in the version number:

```bash
cmake -S . -B build
cmake --build build --target docs
```

The `docs` target appears only when doxygen is installed, and it is never part
of `all` — the reference is built on request. Output lands in `doc/html`; open
`doc/html/index.html`. Neither the output nor `doxygen.log` is committed.

Graphviz is not needed. Doxygen 1.9 or newer is expected; older versions still
work but ignore some of the HTML settings.

## What it contains

The front page covers the two things to know before reading anything else: that
an object is a struct of function pointers reached through `CMCall`, and the
`CMUTIL_Init` / `CMUTIL_Clear` lifecycle.

**Topics** is the way in. The API is grouped by subject rather than listed
alphabetically, and within a group the declaration order of the header is kept,
so a type is followed by its methods and then its constructor:

| Topic | Covers |
| --- | --- |
| Fixed width integers, CMBool and the platform shims | `CMBool`, the integer limits, the MSVC and macOS shims |
| The CMCall convention | `CMCall`, `CMUTIL_CALL_NESTED`, `CMUTIL_CALL_SINGLE_EVAL` |
| Initialization and memory operations | `CMUTIL_Init`, `CMUTIL_Clear`, `CMUTIL_Mem`, the allocator macros |
| Threads, locks and synchronization primitives | `CMUTIL_Thread`, `CMUTIL_ThreadPool`, `CMUTIL_Mutex`, `CMUTIL_Cond`, `CMUTIL_Semaphore`, `CMUTIL_RWLock` |
| Arrays, maps, lists and their iterator | `CMUTIL_Array`, `CMUTIL_Map`, `CMUTIL_List`, `CMUTIL_Iterator` |
| Strings, string arrays, byte buffers and charset conversion | `CMUTIL_String`, `CMUTIL_StringArray`, `CMUTIL_ByteBuffer`, `CMUTIL_CSConv`, the `CMUTIL_Str*` helpers |
| XML parsing and the document model | `CMUTIL_XmlNode` and the parsers |
| JSON parsing and the document model | `CMUTIL_Json`, `CMUTIL_JsonObject`, `CMUTIL_JsonArray`, `CMUTIL_JsonValue` |
| Scheduled and repeating tasks | `CMUTIL_Timer`, `CMUTIL_TimerTask` |
| Generic resource pool | `CMUTIL_Pool` |
| Dynamic library loading | `CMUTIL_Library` |
| Files, directories and file streams | `CMUTIL_File`, `CMUTIL_FileList`, `CMUTIL_FileStream` |
| Configuration files | `CMUTIL_Config` |
| Log system, loggers and appenders | `CMUTIL_LogSystem`, the appenders, the `CMLog*` macros |
| Call stack capture | `CMUTIL_StackWalker` |
| Sockets, datagrams and the HTTP and REST clients | `CMUTIL_Socket`, `CMUTIL_ServerSocket`, `CMUTIL_DGramSocket`, `CMUTIL_HttpClient`, `CMUTIL_RestClient` |
| Child process creation and control | `CMUTIL_Process` |
| Block ciphers, RSA, Base64 and secure random | `CMUTIL_BlockCrypto`, `CMUTIL_RSACrypto`, the key types |

For prose rather than a reference, read the [project README](../README.md); for
working code, [`samples/`](../samples) has one annotated program per subject.

## Keeping it honest

The configuration leaves `EXTRACT_ALL` off and turns every documentation
warning on, so an undocumented entity is reported rather than published as a
blank page:

```
WARN_IF_UNDOCUMENTED = YES
WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = YES
```

Warnings go to `doc/doxygen.log`. **It should be empty.** If a run leaves
anything in it, that is a doc comment to fix — a missing `@param`, a `@param`
naming an argument that no longer exists, a missing `@return` — not a message
to ignore.

One thing worth knowing when editing the header: `@typedef` and `@struct` are
Doxygen commands that take a *declaration*, not a name and a description. A
comment sitting directly above the entity already documents it, so a plain
`@brief` is what belongs there; writing `@typedef CMUTIL_Foo Some description`
creates a phantom symbol and leaves the real one undocumented.
Loading
Loading