CMake Build System Developer Configuration Guide
This document is intended for openHiTLS developers and helps them quickly integrate new features into the pure CMake build system.
1. Overall Build System Architecture
1.1 Three-Layer CMake File Structure (Overview)
openHiTLS CMake files are organized in three layers, each with a clear responsibility:
Layer 1: Global Build Framework (cmake/ directory)
├── CMakeLists.txt ← Root entry, loads all modules below (developers usually don't need to modify)
├── cmake/hitls_options.cmake ← Sole entry for all user-configurable options (option/set)
├── cmake/config.h.in ← Compile-time macro template, input to configure_file; sole entry for all compile-time macros
├── cmake/hitls_define_dependencies.cmake ← Declarations of feature dependencies, parent-child relationships, and optional dependencies
├── cmake/hitls_config_check.cmake ← Dependency validity checks at configuration time
├── cmake/hitls_generate_config_h_file.cmake ← Generates hitls_build_config.h from config.h.in (developers usually don't need to modify)
├── cmake/hitls_compile_options.cmake ← Default global compile/link options (developers usually don't need to modify)
├── cmake/hitls_build_targets.cmake ← Assembles final library targets (bundle / split mode, developers usually don't need to modify)
├── cmake/hitls_collect_feature_macros.cmake ← Collects compile-time macro list (developers usually don't need to modify)
├── cmake/hitls_load_preset.cmake ← Profile loading (full / iso19790 / none, developers usually don't need to modify)
└── cmake/helpers/ ← Internal helper functions (developers usually don't need to modify)
├── hitls_target_helpers.cmake ← hitls_register_objects() registers object libraries
├── hitls_lib_helpers.cmake ← hitls_create_shared/static_library(), etc.
└── hitls_depends_helpers.cmake ← hitls_define_dependency() implementation
Layer 2: Component Top-Level Orchestration (component root directories)
├── bsl/CMakeLists.txt ← add_subdirectory orchestration for BSL component modules
├── crypto/CMakeLists.txt ← add_subdirectory orchestration for Crypto component modules
├── tls/CMakeLists.txt ← add_subdirectory orchestration for TLS component modules
├── pki/CMakeLists.txt ← add_subdirectory orchestration for PKI component modules
└── auth/CMakeLists.txt ← add_subdirectory orchestration for Auth component modules
Layer 3: Module Implementation (algorithm/feature subdirectories)
├── crypto/sha2/CMakeLists.txt ← Declares source file list, creates OBJECT library, registers to global collection
├── crypto/aes/CMakeLists.txt
├── bsl/err/CMakeLists.txt
└── ... (each feature module has its own CMakeLists.txt)
Data Flow: Layer 1 parses user-supplied -D options and determines which components participate in the build → Layer 2 decides which modules participate based on feature switches → Layer 3 registers qualifying source files as OBJECT libraries → Layer 1's hitls_build_targets.cmake assembles all OBJECT libraries into the final shared/static library.
1.2 Files Developers May Need to Modify
When adding a new feature, different files need to be modified depending on the work involved. The following table lists the files that are generally required:
| Scenario | Files to Modify |
|---|---|
Add a feature switch controllable via -D |
cmake/hitls_options.cmake and cmake/config.h.in; if only needed at compile time and not intended for user configuration, modify only config.h.in |
| Add parent-child extension / hard dependency / dependency check for a feature | cmake/hitls_define_dependencies.cmake |
| Add business-layer dependency validity checks (optional, usually for complex dependencies) | cmake/hitls_config_check.cmake |
| Add a new three-layer module directory or modify module orchestration | Layer-2 CMakeLists.txt (e.g., crypto/CMakeLists.txt) |
| Add source files or modify module implementation | Layer-3 CMakeLists.txt (feature module root, e.g., crypto/sha2/CMakeLists.txt) |
Note: Options defined in hitls_options.cmake are user-configurable, while macros in config.h.in are those needed at compile time. They usually need to be modified together to ensure a new feature is correctly configured and used. The difference is: hitls_options.cmake contains both build switches and feature macro switches (e.g., HITLS_BUILD_STATIC to build a static library, HITLS_CRYPTO_SHA256 as a SHA256 feature switch), while config.h.in contains only the final compile-time macros (e.g., HITLS_CRYPTO_SHA256).
2. Scenario-Based Detailed Guide
Adding a Pure-C Algorithm Module (Example: Adding a New SHA3 Hash Module)
- Add the corresponding option to
cmake/hitls_options.cmakeandcmake/config.h.in(to control enabling and disabling of the new feature).
cmake/hitls_options.cmake:
## Md(Hash)
option(HITLS_CRYPTO_MD "MD" OFF)
option(HITLS_CRYPTO_SHA2 "SHA2" OFF)
option(HITLS_CRYPTO_SHA256 "SHA256" OFF)
option(HITLS_CRYPTO_SHA512 "SHA512" OFF)
+ option(HITLS_CRYPTO_SHA3 "SHA3" OFF)
cmake/config.h.in:
// Md(Hash)
#cmakedefine HITLS_CRYPTO_MD
#cmakedefine HITLS_CRYPTO_SHA2
+#cmakedefine HITLS_CRYPTO_SHA3
- Add the dependency relationship for
HITLS_CRYPTO_SHA3incmake/hitls_define_dependencies.cmake(to declare dependency relationships for dependency inference or checking).
## Hash
hitls_define_dependency(HITLS_CRYPTO_MD
DEPS HITLS_CRYPTO
- CHILDREN HITLS_CRYPTO_SHA2
+ CHILDREN HITLS_CRYPTO_SHA2 HITLS_CRYPTO_SHA3
)
hitls_define_dependency(HITLS_CRYPTO_SHA2
DEPS HITLS_CRYPTO_MD
CHILDREN HITLS_CRYPTO_SHA256 HITLS_CRYPTO_SHA512
)
+hitls_define_dependency(HITLS_CRYPTO_SHA3 DEPS HITLS_CRYPTO_MD)
Note: Adding HITLS_CRYPTO_SHA3 to the CHILDREN of HITLS_CRYPTO_MD means that if the user explicitly enables HITLS_CRYPTO_MD, HITLS_CRYPTO_SHA3 will also be automatically enabled and treated as explicitly enabled by the user.
- Add a
CMakeLists.txtfile in the algorithm implementation directorycrypto/sha3/(to compile the current module as an OBJECT library for use in final library target assembly).
crypto/sha3/CMakeLists.txt:
set(_sha3_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/sha3.c
${CMAKE_CURRENT_SOURCE_DIR}/src/noasm_sha3.c
)
add_library(_hitls_crypto_sha3 OBJECT ${_sha3_sources})
target_link_libraries(_hitls_crypto_sha3
PUBLIC
_hitls_crypto_common_include
)
target_include_directories(_hitls_crypto_sha3
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
hitls_register_objects(CRYPTO _hitls_crypto_sha3)
Note: hitls_register_objects registers the current module's OBJECT library into the global collection for assembly into the final library target. It must be called. The first argument indicates the owning component (BSL/CRYPTO/TLS/PKI/AUTH/APPS).
- Add
sha3to the component'scrypto/CMakeLists.txt(to integrate the module into the build system).
crypto/CMakeLists.txt:
if(HITLS_CRYPTO_SHA2)
add_subdirectory(sha2)
endif()
+if(HITLS_CRYPTO_SHA3)
+ add_subdirectory(sha3)
+endif()
Adding an Assembly Implementation to an Algorithm Module (Example: Adding ARMv8 Assembly for SHA3)
- Add the corresponding assembly options to
cmake/hitls_options.cmakeandcmake/config.h.in.
cmake/hitls_options.cmake:
option(HITLS_CRYPTO_SHA2_ASM "SHA2 ASM" OFF)
option(HITLS_CRYPTO_SHA2_ARMV8 "SHA2 ARMv8" OFF)
option(HITLS_CRYPTO_SHA2_X8664 "SHA2 x86_64" OFF)
+option(HITLS_CRYPTO_SHA3_ASM "SHA3 ASM" OFF)
+option(HITLS_CRYPTO_SHA3_ARMV8 "SHA3 ARMv8" OFF)
cmake/config.h.in:
#cmakedefine HITLS_CRYPTO_SHA2_ASM
#cmakedefine HITLS_CRYPTO_SHA2_ARMV8
#cmakedefine HITLS_CRYPTO_SHA2_X8664
+#cmakedefine HITLS_CRYPTO_SHA3_ASM
+#cmakedefine HITLS_CRYPTO_SHA3_ARMV8
- Add the dependency relationship for
HITLS_CRYPTO_SHA3_ARMV8incmake/hitls_define_dependencies.cmake.
hitls_define_dependency(HITLS_CRYPTO_SHA2_ARMV8 DEPS HITLS_CRYPTO_SHA2_ASM)
hitls_define_dependency(HITLS_CRYPTO_SHA2_X8664 DEPS HITLS_CRYPTO_SHA2_ASM)
+hitls_define_dependency(HITLS_CRYPTO_SHA3_ARMV8 DEPS HITLS_CRYPTO_SHA3_ASM)
- Add the conditional source compilation to the
sha3module'scrypto/sha3/CMakeLists.txt.
crypto/sha3/CMakeLists.txt:
set(_sha3_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/sha3.c
- ${CMAKE_CURRENT_SOURCE_DIR}/src/noasm_sha3.c
)
+if(HITLS_CRYPTO_SHA3_ARMV8)
+ list(APPEND _sha3_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/asm/sha3_armv8.S)
+else()
+ list(APPEND _sha3_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/noasm_sha3.c)
+endif()
add_library(_hitls_crypto_sha3 OBJECT ${_sha3_sources})
Adding an Optional Dependency Check for a Feature
- For simple optional dependency checks, add them directly to the corresponding feature dependency entry in
cmake/hitls_define_dependencies.cmake. For example, whenHITLS_CRYPTO_DRBG_HASHis enabled, it needs to check whetherHITLS_CRYPTO_MDis also enabled. Use theDEPS_CHECKparameter:
hitls_define_dependency(HITLS_CRYPTO_DRBG_HASH
DEPS HITLS_CRYPTO_DRBG
+ DEPS_CHECK HITLS_CRYPTO_MD
)
This way, when HITLS_CRYPTO_DRBG_HASH is enabled and HITLS_CRYPTO_MD is not, the system will prompt the user.
Note: The difference between DEPS and DEPS_CHECK: DEPS denotes a required dependency — if it is not enabled, it will be automatically enabled; DEPS_CHECK denotes an optional dependency — if it is not enabled, the user will be prompted but it will not be automatically enabled. Users can suppress the warning with -DHITLS_SKIP_CONFIG_CHECK=ON to skip the check.
- For more complex dependency checks (e.g., conditional dependencies, optional dependency combinations), add them to
cmake/hitls_config_check.cmake(condition + message). For example, whenHITLS_CRYPTO_ECDHorHITLS_CRYPTO_ECDSAis enabled, check that at least one EC curve is also enabled:
if(HITLS_CRYPTO_ECDH OR HITLS_CRYPTO_ECDSA)
if(NOT HITLS_CRYPTO_CURVE_NISTP192 AND NOT HITLS_CRYPTO_CURVE_NISTP224 AND NOT HITLS_CRYPTO_CURVE_NISTP256 AND
NOT HITLS_CRYPTO_CURVE_NISTP384 AND NOT HITLS_CRYPTO_CURVE_NISTP521 AND NOT HITLS_CRYPTO_CURVE_BP256R1 AND
NOT HITLS_CRYPTO_CURVE_BP384R1 AND NOT HITLS_CRYPTO_CURVE_BP512R1)
hitls_add_dependency_warning(
"[HiTLS] The ECDH/ECDSA must work with at least one curve. "
"(HITLS_CRYPTO_ECDH/HITLS_CRYPTO_ECDSA)"
"(HITLS_CRYPTO_CURVE_NISTP192/HITLS_CRYPTO_CURVE_NISTP224/HITLS_CRYPTO_CURVE_NISTP256/"
"HITLS_CRYPTO_CURVE_NISTP384/HITLS_CRYPTO_CURVE_NISTP521/HITLS_CRYPTO_CURVE_BP256R1/"
"HITLS_CRYPTO_CURVE_BP384R1/HITLS_CRYPTO_CURVE_BP512R1)"
)
endif()
endif()
3. Minimal Build Testing
-
Build test: For a newly added feature, it is recommended to enable only that feature during the build test to verify correctness and ensure no unnecessary dependencies have been introduced. You can add
-DHITLS_BUILD_GEN_INFO=ONto generate info files for reviewing the final enabled macros (build/macros.txt) and the source files included in the final library (build/sources.txt). -
Functional test: On top of the build test, it is recommended to write corresponding unit test cases to verify the correctness of the new feature, and to add the corresponding test tasks in CI to ensure the feature remains continuously available.