为什么使用add_library和add_executable链接目标对象库和库之间有区别?

问题描述 投票:0回答:2

这是我的简单

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),而是形成对象文件的一些临时/逻辑分组,可以在其他地方作为同一组引用。 IE。在其他地方,我可以参考
$<TARGET_OBJECTS:server>
来指代tcp_server.otcp_server_priv.o,所以
add_executable(my_server main.cpp $<TARGET_OBJECTS:server>)
行的意思是“编译main.cpp,并将其与tcp_server.o链接” tcp_server_priv.o 制作可执行文件我的服务器”。

add_executable
行同时进行编译和链接。

我希望我的可执行文件也与 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++ 调用中编译

main.cpp
、链接对象文件依赖项和库:

// 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++ -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(my_server main.cpp $<TARGET_OBJECTS:server> pthread)
实际上与
g++ main.cpp tcp_server.o tcp_server_priv.o -lpthread
不一样吗?

c++ cmake
2个回答
7
投票

TLDR:当您了解编译过程是什么样子时,区别会更加明显。

编辑:链接到编译模型以更好地理解,注意:您感兴趣仅对第一张图片


对于这个例子,我将使用一个静态库,即在 Linux 上,它会带有后缀

.a
,在 Windows 上
.lib
。让我们问问自己,当您构建并链接这个库时会发生什么:

  • 前面的步骤对于区分并不重要,所以让我们从下面的步骤开始
  • 编译器将单个
    .cpp
    (或多个)编译为
    .o
    目标文件
  • 然后将这些文件链接在一起/存档到库中
    .a
    /
    .lib

现在如果我们想“链接”一个static库:

  • 我们实际上会将这个存档
    .a
    解压回目标文件中
  • 然后我们将获取我们在可执行文件中编译的
    .o
    目标文件
  • 编译器做了一些优化并抛出不需要的目标文件
  • 将我们拥有的所有剩余目标文件重新打包并链接为可执行文件

所以你可以这样认为区别(以静态库为例):

目标文件:我们不必解压存档,只需将它们与新创建的目标文件链接即可

静态库:我们需要告诉编译器,嘿,我们需要先解压它,以便我们可以链接它。

CMake 的唯一作用是为编译器/链接器“创建某种配置”以完成工作。


关于

OBJECT
STATIC
库之间区别的一个有趣的事情是(这进一步证明了我提到的内容)是,如果您使用
OBJECT
库,您将无法利用优化。

假设您的静态库由三个对象

a.o
b.o
c.o
构成。如果将其打包到
lib.a
中,然后将可执行文件链接到它。优化可能会排除
c.o
(例如),这将使生成的可执行文件更小。

但是如果您与对象库链接

a.o
b.o
c.o
那么所有的对象将被打包为可执行文件。


1
投票

在你的

add_executable(my_server main.cpp $<TARGET_OBJECTS:server> pthread)

,问题在于

add_executable
中使用“pthread”,而不是
$<TARGET_OBJECTS:server>
。您应该这样做来链接到 pthread:

target_link_libraries(my_server PRIVATE pthread)

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)

另请参阅:cmake 和 libpthread 以及 CMake 对象库文档

正如对象库文档中所述,这就是 CMake 的工作原理。您可以像现在一样链接到对象库,但您也可以通过

target_link_libraries
来完成。您可以使用
target_link_libraries
链接到对象库(自 v3.12 起)和共享/静态库。对于
target_link_libraries
来说,没有“区别”。

© www.soinside.com 2019 - 2024. All rights reserved.