需要一些幫助來理解庫并與 cmake 鏈接。對不起,文字墻。
我創建了兩個靜態庫,并且foo依賴于. 只是. 然后我在某個地方安裝以供另一個專案使用。當我開始編譯這個其他專案時,我遇到了聯結器問題。這些問題是對函式的未定義參考。中定義但根本未使用的函式barbarfoobarfoobarfoobar
我對庫是物件/符號的集合有一個基本的了解,所以我假設通過鏈接foo到bar,bar也會有這些物件/符號,但事實并非如此,所以我想知道我是否遺漏了什么或者是否我做錯了什么。
這個問題很有幫助并且有點相似:CMake:如何從子專案的所有靜態庫中創建一個共享庫?
就像建議的鏈接一樣,我嘗試-Wl,--whole-archive像這樣添加標志
set_target_properties(bar PROPERTIES LINK_FLAGS "-Wl,--whole-archive")
但是設定這些鏈接標志不起作用。
我不想使用 SHARED 庫,我更愿意使用 STATIC 庫,我怎樣才能讓我的庫保留所有物件/符號?我是否被迫同時鏈接bar和foo?
或者是使用自定義命令組合庫的唯一解決方案,就像在這個問題中建議的那樣:使用 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}
)
I build those libraries and install the library bar.a on another project that looks like:
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:
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 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
uj5u.com熱心網友回復:
我對庫是物件/符號的集合有一個基本的了解,所以我假設通過將 foo 鏈接到 bar,bar 也會有這些物件/符號,但事實并非如此,所以我想知道我是否錯過了某事,或者我做錯了什么。
確實情況并非如此。C/C 語言標準沒有定義靜態庫的內容,但在大多數平臺上,它們是包含特定目標檔案的檔案。如果這些目標檔案共同依賴于外部符號,那么該靜態庫依賴于其他一些庫或目標檔案,并且所有檔案都必須鏈接到最終的可執行檔案中。
在 CMake 中將一個靜態庫“鏈接”到另一個靜態庫只是宣告了它們之間的依賴關系。沒有用于合并它們的獨立于平臺的機制,在 CMake 中也沒有迫切需要這樣做。
我不想使用 SHARED 庫,我更愿意使用 STATIC 庫,我怎樣才能讓我的庫保留所有物件/符號?我是否被迫同時鏈接 bar 和 foo?
StackOverflow 上有很多問題和答案,詳細說明了如何使用OBJECT庫來完成此任務。比如說,五個:
- https://stackoverflow.com/a/11448878/2137996
- https://stackoverflow.com/a/10635366/2137996
- https://stackoverflow.com/a/68866472/2137996
- https://stackoverflow.com/a/67435497/2137996
- https://stackoverflow.com/a/68406028/2137996
然而,我想說所有這些問題都是錯誤的。CMake 可以很好地處理鏈接庫依賴項。
uj5u.com熱心網友回復:
我擔心,你可能想太多細節了。CMake 正在為您處理聯結器標志。
你想定義兩個庫目標,foo 和 bar,對吧?@Tsyvarev 是絕對正確的,靜態庫不涉及鏈接,但更多的是創建存檔(在 Linuxar中使用)。只有當你創建一個二進制檔案,一個可執行檔案——無論是一個真正的帶有主函式的可執行檔案還是一個 dll/共享物件庫——你才會有一個鏈接程序。
但是 CMake 中的依賴關系即使對于靜態庫也有意義,因為 CMake 不僅傳播實際庫,還包括您作為公共傳遞給目標的目錄和其他屬性。
因此,您希望在專案中使用可執行檔案:所有庫的 CMake 目標,可以將其匯入其他專案。這意味著定義您的庫目標的匯出 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)您可能想要填充一些目標屬性,例如包含目錄。額外的公共編譯器/聯結器標志等。
許多外部庫將其 CMake-config 檔案安裝到默認位置之一(例如在 Linux 上位于 /usr/lib/cmake/ 中,或者如果您使用 Qt,則可以在 Qt 安裝中找到 Windows 上的 CMake 組態檔cmake靜態庫旁邊的目錄中的目錄;搜索有*.cmake疑問的檔案)。您可以將自己的 CMake-config 檔案放在那里,或者讓自己受到其他組件的啟發。
以下是一些鏈接,您可能會覺得有趣:
- https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html
- https://gitlab.kitware.com/cmake/community/-/wikis/doc/tutorials/How-to-create-a-ProjectConfig.cmake-file
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/417887.html
標籤:
