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
36 changes: 35 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1314,9 +1314,43 @@ jobs:
if: failure()
run: docker compose -f ci/docker-compose.bdd.yml logs --no-color

# Anti-drift gate (S30.03): regenerate the committed beta-stack integration
# manifest from CMake and fail if it differs from what is checked in. Keeps
# docs/generated/beta-stack-manifest.txt honest against the pack source lists
# (the generator reads each pack's INTERFACE_SOURCES). Uses the cpputest-freertos
# image because it exports the FreeRTOS / lwIP / Mbed TLS / FatFs upstream paths,
# so every SolidSyslog::* pack target the manifest references is defined.
verify-manifest:
runs-on: ubuntu-latest
container:
image: ghcr.io/davidcozens/cpputest-freertos:sha-0b93766
options: --user root
env:
GIT_CONFIG_COUNT: 1
GIT_CONFIG_KEY_0: safe.directory
GIT_CONFIG_VALUE_0: '*'

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

- name: Regenerate the beta-stack manifest
shell: bash
run: |
cmake -S . -B build/manifest -DBUILD_TESTING=OFF \
-DSOLIDSYSLOG_MANIFEST_PACKS="LwipRaw;LwipRawDnsResolver;MbedTls;FreeRtos;FatFs" \
-DSOLIDSYSLOG_MANIFEST_OUTPUT="$(pwd)/docs/generated/beta-stack-manifest.txt"

- name: Fail on drift
shell: bash
run: |
git diff --exit-code docs/generated/beta-stack-manifest.txt \
|| { echo "::error::docs/generated/beta-stack-manifest.txt is stale — regenerate with the manifest target (see docs/getting-started.md)"; exit 1; }

summary:
if: always() && github.event_name == 'pull_request'
needs: [build-linux-gcc, build-linux-tunable-override, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-tidy-freertos-plustcp, analyze-tidy-freertos-lwip, analyze-cppcheck, analyze-format, analyze-iwyu, analyze-iwyu-freertos-plustcp, analyze-iwyu-freertos-lwip, bdd-linux-syslog-ng, build-windows-msvc, bdd-windows-otel, integration-linux-openssl, integration-linux-mbedtls, integration-windows-openssl, build-freertos-host-tdd-plustcp, build-freertos-target-plustcp, bdd-freertos-qemu-plustcp, build-freertos-target-lwip, bdd-freertos-qemu-lwip]
needs: [build-linux-gcc, build-linux-tunable-override, build-linux-clang, sanitize-linux-gcc, coverage-linux-gcc, analyze-tidy, analyze-tidy-freertos-plustcp, analyze-tidy-freertos-lwip, analyze-cppcheck, analyze-format, analyze-iwyu, analyze-iwyu-freertos-plustcp, analyze-iwyu-freertos-lwip, bdd-linux-syslog-ng, build-windows-msvc, bdd-windows-otel, integration-linux-openssl, integration-linux-mbedtls, integration-windows-openssl, build-freertos-host-tdd-plustcp, build-freertos-target-plustcp, bdd-freertos-qemu-plustcp, build-freertos-target-lwip, bdd-freertos-qemu-lwip, verify-manifest]
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(LayerGuard)
solidsyslog_enforce_layering()
include(Iwyu)
include(Manifest)

# Default to C11 (for the optional C11 atomics counter), but let an externally
# supplied standard win so the pre-release C99 portability check can build the
Expand Down Expand Up @@ -329,3 +330,10 @@ if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING)
enable_testing()
add_subdirectory(Tests)
endif()

# Integration manifest (S30.03) — generated from the configured targets (Core
# SOURCES + each selected pack's INTERFACE_SOURCES) so the non-CMake
# file/include/-D list can't drift from the build. Runs last, after every pack
# target is defined. `cmake --build <dir> --target manifest` prints it. See
# cmake/Manifest.cmake.
solidsyslog_generate_manifest()
48 changes: 48 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
# Dev Log

## 2026-06-10 — S30.03 generate the integration manifest from CMake

E30's anti-drift story. The non-CMake file/include/`-D` manifest is now generated
from the build instead of hand-listed in the doc, so it can't drift from what the
packs ship.

### Decisions
- **`cmake/Manifest.cmake` reads the targets.** The generator pulls the `.c` lists
from the Core lib's `SOURCES` and each selected pack's `INTERFACE_SOURCES`
(populated in S30.02) — the volatile data comes from the build, never
hand-maintained. Stable per-pack knowledge (which config header each pack needs)
stays as a small curated map.
- **Selection via `SOLIDSYSLOG_MANIFEST_PACKS`** (`;`-list of pack short-names;
empty = every defined `SolidSyslog::<Pack>`). Core is always included. The host
Pattern-A adapters baked into the Core target (POSIX/Windows/OpenSSL/C11 atomics)
are filtered OUT — they're host-build conveniences with no place in an embedded
manifest. Include dirs are de-duplicated.
- **`manifest` target** prints the generated file; generation runs at configure
time (writes `${BINARY_DIR}/solidsyslog-manifest.txt`, and also to
`SOLIDSYSLOG_MANIFEST_OUTPUT` when set). Print uses a `-P` helper
(`cmake/PrintManifest.cmake`) so it works on the 3.16 floor (`cmake -E cat` is
3.18+).
- **Committed sample + gating CI diff** (David's call, accepting a new required
check despite the epic's "don't add gates" note). The beta-stack manifest is
committed at `docs/generated/beta-stack-manifest.txt`; the new `verify-manifest`
CI job regenerates it in the cpputest-freertos image and `git diff --exit-code`s.
getting-started.md now points at that generated file (its hand-listed `.c` blocks
are gone) + shows the regenerate command.
- Beta sample now includes the DNS resolver (matches the FreeRtosLwip BDD target,
which uses DNS) — so the manifest demonstrates the opt-in component +
`-DLWIP_DNS=1`.

### Verification (local, cpputest-freertos image)
- `manifest` target emits a correct manifest; Core list has no Platform/ leak;
include dirs de-duplicated.
- The committed beta sample matches a fresh regeneration (`git diff --exit-code`
clean — the gate passes and would catch real drift).
- Default-selection (`cmake --preset debug`, all 7 packs) configures + full junit
build passes.

### Follow-up for David
- **Add `verify-manifest` to branch-protection required checks** (manual GitHub
step) so the drift gate actually blocks merges.

### Deferred
- JSON manifest variant (story said optional/"if cheap" — skipped for now, no
consumer yet).

## 2026-06-10 — S30.02 consumer-friendly CMake umbrellas + dogfood

E30's CI-risk story. Made the six Pattern-B platform packs consumable in one line
Expand Down
181 changes: 181 additions & 0 deletions cmake/Manifest.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Integration manifest generator (S30.03).
#
# Emits the file / include-dir / -D manifest a NON-CMake integrator (IAR / Keil /
# MPLAB / CCS / hand Makefile) needs to compile SolidSyslog into their own
# project. The .c file lists are read straight from the build targets — the Core
# library's SOURCES and each selected pack's INTERFACE_SOURCES (populated in
# S30.02) — so the manifest can never drift from what the packs actually ship.
#
# Selection: SOLIDSYSLOG_MANIFEST_PACKS is a ;-list of pack short-names (without
# the SolidSyslog:: prefix), e.g. "LwipRaw;LwipRawDnsResolver;MbedTls;FreeRtos;FatFs".
# Empty (the default) means "every SolidSyslog::<Pack> target that this configure
# defined". Core is always included.
#
# Output: written to ${CMAKE_BINARY_DIR}/solidsyslog-manifest.txt at configure
# time, and printed by `cmake --build <dir> --target manifest`. If
# SOLIDSYSLOG_MANIFEST_OUTPUT is set it is ALSO written there (used to refresh the
# committed docs/generated sample, which CI diff-checks for drift).

set(SOLIDSYSLOG_MANIFEST_PACKS "" CACHE STRING
"Packs to include in the generated integration manifest (;-list of short \
names without the SolidSyslog:: prefix). Empty = all defined packs.")
set(SOLIDSYSLOG_MANIFEST_OUTPUT "" CACHE FILEPATH
"Optional extra path to also write the generated manifest to (e.g. the \
committed docs/generated sample).")

# Every pack the generator knows about, in manifest display order. Each carries
# the integrator-supplied config header(s) that pack requires (stable knowledge;
# the volatile .c lists come from the targets, not from here).
set(_SOLIDSYSLOG_MANIFEST_KNOWN_PACKS
FreeRtos PlusTcp LwipRaw LwipRawDnsResolver MbedTls FatFs PlusFat)
set(_SOLIDSYSLOG_MANIFEST_CFG_FreeRtos "FreeRTOSConfig.h")
set(_SOLIDSYSLOG_MANIFEST_CFG_PlusTcp "FreeRTOSConfig.h, FreeRTOSIPConfig.h")
set(_SOLIDSYSLOG_MANIFEST_CFG_LwipRaw "lwipopts.h")
set(_SOLIDSYSLOG_MANIFEST_CFG_LwipRawDnsResolver "lwipopts.h (with LWIP_DNS=1)")
set(_SOLIDSYSLOG_MANIFEST_CFG_MbedTls "mbedtls_config.h")
set(_SOLIDSYSLOG_MANIFEST_CFG_FatFs "ffconf.h")
set(_SOLIDSYSLOG_MANIFEST_CFG_PlusFat "FreeRTOSFATConfig.h")

# Make an absolute path repo-relative for display; pass others through unchanged.
function(_solidsyslog_manifest_relpath OUT_VAR PATH)
set(_p "${PATH}")
if(IS_ABSOLUTE "${_p}")
file(RELATIVE_PATH _p "${CMAKE_SOURCE_DIR}" "${_p}")
endif()
set(${OUT_VAR} "${_p}" PARENT_SCOPE)
endfunction()

# Append the .c sources of an INTERFACE pack target (repo-relative) to OUT_VAR.
function(_solidsyslog_manifest_pack_sources OUT_VAR TARGET)
set(_lines "")
get_target_property(_srcs "${TARGET}" INTERFACE_SOURCES)
if(_srcs)
foreach(_s ${_srcs})
_solidsyslog_manifest_relpath(_rel "${_s}")
string(APPEND _lines "${_rel}\n")
endforeach()
endif()
set(${OUT_VAR} "${_lines}" PARENT_SCOPE)
endfunction()

function(solidsyslog_generate_manifest)
# Resolve the selection: explicit list, else every known pack that exists.
set(_selected "")
if(SOLIDSYSLOG_MANIFEST_PACKS)
set(_selected ${SOLIDSYSLOG_MANIFEST_PACKS})
else()
foreach(_pack ${_SOLIDSYSLOG_MANIFEST_KNOWN_PACKS})
if(TARGET SolidSyslog::${_pack})
list(APPEND _selected ${_pack})
endif()
endforeach()
endif()

string(REPLACE ";" ", " _selected_display "${_selected}")
if(NOT _selected_display)
set(_selected_display "(none — Core only)")
endif()

# --- Header ---------------------------------------------------------------
set(_m "# SolidSyslog integration manifest — GENERATED by CMake (S30.03).\n")
string(APPEND _m "# Do not edit by hand. Regenerate with: cmake --build <build-dir> --target manifest\n")
string(APPEND _m "# Selected packs: ${_selected_display}\n")
string(APPEND _m "#\n")
string(APPEND _m "# A non-CMake integrator (IAR / Keil / MPLAB / CCS / Make) compiles the .c\n")
string(APPEND _m "# files below, with the include dirs on the compiler path, against their own\n")
string(APPEND _m "# config headers. See docs/getting-started.md for the walkthrough.\n\n")

# --- Source files ---------------------------------------------------------
string(APPEND _m "## Source files (.c) — compile all of these\n\n")
string(APPEND _m "# Core (always required):\n")
get_target_property(_core_srcs SolidSyslog SOURCES)
foreach(_s ${_core_srcs})
if(IS_ABSOLUTE "${_s}")
# The host Pattern-A adapters (Posix / Windows / OpenSSL / C11 atomics)
# are baked into the Core target by CMake auto-detect. They are
# host-build conveniences and have no place in a (cross / embedded)
# integration manifest — keep only the portable Core/Source set.
string(FIND "${_s}" "/Core/Source/" _in_core)
if(_in_core EQUAL -1)
continue()
endif()
_solidsyslog_manifest_relpath(_rel "${_s}")
else()
set(_rel "Core/Source/${_s}")
endif()
string(APPEND _m "${_rel}\n")
endforeach()

foreach(_pack ${_selected})
if(NOT TARGET SolidSyslog::${_pack})
message(WARNING
"Manifest: SolidSyslog::${_pack} is not a defined target in this "
"configuration; skipping. (Is its upstream env path set?)")
else()
string(APPEND _m "\n# ${_pack}:\n")
_solidsyslog_manifest_pack_sources(_pack_srcs SolidSyslog::${_pack})
string(APPEND _m "${_pack_srcs}")
endif()
endforeach()

# --- Include directories --------------------------------------------------
string(APPEND _m "\n## Include directories\n\n")
set(_incdirs "Core/Interface" "Core/Source")
foreach(_pack ${_selected})
if(TARGET SolidSyslog::${_pack})
get_target_property(_incs SolidSyslog::${_pack} INTERFACE_INCLUDE_DIRECTORIES)
if(_incs)
foreach(_i ${_incs})
_solidsyslog_manifest_relpath(_rel "${_i}")
list(APPEND _incdirs "${_rel}")
endforeach()
endif()
endif()
endforeach()
list(REMOVE_DUPLICATES _incdirs)
foreach(_d ${_incdirs})
string(APPEND _m "${_d}\n")
endforeach()
string(APPEND _m "# Plus, located in YOUR project: the upstream library include dirs (lwIP,\n")
string(APPEND _m "# Mbed TLS, FreeRTOS, FatFs) and the directory holding your config headers.\n")

# --- Required defines -----------------------------------------------------
string(APPEND _m "\n## Required defines\n\n")
string(APPEND _m "-DSOLIDSYSLOG_USER_TUNABLES_FILE=\"my_tunables.h\" # your tunable overrides (optional)\n")
list(FIND _selected "LwipRawDnsResolver" _has_dns)
if(NOT _has_dns EQUAL -1)
string(APPEND _m "-DLWIP_DNS=1 # required by SolidSyslog::LwipRawDnsResolver\n")
endif()
list(FIND _selected "MbedTls" _has_mbedtls)
if(NOT _has_mbedtls EQUAL -1)
string(APPEND _m "# Mbed TLS: point MBEDTLS_USER_CONFIG_FILE (or MBEDTLS_CONFIG_FILE) at your mbedtls config,\n")
string(APPEND _m "# and use the SAME config when building the mbedTLS library itself.\n")
endif()

# --- Config headers you supply --------------------------------------------
string(APPEND _m "\n## Integrator-supplied config headers\n\n")
foreach(_pack ${_selected})
set(_cfg "${_SOLIDSYSLOG_MANIFEST_CFG_${_pack}}")
if(_cfg)
string(APPEND _m "${_cfg} (${_pack})\n")
endif()
endforeach()

# --- Emit -----------------------------------------------------------------
set(_out "${CMAKE_BINARY_DIR}/solidsyslog-manifest.txt")
file(WRITE "${_out}" "${_m}")
set(SOLIDSYSLOG_MANIFEST_FILE "${_out}" CACHE INTERNAL "Generated manifest path")
if(SOLIDSYSLOG_MANIFEST_OUTPUT)
file(WRITE "${SOLIDSYSLOG_MANIFEST_OUTPUT}" "${_m}")
message(STATUS "Manifest also written to ${SOLIDSYSLOG_MANIFEST_OUTPUT}")
endif()

# `cmake --build <dir> --target manifest` prints the generated file.
if(NOT TARGET manifest)
add_custom_target(manifest
COMMAND ${CMAKE_COMMAND} -DSOLIDSYSLOG_MANIFEST_FILE=${_out}
-P ${CMAKE_SOURCE_DIR}/cmake/PrintManifest.cmake
VERBATIM
COMMENT "SolidSyslog integration manifest (${_selected_display})")
endif()
endfunction()
8 changes: 8 additions & 0 deletions cmake/PrintManifest.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Helper for the `manifest` custom target: print the generated manifest to
# stdout. Run in script mode (cmake -P) so it works on the project's CMake 3.16
# floor (cmake -E cat is 3.18+).
if(NOT DEFINED SOLIDSYSLOG_MANIFEST_FILE OR NOT EXISTS "${SOLIDSYSLOG_MANIFEST_FILE}")
message(FATAL_ERROR "Manifest file not found: ${SOLIDSYSLOG_MANIFEST_FILE}")
endif()
file(READ "${SOLIDSYSLOG_MANIFEST_FILE}" _contents)
message("${_contents}")
Loading
Loading