Add CppBeSsot implementation

This commit is contained in:
2026-02-28 02:47:18 -04:00
parent 6e74924255
commit 2787c7343c
15 changed files with 849 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
include_guard(GLOBAL)
include(CMakeParseArguments)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbDependencyCheck.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbSchemaCheck.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbGenTS.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbGenZod.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbGenCpp.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbGenODB.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbGenSqlDDL.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/dbGenMigrations.cmake")
if(NOT DEFINED CPPBESSOT_WORKDIR)
set(CPPBESSOT_WORKDIR "db" CACHE STRING "CppBeSSOT schema root folder")
endif()
if(NOT DEFINED DB_SCHEMA_VERSION_TO_GENERATE)
set(DB_SCHEMA_VERSION_TO_GENERATE "v1.1" CACHE STRING "Schema version to generate artifacts for")
endif()
if(NOT DEFINED DB_SCHEMA_MIGRATION_VERSION_FROM)
set(DB_SCHEMA_MIGRATION_VERSION_FROM "" CACHE STRING
"Optional source schema version for migration generation (e.g. v1.1)")
endif()
if(NOT DEFINED DB_SCHEMA_MIGRATION_VERSION_TO)
set(DB_SCHEMA_MIGRATION_VERSION_TO "" CACHE STRING
"Optional target schema version for migration generation (e.g. v1.2)")
endif()
if(NOT DEFINED DB_SCHEMA_CHANGES_ARE_ERROR)
option(DB_SCHEMA_CHANGES_ARE_ERROR "Treat dirty schema changes as hard CMake error" OFF)
endif()
if(NOT DEFINED CPPBESSOT_AUTO_ENABLE)
option(CPPBESSOT_AUTO_ENABLE "Auto-register CppBeSSOT targets when this file is included" ON)
endif()
function(_cppbessot_try_link_nlohmann target_name)
# Purpose: Link a target to nlohmann_json when the imported target exists.
# Inputs:
# - target_name: CMake target name to link.
# Outputs:
# - Modifies target link interface; no-op when package target is absent.
if(TARGET nlohmann_json::nlohmann_json)
target_link_libraries(${target_name} PUBLIC nlohmann_json::nlohmann_json)
endif()
endfunction()
function(cppbessot_add_generated_libraries)
# Purpose: Create consumable libraries from generated model and ODB sources.
# Inputs:
# - VERSION (optional named arg): Schema version to consume.
# - DB_SCHEMA_VERSION_TO_GENERATE (fallback): Default schema version.
# Outputs:
# - Library targets (when sources exist):
# - cppBeSsotOpenAiModelGen
# - cppBeSsotOdbSqlite
# - cppBeSsotOdbPgSql
# - Alias targets:
# - cppbessot::openai_model_gen
# - cppbessot::odb_sqlite
# - cppbessot::odb_pgsql
# - Emits warnings if expected source sets are missing.
set(options)
set(one_value_args VERSION)
set(multi_value_args)
cmake_parse_arguments(CPPB "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
if(NOT CPPB_VERSION)
set(CPPB_VERSION "${DB_SCHEMA_VERSION_TO_GENERATE}")
endif()
cppbessot_validate_schema_version("${CPPB_VERSION}")
cppbessot_get_version_dir(_version_dir "${CPPB_VERSION}")
set(_cpp_include_dir "${_version_dir}/generated-cpp-source/include")
file(GLOB _model_include_dirs LIST_DIRECTORIES true "${_cpp_include_dir}/*/model")
file(GLOB _model_sources CONFIGURE_DEPENDS
"${_version_dir}/generated-cpp-source/src/model/*.cpp")
if(_model_sources)
add_library(cppBeSsotOpenAiModelGen STATIC ${_model_sources})
set_target_properties(cppBeSsotOpenAiModelGen PROPERTIES
OUTPUT_NAME "cppBeSsotOpenAiModelGen"
POSITION_INDEPENDENT_CODE ON)
target_include_directories(cppBeSsotOpenAiModelGen PUBLIC "${_cpp_include_dir}")
_cppbessot_try_link_nlohmann(cppBeSsotOpenAiModelGen)
add_library(cppbessot::openai_model_gen ALIAS cppBeSsotOpenAiModelGen)
else()
message(WARNING "No generated C++ model sources found for ${CPPB_VERSION}; skipping libcppBeSsotOpenAiModelGen.")
endif()
file(GLOB _sqlite_odb_sources CONFIGURE_DEPENDS
"${_version_dir}/generated-odb-source/sqlite/*-odb.cxx")
if(_sqlite_odb_sources)
add_library(cppBeSsotOdbSqlite SHARED ${_sqlite_odb_sources})
set_target_properties(cppBeSsotOdbSqlite PROPERTIES
OUTPUT_NAME "cppBeSsotOdbSqlite"
POSITION_INDEPENDENT_CODE ON)
target_include_directories(cppBeSsotOdbSqlite PUBLIC
"${_cpp_include_dir}"
"${_version_dir}/generated-odb-source/sqlite"
${_model_include_dirs})
add_library(cppbessot::odb_sqlite ALIAS cppBeSsotOdbSqlite)
else()
message(WARNING "No generated sqlite ODB sources found for ${CPPB_VERSION}; skipping libcppBeSsotOdbSqlite.")
endif()
file(GLOB _pgsql_odb_sources CONFIGURE_DEPENDS
"${_version_dir}/generated-odb-source/postgre/*-odb.cxx")
if(_pgsql_odb_sources)
add_library(cppBeSsotOdbPgSql SHARED ${_pgsql_odb_sources})
set_target_properties(cppBeSsotOdbPgSql PROPERTIES
OUTPUT_NAME "cppBeSsotOdbPgSql"
POSITION_INDEPENDENT_CODE ON)
target_include_directories(cppBeSsotOdbPgSql PUBLIC
"${_cpp_include_dir}"
"${_version_dir}/generated-odb-source/postgre"
${_model_include_dirs})
add_library(cppbessot::odb_pgsql ALIAS cppBeSsotOdbPgSql)
else()
message(WARNING "No generated postgre ODB sources found for ${CPPB_VERSION}; skipping libcppBeSsotOdbPgSql.")
endif()
endfunction()
function(cppbessot_enable)
# Purpose: Entry-point orchestration for dependency checks, custom generation
# targets, aggregate targets, and generated library registration.
# Inputs:
# - CPPBESSOT_WORKDIR
# - DB_SCHEMA_VERSION_TO_GENERATE
# - DB_SCHEMA_MIGRATION_VERSION_FROM
# - DB_SCHEMA_MIGRATION_VERSION_TO
# - DB_SCHEMA_CHANGES_ARE_ERROR (optional behavior control)
# Outputs:
# - Custom targets:
# - db_check_schema_changes
# - db_gen_ts
# - db_gen_zod
# - db_gen_cpp_headers
# - db_gen_odb_logic
# - db_gen_sql_ddl
# - db_gen_migrations
# - db_gen_orm_serdes_and_zod
# - Generated library targets for selected schema version.
cppbessot_initialize_paths()
cppbessot_validate_schema_version("${DB_SCHEMA_VERSION_TO_GENERATE}")
cppbessot_assert_version_dir_exists("${DB_SCHEMA_VERSION_TO_GENERATE}")
cppbessot_check_dependencies()
cppbessot_add_db_check_schema_changes_target()
cppbessot_add_db_gen_ts_target("${DB_SCHEMA_VERSION_TO_GENERATE}")
cppbessot_add_db_gen_zod_target("${DB_SCHEMA_VERSION_TO_GENERATE}")
cppbessot_add_db_gen_cpp_target("${DB_SCHEMA_VERSION_TO_GENERATE}")
cppbessot_add_db_gen_odb_target("${DB_SCHEMA_VERSION_TO_GENERATE}")
cppbessot_add_db_gen_sql_ddl_target("${DB_SCHEMA_VERSION_TO_GENERATE}")
if(NOT "${DB_SCHEMA_MIGRATION_VERSION_FROM}" STREQUAL ""
AND NOT "${DB_SCHEMA_MIGRATION_VERSION_TO}" STREQUAL "")
cppbessot_validate_schema_version("${DB_SCHEMA_MIGRATION_VERSION_FROM}")
cppbessot_validate_schema_version("${DB_SCHEMA_MIGRATION_VERSION_TO}")
cppbessot_assert_version_dir_exists("${DB_SCHEMA_MIGRATION_VERSION_FROM}")
cppbessot_assert_version_dir_exists("${DB_SCHEMA_MIGRATION_VERSION_TO}")
cppbessot_add_db_gen_migrations_target(
"${DB_SCHEMA_MIGRATION_VERSION_FROM}"
"${DB_SCHEMA_MIGRATION_VERSION_TO}")
else()
add_custom_target(db_gen_migrations
COMMAND "${CMAKE_COMMAND}" -E echo
"Set DB_SCHEMA_MIGRATION_VERSION_FROM and DB_SCHEMA_MIGRATION_VERSION_TO to enable migration generation."
COMMAND "${CMAKE_COMMAND}" -E false
VERBATIM
)
set_target_properties(db_gen_migrations PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
add_custom_target(db_gen_orm_serdes_and_zod)
add_dependencies(db_gen_orm_serdes_and_zod
db_gen_ts
db_gen_zod
db_gen_cpp_headers
db_gen_odb_logic
db_gen_sql_ddl)
set_target_properties(db_gen_orm_serdes_and_zod PROPERTIES EXCLUDE_FROM_ALL TRUE)
cppbessot_add_generated_libraries(VERSION "${DB_SCHEMA_VERSION_TO_GENERATE}")
endfunction()
if(CPPBESSOT_AUTO_ENABLE)
cppbessot_enable()
endif()

21
cmake/cppbessot/README.md Normal file
View File

@@ -0,0 +1,21 @@
# CppBeSSOT CMake Module
## Quick include from a parent project
```cmake
# Optional overrides before include:
set(CPPBESSOT_WORKDIR "db")
set(DB_SCHEMA_VERSION_TO_GENERATE "v1.1")
set(DB_SCHEMA_MIGRATION_VERSION_FROM "v1.1")
set(DB_SCHEMA_MIGRATION_VERSION_TO "v1.2")
include(path/to/cppbessot/cmake/cppbessot/CppBeSSOT.cmake)
```
By default the include auto-registers targets and libraries. To disable auto setup:
```cmake
set(CPPBESSOT_AUTO_ENABLE OFF)
include(path/to/cppbessot/cmake/cppbessot/CppBeSSOT.cmake)
cppbessot_enable()
```

View File

@@ -0,0 +1,119 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
include(CheckIncludeFileCXX)
function(_cppbessot_require_program var_name program_name hint)
# Purpose: Locate an executable and fail with a clear install hint if missing.
# Inputs:
# - var_name: Variable name to store executable path.
# - program_name: Program to search in PATH.
# - hint: Human-readable installation guidance.
# Outputs:
# - <var_name>: Absolute executable path (in current scope).
# - No return value; raises FATAL_ERROR if program is not found.
find_program(${var_name} ${program_name})
if(NOT ${var_name})
message(FATAL_ERROR
"Missing required tool `${program_name}`. ${hint}")
endif()
endfunction()
function(_cppbessot_require_npm_package npm_executable package_name)
# Purpose: Ensure an npm package exists either locally in PROJECT_SOURCE_DIR
# or globally in the active npm installation.
# Inputs:
# - npm_executable: Path to npm.
# - package_name: Package name to validate.
# Outputs:
# - No return value; raises FATAL_ERROR when package is not installed.
execute_process(
COMMAND "${npm_executable}" list --depth=0 "${package_name}"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
RESULT_VARIABLE _local_result
OUTPUT_QUIET
ERROR_QUIET
)
if(NOT _local_result EQUAL 0)
execute_process(
COMMAND "${npm_executable}" list -g --depth=0 "${package_name}"
RESULT_VARIABLE _global_result
OUTPUT_QUIET
ERROR_QUIET
)
else()
set(_global_result 0)
endif()
if(NOT _local_result EQUAL 0 AND NOT _global_result EQUAL 0)
message(FATAL_ERROR
"${package_name} is not installed (not found in local or global npm packages). "
"Install with `npm i -D ${package_name}` (project-local) or `npm i -g ${package_name}` (global).")
endif()
endfunction()
function(_cppbessot_require_npx_package_executable npx_executable package_executable)
# Purpose: Ensure npx can execute a package-provided executable without network
# resolution/download (`--no-install`).
# Inputs:
# - npx_executable: Path to npx.
# - package_executable: Executable name exposed by a package.
# Outputs:
# - No return value; raises FATAL_ERROR if execution fails.
execute_process(
COMMAND "${npx_executable}" --no-install "${package_executable}" --help
RESULT_VARIABLE _exec_result
OUTPUT_QUIET
ERROR_VARIABLE _exec_stderr
)
if(NOT _exec_result EQUAL 0)
message(FATAL_ERROR
"${package_executable} is not available through npx. "
"Ensure the supplying package is installed locally or globally. "
"Underlying error: ${_exec_stderr}")
endif()
endfunction()
function(cppbessot_check_dependencies)
# Purpose: Validate required external tools and nlohmann/json availability.
# Inputs:
# - None (uses PATH/toolchain + optional find_package results).
# Outputs:
# - CPPBESSOT_ODB_EXECUTABLE (PARENT_SCOPE)
# - CPPBESSOT_NPX_EXECUTABLE (PARENT_SCOPE)
# - CPPBESSOT_NPM_EXECUTABLE (PARENT_SCOPE)
# - CPPBESSOT_JAVA_EXECUTABLE (PARENT_SCOPE)
# - CPPBESSOT_GIT_EXECUTABLE (PARENT_SCOPE)
# - CPPBESSOT_OPENAPI_ZOD_AVAILABLE (PARENT_SCOPE)
# - No return value; raises FATAL_ERROR on missing dependencies.
_cppbessot_require_program(CPPBESSOT_ODB_EXECUTABLE odb
"Install ODB compiler and ensure `odb` is in PATH.")
_cppbessot_require_program(CPPBESSOT_NPX_EXECUTABLE npx
"Install Node.js/NPM so `npx` is available.")
_cppbessot_require_program(CPPBESSOT_NPM_EXECUTABLE npm
"Install Node.js/NPM so `npm` is available.")
_cppbessot_require_program(CPPBESSOT_JAVA_EXECUTABLE java
"Install a Java runtime (OpenAPI generator uses Java).")
_cppbessot_require_program(CPPBESSOT_GIT_EXECUTABLE git
"Install Git and ensure it is available in PATH.")
_cppbessot_require_npm_package("${CPPBESSOT_NPM_EXECUTABLE}" "openapi-zod-client")
_cppbessot_require_npx_package_executable("${CPPBESSOT_NPX_EXECUTABLE}" "openapi-zod-client")
find_package(nlohmann_json QUIET)
if(NOT nlohmann_json_FOUND)
check_include_file_cxx("nlohmann/json.hpp" CPPBESSOT_HAS_NLOHMANN_JSON_HEADER)
if(NOT CPPBESSOT_HAS_NLOHMANN_JSON_HEADER)
message(FATAL_ERROR
"nlohmann/json headers were not found. On Ubuntu/Debian: `sudo apt install nlohmann-json3-dev`.")
endif()
endif()
set(CPPBESSOT_ODB_EXECUTABLE "${CPPBESSOT_ODB_EXECUTABLE}" PARENT_SCOPE)
set(CPPBESSOT_NPX_EXECUTABLE "${CPPBESSOT_NPX_EXECUTABLE}" PARENT_SCOPE)
set(CPPBESSOT_NPM_EXECUTABLE "${CPPBESSOT_NPM_EXECUTABLE}" PARENT_SCOPE)
set(CPPBESSOT_JAVA_EXECUTABLE "${CPPBESSOT_JAVA_EXECUTABLE}" PARENT_SCOPE)
set(CPPBESSOT_GIT_EXECUTABLE "${CPPBESSOT_GIT_EXECUTABLE}" PARENT_SCOPE)
set(CPPBESSOT_OPENAPI_ZOD_AVAILABLE TRUE PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,35 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_gen_cpp_target version)
# Purpose: Register C++ model generation target using checked-in templates.
# Inputs:
# - version: Schema version to generate for.
# - CPPBESSOT_NPX_EXECUTABLE: Path to `npx`.
# Outputs:
# - CMake target: `db_gen_cpp_headers` (EXCLUDE_FROM_ALL).
# - Files under `<version>/generated-cpp-source`.
cppbessot_validate_schema_version("${version}")
cppbessot_get_version_dir(_version_dir "${version}")
set(_openapi_file "${_version_dir}/openapi/openapi.yaml")
set(_template_dir "${_version_dir}/openapi/templates/cpp-odb-json")
set(_template_config "${_template_dir}/config.yaml")
set(_output_dir "${_version_dir}/generated-cpp-source")
add_custom_target(db_gen_cpp_headers
COMMAND ${CMAKE_COMMAND} -E make_directory "${_output_dir}"
COMMAND "${CPPBESSOT_NPX_EXECUTABLE}" @openapitools/openapi-generator-cli generate
-i "${_openapi_file}"
-g cpp-restsdk
-t "${_template_dir}"
-c "${_template_config}"
-o "${_output_dir}"
--global-property models
COMMENT "Generating C++ model headers/sources for ${version}"
VERBATIM
)
set_target_properties(db_gen_cpp_headers PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,38 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_gen_migrations_target from_version to_version)
# Purpose: Register migration SQL generation between two schema versions.
# Inputs:
# - from_version: Source schema version (changelog input side).
# - to_version: Target schema version (header/changelog output side).
# - CPPBESSOT_ODB_EXECUTABLE: Path to `odb` compiler.
# Outputs:
# - CMake target: `db_gen_migrations` (EXCLUDE_FROM_ALL).
# - Files under `migrations/<from>-<to>/{sqlite,postgre}`.
cppbessot_validate_schema_version("${from_version}")
cppbessot_validate_schema_version("${to_version}")
if("${from_version}" STREQUAL "${to_version}")
message(FATAL_ERROR "Migration `from` and `to` versions must differ.")
endif()
cppbessot_get_version_dir(_from_dir "${from_version}")
cppbessot_get_version_dir(_to_dir "${to_version}")
cppbessot_abs_path(_workdir "${CPPBESSOT_WORKDIR}")
set(_migration_dir "${_workdir}/migrations/${from_version}-${to_version}")
add_custom_target(db_gen_migrations
COMMAND "${CMAKE_COMMAND}"
-DCPPBESSOT_ODB_EXECUTABLE="${CPPBESSOT_ODB_EXECUTABLE}"
-DCPPBESSOT_FROM_VERSION_DIR="${_from_dir}"
-DCPPBESSOT_TO_VERSION_DIR="${_to_dir}"
-DCPPBESSOT_MIGRATION_DIR="${_migration_dir}"
-P "${CMAKE_CURRENT_LIST_DIR}/scripts/run_odb_migrations.cmake"
COMMENT "Generating DB migrations: ${from_version} -> ${to_version}"
VERBATIM
)
set_target_properties(db_gen_migrations PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,27 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_gen_odb_target version)
# Purpose: Register ODB ORM generation target for sqlite and postgre backends.
# Inputs:
# - version: Schema version to generate for.
# - CPPBESSOT_ODB_EXECUTABLE: Path to `odb` compiler.
# Outputs:
# - CMake target: `db_gen_odb_logic` (EXCLUDE_FROM_ALL).
# - Files under `<version>/generated-odb-source/{sqlite,postgre}`.
cppbessot_validate_schema_version("${version}")
cppbessot_get_version_dir(_version_dir "${version}")
add_custom_target(db_gen_odb_logic
COMMAND "${CMAKE_COMMAND}"
-DCPPBESSOT_ODB_EXECUTABLE="${CPPBESSOT_ODB_EXECUTABLE}"
-DCPPBESSOT_VERSION_DIR="${_version_dir}"
-P "${CMAKE_CURRENT_LIST_DIR}/scripts/run_odb_logic.cmake"
DEPENDS db_gen_cpp_headers
COMMENT "Generating ODB ORM sources for ${version} (sqlite + postgre)"
VERBATIM
)
set_target_properties(db_gen_odb_logic PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,27 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_gen_sql_ddl_target version)
# Purpose: Register SQL DDL snapshot generation target for supported backends.
# Inputs:
# - version: Schema version to generate for.
# - CPPBESSOT_ODB_EXECUTABLE: Path to `odb` compiler.
# Outputs:
# - CMake target: `db_gen_sql_ddl` (EXCLUDE_FROM_ALL).
# - Files under `<version>/generated-sql-ddl/{sqlite,postgre}`.
cppbessot_validate_schema_version("${version}")
cppbessot_get_version_dir(_version_dir "${version}")
add_custom_target(db_gen_sql_ddl
COMMAND "${CMAKE_COMMAND}"
-DCPPBESSOT_ODB_EXECUTABLE="${CPPBESSOT_ODB_EXECUTABLE}"
-DCPPBESSOT_VERSION_DIR="${_version_dir}"
-P "${CMAKE_CURRENT_LIST_DIR}/scripts/run_odb_sql_ddl.cmake"
DEPENDS db_gen_cpp_headers
COMMENT "Generating SQL DDL snapshots for ${version} (sqlite + postgre)"
VERBATIM
)
set_target_properties(db_gen_sql_ddl PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,30 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_gen_ts_target version)
# Purpose: Register TypeScript type generation target from OpenAPI input.
# Inputs:
# - version: Schema version to generate for.
# - CPPBESSOT_NPX_EXECUTABLE: Path to `npx`.
# Outputs:
# - CMake target: `db_gen_ts` (EXCLUDE_FROM_ALL).
# - Files under `<version>/generated-ts-types`.
cppbessot_validate_schema_version("${version}")
cppbessot_get_version_dir(_version_dir "${version}")
set(_openapi_file "${_version_dir}/openapi/openapi.yaml")
set(_output_dir "${_version_dir}/generated-ts-types")
add_custom_target(db_gen_ts
COMMAND ${CMAKE_COMMAND} -E make_directory "${_output_dir}"
COMMAND "${CPPBESSOT_NPX_EXECUTABLE}" @openapitools/openapi-generator-cli generate
-i "${_openapi_file}"
-g typescript-fetch
-o "${_output_dir}"
COMMENT "Generating TypeScript types for ${version}"
VERBATIM
)
set_target_properties(db_gen_ts PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,31 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_gen_zod_target version)
# Purpose: Register Zod schema generation target from OpenAPI input.
# Inputs:
# - version: Schema version to generate for.
# - CPPBESSOT_NPX_EXECUTABLE: Path to `npx`.
# Outputs:
# - CMake target: `db_gen_zod` (EXCLUDE_FROM_ALL).
# - File `<version>/generated-zod/schemas.ts`.
cppbessot_validate_schema_version("${version}")
cppbessot_get_version_dir(_version_dir "${version}")
set(_openapi_file "${_version_dir}/openapi/openapi.yaml")
set(_output_dir "${_version_dir}/generated-zod")
set(_output_file "${_output_dir}/schemas.ts")
add_custom_target(db_gen_zod
COMMAND ${CMAKE_COMMAND} -E make_directory "${_output_dir}"
COMMAND "${CPPBESSOT_NPX_EXECUTABLE}" --no-install openapi-zod-client
--input "${_openapi_file}"
--output "${_output_file}"
--export-schemas
COMMENT "Generating Zod schemas for ${version}"
VERBATIM
)
set_target_properties(db_gen_zod PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,114 @@
include_guard(GLOBAL)
if(NOT DEFINED CPPBESSOT_WORKDIR)
set(CPPBESSOT_WORKDIR "db" CACHE STRING "CppBeSSOT schema root folder, relative to PROJECT_SOURCE_DIR or absolute path")
endif()
function(cppbessot_abs_path out_var input_path)
# Purpose: Resolve a path to an absolute path anchored at PROJECT_SOURCE_DIR
# when the input is relative.
# Inputs:
# - out_var: Name of the parent-scope variable to write.
# - input_path: Relative or absolute input path.
# Outputs:
# - <out_var> (PARENT_SCOPE): Absolute resolved path.
if(IS_ABSOLUTE "${input_path}")
set(_resolved "${input_path}")
else()
set(_resolved "${PROJECT_SOURCE_DIR}/${input_path}")
endif()
get_filename_component(_resolved "${_resolved}" ABSOLUTE)
set(${out_var} "${_resolved}" PARENT_SCOPE)
endfunction()
function(cppbessot_initialize_paths)
# Purpose: Initialize commonly used CppBeSSOT module and workdir paths.
# Inputs:
# - CPPBESSOT_WORKDIR (cache/normal variable): Configured schema root path.
# Outputs:
# - CPPBESSOT_CMAKE_DIR (PARENT_SCOPE): Absolute module directory path.
# - CPPBESSOT_MODULE_ROOT (PARENT_SCOPE): Module root directory path.
# - CPPBESSOT_WORKDIR_ABS (PARENT_SCOPE): Absolute schema root path.
get_filename_component(CPPBESSOT_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}" ABSOLUTE)
get_filename_component(CPPBESSOT_MODULE_ROOT "${CPPBESSOT_CMAKE_DIR}/../.." ABSOLUTE)
cppbessot_abs_path(CPPBESSOT_WORKDIR_ABS "${CPPBESSOT_WORKDIR}")
set(CPPBESSOT_CMAKE_DIR "${CPPBESSOT_CMAKE_DIR}" PARENT_SCOPE)
set(CPPBESSOT_MODULE_ROOT "${CPPBESSOT_MODULE_ROOT}" PARENT_SCOPE)
set(CPPBESSOT_WORKDIR_ABS "${CPPBESSOT_WORKDIR_ABS}" PARENT_SCOPE)
endfunction()
function(cppbessot_require_var var_name)
# Purpose: Fail fast if a required CMake variable is missing/empty.
# Inputs:
# - var_name: Variable name to validate.
# Outputs:
# - No return value; raises FATAL_ERROR on invalid input.
if(NOT DEFINED ${var_name} OR "${${var_name}}" STREQUAL "")
message(FATAL_ERROR "Missing required CMake variable `${var_name}`.")
endif()
endfunction()
function(cppbessot_validate_schema_version version)
# Purpose: Validate schema version format and enforce major >= 1.
# Inputs:
# - version: Expected format `v<major>.<minor>` (e.g. v1.1).
# Outputs:
# - No return value; raises FATAL_ERROR if format is invalid.
if(NOT "${version}" MATCHES "^v([1-9][0-9]*)\\.([0-9]+)$")
message(FATAL_ERROR
"Invalid schema version `${version}`. Expected format `v<major>.<minor>` with major >= 1 (e.g. v1.1, v1.2).")
endif()
endfunction()
function(cppbessot_get_version_parts version out_major out_minor)
# Purpose: Parse schema version string into numeric major/minor components.
# Inputs:
# - version: Schema version string.
# - out_major: Parent-scope variable name for major part.
# - out_minor: Parent-scope variable name for minor part.
# Outputs:
# - <out_major> (PARENT_SCOPE): Major component.
# - <out_minor> (PARENT_SCOPE): Minor component.
cppbessot_validate_schema_version("${version}")
string(REGEX REPLACE "^v([1-9][0-9]*)\\.([0-9]+)$" "\\1" _major "${version}")
string(REGEX REPLACE "^v([1-9][0-9]*)\\.([0-9]+)$" "\\2" _minor "${version}")
set(${out_major} "${_major}" PARENT_SCOPE)
set(${out_minor} "${_minor}" PARENT_SCOPE)
endfunction()
function(cppbessot_get_version_dir out_var version)
# Purpose: Resolve the absolute folder path for a specific schema version.
# Inputs:
# - out_var: Parent-scope variable name to receive the path.
# - version: Schema version string.
# Outputs:
# - <out_var> (PARENT_SCOPE): Absolute `${CPPBESSOT_WORKDIR}/<version>` path.
cppbessot_validate_schema_version("${version}")
cppbessot_abs_path(_workdir "${CPPBESSOT_WORKDIR}")
set(${out_var} "${_workdir}/${version}" PARENT_SCOPE)
endfunction()
function(cppbessot_assert_version_dir_exists version)
# Purpose: Assert that a schema version directory exists on disk.
# Inputs:
# - version: Schema version string.
# Outputs:
# - No return value; raises FATAL_ERROR if directory is missing.
cppbessot_get_version_dir(_version_dir "${version}")
if(NOT IS_DIRECTORY "${_version_dir}")
message(FATAL_ERROR "Schema version folder does not exist: ${_version_dir}")
endif()
endfunction()
function(cppbessot_get_model_headers_glob out_var version)
# Purpose: Build a model-header glob expression for a schema version.
# Inputs:
# - out_var: Parent-scope variable name to receive the glob pattern.
# - version: Schema version string.
# Outputs:
# - <out_var> (PARENT_SCOPE): Glob pattern for generated model headers.
cppbessot_get_version_dir(_version_dir "${version}")
set(${out_var} "${_version_dir}/generated-cpp-source/include/*/model/*.h" PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,26 @@
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
function(cppbessot_add_db_check_schema_changes_target)
# Purpose: Register a manual target that reports git-tracked schema changes.
# Inputs:
# - CPPBESSOT_WORKDIR: Schema root directory (relative or absolute).
# - CPPBESSOT_GIT_EXECUTABLE: Git executable path (set by dependency check).
# Outputs:
# - CMake target: `db_check_schema_changes` (EXCLUDE_FROM_ALL).
# - Runtime warning/send-error from the script when changes are detected.
cppbessot_abs_path(_workdir "${CPPBESSOT_WORKDIR}")
add_custom_target(db_check_schema_changes
COMMAND "${CMAKE_COMMAND}"
-DCPPBESSOT_GIT_EXECUTABLE="${CPPBESSOT_GIT_EXECUTABLE}"
-DCPPBESSOT_WORKDIR_ABS="${_workdir}"
-DCPPBESSOT_PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}"
-P "${CMAKE_CURRENT_LIST_DIR}/scripts/check_schema_changes.cmake"
COMMENT "Checking for schema changes under ${CPPBESSOT_WORKDIR}"
VERBATIM
)
set_target_properties(db_check_schema_changes PROPERTIES EXCLUDE_FROM_ALL TRUE)
endfunction()

View File

@@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.16)
if(NOT DEFINED CPPBESSOT_GIT_EXECUTABLE OR CPPBESSOT_GIT_EXECUTABLE STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_GIT_EXECUTABLE is required")
endif()
if(NOT DEFINED CPPBESSOT_WORKDIR_ABS OR CPPBESSOT_WORKDIR_ABS STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_WORKDIR_ABS is required")
endif()
if(NOT DEFINED CPPBESSOT_PROJECT_SOURCE_DIR OR CPPBESSOT_PROJECT_SOURCE_DIR STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_PROJECT_SOURCE_DIR is required")
endif()
execute_process(
COMMAND "${CPPBESSOT_GIT_EXECUTABLE}" -C "${CPPBESSOT_PROJECT_SOURCE_DIR}" rev-parse --show-toplevel
RESULT_VARIABLE _top_result
OUTPUT_VARIABLE _git_toplevel
ERROR_VARIABLE _top_stderr
)
if(NOT _top_result EQUAL 0)
message(FATAL_ERROR "git rev-parse failed while checking schema changes.\n${_top_stderr}")
endif()
string(STRIP "${_git_toplevel}" _git_toplevel)
file(RELATIVE_PATH _workdir_rel "${_git_toplevel}" "${CPPBESSOT_WORKDIR_ABS}")
if(_workdir_rel MATCHES "^\\.\\.")
set(_workdir_rel "${CPPBESSOT_WORKDIR_ABS}")
endif()
execute_process(
COMMAND "${CPPBESSOT_GIT_EXECUTABLE}" -C "${CPPBESSOT_PROJECT_SOURCE_DIR}" status --porcelain -- "${_workdir_rel}"
RESULT_VARIABLE _result
OUTPUT_VARIABLE _output
ERROR_VARIABLE _stderr
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR "git status failed while checking schema changes.\n${_stderr}")
endif()
string(STRIP "${_output}" _output)
if(NOT _output STREQUAL "")
if(DEFINED DB_SCHEMA_CHANGES_ARE_ERROR AND DB_SCHEMA_CHANGES_ARE_ERROR)
message(SEND_ERROR
"Detected changes under `${CPPBESSOT_WORKDIR_ABS}`. Create a new schema version folder and regenerate artifacts.")
else()
message(WARNING
"Detected changes under `${CPPBESSOT_WORKDIR_ABS}`. Consider creating a new schema version folder and regenerating artifacts.")
endif()
endif()

View File

@@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.16)
if(NOT DEFINED CPPBESSOT_ODB_EXECUTABLE OR CPPBESSOT_ODB_EXECUTABLE STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_ODB_EXECUTABLE is required")
endif()
if(NOT DEFINED CPPBESSOT_VERSION_DIR OR CPPBESSOT_VERSION_DIR STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_VERSION_DIR is required")
endif()
set(_include_dir "${CPPBESSOT_VERSION_DIR}/generated-cpp-source/include")
file(GLOB _headers "${_include_dir}/*/model/*.h")
if(NOT _headers)
message(FATAL_ERROR "No model headers found under ${_include_dir}")
endif()
foreach(_backend IN ITEMS sqlite pgsql)
if(_backend STREQUAL "sqlite")
set(_subdir sqlite)
else()
set(_subdir postgre)
endif()
set(_out_dir "${CPPBESSOT_VERSION_DIR}/generated-odb-source/${_subdir}")
file(MAKE_DIRECTORY "${_out_dir}")
execute_process(
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_include_dir}" --std c++11 -d "${_backend}" -q
-o "${_out_dir}" --changelog-dir "${_out_dir}" ${_headers}
RESULT_VARIABLE _result
OUTPUT_VARIABLE _stdout
ERROR_VARIABLE _stderr
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"ODB ORM generation failed for backend `${_backend}`.\n${_stdout}\n${_stderr}")
endif()
endforeach()

View File

@@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.16)
if(NOT DEFINED CPPBESSOT_ODB_EXECUTABLE OR CPPBESSOT_ODB_EXECUTABLE STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_ODB_EXECUTABLE is required")
endif()
if(NOT DEFINED CPPBESSOT_FROM_VERSION_DIR OR CPPBESSOT_FROM_VERSION_DIR STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_FROM_VERSION_DIR is required")
endif()
if(NOT DEFINED CPPBESSOT_TO_VERSION_DIR OR CPPBESSOT_TO_VERSION_DIR STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_TO_VERSION_DIR is required")
endif()
if(NOT DEFINED CPPBESSOT_MIGRATION_DIR OR CPPBESSOT_MIGRATION_DIR STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_MIGRATION_DIR is required")
endif()
set(_to_include_dir "${CPPBESSOT_TO_VERSION_DIR}/generated-cpp-source/include")
file(GLOB _to_headers "${_to_include_dir}/*/model/*.h")
if(NOT _to_headers)
message(FATAL_ERROR "No target-version headers found under ${_to_include_dir}")
endif()
foreach(_backend IN ITEMS sqlite pgsql)
if(_backend STREQUAL "sqlite")
set(_subdir sqlite)
else()
set(_subdir postgre)
endif()
set(_migration_backend_dir "${CPPBESSOT_MIGRATION_DIR}/${_subdir}")
file(MAKE_DIRECTORY "${_migration_backend_dir}")
foreach(_header IN LISTS _to_headers)
get_filename_component(_name "${_header}" NAME_WE)
set(_in_xml "${CPPBESSOT_FROM_VERSION_DIR}/generated-odb-source/${_subdir}/${_name}.xml")
set(_out_xml "${CPPBESSOT_TO_VERSION_DIR}/generated-odb-source/${_subdir}/${_name}.xml")
if(NOT EXISTS "${_in_xml}")
message(FATAL_ERROR "Missing changelog input for `${_name}`: ${_in_xml}")
endif()
execute_process(
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_to_include_dir}" --std c++11 -d "${_backend}"
--generate-schema --schema-format sql -q
-o "${_migration_backend_dir}"
--changelog-in "${_in_xml}"
--changelog-out "${_out_xml}"
"${_header}"
RESULT_VARIABLE _result
OUTPUT_VARIABLE _stdout
ERROR_VARIABLE _stderr
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"Migration generation failed for `${_name}` backend `${_backend}`.\n${_stdout}\n${_stderr}")
endif()
endforeach()
endforeach()

View File

@@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.16)
if(NOT DEFINED CPPBESSOT_ODB_EXECUTABLE OR CPPBESSOT_ODB_EXECUTABLE STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_ODB_EXECUTABLE is required")
endif()
if(NOT DEFINED CPPBESSOT_VERSION_DIR OR CPPBESSOT_VERSION_DIR STREQUAL "")
message(FATAL_ERROR "CPPBESSOT_VERSION_DIR is required")
endif()
set(_include_dir "${CPPBESSOT_VERSION_DIR}/generated-cpp-source/include")
file(GLOB _headers "${_include_dir}/*/model/*.h")
if(NOT _headers)
message(FATAL_ERROR "No model headers found under ${_include_dir}")
endif()
foreach(_backend IN ITEMS sqlite pgsql)
if(_backend STREQUAL "sqlite")
set(_subdir sqlite)
else()
set(_subdir postgre)
endif()
set(_ddl_dir "${CPPBESSOT_VERSION_DIR}/generated-sql-ddl/${_subdir}")
set(_changelog_dir "${CPPBESSOT_VERSION_DIR}/generated-odb-source/${_subdir}")
file(MAKE_DIRECTORY "${_ddl_dir}")
file(MAKE_DIRECTORY "${_changelog_dir}")
execute_process(
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_include_dir}" --std c++11 -d "${_backend}"
--generate-schema --schema-format sql -q
-o "${_ddl_dir}" --changelog-dir "${_changelog_dir}" ${_headers}
RESULT_VARIABLE _result
OUTPUT_VARIABLE _stdout
ERROR_VARIABLE _stderr
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"ODB SQL DDL generation failed for backend `${_backend}`.\n${_stdout}\n${_stderr}")
endif()
endforeach()