# CMakeLists.txt — Build configuration for the LIN Simulator (C++) # # CMAKE BASICS: # ============= # CMake is a build system generator. It doesn't compile code directly. # Instead, it generates platform-specific build files: # - macOS/Linux: Makefiles (then you run `make`) # - Windows: Visual Studio project files # - Any platform: Ninja build files (faster than Make) # # The workflow is: # 1. mkdir build && cd build # 2. cmake .. ← generates build files from this CMakeLists.txt # 3. cmake --build . ← compiles and links the project # 4. ./lin_simulator ← run the application # # Qt6 integration: # CMake has built-in support for Qt via find_package(Qt6). # Qt needs special preprocessing for its features: # - MOC (Meta-Object Compiler): processes Q_OBJECT macros for signals/slots # - UIC: compiles .ui designer files to C++ headers (we don't use these) # - RCC: compiles resource files (icons, etc.) into the binary cmake_minimum_required(VERSION 3.16) project(LINSimulator VERSION 0.1.0 LANGUAGES CXX DESCRIPTION "LIN Simulator using BabyLIN devices" ) # C++17 standard — required for structured bindings, std::optional, etc. set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ── Qt6 auto-processing ── # These three lines enable Qt's special preprocessors: # AUTOMOC: automatically runs MOC on headers containing Q_OBJECT # AUTOUIC: automatically compiles .ui files (if we had any) # AUTORCC: automatically compiles .qrc resource files set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) # ── Find Qt6 ── # find_package searches for Qt6 on the system. # REQUIRED means CMake will error if Qt6 is not found. # COMPONENTS lists which Qt modules we need: # Widgets: GUI widgets (QMainWindow, QPushButton, etc.) # We'll add SerialPort in Step 5 when we need device communication. find_package(Qt6 REQUIRED COMPONENTS Widgets SerialPort) # ── Main application target ── # qt_add_executable is Qt's wrapper around add_executable. # It handles platform-specific details (macOS app bundle, Windows subsystem, etc.) qt_add_executable(lin_simulator src/main.cpp src/main_window.cpp src/main_window.h src/ldf_parser.cpp src/ldf_parser.h src/connection_manager.cpp src/connection_manager.h src/scheduler.cpp src/scheduler.h ) target_link_libraries(lin_simulator PRIVATE Qt6::Widgets Qt6::SerialPort) # ── Tests ── # We use Qt's built-in test framework (QTest) instead of GoogleTest # because it integrates naturally with Qt's event loop and widgets. # QTest provides: # - QVERIFY(condition): assert a condition is true # - QCOMPARE(actual, expected): assert two values are equal # - QTest::mouseClick(): simulate mouse events # - QTest::keyClick(): simulate keyboard events enable_testing() find_package(Qt6 REQUIRED COMPONENTS Test) qt_add_executable(test_main_window tests/test_main_window.cpp src/main_window.cpp src/main_window.h src/ldf_parser.cpp src/ldf_parser.h src/connection_manager.cpp src/connection_manager.h src/scheduler.cpp src/scheduler.h ) target_link_libraries(test_main_window PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test) target_include_directories(test_main_window PRIVATE src) add_test(NAME test_main_window COMMAND test_main_window) # ── Step 2: LDF Parser tests ── # These test the custom LDF parser (ldf_parser.cpp) in isolation, # without any GUI. C++ equivalent of python/tests/test_ldf_handler.py. # # COMPILE DEFINITIONS: # target_compile_definitions() injects #define macros at compile time. # We use it to pass the sample LDF file path to the test code: # # CMake: target_compile_definitions(... LDF_SAMPLE_PATH="/absolute/path/sample.ldf") # Effect: #define LDF_SAMPLE_PATH "/absolute/path/sample.ldf" ← injected into source # Usage: parseLdf(QString(LDF_SAMPLE_PATH)); ← in test code # # This is how C++ projects pass configuration to code at build time. # Python doesn't need this because it resolves paths at runtime with __file__. # # CMAKE_CURRENT_SOURCE_DIR is a built-in CMake variable pointing to the # directory containing this CMakeLists.txt file (/path/to/cpp/). set(LDF_SAMPLE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../resources/sample.ldf") # Parser unit tests — only needs ldf_parser, no main_window qt_add_executable(test_ldf_parser tests/test_ldf_parser.cpp src/ldf_parser.cpp src/ldf_parser.h ) target_link_libraries(test_ldf_parser PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test) target_include_directories(test_ldf_parser PRIVATE src) target_compile_definitions(test_ldf_parser PRIVATE LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}" ) add_test(NAME test_ldf_parser COMMAND test_ldf_parser) # ── Step 2: LDF Loading GUI integration tests ── # These test the full pipeline: parse LDF → populate MainWindow tables. # C++ equivalent of python/tests/test_ldf_loading.py. # # This target needs BOTH ldf_parser AND main_window because it tests # how the GUI responds to loading an LDF file. Each test target in CMake # is a separate executable — it needs all the source files it depends on. # # WHY DUPLICATE SOURCE FILES IN MULTIPLE TARGETS: # Unlike Python where importing a module shares code, each C++ executable # is compiled independently. test_ldf_loading needs main_window.cpp because # it creates MainWindow objects. We could avoid this with a shared library, # but for a small project, listing sources per target is simpler. qt_add_executable(test_ldf_loading tests/test_ldf_loading.cpp src/main_window.cpp src/main_window.h src/ldf_parser.cpp src/ldf_parser.h src/connection_manager.cpp src/connection_manager.h src/scheduler.cpp src/scheduler.h ) target_link_libraries(test_ldf_loading PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test) target_include_directories(test_ldf_loading PRIVATE src) target_compile_definitions(test_ldf_loading PRIVATE LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}" ) add_test(NAME test_ldf_loading COMMAND test_ldf_loading) # ── Step 3: Signal editing tests ── qt_add_executable(test_signal_editing tests/test_signal_editing.cpp src/main_window.cpp src/main_window.h src/ldf_parser.cpp src/ldf_parser.h src/connection_manager.cpp src/connection_manager.h src/scheduler.cpp src/scheduler.h ) target_link_libraries(test_signal_editing PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test) target_include_directories(test_signal_editing PRIVATE src) target_compile_definitions(test_signal_editing PRIVATE LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}" ) add_test(NAME test_signal_editing COMMAND test_signal_editing) # ── Step 4: Rx real-time display tests ── qt_add_executable(test_rx_realtime tests/test_rx_realtime.cpp src/main_window.cpp src/main_window.h src/ldf_parser.cpp src/ldf_parser.h src/connection_manager.cpp src/connection_manager.h src/scheduler.cpp src/scheduler.h ) target_link_libraries(test_rx_realtime PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test) target_include_directories(test_rx_realtime PRIVATE src) target_compile_definitions(test_rx_realtime PRIVATE LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}" ) add_test(NAME test_rx_realtime COMMAND test_rx_realtime)