SimplifyC++ Article
8 Foundation and Architecture Language Implementation Project Structure - CMake for Multi-Component Interpreter Proje
#8 Foundation and Architecture: Language Implementation Project Structure -> CMake for Multi-Component Interpreter Projects
github : https://github.com/ForgeLang/LearnSeries
In building a modern interpreter in C++20/23, using a robust and modular build system is essential. The complexity of a language project increases rapidly with the addition of lexical analysis, parsing, semantic checking, runtime evaluation, testing, and tools. This complexity must be managed with a clean build configuration that supports:
Multiple submodules or libraries
Dependency isolation
Modern C++ standards
Easy testing and documentation integration
This section explains how to structure a multi-component interpreter project using CMake, leveraging the latest standards and practices introduced over the past five years, including C++20 modules, target-based builds, toolchain abstraction, and cross-platform compatibility.
1. Why CMake for Language Projects?
CMake has become the standard for large-scale C++ projects due to its flexibility, IDE integration, cross-platform support, and compatibility with modern build systems like Ninja, MSBuild, and Make. It supports:
Out-of-source builds
Fine-grained control over dependencies and targets
Build type separation (Debug, Release, RelWithDebInfo)
Test integration with
CTestor external frameworks (Catch2, doctest)Native support for C++20/23 features, including modules and
std::format
2. High-Level CMake Layout for the Interpreter
Assuming a project directory named ForgeLang, the structure would be:
/ForgeLang├── CMakeLists.txt├── /cmake → CMake helper modules├── /src│ ├── /core│ ├── /lexer│ ├── /parser│ ├── /semantics│ ├── /runtime│ ├── /stdlib│ └── /main├── /tests├── /examples└── /build → Out-of-source build directory3. Root CMakeLists.txt (Top-Level Configuration)
cmake_minimum_required(VERSION 3.25)project(ForgeLang VERSION 0.1 LANGUAGES CXX)
# Require C++23set(CMAKE_CXX_STANDARD 23)set(CMAKE_CXX_STANDARD_REQUIRED ON)set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Use folders in IDEs like Visual Studioset_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Modules pathlist(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Enable warnings and optionsinclude(cmake/CompilerWarnings.cmake)include(cmake/EnableSanitizers.cmake)
# Add source directoriesadd_subdirectory(src/core)add_subdirectory(src/lexer)add_subdirectory(src/parser)add_subdirectory(src/semantics)add_subdirectory(src/runtime)add_subdirectory(src/main)
# Add testsenable_testing()add_subdirectory(tests)This configuration enforces modern standards, organizes the project cleanly, and integrates useful features (warnings, sanitizers, tests).
4. Example Component CMake (e.g., src/lexer/CMakeLists.txt)
add_library(ForgeLexer STATIC Token.cpp Lexer.cpp)
target_include_directories(ForgeLexer PUBLIC ${CMAKE_SOURCE_DIR}/src)target_link_libraries(ForgeLexer PUBLIC ForgeCore)target_compile_features(ForgeLexer PUBLIC cxx_std_23)
set_target_properties(ForgeLexer PROPERTIES FOLDER "ForgeLang/Lexer")Each component is built as a static library (or module, where supported). This improves compilation time and enforces separation of concerns.
5. Shared Core Module (e.g., src/core/CMakeLists.txt)
add_library(ForgeCore STATIC SourceManager.cpp ErrorReporter.cpp Utility.cpp)
target_include_directories(ForgeCore PUBLIC ${CMAKE_SOURCE_DIR}/src)target_compile_features(ForgeCore PUBLIC cxx_std_23)
# Optional sanitizers and warningstarget_link_options(ForgeCore PRIVATE ${SANITIZER_FLAGS})The core library provides foundational types and services for all modules. All other components (lexer, parser, runtime) depend on it.
6. Main Executable Entry Point (src/main/CMakeLists.txt)
add_executable(ForgeLang main.cpp)
target_link_libraries(ForgeLang PRIVATE ForgeCore ForgeLexer ForgeParser ForgeSemantics ForgeRuntime)
target_compile_features(ForgeLang PUBLIC cxx_std_23)set_target_properties(ForgeLang PROPERTIES FOLDER "ForgeLang/Main")This is the main interpreter binary. Additional REPL or debugging tools can be added as separate executables under /tools or /cli.
7. Using C++20 Modules (Experimental Support)
If your compiler supports it (Clang >= 16, MSVC >= 19.34, GCC >= 13):
target_sources(ForgeParser PRIVATE FILE_SET cxx_modules TYPE CXX_MODULES BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES Parser.ixx)Modules allow replacing header files, reduce compile time, and eliminate transitive dependencies. This is especially useful for high-level interpreter APIs.
8. Testing Subsystem (tests/CMakeLists.txt)
add_executable(ForgeTests LexerTests.cpp ParserTests.cpp)
target_link_libraries(ForgeTests PRIVATE ForgeCore ForgeLexer ForgeParser)
add_test(NAME LexerTests COMMAND ForgeTests)Use modern test frameworks that support C++20, such as Catch2 v3, with test discovery integration for IDEs and CI.
9. Build Commands
Assuming you're using the terminal and Ninja:
mkdir build && cd buildcmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..cmake --build .ctest --output-on-failureThis setup supports fast iteration, cross-platform builds, and integration with tools like Clang-Tidy, Valgrind, or sanitizers.
10. IDE Integration and Tooling
CMake integrates with major IDEs:
Visual Studio 2022 supports C++23 and modules
CLion auto-detects
compile_commands.jsonVSCode with CMake Tools and C++ extensions provides excellent workflow
You can configure per-target compiler flags, debugging info, or even cross-compilation profiles using CMake presets.
Conclusion
Using CMake effectively for a multi-component interpreter project enables structured development, fast builds, and extensibility. By dividing components into standalone modules and libraries, leveraging C++20/23 features, and enforcing modern build practices, the interpreter can evolve cleanly from prototype to production.
This project structure reflects modern systems programming expectations and aligns well with the goals of the language we are creating—clarity, safety, and modularity.
What did you think?
Sign in to react or comment.
Comments
0No comments yet.