Cmake實踐(Cmake Practice)第二部分

參考資料地址:https://github.com/Akagi201/learning-cmake/blob/master/docs/cmake-practice.pdflinux

1、靜態庫與動態庫構建git

本小節目標以下:github

  • 創建一個靜態庫和動態庫,提供HelloFunc函數供其餘程序編程使用,HelloFunc向終端輸出Hello World字符串
  • 安裝頭文件與共享庫

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)庫類型有三種:

  • SHARED:動態庫
  • STATIC: 靜態庫
  • MODULE:在使用dyld的系統中有效,若是不支持dyld則被視爲SHARED

(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模塊都可使用上述環境變量。

相關文章
相關標籤/搜索