CMake中为何add_executable可链接对象文件却无法直接链接库?
问题描述
我写了下面这个简单的CMakeLists.txt:
project(MyProject) # Create the library from server.cpp add_library(server OBJECT tcp_server.cpp tcp_server_priv.cpp) # Add the current directory to the include path for this target target_include_directories(server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) # Create the executable from main.cpp and link it to the library add_executable(my_server main.cpp $<TARGET_OBJECTS:server>)
我知道add_library(x OBJECT y z ...)不会生成静态或共享库(.a或.so),只是对目标文件做逻辑分组,能通过$<TARGET_OBJECTS:server>引用tcp_server.o和tcp_server_priv.o这两个目标文件,上面的add_executable语句会编译main.cpp并链接这些对象文件生成my_server可执行文件。
我想让可执行文件链接libpthread,于是把add_executable语句改成这样:
add_executable(my_server main.cpp $<TARGET_OBJECTS:server> pthread)
但CMake报错了:
-- Configuring done CMake Error at src/CMakeLists.txt:9 (add_executable): Cannot find source file: pthread Tried extensions .c .C .c++ .cc .cpp .cxx .cu .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx CMake Error at src/CMakeLists.txt:9 (add_executable): No SOURCES given to target: med_server CMake Generate step failed. Build files cannot be regenerated correctly. make: *** [Makefile:160: cmake_check_build_system] Error 1
而用g++命令可以在同一个调用里编译源文件、链接对象文件和库:
tcp_server.cpp代码:
#include <iostream> extern void private_func(); void public_func() { std::cout << __FUNCTION__ << std::endl; private_func(); }
tcp_server_priv.cpp代码:
#include <iostream> void private_func() { std::cout << __FUNCTION__ << std::endl; }
main.cpp代码:
#include <semaphore.h> extern void public_func(); int main(int argc, char* argv[]) { sem_t my_sem; public_func(); sem_init(&my_sem, 0, 0); sem_destroy(&my_sem); return 0; }
执行的g++命令和结果:
$ g++ -c ./tcp_server.cpp \ > && g++ -c ./tcp_server_priv.cpp \ > && g++ main.cpp tcp_server.o tcp_server_priv.o -lpthread \ > && ./a.out public_func private_func $
我有两个疑问:
- 为什么
add_executable语句能链接对象文件,却不能直接链接库? - 我尝试的
add_executable写法和上面的g++命令等效吗?
解答
问题1:为什么add_executable不能直接链接库?
CMake的add_executable命令逻辑和g++完全不同:
add_executable的参数里,除目标名外只能放源文件(.cpp/.c等)或对象文件引用(比如$<TARGET_OBJECTS:server>),它的核心是定义要编译的目标,不负责处理链接依赖。- 库文件(如pthread)属于链接阶段的依赖,CMake专门用
target_link_libraries命令管理这类依赖,这是模块化设计的结果,把编译目标定义和链接依赖管理分开,保证配置清晰易维护。 - 你写的
pthread会被CMake当成待编译的源文件查找,自然找不到对应后缀的文件,所以报错。
问题2:尝试的add_executable写法和g++命令不等效
两者逻辑完全无关:
- g++命令里的
-lpthread是告诉编译器链接阶段要关联libpthread库; - 你在
add_executable里加pthread,是让CMake寻找名为pthread的源文件,和链接库的需求完全不沾边。
正确的CMake写法
要实现和给出的g++命令等效的效果,应该用target_link_libraries添加pthread依赖:
project(MyProject) add_library(server OBJECT tcp_server.cpp tcp_server_priv.cpp) target_include_directories(server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(my_server main.cpp $<TARGET_OBJECTS:server>) # 添加pthread链接依赖 target_link_libraries(my_server PRIVATE pthread)
另外,对于pthread,更推荐用FindThreads模块,兼容性更好:
project(MyProject) # 引入FindThreads模块 find_package(Threads REQUIRED) add_library(server OBJECT tcp_server.cpp tcp_server_priv.cpp) target_include_directories(server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(my_server main.cpp $<TARGET_OBJECTS:server>) # 使用Threads::Threads目标来链接 target_link_libraries(my_server PRIVATE Threads::Threads)
内容的提问来源于stack exchange,提问作者StoneThrow
相关产品推荐
相关产品推荐

