构建与运行
程序入口 已经能最小调用动态库。这一章配置、编译、测试并运行这个工程。做完后,15 条棋盘用例全部通过,app 也链接到同一次构建产出的 tictactoe 动态库。构建入口是 running-build.sh。
构建脚本
准备 WSL Ubuntu,并装好 CMake、Clang、Ninja 与 libc++ 开发包。CMake 需要 3.20 或更高;工程用 -stdlib=libc++ 编译,缺这个库会在第一次编译就报错。GoogleTest 由 CMake 在首次配置时下载,机器需要能访问 GitHub;下载结果留在 build/_deps,后续配置直接复用。进入 games/tictactoe,执行脚本:
WSL Ubuntu(工程目录)
bash running-build.sh脚本按顺序完成三项:
| 步 | 做什么 | 由谁执行 |
|---|---|---|
| 配置与编译 | 读取 CMake 规则,取 GoogleTest v1.15.2,在 build/ 下生成 Ninja 文件并编译 |
CMake、Ninja |
| 测试 | 运行 CTest 注册的 15 条棋盘用例 | CTest |
| 运行 | 启动 app,检查动态库调用结果 |
构建脚本 |
首次运行生成 build/,后续运行复用;这个目录由仓库根的 .gitignore 排除。脚本不跑 Release,也不跑 clang-format 与 clang-tidy;编译器警告仍然按工程设置开启。
构建规则
根 CMakeLists.txt 在 tictactoe/、apps/ 之外加入 tests/。它先调用 enable_testing(),让 CTest 接管测试目标。动态库、主程序与测试是三个 target;动态库不反向依赖另外两个。
C++ 标准由 cmake/CompileSettings.cmake 里的 CompileSettings_SetCxxStandard 统一声明:
标准函数调用
CompileSettings_SetCxxStandard(tictactoe PUBLIC c++23)
CompileSettings_SetCxxStandard(app PRIVATE c++23)函数同时写 target_compile_features 与 CXX_STANDARD、CXX_STANDARD_REQUIRED、CXX_EXTENSIONS 三个属性。库用 PUBLIC 把标准传给链接方。CXX_STANDARD_REQUIRED ON 禁止编译器不支持时静默降级;CXX_EXTENSIONS OFF 把 -std=gnu++23 收成 -std=c++23。
警告选项收在同一份 cmake/CompileSettings.cmake,函数按等级取一组常用 flag:
警告等级调用
CompileSettings_SetWarnings(tictactoe PRIVATE LEVEL standard)
CompileSettings_SetWarnings(app PRIVATE LEVEL standard)basic 只开 -Wall 与 -Wextra。standard 再加六项。前三项是 -Werror、-Wshadow 与 -Wold-style-cast。后三项是 -Wpedantic、-Wnon-virtual-dtor 与 -Woverloaded-virtual。项目里两处调用都用 PRIVATE,警告只作用于传入的 target。
测试目标在 tests/CMakeLists.txt 里单独声明:
tests/CMakeLists.txt
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.tar.gz
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)FetchContent_MakeAvailable(googletest) 只构建测试需要的那一部分。tictactoe_test 链接动态库 tictactoe 与 GTest::gtest_main,BUILD_RPATH 设为 $ORIGIN/../lib。这样从 build/bin 启动时能找到同次构建的动态库。gtest_discover_tests 在构建后把 15 条用例注册给 CTest。
GoogleTest 必须和测试目标使用同一套标准库。在 Clang 下,tests/CMakeLists.txt 只给四个 GoogleTest 目标加 -stdlib=libc++。否则 gtest 按 libstdc++ 编译、测试按 libc++ 链接,会出现一批 std:: 符号未定义。
产物落在 build/ 下:
构建产物
build/
bin/app
bin/tictactoe_test
lib/libtictactoe.so
验收结果
脚本退出码为 0、CTest 报告 15 条全部通过、app 打印成功行时,这次构建通过。
核对测试输出
CTest 的最后一行给出总数:
终端输出
100% tests passed, 0 tests failed out of 15
失败时 CTest 打印用例名与断言位置;--output-on-failure 已经把失败输出带到终端。
核对程序输出
Debug 运行从 build/bin 启动 app:
终端输出
井字棋动态库调用成功:已落子 1 手
这行来自 程序入口:main 创建一局、在 (0, 0) 落一子,再用 GetHistory().size() 读回手数。它同时证明动态库被链接、Game 能构造、TryPlace 接受了一手合法落子。
手动核对链接
进入工程目录后可以直接查看动态库依赖:
WSL Ubuntu(工程目录)
ldd build/bin/app | grep tictactoe输出中应出现 libtictactoe.so,路径指向本次构建的 build/lib。如果 ldd 显示 not found,先确认 build/lib/libtictactoe.so 存在,再检查 BUILD_RPATH 是否被覆盖。
常见错误
找不到 libc++
配置成功,但第一次编译就报找不到 <format> 或 libc++ 头文件。
- 出错的原因:工程用
-stdlib=libc++,系统只装了 libstdc++ 开发包。 - 正确的做法:在 WSL Ubuntu 安装 libc++ 与 libc++abi 开发包,再重新执行脚本。
GoogleTest 下载失败
配置时停在下载 v1.15.2.tar.gz。
- 出错的原因:机器无法访问 GitHub,或代理没有覆盖 CMake 的下载请求。
- 正确的做法:先修网络或代理;已有本地源码时,配置时加
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=<本地源码目录>,让 CMake 不再下载。
动态库加载失败
app 能编译,运行时却报找不到 libtictactoe.so。
- 出错的原因:
BUILD_RPATH没有生效,或运行的是旧构建目录里的app。 - 正确的做法:从
build/bin/启动,确认build/lib/libtictactoe.so存在;必要时删掉构建目录重新配置和编译。
测试失败
CTest 返回非零,并打印某个用例的断言。
- 出错的原因:棋盘行为与用例期望不一致,或测试目标链接到了旧库。
- 正确的做法:先读失败用例名与断言行;确认
build/lib/libtictactoe.so与测试来自同一次构建,再修改代码或用例。
自测问题
构建脚本按什么顺序执行?
查看答案
先配置并编译,再跑 CTest,最后启动
app。为什么测试要排在启动程序之前?
查看答案
15 条棋盘用例直接检查库行为,失败能定位到函数;先跑测试,失败时不必再启动程序。
为什么 GoogleTest 要和测试目标用同一套标准库?
查看答案
两套标准库的
std::符号不通用;混用会在链接时出现一批未定义符号。CTest 里注册了多少条用例?
查看答案
15 条,全部来自
testing-board.cpp,由gtest_discover_tests注册。
本章回顾
你已经能配置、编译、跑 15 条棋盘测试并启动 app,确认它链接到了正确的 libtictactoe.so。下一章局面快照;它属于终端程序,当前构建未接入,先按设计保留。