一起学习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:[email protected] QQ:348774226)。

时间: 2024-07-30 16:12:01

一起学习CMake – 03的相关文章

Rhythmk 学习 Hibernate 03 - Hibernate 之 延时加载 以及 ID 生成策略

Hibernate 加载数据 有get,跟Load 1.懒加载: 使用session.load(type,id)获取对象,并不读取数据库,只有在使用返回对象值才正真去查询数据库. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test    public void  test1()    {        Session session = null;         try {             session = Hiber

BME学习总结03

BME学习总结03 //deployafterInfo.usl<bme:view> <bme:panel id="deployafterID" title="DeployMent Success Info"> <bme:datagrid id="deployafterinfoID" property="listDeploySuccess" viewmode="view">

SQL 学习日志03

添加表内容: insert into table_name (字段1,字段2,...) values (值1,值2,....) 查询表内容: select * from table_name select 字段1,字段2 from table_name select top 10 * from table_name select * from table_name where 字段=值 select * from table_name where 字段<>值 select * from tab

python学习笔记(03):函数

默认参数值:   只有在行参表末尾的哪些参数可以有默认参数值,即 def func(a, b=5 )#有效的 def func( a=5,b )#无效的 关键参数: #!/usr/bin/python # Filename: func_key.py def func(a, b=5, c=10): print 'a is', a, 'and b is', b, 'and c is', c func(3, 7) func(25, c=24) func(c=50, a=100) #输出: $ pyth

HTTP 学习笔记03

通用信息头 Cache-Control : no-cache(不缓存当前请求) [*] Connection:close(返回当前请求后立即断开)[*] Date:...(HTTP消息产生的时间) Pragma:no-cache (不缓存) [*] Trailer:Date(哪些能放到实体内容后的头字段) Transfer-Encoding:chunked (指定传输编码方式)[*] Upgrade:HTTP/2.0,SHTTP/1.3 (支持的版本) Via:HTTP/1.1 Proxy1,H

C++ GUI Qt4学习笔记03

C++ GUI Qt4学习笔记03 qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QMainWindow可以创建一个窗口 图形用户界面(GUI)应用程序通常会使用很多的图片,最常见的为应用程序提供图片的方法是使用Qt的资源机制(resource mechanism) 使用Qt资源系统,必须创建一个资源文件,并且在识别该资源文件的.pro文件中添加一行代码. RESOURCES = spr

mongodb 学习笔记 03 -- 查询表达式

mongodb 学习笔记 03 – 查询表达式 不等于,大于,小于 !=: db.stu.find({name:{$ne:'billvsme'}}) 名字不是'billvsme' > : db.stu.find({age:{$gt:18}}) 年纪大于18 < : db.stu.find({age:{$lt:18}}) 年纪小于18 >=: $gte <=: $lte in/not in/all $in :db.goods.find(stu_id:{$in:[93001,93002

Android自定义View学习笔记03

Android自定义View学习笔记03 预备知识 BitMap类 BitMap位图类,其中有一个嵌套类叫Bitmap.Config,内部有四个枚举值.这个类的作用是定义位图存储质量,即存储一个像素的位数,以及是否能显示透明.半透明颜色(Possible bitmap configurations. A bitmap configuration describes how pixels are stored. This affects the quality (color depth) as w

SWIFT学习笔记03

1.断言 let age = -3 assert(age >= 0, "A person's age cannot be less than zero") // 因为 age < 0,所以断言会触发 2.Swift赋值符(=)不返回值,以防止把想要判断相等运算符(==)的地方写成赋值符导致的错误.数值运算符(+,-,*,/,%等)会检测并不允许值溢出. 3.在对负数b求余时,b的符号会被忽略.这意味着 a % b 和 a % -b的结果是相同的,但-a是不一样的. 4.不同