这篇文章是站在VS角度描述CMake基本语法的。具体语法对应关系,参照下表。
CMake函数对应VS相关设置
VS相关设置 | CMake函数 |
---|---|
指定包含头文件目录 | target_include_directories |
指定链接库目录 | target_link_directories |
指定链接库 | target_link_libraries |
指定C++标准 | set(CMAKE_CXX_STANDARD 11) |
指定编译器版本 | cmake -G “Visual Studio 12 2013” |
指定平台集 | cmake -G “Visual Studio 12 2013 Win64” |
指定编译类型 | set(CMAKE_BUILD_TYPE “Debug”)等价于g++ -g |
指定预编译宏 | add_definitions(-Dxxx -Dyyy ) 必须-D开头 |
指定解决方案名称 | project(xxxx) |
文件分类 | source_group(“folder_name” file_list) |
CMake常见内置变量
CMake内置变量 | 说明(路径都是全路径) |
---|---|
PROJECT_NAME | CMakeList.txt里设置的project_name |
CMAKE_BUILD_TYPE | 设置当前工程编译类型(Debug,Release),默认debug |
BUILD_TYPE | 当前工程的编译类型(Debug,Release) |
PROJECT_SOURCE_DIR | 根CMakeLists.txt所在的路径 |
PROJECT_BINARY_DIR | 工程的构建目录(build目录) |
CMAKE_CURRENT_SOURCE_DIR | 当前’CMakeLists.txt’ 所在的路径 |
CMAKE_CURRENT_BINARY_DIR | 当前正在处理的’构建目录’ |
CMAKE_CURRENT_LIST_DIR | 当前处理的’cmake文件’所在的目录 |
CMAKE_CURRENT_LIST_FILE | 当前处理的’CMakeLists.txt或cmake’文件的全路径 |
CMAKE_CURRENT_LIST_LINE | 当前处理的’CMakeLists.txt或cmake’文件的’行号’ |
CMAKE_PROJECT_NAME | 整个项目’配置的project_name |
LIBRARY_OUTPUT_DIRECTORY | 库的存放目录(需要用户设置) |
CMAKE_RUNTIME_OUTPUT_DIRECTORY | 可执行文件的存放目录(需要用户设置,必须在设置可执行文件之前设置) |
设置C++FLAGS,例如MT
#release模式下为MT
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
#debug模式下为MTD
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
Debug和Release模式设置与判断(该模式不支持动态判断,需要设置变量CMAKE_BUILD_TYPE )
#设置编译模式为Debug,默认为Debug
set(CMAKE_BUILD_TYPE "Debug")
#当前编译模式的判断
if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} CUR_BUILD_TYPE)
if(${CUR_BUILD_TYPE} STREQUAL "release")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -o3")
message(STATUS "release build,Cxx flags:${CMAKE_CXX_FLAGS_RELEASE}")
else()
#default mode debug
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -o0")
message(STATUS "debug build,Cxx flags:${CMAKE_CXX_FLAGS_DEBUG}")
endif()
else()
#default mode debug
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -o0")
message(STATUS "debug build,Cxx flags:${CMAKE_CXX_FLAGS_DEBUG}")
endif()
#变量BUILD_TYPE在Debug模式下位Debug,release模式下为Release,未选中模式的时为空。等价于VS的$(Configuration)
message(STATUS "current build type:${BUILD_TYPE}")
设置工程为Win32 windwos 窗口程序
CMake默认生成的vs工程为console程序。如果想生成windows窗口程序需要指定链接属性。
set_target_properties(demo PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
当前运行平台判断
# 在 Linux 平台
CMAKE_HOST_SYSTEM_NAME = "Linux"
CMAKE_HOST_UNIX = 1
CMAKE_HOST_WIN32 = 空
UNIX = 1
WIN32 = 空
# 在 Windows 平台
CMAKE_HOST_SYSTEM_NAME = "Windows"
CMAKE_HOST_UNIX = 空
CMAKE_HOST_WIN32 = 1
UNIX = 空
WIN32 = 1
#判断方法一:通过SYSTEM_NAME
if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux")
message(STATUS "current platform: Linux ")
elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
message(STATUS "current platform: Windows ")
else()
message(STATUS "current platform: unkonw ")
endif()
#判断方法二:通过BOOL值
if(CMAKE_HOST_UNIX)
message(STATUS "current platform: Linux ")
elseif(CMAKE_HOST_WIN32)
message(STATUS "current platform: Windows ")
else()
message(STATUS "current platform: unkonw ")
endif()
指定编译模式
#方法一:Cmake文件中设置变量CMAKE_BUILD_TYPE
set(CMAKE_BUILD_TYPE "Debug")
#方法二:cmake 执行命令的时候设置变量CMAKE_BUILD_TYPE
cmake ./ -DCMAKE_BUILD_TYPE:STRING=Debug
#如果在windows平台
#release版本
cmake --build . --config Release
#debug版本
cmake --build . --config Debug
#如果在linux平台
#release版本
cmake ../ -DCMAKE_BUILD_TYPE=Release
cmake --build .
#Debug版本
cmake ../ -DCMAKE_BUILD_TYPE=Debug
cmake --build .
链接第三方库和头文件包含
#eg:hellow 链接libmymath.a,libtest.a等库,每个库之间以空格分开
target_link_libraries(hellow PUBLIC mymath test)
#指定库位置
target_link_directories(hellow PUBLIC
"/home/jef/work/mymath "
"/home/jef/work/test"
)
#指定lib头文件位置
target_include_directories(hellow PUBLIC
"/home/jef/work/test/include"
"${PROJECT_SOURCE_DIR}/mymath"
)
增加预定义宏
#增加预定义宏,必须-D开头,通常配合option来使用
add_definitions(-DUSE_OPEN_SLL)
#代码中使用
#ifdef USE_OPEN_SLL
#else
#endif
开关选项控制option
通过option的ON和off来控制编译流程
#variable:定义选项名称
#help_text:说明选项的含义
#value:定义选项默认状态,一般是OFF或者ON,除去ON之外,其他所有值都为认为是OFF。
option(<variable> "<help_text>" [value])
eg:如果定义USE_OPEN_SLL=ON
,则定义宏USE_OPEN_SLL
否则不定义
option(USE_OPEN_SLL "USE_OPEN_SLL" ON)
if (USE_OPEN_SLL)
add_definitions(-DUSE_OPEN_SLL)
message(STATUS "USE_OPEN_SLL on")
else ()
message(STATUS "USE_OPEN_SLL off")
endif()
cmake -G "Visual Studio 12 2013 Win64" ../ -DUSE_OPEN_SLL=ON
添加动态库or静态库
#根据宏的定义决定使用动态库或者静态库
#如果定义了MYMATH_STATIC则使用静态库,否则使用动态库
#add_library 表示该target是一个库工程
#add_executable表示该target是一个可执行文件工程
option(MYMATH_STATIC "user mymath static lib" ON)
if(MYMATH_STATIC)
add_library(mymath STATIC mymath.cpp)
else()
add_library(mymath SHARED mymath.cpp)
endif()
cmake -G "Visual Studio 12 2013 Win64" ../ -DUSE_MYMATH=OFF
指定项目依赖项(指定项目生成顺序)
#例如当前项目hellow依赖mymath.lib
add_dependencies(hellow mymath)
执行生成事件
参数 | 含义 |
---|---|
PRE_BUILD | 在目标中执行任何其他规则之前运行 |
PRE_LINK | 在编译源代码之后,链接二进制文件或库文件之前运行 |
POST_BUILD | 在目标内所有其他规则均已执行后运行 |
#在VS中我们经常会执行生成后事件,例如生成后复制文件
add_custom_command(TARGET hellow POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PROJECT_BINARY_DIR}/bin/mymath.dll ${PROJECT_SOURCE_DIR}/bin/mymath.dll)
指定二进制文件或者库文件生成位置
#一定要在设置target之前设置,否则无效
#二进制文件位置
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
#库文件位置
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
设置Target的生成目标名称
#修改输出文件的名称,默认和target同名
set_target_properties(hellow PROPERTIES OUTPUT_NAME main)
文件分类source_group
通过上述生成的工程文件没有分类,并且没有包含头文件。下面我们将按照目录结构进行分类(不使用lib)。
注意涉及函数file和source_group
#获取当前目录下所有的头文件并进行分类
#GLOB-只索引当前目录,不包括子目录
#GLOB_RECURSE-索引当前目录以及包括子目录
file(GLOB project_header_files ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
#进行头文件,只能是相同类型的文件,否则会失败
source_group("Header Files" ${project_header_files})
#获取mymath目录下的头文件和源文件并且进行分类
#获取mymath目录下所有的文件这里并没有使用aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}/mymath" maths_file)
file(GLOB math_header_files ${CMAKE_CURRENT_SOURCE_DIR}/mymath/*.h)
file(GLOB math_src_files ${CMAKE_CURRENT_SOURCE_DIR}/mymath/*.cpp)
#source_group有两种写法
#自己定义folder,名称
#source_group("mymath" FILES ${math_header_files} ${math_src_files})
#根据目录结构截取生成目录名称 files的绝对路径去掉${CMAKE_CURRENT_SOURCE_DIR},即拼成目录名称
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${math_src_files} ${math_header_files})
#最后一步也是最重要的一步,一定要添加到target里面,否则也不会展示
add_executable(hellow ${all_file} ${project_header_files} ${math_header_files} ${math_src_files})
cmake -G "Visual Studio 12 2013 Win64" ../
此时可以看到文件已经分类,层级分明了。
配置项目版本号和信息
#设置项目名称和版本
project(Tutorial VERSION 1.0)
#配置头文件传递版本号给源代码
#配置宏定义,配置默认值为ON,如果想关闭 cmake -DUSE_MYMATH=OFF
#option 除了ON之外其他的都默认为OFF
option(USE_MYMATH "user mymath.lib" ON)
#传递字符串给配置文件
set(var_Key "hellow word")
#传递INT给配置文件
set(INT_Key 1)
#输入文件 输出文件
configure_file(hellow_ver.h.in hellow_ver.h)
#由于配置好的文件hellow_ver.h将被写入二进制目录
#所以我们必须将该目录添加到 include 文件的搜索路径中
target_include_directories(hellow PUBLIC
"d:/test/include"
"${PROJECT_SOURCE_DIR}/mymath"
"${PROJECT_BINARY_DIR}"
)
hellow_ver.h.in
//配置的项目版本号
#define hellow_VERSION_MAJOR @hellow_VERSION_MAJOR@
#define hellow_VERSION_MINOR @hellow_VERSION_MINOR@
//宏定义
#cmakedefine USE_MYMATH
//注意:${}之间的名称要与cmakedefine后的变量名一样
//变量名称和cmakeLists.txt设置的变量名称相同
#cmakedefine var_Key "@var_Key@”
#cmakedefine INT_Key @INT_Key@
运行之后生成的hellow_ver.h
如果想取消USE_MYMATH
可以使用 -DUSE_MYMATH=OFF
cmake -G "Visual Studio 12 2013 Win64" ../ -DUSE_MYMATH=OFF
CMake Demo
下面将建立一个简单的Demo,展示一下CMake的简单应用。该工程名称叫cmakedemo
,依赖一个mymath
库。文件目录结构如下:
project/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(cmakedemo VERSION 1.0)
option(USE_MYMATH "my math as a lib to use" OFF)
#指定C++执行标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED true)
set(CMAKE_BUILD_TYPE "Debug")
#设置输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
#so库文件位置
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
#lib文件位置,windows上有效,linux上无效
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
#所有源文件
aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}" src_files)
if(NOT USE_MYMATH)
aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}/mymath" math_src_files)
else()
set(math_src_files "")
add_subdirectory(mymath)
endif()
#所有头文件
#GLOB-只索引当前目录,不包括子目录
#GLOB_RECURSE-索引当前目录以及包括子目录
file(GLOB_RECURSE header_files ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
#文件分类,必须加入到executable,否则不会分类
#source_group("Source Files" ${src_files} ${math_src_files})
#source_group("Header Files" ${header_files})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${src_files} ${math_src_files} ${header_files})
#打印文件
message(STATUS "src_files= ${src_files} ${math_src_files}")
message(STATUS "header_files= ${header_files}")
#添加生成文件
add_executable(main ${src_files} ${math_src_files} ${header_files})
#增加头文件目录
target_include_directories(main PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/mymath"
)
message(STATUS "include_directories ${CMAKE_CURRENT_SOURCE_DIR}/mymath")
if(USE_MYMATH)
#添加连接库
target_link_libraries(main PUBLIC mymath)
#添加库目录,这个可以省略,默认会包含进去
#target_link_directories(main PUBLIC ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${BUILD_TYPE})
endif()
project/mymath/CMakeLists.txt(注意子CMake不要和根CMake变量名称重复)
#如果是win则需要定义export接口
if(CMAKE_HOST_WIN32)
add_definitions(-DMYPATH_EXPORT)
endif()
#所有源文件
aux_source_directory("${CMAKE_CURRENT_SOURCE_DIR}" math_src_files)
#所有头文件
#GLOB-只索引当前目录,不包括子目录
#GLOB_RECURSE-索引当前目录以及包括子目录
#注意变量名不要和其他CMake文件重复
file(GLOB_RECURSE math_header_files ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
message(STATUS "mymath CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "mymath math_src_files ${math_src_files}")
message(STATUS "mymath math_header_files ${math_header_files}")
add_library(mymath SHARED ${math_src_files} ${math_header_files})
由于linux下的库不需要像windows那样export所以作为动态库的时候,需要进行环境处理。
project/mymath/mymath.h
#pragma once
#ifdef _WIN32
#ifdef MYPATH_EXPORT
#define MYPATH_EXPORT_API extern "C" __declspec(dllexport)
#else
#define MYPATH_EXPORT_API extern "C" __declspec(dllimport)
#endif
#else
#define MYPATH_EXPORT_API
#endif // _WIN32
MYPATH_EXPORT_API int add(int a,int b);
cmake ../ -DUSE_MYMATH=ON
cmake --build ./
本文参考:
CMake 教程,至于CMake的安装和测试将在下一章讲解。
CMake判断Release还是Debug环境
函数简单说明
对于CMake,可能存在两种函数:xxxxx和target_xxxx。下面以
link_libraries
和target_link_libraries
来做讲解。
link_libraries
:针对全局的,即:CMake的所有模块都会添加该库。
target_link_libraries
:针对指定目标的,只有指定目标包含该库。
例如CMake包含连个目标A
和B
,此时如果通过link_libraries(ws2_32 test)
包含ws2_32.lib
和test.lib
那么A
和B
都将包含这两个库。但是如果通过target_link_libraries(A PUBLIC ws2_32 test)
那么此时仅仅目标A包含这两个库。