1.说明
cmake的定义是什么 ?
-----
⾼级编译配置⼯具
当多个⼈⽤不同的语⾔或者编译器开发⼀个项⽬,最终要输出⼀个可执⾏⽂件或者共享库(
dll
,
so
等等)这时候神
器就出现了
-----CMake
!
所有操作都是通过编译
CMakeLists.txt
来完成的
—
简单
官 ⽅⽹站是
www.cmake.org
,可以通过访问官⽅⽹站获得更多关于
cmake
的信息
学习
CMake
的⽬的,为将来处理⼤型的
C/C++/JAVA
项⽬做准备
2.CMake安装
1
、绝⼤多数的
linux
系统已经安装了
CMake
2
、
Windows
或某些没有安装过的
linux
系统,去
http://www.cmake.org/HTML/Download.html
可以下载安装
3.CMake⼀个HelloWord
1
、步骤⼀,写⼀个
HelloWord
#main.cpp
#include <iostream>
int main(){
std::cout << "hello word" << std::endl; }
2
、步骤⼆,写
CMakeLists.txt
#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
、步骤三、使⽤
cmake
,⽣成
makefile
⽂件
cmake .
输出:
[root@localhost cmake]# cmake .
CMake Warning (dev) in CMakeLists.txt:
Syntax Warning in cmake code at
/root/cmake/CMakeLists.txt:7:37
Argument not separated from preceding token by whitespace.
This warning is for project developers. Use -Wno-dev to suppress it.
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- This is BINARY dir /root/cmake
-- This is SOURCE dir /root/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /root/cmake
⽬录下就⽣成了这些⽂件
-CMakeFiles, CMakeCache.txt, cmake_install.cmake
等⽂件,并且⽣成了
Makefile.
现在不需要理会这些⽂件的作⽤,以后你也可以不去理会。最关键的是,它⾃动⽣成了Makefile.
4
、使⽤
make
命令编译
root@localhost cmake]# make
Scanning dependencies of target hello
[100%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
Linking CXX executable hello
[100%] Built target hello
5
、最终⽣成了
Hello
的可执⾏程序
4.CMake⼀个HelloWord-的语法介绍
4.1PROJECT关键字
可以⽤来指定⼯程的名字和⽀持的语⾔,默认⽀持所有语⾔PROJECT (HELLO) 指定了⼯程的名字,并且⽀持所有语⾔ — 建议PROJECT (HELLO CXX) 指定了⼯程的名字,并且⽀持语⾔是 C++PROJECT (HELLO C CXX) 指定了⼯程的名字,并且⽀持语⾔是 C 和 C++
该指定隐式定义了两个 CMAKE 的变量_BINARY_DIR ,本例中是 HELLO_BINARY_DIR_SOURCE_DIR ,本例中是 HELLO_SOURCE_DIRMESSAGE 关键字就可以直接使⽤者两个变量,当前都指向当前的⼯作⽬录,后⾯会讲外部编译
问题:如果改了⼯程名,这两个变量名也会改变
解决:⼜定义两个预定义变量:
PROJECT_BINARY_DIR
和
PROJECT_SOURCE_DIR
,这两个变量和 HELLO_BINARY_DIR,
HELLO_SOURCE_DIR
是⼀致的。所以改了⼯程名也没有关系
4.2SET关键字
⽤来显示的指定变量的
SET(SRC_LIST main.cpp) SRC_LIST
变量就包含了
main.cpp
也可以
SET(SRC_LIST main.cpp t1.cpp t2.cpp)
4.3MESSAGE关键字
向终端输出⽤户⾃定义的信息
主要包含三种信息:
SEND_ERROR
,产⽣错误,⽣成过程被跳过。
SATUS
,输出前缀为
—
的信息。
FATAL_ERROR
,⽴即终⽌所有
cmake
过程
4.4ADD_EXECUTABLE关键字
⽣成可执⾏⽂件
ADD_EXECUTABLE(hello ${SRC_LIST})
⽣成的可执⾏⽂件名是
hello
,源⽂件读取变量
SRC_LIST
中的内容
也可以直接写
ADD_EXECUTABLE(hello main.cpp)
上述例⼦可以简化的写成
PROJECT(HELLO) ADD_EXECUTABLE(hello main.cpp)
注意:⼯程名的
HELLO
和⽣成的可执⾏⽂件
hello
是没有任何关系的
5.语法的基本原则
- 变量使⽤${}⽅式取值,但是在 IF 控制语句中是直接使⽤变量名
- 指令(参数 1 参数 2...) 参数使⽤括弧括起,参数之间使⽤空格或分号分开。 以上⾯的 ADD_EXECUTABLE 指令为例,如果存在另外⼀个 func.cpp 源⽂件就要写成:ADD_EXECUTABLE(hello main.cpp func.cpp)或者ADD_EXECUTABLE(hello main.cpp;func.cpp)
- 指令是⼤⼩写⽆关的,参数和变量是⼤⼩写相关的。但,推荐你全部使⽤⼤写指令
5.1语法注意事项
- SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”),如果源⽂件名中含有空格,就必须要加双引号
- ADD_EXECUTABLE(hello main) 后缀可以不写,他会⾃动去找.c和.cpp,最好不要这样写,可能会有这两个⽂件main.cpp和main
6.内部构建和外部构建
上述例⼦就是内部构建,他⽣产的临时⽂件特别多,不⽅便清理外部构建,就会把⽣成的临时⽂件放在 build ⽬录下,不会对源⽂件有任何影响强烈使⽤外部构建⽅式
6.1外部构建方式举例
//例⼦⽬录,CMakeLists.txt和上⾯例⼦⼀致
[root@localhost cmake]# pwd
/root/cmake
[root@localhost cmake]# ll
total 8 -rw-r--r--. 1 root root 198 Dec 28 20:59 CMakeLists.txt
-rw-r--r--. 1 root root 76 Dec 28 00:18 main.cpp
- 建⽴⼀个build⽬录,可以在任何地⽅,建议在当前⽬录下
- 进⼊build,运⾏cmake .. 当然..表示上⼀级⽬录,你可以写CMakeLists.txt所在的绝对路径,⽣产的⽂件都在build⽬录下了
- 在build⽬录下,运⾏make来构建⼯程
注意外部构建的两个变量
- HELLO_SOURCE_DIR 还是⼯程路径
- HELLO_BINARY_DIR 编译路径 也就是/root/cmake/bulid
7.让Hello World看起来更像一个工程
- 为⼯程添加⼀个⼦⽬录 src,⽤来放置⼯程源代码
- 添加⼀个⼦⽬录 doc,⽤来放置这个⼯程的⽂档 hello.txt
- 在⼯程⽬录添加⽂本⽂件 COPYRIGHT, README
- 在⼯程⽬录添加⼀个 runhello.sh 脚本,⽤来调⽤ hello ⼆进制
- 将构建后的⽬标⽂件放⼊构建⽬录的 bin ⼦⽬录
- 将 doc ⽬录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/
7.1将目标文件放入构建目录的 bin 子目录
每个⽬录下都要有⼀个
CMakeLists.txt
说明
[root@localhost cmake]# tree
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
外层
CMakeLists.txt
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
src
下的
CMakeLists.txt
ADD_EXECUTABLE(hello main.cpp)
7.1.1ADD_SUBDIRECTORY 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
- 这个指令⽤于向当前⼯程添加存放源⽂件的⼦⽬录,并可以指定中间⼆进制和⽬标⼆进制存放的位置
- EXCLUDE_FROM_ALL函数是将写的⽬录从编译中排除,如程序中的example
- ADD_SUBDIRECTORY(src bin)
- 将 src ⼦⽬录加⼊⼯程并指定编译输出(包含编译中间结果)路径为bin ⽬录
- 如果不进⾏ bin ⽬录的指定,那么编译结果(包括中间结果)都将存放在build/src ⽬录
7.1.2更改二进制的保存路径
SET
指令重新定义
EXECUTABLE_OUTPUT_PATH
和
LIBRARY_OUTPUT_PATH
变量 来指定最终的⽬标⼆进制的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
思考:加载哪个 CMakeLists.txt 当中哪⾥要改变⽬标存放路径,就在哪⾥加⼊上述的定义,所以应该在 src 下的 CMakeLists.txt 下写
7.2安装
- ⼀种是从代码编译后直接 make install 安装
- ⼀种是打包时的指定 ⽬录安装。
- 简单的可以这样指定⽬录:make install DESTDIR=/tmp/test
- 稍微复杂⼀点可以这样指定⽬录:./configure –prefix=/usr
7.2.1如何安装HelloWord
使⽤
CMAKE
⼀个新的指令:
INSTALL
INSTALL
的安装可以包括:⼆进制、动态库、静态库以及⽂件、⽬录、脚本等
使⽤
CMAKE
⼀个新的变量:
CMAKE_INSTALL_PREFIX
// ⽬录树结构
[root@localhost cmake]# tree
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│ └── hello.txt
├── README
├── runhello.sh
└── src
├── CMakeLists.txt
└── main.cpp
3 directories, 7 files
安装⽂件 COPYRIGHT 和 README
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
FILES
:⽂件
DESTINATION
:
1
、写绝对路径
2
、可以写相对路径,相对路径实际路径是:
${CMAKE_INSTALL_PREFIX}/<DESTINATION
定义的路径
>
CMAKE_INSTALL_PREFIX
默认是在
/usr/local/
cmake -D CMAKE_INSTALL_PREFIX=/usr
在
cmake
的时候指定
CMAKE_INSTALL_PREFIX
变量的路径
安装脚本runhello.sh
PROGRAMS
:⾮⽬标⽂件的可执⾏程序安装
(
⽐如脚本之类
)
INSTALL(PROGRAMS
runhello.sh
DESTINATION bin)
说明:实际安装到的是
/usr/bin
安装 doc 中的 hello.txt
- ⼀、是通过在 doc ⽬录建⽴CMakeLists.txt ,通过install下的file
- ⼆、是直接在⼯程⽬录通过 INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
DIRECTORY
后⾯连接的是所在
Source
⽬录的相对路径
注意:
abc
和
abc/
有很⼤的区别
⽬录名不以
/
结尾:这个⽬录将被安装为⽬标路径下的
⽬录名以
/
结尾:将这个⽬录中的内容安装到⽬标路径
7.2.2安装过程
cmake ..
make
make install
8.静态库和动态库的构建
任务:1,建⽴⼀个静态库和动态库,提供 HelloFunc 函数供其他程序编程使⽤, HelloFunc 向终端输出 Hello World 字符串。2,安装头⽂件与共享库。
静态库和动态库的区别
- 静态库的扩展名⼀般为“.a”或“.lib”;动态库的扩展名⼀般为“.so”或“.dll”。
- 静态库在编译时会直接整合到⽬标程序中,编译成功的可执⾏⽂件可独⽴运⾏
- 动态库在编译时不会放到连接的⽬标程序中,即可执⾏⽂件⽆法单独运⾏。
8.1构建实例
[root@localhost cmake2]# tree
.
├── 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>
void HelloFunc(){
std::cout << "Hello World" << std::endl; }
项⽬中的
cmake
内容
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} :源⽂件
8.2同时构建静态和动态库
// 如果⽤这种⽅式,只会构建⼀个动态库,不会构建出静态库,虽然静态库的后缀是.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})
8.2.1SET_TARGET_PROPERTIES(性能)
这条指令可以⽤来设置输出的名称,对于动态库,还可以⽤来指定动态库版本和
API
版本
同时构建静态和动态库
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
//对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)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
8.2.2动态库的版本号
⼀般动态库都有⼀个版本号的关联
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2
CMakeLists.txt
插⼊如下
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION
指代动态库版本,
SOVERSION
指代
API
版本。
8.3安装共享库和头⽂件
本例中我们将
hello
的共享库安装到
/lib
⽬录,
将
hello.h
安装到
/include/hello
⽬录
//⽂件放到该⽬录下
INSTALL(FILES hello.h DESTINATION include/hello)
//⼆进制,静态库,动态库安装都⽤TARGETS
//ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执⾏⽬标⼆进制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
注意:
安装的时候,指定⼀下路径,放到系统下
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
8.4使⽤外部共享库和头⽂件
准备⼯作,新建⼀个⽬录来使⽤外部共享库和头⽂件
[root@MiWiFi-R4CM-srv cmake3]# tree
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
main.cpp
#include <hello.h>
int main(){
HelloFunc();
}
8.4.1解决:make后头⽂件找不到的问题
PS
:
include <hello/hello.h>
这样
include
是可以,这么做的话,就没啥好讲的了
关键字:
INCLUDE_DIRECTORIES
这条指令可以⽤来向⼯程添加多个特定的头⽂件搜索路径,路径之间⽤空格分割
在
CMakeLists.txt
中加⼊头⽂件搜索路径
INCLUDE_DIRECTORIES(/usr/include/hello)
8.4.2解决:找到引⽤的函数问题
报错信息:
undefined reference to `HelloFunc()'
关键字:
LINK_DIRECTORIES
添加⾮标准的共享库搜索路径
关键字:
TARGET_LINK_LIBRARIES
添加需要链接的共享库
在
CMakeLists.txt
中插⼊链接共享库,主要要插在
executable
的后⾯
TARGET_LINK_LIBRARIES(main
libhello.so
)
查看
main
的链接情况
[root@MiWiFi-R4CM-srv bin]# ldd main
linux-vdso.so.1 => (0x00007ffedfda4000)
libhello.so => /lib64/libhello.so (0x00007f41c0d8f000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f41c0874000)
libm.so.6 => /lib64/libm.so.6 (0x00007f41c0572000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f41c035c000)
libc.so.6 => /lib64/libc.so.6 (0x00007f41bff8e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f41c0b7c000)
链接静态库
TARGET_LINK_LIBRARIES(main libhello.a)
9.特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH
注意:这两个是环境变量⽽不是
cmake
变量,可以在
linux
的
bash
中进⾏设置
我们上⾯例⼦中使⽤了绝对路径
INCLUDE_DIRECTORIES(/usr/include/hello)
来指明
include
路径的位置
我们还可以使⽤另外⼀种⽅式,使⽤环境变量
export CMAKE_INCLUDE_PATH=/usr/include/hello
本⼈所有视频和笔记都是免费分享给⼤家的,制作
视频和笔记要花费⼤量的时间成本(转载)