ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Caffe编译依赖:gflags 2.1.1安装、路径配置与排错

Caffe编译依赖:gflags 2.1.1安装、路径配置与排错 简介gflags-2.1.1 是 Google 开源命令行标志解析库的稳定历史版本面向需在 C 程序中动态管理运行参数的开发者尤其是仍在维护旧版 Caffe 框架的工程人员。它将参数定义、命令行解析、配置文件读取与 --help 帮助输出集于一体使学习率、批处理大小等训练参数无需改代码重编译即可调整。压缩包共 50 个文件、约 100KB以 9 个 cc 源文件与 4 个 h 头文件承载核心实现辅以 7 个 cmake 构建脚本和 10 个 in 配置模板说明编译安装流程另有 txt 文档、flagfile 示例与单元测试用例可完整走通配置到验证的链路。目前已有 222 人学习下载。读者可获取可直接编译的完整源码树、覆盖解析与标志注册的测试用例、flagfile 配置样例及 CMake 集成参考便于在旧项目中复现稳定行为或借此理解 gflags 的标志注册、参数校验与多语言接口设计。1. 版本先定死为什么 Caffe 这条链上偏偏要 gflags 2.1.1装 gflags 2.1.1 这件事本身不复杂复杂的是它在 Caffe 编译链里的位置。很多人第一反应是去拉最新版结果 Caffe 一编译就是满屏的ParseCommandLineFlags is not a member of google——因为 2.2 之后默认命名空间从google换成了gflags老代码里写死的google::前缀集体失效。反过来把 2.1.1 老老实实装进 /usr/local却忘了执行ldconfig跑起来又是libgflags.so.2.1: cannot open shared object file。这套环境在复现老 Caffe 工程、跑只认 2.1.x 的推理脚本、给旧版训练代码补编译依赖时特别常见装之前把版本、prefix、命名空间三件事定死比装完再救省事得多。2. 装前清场编译器、CMake 与系统里那几份 gflags2.1 工具链版本下限与一条命令自检gflags 2.1.1 的 CMakeLists.txt 里写的是cmake_minimum_required(VERSION 2.8.12)也就是说 CMake 已经低到几乎不用挑它本身还是 C98 兼容的代码不像 2.2.2 那样硬性要求 C11。真正卡人的是它下游的 CaffeCaffe 要 C11所以编译器版本反而成了实际下限。我这边常用的一套组合在下表里Ubuntu 16.04 到 20.04 都能照这个范围配。组件最低可用我这边实际说明GCC / G4.87.5.0Caffe 需要 C11gflags 2.1.1 自身不需要CMake2.8.123.10.22.1.1 的构建脚本按老版本写新版 CMake 也能跑make3.814.1并行编译-j依赖它系统Ubuntu 16.04Ubuntu 18.04只影响包名不影响源码编译进机器第一件事不是解压源码而是把这三条敲一遍确认没有多版本混装# 确认编译器与构建工具的真实指向不要只看 which gcc --version | head -1 g --version | head -1 cmake --version | head -1 make --version | head -1这里的关键是head -1。有些机器上/usr/bin/gcc是 7.x但 PATH 前面挂了个 conda 或某个自编译工具链gcc --version会打出另一套版本。Caffe 编译时用的是 Makefile 里展开的$(CC)和你手动敲的未必是同一个先看清楚再往下走。提示如果你打算在同一台机器上同时维护两套 Caffe一套老版、一套新版建议把 gflags 装到独立 prefix比如/opt/gflags-2.1.1别急着塞进 /usr/local后面第 4 章会讲怎么在 Makefile.config 里指过去。2.2 把系统自带的 gflags 揪出来并决定怎么处理这一步是整套安装里最容易被跳过、也最容易在链接期爆炸的环节。Ubuntu 的libgflags-dev装的是 2.1.2 或 2.2.x头文件在/usr/include/gflags/库在/usr/lib/x86_64-linux-gnu/和你要装的 2.1.1 完全平行。两份头文件、两份 so 同时存在编译器先看到哪一份取决于-I的顺序链接器先找到哪一份取决于-L的顺序和ld.so.cache——这就是玄学感的来源。先查清楚现场# 包管理器层面有没有 apt 装的 gflags dpkg -l | grep -i gflags # 文件层面三处常见位置分别看一眼 ls -l /usr/lib/x86_64-linux-gnu/libgflags* 2/dev/null ls -l /usr/local/lib/libgflags* 2/dev/null ls -l /usr/include/gflags/ 2/dev/null # 动态链接器缓存里注册了哪个版本 ldconfig -p | grep gflags如果dpkg -l有输出说明系统级已经有一份。三种处理策略按你的实际情况选第一种直接卸掉sudo apt remove libgflags-dev最干净但要注意它可能被别的包依赖apt remove会提示此时改用第二种。第二种保留但让 /usr/local 优先Caffe 的 Makefile.config 里INCLUDE_DIRS默认就把/usr/local/include放在前面链接时-L/usr/local/lib也排在/usr/lib前面只要不动默认顺序就不会串。第三种装到独立 prefix在 Caffe 里显式指定路径适合多人共用一台机器、谁也不敢动系统库的场景。还有一种藏得深的conda 环境。echo $CONDA_PREFIX如果有值去看$CONDA_PREFIX/lib/libgflags*和$CONDA_PREFIX/include/gflags/。conda 装的 gflags 常常是 2.2.x而且它的include目录会被某些构建脚本自动加进搜索路径。我一般会在编译 Caffe 前conda deactivate把变量环境清干净再动手省得后面拿ldd一层层倒查。2.3 源码包落盘与目录约定目录约定看着是小事实际上决定了后面出问题时好不好回滚。我的习惯是源码统一放/opt/src构建目录和源码目录分离安装 prefix 用/usr/local——因为 Caffe 的 Makefile.config 默认就去这两个地方找不用改任何路径。# 源码统一存放避免散落在 home 目录里找不回来 sudo mkdir -p /opt/src cd /opt/src # 解压后顶层目录就是 gflags-2.1.1 sudo tar -zxvf gflags-2.1.1.tar.gz cd gflags-2.1.1 # 确认包是完整的这几个文件缺一个都别往下走 ls CMakeLists.txt configure.ac include/gflags/ src/解开后应该能看到CMakeLists.txt、configure.ac、Makefile.am、include/gflags/、src/、cmake/这几项。include/gflags/下有三个头文件gflags.h、gflags_declare.h、gflags_completions.h其中gflags_completions.h是 2.1.x 时代的东西2.2 之后被移除了后面第 5 章讲升级翻车时会用到这个细节。configure.ac和Makefile.am同时存在说明 2.1.1 还保留了 autotools 那一套这一点和后来的版本不一样第 3 章末尾会单独说什么时候才值得走那条路。注意解压时用tar -zxvf而不是直接双击解压一是保证权限位正确二是避免某些归档工具把符号链接解成普通文件后面make会报一些莫名其妙的错。3. 编译安装gflags 2.1.1 的 CMake 参数逐个过3.1 配置阶段五个开关决定后面会不会翻车gflags 2.1.1 的 CMake 默认值有几个和直觉相反的地方不显式覆盖就会在后面某个环节找你麻烦。最典型的是BUILD_SHARED_LIBS默认 OFF——默认只产出静态库libgflags.a而 Caffe 编出来的是libcaffe.so动态库链接没有开 PIC 的静态库直接报relocation R_X86_64_32 ... can not be used when making a shared object。我用的配置命令是这样每一条都有理由# 构建目录和源码分离改参数时直接删掉重建不留缓存 cd /opt/src/gflags-2.1.1 rm -rf build mkdir build cd build cmake .. \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_INSTALL_PREFIX/usr/local \ -DBUILD_SHARED_LIBSON \ -DCMAKE_POSITION_INDEPENDENT_CODEON \ -DBUILD_TESTINGOFF \ -DGFLAGS_NAMESPACEgoogleCMAKE_BUILD_TYPERelease2.1.1 不设这个变量时构建类型是空的编译器不加优化产出的库性能明显偏低。Caffe 前向推理里参数解析不是瓶颈但没理由留个 O0 的库在系统里。CMAKE_INSTALL_PREFIX/usr/local这是 Caffe 默认搜索路径。改成别的目录不是不行但后面 Makefile.config 的INCLUDE_DIRS和LIBRARY_DIRS都得跟着改多一处手工维护就多一处出错的地方。BUILD_SHARED_LIBSON产出libgflags.so。如果某个项目非要静态链接2.1.1 在同一个构建里也会生成libgflags_nothreads.a之类够用。反过来只编静态库再想补动态库就得整个重编。CMAKE_POSITION_INDEPENDENT_CODEON即使这次编的是动态库打开它也保险。将来有人把这套 gflags 改成静态库链接进别的 .so不会因为 PIC 再翻一次车。这一条属于后悔药成本几乎为零。BUILD_TESTINGOFF不关的话2.1.1 会把gflags_unittest一起编出来多花几十秒到几分钟。第一次装建议留着跑一遍单测确认库本身没问题跑完再改 OFF 重编一次或者干脆留着反正不影响安装产物。GFLAGS_NAMESPACEgoogle这条最容易被忽略。2.1.1 的默认值本来就是google显式写出来的意义在于万一以后有人在这份 CMakeCache 上改成gflags或者你把这套参数抄到别的版本上差异会立刻暴露而不是在 Caffe 链接阶段才以undefined reference的形式出现。配置跑完别急着make先确认缓存里的值真的是你写进去的# 确认关键变量落地防止命令行拼错被 CMake 静默忽略 grep -E GFLAGS_NAMESPACE|BUILD_SHARED_LIBS|CMAKE_INSTALL_PREFIX CMakeCache.txt输出里应该能看到GFLAGS_NAMESPACE:STRINGgoogle、BUILD_SHARED_LIBS:BOOLON、CMAKE_INSTALL_PREFIX:PATH/usr/local。如果某个变量不见了多半是拼写问题——CMake 对不认识的-D变量不报错只在缓存里留下一个 unused 警告很容易看漏。3.2 编译安装与 ldconfig并行度、DESTDIR 和 sudo 的边界配置对了编译本身没什么悬念但有两个细节值得说并行度和安装后的动态链接器缓存。# 并行编译核数用 nproc 拿别拍脑袋写 -j8 make -j$(nproc) # 安装到 prefix 指定的位置需要写 /usr/local 所以要 sudo sudo make install # 刷新动态链接器缓存这一步不做运行时就找不到 so sudo ldconfigmake -j$(nproc)在小内存机器上要小心。gflags 2.1.1 的源码量不大一般不会因为并行度过高把内存打爆但如果你的机器同时还跑着别的构建任务-j开到核数容易触发 OOM Killer表现是make突然被 kill、没有任何报错。稳妥做法是-j$(($(nproc)/2))慢一点但不会中断。sudo ldconfig是必须的。/usr/local/lib在多数发行版里已经写进/etc/ld.so.conf.d/的某个配置文件中ldconfig会把它扫进缓存程序运行时按 SONAME 找库就能命中。少了这一步make install明明成功ldd却告诉你libgflags.so.2.1 not found很多人到这一步开始怀疑人生。装完立刻验证产物别等到 Caffe 编译失败才回头查# 1. 库文件应该有实体文件和两条符号链接 ls -l /usr/local/lib/libgflags* # 2. 头文件三个 .h 都在 ls -l /usr/local/include/gflags/ # 3. CMake 配置文件供 find_package 使用 ls -l /usr/local/lib/cmake/gflags/ # 4. pkg-config 文件 ls -l /usr/local/lib/pkgconfig/gflags.pc预期看到libgflags.so.2.1.1是实体libgflags.so.2.1和libgflags.so是指向它的符号链接。SONAME 是libgflags.so.2.1所以运行时找的是中间那条链接这也是为什么后面报错信息里总是出现libgflags.so.2.1而不是2.1.1。/usr/local/lib/cmake/gflags/下的几个文件决定了find_package(gflags)能不能走 config 模式Caffe 的 CMake 构建会用到第 4 章展开。gflags.pc则是 pkg-config 那条路的入口注意它装在/usr/local/lib/pkgconfig这个路径默认不在pkg-config的搜索列表里需要手动加环境变量。如果这次装完想推倒重来2.1.1 的 Makefile 里保留了卸载规则# 卸载依赖安装时生成的 install_manifest.txt必须在 build 目录里执行 cd /opt/src/gflags-2.1.1/build sudo make uninstallmake uninstall只删它自己记录过的文件。如果你中间手动往/usr/local/include/gflags/里拷过东西或者装过另一个版本覆盖了同名文件卸载是不彻底的得手工比对install_manifest.txt清理。3.3 autotools 那条老路什么时候才值得走2.1.1 的源码包里configure.ac和Makefile.am都还在理论上可以走./configure make make install那条经典路径# autotools 方式仅在 CMake 不可用时考虑 cd /opt/src/gflags-2.1.1 ./configure --prefix/usr/local --enable-shared make -j$(nproc) sudo make install命令能跑通但产物和 CMake 方式不一样autotools 不会生成/usr/local/lib/cmake/gflags/那一套 config 文件。如果你的下游是 Caffe 的 CMake 构建它走find_package(gflags)时会退回FindGFlags.cmake里的手工查找逻辑能找到库但版本判断和 include 路径的推断不如 config 模式精确。我的取舍是只要机器上有 CMake 3.x一律走 CMake。autotools 这条路留给两种情况——一是目标环境是某种精简系统CMake 装不上二是你要做交叉编译需要--host参数那套工具链传递机制。除此之外没有理由绕远路。提示两条路不要混着走。先用 autotools 装一遍再用 CMake 装一遍覆盖/usr/local/lib下容易出现同名但来源不同的 .soldconfig之后到底加载哪个取决于文件名排序和时间戳排查起来非常费劲。要么彻底make uninstall再换要么一开始就选对。4. 接进 Caffe路径怎么指、库名怎么给4.1 Makefile.config 三个变量的填法Caffe 用 Makefile 构建时头文件搜索路径和库搜索路径全在Makefile.config里三个变量决定 gflags 能不能被找到# Makefile.config 片段路径类变量的默认值已经包含 /usr/local INCLUDE_DIRS : $(PYTHON_INCLUDE) /usr/local/include LIBRARY_DIRS : $(PYTHON_LIB) /usr/local/lib /usr/lib # 是否用 pkg-config 找依赖默认关闭 USE_PKG_CONFIG : 0INCLUDE_DIRS展开后变成一堆-I参数LIBRARY_DIRS展开成一堆-L参数。因为默认值里已经有/usr/local/include和/usr/local/libgflags 装在默认 prefix 时这三行一个字都不用改这也是第 3 章坚持用/usr/local的原因。如果你把 gflags 装到了别的目录比如/opt/gflags-2.1.1就要在这两行前面各加一项顺序放在最前面保证优先命中# 自定义 prefix 时放在最前面强制优先 INCLUDE_DIRS : /opt/gflags-2.1.1/include $(PYTHON_INCLUDE) /usr/local/include LIBRARY_DIRS : /opt/gflags-2.1.1/lib $(PYTHON_LIB) /usr/local/lib /usr/lib顺序不能反。C 编译器的-I是按出现顺序查找的链接器的-L同理谁在前谁生效。系统里同时存在/usr/include/gflags/gflags.h2.1.2 的和/usr/local/include/gflags/gflags.h2.1.1 的时顺序写反就会编进另一份头文件然后链接时库又是另一份版本错位的经典现场。USE_PKG_CONFIG这个开关打开后会走 pkg-config 找 gflags、glog、protobuf 等依赖。用它能省掉手写路径但前提是gflags.pc在pkg-config的搜索路径里。前面说过/usr/local/lib/pkgconfig默认不在列表内所以要配合环境变量# 让 pkg-config 能找到 /usr/local 下安装的 .pc 文件 export PKG_CONFIG_PATH/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH # 验证版本号应该输出 2.1.1 pkg-config --modversion gflags pkg-config --cflags --libs gflags--modversion输出2.1.1才算找对了。如果输出的是2.1.2或者2.2.2说明命中的是系统包的那份.pc文件检查PKG_CONFIG_PATH有没有生效、/usr/lib/x86_64-linux-gnu/pkgconfig/gflags.pc是不是还在。4.2 CMake 构建 Caffe 时 FindGFlags 的查找顺序换到 CMake 构建 Caffe找 gflags 的逻辑在cmake/Modules/FindGFlags.cmake里。它大致分两步先尝试 config 模式也就是找gflags-config.cmake找不到再退回手工find_pathfind_library在标准路径和GFlags_ROOT之类的提示变量里搜。判断有没有找对看 CMake 配置阶段的这一行输出# 在 Caffe 的 build 目录里执行从输出里捞出 gflags 那一行 cmake .. 21 | grep -i gflags正确的结果长这样include 和 library 都指向/usr/localFound gflags (include: /usr/local/include, library: /usr/local/lib/libgflags.so)如果 library 指向/usr/lib/x86_64-linux-gnu/libgflags.so说明系统那套抢先了如果 include 指向/usr/include而 library 指向/usr/local/lib就是典型的两份混用必须停下来处理不能带着这个状态往下编译。这种组合在链接期不一定报错但运行时行为会非常诡异比如某些 flag 解析不出来、--help输出缺项。实在要强制指定可以用这两个变量覆盖# 显式指定绕过 FindGFlags 的自动搜索 cmake .. \ -DGFlags_INCLUDE_DIR/usr/local/include \ -DGFlags_LIBRARIES/usr/local/lib/libgflags.soCMake 的缓存机制在这里也是个坑。第一次配置时找错了改完环境变量重新cmake ..它可能还用缓存里的旧值。稳妥做法是把build/CMakeCache.txt删掉重新配置而不是反复cmake ..试运气。4.3 用 pkg-config 和一段 CMake 脚本先解耦验证在把 Caffe 牵扯进来之前最好单独确认 gflags 的两种查找方式都通。pkg-config 那条刚才已经验过CMake 这边写个最小工程几行就够# /tmp/check_gflags/CMakeLists.txt cmake_minimum_required(VERSION 3.5) project(check_gflags CXX) # 走 config 模式找 /usr/local/lib/cmake/gflags/gflags-config.cmake find_package(gflags REQUIRED) message(STATUS gflags_INCLUDE_DIR ${gflags_INCLUDE_DIR}) message(STATUS gflags_LIBRARIES ${gflags_LIBRARIES})# 配置并观察输出不编译任何东西 mkdir -p /tmp/check_gflags/build cd /tmp/check_gflags/build cmake .. 21 | grep gflagsfind_package(gflags REQUIRED)优先走 config 模式2.1.1 安装的那套gflags-config.cmake会把gflags_INCLUDE_DIR和gflags_LIBRARIES两个变量填好。如果输出里这两个值都是空的但也没报Could not find的错误说明命中了系统里某个不完整的 config 文件ls /usr/lib/x86_64-linux-gnu/cmake/下一层层找就能定位。这一步的价值在于把问题隔离开。gflags 自己的查找链路通了再去编 Caffe出问题就只可能是 Caffe 的路径配置或依赖顺序不用在两个项目之间来回怀疑。5. 踩坑记录五个把编译卡死的现场5.1 编译链接阶段的三次翻车坑一relocation R_X86_64_32 against symbol ... can not be used when making a shared object现象Caffe 的make all走到链接libcaffe.so那一步报错错误信息很长关键词就是recompile with -fPIC。原因是 gflags 2.1.1 的 CMake 默认BUILD_SHARED_LIBSOFF产出的libgflags.a没开位置无关代码往动态库里塞就炸。解决回第 3 章的配置命令加-DBUILD_SHARED_LIBSON和-DCMAKE_POSITION_INDEPENDENT_CODEON删掉 build 目录重编重装。已经装过静态版的先把/usr/local/lib/libgflags.a和对应的 cmake 配置文件清掉再装一次别让两份产物并存。坑二undefined reference to google::ParseCommandLineFlags(int*, char***, bool)现象编译能过链接阶段报符号找不到而且符号名里带着google::。原因多半是头文件和库版本不匹配——头文件来自 2.1.2/usr/include库里导出的是别的命名空间或者链接顺序有问题-lgflags排在-lcaffe前面静态库的符号没被拉进来。解决先ldd .build_release/lib/libcaffe.so | grep gflags看实际链到了哪个路径再nm -D /usr/local/lib/libgflags.so | grep ParseCommandLineFlags确认符号在不在。Caffe 的 Makefile 里库顺序是写死的真遇到顺序问题改用 -Wl,--start-group ... -Wl,--本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表