hi-nginx-java的無配置路由配置
Linux下C++程式編譯連結的學習例子
一、專案例子的整體結構
-
本專案旨在簡單學習C++編譯連線的過程以及Makefile和Cmake的簡單語法,專案結構如下:
. ├── appC │ ├── include │ ├── lib │ └── src │ ├── CMakeLists.txt │ ├── main.cpp │ └── Makefile ├── cleanAll.sh ├── CMakeLists.txt ├── libA │ ├── output │ │ ├── include │ │ └── lib │ └── src │ ├── A.cpp │ ├── ADll.h │ ├── A.h │ ├── CMakeLists.txt │ └── Makefile ├── libB │ ├── include │ ├── lib │ ├── output │ │ ├── include │ │ └── lib │ └── src │ ├── B.cpp │ ├── BDll.h │ ├── B.h │ ├── CMakeLists.txt │ └── Makefile └── readme.md
-
依賴關係為:main->B->A,cleanAll.sh用來刪除生成的*.o *.a *.so檔案,以便學習不同的連結方式。
二、程式碼結構
-
main程式碼如下:
#include "B.h" #include <iostream> using namespace std; int main (int argc, char *argv[]) { B objB; int result = objB.func(1, 2); cout << "result = " << result << endl; }
-
libA程式碼如下:
/** *********A.cpp************ */ #include "A.h" #include <iostream> using namespace std; A::A() { cout << "A ctor" << endl; } A::~A() { cout << "A dtor" << endl; } int A::func(int x, int y) { return (x + y) * 2; } /** *********A.h************ */ #ifndef _A_H_ #define _A_H_ #include "ADll.h" class LIBA_API A { public: A(); ~A(); int func(int x, int y); }; #endif
-
libB程式碼如下:
/** *******************B.cpp******************** */ #include "B.h" #include "A.h" #include <iostream> using namespace std; class BImpl { public: BImpl() {}; ~BImpl() {}; int func(int x, int y){ return objA.func(x, y); } private: A objA; }; B::B() : _impl(new BImpl) { cout << "B ctor" << endl; } B::~B() { cout << "B dtor" << endl; delete _impl; } int B::func(int x, int y){ return _impl->func(x, y); } /** ************B.h******************* */ #ifndef _B_H_ #define _B_H_ #include "BDll.h" class BImpl; class LIBB_API B { public: B(); ~B(); int func(int x, int y); private: BImpl *_impl; }; #endif
-
以libA下的makefile為例學習makefile:
CXX = g++ #編譯器 AR = ar # 對.o檔案進行歸檔,生成庫 CXXFLAGS = -shared -g -Wall -fPIC -std=c++11 -I./ #g++引數:-shared 呼叫動態庫 -g 可執行程式包含除錯資訊 -Wall 編譯後顯示所有警告 -fPIC 真正的共享.so -std=c++11 支援c++11標準; -I 後跟標頭檔案地址 LDFLAGS = -L./ # 庫檔案地址 ### -Wl,-rpath=your_lib_dir 只有-L選項時有可能編譯通過但是執行時not found 庫,因為-L只有編 譯時有效,可以通過加上此命令記住連結庫的位置; SRCS = $(wildcard *.cpp) # wildcard 函式,展開所有的.cpp檔案 OBJS = $(patsubst %.cpp, %.o, $(SRCS)) patsubst函式 將.cpp全部替換成.o並返回,等於objs等於.cpp編譯的.o檔案 $(info SRCS = $(SRCS)) $(info OBJS = $(OBJS)) # info make的列印函式 TARGET = libA.so libA.a # 編譯目標 OUTPUT = ../output #輸出位置 all: clean $(TARGET) install copy2B # 讓所有的操作順次執行, .PHONY: clean install copy2B #因為make的規則是 目標 :依賴 ,當 main : main.c 時,為了從依賴獲取目標,就會執行之後的規則; #但是當clean: 後沒有依賴時,make會認為依舊獲取到目標,因此不再執行clean後的規則,因此 #引用.PHONY關鍵字,意為欺騙make,clean目標還未達成,即會執行clean之後的規則; # 目標 : 依賴 # 規則 .cpp.o: #自動將.cpp識別為原始檔字尾,.o識別為目標檔案字尾; g++ -c 將.cpp編譯成.o ,而不是可執行 @echo "\ncompile..." $(CXX) -c $< -o $@ -DLIBA_API_EXPORTS $(CXXFLAGS) # $@目標檔案 $^所有的依賴檔案 $<第一個依賴檔案 ¥% 僅當目標時函式庫文 件,表示規則中的目標成員名; $(TARGET): $(OBJS) # .o到.so .a $(CXX) $^ -o $@ $(CXXFLAGS) $(LDFLAGS) #g++生成動態庫 gcc -fPIC -shard -o 目標 原始檔 gcc -c -fPIC *.c gcc -shared -o *.so *.o $(AR) -crsv libA.a $^ # ar 建立靜態庫 -c 禁止再建立庫的檔案時產生的正常資訊 -r 如果已存在,則替換 -s 無論是否修改了庫的內容,強制生成庫符號表 其餘命令可查詢學習 install: @echo "\ninstall to output..." cp A.h ADll.h $(OUTPUT)/include # cp 移動 cp -a $(TARGET) ${OUTPUT}/lib clean: @echo "\nclean..." rm -rf $(TARGET) *.o #清除中間檔案 copy2B: @echo "\ncopy to libB..." cp ../output/include/* ../../libB/include cp ../output/lib* ../../libB/lib
-
現在已經學會了簡單的makefile檔案編寫,接下來就可以進行實踐嘗試了;
三、動態庫的連結
-
將A編譯成動態庫,B依賴A編譯成動態庫,main依賴B生成可執行檔案,依賴A,B的動態庫,
可以通過修改makefile來達到目的。
問題1:
A,B的編譯都順利通過,編譯main時依舊順利通過,但當執行./main時報錯:
./main: error while loading shared libraries: libA.so: cannot open shared object file: No such file or directory # 檢查makefile裡的配置 LDFLAGS = -lA -lB -L../lib/linux -Wl,-rpath=../lib/linux
通過ldd ./main可以檢視動態庫連結資訊,可以發現成功連結到libB.so,但是libA.so not found
$ ldd ./main linux-vdso.so.1 (0x00007ffe1d7ff000) libB.so => ../lib/linux/libB.so (0x00007f173646a000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f17360e1000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1735ec9000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1735ad8000) libA.so => not found libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f173573a000) /lib64/ld-linux-x86-64.so.2 (0x00007f1736870000)
解決方法:由於在Linux上,可執行程式會去系統預設下的路徑去尋找共享庫(*.so),因此動態庫放在自己的目錄下時,編譯時會通過,但是執行時卻會提示找不到。
-
linux系統預設的動態庫路徑有 /lib/ /usr/lib 因此在編譯動態庫時可以直接在makefile裡將動態庫cp到系統的預設路徑下,即可解決問題。
-
如果想自己建立一個用來學習測試的庫目錄,可以將自己的目錄新增到系統的配置檔案中,也可以達到目的,方法如下:
sudo vim /etc/ld.so.conf
通過vim或gedit開啟配置檔案,新增自己的目錄。
sudo /sbin/ldconfig
新增完成之後,儲存並通過以上命令載入配置即可。
至於為什麼可以找到B卻不能找到A,筆者猜測是由於可執行檔案先載入B,此時搜尋完成指定目錄,而B又依賴於A,此時去搜索A的時候,之前的搜尋已經完成,因此直接跳過指定目錄,轉而去搜索系統預設的其他路徑。以上只是隨意猜測,實際具體的原因筆者並未深入瞭解;
-
-
如果我們自己生成的B庫依賴於第三方的A庫,但是不希望主程式知道A庫,該怎麼辦呢?
即將A編譯成靜態庫,在編譯B時直接將A完成連結,此時只需要提供B的動態庫給主程式即可。我們可以通過修改makefile檔案來測試。
-
如果A,B都編譯為靜態庫,同樣需要將A,B都交給main。
四、CMake學習
1、以B和main的CmakeLists.txt檔案為例子,
#工程的CMakeList.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.0) #最低版本要求
PROJECT(test VERSION 1.0) #專案名稱
#編譯器
set(CMAKE_C_COMPILER "bin/gcc") #這裡沒用到交叉編譯,可以直接寫編譯器地址
set(CMAKE_CXX_COMPILER "bin/g++")
#編譯選項
set(CMAKE_CXX_FLAGS "-g -Wall -fPIC std=c++11 -02") #編譯引數
#libA
ADD_SUBDIRECTORY(libA/src) #新增子目錄
#libB
ADD_SUBDIRECTORY(libB/src)
#appC
ADD_SUBDIRECTORY(appC/src)
#libB下的CMakeList.txt
#原始檔
AUX_SOURCE_DIRECTORY(. SRC) # 此函式查詢目錄下所有原始檔 = src
#編譯動態庫
add_definitions("-DLIBB_API_EXPORTS")
#編譯靜態庫,因為B.cpp中include “A.h”,所以要定義LIBA_STATIC
#add_definitions("-DLIBA_STATIC")
#add_definitions("-DLIBB_STATIC")
#標頭檔案目錄
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include) # CMAKE_CURRENT_SOURCE_DIR 當前cmakelists所在的目錄
#庫檔案目錄
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../lib)
#生成動態庫
ADD_LIBRARY(libb_shared SHARED ${SRC}) #用法 ADD_LIBRARY(name [STATIC | SHARED |MODULE] source)
SET_TARGET_PROPERTIES(libb_shared PROPERTIES OUTPUT_NAME "B") #設定目標的一些屬性,此處設定了名字
TARGET_LINK_LIBRARIES(libb_shared A) #連結依賴庫
#生成靜態庫
ADD_LIBRARY(libb_static STATIC ${SRC})
SET_TARGET_PROPERTIES(libb_static PROPERTIES OUTPUT_NAME "B")
TARGET_LINK_LIBRARIES(libb_shared A)
set(OUTPUT ${PROJECT_SOURCE_DIR}/libB/output)
INSTALL(FILES B.h BDll.h DESTINATION ${OUTPUT}/include)
INSTALL(TARGETS libb_shared libb_static
LIBRARY DESTINATION ${OUTPUT}/lib)#動態庫安裝路徑
ARCHIVE DESTINATION ${OUTPUT}/lib}#靜態庫安裝路徑
## 沒用到的命令
##RUNTIME 可執行檔案
##public_HEADER 標頭檔案
#main
#原始檔
AUX_SOURCE_DIRECTORY(. SRC)
#使用靜態庫,因為main.cpp中 include"B.h",所以要定義LIBB_STATIC
add_definitions("-DLIBB_STATIC")
#標頭檔案目錄
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include)
#庫檔案目錄
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../lib)
#生成可執行檔案
ADD_EXECUTABLE(main ${SRC})
TARGET_LINUX_LIBRARIES(main
libB.so
libA.so)
注意在新建build資料夾來進行編譯,每一個CMakeLists生成的makefile需要單獨進行make編譯。