简体   繁体   English

需要帮助了解与 cmake 的链接

[英]Need help understanding linking with cmake

Need some help understanding libraries and linking with cmake.需要一些帮助来理解库并与 cmake 链接。 Sorry for the wall of text.对不起,文字墙。

I create two static libraries, foo and bar , bar depends on foo .我创建了两个 static 库foobarbar取决于foo bar is just an extension of foo . bar只是foo的扩展。 I then install bar somewhere for another project to use.然后我在某处安装bar以供另一个项目使用。 When I got to compile this other project, I run into linker issues.当我开始编译这个其他项目时,我遇到了 linker 问题。 The issues are undefined references to functions.这些问题是对函数的未定义引用。 Functions that are defined in foo but simply not used in barfoo中定义但在bar中根本没有使用的函数

I had a basic understanding that libraries were a collection of objects/symbols, so I had assumed that by linking foo to bar , bar would also have those objects/symbols, but that is not the case, so I am wondering if I am missing something or if I am doing something wrong.我对库是对象/符号的集合有一个基本的了解,所以我假设通过将foo链接到barbar也会有这些对象/符号,但事实并非如此,所以我想知道我是否错过了某事,或者我做错了什么。

This question was helpful and a little bit similar: CMake: how create a single shared library from all static libraries of subprojects?这个问题很有帮助,而且有点相似: CMake: how create a single shared library from all static libraries of subprojects?

Like the link suggested, I tried adding the -Wl,--whole-archive flag like so就像建议的链接一样,我尝试像这样添加-Wl,--whole-archive标志

set_target_properties(bar PROPERTIES LINK_FLAGS "-Wl,--whole-archive")

But setting those link flags did not work.但是设置这些链接标志不起作用。

I do not want to use SHARED libraries, I would prefer to stay with STATIC libraries, how can I have my libraries keep all the objects/symbols?我不想使用共享库,我更愿意使用 STATIC 库,我怎样才能让我的库保留所有对象/符号? Am I forced to link both bar and foo ?我是否被迫同时链接barfoo

Or is the only solution combining libraries using a custom command like it is suggested in this question: Combining several static libraries into one using CMake或者是使用自定义命令组合库的唯一解决方案,就像在这个问题中建议的那样: 使用 CMake 将几个 static 库组合成一个库


Here I try to explain with an example:这里我尝试用一个例子来解释:

I have a project with the following structure:我有一个具有以下结构的项目:

src/
|
|- foo/
|   | - foo.h
|   | - foo.c
|   | - CMakeLists.txt
|- bar/
|   | - bar.h
|   | - bar.c
|   | - CMakeLists.txt

Here is the source code for the files under foo这是foo下文件的源代码

////////////////// foo.c

#include <stdio.h>

#include <Python.h>

#include "foo.h"

static PyObject * module_global;

void init_interpreter()
{
  printf("Initializing interpreter\n");
  Py_Initialize();
}

void fin_interpreter()
{
  printf("Finalizing interpreter\n");
  Py_FinalizeEx();
}

void import_module(const char* module_name)
{
  if(module_name)
  {
    module_global = PyImport_ImportModule(module_name);
  }
  else
  {
    printf("No good module provided\n");
  }
}


////////////////// foo.h

#ifndef FOO_H
#define FOO_H

void init_interpreter();
void fin_interpreter();
void import_module(const char* module_name);

#endif // FOO_H

/////////////////// foo/CMakeLists.txt

find_package (Python REQUIRED COMPONENTS Development)

add_library(foo STATIC
  foo.c
)

target_link_libraries(foo PUBLIC
  ${Python_LIBRARIES}
)

target_include_directories(foo PUBLIC
  ${Python_INCLUDE_DIRS}
  ${CMAKE_CURRENT_SOURCE_DIR}
)

Here is the source code for the files under bar这是bar下文件的源代码

////////////////// bar.c

#include <stdio.h>

#include "foo.h"

void pretty_init_interpreter()
{
  printf("This is a very pretty call!!\n");
  init_interpreter();
}

