使用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
下篇我們將會使用到此次編譯好的庫。