CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件
- cmake可根据CMakeLists.txt文件生成vs工程和makefile文件(通过-G 设置输出生成的文件类型)
注意:本文使用vscode,使用nmake在vc++2013环境下测试。
参考:Cmake 详解
1. 基本语法
# 运行命令
command(arg1,arg2,aeg3 ...)
# 设置参数
set(var_name var_value)
# 使用变量
command(arg1 ${var_name})
# 控制语句
IF(expression)
COMMAND1(ARGS)
ELSE(expression)
COMMAND2(ARGS)
ENDIF(expression)
# expression
IF(var) # 不是空, 0, N, NO, OFF, FALSE, NOTFOUND 或 <var>_NOTFOUND时,为真
IF(NOT var) # 与上述条件相反。
IF(var1 AND var2) # 当两个变量都为真是为真。
IF(var1 OR var2) # 当两个变量其中一个为真时为真。
IF(COMMAND cmd) # 当给定的cmd确实是命令并可以调用是为真
IF(EXISTS dir) # 目录名存在
IF(EXISTS file) # 文件名存在
IF(IS_DIRECTORY dirname) # 当dirname是目录
IF(file1 IS_NEWER_THAN file2) # 当file1比file2新,为真
IF(variable MATCHES regex) # 符合正则
# 循环
WHILE(condition)
COMMAND1(ARGS)
// ...
ENDWHILE(condition)
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(one_dir ${SRC_LIST})
MESSAGE(${one_dir})
ENDFOREACH(onedir)
在项目的每个目录中,都需要编写一个CMakeLists.txt。
2. 简单示例
在vscode中使用cmake:
- 编写cpp文件
// main.cpp
#include <stdio.h>
int main()
{
printf("hello cmake");
}
- 编写CMakeLists.txt文件
# CMake 最低版本要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (CMake_Test)
# 设置变量,源文件列表,${src_list} 指代后面的字符串
set(src_list main.cpp)
# 指定生成目标
add_executable(CMake_Test ${src_list})
- 设置cmake环境
打开vscode的Terminal,输入:
set PATH=D:\cmake\cmake-3.13.2-win64-x64\cmake-3.13.2-win64-x64\bin;%PATH%
- 设置vc++环境
直接使用vs的命令提示符脚本
%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"" x86
- 生成makefiles文件
设置生成为nmake的makefile,否则默认将生成vs项目工程;注意,若生成了vs项目工程再想生成makefile必须先删除vs项目;运行cmake可看到当前支持的生成器(Generators)
cmake . -G "NMake Makefiles"
- 使用nmake编译
nmake
3. 外部构建
在上一节中,cmake执行后生成很多中间文件,清理会比较麻烦;推荐使用外部构建,步骤如下:
- 在CMakeLists.txt的同级目录下,新建一个build文件夹
- 进入build文件夹,执行cmake …/cmake … -G "NMake Makefiles"命令,这样所有的中间文件以及makefile都在build目录下了
- 在build目录下执行make/nmake就可以得到可执行文件
4. 多个源文件项目
先看一下文件目录,有main.cpp文件、service下http_service.h和http_service.cpp文件、utility下json.h和json.cpp文件;main.cpp依赖http_service模块;http_service依赖json模块。

