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
106 changes: 97 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,23 @@ concurrency:
jobs:
cpp-matrix:
runs-on: ubuntu-24.04
container:
image: ubuntu:24.04
name: Generate Test Matrix
outputs:
matrix: ${{ steps.cpp-matrix.outputs.matrix }}
llvm-matrix: ${{ steps.llvm-matrix.outputs.llvm-matrix }}
releases-matrix: ${{ steps.releases-matrix.outputs.releases-matrix }}
steps:
- name: Install prerequisites
run: |
set -e
apt-get update
apt-get install -y git ca-certificates curl nodejs npm
if ! command -v node >/dev/null 2>&1 && command -v nodejs >/dev/null 2>&1; then
ln -s /usr/bin/nodejs /usr/bin/node
fi

- name: Checkout
uses: actions/checkout@v4

Expand All @@ -40,7 +51,6 @@ jobs:
apple-clang *
standards: '20'
latest-factors: |
msvc Optimized-Debug
gcc UBSan Coverage
clang UBSan ASan MSan
apple-clang UBSan ASan
Expand All @@ -55,11 +65,6 @@ jobs:
clang: Release
apple-clang: Release
msvc: RelWithDebInfo
msvc Optimized-Debug: Debug
ccflags: |
msvc Optimized-Debug: /Ob1 /O2 /Zi
cxxflags: |
msvc Optimized-Debug: /Ob1 /O2 /Zi
install: |
gcc: git build-essential pkg-config python3 curl openjdk-11-jdk pkg-config libncurses-dev libxml2-utils libxml2-dev
gcc Coverage: git build-essential pkg-config python3 curl openjdk-11-jdk pkg-config libncurses-dev libxml2-utils libxml2-dev lcov
Expand All @@ -70,7 +75,7 @@ jobs:
libcxx-runtimes: libcxx{{#if (ne compiler 'msvc')}};libcxxabi{{/if}}
llvm-runtimes: {{#if (ine use-libcxx 'true') }}{{{ libcxx-runtimes }}}{{/if}}
llvm-hash: dc4cef81d47c7bc4a3c4d58fbacf8a6359683fae
llvm-build-preset-prefix: {{#if optimized-debug}}optimizeddebug{{else}}{{{lowercase build-type}}}{{/if}}
llvm-build-preset-prefix: {{{lowercase build-type}}}
llvm-build-preset-os: {{#if (ieq os 'windows') }}win{{else}}unix{{/if}}
llvm-sanitizer: {{#if (eq compiler 'gcc')}}{{else if ubsan}}-UBSan{{else if asan}}-ASan{{else if msan}}-MSan{{/if}}
llvm-build-preset: {{{ llvm-build-preset-prefix }}}-{{{ llvm-build-preset-os }}}
Expand Down Expand Up @@ -134,6 +139,16 @@ jobs:
contents: write

steps:
- name: Ensure Node
if: matrix.container != '' && env.ACT == 'true'
run: |
set -e
apt-get update
apt-get install -y nodejs npm
if ! command -v node >/dev/null 2>&1 && command -v nodejs >/dev/null 2>&1; then
ln -s /usr/bin/nodejs /usr/bin/node
fi

# We install git if we are using a container because
# containers don't always include git.
# We need git to ensure actions/checkout@v4 will use git and
Expand Down Expand Up @@ -174,6 +189,55 @@ jobs:
compiler: ${{ matrix.compiler }}
version: ${{ matrix.version }}

- name: Configure symbolizer paths
if: matrix.compiler != 'msvc'
shell: bash
run: |
set -e
candidates=()
# 1) Anything on PATH
if command -v llvm-symbolizer >/dev/null 2>&1; then
candidates+=("$(command -v llvm-symbolizer)")
fi
uname_out="$(uname -s || true)"
# 2) Platform-specific common locations
case "$uname_out" in
Darwin)
if xcrun --find llvm-symbolizer >/dev/null 2>&1; then
candidates+=("$(xcrun --find llvm-symbolizer)")
fi
candidates+=("/opt/homebrew/opt/llvm/bin/llvm-symbolizer")
;;
Linux)
for dir in /usr/lib/llvm-* /usr/lib/llvm; do
if [ -x "$dir/bin/llvm-symbolizer" ]; then
candidates+=("$dir/bin/llvm-symbolizer")
fi
done
;;
MINGW*|MSYS*|CYGWIN*)
for dir in "/c/Program Files/LLVM/bin" "/c/ProgramData/chocolatey/lib/llvm/tools/llvm/bin"; do
if [ -x "$dir/llvm-symbolizer.exe" ]; then
candidates+=("$dir/llvm-symbolizer.exe")
fi
done
;;
esac
sym=""
for c in "${candidates[@]}"; do
if [ -n "$c" ] && [ -x "$c" ]; then
sym="$c"
break
fi
done
if [ -n "$sym" ]; then
echo "Using llvm-symbolizer at: $sym"
echo "LLVM_SYMBOLIZER_PATH=$sym" >> "$GITHUB_ENV"
echo "ASAN_SYMBOLIZER_PATH=$sym" >> "$GITHUB_ENV"
else
echo "Warning: llvm-symbolizer not found; ASan stacks may be unsymbolized." >&2
fi

# If apple-clang on macos, select the newest Xcode.
- name: Select Xcode 16.4
if: matrix.compiler == 'apple-clang'
Expand Down Expand Up @@ -484,6 +548,9 @@ jobs:

- name: CMake Workflow
uses: alandefreitas/cpp-actions/cmake-workflow@v1.8.12
env:
# Bump per-test timeout on Windows to avoid CTest default (1500s) killing slow golden suites.
CTEST_TEST_TIMEOUT: ${{ runner.os == 'Windows' && '3600' || '' }}
with:
cmake-version: '>=3.26'
cxxstd: ${{ matrix.cxxstd }}
Expand Down Expand Up @@ -619,8 +686,9 @@ jobs:
done

dir="./build"
lcov --rc lcov_branch_coverage=0 --gcov-tool "$gcov_tool" --directory "$dir" --capture --output-file "$dir/all.info" --ignore-errors inconsistent
lcov --rc lcov_branch_coverage=0 --ignore-errors inconsistent --list "$dir/all.info"
lcov --rc lcov_branch_coverage=0 --gcov-tool "$gcov_tool" --directory "$dir" --capture --output-file "$dir/all.info" --ignore-errors inconsistent,negative --no-external
lcov --rc lcov_branch_coverage=0 --remove "$dir/all.info" "/usr/*" "/lib/*" "/opt/*" --ignore-errors unused,negative --output-file "$dir/all.info"
lcov --rc lcov_branch_coverage=0 --ignore-errors inconsistent,negative --list "$dir/all.info"
echo "file=$(realpath "$dir/all.info")" >> $GITHUB_OUTPUT

- name: Upload Coverage as Artifact
Expand Down Expand Up @@ -661,6 +729,16 @@ jobs:
contents: write

steps:
- name: Ensure Node
if: matrix.container != '' && env.ACT == 'true'
run: |
set -e
apt-get update
apt-get install -y nodejs npm
if ! command -v node >/dev/null 2>&1 && command -v nodejs >/dev/null 2>&1; then
ln -s /usr/bin/nodejs /usr/bin/node
fi

- name: Install packages
uses: alandefreitas/cpp-actions/package-install@v1.8.12
id: package-install
Expand Down Expand Up @@ -1117,6 +1195,16 @@ jobs:
contents: write

steps:
- name: Ensure Node
if: matrix.container != '' && env.ACT == 'true'
run: |
set -e
apt-get update
apt-get install -y nodejs npm
if ! command -v node >/dev/null 2>&1 && command -v nodejs >/dev/null 2>&1; then
ln -s /usr/bin/nodejs /usr/bin/node
fi

# This calculates a couple of variables, which would normally go in to the regular matrix extra-values
# section, but which depend on paths not known at that point.
- name: Resolved Matrix
Expand Down
45 changes: 42 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ option(MRDOCS_INSTALL "Configure install target" ON)
option(MRDOCS_PACKAGE "Build install package" ON)
option(MRDOCS_BUILD_SHARED "Link shared" ${BUILD_SHARED_LIBS})
option(MRDOCS_BUILD_TESTS "Build tests" ${BUILD_TESTING})
option(MRDOCS_BUILD_STRICT_TESTS "Enable strict tests" ON)
option(MRDOCS_REQUIRE_GIT "Git is required: not being able to extract version build is an error" ON)
if (MRDOCS_BUILD_TESTS OR MRDOCS_INSTALL)
option(MRDOCS_BUILD_DOCS "Build documentation" ON)
Expand Down Expand Up @@ -520,10 +521,39 @@ if (MRDOCS_BUILD_TESTS)
)
endforeach ()

#-------------------------------------------------
# Self-documentation test (always run; warn-as-error toggled by strict flag)
#-------------------------------------------------
set(MRDOCS_SELF_DOC_OUTPUT "${CMAKE_BINARY_DIR}/docs/self-reference")
set(MRDOCS_SELF_DOC_TAGFILE "${MRDOCS_SELF_DOC_OUTPUT}/reference.tag.xml")

add_test(NAME mrdocs-self-doc
COMMAND
mrdocs
"${CMAKE_SOURCE_DIR}/CMakeLists.txt"
"--config=${CMAKE_SOURCE_DIR}/docs/mrdocs.yml"
"--output=${MRDOCS_SELF_DOC_OUTPUT}"
--generator=adoc
"--addons=${CMAKE_SOURCE_DIR}/share/mrdocs/addons"
"--stdlib-includes=${LIBCXX_DIR}"
"--stdlib-includes=${STDLIB_INCLUDE_DIR}"
"--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs"
"--tagfile=${MRDOCS_SELF_DOC_TAGFILE}"
--multipage=true
--concurrency=16
--log-level=debug
$<$<BOOL:${MRDOCS_BUILD_STRICT_TESTS}>:--warn-as-error=true>
)

#-------------------------------------------------
# XML lint
#-------------------------------------------------
find_package(LibXml2)
if (MRDOCS_BUILD_STRICT_TESTS)
# Strict mode expects xml-lint to run; require LibXml2.
find_package(LibXml2 REQUIRED)
else()
find_package(LibXml2)
endif()
if (LibXml2_FOUND)
find_package(Java REQUIRED Runtime)
# FindJava
Expand All @@ -541,7 +571,16 @@ if (MRDOCS_BUILD_TESTS)
file(GLOB_RECURSE XML_SOURCES CONFIGURE_DEPENDS test-files/golden-tests/*.xml)
add_test(NAME xml-lint
COMMAND ${LIBXML2_XMLLINT_EXECUTABLE} --dropdtd --noout
--relaxng ${CMAKE_CURRENT_BINARY_DIR}/mrdocs.rng ${XML_SOURCES}
--relaxng ${CMAKE_CURRENT_BINARY_DIR}/mrdocs.rng ${XML_SOURCES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()

#-------------------------------------------------
# YAML schema check
#-------------------------------------------------
if (PYTHON_EXECUTABLE)
add_test(NAME yaml-schema-check
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/util/generate-yaml-schema.py --check
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
endif()
Expand Down Expand Up @@ -589,7 +628,7 @@ if (MRDOCS_BUILD_DOCS)
set(DOCS_BUILD_DIR ${DOCS_SOURCE_DIR}/build/site)
set(DOCS_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/doc/mrdocs/html)

# Add custom target for generating documentation
# Add a custom target for generating documentation
add_custom_target(generate_docs
ALL
COMMAND ${CMAKE_COMMAND} -E echo "Install npm dependencies"
Expand Down
2 changes: 2 additions & 0 deletions docs/antora-playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ antora:
- require: '@antora/lunr-extension' # https://gitlab.com/antora/antora-lunr-extension
index_latest_only: true
- require: '@cppalliance/antora-cpp-reference-extension'
auto-base-url: true
breadcrumbs: true

asciidoc:
attributes:
Expand Down
29 changes: 21 additions & 8 deletions docs/modules/ROOT/pages/contribute.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
This page contains information for contributors to the Mr.Docs project.
It is intended to provide an overview of the codebase and the process of adding new features.

Before sending changes, run `python util/run_all_tests.py`.
This script is treated as a “source of truth” pass that configures the preferred preset, builds, runs golden + unit + lint/schema/self-doc checks, installs into a local `install/<preset>` prefix, and (by default) builds docs using that fresh install.
Not every policy can be enforced programmatically, but this script covers the checks that are automated; prefer it over ad-hoc command sequences.
Use `--skip-docs` to omit docs or `--no-strict` to relax strict warning handling when needed.

== Codebase Overview

The Mr.Docs codebase is divided into several modules:
Expand Down Expand Up @@ -49,19 +54,27 @@ As a last step, `DoGenerateAction` converts the public `Config` settings into a
[#representing_symbols]
== Representing Symbols

MrDocs has many categories of objects, where we utilize polymorphism with a fixed set of valid derived types, including Symbols (functions, classes, and enums), DocComment blocks, template parameters, template arguments, and data types. For each such family, we follow a consistent file layout. Most of these families are defined in the `mrdocs/Metadata` directory.
MrDocs has many categories of objects, where we utilize polymorphism with a fixed set of valid derived types, including Symbols (functions, classes, and enums), DocComment blocks, template parameters, template arguments, and data types.
For each such family, we follow a consistent file layout.
Most of these families are defined in the `mrdocs/Metadata` directory.

Each base class is defined in its own header and, when necessary, implementation file. Each derived class also has its own header and implementation file. Finally, there is a single aggregator header file that includes all the derived headers. This file centralizes logic that requires knowledge of the full set of variants, such as visitors, comparison operators, and other operations that depend on the discriminator.
Each base class is defined in its own header and, when necessary, implementation file.
Each derived class also has its own header and implementation file.
Finally, there is a single aggregator header file that includes all the derived headers.
This file centralizes logic that requires knowledge of the full set of variants, such as visitors, comparison operators, and other operations that depend on the discriminator.

Suppose we have a polymorphic family of `Symbol` objects, with derived types `Function`, `Class`, and `Enum`. The files would be organized as follows:
Suppose we have a polymorphic family of `Symbol` objects, with derived types `Function`, `Class`, and `Enum`.
The files would be organized as follows:

* The `Symbol/SymbolNodes.inc` file defines the possible derived types and is used to generate code via macros.
* The `Symbol/SymbolKind.hpp` file defines the `SymbolKind` enum, which is used as a discriminator for the derived types.
* The base class `Symbol` is defined in `Symbol/BaseSymbol.hpp` and `Symbol/BaseSymbol.cpp` (if needed).
* The available kinds of derived symbols are defined in `Symbol/<Derived>.hpp` and `Symbol/<Derived>.cpp` files, e.g., `Symbol/Function.hpp` and `Symbol/Function.cpp`.
* The `Symbol.hpp` file includes all derived headers and defines operations that require knowledge of all variants, such as visitors and comparison operators.

This pattern keeps the individual derived types self-contained while making cross-variant operations explicit and localized. When adding a new derived type, contributors should create its header and source file alongside the existing ones and update the corresponding aggregator file to register the new variant. This keeps the codebase predictable, avoids scattering logic, and ensures that operations over polymorphic families remain easy to find and maintain.
This pattern keeps the individual derived types self-contained while making cross-variant operations explicit and localized.
When adding a new derived type, contributors should create its header and source file alongside the existing ones and update the corresponding aggregator file to register the new variant.
This keeps the codebase predictable, avoids scattering logic, and ensures that operations over polymorphic families remain easy to find and maintain.

[#extract_symbols]
=== Extracting Symbols
Expand Down Expand Up @@ -139,8 +152,7 @@ This is necessary to generate the full interface for user-defined types.
After running the AST traversal on all translation units, `CorpusImpl::build` contains finalization steps for the `Corpus` object.
At this point, we process C++ constructs that are not directly represented in the AST.

The first finalization step happens in `CorpusImpl::build` (`src/lib/Lib/CorpusImpl.cpp`), where the `Symbol` objects from a single translation unit
are merged into a map containing the merged results from all other TUs.
The first finalization step happens in `CorpusImpl::build` (`src/lib/Lib/CorpusImpl.cpp`), where the `Symbol` objects from a single translation unit are merged into a map containing the merged results from all other TUs.
The merging step is necessary as there may be multiple identical definitions of the same entity.
For instance, this represents the case where a function is declared at different points in the code base and might have different attributes or comments.
At this step, the doc comments are also finalized.
Expand Down Expand Up @@ -267,7 +279,8 @@ Why This Approach:
* Ensures new contributors are not lost when adapting to project style.
* Encourages consistency without rigid automation.

General Advice: When in doubt, copy the style of the surrounding code. The `.clang-format` file is a tool to help you, not a rule to enforce blindly.
General Advice: When in doubt, copy the style of the surrounding code.
The `.clang-format` file is a tool to help you, not a rule to enforce blindly.

The utility script `./util/reformat.sh` can also be used to check a few project invariants, such as header guards and include order.

Expand All @@ -277,4 +290,4 @@ If you find a bug or have a feature request, please open an issue on the MrDocs

If you would like to contribute a feature or bug fix, please open a pull request on the MrDocs GitHub repository: https://github.com/cppalliance/mrdocs/pulls

If you would like to discuss a feature or bug fix before opening a pull request, discussing happen in the `#mrdocs` channel on the Cpplang Slack: https://cpplang.slack.com/
If you would like to discuss a feature or bug fix before opening a pull request, discussing happen in the `#mrdocs` channel on the Cpplang Slack: https://cpplang.slack.com/
Loading
Loading