[英]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 库foo
和bar
, bar
取决于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 bar
在foo
中定义但在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
链接到bar
, bar
也会有这些对象/符号,但事实并非如此,所以我想知道我是否错过了某事,或者我做错了什么。
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
?我是否被迫同时链接bar
和foo
?
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 missing用nm
看bar
的符号显示缺少功能
$ 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:比如说,五个:
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.