////////////////// bar.h

#ifndef BAR_H
#define BAR_H

void pretty_init_interpreter();

#endif // BAR_H

/////////////////// bar/CMakeLists.txt

add_library(bar STATIC
  bar.c
)

target_link_libraries(bar PUBLIC
  foo
)

target_include_directories(bar PUBLIC
  $<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>
  ${CMAKE_CURRENT_SOURCE_DIR}
)

I build those libraries and install the library bar.a on another project that looks like:我构建了这些库并将库bar.a安装在另一个项目上,如下所示:

src/
|
|- main.c
|- CMakeLists.txt
|- install/
|   | - lib
|   |   | - libbar.a
|   | - include
|   |   | - foo.h
|   |   | - bar.h

Source code in this project:本项目中的源代码:

/////////////// main.c
#include <stdio.h>
#include "foo.h"
#include "bar.h"

int main(int argc, char** argv)
{
  pretty_init_interpreter();

  import_module(NULL);

  fin_interpreter();

  return 0;
}
//////////////// CMakeLists.txt

cmake_minimum_required(VERSION 3.20)

set (CMAKE_C_STANDAR 99)

project(foo-project LANGUAGES C)

find_package (Python REQUIRED COMPONENTS Development)

add_executable(foo-exec main.c)

find_library(BAR_LIB
NAMES
  bar
HINTS
  ${PROJECT_SOURCE_DIR}/install/lib/
NO_DEFAULT_PATH
)

target_link_libraries(foo-exec PUBLIC
  ${BAR_LIB}
  ${Python_LIBRARIES}
)

target_include_directories(foo-exec PUBLIC
  ${Python_INCLUDE_DIRS}
  ${PROJECT_SOURCE_DIR}/install/include/
)

Compiling the executable with mingw, I get the following error:使用mingw编译可执行文件,出现以下错误:

c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: CMakeFiles\foo-exec.dir/objects.a(main.c.obj): in function `main':
main.c:9: undefined reference to `import_module'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: main.c:11: undefined reference to `fin_interpreter'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: ../../install/lib/libbar.a(bar.c.obj): in function `pretty_init_interpreter':
bar/bar.c:8: undefined reference to `init_interpreter'
collect2.exe: error: ld returned 1 exit status

Using nm to look at the symbols of bar shows that the functions are missingnmbar的符号显示缺少功能

$ nm install/lib/libbar.a

bar.c.obj:
00000000 b .bss
00000000 d .data
00000000 N .debug_abbrev
00000000 N .debug_aranges
00000000 N .debug_info
00000000 N .debug_line
00000000 r .eh_frame
00000000 r .rdata
00000000 r .rdata$zzz
00000000 t .text
         U _init_interpreter
00000000 T _pretty_init_interpreter
         U _puts

I had a basic understanding that libraries were a collection of objects/symbols, so I had assumed that by linking foo to bar, bar would also have those objects/symbols, but that is not the case, so I am wondering if I am missing something or if I am doing something wrong.我对库是对象/符号的集合有一个基本的了解,所以我假设通过将 foo 链接到 bar,bar 也会有这些对象/符号,但事实并非如此,所以我想知道我是否错过了某事,或者我做错了什么。

Indeed this is not quite the case .确实情况并非如此 The C/C++ language standards do not define the contents of static libraries, but on most platforms they are archives containing specific object files . C/C++ 语言标准没有定义 static 库的内容,但在大多数平台上,它们是包含特定 object 文件的档案 If those object files collectively depend on external symbols, then that static library depends on some other library or object file and all must be linked into the final executable.如果这些 object 文件共同依赖于外部符号,那么 static 库依赖于其他一些库或 object 文件,并且所有文件都必须链接到最终的可执行文件中。

"Linking" one static library to another in CMake merely declares the dependency between them.在 CMake 中将一个 static 库“链接”到另一个库仅声明它们之间的依赖关系。 There is no platform-independent mechanism for merging them nor, within CMake, is there a pressing need to do so.没有用于合并它们的独立于平台的机制,在 CMake 中也没有迫切需要这样做。

