需要帮助理解与 cmake 的链接
Need help understanding linking with cmake
需要一些帮助来理解库和 link使用 cmake。对不起文字墙。
我创建了两个静态库,foo
和bar
,bar
依赖于foo
。 bar
只是 foo
的延伸。然后我在某个地方安装 bar
供另一个项目使用。当我开始编译这个其他项目时,我 运行 遇到了 linker 问题。问题是对函数的未定义引用。在 foo
中定义但在 bar
中根本没有使用的函数
我有一个基本的理解,图书馆是 objects/symbols 的集合,所以我假设通过 linking foo
到 bar
,bar
也会有那些 objects/symbols,但事实并非如此,所以我想知道我是否遗漏了什么或者我做错了什么。
这个问题很有帮助,有点类似:CMake: how create a single shared library from all static libraries of subprojects?
就像 link 建议的那样,我尝试像这样添加 -Wl,--whole-archive
标志
set_target_properties(bar PROPERTIES LINK_FLAGS "-Wl,--whole-archive")
但是设置那些 link 标志没有用。
我不想使用 SHARED 库,我更愿意使用 STATIC 库,我怎样才能让我的库保留所有 objects/symbols?我是否被迫 link 同时 bar
和 foo
?
或者是使用自定义命令组合库的唯一解决方案,就像这个问题中建议的那样:Combining several static libraries into one using CMake
这里我试着举例说明一下:
我有一个结构如下的项目:
src/
|
|- foo/
| | - foo.h
| | - foo.c
| | - CMakeLists.txt
|- bar/
| | - bar.h
| | - bar.c
| | - CMakeLists.txt
这是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}
)
这是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}
)
我构建了这些库并将库 bar.a
安装到另一个项目,如下所示:
src/
|
|- main.c
|- CMakeLists.txt
|- install/
| | - lib
| | | - libbar.a
| | - include
| | | - foo.h
| | | - bar.h
本项目源代码:
/////////////// 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/
)
用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
用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.
确实情况并非如此。 C/C++ 语言标准没有定义静态库的内容,但在大多数平台上它们是 包含特定目标文件的档案 。如果这些目标文件共同依赖于外部符号,则该静态库依赖于某些 other 库或目标文件,并且 all 必须链接到最终文件中可执行文件。
在 CMake 中将一个静态库“链接”到另一个静态库只是声明它们之间的依赖关系。没有 platform-independent 合并它们的机制,在 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? Am I forced to link both bar and foo?
Whosebug 上有许多 个问题和答案,详细说明了如何使用 OBJECT
个库来完成此任务。比如说,这里有五个:
然而,我要说所有这些问题都是错误的。 CMake 可以很好地处理链接库依赖项。
恐怕您考虑的细节太多了。 CMake 正在为您处理链接器标志。
您想定义两个库目标,foo 和 bar,对吗? @Tsyvarev 是绝对正确的,静态库不涉及链接,但更多的是创建存档(在 Linux ar
中使用)。只有当你创建一个二进制文件,一个可执行文件——无论是一个带有主函数的真正的可执行文件还是一个dll/shared对象库——你都会有一个链接过程。
但是 CMake 中的依赖关系即使对于静态库也是有意义的,因为 CMake 不仅传播实际的库,还包括您传递给目标的目录和其他属性 public。
因此,您希望在您的项目中使用可执行文件:您所有库的 CMake 目标,可以将其导入其他项目。这意味着导出 CMake-file 定义你的库目标
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)
您可能想要填充一些目标属性,例如包含目录。
附加 public compiler/linker 标志等
许多外部库将它们的 CMake-config 文件安装到默认位置之一(在 Linux 上,例如在 /usr/lib/cmake/ 中,或者如果您使用的是 Qt,您可以找到Qt 安装目录 Windows 上的 CMake 配置文件位于静态库旁边的 cmake
目录中;搜索有疑问的 *.cmake
文件)。您可以将自己的 CMake-config 文件放在那里,或者让自己从其他组件的工作方式中获得灵感。
这里有一些链接,您可能会觉得有趣:
需要一些帮助来理解库和 link使用 cmake。对不起文字墙。
我创建了两个静态库,foo
和bar
,bar
依赖于foo
。 bar
只是 foo
的延伸。然后我在某个地方安装 bar
供另一个项目使用。当我开始编译这个其他项目时,我 运行 遇到了 linker 问题。问题是对函数的未定义引用。在 foo
中定义但在 bar
我有一个基本的理解,图书馆是 objects/symbols 的集合,所以我假设通过 linking foo
到 bar
,bar
也会有那些 objects/symbols,但事实并非如此,所以我想知道我是否遗漏了什么或者我做错了什么。
这个问题很有帮助,有点类似:CMake: how create a single shared library from all static libraries of subprojects?
就像 link 建议的那样,我尝试像这样添加 -Wl,--whole-archive
标志
set_target_properties(bar PROPERTIES LINK_FLAGS "-Wl,--whole-archive")
但是设置那些 link 标志没有用。
我不想使用 SHARED 库,我更愿意使用 STATIC 库,我怎样才能让我的库保留所有 objects/symbols?我是否被迫 link 同时 bar
和 foo
?
或者是使用自定义命令组合库的唯一解决方案,就像这个问题中建议的那样:Combining several static libraries into one using CMake
这里我试着举例说明一下:
我有一个结构如下的项目:
src/
|
|- foo/
| | - foo.h
| | - foo.c
| | - CMakeLists.txt
|- bar/
| | - bar.h
| | - bar.c
| | - CMakeLists.txt
这是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}
)
这是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}
)
我构建了这些库并将库 bar.a
安装到另一个项目,如下所示:
src/
|
|- main.c
|- CMakeLists.txt
|- install/
| | - lib
| | | - libbar.a
| | - include
| | | - foo.h
| | | - bar.h
本项目源代码:
/////////////// 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/
)
用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
用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.
确实情况并非如此。 C/C++ 语言标准没有定义静态库的内容,但在大多数平台上它们是 包含特定目标文件的档案 。如果这些目标文件共同依赖于外部符号,则该静态库依赖于某些 other 库或目标文件,并且 all 必须链接到最终文件中可执行文件。
在 CMake 中将一个静态库“链接”到另一个静态库只是声明它们之间的依赖关系。没有 platform-independent 合并它们的机制,在 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? Am I forced to link both bar and foo?
Whosebug 上有许多 个问题和答案,详细说明了如何使用 OBJECT
个库来完成此任务。比如说,这里有五个:
然而,我要说所有这些问题都是错误的。 CMake 可以很好地处理链接库依赖项。
恐怕您考虑的细节太多了。 CMake 正在为您处理链接器标志。
您想定义两个库目标,foo 和 bar,对吗? @Tsyvarev 是绝对正确的,静态库不涉及链接,但更多的是创建存档(在 Linux ar
中使用)。只有当你创建一个二进制文件,一个可执行文件——无论是一个带有主函数的真正的可执行文件还是一个dll/shared对象库——你都会有一个链接过程。
但是 CMake 中的依赖关系即使对于静态库也是有意义的,因为 CMake 不仅传播实际的库,还包括您传递给目标的目录和其他属性 public。
因此,您希望在您的项目中使用可执行文件:您所有库的 CMake 目标,可以将其导入其他项目。这意味着导出 CMake-file 定义你的库目标
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) 您可能想要填充一些目标属性,例如包含目录。 附加 public compiler/linker 标志等
许多外部库将它们的 CMake-config 文件安装到默认位置之一(在 Linux 上,例如在 /usr/lib/cmake/ 中,或者如果您使用的是 Qt,您可以找到Qt 安装目录 Windows 上的 CMake 配置文件位于静态库旁边的 cmake
目录中;搜索有疑问的 *.cmake
文件)。您可以将自己的 CMake-config 文件放在那里,或者让自己从其他组件的工作方式中获得灵感。
这里有一些链接,您可能会觉得有趣: