gflags 2.1.1 源码编译安装与 Caffe 接入避坑指南

发布时间:2026/10/28 10:36:04
gflags 2.1.1 源码编译安装与 Caffe 接入避坑指南

简介:面向C++开发者与深度学习框架Caffe使用者的命令行标志库gflags 2.1.1源码包,适合需要理解参数解析机制、维护旧版Caffe项目或进行二次集成的读者。压缩包共50个文件、约100KB,以cc源码、h头文件、cmake构建脚本和txt说明文档为主,另含in模板、测试用例、flagfile配置示例及html文档,目录按源码、文档、测试与构建配置组织,便于从编译到验证完整梳理库结构。已有222人学习下载,可作为旧版本依赖排查和轻量级参数管理实践的参考。资源保留标志注册、短格式与长格式参数解析、配置文件读取、标志有效性检查及帮助信息输出等核心实现与测试样例,读者可据此对照Caffe中的学习率、批处理大小等参数设置,理解命令行标志从声明到解析的流程,并借助构建脚本与测试文件复现实验环境、定位兼容性问题。

1. 装 gflags 2.1.1 之前,先认清它是「老命名空间」那一支

gflags 2.1.1 安装翻车,大多不在编译阶段,而在装完之后:系统里同时躺着发行版自带的 libgflags-dev 和源码编的 2.1.1,头文件走一份、库文件走另一份,编 Caffe 时甩出一屏 undefined reference。我第一次栽在这上面耗掉大半天,回头才看清是命名空间的锅——2.1.1 的符号全挂在 google 命名空间下,2.2 之后才迁到 gflags 并留兼容别名。老项目、老分支、老 CMake 脚本里写死的google::ParseCommandLineFlags,配 2.2 的头就编不过;反过来拿 2.2 的头去链 2.1.1 的库,链接期更难看。这份 gflags 2.1.1 安装笔记只做一件事:从零把它编出来、装干净、接进 Caffe 的构建流程,装砸了还能退回去。手上压着老版本 Caffe 或者依赖 2.1.x ABI 的工程,照着走就行。

2. 环境清单:编译 gflags 2.1.1 要备齐哪些东西

装任何老版本库,第一件事不是敲命令,是确认工具链别太新也别太旧。gflags 2.1.1 是 C++11 普及初期的东西,源码里对编译器的要求不高,但对 CMake 的 install 规则、对静态库 PIC 的处理,跟现在的习惯差得远。我一般先把环境探一遍再动手,省得编到一半才发现 CMake 版本或者 gcc 版本不对劲,那时候再回滚更麻烦。

2.1 工具链版本对照表

下面这张表是我在几台机器上反复验证过的组合,不追求最新,追求能一次编过、编完能链进 Caffe:

组件建议版本区间说明
gcc / g++5.4 ~ 9.x4.8 能编,但 C++11 支持不完整,容易在模板处报错
CMake3.5 ~ 3.202.8 系列对 2.1.1 的 install 规则支持有差异,不建议
make3.81 以上系统自带通常够用
pthread系统自带gflags 依赖它,链接时必须带-lpthread
gtest只在开测试时需要建议直接关掉,省一份依赖

版本怎么选,我自己的判断标准很简单:操作系统如果是 16.04 那一代,gcc 5.4 配 CMake 3.5,属于原生组合,最省事;如果是 18.04 之后,gcc 7、gcc 9 都行,但要留意高版本 gcc 会把一些老代码里的 warning 升级成 error,后面编译参数里我会留一手。CMake 别用太新的 3.2x 尾部版本,有些老 CMakeLists 里的install写法在新版策略下会报 warning,虽然不致命,但日志会很长,排查时干扰视线。

先探一遍环境,把结果记下来:

gcc --version | head -n 1 g++ --version | head -n 1 cmake --version | head -n 1 make --version | head -n 1

这几行输出看起来啰嗦,但真出问题时它就是唯一的对照基准。我遇到过一台机器 gcc 是 9,但g++软链到 5,结果 CMake 检测用 gcc、编译用 g++,两边对不上,报了一堆莫名其妙的头文件错误。探完顺手确认下g++ --version和gcc --version是不是同一代,不是同一代就先修软链,别急着往下走。

