1.cmake介绍
cmake是一种高级编译配置工具,当多个人用不同的语言或者编译器开发一个项目,最终要输出一个可执行文件或者共享库(dll,so等等)时,Cmake就可以帮你做到,所有操作都是通过编译CMakeLists.txt来完成的。
2. Cmake使用
2.1 CMake构建一个HelloWord-内部构建
1、新建main.cpp
#include <iostream>
int main(){
std::cout<<"hello world!"<<std::endl;
}
2、新建CMakeLists.txt,内部构建两个变量HELLO_SOURCE_DIR和HELLO_BINARY_DIR都是工程路径
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、新建完毕目录结构如下
root@8d3:/work/cmake01# tree
.
├── CMakeLists.txt
└── main.cpp
0 directories, 2 files
4、执行cmake .,生成makefile文和CMakeFiles、CMakeCache.txt、 cmake_install.cmake 等文件,我们只需要关注Makefile文件。
root@8d3:/work/cmake01# cmake .
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is BINARY dir/work/cmake01
-- This is SOURCE dir/work/cmake01
-- Configuring done
-- Generating done
-- Build files have been written to: /work/cmake01
root@8d3:/work/cmake01# ls -l
total 40
-rw-r--r-- 1 root root 13738 Aug 24 16:48 CMakeCache.txt
drwxr-xr-x 5 root root 4096 Aug 24 16:48 CMakeFiles
-rw-r--r-- 1 root root 186 Aug 24 09:08 CMakeLists.txt
-rw-r--r-- 1 root root 5068 Aug 24 16:48 Makefile
-rw-r--r-- 1 root root 1610 Aug 24 16:48 cmake_install.cmake
-rw-r--r-- 1 root root 74 Aug 24 09:03 main.cpp
5、使用make命令编译,生成了hello的可执行程序,运行./hello,打印hello world!
root@8d3:/work/cmake01# make
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello
root@8d3:/work/cmake01# ls
CMakeCache.txt CMakeFiles CMakeLists.txt Makefile cmake_install.cmake hello main.cpp
root@8d3:/work/cmake01# ./hello
hello world!
2.2 CMake一个HelloWord-外部构建
内部构建生产的临时文件特别多,不方便清理,外部构建,就会把生成的临时文件放在build目录下,不会对源文件有任何影响
1、目录结构如下,其中build为新建目录,main.cpp与CMakeLists.txt文件内容与1相同
root@8d3:/work/cmake02# tree
.
├── CMakeLists.txt
├── build
└── main.cpp
1 directory, 2 files
2、进入build,运行cmake .. ,..表示上一级目录,你也可以写CMakeLists.txt所在的绝对路径,生产的文件都在build目录下了
root@8d3:/work/cmake02/build# cmake ..
root@8d3:/work/cmake02/build# ls
CMakeCache.txt CMakeFiles Makefile cmake_install.cmake
3、外部构建变量HELLO_SOURCE_DIR是工程路径,HELLO_BINARY_DIR是编译路径,即build的路径
4、在build目录下,使用make命令编译,生成了hello的可执行程序,运行./hello,打印hello world
root@8d3:/work/cmake02/build# make
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello
root@8d3:/work/cmake02/build# ls
CMakeCache.txt CMakeFiles Makefile cmake_install.cmake hello
root@8d3:/work/cmake02/build# ./hello
hello world!
2.3 安装HelloWord
1、目录结构如下
root@8d3:/work/cmake03# tree
.
├── CMakeLists.txt
├── COPYRIGHT
├── README
├── build
├── doc
│ └── hello.txt
├── runhello.sh
└── src
├── CMakeLists.txt
└── main.cpp
2、主路径CMakeLists.txt内容
PROJECT(HELLO)
//将构建后的目标文件放入构建目录的 bin 子目录
ADD_SUBDIRECTORY(src bin)
/*
* 将COPYRIGHT/README 安装到${CMAKE_INSTALL_PREFIX}/share/doc/cmake/路径
* CMAKE_INSTALL_PREFIX 默认是在 /usr/local/
* 可在cmake时指定 cmake -D CMAKE_INSTALL_PREFIX=/usr
*/
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
//安装脚本runhello.sh PROGRAMS:非目标文件的可执行程序安装
//实际安装到的是 /usr/local/bin
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
//将doc目录下的文件安装到${CMAKE_INSTALL_PREFIX}/share/doc/cmake/路径
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
3、src/CMakeLists.txt内容
ADD_EXECUTABLE(hello main.cpp)
4、src/main.cpp内容
#include <iostream>
int main(){
std::cout<<"hello world!"<<std::endl;
}
5、进入build,运行cmake ..
root@8d3:/work/cmake03/build# cmake ..
root@8d3:/work/cmake03/build# ls
CMakeCache.txt CMakeFiles Makefile bin cmake_install.cmake
root@8d3:/work/cmake03/build# cd bin/
root@8d3:/work/cmake03/build/bin# ls
CMakeFiles Makefile cmake_install.cmake
6、在build目录下,使用make命令编译,在build/bin目录下生成了hello的可执行程序,运行./hello,打印hello world
root@8d3:/work/cmake03/build# make
[ 50%] Building CXX object bin/CMakeFiles/hello.dir/main.o
[100%] Linking CXX executable hello
[100%] Built target hello
root@8d3:/work/cmake03/build# ls
CMakeCache.txt CMakeFiles Makefile bin cmake_install.cmake
root@8d3:/work/cmake03/build# cd bin/
root@8d3:/work/cmake03/build/bin# ls
CMakeFiles Makefile cmake_install.cmake hello
root@8d3:/work/cmake03/build/bin# ./hello
hello world!
7、在build目录下,执行make install,将配置的文档和脚本安装至相应路径
root@8d3:/work/cmake03/build# make install
Consolidate compiler generated dependencies of target hello
[100%] Built target hello
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/share/doc/cmake/COPYRIGHT
-- Installing: /usr/local/share/doc/cmake/README
-- Installing: /usr/local/bin/runhello.sh
-- Up-to-date: /usr/local/share/doc/cmake
-- Installing: /usr/local/share/doc/cmake/hello.txt
2.4 静态库和动态库的构建
2.4.1 静态库和动态库的区别
1、静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”。
2、静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
3、动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行
2.4.2 构建安装动态库
1、目录结构如下
root@8d3:/work/cmake06# tree
.
├── CMakeLists.txt
├── build
└── lib
├── CMakeLists.txt
├── hello.cpp
└── hello.h
2 directories, 4 files
2、各个文件内容如下
1.主目录 CMakeLists.txt
PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)
2.lib/CMakeLists.txt
SET(LIBHELLO_SRC hello.cpp)
/**
* hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
* SHARED,动态库 STATIC,静态库
* ${LIBHELLO_SRC} :源文件
/
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
INSTALL(FILES hello.h DESTINATION include/hello)
INSTALL(TARGETS hello LIBRARY DESTINATION lib)
3.lib/hello.h
#ifndef HELLO_H
#define Hello_H
void HelloFunc();
#endif
4.lib/hello.cpp
#include "hello.h"
#include <iostream>
void HelloFunc(){
std :: cout << "hello world" << std :: endl;
}
3、在build目录下依次执行cmake .. 、make 、make install命令,动态库成功生成并安装至对应路径/usr/local/lib/libhello.so(/usr/local是make install默认的安装路径,/bin是CMakeLists.txt文件中指定的DESTINATION路径)
root@8d3:/work/cmake06/build# cmake ..
root@8d3d08540d36:/work/chenlu/cmake06/build# make
[ 50%] Building CXX object bin/CMakeFiles/hello.dir/hello.o
[100%] Linking CXX shared library libhello.so
[100%] Built target hello
root@8d3:/work/cmake06/build# make install
Consolidate compiler generated dependencies of target hello
[100%] Built target hello
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/include/hello/hello.h //hello.h安装到了此路径
-- Installing: /usr/local/lib/libhello.so //libhello.so安装到了此路径
root@8d3:/work/cmake06/build# ls
CMakeCache.txt CMakeFiles Makefile bin cmake_install.cmake install_manifest.txt
root@8d3:/work/cmake06/build# cd bin/
root@8d3:/work/cmake06/build/bin# ls
CMakeFiles Makefile cmake_install.cmake libhello.so //bin目录下生成了libhello.so
2.4.3 构建和安装动态库和静态库
使用不同的名字
// 如果用这种方式,只会构建一个动态库,不会构建出静态库,虽然静态库的后缀是.a
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
// 修改静态库的名字,这样是可以的,但是我们往往希望他们的名字是相同的,只是后缀不同而已
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
使用相同的名字
1.目录结构与构建动态库相同,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)
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
2. 在build目录下依次执行cmake .. 、make 、make install命令,动态库和静态库成功生成
root@8d3:/work/cmake07/build# cmake ..
root@8d3:/work/cmake07/build# make
[ 25%] Building CXX object bin/CMakeFiles/hello_static.dir/hello.o
[ 50%] Linking CXX static library libhello.a
[ 50%] Built target hello_static
[ 75%] Building CXX object bin/CMakeFiles/hello.dir/hello.o
[100%] Linking CXX shared library libhello.so
[100%] Built target hello
root@8d3:/work/cmake07/build# make install
Consolidate compiler generated dependencies of target hello_static
[ 50%] Built target hello_static
Consolidate compiler generated dependencies of target hello
[100%] Built target hello
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/include/hello/hello.h //头文件安装地址
-- Installing: /usr/local/lib/libhello.so //动态库安装地址
-- Installing: /usr/local/lib/libhello.a //静态库安装地址
2.4.4 使用外部共享库和头文件
1.目录结构如下:
root@8d3:/work/cmake08# tree
.
├── CMakeLists.txt
├── build
└── src
├── CMakeLists.txt
└── main.cpp
2、各个文件内容如下
1.主目录 CMakeLists.txt
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
2.src/CMakeLists.txt
//将hello.h的文件地址包含至工程中头文件的搜索路径
INCLUDE_DIRECTORIES(/usr/local/include/hello)
ADD_EXECUTABLE(hello main.cpp)
//链接动态库
TARGET_LINK_LIBRARIES(hello libhello.so)
3.src/main.cpp
#include <hello.h>
int main(){
HelloFunc();
}
3、在build目录下,执行cmake..和make命令编译,在build/bin目录下生成了hello的可执行程序
root@8d3:/work/cmake08/build# cmake ..
root@8d3:/work/cmake08/build# make
[ 50%] Building CXX object bin/CMakeFiles/hello.dir/main.o
[100%] Linking CXX executable hello
[100%] Built target hello
root@8d3:/work/cmake08/build# cd bin/
root@8d3:/work/cmake08/build/bin# ls
CMakeFiles Makefile cmake_install.cmake hello
4、运行./hello,产生如下报错,因为生成的libhello.so文件在/usr/local/lib目录下,应该将其移至/usr/lib目录下(若电脑为64位,则移至/usr/lib64)
root@8d3:/work/cmake08/build/bin# ./hello
./hello: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
root@8d3:/work/cmake08/build/bin#
5、动态文件移至/usr/lib目录下,再执行./hello,程序成功运行
root@8d3:/work/cmake08/build/bin# mv /usr/local/lib/libhello.so /usr/lib
root@8d3:/work/cmake08/build/bin# ./hello
hello world
3. CMake语法介绍
3.1 关键字介绍
3.1.1 PROJECT
可以用来指定工程的名字和支持的语言,默认支持所有语言
PROJECT (HELLO) 指定了工程的名字,并且支持所有语言—建议
PROJECT (HELLO CXX) 指定了工程的名字,并且支持语言是C++
PROJECT (HELLO C CXX) 指定了工程的名字,并且支持语言是C和C++
该指定隐式定义了两个CMAKE的变量
<projectname>_BINARY_DIR,例1中是 HELLO_BINARY_DIR
<projectname>_SOURCE_DIR,例1中是 HELLO_SOURCE_DIR
MESSAGE关键字就可以直接使用者两个变量
问题:如果改了工程名,这两个变量名也会改变
解决:又定义两个预定义变量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,这两个变量和HELLO_BINARY_DIR,HELLO_SOURCE_DIR是一致的。所以改了工程名也没有关系
3.1.2 SET
用来显示指定变量的
SET(SRC_LIST main.cpp) SRC_LIST变量就包含了main.cpp
也可以 SET(SRC_LIST main.cpp t1.cpp t2.cpp)
更改二进制的保存路径
SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
3.1.3 MESSAGE
向终端输出用户自定义的信息
主要包含三种信息:
- SEND_ERROR,产生错误,生成过程被跳过
- STATUS,输出前缀为—的信息
- FATAL_ERROR,立即终止所有 cmake 过程
3.1.4 ADD_EXECUTABLE
生成可执行文件
ADD_EXECUTABLE(hello ${SRC_LIST}) 生成的可执行文件名是hello,源文件读取变量SRC_LIST中的内容
也可以直接写 ADD_EXECUTABLE(hello main.cpp)
例1可以简化的写成
PROJECT(HELLO)
ADD_EXECUTABLE(hello main.cpp)
注意:工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的
3.1.5 ADD_SUBDIRECTORY
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置,EXCLUDE_FROM_ALL函数是将写的目录从编译中排除,如程序中的example
ADD_SUBDIRECTORY(src bin),指将 src 子目录加入工程并指定编译输出(包含编译中间结果)路径为bin 目录,如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在build/src 目录
3.1.6 INSTALL
1.安装文件
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
2.非目标文件的可执行程序安装(如脚本)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
3.安装目录下所包含的所有文件
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
4.安装过程
make install
DESTINATION,写绝对路径,也可以写相对路径,相对路径实际路径是:${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>
CMAKE_INSTALL_PREFIX 默认是在 /usr/local/
cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的时候指定CMAKE_INSTALL_PREFIX变量的路径
3.1.7 ADD_LIBRARY
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
SHARED,动态库 STATIC,静态库
${LIBHELLO_SRC} :源文件
3.1.8 SET_TARGET_PROPERTIES
这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本
//对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
//cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
3.1.9 TARGET_LINK_LIBRARIES
链接静态库
TARGET_LINK_LIBRARIES(main libhello.a) main二进制执行文件名
链接动态库
TARGET_LINK_LIBRARIES(main libhello.so) main二进制执行文件名
3.1.10 SET_TARGET_PROPERTIES
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION 指代动态库版本,SOVERSION 指代 API 版本,生成文件如下:
root@8d3:/work/cmake09/build/bin# ls -l
total 36
drwxr-xr-x 3 root root 4096 Aug 25 10:37 CMakeFiles
-rw-r--r-- 1 root root 7459 Aug 25 10:37 Makefile
-rw-r--r-- 1 root root 3203 Aug 25 10:37 cmake_install.cmake
lrwxrwxrwx 1 root root 13 Aug 25 10:37 libhello.so -> libhello.so.1
lrwxrwxrwx 1 root root 15 Aug 25 10:37 libhello.so.1 -> libhello.so.1.2
-rwxr-xr-x 1 root root 16856 Aug 25 10:37 libhello.so.1.2
3.2 语法的基本原则
1、变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名
2、指令(参数 1 参数 2...) 参数使用括弧括起,参数之间使用空格或分号分开。 以上面的 ADD_EXECUTABLE 指令为例,如果存在另外一个 func.cpp 源文件,可以写成以下两种:
ADD_EXECUTABLE(hello main.cpp func.cpp)
ADD_EXECUTABLE(hello main.cpp;func.cpp)
3、指令是大小写无关的,参数和变量是大小写相关的。推荐全部使用大写指令
3.3 语法的注意事项
1、SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”),如果源文件名中含有空格,就必须要加双引号
2、ADD_EXECUTABLE(hello main) 后缀可以不写,他会自动去找.c和.cpp,最好不要这样写,可能会有这两个文件main.cpp和main