一、一个典型CMakeLists.txt都包含哪些部分?
👉 标准结构如下:
1. 基本信息
cmake_minimum_required(...)
project(...)
完整示例(推荐):
project(SoC_Sim
VERSION 1.0
DESCRIPTION "SystemC SoC Simulation Platform"
LANGUAGES CXX
)
2. 全局配置
set(...)
option(...)
3. 依赖查找
find_package(...)
include_directories(...)
link_directories(...)
target_include_directories(...)
4. 源码组织
add_library(...)
add_executable(...)
5. 链接关系
target_link_libraries(...)
6. 编译选项
target_compile_options(...)
target_compile_definitions(...)
7. 子模块
add_subdirectory(...)
8. 安装/导出(可选)
install(...)
二、逐块解释
1️⃣ 基本信息(必须有)
cmake_minimum_required(VERSION 3.16)
project(SoC_Sim)
👉 作用:
cmake_minimum_required(VERSION <min>...\
"这个工程至少需要这个版本的 CMake 才能正确构建"。
project(<name>
VERSION \
DESCRIPTION \
LANGUAGES \
整个工程的"身份信息 + 语言环境 + 编译器初始化"
2️⃣ 全局配置(控制工程行为)
set(CMAKE_CXX_STANDARD 17)
1. set(...) ------ 定义变量(最基础)
基本语法
set(<变量名> <值>)
例如:
set(CMAKE_CXX_STANDARD 17)
set(SYSTEMC_HOME "C:/systemc")
常见用法
✅ 字符串 / 路径
set(SYSTEMC_HOME "C:/msys64/home/systemc")
✅ 列表(非常常用)
set(SRC
main.cpp
ddr_ctrl.cpp
noc.cpp
)
👉 等价于一个"数组"
✅ 布尔变量
set(DEBUG ON)
set(USE_DDR OFF)
👉 但注意:这只是普通变量,不是用户可配置项
✅ 缓存变量(重要)
set(SYSTEMC_HOME "C:/systemc" CACHE PATH "SystemC install path")
👉 特点:
-
会出现在
cmake-gui -
用户可以修改
-
会写入缓存(CMakeCache.txt)
变量使用方式
${SYSTEMC_HOME}
作用域(重点)
子目录可以访问父目录变量
但不会反向传递(默认)
👉 类似 RTL 层级作用域
2. option(...) ------ 开关变量(给用户用)
基本语法
option(<变量名> "说明" [默认值])
例如:
option(ENABLE_TRACE "Enable waveform tracing" ON)
它在干什么?
👉 定义一个:
用户可以控制的 ON/OFF 开关
使用方式
if(ENABLE_TRACE)
message("Trace enabled")
endif()
用户如何修改?
cmake -G Ninja .. -DENABLE_TRACE=OFF
👉 或 GUI 工具里勾选
3️⃣ 依赖管理(重点)
方法1:手动路径(你现在用)
include_directories(${SYSTEMC_HOME}/include)
**语法:**include_directories(<dir1> <dir2> ...)
例如:
include_directories(${SYSTEMC_HOME}/include)
它在干什么?
👉 本质:
告诉编译器去哪里找头文件
link_directories(${SYSTEMC_HOME}/lib-linux64)
**语法:**link_directories(<dir1> <dir2> ...)
例如:
link_directories(${SYSTEMC_HOME}/lib-linux64)
它在干什么?
👉 本质:
告诉链接器去哪里找库文件(.so / .a)
方法2:自动查找(更专业)
find_package(SystemC REQUIRED)
**语法:**find_package(<PackageName> REQUIRED VERSION x.x COMPONENTS ...)
例如:
find_package(SystemC REQUIRED)
它在干什么?
👉 本质:
在系统中查找某个库,并加载它的配置(头文件、库、编译选项)
成功后会得到什么?
通常会生成:
SystemC_INCLUDE_DIRS
SystemC_LIBRARIES
或者更现代:
SystemC::systemc (推荐)
推荐写法(现代 CMake)
find_package(SystemC REQUIRED)
target_link_libraries(sim
SystemC::systemc
)
👉 自动完成:
-
include path
-
link library
-
compile options
如果找不到怎么办?
你可以指定路径:
cmake -DCMAKE_PREFIX_PATH=/path/to/systemc ..
工程意义(非常重要)
👉 find_package 是:
唯一"可扩展 + 可复用 + 工业标准"的依赖管理方式
👉 推荐以后用这个(更工程化)
4️⃣ 源码组织(核心)
可执行程序:
add_executable(sim main.cpp)
基本语法:
add_executable(<name> sources...)
示例:
add_executable(sim tb_top.cpp)
👉 生成:
sim.exe / sim
特点:
-
必须有入口函数(
main/sc_main) -
是最终运行程序
库(推荐!):
基本语法:
add_library(<name> STATIC \| SHARED \| MODULE \| INTERFACE sources...)
各参数解释:
🔹 <name>
库名(target 名)
add_library(memory ...)
👉 后面用:
target_link_libraries(sim memory)
🔹 类型(4种)
✅ 1. STATIC(默认最常用)
add_library(memory STATIC ddr_ctrl.cpp bank.cpp)
👉 生成:
libmemory.a (Linux)
memory.lib (Windows)
特点:
-
编译进最终程序
-
不依赖运行时库
✅ 2. SHARED(动态库)
add_library(noc SHARED router.cpp)
👉 生成:
libnoc.so / noc.dll
特点:
-
运行时加载
-
可替换
✅ 3. MODULE(插件)
add_library(plugin MODULE plugin.cpp)
👉 不会被自动链接
👉 用于 dlopen 这类场景
✅ 4. INTERFACE(重点🔥)
add_library(soc_core INTERFACE)
👉 特点:
-
❌ 不生成任何库文件
-
✅ 只传递依赖关系
🔹 [sources...]
add_library(memory
ddr_ctrl.cpp
bank.cpp
)
👉 可以为空(INTERFACE 时)
add_library(core
fifo.cpp
noc.cpp
ddr_ctrl.cpp
)
👉 类比:
-
library = 模块(IP)
-
executable = 仿真顶层
5️⃣ 链接关系(必须理解)
target_link_libraries(sim
core
systemc
m
)
👉 表示:
- sim 依赖 core
- 依赖 SystemC库
基本语法:
target_link_libraries(<target>
PRIVATE \| PUBLIC \| INTERFACE
<lib1> <lib2> ...
)
真正关键:三种作用域(核心🔥)
PRIVATE
target_link_libraries(memory PRIVATE systemc)
👉 含义:
memory 自己用 systemc,但不会传递给别人
PUBLIC
target_link_libraries(memory PUBLIC systemc)
👉 含义:
memory 用 systemc,并且依赖它的人也必须用
INTERFACE
target_link_libraries(soc_core INTERFACE memory)
👉 含义:
自己不用,但使用它的人必须用
推荐写法(标准):
memory模块
add_library(memory ddr_ctrl.cpp)
target_link_libraries(memory
PUBLIC systemc
)
noc模块
add_library(noc router.cpp)
target_link_libraries(noc
PUBLIC systemc
)
聚合
add_library(soc_core INTERFACE)
target_link_libraries(soc_core
INTERFACE memory noc
)
仿真顶层
add_executable(sim tb_top.cpp)
target_link_libraries(sim
soc_core
m
)
依赖传播结果:
sim
└── soc_core
├── memory
│ └── systemc
└── noc
└── systemc
👉 sim 自动拥有:
- systemc
- memory
- noc
常见错误
❌ 1. 全用 PRIVATE
target_link_libraries(memory PRIVATE systemc)
👉 结果:
sim 找不到 systemc ❌
❌ 2. 不用 INTERFACE 聚合
target_link_libraries(sim memory noc systemc)
👉 问题:
- sim 依赖过多
- 层次混乱
❌ 3. 顺序错误(老编译器)
target_link_libraries(sim systemc memory) ❌
👉 有些平台会炸
6️⃣ 编译选项(调试/优化)
target_compile_options(sim PRIVATE -O2 -Wall)
语法:
target_compile_options(<target>
PRIVATE \| PUBLIC \| INTERFACE
<options...>
)
它在干什么?
👉 本质:
给编译器加参数(类似 g++ 后面的 -O2 -Wall)
示例
target_compile_options(sim PRIVATE -O2 -Wall)
👉 等价:
g++ -O2 -Wall ...
常见选项
target_compile_options(sim PRIVATE
-O2 # 优化
-g # 调试信息
-Wall # 警告
-Wextra
)
作用域(和 link 一样🔥)
PRIVATE
target_compile_options(memory PRIVATE -O2)
👉 只影响 memory 自己
PUBLIC
target_compile_options(memory PUBLIC -O2)
👉 memory 和依赖它的模块都会用
INTERFACE
target_compile_options(soc_core INTERFACE -O2)
👉 自己不用,但下游会用
7️⃣ 宏定义(非常有用)
语法:
target_compile_definitions(<target>
PRIVATE \| PUBLIC \| INTERFACE
<definitions...>
)
target_compile_definitions(sim PRIVATE DEBUG=1)
👉 类似 RTL:
`define DEBUG
结合 option 使用(工程级)
示例:
cmake:
option(ENABLE_TRACE "Enable waveform" ON)
if(ENABLE_TRACE)
target_compile_definitions(sim PRIVATE ENABLE_TRACE)
endif()
c++:
#ifdef ENABLE_TRACE
dump_wave();
#endif
8️⃣ 子目录(大项目必备)
add_subdirectory(src)
add_subdirectory(tb)
add_subdirectory(noc)
👉 用于:
-
分模块管理
-
团队协作
-
👉
add_subdirectory(...)的本质是:
"把子目录的 CMakeLists.txt 纳入当前工程,并参与构建"
基本语法
add_subdirectory(<source_dir> [binary_dir] [EXCLUDE_FROM_ALL])
参数解释
🔹
<source_dir>(必须)add_subdirectory(src)👉 表示:
进入 src/ 目录,执行里面的 CMakeLists.txt
🔹
[binary_dir](可选)add_subdirectory(src build_src)👉 指定这个子目录的构建输出目录
👉 很少用,一般不用写
🔹
[EXCLUDE_FROM_ALL](高级)add_subdirectory(test EXCLUDE_FROM_ALL)
👉 表示:
默认不参与构建(除非被依赖)
它到底干了什么?
当你写:
add_subdirectory(src)
👉 实际发生:
- 进入 src/CMakeLists.txt
- 执行里面所有 add_library / add_executable
- 把这些 target 注册到整个工程
9️⃣ 安装(一般后期才用)
install(TARGETS sim DESTINATION bin)
👉
install(...)解决的是:"别人怎么用你的工程"
先给一句本质:
👉
install(...)的本质是:
"定义构建完成后,文件要被拷贝到哪里、以什么结构对外发布"
基本语法(常见形式):
install(TARGETS ...)
install(FILES ...)
install(DIRECTORY ...)
install(EXPORT ...)
👉 这四个就是你 90% 会用到的
最核心:install(TARGETS ...)
语法
install(TARGETS <targets...>
RUNTIME DESTINATION <dir> # 可执行文件
LIBRARY DESTINATION <dir> # 动态库
ARCHIVE DESTINATION <dir> # 静态库
)
示例
add_executable(sim tb_top.cpp)
install(TARGETS sim
RUNTIME DESTINATION bin
)
👉 安装后:
install_prefix/
└── bin/
└── sim
库安装
add_library(memory STATIC ddr_ctrl.cpp)
install(TARGETS memory
ARCHIVE DESTINATION lib
)
👉 生成:
lib/libmemory.a
安装头文件(非常重要)
install(FILES ...)
install(FILES
include/ddr_ctrl.h
DESTINATION include
)
install(DIRECTORY ...)(推荐)
install(DIRECTORY include/
DESTINATION include
)
👉 会保留目录结构
安装完整工程结构(标准做法)
推荐结构
install(TARGETS memory noc
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
)
install(DIRECTORY include/
DESTINATION include
)
👉 安装后:
install_prefix/
├── lib/
│ ├── libmemory.a
│ └── libnoc.a
└── include/
├── ddr_ctrl.h
└── noc.h
install(EXPORT ...)(高级🔥)
👉 这是配合 find_package 的关键
语法
install(EXPORT <export_name>
DESTINATION lib/cmake/<PackageName>
)
示例
install(TARGETS memory noc
EXPORT SoCTargets
)
install(EXPORT SoCTargets
DESTINATION lib/cmake/SoC
)
👉 作用:
生成:
SoCTargets.cmake
👉 这样别人可以:
find_package(SoC)
target_link_libraries(app SoC::memory)
安装路径控制
默认路径
cmake --install .
👉 默认安装到:
/usr/local
自定义路径
cmake -DCMAKE_INSTALL_PREFIX=./install ..
cmake --build .
cmake --install .
👉 安装到:
./install/