2.2 依赖安装与源码包和发行版包的选择

gflags 有两条安装路线:发行版仓库的libgflags-dev,和自己下源码编。这两条路的差别不只是版本高低,还有 ABI 细节。

发行版仓库里给的基本是打过补丁的构建版本,16.04 那一代大概率是 2.1.2,18.04 之后跳到 2.2.x。你要的是严格的 2.1.1,那就只能走源码,因为 2.1.2 虽然小版本号接近,但补丁里改过什么不好追溯,跟老 Caffe 的兼容性不一定一致。我一般把源码包统一放在~/src下,所有手工编的库都从这里出,好处是半年后回来找还能找到原始包和当时的构建目录。

# 基础工具链,缺什么补什么 sudo apt-get update sudo apt-get install -y build-essential cmake # 源码包统一存放目录 mkdir -p ~/src cd ~/src tar -zxvf gflags-2.1.1.tar.gz ls -l gflags-2.1.1/

第一步装的是编译器和 CMake,build-essential会把 gcc、g++、make、libc-dev 一起带进来,比一个个装省事。第二步建目录纯属个人习惯,好处是后面卸载、比对、回滚都有据可查。第三步解包,注意包名取决于你拿到的归档是.tar.gz还是.zip,如果是 zip 就用unzip gflags-2.1.1.zip。ls那行不是多余——解包后目录名偶尔会带版本后缀或者多一层嵌套,先看一眼,免得cd进去发现是空的。

如果系统里之前 apt 装过 libgflags-dev,装源码版之前先决定要不要卸载。我的做法是不卸,而是把源码版装到独立前缀(比如/opt/gflags-2.1.1),靠 CMake 参数显式指过去。这样系统里那份继续给别的软件用,两份互不打扰。代价是每次构建都要带路径参数,但比「卸了系统包导致某个依赖它的程序起不来」要安全得多。

提示:确定要卸系统包时,sudo apt-get remove libgflags-dev只卸开发头文件和静态库,.so运行时库可能被其他包依赖而留下,卸完用ldconfig -p | grep gflags再确认一遍。

3. 源码编译:cmake 参数给对了,后面少熬两小时

源码编译这一步,命令谁都会敲,差别全在参数。gflags 2.1.1 的 CMakeLists 默认配置有几个坑:默认出静态库、默认开测试、install 路径按/usr/local走。静态库这条最坑,单独编 gflags 时不觉得有问题,等它被链进 Caffe 的_caffe.so那一刻才炸,报recompile with -fPIC。所以参数要一次给全,别指望后面补。

3.1 解包与构建目录的规矩

CMake 系的项目我一律用 out-of-source 构建,也就是在源码目录外面单开一个 build 目录,绝不在源码树里直接cmake .。原因很实在:源码树里一旦生成CMakeCache.txt,后面想换参数重编,缓存会一直捣乱,最典型的症状是你明明改了CMAKE_INSTALL_PREFIX,make install还是往老路径装。

cd ~/src/gflags-2.1.1 mkdir -p build && cd build # 看一眼源码树结构,确认 CMakeLists.txt 在这一层 ls ../

mkdir -p build && cd build是标准动作,-p保证目录存在也不报错,写进脚本里安全。后面那行ls ../是给自己看的,确认CMakeLists.txt就在上一级,有些归档包会多套一层同名目录,直接cmake ..会报找不到 CMakeLists.txt,白白浪费一次执行。

想彻底干净重来的时候,别在 build 目录里make clean凑合,直接删:

cd ~/src/gflags-2.1.1 rm -rf build && mkdir build && cd build

这条「后悔药」命令我用了很多年,比任何make distclean都可靠。CMake 的缓存文件藏得深,手工清永远清不干净,整个 build 目录删掉重来是最省心的做法。前提是你没在 build 目录里放别的东西——所以构建目录只放构建产物,别往里塞脚本。

3.2 六个 cmake 参数逐个说清

参数给全,一条命令下去:

