使用 cmake 时如何在 C++ 中使用库(*.a 文件)

Posted

技术标签:

【中文标题】使用 cmake 时如何在 C++ 中使用库(*.a 文件)【英文标题】:How to use libraries (*.a files) in C++ when using cmake 【发布时间】:2020-02-12 15:32:24 【问题描述】:

所以最近我又开始编写 C++。我想学习如何正确使用 Cmake,并从现在开始为我的爱好项目编写适当的测试。

我基本上只是想将 GTest 集成到我的项目中。我设置了一个 TestProject 目录并执行了以下操作:

    克隆了GTest repository 使用 cmake 在 googletest 存储库中构建代码 将googletest/build/lib 目录复制到TestProject/googletest/include 目录复制到TestProject/

现在当我尝试使用 cmake 时,链接时出现以下错误:

/usr/bin/ld: ./libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<testing::TestPartResultReporterInterface*>::~ThreadLocal()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED2Ev[_ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED5Ev]+0x24): undefined reference to `pthread_getspecific'
/usr/bin/ld: gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED2Ev[_ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEED5Ev]+0x39): undefined reference to `pthread_key_delete'
/usr/bin/ld: ./libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::~ThreadLocal()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED2Ev[_ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED5Ev]+0x24): undefined reference to `pthread_getspecific'
/usr/bin/ld: gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED2Ev[_ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEED5Ev]+0x39): undefined reference to `pthread_key_delete'
/usr/bin/ld: ./libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::GetOrCreateValue() const':
gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv]+0x25): undefined reference to `pthread_getspecific'
/usr/bin/ld: gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE16GetOrCreateValueEv]+0x88): undefined reference to `pthread_setspecific'
/usr/bin/ld: ./libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<testing::TestPartResultReporterInterface*>::CreateKey()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE9CreateKeyEv[_ZN7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE9CreateKeyEv]+0x27): undefined reference to `pthread_key_create'
/usr/bin/ld: ./libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::CreateKey()':
gtest-all.cc:(.text._ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE9CreateKeyEv[_ZN7testing8internal11ThreadLocalISt6vectorINS0_9TraceInfoESaIS3_EEE9CreateKeyEv]+0x27): undefined reference to `pthread_key_create'
/usr/bin/ld: ./libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<testing::TestPartResultReporterInterface*>::GetOrCreateValue() const':
gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv]+0x25): undefined reference to `pthread_getspecific'
/usr/bin/ld: gtest-all.cc:(.text._ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv[_ZNK7testing8internal11ThreadLocalIPNS_31TestPartResultReporterInterfaceEE16GetOrCreateValueEv]+0x88): undefined reference to `pthread_setspecific'

我的项目目录下的文件是:

TestProject/CMakeLists.txt

cmake_minimum_required(VERSION 3.16)

project(TestGoogleTest)

set(ROOT_DIR "$PROJECT_SOURCE_DIR")
set(SRC "$ROOT_DIR/src")
set(LIB "$ROOT_DIR/lib")
set(INCLUDE "$ROOT_DIR/include")

link_directories("$LIB")
include_directories("$INCLUDE")

message("INCLUDE PATH: $INCLUDE")

set(SOURCES "$SRC/main.cpp" "$INCLUDE/gtest/gtest.h")

add_executable(main "$SOURCES")
target_link_libraries(main libgtest.a)

TestProject/src/main.cpp:

#include "gtest/gtest.h"
#include <iostream>

int main(int argc, char **argv) 

  testing::InitGoogleTest(&argc, argv);
  std::cout << "Hello world!" << std::endl;
  return RUN_ALL_TESTS();

如果我用系统 gtest 命令手动编译它就可以了

g++ src/main.cpp -o main -lgtest

我很乐意了解导致问题的原因:)

【问题讨论】:

你还需要链接libpthread 除非路径TestProject/libgtest.a 有效,否则target_link_libraries() 命令可能不会知道找到这个库。您应该改为提供库的 完整 路径。更好的是,只需使用 CMake 的 FindGtest 模块在您的机器上定位 GTest,而不是手动复制所有内容.. @user253751 这导致了另一个相同类型的编译器错误。我可以继续包含所有库,但我觉得这个解决方案可能会很混乱。还是谢谢 @squareskittles 我尝试使用完整路径,但没有成功。但是,您发布的 FindGtest 链接非常有帮助。这很好用,在 CMakeLists.txt 中看起来很精简。最后一个问题:假设我想包含另一个不是 libgtest 的库。我用于 libgtest 的粗略方法是否适用于其他场景? @squareskittles 谢谢!我会这样做的。也感谢进一步阅读的来源:) 【参考方案1】:

简短说明

在你的CMakeLists.txt

您尚未为 c++11 设置 GoogleTest 需要的标志 您尚未链接pthread 库,因此GoogleTest 找不到与此相关的定义

详细说明

虽然有一些更好的方法可以实现相同的目标,但我将坚持您为您的应用程序所遵循的构建策略。

我假设以下目录结构

├── CMakeLists.txt
├── include
│   └── gtest
│       ├── gtest-death-test.h
│       ├── gtest.h
│       ├── gtest-matchers.h
│       ├── gtest-message.h
│       ├── gtest-param-test.h
│       ├── gtest_pred_impl.h
│       ├── gtest-printers.h
│       ├── gtest_prod.h
│       ├── gtest-spi.h
│       ├── gtest-test-part.h
│       ├── gtest-typed-test.h
│       └── internal
│           ├── custom
│           │   ├── gtest.h
│           │   ├── gtest-port.h
│           │   ├── gtest-printers.h
│           │   └── README.md
│           ├── gtest-death-test-internal.h
│           ├── gtest-filepath.h
│           ├── gtest-internal.h
│           ├── gtest-param-util.h
│           ├── gtest-port-arch.h
│           ├── gtest-port.h
│           ├── gtest-string.h
│           └── gtest-type-util.h
├── libs
│   ├── libgmock.a
│   ├── libgmock_main.a
│   ├── libgtest.a
│   └── libgtest_main.a
├── main
└── src
    └── main.cpp

然后我直接从 shell 调用g++,输入以下行(试图模仿你的调用策略)

g++ src/main.cpp --std=c++11 -o main -Iinclude -Llib -lgtest -pthread

标志的含义

GoogleTest 需要c++11 兼容的编译器,因此我们需要--std=c++11 标志 -Iinclude 需要使用include 下的标头而不指定include 目录名称 -Llib 指定 gtest 库所在的路径 pthread 是使用 GoogleTest 所必需的,尽管可以构建 GoogleTest 使其可以以单线程方式使用

它可以正常工作而不会引起任何问题

然后我模仿了你的CMakeLists.txt 并添加了一些下面突出显示的内容(工作没有任何问题)

cmake_minimum_required(VERSION 3.16)

project(TestGoogleTest)
# added the line below to find libraries for threading
find_package(Threads)

set(CMAKE_CXX_STANDARD 11)

set(ROOT_DIR "$PROJECT_SOURCE_DIR")
set(SRC "$ROOT_DIR/src")
set(LIB "$ROOT_DIR/lib")

# added the line below to enable c++11 features
set(INCLUDE "$ROOT_DIR/include") 

link_directories("$LIB")
include_directories("$INCLUDE")

message("INCLUDE PATH: $INCLUDE")

set(SOURCES "$SRC/main.cpp" "$INCLUDE/gtest/gtest.h")

add_executable(main "$SOURCES")

# modified the line below to express link dependency on gtest and thread libraries
# lib prefix can be omitted here
target_link_libraries(main gtest $CMAKE_THREAD_LIBS_INIT)

【讨论】:

【参考方案2】:

我通过使用@squareskittles 提出的答案解决了我的问题。我的CMakeLists.txt 现在也更干净了,看起来像这样

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)

project(TestGoogleTest)

enable_testing()
find_package(GTest REQUIRED)

set(ROOT_DIR "$PROJECT_SOURCE_DIR")
set(SRC "$ROOT_DIR/src")

set(SOURCES "$SRC/main.cpp")

add_executable(test "$SOURCES")
target_link_libraries(test GTest::GTest GTest::Main)

我确信@SALEH 提供的答案是按照我想先解决的方式解决问题。不过我还没试过。

谢谢大家!!!

【讨论】:

【参考方案3】:

g++ -o main -lgtest src/main.cpp "libname".a

【讨论】:

欢迎来到 Stack Overflow!感谢您的回答,但这并没有像问题所要求的那样使用 CMake。

以上是关于使用 cmake 时如何在 C++ 中使用库(*.a 文件)的主要内容,如果未能解决你的问题,请参考以下文章

CMake C++ 包含静态系统库到项目 - 如何

调试不适用于 Android Studio 的 C++/本机库模块(使用 Cmake)

Clion(CMake工具)中如何引入第三方库

如何在 CMake 中正确创建目标之间的依赖关系?

如何在 KDevelop 中开发共享库?

在 CMake 中混合 C 和 C++,啥 CMakeCCompilerId.c 以及如何丢弃它