我有一個B對配置物件執行一些操作的大型庫MY_CONFIG。C該庫與提供主要功能的更大的庫鏈接。最后,C鏈接到可執行檔案main。MY_CONFIG每個計算單元的配置物件都是唯一的。由于目標是C為所有系統生成一個庫,因此我目前正在嘗試生成一個A僅包含已定義配置物件的附加庫。
換句話說,所有計算單元最終都會收到相同C但不同的A庫。
但是,問題是,undefined reference在嘗試將 main 鏈接到Cand時出現聯結器錯誤A。
讓我們用一些代碼來說明這個例子:
A.cpp
#include "B.hpp"
Config MY_CONFIG = {
.id=2
};
B.hpp
int get_config_id ();
typedef struct Config {
int id;
} Config;
B.cpp
#include "B.hpp"
extern Config MY_CONFIG;
int get_config_id () {
Config work = MY_CONFIG;
return work.id;
}
C.hpp
void print_config();
C.cpp
#include "B.hpp"
#include <iostream>
void print_config() {
std::cout << "Config ID: " << get_config_id() << std::endl;
}
主檔案
#include <iostream>
#include "C.hpp"
int main() {
print_config();
return 0;
}
CMakeLists.txt
project(config)
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pthread")
add_library(A STATIC A/A.cpp)
target_include_directories(A PUBLIC B)
add_library(B STATIC B/B.cpp)
target_include_directories(B PUBLIC B)
add_library(C STATIC C/C.cpp)
target_include_directories(C PUBLIC C)
target_link_libraries(C B)
add_executable(main main.cpp)
# Apparently, this is not possible. But why?
target_link_libraries(main C A)
如您所見,我在B.cppextern中使用了for 關鍵字。我的直覺是,一旦構建了可執行的 main 就解決了。由于is in的定義,我還將 main 與 A 鏈接起來。編譯程序中出現以下錯誤:MY_CONFIGMY_CONFIGA.cpp
/usr/bin/ld: libB.a(B.cpp.o): in function `get_config_id()':
B.cpp:(.text 0xa): undefined reference to `MY_CONFIG'
collect2: error: ld returned 1 exit status
如果我將 A 與 C 聯系起來,那么它作業得非常好。但這是我想避免的事情,因為我需要為所有計算系統提供單獨的 C。一個重要的旁注:我只能使用靜態庫(沒有共享物件)。
我的意圖通常是不可能的還是我錯過了什么?
uj5u.com熱心網友回復:
CMake 對靜態庫之間的回圈依賴沒有問題。只需宣告實際的依賴關系,CMake 會處理其余的。這是我修改后的 CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
project(config)
set(CMAKE_CXX_STANDARD 11)
add_library(A STATIC A/A.cpp)
target_include_directories(A PUBLIC "${CMAKE_CURRENT_LIST_DIR}/A")
target_link_libraries(A PRIVATE B)
add_library(B STATIC B/B.cpp)
target_include_directories(B PUBLIC "${CMAKE_CURRENT_LIST_DIR}/B")
target_link_libraries(B PRIVATE A)
add_library(C STATIC C/C.cpp)
target_include_directories(C PUBLIC "${CMAKE_CURRENT_LIST_DIR}/C")
target_link_libraries(C PRIVATE B)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE C)
使用您的確切來源,我可以像這樣構建和運行:
$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
...
$ cmake --build build --verbose
[1/8] /usr/bin/c -I/path/to/B -I/path/to/A -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/B.dir/B/B.cpp.o -MF CMakeFiles/B.dir/B/B.cpp.o.d -o CMakeFiles/B.dir/B/B.cpp.o -c /path/to/B/B.cpp
[2/8] /usr/bin/c -I/path/to/A -I/path/to/B -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/A.dir/A/A.cpp.o -MF CMakeFiles/A.dir/A/A.cpp.o.d -o CMakeFiles/A.dir/A/A.cpp.o -c /path/to/A/A.cpp
[3/8] : && /usr/bin/cmake -E rm -f libB.a && /usr/bin/ar qc libB.a CMakeFiles/B.dir/B/B.cpp.o && /usr/bin/ranlib libB.a && :
[4/8] : && /usr/bin/cmake -E rm -f libA.a && /usr/bin/ar qc libA.a CMakeFiles/A.dir/A/A.cpp.o && /usr/bin/ranlib libA.a && :
[5/8] /usr/bin/c -I/path/to/C -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /path/to/main.cpp
[6/8] /usr/bin/c -I/path/to/C -I/path/to/B -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/C.dir/C/C.cpp.o -MF CMakeFiles/C.dir/C/C.cpp.o.d -o CMakeFiles/C.dir/C/C.cpp.o -c /path/to/C/C.cpp
[7/8] : && /usr/bin/cmake -E rm -f libC.a && /usr/bin/ar qc libC.a CMakeFiles/C.dir/C/C.cpp.o && /usr/bin/ranlib libC.a && :
[8/8] : && /usr/bin/c -O3 -DNDEBUG CMakeFiles/main.dir/main.cpp.o -o main libC.a libB.a libA.a libB.a libA.a && :
$ ./build/main
Config ID: 2
如您所見,最后的鏈接行生成C B A B A. 后兩者不會傷害任何東西,因為它們永遠不會被真正搜查。
如果你想打破回圈,你可以將A需要的B部分拆分到另一個庫中,讓它們都依賴它。
現在我有以下檔案:
// A/A.cpp
#include "common.hpp"
Config MY_CONFIG = {
.id=2
};
// common/common.hpp
typedef struct Config {
int id;
} Config;
// B/B.cpp
#include "B.hpp"
#include "common.hpp"
extern Config MY_CONFIG;
int get_config_id () {
Config work = MY_CONFIG;
return work.id;
}
// B/B.hpp
int get_config_id ();
然后像這樣調整構建:
cmake_minimum_required(VERSION 3.22)
project(config)
set(CMAKE_CXX_STANDARD 11)
add_library(common INTERFACE)
target_include_directories(common INTERFACE "${CMAKE_CURRENT_LIST_DIR}/common")
add_library(A STATIC A/A.cpp)
target_include_directories(A PUBLIC "${CMAKE_CURRENT_LIST_DIR}/A")
target_link_libraries(A PRIVATE common)
add_library(B STATIC B/B.cpp)
target_include_directories(B PUBLIC "${CMAKE_CURRENT_LIST_DIR}/B")
target_link_libraries(B PRIVATE common A)
add_library(C STATIC C/C.cpp)
target_include_directories(C PUBLIC "${CMAKE_CURRENT_LIST_DIR}/C")
target_link_libraries(C PRIVATE B)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE C)
當我們構建時,我們看到鏈接順序是正確的,并且重復的庫已經消失了:
$ cmake --build build --verbose
[1/8] /usr/bin/c -I/path/to/A -I/path/to/common -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/A.dir/A/A.cpp.o -MF CMakeFiles/A.dir/A/A.cpp.o.d -o CMakeFiles/A.dir/A/A.cpp.o -c /path/to/A/A.cpp
[2/8] /usr/bin/c -I/path/to/B -I/path/to/common -I/path/to/A -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/B.dir/B/B.cpp.o -MF CMakeFiles/B.dir/B/B.cpp.o.d -o CMakeFiles/B.dir/B/B.cpp.o -c /path/to/B/B.cpp
[3/8] : && /usr/bin/cmake -E rm -f libA.a && /usr/bin/ar qc libA.a CMakeFiles/A.dir/A/A.cpp.o && /usr/bin/ranlib libA.a && :
[4/8] : && /usr/bin/cmake -E rm -f libB.a && /usr/bin/ar qc libB.a CMakeFiles/B.dir/B/B.cpp.o && /usr/bin/ranlib libB.a && :
[5/8] /usr/bin/c -I/path/to/C -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /path/to/main.cpp
[6/8] /usr/bin/c -I/path/to/C -I/path/to/B -O3 -DNDEBUG -std=gnu 11 -MD -MT CMakeFiles/C.dir/C/C.cpp.o -MF CMakeFiles/C.dir/C/C.cpp.o.d -o CMakeFiles/C.dir/C/C.cpp.o -c /path/to/C/C.cpp
[7/8] : && /usr/bin/cmake -E rm -f libC.a && /usr/bin/ar qc libC.a CMakeFiles/C.dir/C/C.cpp.o && /usr/bin/ranlib libC.a && :
[8/8] : && /usr/bin/c -O3 -DNDEBUG CMakeFiles/main.dir/main.cpp.o -o main libC.a libB.a libA.a && :
uj5u.com熱心網友回復:
代替
target_link_libraries(main C A)
你可以使用
target_link_libraries(main C B A)
這將在聯結器命令列中的庫之間產生正確的順序。因此,您將克服“未定義的參考”錯誤。
解釋
該庫使用在庫中定義的B符號(特別是變數)。因此,要成功鏈接生成的可執行檔案,您需要:MY_CONFIGA
- 確保
A.a在B.a聯結器的命令列之后。 - 使用選項,它消除了聯結器的排序敏感性。
-Wl,--whole-archive您可以通過使用選項來遵循第二種方式。參見例如那個問題。
對于遵循第一種方式,即確保鏈接庫的順序,應該考慮到 CMake 提供了兩種強制排序方式:
庫之間的鏈接:
target_link_libraries(B A)這是最方便和可擴展的方式。它確保在聯結器的命令列
A.a中最后一個B.a.但是這種聯系不適合您的目的。
中的庫順序
target_link_libraries。例如target_link_libraries(main <...> B A <...>)將確保至少有一個
B后跟A.這種方式的保證比第一種方式少。例如,該命令不會阻止 CMake 生成聯結器命令列
<...> B.a A.a B.a <...>,這將再次導致未定義的參考錯誤。但是 CMake 不會在沒有需要的情況下在命令列中復制庫。
例如,而不是
target_link_libraries(main C A)
你可以使用
target_link_libraries(main C B A)
因此,聯結器命令列將完全按照該順序包含庫:
ld <...> C.a B.a A.a
請注意,在給定的命令列中B.a,C.a它與您的另一個鏈接相關CMakeLists.txt:
target_link_libraries(C B)
所以 CMake 不會在B.a其中添加另一個。
聯動
target_link_libraries(C B)
您當前在代碼中擁有的內容僅確保B.a將在聯結器命令列之后的某個位置出現。C.a
有額外的
target_link_libraries(main C A)
您可以期望 CMake 生成正確的命令列
ld <...> C.a B.a A.a
滿足這兩種聯系。
但是 CMake 完全可以生成另一個命令列:
ld <...> C.a A.a B.a
哪里B.a沒有立即跟進C.a。
正是在您的情況下,該命令列會導致“未定義的參考”錯誤。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/410797.html
標籤:
上一篇:如何僅通過克隆其存盤庫并將其src復制到我的專案來使用catch2?
下一篇:通過遞回c 檢查回文
