一起学习CMake – 03
这一节我们就一起来看看如何用CMake来链接自己写的lib库,如何进行这些库文件的管理。
一个团队共同开发软件时,一般都是分模块进行作业的,每个人负责整个软件中的一部分,然后再整合成一个完整的软件系统。具体的做法一般是某个人开发的东西是以链接库的形式供团队中的其他人进行调用,或者供本人负责的程序的其他模块进行调用。
比如,A童鞋开发了一种算法,能做数A与数B的加法运算,A童鞋把它编译成lib库的形式给B童鞋调用,提供给B童鞋的就只有该加法运算的头文件(让B童鞋知道这个函数的接口是怎么样的)以及相应的函数实现lib库文件,B童鞋拿到了这两个文件以后,就可以在自己的程序里面直接调用A童鞋的加法运算。
当涉及到大型的软件开发项目时,这种链接库的形式普遍存在,文件少则几十上百,多则成千上万,这个时候就需要一个工具来对这些链接库进行管理,个人觉得CMake就是一个最好的选择。有用过VTK,ITK等开源工具包的童鞋应该都知道,VTK等编译完了以后会产生好多lib文件,比如vtkCommon.lib, vtkFiltering.lib, vtkGenericFiltering.lib, vtkGraphics.lib, vtkHybrid.lib, vtkImaging.lib, vtkIO.lib……等,而且好多刚学VTK的童鞋在编译链接VTK工程时会经常碰到类似“***无法解析的外部命令”,“***.h找不到”等等这样的错误。如果你是用CMake来构建工程的话,相信这些问题都是小菜一碟而已,所以了解一点CMake的知识对你使用那些开源工具包是灰常有帮助的。
不扯那么多了,进入主题吧。这一节我们就一起来看看如何用CMake来链接自己写的lib库,如何进行这些库文件的管理。
跟《一起学习CMake - 02》一样,在CMake-Study目录下再建一个空的文件夹,就叫HelloCMake03吧(在我机子上完整路径是:D:\CMake\CMake-Study\HelloCMake3),然后把HelloCMake02里的文件都copy到这个新建文件夹里去,等下我们就在这个基础上进行更改。接着在HelloCMake03目录下再建一个新的文件夹,等下里面会存放我们自己要实现lib的文件,简单起见,我们就做一个加法运算可以了,文件夹就起名:AddFunction。接着在AddFunction里新建两个文件,分别是AddFunction.h和AddFunction.cpp,什么作用应该不用再解释了。
AddFunction.h的代码如下:
//做整数的加法运算
int AddFunction(int x, int y);
AddFunction.cpp里的代码如下:
#include <iostream>
int AddFunction(int x, int y)
{
std::cout<<x<<" + "<<y<<" = "<<x+y<<std::endl;
return x + y;
}
然后在AddFunction文件夹里再建立一个CMakeLists.txt文件,CMake原则上要求每个文件夹里都要有一个名叫CMakeLists.txt的文件,因为我们等下是要把AddFunction.h和AddFunction.cpp这两个文件生成一个lib库文件,所以我们必须告诉CMake这个事情,而CMake是只认CMakeLists.txt文件的,所以这个目录里应该要有这个文件存在,里面的内容如下:
add_library(AddFunction AddFunction.cpp)
就这么一行代码。表示什么意思?add_library命令与add_executable命令(在第01节里有介绍)其实是差不多的,前者就是生成lib库文件,后者就是生成exe文件;它们所带的参数也都是一样的,就是用这个参数列表里的源文件来生成这些lib或exe文件。
这样就完成了自己的lib库文件创建的一些工程。接着回到AddFunction的外层目录里,里面有HelloCMake.cpp; HelloCMakeConfig.h.in; CMakeLists.txt这三个文件,先打开CMakeLists.txt文件吧,把里面的代码改为:
cmake_minimum_required(VERSION 2.6)
project(HelloCmake)
# 在CMake里设置HelloCMake软件的主版本号为1,次版本号为0。
set ( HelloCMake_VERSION_MAJOR 1 )
set ( HelloCMake_VERSION_MINOR 0 )
# 是否要使用我们自己的lib库里的加法函数。默认是使用。
option(USE_AddFunction "Use our Add Function" ON)
configure_file(
"${PROJECT_SOURCE_DIR}/HelloCMakeConfig.h.in"
"${PROJECT_BINARY_DIR}/HelloCMakeConfig.h"
)
Include_directories ("${PROJECT_BINARY_DIR}")
# 是否加载AddFunction库文件?
if (USE_AddFunction)
include_directories ("${PROJECT_SOURCE_DIR}/AddFunction")
add_subdirectory (AddFunction)
set (EXTRA_LIBS ${EXTRA_LIBS} AddFunction)
endif (USE_AddFunction)
add_executable(HelloCMake hellocmake.cpp)
target_link_libraries (HelloCMake ${EXTRA_LIBS})
红色字体标注的是新加的代码,一起来看看这些代码作用是什么。首先是option一行代码,option也是CMake里的命令,它的作用就是在CMake GUI上增加一个选项(如图(1)所示),具体到这个例子就是增加选项”USE_AddFunction”;第二个参数”User our Add Function”是标注信息,也就是当你的鼠标停留在CMake GUI的”USE_AddFunction”选项上是会有提示信息出现;第三个参数就是这个选项的值,默认是ON,也就是使用我们自己的加法库。如果更改了这些值,然后用CMake进行Configure, Generate时,这些选项的值会保存在你在”where to build the binaries”指定的编译目录里的CMakeCache.txt文件里。当你再次打开CMake时,CMake会自动去读取CMakeCache.txt文件里的各个选项的值。
(1)
再看看if/endif语句块,它的作用就是根据用户的选择(即USE_AddFunction的值)来决定是否要包含子目录AddFunction(include_directories/add_subdirectory两行代码)到头文件的搜索路径中去;以及设置变量EXTRA_LIBS的值为AddFunction.lib(set一行代码)。Set命令是CMake里用于设置变量值的一个命令,使用频率灰常高。还有,if/endif语句块必须要成对出现,if和endif后面所带的参数也必须一致。
target_link_libraries命令也是用得灰常多的一个命令,它的作用就是把${EXTRA_LIBS}这个变量里的库文件链接到HelloCMake这个工程里去。${……}是取某个变量的值的意思。
最外层的CMakeLists.txt内容介绍完,接着看看HelloCMakeConfig.h.in里要添加什么东西?在该文件的最后加入如下代码:
#cmakedefine USE_AddFunction
这行代码是告诉CMake在生成HelloCMakeConfig.h文件时用”#define USE_AddFunction”或者”/*#defineUSE_AddFunction*/”来代替” #cmakedefine USE_AddFunction”,到底是前者还是后者,取决于USE_AddFunction选项的值(ON还是OFF)。编译完HelloCMake这个工程以后,打开HelloCMakeConfig.h看看就知道怎么回事了。
接着来看看HelloCMake.cpp文件,完整代码如下:
#include <iostream>
#include "HelloCMakeConfig.h"
#ifdef USE_AddFunction
#include "AddFunction.h"
#endif
int main(int argc, char *argv[])
{
std::cout<<"HelloCMake软件的主版本号是:"
<< HelloCMake_VERSION_MAJOR << std::endl;
std::cout<<"HelloCMake软件的次版本号是:"
<< HelloCMake_VERSION_MINOR << std::endl;
fprintf(stdout, "%s Version is: %d.%d\n",
argv[0],
HelloCMake_VERSION_MAJOR,
HelloCMake_VERSION_MINOR);
std::cout<<"Study CMake Together - HelloCMake2"<<std::endl;
int a, b;
std::cin>>a>>b;
#ifdef USE_AddFunction
int addResult = AddFunction(a,b);
#else
int addResult = a + b;
#endif
return 0;
}
增加的代码都粗体字显示,这些代码都比较简单,一看就能明白了,这里就不多作介绍。有了这些文件以后,走一遍CMake(Configure, Generate),整个工程也就构建完成了。
我们来看看到底发生了哪些变量,有图有真相,看图吧:
(2)
(3)
(4)
知道了这些东西,以后你在使用VTK, ITK等工具包时,再碰到类似前文提到的错误时,也就知道怎么回事了吧?下一节我们结合VTK等工具包来看看怎么链接VTK里的库文件到自己的工程里去。
如果有不对的地方,请告诉我(水灵 MSN:shuiling119@hotmail.com QQ:348774226)。
一起学习CMake – 03的更多相关文章
- 一起学习CMake – 02
本节介绍如何用CMake来设置软件的版本号 在<一起学习CMake - 01>中我们看到了如何用CMakeLists.txt来构建一个最简单的工程,这一节里我们一起来看看如何用CMake对 ...
- 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试
机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...
- OpenCV 学习笔记03 边界框、最小矩形区域和最小闭圆的轮廓
本节代码使用的opencv-python 4.0.1,numpy 1.15.4 + mkl 使用图片为 Mjolnir_Round_Car_Magnet_300x300.jpg 代码如下: impor ...
- OpenCV 学习笔记03 findContours函数
opencv-python 4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...
- 一起学习CMake – 01
一起学习CMake – 01 本节介绍CMake里最常用的三个命令,分别是cmake_minimum_required; project; add_executable等. CMake是个好东西,在使 ...
- C++ GUI Qt4学习笔记03
C++ GUI Qt4学习笔记03 qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QM ...
- SaToken学习笔记-03
SaToken学习笔记-03 如果排版有问题,请点击:传送门 核心思想 所谓权限验证,验证的核心就是一个账号是否拥有一个权限码 有,就让你通过.没有?那么禁止访问! 再往底了说,就是每个账号都会拥有一 ...
- Redis:学习笔记-03
Redis:学习笔记-03 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 7. Redis配置文件 启动 ...
- OGG学习笔记03
OGG学习笔记03-单向复制简单故障处理 环境:参考:OGG学习笔记02-单向复制配置实例实验目的:了解OGG简单故障的基本处理思路. 1. 故障现象故障现象:启动OGG源端的extract进程,da ...
随机推荐
- 我的第一个REST客户端程序!
Delphi:XE8 看了好几天的资料了,也没有弄出来一个REST程序,尝试了XE8中带的例子,也都没有搞懂.我在网上不断搜索,看是否能够找到适合自己的文章,希望能够做出来一个REST的小例子,万幸, ...
- HDU 3466 Proud Merchants
题目大意:现在给出商品,有三个参数,记为pi,qi,vi,vi是商品的在你心里价值,pi是商品的价格,qi是你要买商品的时候至少需要的钱然后求可得的最大价值. 单词积累:Merchants 商人 t ...
- 你应当知道的Java牛人
Java领域有非常多著名的人物,他们为Java社区编写框架.产品.工具或撰写书籍改变了Java编程的方式. 本文是<最受欢迎的8位Java牛人>的2.0版本号. PS:排名不分先后.本文的 ...
- mysql待整理
1. MYSQL SQL_NO_CACHE的真正含义 http://www.dewen.org/q/5149/Mysql 是 结果不缓存,但查询还是缓存了. 如果要重新测试,就在查询前先执行一下&qu ...
- sql 和 nosql 说明
在传统的数据库中, 数据库的格式是由表(table).行(row).字段(field)组成的.表有固定的结构,规定了每行有哪些字段,在创建时被定义,之后修改很困难.行的格式是相同的,由若干个固定的字段 ...
- virtual 关键字
virtual 关键字用于修饰方法.属性.索引器或事件声明,并且允许在派生类中重写这些对象.例如,此方法可被任何继承它的类重写. public virtual double Area() { retu ...
- java int and string convert
int -> String int i=12345; String s=""; 第一种方法:s=i+""; 第二种方法:s=String.valueOf( ...
- Linux 网络编程: gethostbyname( ), getservbyname( )
前言 最近在学习网络编程,用到几个应该比较常用的网络编程函数,所以写篇博客来记录一下,毕竟学得快忘得也快.国庆节在宿舍写着博客看着各个景点人山人海倒也快哉~ gethostbyname( ) 这个函数 ...
- hdu 4779 Tower Defense 2013杭州现场赛
/** 题意: 有两种塔,重塔,轻塔.每种塔,能攻击他所在的一行和他所在的一列, 轻塔不 能被攻击,而重塔可以被至多一个塔攻击,也就是说重塔只能被重塔攻击.在一个n*m 的矩阵中,最少放一个塔,可放多 ...
- spring的定时执行代码 跑批
最近公司上线了抽奖的活动,活动需求 1:每天凌晨更新状态,实现自动开启和关闭活动 2:活动结束自动抽取中奖号码 在这里提供spring的定时调度功能 1:首先是配置文件 在你的web.xml中,查看配 ...