一、What(CMake是什么)
CMake(Cross platform Make)是一个开源的跨平台的自动化构建(编译)工具,可以用简单的语句来描述所有平台的编译过程,并生成各个平台下对应的工程文件,它并不依赖于某特定的编译器
二、Why(为什么要使用CMake)
- 跨平台
- 命令使用简单
- 所有的配置一目了然,很直观
三、How(怎么使用CMake)
1、第一项肯定是下载CMake了,这里以Windows为例
2、CMake的所有操作都是在CMakeLists.txt里面完成的,所以主要就是完成CMakeLists.txt文件了
四、CMake的使用
1、一个最简单的cmake
在目录下面有一个main.cpp和一个CMakeLists.txt文件
main.cpp内容如下:
#include <iostream>
int main(void) {
std::cout << "cmake test" << std::endl;
getchar();
return 0;
}
然后CMakeLists.txt内容如下:
# 设置cmake的最小版本
cmake_minimum_required(VERSION 3.6)
# 设置项目名称
project(cmake_test)
# 设置C++的版本号
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 搜集当前目录下的源文件
aux_source_directory(. SRCS)
# 生成可执行程序
add_executable(cmake_exec ${SRCS})
打开cmd,输入命令如下
md build
cd build
cmake ..
然后会生成一个sln文件,如下图所示
双击打开这个sln,然后将cmake_exec设置为默认启动项编译运行即可
2、设置vs默认启动项,加在add_executable后面
# 设置vs默认启动项
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cmake_exec)
这个时候删除build目录,然后再重新来一次
3、执行cmake的时候执行编译器版本和输出路径
cmake -G"Visual Studio 14 2015" CMakeLists.txt -B./platforms_project/win32
-G:表示指定编译器的版本
-B:表示要输出的路径
4、到此一个最简单的cmake程序就完了
五、CMake常用操作指令
1、设置CMake的版本
cmake_minimum_required(VERSION 3.6)
2、设置当前项目的名称
project(project_name)
3、设置当前项目中使用C++的版本
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
4、设置要包含头文件的路劲
include_directories(directories)
5、设置要链接库的路径
link_directories(directories)
6、设置输出路径
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY path)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY path)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY path)
set(EXECUTABLE_OUTPUT_PATH path)
例子:
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../output/bin/${Configuration})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../output/bin/${Configuration})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../output/bin/${Configuration})
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/../output/bin/${Configuration})
7、搜集某个目录下面的源文件
aux_source_directory(${CMAKE_SOURCE_DIR}/src SRCS)
8、生成静态库或动态库
add_library(library_name ${source}) // 默认静态
add_library(library_name SHARED/STATIC ${source})
9、生成可执行程序
add_executable(exec ${SRCS})
10、设置要链接的库
target_link_libraries(name link_name)
比如
target_link_libraries(cmake_exec test_lib)
11、设置链接时候的一些参数
add_link_options(options)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} options")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} options")
例如(Windows下):
# 增量连接
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /IGNOREIDL /INCREMENTAL")
# 设置def
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEF:${CMAKE_CURRENT_SOURCE_DIR}/dll/wemeet.def")
12、添加子目录
add_subdirectory(subdirectory)
如果不是子目录应该怎么办
add_subdirectory(directory target)
13、划分目录结构
source_group
一般的用法如下:
source_group(common REGULAR_EXPRESSION *.*/common/*.*)
这样会将common目录下面的文件全部分组为common
14、执行系统命令
execute_process
比如运行一个python脚本:
execute_process(COMMAND python copy.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/scripts)
备注:
WORKING_DIRECTORY :表示在哪个目录下执行
copy.py里面就是把一个文件从一个目录拷贝到另外一个目录
15、字符串比较
STREQUAL 相等比较
STRLESS 小于
STRGREATER 大于
使用方法:if("hello" STREQUAL "hello")
16、输出调试打印信息,类似于printf
message("hello")
17、判断某个变量是否设置了
if(DEFINED var)
判断某个环境变量是否存在
if(ENV{VAR})
设置环境变量
set(ENV{var} var)
18、判断文件是否存在
if(EXISTS file)
19、判断CMake创建的逻辑目标是否存在
if(TARGET name)
逻辑目标:
add_executable(exec ${SRCS})
这里的exec就是逻辑目标,所以可以写为
if(TARGET exec)
20、设置依赖
ADD_DEPENDENCIES
表示某个工程必须在它之前被执行
比如:
target_link_libraries(cmake_exec test_lib)
表示cmake_exec要连接test_lib,那么我们就要保证test_lib必须在cmake_exec之前生成,这个时候就可以使用ADD_DEPENDENCIES
ADD_DEPENDENCIES(cmake_exec test_lib)
21、设置自定义命令,具体操作可查看文档
add_custume_command
举个例子,比如我们有的时候想在编译之前、链接之前或者在所有项目生成之后执行某些命令,可以这么做
PRE_BUILD:编译之前
PRE_LINK:链接之前
POST_BUILD:所有操作执行之后
具体操作可参考:
add_custom_command(TARGET wemeet
PRE_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/scripts/copy_file.bat
COMMENT "copy file")
22、设置fpic
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
23、创建函数
macro(<name> [arg1])
endmacro(<name>)
比如设置一个函数,按照目录对源文件进行分组
根据目录划分target
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
macro(set_target_folder_if_exist target folder)
if(TARGET ${target})
set_target_properties(${target} PROPERTIES FOLDER ${folder})
endif()
endmacro()
调用:
set_target_folder_if_exist(cmake_exe cmake)
24、设置警告的等级
add_definitions("/W4 /WX")
也就是将警告视为错误
25、cmake判断平台
Windows平台判断:
if(WIN32)
endif()
LINUX平台判断:
if(UNIX AND NOT APPLE AND NOT ANDROID)
endif()
MAC平台判断:
if(APPLE)
endif()
26、cmake设置编译为release
# SET(CMAKE_BUILD_TYPE Release) # 这条命令在Windows下面设置不生效
SET(CMAKE_CONFIGURATION_TYPES "Release" CACHE STRING "" FORCE)
27、cmake 区别平台,目前 Windows,Linux,Android,Mac 平台都可以很好的区分,但是没有想到怎么区分 iOS
if(WIN32)
message("WIN32")
elseif(UNIX AND NOT APPLE AND NOT ANDROID)
message("LINUX")
elseif(ANDROID)
message("ANDROID")
elseif(APPLE)
message("APPLE")
else()
message("else")
endif()
下面是一些在Windows上常用到的指令
27、设置unicode编码
add_definitions(-DUNICODE -D_UNICODE)
28、release模式下生成pdb文件
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF")
29、设置多处理器编译
add_definitions("/MP")
30、设置默认启动项
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT app_name)
31、引入外部的工程
include_external_msproject
比如外面的一个工程是vcxproj的,但是要在cmake里面使用到就可以这么做
include_external_msproject(cmake_test ${CMAKE_SOURCE_DIR}/../../test/cmake_test.vcxproj)
32、使用预编译头
macro(use_precompiled_header TARGET HEADER_FILE SRC_FILE)
get_filename_component(HEADER ${HEADER_FILE} NAME)
if (MSVC AND NOT NMAKE AND NOT OGRE_UNITY_BUILD)
set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS /Yu"${HEADER}")
set_source_files_properties(${SRC_FILE} PPROPERTIES COMPILE_FLAGS /Yc"${HEADER}")
elseif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX)
endif ()
endmacro()
比如:
use_precompiled_header(cmake_exe ${CMAKE_SOURCE_DIR}/src/stdafx.h
${CMAKE_SOURCE_DIR}/src/stdafx.cpp)
33、当把警告视为错误的时候,屏蔽掉一些警告
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4100 /wd4505 /wd4996")
34、强制指定Windows sdk的版本号,注意要写在project之前,在设置完cmake_minimum_required之后就需要指定,不然可能会不生效
set(CMAKE_SYSTEM_VERSION "10.0.17763.0" CACHE STRING INTERNAL FORCE)
set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION "10.0.17763.0" CACHE STRING INTERNAL FORCE)
四、CMake 中 macro、function
1、ARGC,ARGV,ARGN 的含义
ARGC:记录传入的参数个数
ARGV:包含所有传入参数的 list
ARGN:包含传入参数的 list,这个 list 是指 macro、function 指定参数之后的所有参数
测试代码1
function(file_source_group_1)
message("=== file_source_group_1 ===")
message(${ARGC})
message(${ARGV})
message(${ARGN})
message("=== file_source_group_1 ===")
endfunction(file_source_group_1)
file_source_group_1(hello include/hello.hpp src/hello.cpp)
输出结果1
=== file_source_group_1 ===
3
helloinclude/hello.hppsrc/hello.cpp
helloinclude/hello.hppsrc/hello.cpp
=== file_source_group_1 ===
可见,如果 function 后面不指定任何参数,那么 ARGV 和 ARGN 结果是一样的
测试代码2
function(file_source_group_2 hello)
message("=== file_source_group_2 ===")
message(${ARGC})
message(${ARGV})
message(${ARGN})
message("=== file_source_group_2 ===")
endfunction(file_source_group_2)
file_source_group_2(hello include/hello.hpp src/hello.cpp)
输出结果2
=== file_source_group_2 ===
3
helloinclude/hello.hppsrc/hello.cpp
include/hello.hppsrc/hello.cpp
=== file_source_group_2 ===
在 function(file_source_group_2 hello) 函数里面多指定了一个参数,标识第一个传入的参数用 hello 捕获,这个时候 ARGV 还是所有的参数 list,但是 ARGN 是除了第一个参数的所有参数了
2、使用 function 自动生成 VS 的目录
function(add_source_group file_source)
# 如果给定路径是一个绝对路径
if (IS_ABSOLUTE "${file_source}")
file(RELATIVE_PATH source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${file_source}")
else()
set(source_rel "${file_source}")
endif()
get_filename_component(source_dir "${source_rel}" PATH)
# string(REPLACE "/" "\\" dir_name "${source_path}")
source_group("${source_dir}" FILES "${file_source}")
endfunction()
function(file_source_group)
foreach(file_source IN ITEMS ${ARGN})
add_source_group(${file_source})
endforeach()
endfunction(file_source_group)
set(DEMO_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/include/hello.hpp
${CMAKE_CURRENT_SOURCE_DIR}/src/hello.cpp
${CMAKE_CURRENT_SOURCE_DIR}/include/123/test.h
)
file_source_group(${DEMO_SOURCE})
四、CMake常见的宏
1、CMAKE_SOURCE_DIR :工程顶层目录所在的路径
2、CMAKE_CURRENT_SOURCE_DIR:当前CMakeLists.txt所在的路径,建议使用CURRENT_SOURCE,不建议使用SOURCE_DIR,因为如果主工程目录改变了,SOURCE_DIR也就会改变,很容易造成编译错误
3、CMAKE_BINARY_DIR:工程编译时的路径
4、PROJECT_NAME:工程名字
5、EXECUTABLE_OUTPUT_PATH:可执行文件的输出路径
6、LIBRARY_OUTPUT_PATH:库的输出路径
五、CMake和conan一起使用
cmake和conan搭配一起使用,具体代码如下
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
.........
target_link_libraries(name ${CONAN_LIBS})
六、编译
Linux
cmake . // .表示CMakeLists.txt所在的路径,也可以新建一个build目录去编译
make
Windows
cmake .
然后使用vs打开生成的sln文件
cmake -G "Visual Studio 15 Win64" .
编译 64 位,默认生成是 32 位
Mac
cmake -G "XCode" .
然后使用XCode打开
七、写在最后
有错误欢迎指正,欢迎一起交流,谢谢!