cmake .. \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_TESTING=OFF \ -DCMAKE_CXX_FLAGS="-fPIC" \ -DCMAKE_C_FLAGS="-fPIC"

逐个说:

CMAKE_INSTALL_PREFIX决定make install把东西放哪。写/usr/local是最省事的做法,头文件落/usr/local/include/gflags/,库落/usr/local/lib/,编译器默认搜索路径就包含这两个,后面编译 Caffe 不用额外加-I、-L。想多版本共存就换成/opt/gflags-2.1.1,代价是每次构建都得显式指定路径。

CMAKE_BUILD_TYPE=Release影响优化级别和是否带调试符号。不写这个参数,CMake 会按空构建类型处理,优化级别是-O0,编出来的库跑 Caffe 会明显慢。写 Release 就是-O3 -DNDEBUG,跟大多数发行版包一致。

BUILD_SHARED_LIBS=ON是这次的关键。gflags 2.1.1 默认编静态库libgflags.a,静态库如果没有编进位置无关代码,被链进共享库时链接器会直接拒绝。开这个开关出libgflags.so,从根上绕开 PIC 问题。如果你的工程确实需要静态库,那就保留OFF,但必须同时给CMAKE_CXX_FLAGS和CMAKE_C_FLAGS加上-fPIC,让静态库也带位置无关代码。

BUILD_TESTING=OFF关掉测试。2.1.1 的测试依赖 gtest,本机没装 gtest 的话,make到测试目标就断,前面编好的库还在,但整个构建返回非零,写脚本时会被判定为失败。关掉它,编译时间也能砍掉一大截。如果 CMake 提示这是 unused variable,说明这个版本的 CMakeLists 没认这个开关,忽略即可,不影响结果。

两个FLAGS参数是保险。-fPIC已经由BUILD_SHARED_LIBS=ON覆盖了,但显式再写一遍没有坏处,某些老 CMakeLists 里 PIC 是靠CMAKE_POSITION_INDEPENDENT_CODE控制的,2.1.1 不一定有这个变量,直接给编译 flag 最稳。

配置跑完,看最后几行输出。正常情况下会打印编译器、构建类型、安装前缀,还有一行Configuring done、Generating done。如果中途出现Could NOT find开头的行,先看它找的是什么——找 gtest 属于正常,因为你关了测试;找别的就要停下来查。

3.3 编译、安装落位与卸载后悔药

配置通过就可以编了。并行度按 CPU 核数给,nproc拿到几就用几,别硬写-j8,小机器上会把内存吃满然后被 OOM killer 干掉:

make -j$(nproc)

编完先别急着sudo make install,在 build 目录里确认产物出来了:

ls -l lib/

应该能看到libgflags.so、libgflags.so.2.1、libgflags.so.2.1.1这一组软链,或者静态库libgflags.a,取决于你开没开BUILD_SHARED_LIBS。软链的层级关系是 CMake 的 SOVERSION 机制自动生成的,别手工去改。

然后安装:

sudo make install

装完确认落位:

ls -l /usr/local/include/gflags/ ls -l /usr/local/lib/libgflags*

头文件应该在/usr/local/include/gflags/下,至少要看到gflags.h。这里有个细节值得记住:gflags 的头文件是放在gflags/子目录里的,代码里写#include <gflags/gflags.h>,不是#include <gflags.h>。我第一次装的时候把-I指到了include/gflags,然后代码里还是写gflags/gflags.h,路径拼起来变成include/gflags/gflags/gflags.h,报找不到文件,查了半天。

装完刷新动态库缓存:

sudo ldconfig ldconfig -p | grep gflags

ldconfig重建/etc/ld.so.cache,把/usr/local/lib下新增的库登记进去,不加这一步,运行时可能报error while loading shared libraries。第二条命令用来确认登记成功,输出里应该能看到libgflags.so.2.1和它的完整路径。如果路径指向的不是你刚装的那份,说明系统里有多份同名库,得往下看第 5 章。

装坏了要退,CMake 留了一份清单文件,在 build 目录下:

cd ~/src/gflags-2.1.1/build sudo xargs rm -v < install_manifest.txt sudo ldconfig

install_manifest.txt记录了make install拷贝的每一个文件的绝对路径,xargs rm按行删掉。这个文件是 CMake 自动生成的,前提是你没删 build 目录。所以我前面强调 build 目录别乱清——它就是卸载时的后悔药。删完再ldconfig刷新缓存,ldconfig -p | grep gflags应该就空了。

4. 接进 Caffe:让 CMake 认准这份 2.1.1

库装好了,真正的麻烦才刚开始。Caffe 的 CMake 构建里要找 gflags,而 gflags 2.1.1 不提供 CMake config 包文件,find_package(gflags CONFIG)这条路走不通,CMake 只能退回模块模式,找Findgflags.cmake。Caffe 的cmake/Modules下自带一份,但那份脚本对新老版本的探测逻辑有偏向,经常找到系统里另一份 2.2 上去。所以接进 Caffe 之前,先把系统里到底有几份 gflags 摸清楚。

4.1 先确认系统里到底有几份 gflags

三条命令,从不同角度查:

# 查动态库缓存里登记了哪些 ldconfig -p | grep gflags # 查发行版包管理器装过什么 dpkg -l | grep -i gflags # 全盘扫,找出所有库文件和头文件 find /usr /usr/local /opt -name "libgflags*" -o -name "gflags.h" 2>/dev/null

第一条看运行时链接会命中哪个,ldconfig -p的输出顺序就是库搜索顺序,排在前面的优先。第二条看是不是 apt 装过libgflags-dev,如果有,说明系统里至少还有一份。第三条最彻底,把散落在各个前缀下的库和头文件都翻出来,包括你自己装到/opt下的那份。

三条命令的输出要交叉比对。典型的乱象是:头文件在/usr/local/include/gflags/gflags.h(你的 2.1.1),动态库却优先命中/usr/lib/x86_64-linux-gnu/libgflags.so.2.2(系统包)。这种「头库错配」编出来的程序,编译期不报错,链接期也不一定报错,运行期行为诡异——因为函数的参数布局在两代之间可能有细微差别,行内代码按新头文件生成,实际执行的却是老库。

比对完做个决定。要么彻底清掉系统那份(sudo apt-get remove libgflags-dev,谨慎点再加libgflags2.2),要么保留系统份、把 2.1.1 装到独立前缀,构建时用参数强指。我倾向后者,动系统包的风险是别的软件起不来,而代价只是多写几个参数。

4.2 手写 Findgflags.cmake 的兜底方案

Caffe 自带的Findgflags.cmake有自己的查找顺序偏好,经常先命中系统路径。与其去改它,不如在自己的工程里放一份模块文件,用CMAKE_MODULE_PATH挂到最前面,让它优先被用。

在工程根目录建cmake/Modules/Findgflags.cmake:

# 优先在 GFLAGS_ROOT 指定的前缀下找,其次才是系统路径 find_path(GFLAGS_INCLUDE_DIR NAMES gflags/gflags.h HINTS ${GFLAGS_ROOT} PATHS /usr/local /usr /opt/gflags-2.1.1 PATH_SUFFIXES include NO_DEFAULT_PATH) find_library(GFLAGS_LIBRARY NAMES gflags HINTS ${GFLAGS_ROOT} PATHS /usr/local /usr /opt/gflags-2.1.1 PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(gflags DEFAULT_MSG GFLAGS_INCLUDE_DIR GFLAGS_LIBRARY) if(GFLAGS_FOUND) set(GFLAGS_INCLUDE_DIRS ${GFLAGS_INCLUDE_DIR}) set(GFLAGS_LIBRARIES ${GFLAGS_LIBRARY}) set(GFLAGS_FOUND TRUE) endif() mark_as_advanced(GFLAGS_INCLUDE_DIR GFLAGS_LIBRARY)

find_path找的是头文件所在的目录,NAMES写gflags/gflags.h而不是gflags.h,因为 2.1.1 的头文件装在gflags/子目录下,写错了会找不到。HINTS给的是软提示,用户可以通过-DGFLAGS_ROOT=/opt/gflags-2.1.1传进来,PATHS给的是硬编码的候选目录,两个都写能覆盖大部分场景。

NO_DEFAULT_PATH是个有争议的参数。加上它,查找就被限制在HINTS和PATHS里,不会去系统默认路径乱翻,好处是绝对不会误命中另一份;坏处是如果你的路径不在列表里,就彻底找不到。稳妥的做法是第一次加上它,确认找到了目标版本,之后如果换机器路径变了,改PATHS或者传GFLAGS_ROOT。

find_package_handle_standard_args负责把GFLAGS_INCLUDE_DIR和GFLAGS_LIBRARY这两个变量翻译成标准的gflags_FOUND,顺带在找不到时打印一条可读的错误。后面把变量整理成GFLAGS_INCLUDE_DIRS和GFLAGS_LIBRARIES,是为了兼容 Caffe 里已有的用法——它期望的就是这两个名字。

在顶层CMakeLists.txt里挂上去:

# 把自己的模块目录插到搜索路径最前面,优先于 Caffe 自带的 list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/Modules") find_package(gflags REQUIRED) if(gflags_FOUND) message(STATUS "Using gflags: ${GFLAGS_LIBRARIES}") message(STATUS "gflags headers: ${GFLAGS_INCLUDE_DIRS}") include_directories(${GFLAGS_INCLUDE_DIRS}) endif()

list(INSERT ... 0 ...)把自定义目录插到索引 0,也就是最前面,保证覆盖 Caffe 自带的同名模块。后面两条message(STATUS ...)不是装饰,是给配置阶段留证据——每次cmake ..的输出里都能看到实际用的是哪份库,出问题时翻构建日志一眼就能定位,比编到一半报错再回头猜要快得多。

4.3 pkg-config 与命令行直编两种兜底方式

不是所有工程都用 CMake。Caffe 的 Makefile 配置那条路走的是Makefile.config里写死路径,纯命令行编译的小工具则直接给-l参数。这两种场景各有一套写法。

先看 pkg-config。gflags 2.1.1 的安装规则里是否带.pc文件,不同来源的归档包不一样,先查再决定用不用:

find /usr/local -name "gflags.pc" 2>/dev/null pkg-config --modversion gflags

第二条有输出说明能用,输出空就是没有.pc文件,这条路直接放弃,别硬试。有的话,编译命令可以写得干净些:

g++ -std=c++11 demo.cpp -o demo $(pkg-config --cflags --libs gflags) -lpthread

--cflags给头文件路径,--libs给库路径和库名,命令替换把结果摊进编译命令。注意补充的-lpthread——gflags 内部用了线程相关的设施,glibc 2.34 之前的系统必须显式链上,漏了会报undefined reference to pthread_*。

没有.pc文件就用最原始的方式,路径写死:

g++ -std=c++11 demo.cpp -o demo \ -I/usr/local/include \ -L/usr/local/lib \ -lgflags -lpthread

-I指向头文件所在的父目录,因为代码里写的是#include <gflags/gflags.h>,编译器会在这个目录下拼gflags/gflags.h。-L同理,指向库目录。-lgflags让链接器去找libgflags.so或者libgflags.a。

Makefile 场景下,把这几项写进变量:

GFLAGS_INC := /usr/local/include GFLAGS_LIB := /usr/local/lib CFLAGS += -I$(GFLAGS_INC) LDFLAGS += -L$(GFLAGS_LIB) -lgflags -lpthread

写成变量而不是散在命令行里,好处是换前缀只改两行。注意LDFLAGS里的顺序,-L必须在-lgflags前面,链接器的目录搜索参数对顺序敏感,写反了会报找不到库。

5. 避坑排查:五条现场记录

装 gflags 2.1.1 遇到问题的形态高度集中,就那么几类。下面五条按「现象 → 原因 → 解决」写,都是我实际踩过、并且不止踩过一次的。

5.1 链接报recompile with -fPIC

现象:编译 Caffe 到最后链接_caffe.so这一步,报/usr/local/lib/libgflags.a(gflags.cc.o): relocation R_X86_64_32S against '.rodata' can not be used when making a shared object; recompile with -fPIC。

