CMakeake是什么,是用来做什么的,以及如何?
cmake是一个用于管理和构建源代码的工具。
cmake广泛应用于编译C/C++代码,其实也可以用于编译其他语言。
cmake可实现跨平台构建、编译c/c++源码。
cmake除了用于编译外还提供了ctest, cpack用于测试,安装或打包。
第一章适用于新手,在不了解cmake语言的情况下可快速构建自己的项目
.
├── include
│ ├── hea2.h
│ └── head1.h
├── main.c
└── src
├── fun1.c
└── fun2.c
在根目录下新建CMakeLists.txt文件:
# 新项目必须的
cmake_minimum_required(VERSION 3.10)
# 新项目必须的
project(tutorial VERSION 1.0)
# 生成可执行文件必须的
add_executable(main
main.c
src/fun1.c
src/fun2.c
)
# 指定头文件搜索路径
target_include_directories(main PUBLIC
./include
)
接下来,在根目录下新建build目录,然后运行cmake命令
mkdir build && cd build
cmake .. # 生成构建系统
cmake --build . #执行构建
和1的区别仅为在CMakeLists.txt中把依赖添加进去:
target_link_libraries(main
libname1
libname2
)
target_link_directories(main PUBLIC
./lib
)
然后执行cmake 命令即可
若需要将源文件编译成库而不是可执行文件,则仅需修改CMakeLists.txt文件即可
生成静态库:
add_library(main STATIC # STATIC可写可不写
main.c
src/fun1.c
src/fun2.c
)
生成动态库:
cmake_minimum_required(VERSION 3.10)
project(tutorial VERSION 1.0)
add_library(main SHARED
main.c
src/fun1.c
src/fun2.c
)
target_include_directories(main PUBLIC
./include
)
通过cmake 命令生成构建系统
参数 | 含义 |
---|---|
-S | 指定源文件根目录,必须包含一个CMakeLists.txt文件 |
-B | 指定构建目录,构建生成的中间文件和目标文件的生成路径 |
-D | 指定变量,格式为-D =,-D后面的空格可以省略 |
指定当前目录为源文件目录,其中包含CMakeLists.txt文件,使用build目录作为构建目录
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DAUTHOR=me
使用-D设置的变量在CMakeLists.txt中生效,可以设置cmake的内置支持的一些变量控制构建的行为;当然也可以使用自定义的变量,在CMakeLists.txt中自行判断做不同的处理。
cmake --build .
–buid 后面跟构建系统所在目录
如果在CMakeLists.txt中添加如下包含测试的功能则在执行构建后可进行测试
enable_testing()
# include(CTest) # 添加CTest模块, 可代替enable_testing
add_test(NAME normal_test COMMAND main )
set_tests_properties(normal_test PROPERTIES PASS_REGULAR_EXPRESSION "yes") # main程序输出“yes”视为通过
在CMakeLists.txt中添加如下内容:
install(TARGETS binary_nam DESTINATION ./install/bin)
install(FILES "include/head.h" DESTINATION ./install/inc)
install(EXPORT Json FILE FILE Json.cmake DESTINATION ./install/lib)
然后执行
cmake --install .
或者
cmake --install . --prefix "~/myuser/installdir"
打包部分测试未通过
CMake中使用set
和unset
命令设置或者取消设置变量
一般变量
设置的变量可以是字符串,数字或者列表(直接设置多个值,或者使用分号隔开的字符串格式为"val1;val2;val3")
# set var
set(val1 a)
set(val2 "hello world")
# set list
set(list1 a b) # saved as "a;b"
set(list2 a;b)
set(list3 "a;b")
set(num 90) # saved as string, but can compare with other number string
set(falg ON) # bool value
- 如果要设置的变量值包含空格,则需要使用双引号或者使用""转义,否则可以省略双引号;
- 如果设置多个值或者字符串值的中间有";“,则保存成list,同样是以”;"分割的字符串;
- 变量可以被list命令操作,单个值的变量相当于只有一个元素的列表;
- 引用变量:${
},在if()条件判断中可以简化为只用变量名 。
Cache变量
Cache变量(缓存条目,cache entries)的作用主要是为了提供用户配置选项,如果用户没有指定,则使用默认值,设置方法如下:
# set(<variable> <value>... CACHE <type> <docstring> [FORCE])
set(CACHE_VAR "Default cache value" CACHE STRING "A sample for cache variable")
- 主要为了提供可配置变量,比如编译开关;
- 引用CACHE变量:$CACHE{}
- Cache变量会被保存在构建目录下的CMakeCache.txt中,缓存起来之后是不变的,除非重新配置更新
环境变量
修改当前处理进程的环境变量,设置和引用格式为:
# set(ENV{} [])
set(ENV{ENV_VAR} "$ENV{PATH}")
message("Value of ENV_VAR: $ENV{ENV_VAR}")
和CACHE变量类似,要引用环境变量,格式为:$ENV{}。
常量:
对于变量,只要其值不是常量中为False的情形,则均视为True。
message([] "message text"...)
mode:
set(emp_str "")
if (NOT emp_str AND flag and num less 50 and not not_define_var)
message(STATUS "first ")
elseif (emp_str)
message(STATUS "second")
else()
message("third)
endif()
此外还有 for/while等
list(APPEND )
CMake的file命令支持的操作比较多,可以读写、创建或复制文件和目录、计算文件hash、下载文件、压缩文件等等。
file(GLOB_RECURSE ALL_SRC
src/module1/*.c
src/module2/*.c
)
GLOB_RECURSE表示执行递归查找,查找目录下所有符合指定正则表达式的文件。
使用configure_file命令可以将配置文件模板中的特定内容替换,生成目标文件。 输入文件中的内容@VAR@或者${VAR}在输出文件中将被对应的变量值替换。 使用方式为:
PROJECT(tutorial VERSION 1.0.1.1)
configure_file(version.h.in "${PROJECT_BINARY_DIR}/version.h")
version.h.in
#define VERSION "@VERSION@"
#define MAJOR @tutorial_VERSION_MAJOR@
#define MINOR @tutorial_VERSION_MINOR@
#define PATCH @tutorial_VERSION_PATCH@
使用execute_process命令可以执行一条或者顺序执行多条系统命令,对于需要使用系统命令获取一些变量值是有用的。比如获取当前仓库最新提交的commit的commit id:
execute_process(COMMAND bash "-c" "git rev-parse --short HEAD" OUTPUT_VARIABLE COMMIT_ID)
通过find_library在指定的路径和相关默认路径下查找指定名字的库,常用的格式如下:
find_library ( name1 [path1 path2 ...])
找到的库就可以被其他target使用,表明依赖关系。
include命令将cmake文件或者模块加载并执行
include(CPack) # 开启打包功能
include(CTest) # 开启测试相关功能
CMake自带有很多有用的模块,可以看看官网的链接:cmake-modules,对支持的功能稍微有所了解,后续有需要再细看文档。当然,如果感兴趣,也可以直接看CMake安装路径下的目录CMake\share\cmake-\Modules中的模块源文件。
关于CMake脚本源文件的示例位于路径:cmake/script_demo.cmake,可以使用cmake -P cmake/script_demo.cmake执行查看结果; 关于配置文件生成的操作在项目根目录的CMakeLists.txt中也有示例。
main CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(tutorial VERSION 2.3)
set(binary_name haha)
add_executable(
${binary_name}
main.cpp
)
message("project_source_dir: ${PROJECT_SOURCE_DIR}, binary: ${PROJECT_BINARY_DIR}")
configure_file(tutorialconfig.h.in tutorialconfig.h) # comm cmake and src
######################### change c++ standard ################################
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_17)
target_link_libraries(${binary_name} PUBLIC tutorial_compiler_flags)
#-------------------------------------------------------------------------------
add_subdirectory(libs/json) # subproj CMakeLists.txt
target_link_libraries(${binary_name} PUBLIC Json)
target_include_directories(${binary_name} PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/libs/json"
"${PROJECT_SOURCE_DIR}/libs/math"
)
target_link_options(${binary_name} PUBLIC "-D_BUG_")
# cmake . -DUSE_MYLIB=OFF
option(USE_MYLIB "Use providesd imt XXX" ON)
if (USE_MYLIB)
target_compile_definitions(${binary_name} PRIVATE "USE_MYLIB")
message("oo USE_MYLIB macro used")
else()
message("oo USE_MYLIB macro not used")
endif()
######################### Generator Expression ###############################
set(gcc_like_cxx "$" )
set(msvc_cxx "$" )
target_compile_options(
tutorial_compiler_flags INTERFACE
# "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
# "$<${msvc_cxx}:-W3>"
# 仅在构建时启用,在作为项目的模块时不启用
"$<${gcc_like_cxx}:$>"
"$<${msvc_cxx}:$>"
)
########################### install & ctest ############################
# cmake --install .
# cmake --install . --prefix "/home/myuser/installdir"
install(TARGETS ${binary_name} DESTINATION ${PROJECT_SOURCE_DIR}/install/bin)
install(FILES "${PROJECT_BINARY_DIR}/tutorialconfig.h" DESTINATION ${PROJECT_SOURCE_DIR}/install/include)
# install(EXPORT Json FILE Json.cmake DESTINATION ${PROJECT_SOURCE_DIR}/install/lib)
enable_testing() # enable testing
add_test(NAME Runs COMMAND ${binary_name} 25)
add_test(NAME Usage COMMAND ${binary_name} 4)
set_tests_properties(Usage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
add_test(NAME StandardUse COMMAND ${binary_name} 4)
set_tests_properties(StandardUse PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2")
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endfunction()
do_test(${binary_name} 4 "4 is 2")
do_test(${binary_name} 5 "5 is 2")
do_test(${binary_name} 6 "6 is 2")
do_test(${binary_name} 7 "7 is 2")
do_test(${binary_name} 8 "8 is 2")
do_test(${binary_name} 9 "9 is 2")
############################# Dashboard ######################
include(CTest) # include CTest module
if(UNIX)
message(hello------------)
endif()
########################## system introspection ##############################
include(CheckCXXSourceCompiles) # include this module, check_XXX_compiles
check_cxx_source_compiles(
"
#include
int main() {
std::log(1.0);
return 0;
}
"
HAVE_LOG
)
check_cxx_source_compiles(
"
#include
int main() {
std::exp(1.0);
return 0;
}
"
HAVE_EXP
)
if (HAVE_LOG AND HAVE_EXP)
target_compile_definitions(${binary_name} PRIVATE "HAVE_LOG" "HAVE_EXP")
message("support exp and log function")
else()
message("not support exp or log function")
endif()
######################### packaging && installer ? failed #############################
# This module will include any runtime libraries that are needed by the project for the current platform
include(InstallRequiredSystemLibraries)
# set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${tutorial_VERSION_MINOR}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
# then cpack / cpack -G ZIP -C Debug -G:generator,-C:multi-config specify configuration
sub CMakeLists.txt
include(MakeTable.cmake) # include and execute
# cmake_minimum_required(VERSION 3.0)
# project(cppjsonrw VERSION 1.9)
add_library(Json json_reader.cc json_value.cc json_writer.cc ${CMAKE_CURRENT_BINARY_DIR}/table.h)
target_include_directories(
Json
INTERFACE # consumers require but the producer doesn't
# if not exprot
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
# else export
# $
# $
)
target_link_libraries(Json PUBLIC tutorial_compiler_flags)
message("Jproject source dir: ${PROJECT_SOURCE_DIR}")
message("Jcmake source dir: ${CMAKE_SOURCE_DIR}")
message("Jcmake current source dir: ${CMAKE_CURRENT_SOURCE_DIR}")
####################### static or dynamic lib ################################
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
########################### install ############################
set(installable_libs tutorial_compiler_flags Json)
# if (TARGET Json)
# list(APPEND installable_libs Json)
# endif()
install(TARGETS ${installable_libs} EXPORT Json DESTINATION ${PROJECT_SOURCE_DIR}/install/lib)
install(FILES json.h DESTINATION ${PROJECT_SOURCE_DIR}/install/include)
####################### custom command and generated file ####################
# 配置项目
project(tutorial VERSION 1.0.1 LANGUAGES C CXX)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 17)
通过命令add_compile_options可为所有编译器配置选项(同时对多个编译器生效);通过设置变量CMAKE_C_FLAGS可以配置C编译器选项;设置变量CMAKE_CXX_FLAGS可配置C++编译器的编译选项。
add_compile_options(-Wall -g)
// add_compile_options("-g;-Wall")
set(CMAKE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -std=c99")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe -std=c++17")
cmake --build . -DCMAKE_BUILD_TYPE=Debug
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
名 | |
---|---|
PROJECT_SOURCE_DIR | 最近的CMakeLists.txt中含project命令所在目录 |
PROJECT_BINARY_DIR | 最近的含有project的对应构建树中所在目录 |
CMAKE_SOURCE_DIR | 顶层CMakeLists.txt所在目录 |
CMAKE_BINARY_DIR | 对应构建树顶层目录 |
CMAKE_CURRENT_SOURCE_DIR | 当前CMakeLists.txt所在目录 |
CMAKE_CURRENT_BINARY_DIR | 对应构建树当前所在目录 |
https://cmake.org/cmake/help/latest/
cmake-commands
cmake-modules(7)-https://cmake.org/cmake/help/latest/manual/cmake-modules.7.html