feat: Projektstruktur nach P1204R0

Layout, Build und Konventionen analog zu mydb und cxx_scaffold_cli:
Header und Quellen nebeneinander in playground/, Unit-Tests als
.test.c/.test.cpp daneben, Integrationstests in tests/.

Geruest-Module counter (C, Unity) und notebook (C++, doctest) zeigen
jede Regel einmal an lauffaehigem Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 15:56:31 +02:00
parent 5b7117b2fa
commit da43859b97
30 changed files with 15829 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
# ---------------------------------------------------------------------------
# playground
#
# Struktur nach P1204R0 "Canonical Project Structure":
# playground/playground/ Quellcode UND Header nebeneinander (kein include/ + src/)
# playground/tests/ nur Integrationstests gegen die oeffentliche API
# *.test.c/.cpp Unit-Tests direkt neben dem getesteten Modul
#
# Include-Konvention: eigene Header immer <playground/foo.h>, nie "foo.h".
# Spitze Klammern durchsuchen nur die -I-Pfade, niemals das Verzeichnis der
# inkludierenden Datei -- ein fehlender Header fliegt damit sofort auf, statt
# dass zufaellig ein gleichnamiger lokaler Header gefunden wird.
#
# Extensions: .h/.c fuer C, .hpp/.cpp fuer C++. Die Extension sagt also die
# Sprache -- in einem gemischten Projekt ist das der Punkt.
# ---------------------------------------------------------------------------
# 3.28 = die CMake-Version aus Ubuntu 24.04 LTS. NICHT auf die lokale 4.4
# hochziehen: genau das macht hello-cpp (4.4), und das laesst sich auf keinem
# LTS-Server mehr konfigurieren.
cmake_minimum_required(VERSION 3.28)
project(playground
VERSION 0.1.0
DESCRIPTION "Experimentierprojekt nach P1204R0"
LANGUAGES C CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# ---------------------------------------------------------------------------
# Sprachstandards: C23 + C++23, keine GNU-Extensions
# ---------------------------------------------------------------------------
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ---------------------------------------------------------------------------
# compile_commands.json: HIER, nicht auf der Kommandozeile.
# Muss vor dem ersten Target stehen, sonst bekommt clangd eine veraltete Datei.
# ---------------------------------------------------------------------------
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# C++20-Modul-Scanning aus. Sonst schreibt Ninja bei CXX_STANDARD 23 ein
# "@CMakeFiles/<target>.dir/<datei>.o.modmap" in jeden Compile-Command, und
# clangd stolpert ueber die Response-Datei, sobald build/ geleert wurde.
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
# Ohne explizites CMAKE_BUILD_TYPE waeren alle -O/-g-Flags leer.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
# Binaries an einen vorhersagbaren Ort, damit die File-API-Artefakte und der
# Neovim-Target-Picker stabile Pfade sehen.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
# ---------------------------------------------------------------------------
# ccache, wenn vorhanden. Ueber COMPILER_LAUNCHER, NICHT ueber die
# Compiler-Shims in ccaches libexec -- die faengt jeden Compiler-Aufruf des
# gesamten Systems ab.
# ---------------------------------------------------------------------------
if(NOT CMAKE_C_COMPILER_LAUNCHER AND NOT CMAKE_CXX_COMPILER_LAUNCHER)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
message(STATUS "ccache: ${CCACHE_PROGRAM}")
endif()
endif()
include(Warnings)
include(Sanitizers)
# ---------------------------------------------------------------------------
# Kernbibliothek - neue Dateien hier manuell eintragen (kein GLOB).
#
# GLOB waere bequem, aber CMake merkt nicht, wenn eine Datei dazukommt: der
# Build bleibt gruen und die neue Datei ist einfach nicht dabei.
#
# *.test.c und *.test.cpp gehoeren NICHT hierher (P1204R0 Regel 7.1) -- die
# werden unten zu eigenen Executables.
# ---------------------------------------------------------------------------
set(PLAYGROUND_SOURCES
playground/counter.c
playground/notebook.cpp
)
set(PLAYGROUND_HEADERS
playground/counter.h
playground/notebook.hpp
playground/details/bits.h
)
add_library(playground_core STATIC ${PLAYGROUND_SOURCES} ${PLAYGROUND_HEADERS})
# Der Include-Root ist das PROJEKTWURZELVERZEICHNIS, nicht playground/playground.
# Nur so loest <playground/counter.h> auf playground/playground/counter.h auf.
target_include_directories(playground_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(playground_core PRIVATE playground_warnings)
# ---------------------------------------------------------------------------
# Programm
# ---------------------------------------------------------------------------
add_executable(playground playground/main.cpp)
target_link_libraries(playground PRIVATE playground_core playground_warnings)
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
enable_testing()
# Fehlt third_party/, bleibt das Projekt konfigurierbar (clangd funktioniert)
# und nur die Unit-Test-Targets entfallen.
set(PLAYGROUND_HAVE_UNITY FALSE)
set(PLAYGROUND_HAVE_DOCTEST FALSE)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/unity/unity.c")
set(PLAYGROUND_HAVE_UNITY TRUE)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/doctest/doctest.h")
set(PLAYGROUND_HAVE_DOCTEST TRUE)
endif()
if(PLAYGROUND_HAVE_UNITY OR PLAYGROUND_HAVE_DOCTEST)
add_subdirectory(third_party)
else()
message(WARNING "third_party/ ist leer -- keine Unit-Test-Targets.")
endif()
# Unit-Test in C: <modul>.test.c -> Executable <modul>.test -> CTest
# "unity.<modul>". Der neotest-Adapter in Neovim verlaesst sich auf genau
# diesen Namen.
function(playground_add_unity_test module)
if(NOT PLAYGROUND_HAVE_UNITY)
return()
endif()
set(target "${module}.test")
add_executable(${target} "playground/${module}.test.c")
target_link_libraries(${target} PRIVATE playground_core unity playground_warnings)
add_test(NAME "unity.${module}" COMMAND ${target})
endfunction()
# Unit-Test in C++: <modul>.test.cpp. doctest_discover_tests() ruft die
# Executable beim Build mit --list-test-cases auf und legt EINEN CTest-Eintrag
# pro TEST_CASE an. Das ist Pflicht, nicht Kosmetik: neotest-ctest kann
# Testnamen nur so auf Quellpositionen abbilden.
function(playground_add_doctest_test module)
if(NOT PLAYGROUND_HAVE_DOCTEST)
return()
endif()
set(target "${module}.test")
add_executable(${target} "playground/${module}.test.cpp")
target_link_libraries(${target} PRIVATE playground_core doctest playground_warnings)
doctest_discover_tests(${target} TEST_PREFIX "doctest.")
endfunction()
playground_add_unity_test(counter)
playground_add_doctest_test(notebook)
# Integrationstests (P1204R0 Regel 7.2): eigenes tests/-Verzeichnis, laufen
# gegen die oeffentliche API -- also genau gegen das, was ein Benutzer bekommt.
add_subdirectory(tests)
# ---------------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------------
install(TARGETS playground RUNTIME DESTINATION bin)
install(DIRECTORY playground/
DESTINATION "include/playground"
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
PATTERN "private" EXCLUDE)
message(STATUS "playground ${PROJECT_VERSION} | C${CMAKE_C_STANDARD} C++${CMAKE_CXX_STANDARD} | "
"${CMAKE_BUILD_TYPE} | ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")