CMake編程實踐(五) 編譯靜態庫和動態庫

使用Cmake 編譯庫

本篇使用CMake編譯一個動態庫和靜態庫,並安裝到系統中,對應的工程是cmake-utilsbox-lib

編譯靜態庫

指定編譯靜態庫,關鍵詞爲static,不添加關鍵字默認靜態庫

add_library(utils ${LIB_SOURCE})

在使用該庫的工程中鏈接都剛纔編譯的庫:

target_link_libraries(${PROJECT_NAME} utils)

編譯動態庫

指定編譯動態庫,關鍵詞爲shared

add_library(utils SHARED ${LIB_SOURCE})

在使用該庫的工程中鏈接都剛纔編譯的庫:

target_link_libraries(${PROJECT_NAME} utils)

網上很多文章說 cmake在構建一個新的庫時,會嘗試清理掉其他使用這個名字的庫,但我測試並不會清除,我的cmake version 3.5.1
但這需要編譯兩次有點麻煩,所以可以在cmake中指定同時輸出靜態庫和動態庫

先來看下目錄樹

.
├── CMakeLists.txt
├── doc
│ └── Introduction.txt
├── README.md
├── src
│ ├── CMakeLists.txt
│ ├── utilsbox.cpp
│ └── utilsbox.h
└── test
    ├── CMakeLists.txt
    └── main.cpp

先來看下項目根CMakeLists.txt,由於本次主要編譯的是庫,無法直接驗證,所以用了ADD_TEST指令指定了一個測試程序,用來驗證庫是否正常可用,但這不是強制的,主要是順便也學習下ADD_TEST的用法。其他的沒什麼特別的,看註釋已經很清楚了。

# 聲明編譯要求cmake最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)

# ADD_DEFINITIONS(-std=c++11)

# 聲明一個cmake工程
PROJECT(UtilsBox)

# 添加子目錄, 並指定指定中間二進制和目標二進制存放的位置
ADD_SUBDIRECTORY(src bin)
ADD_SUBDIRECTORY(test)

# 這裏相當於定義一個指定的測試程序,以便make test調用
ADD_TEST(utilsbox-test ${PROJECT_BINARY_DIR}/test/utilsbox-test)
ENABLE_TESTING()

# 安裝當前工程中doc/目錄下的所有內容到doc目錄下
INSTALL(DIRECTORY doc/ DESTINATION doc)

src/CMakeLists.txt

# 聲明編譯要求cmake最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)

# 配置支持C++11
ADD_DEFINITIONS(-std=c++11)

# 添加庫源文件
SET(LIB_SOURCE utilsbox.cpp)

# 添加靜態庫,關鍵詞爲static
ADD_LIBRARY(utilsbox STATIC ${LIB_SOURCE})

# 添加動態庫,關鍵詞爲shared
ADD_LIBRARY(utilsbox_shared SHARED ${LIB_SOURCE})

# 指定動態庫的輸出名稱
SET_TARGET_PROPERTIES(utilsbox_shared PROPERTIES OUTPUT_NAME "utilsbox")

# 指定動態庫版本, 視需求而定,可不加
# VERSION:動態庫版本,SOVERSION:API版本
SET_TARGET_PROPERTIES(utilsbox_shared PROPERTIES VERSION 0.1.1 SOVERSION 0)

# 指定庫編譯輸出目錄
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# 指定庫安裝位置
# SET(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/lib_utilsbox)

INSTALL(FILES utilsbox.h DESTINATION include)
INSTALL(TARGETS utilsbox utilsbox_shared
    # 動態庫安裝位置
    LIBRARY DESTINATION lib
    # 靜態庫安裝位置
    ARCHIVE DESTINATION lib
    )

上面主要用到了ADD_LIBRARY用於編譯生成庫,使用STATIC關鍵字或不加任何關鍵字默認生成靜態庫,使用SHARED關鍵字生成動態庫,由於在CMake體系中不允許存在兩個同名的庫,所以我們把動態庫的名稱後面加了一個_shared後綴,爲了保證最終生成的庫名稱是相同的,所以又使用了SET_TARGET_PROPERTIES()中的OUTPUT_NAME選項指定動態庫輸出的名稱。雖然繞了一圈,但最終目的是同時編譯出同名的靜態庫和動態庫。

SET_TARGET_PROPERTIES() 指令用來指定動態庫版本(靜態庫我試過無效)。

SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib),指定編譯生成的庫存放在lib目錄下

使用INSTALL指定庫的安裝位置,注意LIBRARY指定動態庫的目錄,ARCHIVE指定靜態庫的路徑,具體用法在前面已經講過了,這裏就不贅述了。

src/utilsbox.cpp

#include <iostream>
#include <stdarg.h>
#include <vector>
#include "utilsbox.h"

using namespace std;

std::string UtilsBox::GetVersion()
{ 
 std::string version = UTILSBOX_VERSION;
 return version;
}

std::string UtilsBox::format(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    int len = vsnprintf(nullptr, 0, fmt, ap);
    va_end(ap);
    std::string buf(len+1, '\0');
    va_start(ap, fmt);
    vsnprintf(&buf[0], buf.size(), fmt, ap);
    va_end(ap);
    buf.pop_back();
    return buf;
}

UtilsBox::format()用來格式化輸入字符串,和printf的功能類似,但返回的是string。

src/utilsbox.h

#ifndef UTILSBOX_H
#define UTILSBOX_H

#include <string>

#define UTILSBOX_VERSION "v0.1.1"

namespace UtilsBox
{
    std::string GetVersion(void);
    std::string format(const char *fmt, ...);
}

#endif

test/CMakeLists.txt

# 聲明編譯要求cmake最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)

# 添加源文件
SET(SOURCE main.cpp)

# 指定庫的位置爲項目根目錄下的lib目錄
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/build/lib)
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src)

# 添加一個可執行程序,名稱和工程名稱保持一致
ADD_EXECUTABLE(utilsbox-test ${SOURCE})
TARGET_LINK_LIBRARIES(utilsbox-test utilsbox)

test/main.cpp

#include <iostream>
#include "utilsbox.h"

using namespace std;

int main(int argc, char** argv)
{
 cout << UtilsBox::GetVersion() << endl;
 return 0;
}

一個簡單的測試程序,測試輸出的庫是否正常。

編譯:

mkdir build && cd build
cmake ..
make
# 可以看到在build/lib/下已經生成靜態庫和動態庫了
ls ./lib 
libutilsbox.a libutilsbox.so libutilsbox.so.0 libutilsbox.so.0.1.1

#安裝
sudo make install
[ 33%] Built target utilsbox
[ 66%] Built target utilsbox_shared
[100%] Built target utilsbox-test
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/local/doc
-- Up-to-date: /usr/local/doc/Introduction.txt
-- Up-to-date: /usr/local/include/utilsbox.h
-- Installing: /usr/local/lib/libutilsbox.a
-- Installing: /usr/local/lib/libutilsbox.so.0.1.1
-- Up-to-date: /usr/local/lib/libutilsbox.so.0
-- Up-to-date: /usr/local/lib/libutilsbox.so

可以看到已經安裝到/usr/local/lib/下了
至此我們已經完成了靜態庫和動態庫的編譯生成和安裝,完成了以下幾個功能點:
1.同時輸出靜態庫和動態庫
2.指定動態庫版本(靜態庫我試過無效)
3.指定庫編譯輸出的目錄
4.指定庫安裝的目錄
5.指定庫存放目錄

cmake-demo源碼已上傳到github,歡迎Star

下篇我們將會使用到此次編譯好的庫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章