原因:gflags 2.1.1 默认BUILD_SHARED_LIBS=OFF,产出的是静态库,而且这个版本的 CMakeLists 没给静态库加-fPIC,生成的目标文件不包含位置无关代码。静态库被链进可执行文件没问题,被链进.so就会被链接器拒绝,因为共享库加载地址随机,代码必须能搬到任意地址执行。

解决:删掉 build 目录重编,配置时加-DBUILD_SHARED_LIBS=ON。如果工程确实要求静态链接,那就保留OFF,同时给-DCMAKE_CXX_FLAGS="-fPIC"和-DCMAKE_C_FLAGS="-fPIC",让静态库也带位置无关代码。两个方案选一个,别混用。

5.2 运行时报error while loading shared libraries: libgflags.so.2.1

现象:程序编译链接全过,一运行就报找不到libgflags.so.2.1,但ls /usr/local/lib明明能看到这个文件。

原因:动态链接器运行时读的是/etc/ld.so.cache,不是实时扫目录。make install只是把文件拷到/usr/local/lib,没更新缓存,链接器不知道这个库的存在。/usr/local/lib在部分系统上并不在默认搜索路径里,只有ldconfig把它登记进缓存之后才生效。

解决:sudo ldconfig刷新缓存,再ldconfig -p | grep gflags确认。如果前缀是自定义的(比如/opt/gflags-2.1.1/lib),ldconfig默认不管这个目录,需要写配置文件:

echo "/opt/gflags-2.1.1/lib" | sudo tee /etc/ld.so.conf.d/gflags-2.1.1.conf sudo ldconfig

临时应急可以export LD_LIBRARY_PATH=/opt/gflags-2.1.1/lib:$LD_LIBRARY_PATH,但别写进.bashrc,长期靠环境变量兜底会让问题越藏越深,换台机器就复现。

5.3 CMake 找到的是系统里那份 2.2

现象:配置阶段一切正常,编译时报'gflags' has not been declared,或者链接期报undefined reference to google::ParseCommandLineFlags。

原因:CMake 的find_package命中了系统/usr/lib/x86_64-linux-gnu/下那份 2.2。2.2 的头文件里符号在gflags命名空间,google命名空间只留了兼容别名,而老 Caffe 代码写死google::ParseCommandLineFlags,两者对不上。

解决:在自己的模块里加NO_DEFAULT_PATH限制查找范围,或者配置时显式传前缀-DGFLAGS_ROOT=/opt/gflags-2.1.1。配置完看CMakeCache.txt里的GFLAGS_INCLUDE_DIR指向哪,不对就删掉 build 目录重来——CMake 会把查找结果缓存下来,不删缓存的话,你改了参数它照样用老值。

5.4 头文件和库版本错配,编译通过但行为诡异

现象:编译链接都过,程序跑起来 flag 解析结果不对,或者某些 flag 干脆不生效,加--help也看不到自定义 flag。

原因:头文件来自一份 gflags,库来自另一份。比如-I指到了/usr/local/include(2.1.1 的头),-L却因为搜索顺序命中了系统 2.2 的.so。两代之间内部结构体的布局有过调整,按新头文件生成的访问代码去读老库的数据,越界读或者读到垃圾值,表现就是「不崩但不对」。

解决:用ldd看运行时实际加载哪一份:

ldd ./demo | grep gflags

输出应该指向你装的那份。不一致就调整-L顺序,或者干脆把系统那份卸了。CMake 工程里同样可以在配置阶段打印GFLAGS_LIBRARIES确认。

5.5 编译测试目标失败,整个构建返回非零

现象:make跑到 60% 左右报错,错误信息里出现 gtest 相关的头文件找不到,前面编好的库其实没问题,但整个构建以失败退出。

原因:gflags 2.1.1 的 CMakeLists 默认开了测试开关,测试代码依赖 gtest。本机没装 gtest,测试目标就编不过。这个问题迷惑性强,因为报错位置离真正的库代码很远。

解决:配置时加-DBUILD_TESTING=OFF。已经配过的话,删 build 目录重来,别在缓存上改。真需要跑测试就先装 gtest 开发包,但为了装一个参数解析库而引入一份测试框架的依赖,多数时候不划算。

