(轉)HelloWorld CMake CMake中構建靜態庫與動態庫及其使用

繼續完善Hello World,創建它的共享庫, 包括靜態庫和動態庫。編程

 

本節的任務:bash

1,創建一個靜態庫和動態庫,提供HelloFunc函數供其餘程序編程使用,HelloFuncide

向終端輸出Hello World字符串。函數

2,安裝頭文件與共享庫。學習

3 編寫一個程序使用建立的共享庫(靜態庫和動態庫)。ui

 

cd /home/ccj/CMakeDemothis

mkdir t3spa

 

 

cd /backup/cmake/t3three

mkdir lib開發

 

t3目錄下創建CMakeLists.txt,內容以下:

PROJECT(HELLOLIB)

# 經過在主工程文件CMakeLists.txt中修改ADD_SUBDIRECTORY (lib) 指令來指定一個編譯輸出位置;

# 指定本工程中靜態庫libhello.so生成的位置,即 build/lib;

ADD_SUBDIRECTORY(lib)

# 也能夠經過變動爲其餘的位置,如

# ADD_SUBDIRECTORY(lib lib_new)

# 則,靜態庫libhello.so生成的位置變爲 build/lib_new;

 

lib目錄下創建兩個源文件hello.cpp hello.h

hello.cpp內容以下:

#include "hello.h"

using namespace std;

void HelloFunc(){

cout << "Hello World/n";

}

hello.h內容以下:

#ifndef HELLO_H

#define HELLO_H

#include <stdio.h>

void HelloFunc();

#endif

 

lib目錄下創建CMakeLists.txt,內容以下:

注意: 這裏咱們將Section 7以前的CMakeLists.txt中應有的內容提供以下:

SET (LIBHELLO_SRC hello.cpp)

# SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

 

# three kinds of libraries:

# 1. shared, i.e., dynamic library,動態庫文件擴展名常爲 "*.so"

# 2. static, i.e., static library 靜態庫文件擴展名常爲 "*.a";

# 並且,咱們一般但願靜態庫和動態庫文件名保持一致,只是擴展名不一樣;

# 3. module, this parameter is valid only when dyld is supported;

# otherwise, will be considered as shared

# 添加動態庫,關鍵詞爲shared,你不須要寫全libhello.so

# 只須要填寫hello便可,cmake系統會自動爲你生成 libhello.X

ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC})

# 添加靜態庫,關鍵詞爲static

# ADD_LIBRARY (hello STATIC ${LIBHELLO_SRC})

# 仍然用hello做爲target名時,是不能成功建立所需的靜態庫的,

# 由於hello做爲一個target是不能重名的, 故把上面的hello修改成hello_static

# 同理,你不須要寫全libhello_static.a

# 只須要填寫hello便可,cmake系統會自動爲你生成 libhello_static.X

ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC})

 

# 按照通常的習慣,靜態庫名字跟動態庫名字應該是一致的,只是擴展名不一樣;

# 即:靜態庫名爲 libhello.a 動態庫名爲libhello.so

# 因此,但願 "hello_static" 在輸出時,不是"hello_static",而是以"hello"的名字顯示,故設置以下:

SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")

 

GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)

MESSAGE (STATUS "This is the hello_static OUTPUT_NAME: " ${OUTPUT_VALUE})

 

# cmake在構建一個新的target時,會嘗試清理掉其餘使用這個名字的庫,

# 所以,在構建libhello.a時,就會清理掉libhello.so.

# 爲了迴避這個問題,好比再次使用SET_TARGET_PROPERTIES定義 CLEAN_DIRECT_OUTPUT屬性。

SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

 

# 按照規則,動態庫是應該包含一個版本號的,

# VERSION指代動態庫版本,SOVERSION指代API版本。

SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)

 

# 咱們須要將libhello.a, libhello.so.x以及hello.h安裝到系統目錄,才能真正讓其餘人開發使用,

# 在本例中咱們將hello的共享庫安裝到<prefix>/lib目錄;

# hello.h安裝<prefix>/include/hello目錄。

INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib

ARCHIVE DESTINATION lib)

INSTALL (FILES hello.h DESTINATION include/hello)

 

 

:

仍然採用out-of-source編譯的方式,按照習慣,咱們創建一個build目錄,在build

目錄中

cmake ..

make

這時,你就能夠在lib目錄獲得一個libhello.so,這就是咱們指望的共享庫。

若是你要指定libhello.so生成的位置,能夠經過:

  • 在主工程文件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)

你不須要寫全 libhello.so,只須要填寫hello便可,cmake系統會自動爲你生成 libhello.X

 

類型有三種:

  • SHARED,動態庫;
  • STATIC,靜態庫
  • MODULE,在使用dyld的系統有效,若是不支持dyld,則被看成SHARED對待。

 

EXCLUDE_FROM_ALL參數的意思是這個庫不會被默認構建,除非有其餘的組件依賴或者手動構建。

 

 

一樣使用上面的指令,咱們在支持動態庫的基礎上再爲工程添加一個靜態庫,按照通常的習

慣,靜態庫名字跟動態庫名字應該是一致的,只不事後綴是.a罷了。

下面咱們用這個指令再來添加靜態庫:

 

ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

 

而後再在build目錄進行外部編譯,咱們會發現,靜態庫根本沒有被構建,仍然只生成了

一個動態庫。由於hello做爲一個ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

就能夠構建一個libhello_static.a的靜態庫了。

這種結果顯示不是咱們想要的,咱們須要的是名字相同的靜態庫和動態庫,由於target

稱是惟一的,因此,咱們確定不能經過ADD_LIBRARY指令來實現了。這時候咱們須要用到

另一個指令:

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兩個庫了。

 

SET_TARGET_PROPERTIES對應的指令是:

GET_TARGET_PROPERTY (VAR target property)

 

具體用法以下例,咱們向lib/CMakeListst.txt中添加:

GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)

MESSAGE(STATUS "This is the hello_static OUTPUT_NAME: "${OUTPUT_VALUE})

若是沒有這個屬性定義,則返回NOTFOUND.

 

讓咱們來檢查一下最終的構建結果,咱們發現,libhello.a已經構建完成,位於

build/lib目錄中,可是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.solibhello.a了。

 

 

按照規則,動態庫是應該包含一個版本號的,咱們能夠看一下系統的動態庫,通常狀況是

libhello.so.1.2

libhello.so ->libhello.so.1

libhello.so.1->libhello.so.1.2

爲了實現動態庫版本號,咱們仍然須要使用SET_TARGET_PROPERTIES指令。

具體使用方法以下:

SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION指代動態庫版本,SOVERSION指代API版本。

將上述指令加入lib/CMakeLists.txt中,從新構建看看結果。

build/lib目錄會生成:

libhello.so.1.2

libhello.so.1->libhello.so.1.2

libhello.so ->libhello.so.1

 

 

目錄,將hello.h安裝<prefix>/include/hello目錄。

利用上一節瞭解到的INSTALL指令,咱們向lib/CMakeLists.txt中添加以下指令:

INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib

ARCHIVE DESTINATION lib)

INSTALL (FILES hello.h DESTINATION include/hello)

注意,靜態庫要使用ARCHIVE關鍵字

 

經過:

cmake -DCMAKE_INSTALL_PREFIX=/usr/loal ..

make

sudo make install

咱們就能夠將頭文件和共享庫安裝到系統目錄/usr/local/lib/usr/local/include/hello中了。

以下圖:

 

 

 

前幾個sections中,咱們已經完成了libhello靜、動態庫的構建以及安裝,本section中咱們須要

編寫一個程序使用構建好的共享庫。

 

)在目錄下創建目:#include "hello.h"

int main(){

HelloFunc();

return 0;

}

 

編寫工程主文件CMakeLists.txt

PROJECT(NEWHELLO)

ADD_SUBDIRECTORY(src)

 

編寫src/CMakeLists.txt

