cmake笔记

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:

  1. 编写cpp文件
// main.cpp
#include <stdio.h>
int main()
{
	printf("hello cmake");
}
  1. 编写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})
  1. 设置cmake环境
    打开vscode的Terminal,输入:
set PATH=D:\cmake\cmake-3.13.2-win64-x64\cmake-3.13.2-win64-x64\bin;%PATH%
  1. 设置vc++环境
    直接使用vs的命令提示符脚本
%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"" x86
  1. 生成makefiles文件
    设置生成为nmake的makefile,否则默认将生成vs项目工程;注意,若生成了vs项目工程再想生成makefile必须先删除vs项目;运行cmake可看到当前支持的生成器(Generators)
cmake . -G "NMake Makefiles"
  1. 使用nmake编译
nmake

3. 外部构建

在上一节中,cmake执行后生成很多中间文件,清理会比较麻烦;推荐使用外部构建,步骤如下:

  1. 在CMakeLists.txt的同级目录下,新建一个build文件夹
  2. 进入build文件夹,执行cmake …/cmake … -G "NMake Makefiles"命令,这样所有的中间文件以及makefile都在build目录下了
  3. 在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 路径的后缀
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值