1. 程式人生 > >CMake的hello world(三) 靜態庫與動態庫構建

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