ADD_EXECUTABLE(main main.cpp)

 

)外部構建cmake ..

make

 

構建失敗,若是須要查看細節,可使用 make VERBOSE=1來構建。

錯誤結果以下:

1

 

 

)引入頭文件搜索路徑。

hello.h位於/usr/local/include/hello目錄中,並無位於,同志,要這麼幹,我這

一節就沒什麼可寫了,只能選擇一個glib或者libX11來寫了,這些代碼寫出來不少同志

是看不懂的)

爲了讓咱們的工程可以找到hello.h頭文件,咱們須要引入一個新的指令

INCLUDE_DIRECTORIES,其完整語法爲:

INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

這條指令能夠用來向工程添加多個特定的頭文件搜索路徑,路徑之間用如今咱們在src/CMakeLists.txt中添加一個頭文件搜索路徑,方式很簡單,加入:

INCLUDE_DIRECTORIES(/usr/local/include/hello)

 

進入build目錄,從新進行構建,這時找不到hello.h的錯誤已經消失,可是出現了一個新的錯誤:

 

2

main.cpp:(.text+0x5): undefined reference to `HelloFunc()'

 

 

由於咱們並無link到共享庫libhello上。

 

target這個指令能夠用來爲target添加須要連接的共享庫,本例中是一個可執行文件,可是一樣能夠用於爲本身編寫的共享庫添加共享庫連接。

 

爲了解決咱們前面遇到的"HelloFunc()"未定義錯誤,咱們須要做的是向

src/CMakeLists.txt中添加以下指令:

也能夠寫成

進入build目錄從新進行構建。

cmake ..

make

這時咱們就獲得了一個鏈接到libhello的可執行程序main,位於build/src目錄,

運行main的結果是

讓咱們來檢查一下main的連接狀況:

$ ldd src/main

雖然了這個結果,可是沒有理解明白,到底連接什麼庫

 

能夠清楚的看到main確實連接了共享庫libhello,並且連接的是動態庫libhello.so.1

 

$ ldd src/main

雖然了這個結果,可是沒有理解明白,到底連接什麼庫

 

說明,main確實連接到了靜態庫libhello.a

 

)特殊的環境變量:務必注意CMAKE_INCLUDE_PATH CMAKE_LIBRARY_PATH這兩個是環境變量來進行,使用bash的方法以下:

而後在頭文件中將INCLUDE_DIRECTORIES(/usr/local/include/hello)替換爲:

FIND_PATH(myHeader hello.h)

IF(myHeader)

INCLUDE_DIRECTORIES(${myHeader})

ENDIF(myHeader)

 

上述的一些指令咱們在後面會介紹。這裏簡單說明一下,FIND_PATH用來在指定路徑中搜索文件名,好比:FIND_PATH (myHeader NAMES hello.h PATHS /usr/local/include /usr/local/include/hello)

這裏咱們沒有指定路徑,可是,cmake仍然能夠幫咱們找到hello.h存放的路徑,就是因

爲咱們設置了環境變量CMAKE_INCLUDE_PATH

若是你不使用FIND_PATHCMAKE_INCLUDE_PATH變量的設置是不會起做用的,你不能指

望它會直接爲編譯器命令添加參數-I<CMAKE_INCLUDE_PATH>

 

同理,CMAKE_LIBRARY_PATH能夠用在FIND_LIBRARY中。一樣,由於這些變量直接爲FIND_指令所使用,因此全部使用FIND_指令的cmake模塊都會受益。

 

到這裏爲止,您應該基本可使用cmake工做了,可是還有不少高級的話題沒有探討,比

如編譯條件檢查、編譯器定義、平臺判斷、如何跟pkgconfig配合使用等等。

到這裏,或許你能夠理解前面講到的"cmake的使用過程其實就是學習cmake語言,並編寫

cmake程序的過程",既然是"cmake語言",天然涉及到變量、語法等. 下一節,咱們將拋開程序的話題,看看經常使用的CMAKE變量以及一些基本的控制語法規則。

相關文章
相關標籤/搜索