做网站的大公司,wordpress视频适应手机,那些公司做网站好,香洲区建设局网站CMake构建静态库与动态库一、任务二、准备工作三、编译共享库四、ADD_LIBRARY指令五、编译静态库5.1、SET_TARGET_PROPERTIES指令5.2、GET_TARGET_PROPERTY指令六、动态库版本号七、安装共享库和头文件八、使用外部共享库和头文件8.1、准备工作8.2、引入头文件搜索路径8.3、为 …
CMake构建静态库与动态库一、任务二、准备工作三、编译共享库四、ADD_LIBRARY指令五、编译静态库5.1、SET_TARGET_PROPERTIES指令5.2、GET_TARGET_PROPERTY指令六、动态库版本号七、安装共享库和头文件八、使用外部共享库和头文件8.1、准备工作8.2、引入头文件搜索路径8.3、为 target 添加共享库九、使用外部静态库和头文件十、特殊的环境变量总结一、任务
用例子的方式通俗易懂地解释CMake构建静态库与动态库的过程。
任务
建立一个静态库和动态库提供HelloFunc 函数供其他程序编程使用HelloFunc向终端输出Hello World 字符串。安装头文件与共享库。编写一个程序去使用构建的共享库。
二、准备工作
1建立 t3 目录用于存放本节涉及到的工程。
mkdir t32建立共享库。
cd t3
mkdir lib3在 t3 目录下建立CMakeLists.txt内容如下
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)4在 lib 目录下建立两个源文件hello.c与hello.h。
hello.c 内容如下
#include hello.h
void hello_func(void)
{printf(Hello World!\n);return;
}hello.h 内容如下
#ifndef HELLO_H_
#define HELLO_H_ (1)
#include stdio.h
void hello_func(void);
#endif5在 lib 目录下建立CMakeLists.txt内容如下
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})三、编译共享库
采用out-of-source 编译的方式建立一个build 目录在build目录中执行
mkdir build
cd build
cmake ..
make这时就可以在lib 目录得到一个libhello.so这就是共享库。
如果要指定libhello.so 生成的位置可以通过在主工程文件CMakeLists.txt 中修改 ADD_SUBDIRECTORY(lib)指令来指定一个编译输出位置或者在lib/CMakeLists.txt 中添加 SET(LIBRARY_OUTPUT_PATH 路径)来指定一个新的位置。
四、ADD_LIBRARY指令
语法
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)不需要写全libhello.so只需要填写hello 即可cmake 系统会自动生成 libhello.X
类型有三种:
SHARED动态库。STATIC静态库。MODULE在使用 dyld 的系统有效如果不支持dyld则被当作SHARED 对待。
EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建除非有其他的组件依赖或者手工构建。
五、编译静态库
静态库名字跟动态库名字应该是一致的只不过后缀是.a 罢了。下面用这个指令再来添加静态库 ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})注意如果是在上面的编译动态库的基础上添加的上述内容会发现静态库根本没有被构建仍然只生成了一个动态库。因为hello 作为一个target 是不能重名的所以静态库构建指令无效。如果把上面的hello 修改为hello_static:
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})就可以构建一个libhello_static.a 的静态库了。
但是这种结果显示不是想要的我们需要的是名字相同的静态库和动态库因为 target 名称是唯一的所以肯定不能通过 ADD_LIBRARY 指令来实现了。这时候我们需要用到另外一个指令SET_TARGET_PROPERTIES。
5.1、SET_TARGET_PROPERTIES指令
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 两个库了。
lib/CMakeLists.txt最终的的内容
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME hello)5.2、GET_TARGET_PROPERTY指令
与SET_TARGET_PROPERTY对应的指令是GET_TARGET_PROPERTY基本语法为
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.0
libhello.so -libhello.so.1
libhello.so.1-libhello.so.1.0为了实现动态库版本号需要使用 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七、安装共享库和头文件
以上面的例子需要将libhello.a, libhello.so.x 以及 hello.h 安装到系统目录才能真正让其他人开发使用在本例中我们将 hello 的共享库安装到 prefix /lib目录将hello.h 安装到 prefix /include/hello 目录。
利用上一篇了解到的INSTALL 指令向lib/CMakeLists.txt 中添加如下指令
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)注意静态库要使用ARCHIVE 关键字
通过如下命令安装
cmake -DCMAKE_INSTALL_PREFIX/usr ..
make
sudo make install这样就可以将头文件和共享库安装到系统目录/usr/lib 和/usr/include/hello 中了。
[ 50%] Built target hello
[100%] Built target hello_static
Install the project...
-- Install configuration:
-- Installing: /usr/lib/libhello.so.1.2
-- Installing: /usr/lib/libhello.so.1
-- Installing: /usr/lib/libhello.so
-- Installing: /usr/lib/libhello.a
-- Installing: /usr/include/hello/hello.h如果报错
CMake Error: cmake_symlink_library: System Error: Operation not supported
CMake Error: cmake_symlink_library: System Error: Operation not supported
make[2]: *** [lib/CMakeFiles/hello_dynamic.dir/build.make:85: lib/libhello.so.1.2] Error 1
make[2]: *** Deleting file lib/libhello.so.1.2
make[1]: *** [CMakeFiles/Makefile2:130: lib/CMakeFiles/hello_dynamic.dir/all] Error 2
make: *** [Makefile:130: all] Error 2则说明可能是通过hgfs共享的Windows目录下进行make生成so时需要在纯linux环境运行比如把t3拷贝到~/t3 并且先把对应的build目录里面的内容清空重新执行
cmake ..
make八、使用外部共享库和头文件
8.1、准备工作
1建立 t4 目录本节所有资源将存储在t4 目录。
mkdir t42在t4目录下建立src 目录。
cd t4
mkdir src3src目录下编写源文件main.c内容如下
#include hello.h
int main(void)
{hello_func();return 0;
}4t4目录下编写工程主文件CMakeLists.txt。
PROJECT(NEWHELLO)
ADD_SUBDIRECTORY(src)5t4/src目录下编写src/CMakeLists.txt。
ADD_EXECUTABLE(main main.c)8.2、引入头文件搜索路径
准备工作完成后直接构建是失败的因为找不到头文件hello.h。错误输出为是
cmake/t4/src/main.c:1:19: error: hello.h: 没有那个文件或目录为了让我们的工程能够找到hello.h 头文件需要引入一个新的指令INCLUDE_DIRECTORIES其完整语法为
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)这条指令可以用来向工程添加多个特定的头文件搜索路径路径之间用空格分割如果路径中包含了空格可以使用双引号将它括起来默认的行为是追加到当前的头文件搜索路径的后面可以通过两种方式来进行控制搜索路径添加的方式
CMAKE_INCLUDE_DIRECTORIES_BEFORE通过SET 这个cmake 变量为on可以将添加的头文件搜索路径放在已有路径的前面。 通过AFTER 或者BEFORE 参数也可以控制是追加还是置前。
现在我们在src/CMakeLists.txt 中添加一个头文件搜索路径方式很简单加入
INCLUDE_DIRECTORIES(/usr/include/hello)进入build 目录重新进行构建这时找不到 hello.h 的错误已经消失但是出现了一个新的错误
CMakeFiles/main.dir/main.o在函数‘main’中
main.c:(.text0x5)对‘hello_func’未定义的引用
collect2: error: ld returned 1 exit status
src/CMakeFiles/main.dir/build.make:96: recipe for target src/main failed
make[2]: *** [src/main] Error 1
CMakeFiles/Makefile2:97: recipe for target src/CMakeFiles/main.dir/all failed
make[1]: *** [src/CMakeFiles/main.dir/all] Error 2
Makefile:90: recipe for target all failed
make: *** [all] Error 2因为我们并没有link 到共享库libhello 上。
8.3、为 target 添加共享库
现在需要完成的任务是将目标文件链接到 libhello这里我们需要引入两个新的指令LINK_DIRECTORIES 和TARGET_LINK_LIBRARIES。
LINK_DIRECTORIES 的全部语法是
LINK_DIRECTORIES(directory1 directory2 ...)这个指令非常简单添加非标准的共享库搜索路径比如在工程内部同时存在共享库和可执行二进制在编译时就需要指定一下这些共享库的路径。这个例子中我们没有用到这个指令。
TARGET_LINK_LIBRARIES 的全部语法是:
TARGET_LINK_LIBRARIES(target library1 debug | optimized library2 ...)这个指令可以用来为target 添加需要链接的共享库本例中是一个可执行文件但是同样可以用于为自己编写的共享库添加共享库链接。
为了解决我们前面遇到的HelloFunc 未定义错误需要做的是向 src/CMakeLists.txt 中添加如下指令
TARGET_LINK_LIBRARIES(main hello)也可以写成
TARGET_LINK_LIBRARIES(main libhello.so)这里的hello指的是前面构建的共享库 libhello.XX。
进入build 目录重新进行构建。
cmake ..
make这时就得到了一个连接到libhello 的可执行程序main位于build/src目录运行 main 的结果是输出
$ ./main
Hello World来看一下main 的链接情况
$ ldd src/main linux-vdso.so.1 (0x00007ffd531fd000)libhello.so.1 /usr/lib/libhello.so.1 (0x00007ff212afe000)libc.so.6 /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff21270d000)/lib64/ld-linux-x86-64.so.2 (0x00007ff212d00000)可以清楚的看到main 确实链接了共享库libhello而且链接的是动态库 libhello.so.1。
九、使用外部静态库和头文件
那如何链接到静态库呢方法很简单
将 TARGET_LINK_LIBRRARIES 指令修改为:
TARGET_LINK_LIBRARIES(main libhello.a)重新构建后再来看一下main 的链接情况
$ ldd src/main linux-vdso.so.1 (0x00007ffc7a161000)libc.so.6 /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff1da80c000)/lib64/ld-linux-x86-64.so.2 (0x00007ff1dabfd000)说明main 确实链接到了静态库libhello.a。
十、特殊的环境变量
CMAKE_INCLUDE_PATH 和CMAKE_LIBRARY_PATH。 务必注意这两个是环境变量而不是cmake 变量。
使用方法是要在bash 中用export 或者在csh 中使用set 命令设置或者 CMAKE_INCLUDE_PATH/home/include cmake …等方式。
这两个变量主要是用来解决以前autotools 工程中 --extra-include-dir 等参数的支持的。也就是如果头文件没有存放在常规路径(/usr/include, /usr/local/include 等)则可以通过这些变量就行弥补。
以本例中的hello.h 为例它存放在/usr/include/hello 目录所以直接查找肯定是找不到的。
前面我们直接使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)告诉工程这个头文件目录。
为了将程序更智能一点我们可以使用 CMAKE_INCLUDE_PATH 来进行使用bash 的方法如下
export CMAKE_INCLUDE_PATH/usr/include/hello 然后在头文件中将INCLUDE_DIRECTORIES(/usr/include/hello)替换为
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)FIND_PATH 用来在指定路径中搜索文件名比如
FIND_PATH(myHeader NAMES hello.h PATHS /usr/include /usr/include/hello)这里我们没有指定路径但是cmake 仍然可以帮我们找到hello.h 存放的路径就是因为我们设置了环境变量CMAKE_INCLUDE_PATH。
如果不使用FIND_PATHCMAKE_INCLUDE_PATH 变量的设置是没有作用的不能指望它会直接为编译器命令添加参数-I CMAKE_INCLUDE_PATH 。
以此为例CMAKE_LIBRARY_PATH 可以用在 FIND_LIBRARY 中。
同样因为这些变量直接为FIND_指令所使用所以所有使用FIND_指令的cmake 模块都会受益。
总结
通过ADD_LIBRARY 指令构建动态库和静态库。通过SET_TARGET_PROPERTIES 同时构建同名的动态库和静态库。通过SET_TARGET_PROPERTIES 控制动态库版本。使用INSTALL 指令来安装头文件和动态、静态库。通过INCLUDE_DIRECTORIES 指令加入非标准的头文件搜索路径。通过LINK_DIRECTORIES 指令加入非标准的库文件搜索路径。通过TARGET_LINK_LIBRARIES 为库或可执行二进制加入库链接。CMake链接到静态库的方式。CMake还有很多高级的话题没有探讨比如编译条件检查、编译器定义、平台判断、如何跟 pkgconfig 配合使用等等。cmake 的使用过程其实就是学习cmake 语言并编写 cmake 程序的过程既然是“cmake 语言”自然涉及到变量、语法等。