代码如下:
// main.cpp
#include "service/http_service.h"
#include <stdio.h>
int main()
{
post("http://www.baidu.com/","wd","cmake");
}
// http_service.h
#ifndef __HTTP_SERVICE__
#define __HTTP_SERVICE__
// 生成dll必须有导出;否则将不会生成.lib文件导致链接失败
extern "C" __declspec(dllexport) void post(const char * url,const char * data_name,const char * data_value);
#endif
// http_service.cpp
#include "http_service.h"
#include "json.h"
#include <stdio.h>
void post(const char *url, const char *data_name, const char *data_value)
{
printf("post to %s:", url);
json_serialize(data_name, data_value);
}
// json.h
#ifndef __JSON__
#define __JSON__
void json_serialize(const char * _key,const char * _value);
#endif
// json.cpp
#include "json.h"
#include <stdio.h>
void json_serialize(const char *_key, const char *_value)
{
printf("{\"%s\":\"%s\"}", _key, _value);
}
4.1 单个CMakeLists.txt文件编译
按前文所写的方式进行编译,只需在根目录下编写一个CMakeLists.txt即可
CMakeLists.txt文件内容:
# CMake 最低版本要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (cmake_test)
# 添加头文件搜索目录
INCLUDE_DIRECTORIES(service)
INCLUDE_DIRECTORIES(utility)
# 设置变量,源文件列表
set(src_list main.cpp service/http_service.cpp utility/json.cpp)
# 指定生成目标
add_executable(main ${src_list})
4.2 各个模块独立编译
还有一种方式,就是各个模块独立编译,最后链接到可执行文件;这样需要在每个模块下都编写CMakeLists.txt文件。例如,我们想将json模块编译成静态库,将http_service模块编译成动态库,最后再链接到一起。我们需要在service和utility下分别编写CMakeLists.txt文件,并修改根目录下CMakeLists.txt:
service/CMakeLists.txt:
# 添加构建子模块;第一个参数指定子模块源文件的绝对路径;第二个参数指定相对当前模块目标的存放路径,该子模块生成的文件将存放在此路径下(本例即相对build/service的相对路径:build/utility)
# add_subdirectory(D:/vscode/cpp/cmake_test/utility ../utility)
# 添加构建子模块;第一个参数指定子模块源文件的相对当前CMakeLists.txt路径;第二个参数指定相对当前模块目标的存放路径,该子模块生成的文件将存放在此路径下(本例即相对build/service的相对路径:build/utility)
add_subdirectory(../utility ../utility)
# 生成动态库
add_library(http_service SHARED http_service.cpp)
# 链接json
target_link_libraries(http_service json)
utility/CMakeLists.txt:
# 生成静态库
add_library(json STATIC json.cpp)
CMakeLists.txt:
# CMake 最低版本要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (cmake_test)
# 添加头文件搜索目录
INCLUDE_DIRECTORIES(service)
INCLUDE_DIRECTORIES(utility)
# 添加子模块并构建
add_subdirectory(service)
add_executable(main main.cpp)
# 链接dll
target_link_libraries(main http_service)
4.2.1 生成Makefile
D:\vscode\cpp\cmake_test\build>cmake .. -G "NMake Makefiles"
结果如下:

执行nmake,生成了http_service.dll、http_service.lib、json.lib、main.exe:

注意:main.exe执行时需将http_service.dll拷贝过去!!!
5. find模块
在一个大型项目中具体指定头文件目录、库目录是比较复杂的,而且无法复用;编写find模块可以很方便的自动查找头文件目录和库目录。
6. 配置CMakeLists.txt中的参数
main.cpp文件中有三个参数值是写死的,我们写到配置文件中。
添加config.h.in文件:

在config.h.in添加三个参数定义:
#define URL "@URL@"
#define DATA_NAME "@DATA_NAME@"
#define DATA_VALUE "@DATA_VALUE@"
修改CMakeLists.txt:
# CMake 最低版本要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (cmake_test)
# 添加头文件搜索目录
INCLUDE_DIRECTORIES(service)
INCLUDE_DIRECTORIES(utility)
######## 添加配置 ###########
# 通过config.h传递参数给源文件,将在build下生成config.h文件
set(URL "https://www.baidu.com/")
set(DATA_NAME "wd")
set(DATA_VALUE "cmake")
configure_file(
${PROJECT_SOURCE_DIR}/config.h.in
${PROJECT_BINARY_DIR}/config.h
)
add_subdirectory(service)
# 指定生成目标
add_executable(main main.cpp)
target_link_libraries(main http_service)
修改main.cpp
#include "service/http_service.h"
#include <stdio.h>
// 添加配置文件,配置文件生成路径是build/config.h
#include "build/config.h"
int main()
{
post(URL,DATA_NAME,DATA_VALUE);
}
7. 区分开发版和发布版
开发板编译脚本make_debug.bat:
::删除debug下旧文件
rmdir /s /q debug
mkdir debug
::在debug下构建
cd debug
::生成makefile,配置为debug版,nmake编译命令会使用/MDd (使用了CMAKE_C_FLAGS_DEBUG)
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=debug
::编译
NMake
::复制依赖dll文件
copy service\http_service.dll .\
发布版编译脚本make_release.bat:
::删除release下旧文件
rmdir /s /q release
mkdir release
::在release下构建
cd release
::生成makefile,配置为release版,nmake编译命令会使用/MD /DNDEBUG (使用CMAKE_C_FLAGS_RELEASE)
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=release
::编译
NMake
::复制依赖dll文件
copy service\http_service.dll .\
若需要添加其他debug或release的宏,可以在CMakeLists.txt中修改CMAKE_C_FLAGS_DEBUG/CMAKE_C_FLAGS_RELEASE的值,例如:
# 添加值
set(CMAKE_C_FLAGS_DEBUG "/DEBUG ${CMAKE_C_FLAGS_DEBUG}")
# 修改值
set(CMAKE_C_FLAGS_RELEASE "/MD")
# 输出CMAKE_C_FLAGS_DEBUG/CMAKE_C_FLAGS_RELEASE
message(STATUS "debug flags: ${CMAKE_C_FLAGS_DEBUG}")
message(STATUS "release flags: ${CMAKE_C_FLAGS_RELEASE}")
8. 指令参考
- PROJECT(projectname) # 项目名
- SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) 设置参数
- MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display”…) 向终端输出用户定义的信息
- ADD_EXECUTABLE(exe ${SRC_LIST}) # 生成可执行文件 exe
- ADD_LIBRARY(mod [SHARED|STATIC|MODULE] ${SRC_LIST}) # 生成库 libmod.so
- ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) 指定cmake子目录源代码所在路径,以及编译后二进制文件存放目录
- $ENV{HOME} # 使用环境变量 HOME
- INCLUDE_DIRECTORIES(header_file_dir) # 添加头文件搜索目录
- LINK_DIRECTORIES(lib_file_dir) # 添加库文件搜索目录
- TARGET_LINK_LIBRARIES(exe mod1) # 将 libmod1.so 链接到 exe 中
- ADD_DEFINITIONS(-DENABLE_DEBUG) # 向C/C++编译器中添加宏定义 ENABLE_DEBUG
- ADD_DEPENDENCIES(target-name depend-target1 ) # 定义依赖
- AUX_SOURCE_DIRECTORY(. SRC_LIST) 发现一个目录下所有的源代码文件并将列表存储在一个变量中
- EXEC_PROGRAM 用于在构建时,运行shell命令
- INCLUDE(file1 [OPTIONAL]) # 载入 CMakeLists.txt 文件
- INCLUDE(module [OPTIONAL]) # 载入 cmake 模块
9. 变量参考
项目目录相关:
# 构建发生的目录
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
# 不论采用何种编译方式,都是工程顶层目录
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
CMAKE_CURRENT_SOURCE_DIR # 当前处理的CMakeLists.txt所在的路径
CMAKE_CURRRENT_BINARY_DIR # 内部编译: 跟CMAKE_CURRENT_SOURCE_DIR一致
# 外部编译: 指的是构建目录
# add_subdirectory(src bin) 会更改它的值为 bin
CMAKE_CURRENT_LIST_FILE # 当前输出所在的CMakeLists.txt的完整路径
CMAKE_CURRENT_LIST_LINE # 当前输出所在的行
CMAKE_MODULE_PATH # 模块所在路径
EXECUTABLE_OUTPUT_PATH # 可执行文件存放目录
LIBRARY_OUTPUT_PATH # 库存放目录
CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE # 将工程提供的头文件目录始终置于系统头文件目录的前面
CMAKE_INCLUDE_PATH # 头文件搜索目录
CMAKE_LIBRARY_PATH # 库搜索目录
系统信息:
CMAKE_MAJOR_VERSION # CMAKE主版本号,比如2.4.6中的2
CMAKE_MINOR_VERSION # CMAKE次版本号,比如2.4.6中的4
CMAKE_PATCH_VERSION # CMAKE补丁等级,比如2.4.6中的6
CMAKE_SYSTEM # 系统名称,比如Linux-2.6.22
CMAKE_SYSTEM_NAME # 不包含版本的系统名,比如Linux
CMAKE_SYSTEM_VERSION # 系统版本,比如2.6.22
CMAKE_SYSTEM_PROCESSOR # 处理器名称,比如i686
UNIX # 在所有的类Unix平台为TRUE,包括OSX和cygwin
WIN32 # 在所有的Win32平台为TRUE,包括cygwin
开关选项:
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS # 用来控制IF ELSE语句的书写方式
BUILD_SHARED_LIBS # 这个开关用来控制默认的库编译方式: 动态库 静态库
CMAKE_C_FLAGS # 设置C编译选项
MAKE_CXX_FLAGS # MAKE_CXX_FLAGS
CMAKE_INCLUDE_CURRENT_DIR # 自动将每个CMakeLists.txt的所在目录依次加入到 头文件搜索目录
编译参数相关:
message(STATUS "CMAKE_C_FLAGS = " ${CMAKE_C_FLAGS})
message(STATUS "CMAKE_C_FLAGS_DEBUG = " ${CMAKE_C_FLAGS_DEBUG})
message(STATUS "CMAKE_C_FLAGS_RELEASE = " ${CMAKE_C_FLAGS_RELEASE})
message(STATUS "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS})
message(STATUS "CMAKE_CXX_FLAGS_DEBUG = " ${CMAKE_CXX_FLAGS_DEBUG})
message(STATUS "CMAKE_CXX_FLAGS_RELEASE = " ${CMAKE_CXX_FLAGS_RELEASE})
message(STATUS "CMAKE_EXE_LINKER_FLAGS = " ${CMAKE_EXE_LINKER_FLAGS})
message(STATUS "CMAKE_EXE_LINKER_FLAGS_DEBUG = " ${CMAKE_EXE_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_EXE_LINKER_FLAGS_RELEASE = " ${CMAKE_EXE_LINKER_FLAGS_RELEASE})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS = " ${CMAKE_SHARED_LINKER_FLAGS})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS_DEBUG = " ${CMAKE_SHARED_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS_RELEASE = " ${CMAKE_SHARED_LINKER_FLAGS_RELEASE})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS = " ${CMAKE_STATIC_LINKER_FLAGS})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS_DEBUG = " ${CMAKE_STATIC_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS_RELEASE = " ${CMAKE_STATIC_LINKER_FLAGS_RELEASE})
find_package
set(RK_ROOT_INC "/home/qilimi/project/rk-linux/rvxx_415_tb/rv1126_rv1109/buildroot/output/rockchip_rv1126_evb_dualcam_tb/host/arm-buildroot-linux-gnueabihf/sysroot/usr/include/")
set(RK_ROOT_LIB "/home/qilimi/project/rk-linux/rvxx_415_tb/rv1126_rv1109/buildroot/output/rockchip_rv1126_evb_dualcam_tb/host/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/")
find_path(EASYMEDIA_INCLUDE_DIR
NAMES buffer.h
HINTS ${PKG_EASYMEDIA_INCLUDEDIR} ${PKG_EASYMEDIA_INCLUDE_DIRS} ${RK_ROOT_INC}
PATH_SUFFIXES easymedia)
find_library(EASYMEDIA_LIBRARY
NAMES easymedia
HINTS ${PKG_EASYMEDIA_LIBDIR} ${PKG_EASYMEDIA_LIBRARY_DIRS} ${RK_ROOT_LIB}
)
# HINTS 后面接的是查找路径
# NAMES 后面接的是需要查找的文件名称
# PATH_SUFFIXES 路径的后缀
1295

被折叠的 条评论
为什么被折叠?



