文章索引
cmake我刚开始在那里linux系统下管理c 一个在程序中最头疼的主人,必须学习自己的基本命令、变量和环境变量。我将在一到三章中介绍如何使用它cmake构建一个简单的库文件,不引用任何库文件cmake工程的具体流程,其输出结果在窗口打印helloworld,第四章将说明具体实现操作。第五章主要介绍如何创建动态库并安装在系统中,其中5.1.创建动态库的方法不常用,但原理是一样的,在5.3介绍了创建动态库的常用方法.5总结并附上本章所需的所有代码和指令。
目录:
- 文章索引
- 一、CMAKE版本声明
- 二、PROJECT
-
- 2.1用法:
- 2.2 生成的变量ProjectName_BINARY_DIR与ProjectName_SOURCE_DIR
- 2.3 在不同的编译模式下生成变量的路径
- 三、外部编译有两种形式
-
- 3.1当.cpp文件放在工程路径下src文件夹中
-
- 3.1.1 ADD_SUBDIRECTORY命令
- 3.1.2 EXECUTABLE_OUTPUT_PATH变量
- 3.1.3 LIBRARY_OUTPUT_PATH变量
- 3.1.4 ADD_EXECUTABLE命令
- 四、小结
- 五、创建自己的共享库(动态库)
-
- 5.1 在build/bin中生成.so文件(具体实现)
- 5.2 ADD_LIBRARY命令
- 5.3 另一种创建动态库的方法(常用)
-
- 5.3.1 INCLUDE_DIRECTORIES命令
- 5.4 共享库和头文件安装创建
-
- 5.4.1 INSTALL命令
- 5.5 小结
- 六、用好的libhello.so共享库
-
- 6.1 案例介绍:
- 6.2 TARGET_LINK_LIBRARIES命令
- 6.3 总结
一、CMAKE版本声明
通常使用如下语法对CMAKE限制最低版本
cmake_minimum_required(VERSION 2.8)
二、PROJECT
2.1用法:
本指令的一般形式不展开说明,,常见写法:
PROJECT(ProjectName)
其中ProjectName为工程名,可自定,一般与工程目录名相同,
2.2 生成的变量ProjectName_BINARY_DIR与ProjectName_SOURCE_DIR
使用PROJECT两个变量将在指令后产生,一个是ProjectName_BINARY_DIR(二进制文件路径)ProjectName_SOURCE_DIR(源文件路径) 注意:ProjectName根据自己的工程名称,是当前的工程名称PROJECT()确定名称
2.3 在不同的编译模式下生成变量的路径
(即不另建build文件夹直接用于源文件夹cmake),ProjectName_BINARY_DIR和ProjectName_SOURCE_DIR同样的路径内容是当前项目所在的文件夹路径 可利用MESSAGE指令来验证
MESSAGE(STATUS "This is BINARY dir " ${
HELLO_BINARY_DIR}) MESSAGE(STATUS "This is SOURCE dir "${
HELLO_SOURCE_DIR})
注:其中之一${}符号表示取{}中的值。 (即建立build文件夹,在build编译文件夹) ProjectName_BINARY_DIR中内容是build文件夹路径,ProjectName_SOURCE_DIR内容仍然是当前项目所在的文件夹路径,也可以使用MESSAGE验证指令。
三、外部编译有两种形式
刚才提到外部编译,通常有两种形式,一种是将军.cpp存储在项目中的新文件夹src另一种是直接放在工程目录下。
3.1当.cpp文件放在工程路径下src文件夹中
类似于下图,在工程中t创建文件夹src并将文件夹.cpp文件放入src文件夹中 需要分别在工程文件夹和工程文件夹中src创建文件夹CMakeLists.txt文件。 在工程文件夹中CMakeLists.txt中使用ADD_SUBDIRECTORY指令,然后在src的CMakeLists.txt中写ADD_EXECUTABLE等待其他操作。 上图中有一些指令没有介绍,比如ADD_EXECUTABLE、SET、INSTALL、ADD_SUBDIRECTORY等待,将在下面逐一解释。
3.1.1 ADD_SUBDIRECTORY命令
该命令的一般格式为:
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
是将source_dir内的cpp文件添加入项目工程中。 其中表示源文件目录,刚刚使用过的此命令中的src对应的就是source_dir,为用户指定在编译此cpp文件过程中生的二进制中间文件所处的目录(此项可写可不写),,则编译过程中二进制中间文件会生成在build目录下的src文件夹下(此文件夹是编译过程自动生成的),,则中间文件会生成在build/binary_dir/文件夹中(相当于将src重命名)。
3.1.2 EXECUTABLE_OUTPUT_PATH变量
此变量表示由.cpp文件经过make后生成的可执行文件输出路径,常和SET命令搭配使用,在ADD_EXECUTABLE命令后面进行SET,?在哪加了ADD_EXECUTABEL命令就在那SET。
3.1.3 LIBRARY_OUTPUT_PATH变量
此变量表示.cpp文件中使用到的库,静态库或者动态库的可执行二进制文件输出路径,若工程过于简单,没有include编写的库文件,则可不SET。
3.1.4 ADD_EXECUTABLE命令
:由.cpp文件生成可执行文件,生成的路径由EXECUTABLE_OUTPUT_PATH决定,如果没有对此变量进行SET,如果使用的是外部编译,默认生成在build文件夹下。 :
ADD_EXECUTABLE(ExeName source.cpp)
其中ExeName为生成的二进制可执行文件的名称,可以用户自定义,后面的source.cpp是.cpp文件,由于写ADD_EXECUTABLE的CMakeLists.txt通常是写在存.cpp文件夹中,所以路径直接写source.cpp即可。
四、小结
学会上面一些基础操作即可上手写一个像样的(不包含任何第三方头文件)的C++工程了,例如要求如下: 1.建立一个helloworld工程,为工程添加一个子目录src,用来放置工程源代码; 2.添加一个子目录doc,用来放置这个工程的文档hello.txt 3.在工程目录添加文本文件COPYRIGHT, README 4.在工程目录添加一个runhello.sh脚本,用来调用hello二进制; 5.将构建后的目标文件放入构建目录的bin子目录;
按照要求建立后,文件目录框架如下: 工程文件的CMakeLists.txt如下:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
src中的CMakeLists.txt如下:
SET(EXECUTABLE_OUTPUT_PATH ${
PROJECT_BINARY_DIR}/bin)
main.c内容如下:
#include<stdio.h>
int main()
{
printf("helloSLAM\n");
return 0;
}
脚本.sh文件内容如下:
mkdir build
cd build
cmake ..
make
cd bin
./hello
注意:要为脚本文件添加可执行权限才可运行
五、创建属于自己的共享库(动态库)
由于一个大型的cmake程序需要依赖各种各样的库,所以会添加别人写好的动态库尤为重要,在学习使用别人写好的动态库之前,我们需要先学会如何自己写一个动态库,并将其安装在系统中,这样才可以让别的程序去使用。主要分为两步,首先创建共享库并生成在build/bin中,然后安装在系统中
5.1 创建共享库,在build/bin中生成.so文件(具体实现)
注意:具体过程中的解释放在后面说明 整体思路为:创建一个lib文件夹,其中包括.cpp文件和.h文件,一个写实现一个写声明,分别在工程文件夹下和lib文件夹下创建CMakeLists.txt文件,大体文件树如下: 其中hello.c内容为:
#include"hello.h"
void HelloFunc()
{
printf("HELLOSLAM\n");
}
其中hello.h内容为:
#ifndef HELLO_H
#define HELLO_H
#include<stdio.h>
void HelloFunc();
#endif
工程文件夹下的CMakeLists.txt文件内容为:
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
Lib文件夹下的CMakeLists.txt文件内容为:
ADD_LIBRARY(hello SHARED
hello.c
hello.h)
通过在build文件夹cmake …并且make后,会在build/bin文件夹下建立libhello.so共享库
5.2 ADD_LIBRARY命令
除了ADD_LIBARARY命令外,其他都是基本的C语言基础,还有我们上文提到过的内容,这里就不再过多介绍了。
ADD_LIBRARY(libname [SHARED|STATIC|MODULE][EXCLUDE_FROM_ALL]
source1
source2
...
sourceN)
其中libname是指定生成库的名字,在5.1中,我们指定叫hello,则系统自动会帮我们生成libhello.X的库在build/bin中,其中的X会根据库的类型不同而不同,这就是我们要说的第二个参数。 SHARED
,动态库(扩展名为.so) STATIC
,静态库(扩展名为.a) MODULE
,在使用dyld的系统有效,如果不支持dyld,则被当作SHARED对待。 EXCLUDE_FROM_ALL
,参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手工构建。 在5.1中我们使用的是SHARED,构建的是libhello.so的共享库 最后就是我们指定需要添加的库函数源文件,也就是实现库功能的.cpp文件。 注意:第三个参数中,.h文件可写可不写,写上是为了让读者更清楚.cpp文件对应的.h文件,在5.1的例子中写上了,读者可自己验证不写会怎样
5.3 创建动态库的另一种方法(常用)
我们刚刚已经完整的演示了如何创建属于自己的共享库.so文件,这种把.h和.cpp同时放在工程下的Lib文件夹方式不常用,下面介绍一种常用的方式(此方式原理和5.1相同,只是以一种更直观的目录数的形式展示): 工程的目录树如下: 把头文件放到了include文件夹中,实现文件放到了src文件夹中,并只用在主工程下写一个CMakeLists.txt即可,比5.1介绍的方法更直观,并且可以少写一个CMakeLists.txt。 注意:各文件内容和5.1相同,只有CMakeLists.txt内容不同,故下面只展示CMakeLists.txt内容 CMakeLists.txt内容如下:
PROJECT(HELLOLIB)
INCLUDE_DIRECTORIES(include)
ADD_LIBRARY(hello SHARED
src/hello.c
include/hello.h)
里面有了一个新的命令INCLUDE_DIRECTORIES,将会在5.3.1中进行介绍,之所以会有此命令,是因为hello.c文件include了hello.h头文件,如果不加此命令,在make的过程中会报找不到hello.h文件的错误,这是因为.c文件和.h没有放在一个文件夹下(不像5.1节中把.c和.h文件放在lib文件夹中) 具体实现结果请读者自己实现。 至此,我们已经成功在build/bin中创建了我们自己写的动态库(共享库),接下来我们需要将动态库和库的头文件安装到系统中,这样别人才能去调用。
5.3.1 INCLUDE_DIRECTORIES命令
include_directories ([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
将指定目录添加到编译器的头文件搜索路径之下,dir是相对路径,前缀是当前工作空间文件夹,在5.3中前缀为~/cmake/t3/。 第一个参数:默认情况下,include_directories命令会将目录添加到列表最后,也可以在每次调用include_directories命令时使用AFTER或BEFORE选项来指定是添加到列表的前面或者后面。 通常第一个和第二个参数不设置,只写需要包含头文件的文件夹相对路径即可。
5.4 安装创建的共享库和头文件
我们只需要修改一下5.3的工程文件夹下的CMakeLists.txt内容即可: 添加了两条INSTALL命令,一个是安装动态库,一个是安装头文件。
PROJECT(HELLOLIB)
INCLUDE_DIRECTORIES(include)
ADD_LIBRARY(hello SHARED
src/hello.c
include/hello.h)
INSTALL(TARGETS hello
LIBRARY DESTINATION lib)
INSTALL(FILES include/hello.h DESTINATION include/hello)
在Build文件夹中输入的命令格式相应做出一点改变
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
sudo make install
在cmake的同时指定了CMKAE_INSTALL_PREFIX这个环境变量的值,结果是我们将hello的共享库安装到"prefix"/lib目录,将hello.h安装到"prefix"/include/hello目录。其中CMKAE_INSTALL_PREFIX环境变量指定了"prefix"的路径,
5.4.1 INSTALL命令
在上文中,我们利用INSTALL将动态库和库所需要的头文件安装在了系统中,下面将介绍这两条命令
INSTALL(TARGETS targets
[[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION <dir>]
)
参数中的TARGETS后面跟的就是我们通过ADD_EXECUTABLE或者ADD_LIBRARY定义的目标文件,可能是可执行二进制、动态库、静态库。目标类型也就相对应的有三种,RUNTIME特指可执行目标二进制,ARCHIVE特指静态库,LIBRARY特指动态库。
DESTINATION定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX其实就无效了。如果你希望使用CMAKE_INSTALL_PREFIX来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>
INSTALL(FILES
files DESTINATION <dir>
[PERMISSIONS permissions...])
可用于安装一般文件,并可以指定访问权限。 第一个参数files表示文件的路径,这里是相对路径, 第三个参数是“dir”,也就是需要指定安装后的路径,也是此指令所在路径下的相对路径即${CMAKE_INSTALL_PREFIX}/<DESTINATION定义的路径>。如果默认不定义权限PERMISSIONS,安装后的权限为:OWNER_WRITE, OWNER_READ, GROUP_READ,和WORLD_READ,即644权限。
5.5 小结
至此我们已经可以独立创建属于自己的共享库,并且可以将其安装在系统中供他人使用,本章所用到的完整代码如下:
文件目录树: hello.h头文件:
#ifndef HELLO_H
#define HELLO_H
#include<stdio.h>
void HelloFunc();
#endif
hello.c实现文件:
#include"hello.h"
void HelloFunc()
{
printf("HELLOSLAM\n");
}
工程文件夹下的CMakeLists.txt:
PROJECT(HELLOLIB)
INCLUDE_DIRECTORIES(include)
ADD_LIBRARY(hello SHARED
src/hello.c
include/hello.h)
INSTALL(TARGETS hello
LIBRARY DESTINATION lib)
INSTALL(FILES include/hello.h DESTINATION include/hello)
在build文件夹下输入的指令:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
sudo make install
将生成的libhello.so文件安装在了/usr/lib中,将hello.h文件安装在了/usr/include/hello文件夹中
六、使用建好的libhello.so共享库
6.1 案例介绍:
使用建立好的共享库步骤有两个,首先用INCLUDE_DIRECTORIES指令添加头文件,然后用TARGET_LINK_LIBRARIES指令将可执行文件和共享库链接在一起。 注意:两个步骤缺一不可 首先先展示我们案例的文件夹目录: 其中main.c内容为:
#include<hello.h>
int main()
{
HelloFunc();
return 0;
}
工程文件夹下的CMakeLists.txt:
PROJECT(NEWHELLO)
INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(main main.c)
TARGET_LINK_LIBRARIES(main hello)
本案例完成的功能是main函数去调用动态库libhello.so中的HelloFuc()函数完成相应的功能。 其中INCLUDE_DIRECTORIES命令熟悉吧,这是我们的老朋友了,是用来为程序添加头文件搜索路径用的,我们在这里就不多介绍了,下面将介绍TARGET_LINK_LIBRARIES命令的使用。
6.2 TARGET_LINK_LIBRARIES命令
这个指令可以用来为target添加需要链接的共享库,本例中是一个可执行文件,
TARGET_LINK_LIBRARIES(target
library1
library2
...)
本例中target为ADD_EXECUTABLE命名的二进制target名称,library为共享库名,注意:不用写libhello.so全程,只需要写hello即可(名称和ADD_LIBRARIES相同),有些时候需要写他的绝对路径!
6.3 总结
至此我们已经将所以CMAKE基础用法讲解完毕,复杂的工程用到的基础也是这点东西,学完这些可以看懂一个CMAKE工程都输出了那些二进制可执行文件,这些文件都依赖了那些库。他们的头文件在哪可执行文件在哪。