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_sharedhello_static,否则应该用库文件名。链接时,如果链接库放在非系统特定的库目录下,那么需要通过 LINK_DIRECTORIES(路径) 指定库的搜索路径,或者直接用绝对路径。

如果无法在 CMakeLists.txt 中指定 INCLUDE_DIRECTORIESLINK_DIRECTORIES ,还可以通过 shell 变量 CMAKE_INCLUDE_PATHCMAKE_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_PATHFIND_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

就会正确地运行和输出。

posted @ 2017-01-20 16:52  drop *  阅读(1197)  评论(0编辑  收藏  举报