本项目的构建系统 CMakeLists.txt 是实现跨平台编译和“一键构建”的核心。它不仅能根据目标平台(Linux PC, Windows, 嵌入式 ARM)链接不同的库,更精妙的是,它能在编译前自动修改 LVGL 的配置文件,使开发者无需手动在 lv_conf.hlv_drv_conf.h 中切换宏定义。

核心设计思想

  1. 平台自动化检测: 通过 CMake 内置变量(如 CMAKE_CROSSCOMPILING, WIN32)自动识别当前是为哪个平台进行编译。
  2. 依赖隔离与预置:
    • 对于 Windows嵌入式 ARM 平台,项目在 libs/ 目录下预置了编译好的静态库 (.a),实现了“开箱即用”,用户无需在系统上安装 SDL2 或 Freetype。
    • 对于 Linux PC 平台,则采用标准的 find_package 方式,使用系统中已安装的开发库,符合 Linux 的开发习惯。
  3. 配置自动化修改: 这是本构建系统最高级的特性。它通过读取、正则替换、重写 lv_conf.hlv_drv_conf.h 文件,自动启用或禁用 SDL, Framebuffer, evdev 等驱动,彻底免去了手动配置的麻烦。

脚本逐段详解

1. 项目基本设置

1
2
3
4
5
6
7
8
9
10
cmake_minimum_required(VERSION 3.5)
project(main C)

if(WIN32)
# ... 路径检查 ...
endif()

set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
  • project(main C): 定义项目名称为 main,语言为 C。
  • if(WIN32): 一个非常贴心的检查。由于 Windows 平台对非 ASCII 字符路径的支持不佳,这里会检测项目路径是否包含中文等字符,并在发现时报错,避免后续难以排查的编译问题。
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY: 指定生成的可执行文件(如 main.exemain)统一存放在项目根目录下的 bin/ 文件夹中,保持根目录整洁。

2. 核心:平台检测与依赖配置

这是实现跨平台链接的关键部分。脚本定义了一个变量 PLATFORM_LIBS,并根据检测到的平台向其填充不同的库。

1
2
3
4
5
6
7
8
9
10
# --- 平台检测与配置 ---
set(TARGET_PLATFORM "unknown")

if(CMAKE_CROSSCOMPILING)
# ... ARM 配置 ...
elseif(WIN32)
# ... Windows 配置 ...
elseif(UNIX AND NOT APPLE)
# ... Linux PC 配置 ...
endif()
  • 嵌入式 ARM (if(CMAKE_CROSSCOMPILING)):

    • 触发条件: 当你在 cmake 命令中使用了 -DCMAKE_TOOLCHAIN_FILE 参数指定交叉编译工具链时,CMAKE_CROSSCOMPILING 变量会自动变为 TRUE
    • 库配置: 它通过 add_library(freetype_local STATIC IMPORTED) 创建了一个名为 freetype_local 的“导入目标”,并将其指向 libs/freetype/lib/arm/libfreetype.a 这个预编译好的静态库。最终将 pthread, freetype_localm (数学库) 添加到 PLATFORM_LIBS 中。
  • Windows (elseif(WIN32)):

    • 触发条件: 在 Windows 系统上编译时自动满足。
    • 库配置: 直接指定 libs/ 目录下预置的 SDL2 和 Freetype 静态库 (.a) 的完整路径,并包含了编译 Windows GUI 程序所需的一系列系统库(如 gdi32, winmm 等)。
  • Linux PC (elseif(UNIX AND NOT APPLE)):

    • 触发条件: 在 Linux 系统上进行本地编译时满足。
    • 库配置: 使用 find_package() 在系统中查找 SDL2, Threads (线程库) 和 Freetype。这要求用户的系统上必须通过包管理器(如 sudo apt-get install libsdl2-dev libfreetype-dev)预先安装好这些开发库。
目标平台检测方式库来源PLATFORM_LIBS 内容
嵌入式 ARMCMAKE_CROSSCOMPILING 为 TRUE项目内置的静态库pthread, freetype_local, m
WindowsWIN32 为 TRUE项目内置的静态库libSDL2.a, libfreetype.a, 系统库…
Linux PCUNIX AND NOT APPLE系统安装的开发库SDL2::SDL2, Threads::Threads, …

3. 自动化核心:动态修改 LVGL 配置文件

这是整个构建脚本最智能的部分。它解决了为不同平台维护不同配置文件的痛点。

1
2
3
4
5
6
# --- 动态修改配置文件 ---
file(READ ${LV_DRV_CONF_PATH} LV_DRV_CONF_CONTENT)
# ...
string(REGEX REPLACE "(#define[ \t]+USE_SDL[ \t]+)" "\\11" LV_DRV_CONF_CONTENT "${LV_DRV_CONF_CONTENT}")
# ...
file(WRITE ${LV_DRV_CONF_PATH} "${LV_DRV_CONF_CONTENT}")
  • 工作流程:

    1. file(READ ...): 将 lv_drv_conf.hlv_conf.h 的全部内容读入 CMake 的字符串变量中。
    2. if(TARGET_PLATFORM ...): 根据上一步检测到的平台,进入不同的逻辑分支。
    3. string(REGEX REPLACE ...): 使用正则表达式查找特定的宏定义行,例如 #define USE_SDL 0#define USE_SDL 1
      • (#define[ \t]+USE_SDL[ \t]+): 这是一个捕获组,匹配宏定义的前半部分。
      • [01]: 匹配当前的设置值,无论是 0 还是 1。
      • "\\11": 这是替换内容。\\1 是对第一个捕获组的回引(即 #define USE_SDL),后面的 1 是新的值。这样就能精确地将宏的值修改为 1,同时保持原有的格式不变。
    4. file(WRITE ...): 将修改后的字符串内容写回到原始文件中。
  • 效果: 当你为 PC 编译时,脚本会自动将 USE_SDL 改为 1USE_FBDEV 改为 0;而当你交叉编译时,则会自动将 USE_SDL 改为 0USE_FBDEV 改为 1。整个过程在 cmake 配置阶段完成,对开发者完全透明。

4. 源码聚合与编译

1
2
include_directories(...)
file(GLOB_RECURSE ALL_SOURCES "lvgl/src/*.c" ...)
  • include_directories: 将所有需要的头文件路径(如 . 根目录, UI/, lvgl/)添加到编译器的搜索路径中。
  • file(GLOB_RECURSE ALL_SOURCES ...): 递归地查找所有指定的 .c 源文件,并将它们的路径列表存入 ALL_SOURCES 变量中。这避免了手动逐一列出所有源文件的繁琐工作。

5. 生成最终可执行文件

1
2
add_executable(${PROJECT_NAME} main.c ... ${ALL_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${PLATFORM_LIBS})
  • add_executable: 创建最终的可执行文件目标(名为 main),并将入口文件 main.c 以及 ALL_SOURCES 变量中收集的所有源文件都加入编译。
  • target_link_libraries: 这是最后一步,将可执行文件与之前根据平台配置好的 PLATFORM_LIBS 变量中的所有库进行链接,生成最终可运行的程序。

通过这些步骤,使这份 CMake 脚本实现了一个高度自动化、可移植且对用户友好的构建环境