miniblink49 内嵌 V8 的 Google Test(gtest)构建与集成指南:从源码编译到宏级定制
【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核,用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49
导读
本文以 miniblink49 仓库中 V8 7.5 所捆绑的 Google Test 构建文档 为主线,系统讲解 Google Test(gtest)的四种构建方式、CMake 与 Makefile 的完整配置、以及GTEST_*系列编译宏的定制原理。读者将掌握:如何把 gtest 以静态库或共享库形式接入自己的 C++ 项目、如何规避 TR1 tuple 与宏命名冲突、如何验证多线程环境下的线程安全,以及如何在 Chromium/V8 的 GN 构建体系中复用这份测试框架。文中所有命令与配置均可直接在仓库对应目录中验证。
一、仓库中的 gtest 位置与角色
在 miniblink49 中,Google Test 以第三方测试依赖的形式内嵌于 v8_7_5/testing/gtest/ 目录,服务于 V8 7.5 自身的单元测试体系。目录结构完整保留了 gtest 官方布局:
src/:核心实现,包括 gtest-all.cc(聚合编译入口,#include全部 gtest 实现)、gtest_main.cc(提供现成main())、gtest-death-test.cc(死亡测试)、gtest-printers.cc(值打印)等;include/gtest/:全部公共头文件,测试程序只需#include "gtest/gtest.h";samples/:10 个官方示例(sample1_unittest.cc~sample10_unittest.cc),演示断言、测试固件、参数化测试、类型参数化测试等;make/、msvc/、xcode/、codegear/:各平台手写构建脚本;CMakeLists.txt:跨平台 CMake 构建脚本;BUILD.gn:供 Chromium 系 GN 构建体系使用的目标定义(V8 即运行于此类体系)。
README 的核心主张是:告诉你的构建系统 gtest 的头文件与源文件在哪里,剩下的交给编译器和链接器。下文按 README 的脉络逐层展开。
二、通用构建指令:手写编译 gtest
2.1 Setup:确定${GTEST_DIR}
将 gtest 解压/内嵌到目录${GTEST_DIR}(在本仓库即v8_7_5/testing/gtest)后,需要让构建系统知道两处位置:
${GTEST_DIR}/include:作为系统头文件搜索路径(-isystem),这样编译器不会对 gtest 头文件内部的警告大呼小叫;${GTEST_DIR}:作为普通头文件搜索路径(-I),因为部分内部头文件(如src/gtest-internal-inl.h)以相对路径被引用。
2.2 Build:两步编译
第一步,把 gtest-all.cc 编译成目标文件并打包成静态库:
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ -pthread -c ${GTEST_DIR}/src/gtest-all.cc ar -rv libgtest.a gtest-all.o注意:
-pthread是必需的,因为 gtest 内部使用线程(线程安全断言、死亡测试的超时控制等)。
第二步,编译你的测试源文件并链接:
g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ -o your_test这里的your_test.cc中#include "gtest/gtest.h"后,用TEST()/TEST_F()等宏编写用例,并链接gtest_main(或自行实现main()后调用RUN_ALL_TESTS())。
2.3 快速验证:make 目录
make/Makefile 是一个可直接运行的完整示例,它不构建 gtest 自身测试,只产出libgtest.a、libgtest_main.a和一个示例测试。其关键变量:
GTEST_DIR = .. USER_DIR = ../samples CPPFLAGS += -isystem $(GTEST_DIR)/include CXXFLAGS += -g -Wall -Wextra -pthread TESTS = sample1_unittest只要环境默认设置正确,以下命令应直接成功:
cd ${GTEST_DIR}/make make ./sample1_unittest从 Makefile 可见编译 gtest 只需src/*.cc与src/*.h加上全部头文件(GTEST_SRCS_),依赖关系写得“保守但不优化”——因为 gtest 编译极快、普通用户很少改动其源码。若出现错误,按make/Makefile中的注释调整GTEST_DIR、USER_DIR等变量即可。
三、使用 CMake 构建:跨平台首选
gtest 自带 CMakeLists.txt,"C"代表 cross-platform,可在 Windows(Visual Studio)、macOS(Xcode)、Linux(Makefile)等环境生成原生构建工程。典型工作流:
mkdir mybuild # 创建存放构建输出的目录 cd mybuild cmake ${GTEST_DIR} # 生成原生构建脚本如需一并构建官方示例:
cmake -Dgtest_build_samples=ON ${GTEST_DIR}- 在 *nix 上会生成 Makefile,执行
make即得libgtest.a; - 在 Windows + Visual Studio 下会生成
gtest.sln与若干.vcproj,可直接用 IDE 构建; - 在 macOS + Xcode 下会生成
.xcodeproj。
3.1 CMake 提供的可配置选项
从 CMakeLists.txt 的option()声明中可以整理出完整的开关表:
| CMake 选项 | 默认值 | 作用 |
|---|---|---|
BUILD_SHARED_LIBS | OFF | 是否构建共享库(Windows 上即 DLL) |
gtest_force_shared_crt | OFF | 即使 gtest 以静态库构建,也强制使用共享(DLL)运行时库;当宿主项目使用共享 CRT 时必须打开 |
gtest_build_tests | OFF | 是否构建 gtest 自身全部测试 |
gtest_build_samples | OFF | 是否构建samples/下的示例程序 |
gtest_disable_pthreads | OFF | 禁用 pthread 使用 |
gtest_hide_internal_symbols | OFF | 构建共享库时隐藏内部符号(等价设置CMAKE_CXX_VISIBILITY_PRESET=hidden) |
CMake 脚本中还内置了 Visual Studio 的 tuple 支持矩阵(第 72~80 行注释):
- VS 2010 及更早(MSVC ≤ 1600):使用 gtest 自带的 tuple;
- VS 2012(MSVC 1700):使用
std::tr1::tuple且需定义_VARIADIC_MAX=10(脚本已自动添加/D _VARIADIC_MAX=10); - VS 2013 起(MSVC ≥ 1800):直接使用
std::tr1::tuple。
目标产物为两个库:gtest(核心库,编译自 src/gtest-all.cc)与gtest_main(额外包含 src/gtest_main.cc,提供main(),并链接gtest)。用户测试应链接二者之一。
四、Legacy 构建脚本(不推荐,但兼容)
在转向 CMake 之前,gtest 长期维护着 Visual Studio、Xcode 与 Autotools 三套手写工程。README 明确说明:它们已不再积极维护,强烈建议优先使用前两节的通用构建或 CMake 方式;仅当确有需要时才使用。
MSVC:打开
msvc\gtest.sln或msvc\gtest-md.sln。文件名以-md结尾的使用动态 CRT(/MD或/MDd),不带后缀的使用静态 CRT(/MT或/MTd)。关键约束:编译 gtest 与编译测试代码必须使用相同的 CRT 选项,否则链接期会报 CRT 符号冲突;VS 2005 及以上建议使用-md版本(/MD是新项目的默认选项)。Xcode:打开
xcode/gtest.xcodeproj并构建gtesttarget,通用二进制 framework 输出到构建目录;命令行下可直接执行:xcodebuild该命令在默认构建位置生成
Release配置的gtest.framework。若使用 Xcode 4.x 及以上,需二选一:要么在xcode/Config/General.xconfig中注释掉SDKROOT、MACOS_DEPLOYMENT_TARGET、GCC_VERSION三个选项(代价是无法再针对旧版 macOS 构建),要么安装旧版 SDK。Autotools:仓库根目录保留
configure.ac、Makefile.am、m4/等文件,对应传统./configure && make流程(CMake 时代之前的产物)。
五、Tweaking Google Test:GTEST_*控制宏
gtest 通过一组形如GTEST_XYZ、取值为1/0的编译期宏实现特性开关,在编译器命令行以-D定义即可。完整清单见 include/gtest/internal/gtest-port.h 顶部的注释块(第 70~180 行),该文件集中处理平台差异、线程、tuple、共享库等可移植性问题。以下为 README 点名的常用宏及源码佐证。
5.1 选择 TR1 tuple 库
部分 gtest 特性(如参数化测试TEST_P)依赖 C++ TR1 的 tuple,而早期编译器未必提供。gtest 自带一个“够用”的 TR1 tuple 子集实现,编译器缺 tuple 时自动启用(见 gtest-port.h 第 634~677 行的条件编译逻辑)。
若你的项目已使用 TR1 tuple,必须让 gtest 与项目使用同一份,否则两个 tuple 实现会冲突。编译 gtest 与测试时都加上:
-DGTEST_USE_OWN_TR1_TUPLE=0若想强制 gtest 用自己的 tuple:
-DGTEST_USE_OWN_TR1_TUPLE=1若完全禁用 tuple(相应特性随之关闭):
-DGTEST_HAS_TR1_TUPLE=0
仓库佐证:gtest-tuple.h 与其
.pump模板文件位于include/gtest/internal/,而 gtest-port.h 中GTEST_HAS_TR1_TUPLE与GTEST_USE_OWN_TR1_TUPLE的默认推导(第 634/646 行)会依据编译器版本自动决定。
5.2 多线程测试与线程安全
gtest 在具备 pthread 的环境下是线程安全的。#include "gtest/gtest.h"后可用GTEST_IS_THREADSAFE宏探测:若该宏被#define为 1 则线程安全,未定义则否。其定义位于 gtest-port.h 第 926~929 行,条件为操作系统支持且GTEST_HAS_PTHREAD为真。
如果 gtest 对 pthread 的自动探测有误,可强制指定:
-DGTEST_HAS_PTHREAD=1 # 强制启用 pthread -DGTEST_HAS_PTHREAD=0 # 强制禁用链接期陷阱:启用 pthread 后,编译器与链接器可能需要显式加-pthread(或-lpthread)标志,否则出现链接错误。使用 CMake 脚本或旧的 Autotools 脚本时此步已自动处理;手写构建脚本则需自行查阅编译器手册。仓库的 make/Makefile 正是在CXXFLAGS中显式写了-pthread来规避该问题。
5.3 作为共享库(DLL)构建
gtest 体量小,默认建议以静态库链接。若偏好共享库,分两侧设置:
编译gtest 本身为共享库:
-DGTEST_CREATE_SHARED_LIBRARY=1同时需告知链接器产出共享库(见各链接器手册)。
编译使用 gtest 的测试:
-DGTEST_LINKED_AS_SHARED_LIBRARY=1
README 特别提醒:虽然对部分编译器(如 GCC)当前不强制,但这是为未来优化加载速度(符号可见性控制)预留的接口,建议始终加上这两个宏,否则 gtest 未来版本可能破坏你的构建脚本。仓库 CMakeLists.txt 第 201~208 行的gtest_dll目标即为共享库自测示例(编译时定义GTEST_LINKED_AS_SHARED_LIBRARY=1)。
5.4 规避宏名冲突
C++ 宏不遵循命名空间,两个库若定义了同名宏,#include后必然冲突。gtest 允许通过GTEST_DONT_DEFINE_FOO=1把宏FOO改名为GTEST_FOO来规避。当前支持改名的宏为FAIL、SUCCEED、TEST:
-DGTEST_DONT_DEFINE_TEST=1此后定义测试需写:
GTEST_TEST(SomeTest, DoesThis) { ... }而非:
TEST(SomeTest, DoesThis) { ... }仓库佐证:
GTEST_DONT_DEFINE_*系列宏的实际处理逻辑分布在 gtest.h 等公共头文件的底部——先#define GTEST_TEST(...)作为内部实现,再在#ifndef GTEST_DONT_DEFINE_TEST分支下将TEST别名化,从而支持上述改名机制。
六、Developing Google Test:自测与代码生成
6.1 运行 gtest 自身测试
修改 gtest 源码后,应编译并运行其自带测试验证不破坏既有功能。CMake 方式:
mkdir mybuild cd mybuild cmake -Dgtest_build_tests=ON ${GTEST_DIR} make make test注意两点:
部分 gtest 测试用 Python 编写,需预装 Python;若 CMake 报
Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE),显式指定解释器路径:cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR}自测体系覆盖极广。以 CMakeLists.txt 第 142~286 行为证,
gtest_build_tests=ON时会构建:- 常规 C++ 测试:
gtest-death-test_test(死亡测试)、gtest_environment_test(环境变量)、gtest-param-test_test(参数化测试)、gtest-typed-test_test(类型参数化)、gtest_unittest(核心)等 20 余个; - 非常规编译旗标测试:禁用异常(
gtest_no_exception)、禁用 RTTI(gtest_main_no_rtti)、开关GTEST_ENABLE_CATCH_EXCEPTIONS_的死亡测试变体、共享库测试gtest_dll等; - Python 驱动测试:
gtest_color_test、gtest_filter_unittest、gtest_output_test、gtest_shuffle_test、gtest_xml_output_unittest等 10 余个,通过py_test()辅助函数注册。
- 常规 C++ 测试:
6.2 使用 Pump 重新生成代码
gtest 中一批“重复模式”头文件(如 gtest-param-util-generated.h、gtest-type-util.h、gtest-tuple.h、gtest-param-test.h)由 Pump 模板生成:源码维护.pump模板文件,运行时执行 scripts/pump.py 输出实际.h。只有当你需要修改这些模板时才需关心再生成流程,详细用法参见 Pump 手册。
七、在 Chromium/V8 的 GN 体系中使用
除 CMake 与手写构建外,本仓库还提供 v8_7_5/testing/gtest/BUILD.gn,这是 Chromium/V8 在 GN 构建(gn gen && ninja)下复用 gtest 的接入层:
static_library("gtest"):testonly = true,仅列出公共头文件与empty.cc(该空文件是规避 Mac/Windows 上静态库无源文件报错、以及 Android 上complete_static_lib重复符号问题的 workaround),实际实现通过public_deps委托给//third_party/googletest:gtest;- 通过
public_configs注入UNIT_TEST宏(gtest_direct_config); source_set("gtest_main"):同样委托//third_party/googletest:gtest_main;- 附带
gtest_include_multiprocess、gtest_include_platform_test、gtest_include_objc_support、gtest_include_ios_coverage等条件开关,按平台(Mac/iOS)追加gtest_mac、覆盖率支持等文件。
这印证了 README 的论断:gtest 与具体构建系统解耦,任意构建系统只要找到头文件与源文件即可接入——无论是裸g++、make、CMake、Visual Studio、Xcode,还是 Chromium 系的 GN。
八、FAQ 与排错速查
| 现象 | 原因与对策 |
|---|---|
| 链接期报 pthread 相关未定义符号 | 使用了线程安全特性但未加-pthread/-lpthread;CMake 与 Autotools 自动处理,手写构建需手动补 |
| 编译 gtest 与测试的 CRT 选项不一致导致链接错误 | 静态/动态 CRT(/MT与/MD)必须统一,VS 场景见本文第四节 |
| tuple 相关编译错误或重复定义 | 项目已用 TR1 tuple 时设置-DGTEST_USE_OWN_TR1_TUPLE=0保持统一,或-DGTEST_HAS_TR1_TUPLE=0整体禁用 |
宏TEST/FAIL/SUCCEED与第三方库冲突 | 用-DGTEST_DONT_DEFINE_XXX=1改名为GTEST_XXX |
| CMake 找不到 Python | -DPYTHON_EXECUTABLE=path/to/python显式指定(构建 gtest 自身测试时才需要) |
| 共享库链接异常 | 编译 gtest 加-DGTEST_CREATE_SHARED_LIBRARY=1,编译测试加-DGTEST_LINKED_AS_SHARED_LIBRARY=1 |
九、小结
从 README.md 的构建指南出发,本文完整覆盖了 gtest 的三条接入路径(手写编译、make、CMake)与两条兼容路径(MSVC/Xcode/Autotools 旧脚本、GN 的 BUILD.gn),并深入 gtest-port.h 与 CMakeLists.txt 源码层验证了 tuple 选择、线程安全、共享库与宏改名等定制机制。对于 miniblink49 这样同时携带 V8 多版本与 Chromium 系代码库的项目,理解这份捆绑 gtest 的构建细节,是维护其测试体系、避免跨平台链接坑位的基础能力——所有命令与配置均可直接在本仓库v8_7_5/testing/gtest目录下复现验证。
【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核,用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考