CMake基本使用
文章目录
1. Linux环境安装
-
安装gcc软件依赖
yum install -y gcc gcc-c++ make automake # 输出版本则安装成功 gcc -v
-
下载最新CMake
# 下载完成后解压 tar -xvf [压缩包]
-
进入根目录,运行命令
./bootstrap
如果出现以下错误:
运行命令:yum install -y openssl openssl-devel
然后重新运行即可
./bootstrap
-
运行gmake
gmake
-
进行安装
gmake install
输入
cmake -v
,如果出现版本号则安装成功:
2. 使用CMake运行C++程序
2.1 运行简单的示例程序
用vim编写一段C++代码:
#include <iostream>
using namespace std;
int main() {
cout << "hello world" << endl;
}
在同一个目录下编写CMakeLists.txt
文件:
PROJECT (HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir" ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE (hello ${SRC_LIST})
使用CMake生成Makefile
文件:
cmake .
自动生成了以下文件:
使用make命令编译:
make
编译出了一个可执行文件
运行可执行文件:
hello
3. CMake基本语法
用上面编写的CMakeLists.txt
文件做示例:
PROJECT (HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir" ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE (hello ${SRC_LIST})
3.1 关键字
PROJECT关键字
可以用来指定工程的名字和支持的语言,默认支持所有语言.
PROJECT (HELLO)
:指定了工程的名字,并且支持所有语言.
PROJECT (HELLO CXX)
:指定了 工程的名字,并且支持语言是C++.
PROJECT (HELLO C CXX)
:指定了工程的名字,并且支持语言是C和C++.
该指令隐式定义了两个CMAKE的变量
<projectname>_ BINARY_ DIR
,本例中是HELLO_ BINARY_ DIR
.
<projectname>_ SOURCE_ DIR
, 本例中是HELLO_ SOURCE DIR
.
MESSAGE
关键字就可以直接使用这两个变量,当前都指向当前的工作目录,后面会讲外部编译.
问题:如果改了工程名,这两个变量名也会改变.
解决:可以使用CMake为我们预定义的两个变量: PROJECT_ BINARY_ DIR
和PROJECT_SOURCE_ DIR
, 这两个变量和HELLO_ BINARY_ DIR
, HELLO_ SOURCE_ DIR
是一致的.
SET关键字
用于显示的指定变量.
SET(SRC_ LIST main.cpp)
指令中的SRC_ LIST
变量就包含了main.cpp
.
如果当前有多个cpp文件,也可以SET(SRC_LIST main.cpp t1.cpp t2.cpp)
.
MESSAGE关键字
向终端输出用户自定义的信息
主要包含三种信息:
SEND_ ERROR
,产生错误,生成过程被跳过.SATUS
, 输出前缀为-
的信息.FATAL_ ERROR
,立即终止所有cmake过程.
ADD_EXECUTABLE关键字
生成可执行文件
ADD_ EXECUTABLE(hello ${SRC_ LIST})
指令表示生成的可执行文件名是hello
,源文件读取变量SRC_ LIST
中的内容.
也可以直接写ADD_ EXECUTABLE(hello main.cpp)
.
所以工程名HELLO
与生成的可执行文件名hello
是没有任何关系的.
上述例子可以简化的写成:
PROJECT(HELLO)
ADD_EXECUTABLE(hello main.cpp)
3.2 语法的基本原则
- 变量使用
${}
方式取值,但是在IF控制语句中可以直接使用变量名. - 指令(参数1 参数2…) 参数使用括弧括起,参数之间使用空格或分号分开.以上面的
ADD_ EXECUTABLE
指令为例,如果存在另外一个func.cpp
源文件
就要写成:ADD_ EXECUTABLE(hello main.cpp func.cpp)
或者ADD_ EXECUTABLE(hello main.cpp;func.cpp)
. - 指令是大小写无关的,但是参数和变量是大小写相关的。推荐全部使用大写指令.
SET(SRC_ LIST main.cpp)
可以写成SET(SRC_ LIST "main.cpp")
, 但是如果源文件名中含有空格,就必须要加双引号.
3.3 内部构建和外部构建
- 内部构建:上述例子就是内部构建,生产的临时文件特别多,不方便清理.
- 外部构建:会把生成的临时文件放在
build
目录下,不会对源文件有任何影响,推荐使用外部构建的方式.
外部构建
先回退到刚刚编写完CMakeLists.txt
文件的时候:
创建一个
build
目录并进入:
mkdir build
cd build
在build
目录中执行cmake
命令:
cmake ..
在build
目录中执行make
命令:
make
生成的临时文件都会在build目录中:
4. 工程化项目
- 为工程添加一个子目录
src
,用来放置工程源代码. - 添加一个子目录
doc
,用来放置这个工程的文档hello.txt
. - 在工程目录添加文本文件
COPYRIGHT
README
. - 在工程目录添加一个
runhello.sh
脚本,用来调用hello
二进制. - 将构建后的目标文件放入构建目录的
bin
子目录. - 将
doc
目录的内容以及COPYRIGHT/README
安装到/usr/share/doc/cmake/
.
.
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│ └── hello.txt
├── README
├── runhello.sh
└── src
├── CMakeLists.txt
└── main.cpp
4.1 生成二进制文件到bin目录中
每个目录下都要有一个CMakeLists.txt
说明:
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
外层CMakeLIsts.txt
:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
src下的CMakeLists.txt
:
ADD_EXECUTABLE(hello main.cpp)
ADD_SUBDIRECTORY指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
- 这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置.
EXCLUDE_FROM_ALL
函数是将写的目录从编译中排除,如程序中的example.ADD_SUBDIRECTORY(src bin)
将src子目录加入工程并指定编译输出(包含编译中间结果)路径为bin目录
如果不进行bin目录的指定,那么编译结果(包括中间结果)都将存放在build/src目录.
更改二进制的保存路径
SET
指令重新定义EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_ PATH
变量来指定最终的目标二进制的位置:
SET(EXECUTABLE_OUTPUT_PATH ../../src/bin)
SET(LIBRARY_OUTPUT_PATH ../../src/lib)
注意:哪里要改变目标存放路径,就在哪里加入上述的定义,所以就在src
目录下的CMakeLists.txt
中写.
改变之后重新执行:
.
├── build
├── CMakeLists.txt
└── src
├── bin
│ └── hello
├── CMakeLists.txt
└── main.cpp
可以看到可执行文件hello
已经到了src/bin
目录下.
4.2 使用CMake进行安装
安装文件COPYRIGHT和README
在根目录下的CMakeLists.txt
文件中加上:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
FILES:文件名.
DESTINATION:
1.可以直接写绝对路径.
2.也可以写相对路径,相对路径实际路径是:${CMAKE_INSTALL _PREFIX}/<DESTINATION>
定义的路径.
CMAKE_JINSTALL_PREFIX
默认是在/usr/local/
.
cmake-DCMAKE_JNSTALL_PREFIX=/usr
在cmake的时候指定CMAKE_ JNSTALL_ PREFIX
变量的路径.
安装脚本runhello.sh
PROGRAMS
:非目标文件的可执行程序安装(比如脚本之类).
在根目录下的CMakeLists.txt
文件中加上:
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
说明:实际安装到的是/usr/bin
.
安装doc目录下的hello.txt
在根目录下的CMakeLists.txt
文件中加上:
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
DIRECTORY
后面连接的是所在Source
目录的相对路径.
注意: abc
和abc/
有很大的区别
- 目录名不以/结尾:这个目录将被安装为目标路径下的abc.
- 目录名以/结尾:将这个目录中的内容安装到目标路径.
安装
cd build
cmake ..
make
make install
5. 静态库与动态库的构建和使用
静态库和动态库的区别:
- 静态库的扩展名一般为".a”或".lib"; 动态库的扩展名- -般为".so"或".dIl".
- 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行.
- 动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行.
下面我们来创建一个静态库和动态库,提供HelloFunc函数供其它程序编程使用,HelloFunc向终端输出Hello World字符串.
构建实例:
.
├── build
├── CMakeLists.txt
└── lib
├── CMakeLists.txt
├── hello.cpp
└── hello.h
hello.h
中的内容:
#ifndef HELLO_H
#define Hello_H
void HelloFunc();
#endif
hello.cpp
中的内容:
#include "hello.h"
#include <iostream>
using namespace std;
void HelloFunc() {
cout << "hello world" << endl;
}
5.1 单独构建静态库或动态库
根目录下CMakeLists.txt
中的内容:
PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)
lib目录下CMakeLists.txt
中的内容:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
- hello: 就是正常的库名,生成的名字前面会加上lib,最终产生的文件是
libhello.so
. - SHARED:动态库;STATIC: 静态库.
${LIBHELLO_ SRC}
:源文件.
5.2 同时构建静态库和动态库
SET_TARGET_PROPERTIES
这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本.
同时构建静态和动态
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_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
编译之后:
5.3 动态库的版本号
一般动态库都有一个版本号的关联.
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2
CMakeList.txt
插入如下:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION
指代动态库版本,SOVERSION
指代API版本.
5.4 安装共享库和头文件
本例中我们将hello
的共享库安装到<prefix>/lib
目录,将hello.h
安装到<prefix>/include/hello
目录.
在lib目录下CMakeLists.txt
的添加:
# 文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)
# 二进制、静态库、动态库安装都用TARGETS
# ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
安装的时候指定一下路径,放到系统下:
cmake -D CMAKE_INSTALL_PREFIX=/usr ..
5.5 使用共享库和头文件
构建实例:
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── main.cpp
main.cpp中的内容:
#include <hello.h>
using namespace std;
int main() {
HelloFunc();
}
根目录下的CMakeLists.txt
:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
src下的CMakeLists.txt
:
ADD_EXECUTABLE(hello main.cpp)
然后编译:
会报找不到hello.h
头文件的错误:
需要在src下的
CMakeLists.txt
加上:
# 表示在hello目录下去找头文件
INCLUDE_DIRECTORIES(/usr/include/hello)
再编译,还会报错,找不到HelloFunc这个函数:
可以使用TARGET_LINK_LIBRARIES
关键字添加需要链接的共享库,注意一定要写在ADD_EXECUTABLE
关键字的后面:
TARGET_LINK_LIBRARIES (hello libhello.so)
注意hello是生成的二进制文件的名字.
所以最终src下的CMakeLists.txt
文件内容:
INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(hello main.cpp)
TARGET_LINK_LIBRARIES (hello libhello.so)
再次编译的时候,就可以成功生成可执行文件hello
了,但是运行hello的时候会再次报错:
原因是我们之前生成的动态库libhello.so
是在/usr/lib/
目录下的,但是我的linux系统是64位的,需要把这个动态库移动到/usr/lib64/
目录下才能使用:
mv /usr/lib/libhello.so /usr/lib64/
之后就可以正常运行了: