本项目的构建系统 CMakeLists.txt
是实现跨平台编译和“一键构建”的核心。它不仅能根据目标平台(Linux PC, Windows, 嵌入式 ARM)链接不同的库,更精妙的是,它能在编译前自动修改 LVGL 的配置文件,使开发者无需手动在 lv_conf.h
和 lv_drv_conf.h
中切换宏定义。
核心设计思想
- 平台自动化检测: 通过 CMake 内置变量(如
CMAKE_CROSSCOMPILING
,WIN32
)自动识别当前是为哪个平台进行编译。 - 依赖隔离与预置:
- 对于 Windows 和 嵌入式 ARM 平台,项目在
libs/
目录下预置了编译好的静态库 (.a
),实现了“开箱即用”,用户无需在系统上安装 SDL2 或 Freetype。 - 对于 Linux PC 平台,则采用标准的
find_package
方式,使用系统中已安装的开发库,符合 Linux 的开发习惯。
- 对于 Windows 和 嵌入式 ARM 平台,项目在
- 配置自动化修改: 这是本构建系统最高级的特性。它通过读取、正则替换、重写
lv_conf.h
和lv_drv_conf.h
文件,自动启用或禁用 SDL, Framebuffer, evdev 等驱动,彻底免去了手动配置的麻烦。
脚本逐段详解
1. 项目基本设置
1 | cmake_minimum_required(VERSION 3.5) |
project(main C)
: 定义项目名称为main
,语言为 C。if(WIN32)
: 一个非常贴心的检查。由于 Windows 平台对非 ASCII 字符路径的支持不佳,这里会检测项目路径是否包含中文等字符,并在发现时报错,避免后续难以排查的编译问题。CMAKE_RUNTIME_OUTPUT_DIRECTORY
: 指定生成的可执行文件(如main.exe
或main
)统一存放在项目根目录下的bin/
文件夹中,保持根目录整洁。
2. 核心:平台检测与依赖配置
这是实现跨平台链接的关键部分。脚本定义了一个变量 PLATFORM_LIBS
,并根据检测到的平台向其填充不同的库。
1 | # --- 平台检测与配置 --- |
嵌入式 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_local
和m
(数学库) 添加到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 内容 |
---|---|---|---|
嵌入式 ARM | CMAKE_CROSSCOMPILING 为 TRUE | 项目内置的静态库 | pthread , freetype_local , m |
Windows | WIN32 为 TRUE | 项目内置的静态库 | libSDL2.a , libfreetype.a , 系统库… |
Linux PC | UNIX AND NOT APPLE | 系统安装的开发库 | SDL2::SDL2 , Threads::Threads , … |
3. 自动化核心:动态修改 LVGL 配置文件
这是整个构建脚本最智能的部分。它解决了为不同平台维护不同配置文件的痛点。
1 | # --- 动态修改配置文件 --- |
工作流程:
file(READ ...)
: 将lv_drv_conf.h
和lv_conf.h
的全部内容读入 CMake 的字符串变量中。if(TARGET_PLATFORM ...)
: 根据上一步检测到的平台,进入不同的逻辑分支。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,同时保持原有的格式不变。
file(WRITE ...)
: 将修改后的字符串内容写回到原始文件中。
效果: 当你为 PC 编译时,脚本会自动将
USE_SDL
改为1
,USE_FBDEV
改为0
;而当你交叉编译时,则会自动将USE_SDL
改为0
,USE_FBDEV
改为1
。整个过程在cmake
配置阶段完成,对开发者完全透明。
4. 源码聚合与编译
1 | include_directories(...) |
include_directories
: 将所有需要的头文件路径(如.
根目录,UI/
,lvgl/
)添加到编译器的搜索路径中。file(GLOB_RECURSE ALL_SOURCES ...)
: 递归地查找所有指定的.c
源文件,并将它们的路径列表存入ALL_SOURCES
变量中。这避免了手动逐一列出所有源文件的繁琐工作。
5. 生成最终可执行文件
1 | add_executable(${PROJECT_NAME} main.c ... ${ALL_SOURCES}) |
add_executable
: 创建最终的可执行文件目标(名为main
),并将入口文件main.c
以及ALL_SOURCES
变量中收集的所有源文件都加入编译。target_link_libraries
: 这是最后一步,将可执行文件与之前根据平台配置好的PLATFORM_LIBS
变量中的所有库进行链接,生成最终可运行的程序。
通过这些步骤,使这份 CMake 脚本实现了一个高度自动化、可移植且对用户友好的构建环境