CMakeLists.txt添加编译标志后pthread链接失败的原因及解决方法
首先来说说你遇到的问题根源:你把链接器专用的-lpthread标志放到了CMAKE_CXX_FLAGS里,这个变量是给编译阶段用的。虽然有些情况下编译器会顺手把这些标志传给链接器,但CMake的构建流程里,编译和链接是完全分离的步骤,这种做法既不规范,还容易因为链接顺序的问题导致符号找不到。
直接用g++命令时,-lpthread在命令行的最后,链接器会先处理你的目标文件,再从pthread库中查找未定义的符号,所以能正常工作。但在CMake里如果把-lpthread塞进CMAKE_CXX_FLAGS,链接时它的位置可能在你的目标文件之前——链接器是从左到右处理的,这时候它会先扫描pthread库,发现暂时不需要里面的符号就跳过,等处理到你的目标文件时,再找pthread的符号就找不到了,这就是你看到undefined reference to pthread_detach的原因。
接下来给你两种解决方案,优先推荐第一种:
方案一:使用CMake自带的FindThreads模块(跨平台友好)
CMake有专门处理线程库的模块,能自动适配不同操作系统(比如Linux下用pthread,Windows下用系统线程库),不需要手动写-lpthread。修改你的CMakeLists.txt如下:
cmake_minimum_required (VERSION 3.0) project (foo) # 设置C++11标准,比直接加-std=c++11更规范 set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD_REQUIRED ON) # 添加Boost相关的编译定义 add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY) # 查找线程库 find_package(Threads REQUIRED) # 生成可执行文件 add_executable (first first.cpp) # 把线程库链接到目标上 target_link_libraries(first PRIVATE Threads::Threads)
这种方式是CMake官方推荐的做法,能保证链接顺序正确,而且跨平台兼容性拉满。
方案二:手动指定链接库(仅适用于Linux类系统)
如果你一定要手动控制链接标志,别再往CMAKE_CXX_FLAGS或者CMAKE_EXE_LINKER_FLAGS里塞了,直接用target_link_libraries把pthread库链接到你的目标上:
cmake_minimum_required (VERSION 3.0) project (foo) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DBOOST_ERROR_CODE_HEADER_ONLY") add_executable (first first.cpp) # 直接链接pthread库 target_link_libraries(first PRIVATE pthread)
要是你坚持想用CMAKE_EXE_LINKER_FLAGS,得确保它被正确应用,但这种方式不如第一种跨平台,不推荐:
cmake_minimum_required (VERSION 3.0) project (foo) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DBOOST_ERROR_CODE_HEADER_ONLY") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread") add_executable (first first.cpp)
总结一下:CMake里编译和链接是分开的阶段,编译标志和链接标志要放到对应的变量里,或者用更规范的target_link_libraries来管理依赖,这样才能避免链接顺序带来的问题。
内容的提问来源于stack exchange,提问作者user6646922

