Linux下構建自己的C++共享庫并配合pkg-config生成鏈接選項
本文將以C++鏈表的新建、打印操作為例構建自己的共享庫,并在實際調試代碼時嘗試使用。我們在做數據結構題時經常需要將鏈表打印出來看一下結果,但是并沒有一種庫函數可以讓我們直接調用來打印自己的基于ListNode的鏈表(LeetCode的題目通常是這樣的鏈表)。因此本文將以此為例,介紹怎樣構建自己的動態鏈接庫,并通過pkg-config生成對應的鏈接選項。
編譯生成共享庫并添加到環境變量
源文件編譯生成共享庫
我們的鏈表庫的源代碼LinkedList.cpp
是這樣的,僅有兩個函數新建、打印做例子。其源文件和頭文件如下:
// LinkedList.cpp
#include <iostream>
#include <vector>using namespace std;struct ListNode{int val;ListNode* next;ListNode(int x) : val(x), next(NULL) {}
};ListNode* createList(const vector<int> vec){ListNode* head = new ListNode(0);ListNode* prev = head;for (int i : vec){ListNode* next = new ListNode(i);head->next = next;head = next;}return prev->next;
}void printList(ListNode* head){ListNode* p = head;while(p){cout << p->val << " -> ";p = p->next;}cout << "nullptr" << endl;
}// dsutils.h
#include <iostream>
#include <vector>
using namespace std;struct ListNode;
ListNode* createList(const vector<int> vec);
void printList(ListNode* head);
我們先來編譯鏈接生成共享庫:
g++ -shared -fpic LinkedList.cpp -o libmlist.so
由于我們的共享庫要在運行時動態鏈接,因此需要將它放到特定的目錄下,并將該目錄添加到環境變量LD_LIBRARY_PATH
,否則會在運行時報找不到庫的錯誤。(關于動態鏈接與加載,可參考:Linux下的ELF文件、鏈接、加載與庫(含大量圖文解析及例程))
mkdir /home/song/mlib
cp libmlist.so /home/song/mlib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/song/mlib
頭文件dsutils.h
也放在一個目錄下:
mkdir /home/song/minclude
cp dsutils /home/song/minclude
測試代碼編譯運行
現在我們創建一個測試代碼test.cpp
,
// test.cpp
#include <iostream>
#include <vector>#include "dsutils.h"using namespace std;int main(int argc, char* argv[]){vector<int> vec = {1, 2, 3, 4, 5};ListNode* head = createList(vec);printList(head);return 0;
}
其實我們現在就可以用了,只是需要比較復雜的鏈接選項。我們要這樣編譯(其選項具體含義可參考:gcc參數 -i, -L, -l, -include):
g++ test.cpp -I/home/song/minclude -L/home/song/mlib -lmlist -o test
就可以正常生成可執行文件了,其運行輸出為:
$ ./test
1 -> 2 -> 3 -> 4 -> 5 -> nullptr
但問題時,總不能每次都打這么一長串編譯選項,現在只有一個庫文件還好,如果大項目中庫文件項目多了,就記不住了。這時我們就需要pkg-config工具來幫助我們生成鏈接選項。
利用pkg-config生成編譯鏈接選項
安裝
如果還沒有安裝過pkg-config工具的讀者可以:
下載、解壓、安裝、驗證一氣呵成:
# 下載
wget https://pkg-config.freedesktop.org/releases/pkg-config-0.29.2.tar.gz
# 解壓
tar -zxvf pkg-config-0.29.2.tar.gz
# 安裝
cd pkg-config-0.29.2/
./configure
make
make check
sudo make install
# 驗證
pkg-config --version
# 輸出:0.29.2 安裝成功
配置dsutils.pc
安裝完成后,我們需要配置pc文件,來告訴pkg-config如何幫我們生成鏈接選項,即我們有dsutils.pc
:
lib_dir=/home/song/mlib
include_dir=/home/song/mincludeName: dsutils
Description: My utils of data structure.
Version: 0.1
Cflags: -I${include_dir}
Libs: -L${lib_dir} -lmlist
將配置好的dsutils.pc
放到pkg-config的目錄/usr/local/lib/pkgconfig/
下:
這時,可以在命令行中測試一下:
pkg-config dsutils --libs --cflags
一切正常的話會輸出:
-I/home/song/minclude -L/home/song/mlib -lmlist
讀者可能已經發現了,這就是上面那一串長長的編譯鏈接選項。這樣做的另一個好處是,如果我們有更多的dsutils,如libmbtree.so
,可以直接在dsutils.pc
中添加,來增加鏈接選項,而不用去記那么多的庫。
測試
測試只要將上面的選項在編譯時添加上即可:
g++ test.cpp -o test `pkg-config dsutils --libs --cflags`
如果一切正常則會產生一個可執行文件test
,運行它,我們將得到輸出:
1 -> 2 -> 3 -> 4 -> 5 -> nullptr
如果過程中有報錯,請先參考Linux下編譯、鏈接、加載運行C++ OpenCV的兩種方式及常見問題的解決。如問題還不能解決,歡迎留言討論。