參考資料地址:https://github.com/Akagi201/learning-cmake/blob/master/docs/cmake-practice.pdflinux
1、靜態庫與動態庫構建git
本小節目標以下:github
1. 創建工做目錄t3編程
mkdir -p /backup/cmake/t3bash
2. 創建共享庫目錄函數
cd /backup/cmake/t3ui
mkdir libspa
在t3工程目錄下創建CMakeLists.txt:debug
// /backup/cmake/t3/CMakeLists.txt
1 PROJECT(HELLOLIB) 2 ADD_SUBDIRECTORY(lib)
在lib目錄下創建源文件hello.c與hello.h:code
//hello.c
1 #include "hello.h" 2 void HelloFunc() 3 { 4 printf("Hello World\n"); 5 }
//hello.h
1 #ifndef HELLO_H 2 #define HELLO_H 3 #include <stdio.h> 4 void HelloFunc(); 5 #endif
在lib子目錄下創建CMakeLists.txt:
// /backup/cmake/t3/CMakeLists.txt
1 SET(LIBHELLO_SRC hello.c) 2 ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
3. 編譯共享庫
mkdir build + cd build
cmake .. + make //在lib目錄生成共享庫libhello.so
//可修改工程目錄t3中的CMakeLists.txt指定生成位置:ADD_SUBDIRECTORY(lib <目錄>)指令來指定一個編譯輸出位置
//或者在lib/CMakeLists.txt中添加SET(LIBRARY_OUTPUT_PATH <路徑>)來指定一個新的位置
ADD_LIBRARY指令的語法:
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
注:
(1)無需寫全libhello.so,只須要將libname設置爲hello便可,cmake系統會自動生成libhello.X
(2)庫類型有三種:
(3)EXCLUDE_FROM_ALL參數的意思是這個庫不會被默認構建,除非有其餘的組件依賴或者手工構建
4. 添加靜態庫(lib/CMakeLists.txt)
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC}) //靜態庫和動態庫名字一致,生成後綴爲.a
添加上述命令並從新進行外部編譯後,仍然僅生成動態庫,並無構建靜態庫。緣由:hello做爲一個target是不能重名的,因此,靜態庫構建指令無效。
(1)解決方案一:ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
構建了libhello_static.a的靜態庫;不足:靜態庫和動態庫名字不一樣
(2)解決方案二:採用SET_TARGET_PROPERTIES指令,基本語法以下
SET_TARGET_PROPERTIES(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
設置輸出的名稱,對於動態庫,還能夠用來指定動態庫版本和API版本
在lib/CMakeLists.txt繼續添加:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
至此,能夠同時獲得libhello.so和libhello.a兩個庫。
補充:對應的GET_TARGET_PROPERTY指令
GET_TARGET_PROPERTY(VAR target property)
具體用法:向lib/CMakeLists.txt添加
GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE(STATUS 「This is the hello_static OUTPUT_NAME: 」 ${OUTPUT_VALUE}) //若是未定義該屬性,則返回NOTFOUND
(3)問題:檢查最終的構建結果,會發現build/lib目錄中存在libhello.a,可是libhello.so卻消失了!
緣由:cmake在構建一個新的target時,會嘗試清理掉其餘使用這個名字的庫,所以在構建libhello.a時,會清理掉libhello.so
解決方案:使用SET_TARGET_PROPERTIES定義CLEAN_DIRECT_OUTPUT屬性
向lib/CMakeLists.txt中添加:
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
至此,build/lib目錄中同時生成了libhello.so和libhello.a庫
5. 動態庫版本號:使用SET_TARGET_PROPERTIES指令
在lib/CMakeLists.txt中加入:SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
構建結果會在build/lib目錄生成:
libhello.so.1.2
libhello.so.1 -> libhello.so.1.2
libhello.so -> libhello.so.1
6. 安裝共享庫和頭文件(將libhello.a, libhello.so.x以及hello.h安裝到系統目錄,以供其餘人開發使用)
在lib/CMakeLists.txt中添加以下指令:
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
注:靜態庫要使用ARCHIVE關鍵字
執行cd build + cmake -DCMAKE_INSTALL_PREFIX=/usr .. + make + make install 將頭文件和共享庫安裝到系統目錄/usr/lib和/usr/include/hello目錄中
2、使用外部共享庫和頭文件
1. 建立工做目錄
mkdir -p /bakcup/cmake/t4
2. 創建src子目錄 mkdir src,編寫源文件main.c:
//main.c
1 #include<hello.h> 2 int main() 3 { 4 HelloFunc(); 5 return 0; 6 }
編寫工程目錄CMakeLists.txt:
// /backup/cmake/t4/CMakeLists
1 PROJECT(NEWHELLO) 2 ADD_SUBDIRECTORY(src)
編寫src/CMakeLists.txt:
// /backup/cmake/t4/src/CMakeLists
1 ADD_EXECUTABLE(main main.c)
3. 外部構建
mkdir build + cd build + cmake .. + make VERBOSE=1
構建失敗,錯誤輸出爲:/backup/cmake/t4/src/main.c:1:19: error: hello.h: 沒有那個文件或目錄
緣由:找不到頭文件
4. 引入頭文件搜索路徑(上一節的hello.h位於/usr/include/hello目錄中,並無位於系統標準的頭文件路徑)
解決方法:採用INCLUDE_DIRECTORIES指令
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
用來向工程添加多個特定的頭文件搜索路徑,路徑之間用空格分割,若是路徑中包含了空格,可使用雙引號將它括起來,默認的行爲是追加到當前的頭文件搜索路徑的後面,能夠經過兩種方式來進行控制搜索路徑添加的方式:
(1)經過SET指令設置cmake變量CMAKE_INCLUDE_DIRECTORIES_BEFORE爲on,將添加的頭文件搜索路徑放在已有路徑的前面
(2)經過上述指令的AFTER或者BEFORE參數,控制是追加仍是置前
所以,在src/CMakeLists.txt中添加頭文件搜索路徑:INCLUDE_DIRECTORIES(/usr/include/hello)
從新進入build構建,仍然會失敗,新的錯誤爲:main.c:(.text+0x12): undefined reference to `HelloFunc'
緣由:目標文件沒有link到共享庫libhello上
5. 爲target添加共享庫
解決方法:LINK_DIRECTORIES和TARGET_LINK_LIBRARIES指令
LINK_DIRECTORIES(directory1 directory2 ...) //添加非標準的共享庫搜索路徑
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...) //添加連接,爲target添加須要連接的共享庫或可執行二進制文件,本例中是一個可執行文件,也能夠用於爲本身編寫的共享庫添加共享庫連接
所以,在src/CMakeLists.txt中添加以下指令:TARGET_LINK_LIBRARIES(main hello) 或者 TARGET_LINK_LIBRARIES(main libhello.so)
從新進入build構建,獲得了一個鏈接到libhello的可執行程序main,位於build/src目錄
查看main的連接狀況:ldd src/main //ldd:list dynamic dependencies,列出動態庫依賴關係
linux-gate.so.1 => (0xb7ee7000)
libhello.so.1 => /usr/lib/libhello.so.1 (0xb7ece000) //連接動態庫libhello.so.1
libc.so.6 => /lib/libc.so.6 (0xb7d77000)
/lib/ld-linux.so.2 (0xb7ee8000)
如何連接到靜態庫?修改連接指令爲:TARGET_LINK_LIBRARIES(main libhello.a)
ldd src/main:
linux-gate.so.1 => (0xb7fa8000)
libc.so.6 => /lib/libc.so.6 (0xb7e3a000)
/lib/ld-linux.so.2 (0xb7fa9000)
結果無動態庫,代表確實連接到靜態庫
6. 特殊的環境變量CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH(非cmake變量)
用於在bash中用export或者在csh中使用set命令設置或者CMAKE_INCLUDE_PATH=/home/include cmake ..等方式;主要用於彌補頭文件沒有存放在常規路徑的搜索狀況
例如前面咱們直接使用了絕對路徑INCLUDE_DIRECTORIES(/usr/include/hello)告訴工程這個頭文件目錄。爲了將程序更智能一點,咱們可使用CMAKE_INCLUDE_PATH來進行,使用bash的方法以下:
export CMAKE_INCLUDE_PATH=/usr/include/hello
而後在src/CMakeLists.txt中將INCLUDE_DIRECTORIES(/usr/include/hello)替換爲:
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)
//FIND_PATH用來在指定路徑中搜索文件名,如:FIND_PATH(myHeader NAMES hello.h PATHS /usr/include /usr/include/hello),此處沒有指定路徑但仍然能夠搜索到的緣由爲:設置了環境變量CMAKE_INCLUDE_PATH;相應的FIND_LIBRARY可使用CMAKE_LIBRARY_PATH變量,全部使用FIND_指令的cmake模塊都可使用上述環境變量。