CMake的hello world(三) 靜態庫與動態庫構建
本系列都是學習<CMake實踐>這本書,書下載連結
https://download.csdn.net/download/hjxu2016/10741464
這次任務是建立一個靜態庫和動態庫, 提供helloFunc函式供其他程式程式設計使用,HelloFunc向終端輸出 Hello World字串
在 t3 目錄下建立 CMakeLists.txt,內容如下:
PROJECT(HELLOLIB) ADD_SUBDIRECTORY(lib) # ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) # 這個指令用於向當前工程新增存放原始檔的子目錄, 並可以指定中間二進位制和目標二進位制存放的位置. # EXCLUDE_FROM_ALL 引數的含義是將這個目錄從編譯過程中排除, # 比如工程的example, 可能就需要工程構建完成後,再進入example目錄單獨進行構建
在 lib 目錄下建立兩個原始檔 hello.cpp 與 hello.h
hello.cpp 內容如下
#include"hello.h"
void HelloFunc(){
std::cout<<"hello World"<<std::endl;
}
hello.h 內容如下:
#ifndef HELLO_H
#define HELLO_H
#include <iostream>
void HelloFunc();
#endif
在 lib 目錄下建立 CMakeLists.txt,內容如下:
SET(LIBHELLO_SRC hello.cpp) ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}) # ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN) # 不需要寫全 libhello.so,只需要填寫hello即可,cmake系統會自動生成libhello.X的 # 型別有三種 # SHARED, 動態庫 # STATIC, 靜態庫 # MODULE, 在使用dyld的系統有效,如果不支援dyld,則被當做SHARED對待,what is dyld? # EXCLUDE_FROM_ALL 意思是這個庫不會被預設構建,除非有其他的元件依賴或者手工構建
這時候建立一個build目錄,整個專案tree如下:
.
├── build
├── CMakeLists.txt
├── CMakeLists.txt~
└── lib
├── CMakeLists.txt
├── CMakeLists.txt~
├── hello.cpp
├── hello.cpp~
├── hello.h
└── hello.h~
然後進入build目錄,採用外部編輯
cmake ..
make
這時,你就可以在 lib 目錄得到一個 libhello.so,這就是我們期望的共享庫。
問題來了,
下面我們用這個指令再來新增靜態庫:
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
然後再在 build 目錄進行外部編譯,我們會發現,靜態庫根本沒有被構建,仍然只生成了
一個動態庫。因為 hello 作為一個 target 是不能重名的,所以,靜態庫構建指令無效。
如果我們把上面的 hello 修改為 hello_static:
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 兩個庫了。
這時候我們看CMakeLists.txt內容
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
# ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
# 不需要寫全 libhello.so,只需要填寫hello即可,cmake系統會自動生成libhello.X的
# 型別有三種
# SHARED, 動態庫
# STATIC, 靜態庫
# MODULE, 在使用dyld的系統有效,如果不支援dyld,則被當做SHARED對待,what is dyld?
# EXCLUDE_FROM_ALL 意思是這個庫不會被預設構建,除非有其他的元件依賴或者手工構建
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
# SER_TARGET_PROPERTIES(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
# 這個指令可以用來設定輸出的名稱,對於動態庫,還可以用來指定動態庫版本和API版本
與他對應的指令是:
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.so 和
libhello.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