cmake_minimum_required( VERSION 3.20 )
set(PROJECT_NAME cparse)
project(${PROJECT_NAME} VERSION 1.3.0.2
DESCRIPTION "Small hand-written recursive-descent parser core (Cursor / Parser / Generator)")
# ── Standalone vs embedded detection ─────────────────────────────────────────
# CPARSE_STANDALONE is ON when this CMakeLists is the top-level project and OFF
# when cparse is consumed via add_subdirectory() (e.g. from fson, which is in
# turn embedded in FFS). In embedded mode tests/examples default OFF, the build
# type is left to the host, and the CPack section is skipped so the host's
# packaging is untouched.
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(CPARSE_STANDALONE ON)
else()
set(CPARSE_STANDALONE OFF)
endif()
option(TESTING "Compile Test Targets" ${CPARSE_STANDALONE})
option(EXAMPLE "Compile Example Targets" ${CPARSE_STANDALONE})
# "examples/" (plural): one directory per example (toy-calc, key-value,
# csv-lite), each with its own src/ (buildable CMakeLists.txt) and doc/
# (railroad-diagram .rrd source + .svg). Ships in the release source
# archive like everything else in the tree — see downloads.md.
# Install component for the runtime artefacts (libcparse). A host project may
# override this so the library is staged and deployed with its own components.
set(CPARSE_INSTALL_COMPONENT "cparse" CACHE STRING
"CMake install component for the cparse runtime library")
if(CPARSE_STANDALONE)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
endif()
include(GNUInstallDirs)
set(CMAKE_VERBOSE_MAKEFILE OFF)
set(CMAKE_CXX_STANDARD 20)
if(CPARSE_STANDALONE AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
# ctest is enabled whenever TESTING or EXAMPLE is on — the examples/ smoke
# tests (run each example, diff its output against the walkthrough's
# documented transcript) are plain add_test()/diff, no GTest needed, so they
# must not depend on GTest being found. This has to run BEFORE
# add_subdirectory(examples) below: enable_testing() only affects
# subdirectories added after it.
if(${TESTING} OR ${EXAMPLE})
enable_testing()
endif()
# Setup gTest
if (${TESTING})
find_package(GTest QUIET)
if(${GTest_FOUND})
include_directories(${GTEST_INCLUDE_DIR})
else()
message(STATUS "GTest not found, disabling tests")
set(TESTING OFF)
endif()
endif()
# The project
add_subdirectory(src)
if(${EXAMPLE} AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples")
add_subdirectory(examples)
message(STATUS "The examples directory added")
endif()
if(${TESTING} AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test")
add_subdirectory(test)
message(STATUS "The test directory added")
endif()
# ── API documentation (Doxygen) ─────────────────────────────────────────────
# `cmake --build <dir> --target docs` writes the HTML tree to <dir>/docs/html/.
# That same tree is published at https://cparse.fedem.eu/api (dropped under the
# deployed tenant's static/api/). The hand-written companion is the Reference
# Manual at /reference. Doxygen absent → no `docs` target, nothing else changes.
# Standalone only: an embedding host would collide on the target name.
if(CPARSE_STANDALONE)
find_package(Doxygen QUIET OPTIONAL_COMPONENTS dot)
if(DOXYGEN_FOUND)
set(DOXYGEN_INPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/cparse")
set(DOXYGEN_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/docs")
# The header carries a 4-part version; the Doxygen tree and the release
# archives use the 3-part form.
#
# FORCE is load-bearing, not decorative: set(... CACHE ...) without it
# is a no-op once the cache entry already exists (e.g. ws-release.sh
# reuses build/Release/ across releases), so a version bump would
# silently NOT reach the generated docs — the previous release's
# number would keep showing forever. FORCE means this can no longer be
# overridden with -DDOXYGEN_PROJECT_NUMBER=...; nothing relies on that.
set(DOXYGEN_PROJECT_NUMBER
"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
CACHE STRING "Version string shown in the Doxygen HTML header" FORCE)
if(TARGET Doxygen::dot)
set(DOXYGEN_HAVE_DOT YES)
else()
set(DOXYGEN_HAVE_DOT NO)
endif()
option(CPARSE_DOCS_WARN_AS_ERROR
"Fail the 'docs' build on any Doxygen warning" OFF)
if(CPARSE_DOCS_WARN_AS_ERROR)
set(DOXYGEN_WARN_AS_ERROR FAIL_ON_WARNINGS)
else()
set(DOXYGEN_WARN_AS_ERROR NO)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in"
"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" @ONLY)
add_custom_target(docs
COMMAND ${CMAKE_COMMAND} -E make_directory "${DOXYGEN_OUTPUT_DIR}"
COMMAND ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Doxygen → ${DOXYGEN_OUTPUT_DIR}/html/index.html"
VERBATIM)
else()
message(STATUS "Doxygen not found — 'docs' target unavailable")
endif()
endif()
#
# installation package
#
configure_file(cparse.pc.in ${CMAKE_CURRENT_BINARY_DIR}/cparse.pc @ONLY)
install(TARGETS cparse
LIBRARY
COMPONENT ${CPARSE_INSTALL_COMPONENT}
DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cparse.pc
COMPONENT ${CPARSE_INSTALL_COMPONENT}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
if(CPARSE_STANDALONE)
## create packages
set(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} cparse)
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_RELEASE "1")
set(CPACK_PACKAGE_DESCRIPTION "A small parser library core project")
set(CPACK_PACKAGE_VENDOR "Fedem Software")
set(CPACK_PACKAGE_CONTACT "Fehmi Demiralp <demiralp@fedem.eu>")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_PACKAGING_INSTALL_DIRECTORY ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_HOMEPAGE_URL "https://cparse.fedem.eu")
set(CPACK_PACKAGE_LICENSE "MIT")
set(CPACK_GENERATOR 7Z TGZ ZIP DEB RPM)
# RPM-specific settings
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_COMPONENT_CPARSE_DISPLAY_NAME "CParse Package")
set(CPACK_COMPONENT_CPARSE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
set(CPACK_RPM_CPARSE_PACKAGE_GROUP "Development/Tools")
set(CPACK_RPM_CPARSE_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_CPARSE_PACKAGE_URL "https://cparse.fedem.eu")
set(CPACK_RPM_CPARSE_PACKAGE_ARCHITECTURE "x86_64")
set(CPACK_RPM_CPARSE_PACKAGE_RELEASE ${CPACK_PACKAGE_RELEASE})
set(CPACK_RPM_CPARSE_PACKAGE_REQUIRES "gcc >= 12.0")
set(CPACK_ARCHIVE_CPARSE_FILE_NAME "C${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
# Debian-specific settings
set(CPACK_DEBIAN_PACKAGE_DEPENDS "")
include(CPack)
endif() # CPARSE_STANDALONE