引言

CMake 实践帮助我们对 CMake 有一个系统全面的了解,并且有大量示例以供参考,至少在实际项目中可以让我们有能力看懂并修改项目中现有的 CMake 。

阅读完 CMake 实践文档,认为自己的任务也就结束了,可这样总感觉不是自己的东西,不如整理一下并吸收其中自己认为最有用的东西,这样也能极大的减轻自己的记忆负担。

与此同时 CMake 实践行文组织过于复杂,不方便遇到问题时快速查阅,所以我做了一些调整与总结,希望能够对读者更加友好。

本文不能代替 CMake 实践文档,有时间还是把 CMake 实践文档认真阅读一遍。

CMake 生成库

假设我们存在一个这样的任务:

  1. 建立一个静态库和动态库,提供 HelloFunc 函数以供其他程序编程使用,HelloFunc 向终端输出 Hello World 字符串。
  2. 安装头文件与共享库。

静态库和动态库的区别

  • 静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”。
  • 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行(如果程序编译成功,即使离开静态库,程序也是可以独立运行)。
  • 动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行(如果程序编译成功,必须要有动态库的存在程序才可以运行,比如使用windows运行一些游戏程序时,会报缺少 .dll 文件的错误,导致程序无法正常运行,其实就是缺少动态库)。

CMake 生成库简单实例

按照惯例,我们先来一个简单地实例,以便对 CMake 生成库有一个直观的了解。

  1. 创建以下工程结构

    yxm@192:~/test3$ tree
    .
    ├── build
    ├── CMakeLists.txt
    └── lib├── CMakeLists.txt├── hello.cpp└── hello.h2 directories, 4 files
    
  2. hello.h中的内容

    #ifndef HELLO_H
    #define Hello_Hvoid HelloFunc();#endif
    
  3. hello.cpp中的内容

    #include <iostream>
    #include "hello.h"void HelloFunc(){std::cout << "Hello World" << std::endl;
    }
    
  4. 项目中的cmake内容

    PROJECT(HELLO)
    ADD_SUBDIRECTORY(lib bin)
    
  5. lib中CMakeLists.txt中的内容

    SET(LIBHELLO_SRC hello.cpp)
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    

    ADD_LIBRARY 指令详细可见下文 CMake 语法。

  6. 外部编译过程:

    1. 进入 build,运行 cmake …

    2. 在 build 目录下,运行 make 命令编译 Makefile 文件,并生成动态库。

CMake 同时构建静态库与动态库

生成动态库与静态库

有上面的例子可以看出,使用 ADD_LIBRARY 指令就可以同时构建静态和动态库:

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

但是如果使用这种方式,只会构建一个动态库,不会构建出静态库,虽然静态库的后缀是.a,此时我们可以修改静态库的名字,这样是可以同时构建动态库和静态库:

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

但是我们往往希望他们的名字是相同的,只是后缀不同而已,此时可以使用 SET_TARGET_PROPERTIES 指令(该指令详细可见下文 CMake 语法),修改lib目录下CMakeLists.txt文件:

SET(LIBHELLO_SRC hello.cpp)ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})# 对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")
# cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,如果没有清理还是会只会构建一个动态库,不会构建出静态库
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})# 对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

外部编译过程:

  1. 进入 build,运行 cmake …

  2. 在 build 目录下,运行 make 命令编译 Makefile 文件,并生成动态库与静态库。

修改动态库版本号

同时我们还可以修改动态库的版本号

// 一般动态库都有一个版本号的关联
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2

修改 lib/CMakeLists.txt ,重新构建看看结果:

SET(LIBHELLO_SRC hello.cpp)ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

安装共享库和头文件

本例中我们将 hello 的共享库安装到 <prefix>/lib 目录;将 hello.h 安装到 <prefix>/include/hello 目录,这样共享库才能够被调用。

修改lib目录下CMakeLists.txt文件:

SET(LIBHELLO_SRC hello.cpp)ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)# 文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)# 二进制,静态库,动态库安装都用TARGETS
# ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

外部编译过程:

  1. 进入 build,运行 cmake -DCMAKE_INSTALL_PREFIX=/usr …

    注意:安装的时候,指定一下路径,放到系统下,-D之后加不加空格都可。

    注意:直接安装在 usr 系统目录下,以便后续可以直接调用

  2. 在 build 目录下,运行 make 命令编译 Makefile 文件,并生成动态库与静态库。

  3. 在 build 目录下,运行 make install

使用外部动态库和头文件

  1. 新建一个目录来使用外部共享库和头文件,创建以下工程结构:

    yxm@192:~/test4$ tree
    .
    ├── build
    ├── CMakeLists.txt
    └── src├── CMakeLists.txt└── main.cpp2 directories, 3 files
    
  2. main.cpp中的内容

    #include <hello.h>int main(){HelloFunc();
    }
    
  3. 项目中的CMakeLists.txt内容

    PROJECT(HELLO)
    ADD_SUBDIRECTORY(src bin)
    
  4. src中CMakeLists.txt中的内容

    ADD_EXECUTABLE(hello main.cpp)
    
  5. 外部编译过程:

    1. 进入 build,运行 cmake …

    2. 在 build 目录下,运行 make 命令编译 Makefile 文件。注意此时 make 会报错:

解决:make 后头文件找不到的问题

make 时会提示找不到头文件,两种解决方法:

  1. 修改成 include <hello/hello.h> ,但这样修改代码冗余。
  2. 当然也可以使用一下关键字:INCLUDE_DIRECTORIES (详细见下文 CMake 语法)

这里使用第二种方法,在src下的 CMakeLists.txt 文件中加入头文件搜索路径:

INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(hello main.cpp)

外部编译过程:

  1. 进入 build,运行 cmake …

  2. 在 build 目录下,运行 make 命令编译 Makefile 文件。注意此时还是 make 会报错:

解决:找到引用的函数问题

