CMake 学习笔记 03 - 构建静态和动态库
CMake 学习笔记 03 - 构建静态和动态库
源代码见 https://github.com/fengyc/cmake-tutorial
组织为静态和动态库
通常的项目都会划分模块,模块以库的形式进行链接(动态或静态),在 cmake 中进行这一构建操作也是比较简单。
还是原来的 helloworld 项目,现在从 helloworld3 拷贝到 helloworld4 。原来直接写在 main 中的输出部分,现在提取出一个 sayHello 函数供重复调用。
增加一个 lib 目录,里面增加 hello.h 和 hello.c ,内容分别为 ::
#ifndef HELLO_H
#define HELLO_H
void sayHello();
#endif
和 ::
#include <stdio.h>
#include "hello.h"
void sayHello()
{
printf("Hello, world!\n");
}
在 lib 中增加一个 CMakeLists.txt ::
SET(SRC_LIST hello.c)
ADD_LIBRARY(hello_shared SHARED ${SRC_LIST})
SET_TARGET_PROPERTIES(hello_shared PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_shared PROPERTIES VERSION 1.2 SOVERSION 1)
ADD_LIBRARY(hello_static STATIC ${SRC_LIST})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES VERSION 1.2 SOVERSION 1)
INSTALL(TARGETS hello_shared;hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include)
ADD_LIBRARY
指令指示生成一个库目标文件,可指定库的类型为 SHARED 或 STATIC 。
SET_TARGET_PROPERTIES
指令设置目标的属性,其中 CLEAN_DIRECT_OUTPUT
部分用于指示在生成具有相同名字的(OUTPUT_NAME
)的目标时,是否清理上次生成的内容。由于这罗的动态和静态库都使用 hello 这个名字,因此需要设置此不见标志。
这个配置文件同时生成了动态连接库和静态连接库,并把头文件安装到 include 目录。
引用动态库
修改 src 中的 main.c ::
#include <stdio.h>
#include "hello.h"
int main(int argc, char* argv[]){
sayHello();
return 0;
}
并在 src 中的 CMakeLists.txt 中,包含 lib 目录,以及让 hello 可执行文件链接到生成的库 ::
ADD_SUBDIRECTORY(lib)
INCLUDE_DIRECTORIES(lib)
ADD_EXECUTABLE(hello main.c)
TARGET_LINK_LIBRARIES(hello hello_shared)
INSTALL(TARGETS hello DESTINATION bin)
INCLUDE_DIRECTORIES
指定 include 时要包含的头文件搜索路径。
TARGET_LINK_LIBRARIES
指示链接到目标库。由于我们的库与源代码在同一个 cmake 构建系统中,可直接用目标名 hello_shared
或 hello_static
,否则应该用库文件名。链接时,如果链接库放在非系统特定的库目录下,那么需要通过 LINK_DIRECTORIES(路径)
指定库的搜索路径,或者直接用绝对路径。
如果无法在 CMakeLists.txt 中指定 INCLUDE_DIRECTORIES
和 LINK_DIRECTORIES
,还可以通过 shell 变量 CMAKE_INCLUDE_PATH
和 CMAKE_LIBRARY_PATH
,在 cmake 构建时传递给 cmake 。
然后,在 CMakeLists.txt 中通过 FIND_PATH
指令来判断是否找到相应的头文件路径,并包含进来 ::
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)
FIND_PATH
用法为 ::
FIND_PATH(变量名 NAMES 需要查找的文件名列表 PATHS 待查找的路径列表)
类似地,可用 CMAKE_LIBRARY_PATH
和 FIND_LIBRARY
判断库是否存在。
顶层目录中的 CMakeLists.txt 保持不变,用来构建其它文件。
同样,在 build 目录中,进行构建 ::
(mkdir -p build; cd build; cmake -DCMAKE_INSTALL_PREFIX=$PWD/usr; make install)
生成后,看 build 中的 usr ,有以下的目录树 ::
usr/
├── bin
│ ├── hello
│ └── runhello.sh
├── include
│ └── hello.h
├── lib
│ ├── libhello.a
│ ├── libhello.so -> libhello.so.1
│ ├── libhello.so.1 -> libhello.so.1.2
│ └── libhello.so.1.2
└── share
└── doc
└── HELLO
├── hello.txt
└── README
要注意,直接运行 ./usr/bin/hello
时,会报告无法找到 libhello.so.1
的错误(如果是链接到静态库 hello_static
则不会出现此错误) ::
/usr/bin/hello: error while loading shared libraries: libhello.so.1: cannot open shared object file: No such file or directory
这是由于系统默认的库搜索路径在 /etc/ld.so.config
中指定,可通过 LD_LIBRARY_PATH
变量暂时指定一个路径,即在 build 目录下 ::
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/usr/lib ./usr/bin/hello
就会正确地运行和输出。