CMake的hello world(三) 静态库与动态库构建

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/hjxu2016/article/details/83343743

本系列都是学习<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

猜你喜欢

转载自blog.csdn.net/hjxu2016/article/details/83343743