使用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
下篇我们将会使用到此次编译好的库。