报错信息:undefined reference to `HelloFunc()',所以我们需要将 .so 文件关联起来。

解决方法有两种:

  • 关键字:LINK_DIRECTORIES 添加非标准的共享库搜索路径

    指定第三方库所在路径,LINK_DIRECTORIES(/home/myproject/libs)

  • 关键字:TARGET_LINK_LIBRARIES 添加需要链接的共享库(详细见下文 CMake 语法)

​ TARGET_LINK_LIBRARIES 的时候,只需要给出动态链接库的名字就行了。

这里使用第二种方法,在src下的 CMakeLists.txt 文件中添加需要链接的共享库(主要要插在executable的后面):

INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(hello main.cpp)
TARGET_LINK_LIBRARIES(hello libhello.so)

外部编译过程:

  1. 进入 build,运行 cmake …

  2. 在 build 目录下,运行 make 命令编译 Makefile 文件。

  3. 在 build/bin 目录下,运行 ./hello ,执行结果如下:

    yxm@192:~/test4/build$ cd bin/
    yxm@192:~/test4/build/bin$ ./hello
    Hello World
    
  4. 注意:如果你的 linux 虚拟机是64位会报错,需要移动动态库到64位下:

使用外部静态库

上面的例子使用的是外部动态库,如果想要使用外部静态库,步骤也是相同的,只需要将上面例子中.so换成.a即可,不过使用外部静态库不需要头文件。

TARGET_LINK_LIBRARIES(main libhello.a)

补充:

特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

注意:这两个是环境变量而不是 cmake 变量,可以在linux的bash中进行设置

我们上面例子中使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)来指明include路径的位置,我们还可以使用另外一种方式,使用环境变量export CMAKE_INCLUDE_PATH=/usr/include/hello

CMake 语法

(1)ADD_LIBRARY 语法

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

  • hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
  • SHARED,动态库 STATIC,静态库
  • ${LIBHELLO_SRC} :源文件

(2)SET_TARGET_PROPERTIES 语法

这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本

  • 对hello_static的重名为hello,例如:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME “hello”)

  • 指定动态库版本和 API 版本,例如:SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

    其中VERSION 指代动态库版本,SOVERSION 指代 API 版本。

(3)INCLUDE_DIRECTORIES 语法

找头⽂件:可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割

(4)TARGET_LINK_LIBRARIES 语法

TARGET_LINK_LIBRARIES 用于从链接库到执⾏⽂件上

CMake 常用总结二:CMake 生成静态库与动态库相关推荐

  1. cmake:add_library生成静态库和动态库

    此文为:轻松入门cmake系列教程 有时我们只需要编译出动态库,静态库,然后等着让其它程序去使用.让我们看下这种情况该如何使用cmake 实验 实验一:生成静态库 编写代码 项目结构如下: [CMak ...

  2. CMake教程(二)- 添加静态库文件和动态库文件

    CMake教程(二)- 添加静态库文件和动态库文件 什么是库文件 静态链接库 动态链接库 静态库和动态库的区别 如何在CMake中添加库文件 CMake 中 target_link_libraries ...

  3. HelloWorld CMake Demo 03:CMake中构建静态库与动态库及其使用

    继续完善Hello World,建立它的共享库,包括静态库和动态库. 本节的任务: 1,建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc向终端输出Hello W ...

  4. CMake I 编译静态库、动态库和对象库

    目录 一.源文件 1.Message.h 2.Message.cpp 3.helloworld.cpp 二.CMakeLists.txt 1.源文件 2.CMake语言说明 (1)cmake_mini ...

  5. android jni通过cmake使用第三方静态库和动态库

    google 官方现在推荐使用cmake来构建jni. 本人正好工作需要使用第三方的静态库和动态库,写此文 权当做个记录. 首先修改app的build.gradle文件 ndk {abiFilter ...

  6. 【Cmake实战:番外】库、动态库和静态库(.dll,.so,.lib,.a)

    [Cmake实战:番外]库.动态库和静态库(.dll,.so,.lib,.a) 一.什么是库 二.库的种类 三.命名方式 四.动态库和静态库的特点 五.如何知道一个可执行程序依赖哪些库 六.linux ...

  7. Linux下GCC生成和使用静态库和动态库详解(二)

    2.1准备好测试代码hello.h.hello.c和main.c: hello.h(见程序1)为该函数库的头文件. hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏 ...

  8. 用gcc生成静态库和动态库和使用opencv库编写打开摄像头压缩视频

    文章目录 一.用gcc生成静态库和动态库 1.编辑生成程序hello.h.hello.c.main.c 2.将hello.c生成.o文件 3.使用静态库 4.动态库的使用 二.a与.so库文件的生成与 ...

  9. GCC生成静态库和动态库

    目录 1)阅读.理解和学习材料"用gcc生成静态库和动态库.pdf"和"静态库.a与.so库文件的生成与使用.pdf",请在Linux系统(Ubuntu)下如实 ...

最新文章

  1. 吴恩达机器学习入门 2018 高清视频公开,还有习题解答和课程拓展,网友:找不到理由不学!...
  2. 审稿人眼中的好论文到底长什么样?
  3. 牛客第七场 Sudoku Subrectangles
  4. 字典删除多个键值对方法_Life is short,you need Python——Python序列(元组、字典、集合)...
  5. dataGridView 行头那一块儿空白是否可见的设置
  6. linux备份mysql怎样操作,Linux下自动备份MySQL数据库详细操作步骤(转载)
  7. 聊聊微服务架构及分布式事务解决方案!
  8. 【Java系列】八大排序算法
  9. 跑得快,打不死!清华大学开发“小强”机器人,壮汉狂踩也挡不住前进步伐
  10. Android前台服务讲解一
  11. MIMO系列之分集与复用
  12. 在小百合注册了一个帐号
  13. Office Visio 2007 中文版 安装
  14. pip install 使用豆瓣源
  15. matlab最大回撤值,用matlab计算区间最大回撤值和最大回撤率
  16. 自定义小程序中的showToast
  17. 一定要讲给孩子们的20个小故事
  18. 【计算机毕业设计】基于微信小程序的高校课堂考勤签到系统
  19. 地质灾害防治网格化管理平台
  20. 数据结构与算法-普利姆算法(Prim) | 尚硅谷韩顺平

热门文章

  1. css制作3D立体旋转效果
  2. mc1.8.1怎么局域网java_我的世界Minecraft局域网联机方法 这几步你要了解
  3. CISCO X8系列AP升级详解
  4. 计算机二级自学考试,关于全国计算机等级考试(NCRE)与高等教育自学考试课程衔接的通知...
  5. 苟日新,日日新,又日新
  6. Converter/MultiBinding示例
  7. CSS-三栏布局新手上路
  8. 为什么越来越多的网站域名不加www前缀?
  9. 谷歌手机pixel4 夜景_您应该购买Google Pixel 4a的5个理由
  10. win10局域网中只能发现部分计算机,Windows10系统局域网中共享计算机找不到怎么办...