注意:这五条里有三条的根因都是「系统里存在多份 gflags」。装之前花两分钟把 4.1 节那三条命令跑一遍,能省掉后面大部分的排查时间。

6. 装完怎么验证:一段代码加一个 flagfile

装完不验证,等于没装。验证要简单到一分钟能跑完,又要能覆盖真实使用路径。我用的是一段二十行的 demo,加上一个 flagfile,两样东西就能把「头文件对不对、库链没链上、运行时加载哪一份、参数解析正不正常」全测出来。

// demo.cpp:同时覆盖 string、int32、bool 三种 flag 类型 #include <gflags/gflags.h> #include <iostream> DEFINE_string(name, "world", "who to greet"); DEFINE_int32(times, 1, "how many times to repeat"); DEFINE_bool(verbose, false, "print extra info"); int main(int argc, char** argv) { // 第三个参数 true 表示解析完把 flag 从 argv 里摘掉 google::ParseCommandLineFlags(&argc, &argv, true); if (FLAGS_verbose) { std::cout << "[verbose] times=" << FLAGS_times << "\n"; } for (int i = 0; i < FLAGS_times; ++i) { std::cout << "hello " << FLAGS_name << "\n"; } return 0; }

DEFINE_string/DEFINE_int32/DEFINE_bool是 gflags 定义 flag 的三个宏,宏的第一个参数就是 flag 名,第二个是默认值,第三个是--help里显示的说明。google::ParseCommandLineFlags是 2.1.1 的入口,注意前缀是google::而不是gflags::,这一点直接决定编译能不能过。第三个参数传true,解析后会把已识别的 flag 从argv里移除,剩下的是位置参数,后面要处理文件名之类的输入时很有用。

编译并跑三种情况:

# 直编方式 g++ -std=c++11 demo.cpp -o demo -I/usr/local/include -L/usr/local/lib -lgflags -lpthread ./demo ./demo --name=caffe --times=3 ./demo --help | head -n 20

第一条不带参数,输出一行hello world,说明默认值生效、库链上了、运行时加载正常。第二条输出三行hello caffe,说明字符串和整型 flag 都能解析。第三条打印帮助,能看到--name、--times、--verbose三个自定义 flag 和它们的中文说明——这一条最关键,它证明 gflags 的注册机制在运行时真的跑起来了,而不只是链接成功。

再用 flagfile 测一次,这是 Caffe 训练脚本里最常用的用法:

# 把参数写进文件,一行一个 cat > train.flags <<'EOF' --name=caffe --times=2 --verbose EOF ./demo --flagfile=train.flags

输出两行hello caffe加一行[verbose] times=2。--flagfile让 gflags 从文件读参数,文件里一行一个,--flag和-flag两种写法都认。Caffe 的 solver 和训练脚本大量用这个方式传参,这一步过了,说明你装的这份 2.1.1 已经能承接真实工程的参数流。

最后用 CMake 方式再走一遍,确认find_package那条路也通:

cmake_minimum_required(VERSION 3.5) project(gflags_check CXX) set(CMAKE_CXX_STANDARD 11) list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake/Modules") find_package(gflags REQUIRED) message(STATUS "gflags lib: ${GFLAGS_LIBRARIES}") add_executable(demo demo.cpp) target_include_directories(demo PRIVATE ${GFLAGS_INCLUDE_DIRS}) target_link_libraries(demo ${GFLAGS_LIBRARIES} pthread)

配置输出里那行gflags lib:后面跟的路径,就是最终链接的库。我习惯把这个输出截图存进工程的部署记录里,半年后别人接手,看到路径就知道当时装的是哪一份,不用重新摸一遍。

从那以后我每次装这类老版本 C++ 库,都强制走一遍「先查重、再编、编完 ldd 验、验完记路径」四步,哪怕只是临时装一台测试机也一样。少走一步,后面排查花的时间都是它的十倍。这套 gflags 2.1.1 的安装流程你可以直接抄,坑我都替你踩过一遍了。希望帮到你。

本文还有配套的精品资源,点击获取