致每一个还坚持在互联网和软件行业的同路人。
最近有朋友问我为什么选择当程序员,我也不明白。可能是缺少社交能力,可能更喜欢心理上保存平静,可能是为了那份对于从无到有的沉浸感和满足感。
定义
cmake是一种高级编译工具,所有的操作系统都是通过编译CMakeList.txt来完成的,当多个人用不同的语言或者编译器开发同一个项目的时候,最终要输出一个可执行文件或者共享库的时候使用cmake很方便。
camke的下载网址:cmake不同版本下载地址
PROJECT关键字
可以用来指定工程的名字和支持的语言,默认支持所有的语言。
project(XXXX) ===> 指定了工程的名字,并且支持所有语言
project(XXXX CXX) ===> 指定了工程的名字,并且支持的语言是C++
project(XXXX C CXX) ===> 指定了工程的名字,并且支持的语言是C 和 C++
XXXX_BINARY_DIR 和 XXXX _SOURCE_DIR
<projectname>_BINARY_DIR 和 <projectname>_SOURCE_DIR它们都指向了当前的工作目录,当工程的名字发生改变时这两个变量的名字也会跟着改变。
使用 PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR
SET关键字
用来显示指定变量
SET(SRC_LIST main.cpp) ==> SRC_LIST变量就包含了main.cpp
SET(SRC_LIST main.cpp demo1.cpp demo3.cpp demo1.h) ==> SRC_LIST变量就包含了main.cpp demo1.cpp demo3.cpp demo1.h
设置使用g++编译器
set(CMAKE_CXX_COMPILER "g++")
指定编译器
set(CMAKE_CXX_STANDARD 11)
添加c++11的支持,nation指定目标程序的cpu架构来进行程序优化 -O3优化等级
set(CMAKE_CXX_FLAGS "-std=c++11 -march=native -O3")
===> native 就是相当于自检查cpu,-march是gcc优化选项,-Ox 这个参数只有在 cmake -DCMAKE_BUILD_TYPE=release时有效
版本号
set (XXXX VERSION 0.0.1)
MESSAGE关键字
向终端输出用户自定义的信息
主要包含三种信息:
SEND_ERROR ==> 产生错误,生成编译过程中被跳过
STAUS ==> 输出前缀为一的信息
FATAL_ERROR ==> 立即终止所有cmake过程
message(STATUS "This is BINARY dir" $(PROJECT_BINARY_DIR))
message(STATUS "This is SOURCE dir" $(PROJECT_SOURCE_DIR))
ADD_EXECUTABLE关键字
生成可执行文件
ADD_EXECUTABLE(XXX ${SRC_LIST}) ===> 生成可执行文件hello,源文件读取变量SRC_LIST中的内容
ADD_EXECUTABLE(XXX main.cpp) ===> 也可以直接填写 main.cpp
工程名和生成的可执行文件hello是没有任何关系 ==> 工程名和可执行
语法的基本原则
变量使用${}方法取值,但是在IF控制语法中是直接使用变量名的
指令(参数1 参数2...)参数使用括号()括起来,参数之间使用空格或者分号分隔。
例如:ADD_EXECUTABLE(XXX main.cpp demo.cpp)
或者ADD_EXECUTABLE(XXX main.cpp demo.cpp)
指令是大小写无关的,参数和变量是大小写相关的 ==> 建立指令全部大写
语法注意事项:
SET{SRC_LIST main.cpp}可以写成SET{SRC_LIST "main.cpp"} ==> 如果源文件名中含有空格,就必须要加双引号。
ADD_EXECUTABLE(hello main) => 后缀可以不写,会自动寻找.c和.cpp文件,最好同时存在main.cpp和main.c
内部构建和外部构建
内部构建: 上述例子就是内部构建,他产生的临时文件特别多,不方便清理。
外部构建: 就会把生成的临时文件放到build目录下,不会对源文件源任何影响
推荐使用外部构建:
1.建立一个build目录,可以是任何地方,建立在当前目录下
2.进入build,运行cmake .. ==> 表示上级目录,你可以写CMakeLists.txt所在的绝对路径,生成的文件都在build目录下了
3.在build目录下,运行make来构建项目
使用外部构建
PROJECT_SOURCE_DIR ==> 指工程路径
PROJECT_BINARY_DIR ==> 编译路径 build目录的绝对路径
HELLO更像一个工程
1.为工程添加一个子目录src,用来放置工程源代码
2.添加一个子目录dec,用来存放这个工程的文档
3.在工程目录添加文本文件 COPYRIGHT README ==> 描述项目的安装、编译方法
4.在工程目录添加一个 runhello.sh脚本,用来调用hello二进制
5.将构建后的目标文件放入构建目标的bin子目录
6.将doc目录的内容以及COPYRIGHT/README 安装到 /usr/share/doc/cmake
.
|---- build
|---- CMakeList.txt
|---- src
|---- CMakeList.txt
|---- main.cpp
# ==> 外层CMakeLists.txt
# PROJECT(CmakeList)
# ADD_SUBDIRECTORY(src bin)
# ==> src下的CMakeLists.txt
# ADD_EXECUTABLE(hello main.cpp)
会生成很多中间文件,我们关注我们需要关注的部分就可以了
ADD_SUBDIRECTORY指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXECLUDE_FROM_ALL])
1.这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。
2.EXECLUDE_FROM_ALL函数从将某个目录排除在外
ADD_SUBDIRECTORY(src bin)
1.将src子目录加入工程并指定编译输出(包含编译中间结果) 路径为bin目录
2.如果不进行bin目录的指定,那么编译结果(包括中间结果)都将存放在build 和 src下
上述没有指定bin的路径,其实我们可以通过更改二进制的保存路径来指定bin目录,不使用默认生成的路径(build中)。
更改二进制的保存路径
SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH变量来指定最终的目标二进制的位置。
SET{EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin}
SET{LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib}
注:哪里要改变目标存放的路径,就在哪里加入上述的定义
安装
一种是从代码编译后直接make install安装,一种是打包时指定目录安装。
指定目录安装:
make install DESTDIR=/xxx/xxx
./configure -prefix=/xxx/xxx
-------------------------------------------------------------------------------------------------------------------------
使用CMAKE一个指令: INSTALL
INSTALL的安装可以包括:二进制、动态库、静态库、文件、目录、脚本等
使用CMake一个新的变量: CMAKE_INSTALL_PREFIX
-------------------------------------------------------------------------------------------------------------------------
目录结构:
.
|__build
|__CMakeList.txt|__COPYRIGHT
|__doc
| |__hello.txt
|__README
|__XXXX.sh
|__src
|__CMakeList.txt
|__main.cpp-------------------------------------------------------------------------------------------------------------------------安装文件COPYRIGHT和README
INSTALL(FILES COPYRIGHT README DESTINATION xxx/xxx/xxx/)
FILES ====>文件
DESTINATION ===> 路径
1.可以写绝对路径
2.可以写相对路径,相对路径实际是: ${CMAKE_INSTALL_PREFIX}/<DESTINATION>
CMAKE_INSTALL_PREFIX 默认是 /usr/local/
cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的时候指定CMAKE_INSTALL_PREFIX
安装脚本
INSTALL(PROGRAMS xxxx.sh DESTINATION bin)
PROGRAMS: 非目标文件的可执行程序安装(比如脚本之类)
说明: 实际安装到的是 /usr/local/ ==> 默认路径是/usr/local
安装doc中的hello.txt
1.通过在doc目录构建CMakeList.txt,通过install下的file
2.直接通过在工程目录通过
INSTALL(DIRECTORY doc/ DESTINATION xxx/xxx/xxx)
DIRECTORY后面连接的是所在Source目录下的相对路径
注意:abc和abc/有很大区别:
目录名不以/结尾: 将整个目录安装到目标路径
目录名以/结尾: 将整个目录中的内容安装到目标路径
静态库和动态库的构建
任务:
1.建立一个静态库和动态库,提供HelloFunc函数供其他程序使用,HelloFunc向终端输出 Hello World
2.安装头文件与共享库
静态库和动态库的区别:
1.静态库的扩展名一般为 ".a" 或者 ".lib",动态库的扩展名一般为".so"或者".dll"
2.静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
3.动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行
构建实例:
.
|--- build
|--- CMakeLists.txt
|--- lib|--- CMakeLists.txt
|--- hello.cpp
|--- hello.h
同时构建静态库和动态库
1.一种错误的示范:这种方式只会构建一个动态库,不会构建出静态库,虽然静态库.a
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
2.修改动态库的名字,这样是可以的
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
3.但是在实际的开发过程中,我们往往希望静态库和动态库的名字相同
SET_TARGET_PROPERTIES ==> 这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和API版本
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)
动态库的版本号
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
安装共享库和头文件
<prefix>的默认路径是 /usr/local
文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)
二进制、静态库、动态库安装都用TARGETS
ARCHIVE 特指静态库, LIBRARY 特指动态库, RUNTIME 特指可执行目标二进制
使用外部共享库和头文件
准备一个项目:去调用上面生成的动态库
.
|--- build
|--- CMakeLists
|--- src
|--- CMakeLists.txt
|--- main.cpp
DEBUG编译
cmake -DCMAKE_BUILD_TYPE=debug ..
cmake -DCMAKE_BUILD_TYPE=release ..