雙笙子佯謬老師的【公開課】現(xiàn)代CMake高級教程課程筆記
第 3 章:鏈接庫文件
main.cpp 調(diào)用 mylib.cpp 里的 say_hello 函數(shù)
$tree
.
├── CMakeLists.txt
├── main.cpp
├── mylib.cpp
└── mylib.h
// main.cpp
#include "mylib.h"
int main() {
say_hello();
}
// mylib.h
#pragma once
void say_hello();
// mylib.cpp
#include "mylib.h"
#include <cstdio>
void say_hello()
{
printf("hello, mylib!\n");
}
1. 直接鏈接到一起編譯
# CMakeLists.txt
add_executable(main main.cpp mylib.cpp)
2. mylib 作為一個靜態(tài)庫
add_library(mylib STATIC mylib.cpp)
add_executable(main main.cpp)
target_link_libraries(main PUBLIC mylib)
編譯:
$cmake --build build
[build] [1/3 33% :: 0.194] Building CXX object CMakeFiles/mylib.dir/mylib.cpp.o
[build] [2/3 66% :: 0.420] Linking CXX static library libmylib.a
[build] [3/3 100% :: 0.741] Linking CXX executable main
[build] Build finished with exit code 0
生成了 libmylib.a:
$ls build
CMakeCache.txt CMakeFiles build.ninja cmake_install.cmake compile_commands.json libmylib.a main mylib
3. mylib 作為一個動態(tài)庫
add_library(mylib SHARED mylib.cpp)
add_executable(main main.cpp)
target_link_libraries(main PUBLIC mylib)
編譯:
[main] Building folder: CMakeLession
[build] Starting build
[proc] Executing command: /usr/bin/cmake --build /mnt/h/Code/lessonCode/CMakeLession/build --config Debug --target all --
[build] [1/3 33% :: 0.485] Building CXX object CMakeFiles/mylib.dir/mylib.cpp.o
[build] [2/3 66% :: 0.786] Linking CXX shared library libmylib.so
[build] [3/3 100% :: 1.071] Linking CXX executable main
[build] Build finished with exit code 0
[main] Building folder: CMakeLession
[build] Starting build
[proc] Executing command: /usr/bin/cmake --build /mnt/h/Code/lessonCode/CMakeLession/build --config Debug --target all --
[build] [1/3 33% :: 0.485] Building CXX object CMakeFiles/mylib.dir/mylib.cpp.o
[build] [2/3 66% :: 0.786] Linking CXX shared library libmylib.so
[build] [3/3 100% :: 1.071] Linking CXX executable main
[build] Build finished with exit code 0
4. mylib 作為一個對象庫
對象庫類似于靜態(tài)庫,但不生成 .a 文件,只由 CMake 記住該庫生成了哪些對象文件
add_library(mylib OBJECT mylib.cpp)
add_executable(main main.cpp)
target_link_libraries(main PUBLIC mylib)
編譯:
cmake --build build
[ 33%] Building CXX object CMakeFiles/mylib.dir/mylib.cpp.o
[ 33%] Built target mylib
[ 66%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
[100%] Built target main
對象庫類似于靜態(tài)庫,但不生成 .a 文件,只由 CMake 記住該庫生成了哪些對象文件,對象庫是 CMake 自創(chuàng)的,繞開了編譯器和操作系統(tǒng)的各種繁瑣規(guī)則,保證了跨平臺統(tǒng)一性。在自己的項目中,我推薦全部用對象庫(OBJECT)替代靜態(tài)庫(STATIC)避免跨平臺的麻煩。
對象庫僅僅作為組織代碼的方式,而實際生成的可執(zhí)行文件只有一個,減輕了部署的困難。
? ls build/CMakeFiles/mylib.dir
DependInfo.cmake build.make cmake_clean.cmake compiler_depend.make compiler_depend.ts depend.make flags.make mylib.cpp.o mylib.cpp.o.d progress.make
靜態(tài)庫的麻煩:GCC 編譯器自作聰明,會自動剔除沒有引用符號的那些對象
例:
CMakeLists.txt
add_library(mylib STATIC mylib.cpp)
mylib.cpp
#include <cstdio>
// 此處進行靜態(tài)初始化
static int unused = printf("mylib initialized!");
main.cpp
#include <cstdio>
int main()
{
printf("main function\n");
}
編譯:
? cmake --build build
[ 50%] Built target mylib
Consolidate compiler generated dependencies of target main
[ 75%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
[100%] Built target main
查看符號:
.init_array:0000000000003DB8 _init_array segment qword public 'DATA' use64
.init_array:0000000000003DB8 assume cs:_init_array
.init_array:0000000000003DB8 ;org 3DB8h
.init_array:0000000000003DB8 __frame_dummy_init_array_entry dq offset frame_dummy
.init_array:0000000000003DB8 ; DATA XREF: LOAD:0000000000000168↑o
.init_array:0000000000003DB8 ; LOAD:00000000000002F0↑o
.init_array:0000000000003DB8 _init_array ends
這里可以看到?jīng)]有 mylib 的初始化
對象庫可以繞開編譯器的不統(tǒng)一:保證不會自動剔除沒引用到的對象文件。我們改成 OBJECT:
CMakeLists.txt
add_library(mylib STATIC mylib.cpp)
使用 ida 可以看到 mylib 初始化:
.init_array:0000000000003DA8 _init_array segment qword public 'DATA' use64
.init_array:0000000000003DA8 assume cs:_init_array
.init_array:0000000000003DA8 ;org 3DA8h
.init_array:0000000000003DA8 __frame_dummy_init_array_entry dq offset frame_dummy
.init_array:0000000000003DA8 ; DATA XREF: LOAD:0000000000000168↑o
.init_array:0000000000003DA8 ; LOAD:00000000000002F0↑o
.init_array:0000000000003DB0 dq offset _GLOBAL__sub_I_mylib_cpp
.init_array:0000000000003DB0 _init_array ends
雖然動態(tài)庫也可以避免剔除沒引用的對象文件,但引入了運行時鏈接的麻煩
小技巧
add_library 無參數(shù)
add_library 無參數(shù)時,是靜態(tài)庫還是動態(tài)庫?
會根據(jù) BUILD_SHARED_LIBS 這個變量的值決定是動態(tài)庫還是靜態(tài)庫。ON 則相當(dāng)于 SHARED,OFF 則相當(dāng)于 STATIC。如果未指定 BUILD_SHARED_LIBS 變量,則默認為 STATIC。
因此,如果發(fā)現(xiàn)一個項目里的 add_library 都是無參數(shù)的,意味著你可以用:cmake -B build -DBUILD_SHARED_LIBS:BOOL=ON
來讓他全部生成為動態(tài)庫。稍后會詳解命令行傳遞變量的規(guī)則。
設(shè)定一個變量的默認值
要讓 BUILD_SHARED_LIBS 默認為 ON,可以用下圖這個方法:
如果該變量沒有定義,則設(shè)為 ON,否則保持用戶指定的值不變。
這樣當(dāng)用戶沒有指定 BUILD_SHARED_LIBS 這個變量時,會默認變成 ON。
也就是說除非用戶指定了 -DBUILD_SHARED_LIBS:BOOL=OFF 才會生成靜態(tài)庫,否則默認是生成動態(tài)庫。
if (NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
endif()
常見坑點
動態(tài)庫無法鏈接靜態(tài)庫(3.22.1 可以正常鏈接)
add_library(otherlib STATIC otherlib.cpp)
add_library(mylib SHARED mylib.cpp)
target_link_libraries(mylib PUBLIC otherlib)
add_executable(main main.cpp)
target_link_libraries(main PUBLIC mylib)
讓靜態(tài)庫編譯時也生成位置無關(guān)的代碼(PIC),這樣才能裝在動態(tài)庫里文章來源:http://www.zghlxwxcb.cn/news/detail-434369.html
也可以只針對一個庫,只對他啟用位置無關(guān)的代碼(PIC)文章來源地址http://www.zghlxwxcb.cn/news/detail-434369.html
add_library(otherlib STATIC otherlib.cpp)
set_property(TARGET otherlib PROPERTY POSITION_INDEPENDENT_CODE ON)
add_library(mylib SHARED mylib.cpp)
target_link_libraries(mylib PUBLIC otherlib)
add_executable(main main.cpp)
target_link_libraries(main PUBLIC mylib)
到了這里,關(guān)于現(xiàn)代CMake高級教程 - 第 3 章:鏈接庫文件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!