I do not want to use SHARED libraries, I would prefer to stay with STATIC libraries, how can I have my libraries keep all the objects/symbols?我不想使用共享库,我更愿意使用 STATIC 库,我怎样才能让我的库保留所有对象/符号? Am I forced to link both bar and foo?我是否被迫同时链接 bar 和 foo?

There are many questions and answers on StackOverflow detailing how to use OBJECT libraries to accomplish this. StackOverflow 上有很多问题和答案,详细说明了如何使用OBJECT库来完成此任务。 Here's, say, five:比如说,五个:

  1. https://stackoverflow.com/a/11448878/2137996 https://stackoverflow.com/a/11448878/2137996
  2. https://stackoverflow.com/a/10635366/2137996 https://stackoverflow.com/a/10635366/2137996
  3. https://stackoverflow.com/a/68866472/2137996 https://stackoverflow.com/a/68866472/2137996
  4. https://stackoverflow.com/a/67435497/2137996 https://stackoverflow.com/a/67435497/2137996
  5. https://stackoverflow.com/a/68406028/2137996 https://stackoverflow.com/a/68406028/2137996

Yet, I would say that all of these questions are misguided.然而,我想说所有这些问题都是错误的。 CMake handles linking library dependencies just fine. CMake 可以很好地处理链接库依赖项。

I fear, you might be thinking in too many details.我担心,你可能想太多细节了。 CMake is handling the linker flags for you. CMake 正在为您处理 linker 标志。

You want to define two library targets, foo and bar, right?你想定义两个库目标,foo 和 bar,对吧? @Tsyvarev is absolutely right, static libraries don't involve linking, but it's more the creation of an archive (in Linux ar is used). @Tsyvarev 是绝对正确的,static 库不涉及链接,但更多的是创建档案(在 Linux 中使用ar )。 Only if you create a binary, an executable - be it a real executable with a main function or a dll/shared object library - you will have a linking process.仅当您创建二进制文件、可执行文件时——无论是具有主 function 或 dll/共享 object 库的真正可执行文件——您将拥有一个链接过程。

But dependencies in CMake make sense even for static libraries, because CMake propagates not only the actual library, but also include directories and other properties you pass to your target as public.但是 CMake 中的依赖关系即使对于 static 库也是有意义的,因为 CMake 不仅传播实际库,还包括您作为公共传递给目标的目录和其他属性。

So, what you want in your project with the executable: a CMake target for all of your libraries, which can be imported to other projects.因此,您希望在项目中使用可执行文件:所有库的 CMake 目标,可以将其导入其他项目。 This means an export CMake-file defining your library targets with这意味着定义您的库目标的导出 CMake 文件

add_library(foo STATIC IMPORTED)
add_library(bar STATIC IMPORTED)
target_link_libraries(bar PUBLIC foo)

( https://cmake.org/cmake/help/latest/command/add_library.html?highlight=add_library#imported-libraries ) And you might want to populate some target properties, like include directories. https://cmake.org/cmake/help/latest/command/add_library.html?highlight=add_library#imported-libraries )并且您可能想要填充一些目标属性,例如包含目录。 additional public compiler/linker flags, etc.额外的公共编译器/链接器标志等。

Many external libraries install their CMake-config files to one of the default locations (on Linux for example that's in /usr/lib/cmake/, or if you are using Qt, you can find the CMake config files on Windows in the Qt installation directory in a cmake directory next to the static libraries; do a search for *.cmake files in doubt). Many external libraries install their CMake-config files to one of the default locations (on Linux for example that's in /usr/lib/cmake/, or if you are using Qt, you can find the CMake config files on Windows in the Qt installation static 库旁边的cmake目录中的目录;搜索*.cmake文件有疑问)。 You can put your own CMake-config files there or let yourself be inspired by the way other components do it.您可以将自己的 CMake-config 文件放在那里,或者让自己受到其他组件的启发。

Here are some links, you might find interesting:以下是一些链接,您可能会觉得有趣:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM