概述
- cmake/meson //生成特定平台的makefile(.makefile .sln .ninja)
- make/ninja/vcbuild //根据makefile编译exe (.so .out .dll .exe)
CMake
- 自定义变量
set (NAME main.c)
${NMAE} #引用变量,if判断除外
单目录
- CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8) - 项目信息
project (Demo2) - 指定生成目标
add_executable(Demo main.cc MathFunctions.cc)
Tips: 查找当前目录下的所有源文件并将名称保存到 DIR_SRCS 变量 aux_source_directory(. DIR_SRCS)
多目录
- 添加 math 子目录
add_subdirectory(math) - 指定生成目标
add_executable(Demo main.cc) - 添加链接库
target_link_libraries(Demo MathFunctions) - 生成链接库 add_library (MathFunctions ${DIR_LIB_SRCS})
在该文件中使用命令 add_library 将 src 目录中的源文件编译为静态链接库。
自动生成头文件
- 加入一个配置头文件(*.h.in),根据CMake的option来控制编译参数。
安装
1. TARGETS
install (TARGETS T1 T2 RUNTIME DESTINATION binary LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
Tips:
| windows | linux | |
|---|---|---|
| RUNTIME | *.exe *.dll | *.out |
| LIBRARY | *.so | |
| ARCHIVE | *.lib | *.a |
Of course,we can install all targets in one Directory such as:
install (TARGETS T1 T2 DESTINATION binary)
install (TARGETS MathFunctions DESTINATION "C:/Users/test/Desktop" )
2. DIRECTORY
install(DIRECTORY "${MVNCDIR}/lib/" DESTINATION "lib"
3. FILES
install(FILES "${CMAKE_BINARY_DIR}/output/bin/ssleay32.dll" DESTINATION "bin")
4. 使用wix 生成一键安装包
set(CPACK_GENERATOR WIX)
set(CPACK_WIX_UPGRADE_GUID "F9AAAAE2-D6AF-4EA4-BF46-B3E265400CC7")
set(CPACK_WIX_PATCH_FILE "${CMAKE_SOURCE_DIR}/CPackWixPatches.xml")
include (InstallRequiredSystemLibraries)
include (CPack)
FindPkgConfig
使用pc文件获取相关的目录信息, 使用pkg-config,三个常用字符串。
CMake
PKG_CONFIG_FOUND ... if pkg-config executable was found
PKG_CONFIG_EXECUTABLE ... pathname of the pkg-config program
PKG_CONFIG_VERSION_STRING ... the version of the pkg-config program found
(since CMake 2.8.8)
常用的三个函数:
- pkg_get_variable()
示例:pkg_get_variable(GI_GIRDIR gobject-introspection-1.0 girdir)
将gobject-introspection-1.0中的girdir变量读取到GI_GIRDIR
- pkg_check_modules() 示例:pkg_check_modules (GLIB2 glib-2.0) 检查glib-2.0 并自动设置以下变量
<XXX>_FOUND ... set to 1 if module(s) exist
<XXX>_LIBRARIES ... only the libraries (without the '-l')
<XXX>_LINK_LIBRARIES ... the libraries and their absolute paths
<XXX>_LIBRARY_DIRS ... the paths of the libraries (without the '-L')
<XXX>_LDFLAGS ... all required linker flags
<XXX>_LDFLAGS_OTHER ... all other linker flags
<XXX>_INCLUDE_DIRS ... the '-I' preprocessor flags (without the '-I')
<XXX>_CFLAGS ... all required cflags
<XXX>_CFLAGS_OTHER ... the other compiler flags
- pkg_search_module
与
pkg_check_modules类似,匹配一个module成功后,就退出。 pkg_search_module (BAR libxml-2.0 libxml2 libxml>=2)
变量
第一个读取的是环境变量,通过export name= 传入
第二个读取的是局部变量,通过-D参数传入
message(STATUS "env $ENV{NAME}")
message(STATUS "yy ${NAME}")
find_package()
使用*.cmake 信息获取相关的配置信息
cmake本身不提供任何搜索库的便捷方法,所有搜索库并给变量赋值的操作必须由cmake代码完成。find_package采用两种模式搜索库:
- Module模式:搜索CMAKE_MODULE_PATH指定路径下的FindXXX.cmake文件。(这个内部大部分调用的find_lib 函数,查找/usr/lib 以及/usr/local/lib目录下的内容)
- Config模式:搜索XXX_DIR指定路径下的XXXConfig.cmake文件。
- CMAKE_MODULE_PATH 以及 XX_DIR不一定是一个路径,可能是多个路径
- cmake先执行Module模式,后config模式
- 要么设置CMAKE_MODULE_PATH(对应的FindXXX.cmake),要么设置XXX_DIR(对应XXXConfig.cmake)
- CMAKE中可以使用命令行参数传递变量,也支持使用全局环境变量的形式传递,建议使用前者。
使用示例
方法1:使用json-c-config, 仅仅是对当前package有效。
find_package("json-c") //在CMakelists.txt
cmake .. -Djson-c_DIR=/home/test/install/lib/cmake/json-c //传入 json-c-config.cmake的路径
方法2: 使用json-c-config,所有的find_package以及find_file都会在这个目录下去查找,只需要到install目录,不需要到lib/cmake/jsonc。
find_package("json-c") //在CMakelists.txt
cmake .. -DCMAKE_PREFIX_PATH=/home/test/install/ //传入安装路径
方法3: 使用Findjson-c.cmake,通过指定 `CMAKE_MODULE_PATH`
函数
# basic function
function(doubleIt VALUE)
math(EXPR RESULT "${VALUE} * 2")
message("${RESULT}")
endfunction()
doubleIt("4") # Prints: 8
# with returnvalue
function(doubleItReturn VARNAME VALUE)
math(EXPR RESULT "${VALUE} * 2")
set(${VARNAME} "${RESULT}" PARENT_SCOPE) # Set the named variable in caller's scope
endfunction()
doubleItReturn(RESULT "4") # Tell the function to set the variable named RESULT
message("${RESULT}") # Prints: 8
# with macro
macro(doubleItMac VARNAME VALUE)
math(EXPR ${VARNAME} "${VALUE} * 2") # Set the named variable in caller's scope
endmacro()
doubleItMac(RESULT "4") # Tell the macro to set the variable named RESULT
message("${RESULT}") # Prints: 8
# with list
function(doubleEach)
foreach(ARG ${ARGN}) # Iterate over each argument
math(EXPR N "${ARG} * 2") # Double ARG's numeric value; store result in N
message("${N}") # Print N
endforeach()
endfunction()
doubleEach(5 6 7 8) # Prints 10, 12, 14, 16 on separate lines
- 简单函数与带返回值函数的区别就是带返回值的函数,对PARENT_SCOPE参数进行设置
- 函数与宏定义的区别,就是作用域的不同
- 函数名字都是大小写不敏感
- 使用其他文件中的函数,需要
include,类似c++,find_package内部其实执行的就是include(FindSDL2.cmake),
参考示例 preshing.com/20170522/le…
modern cmake
推荐使用target_include_directories(show PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)而不是include_directories()
因为在当这个target直接给其他库使用时,直接使用这次target就可以了,不需要额外增加包含目录,因为include目录已经在打包在target这个大结构体中。
private,public,interface区别(11,10,01的区别)public=private+interface
INTERFACE 表示这个target不使用这个包含目录,但是会传递给使用它的target
PRIVATE 表示这个target使用这个包含目录,但是不会传递给使用它的目录。
PUBLIC 表示这个target使用这个包含目录,且也传递给使用它的target
调试命令
cmake 调试
cmake --debug-output cmake文件级别调用细节
cmake --trace cmake函数级别调用细节
cmake --trace-expand cmake函数级别调用细节(已经将变量替换)
make 调试
make VERBOSE=1或者make VERBOSE=ON
EXCLUDE_FROM_ALL
正如字面意思,从all(默认)中排除这个target, 需要手动指定。
在以下几种情况中适用
make默认执行的是make all, 但是如果这个项目exclude_from_all,就需要手动指定target才行。make install默认执行的也是make install all, 如果这个项目设置了install exclude_from_all, make install 也不会安装这个项目。但目前没找到单独install 某个target的操作。
设置exclude_from_all还有一种方式,就是add_subdirectory的时候加入,这个目录下所有的都会设置成exclude_from_all。
default output path
这是输出目录,这种可以解决windows 运行需要设置path的问题
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
target类别
- STATIC 静态链接库
- SHARED 动态链接库
- MODULE 动态链接库,但是只能作为plugin使用,例如dlopen等,且不能被其他target link
- OBJECT 源码编译文件集合,没有archive,可以被其他target link, 以及作为源码被其他target编译。
参考代码如下
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)
# usage 1
add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)
# usage 2
add_executable(test_exe test.cpp)
target_link_libraries(test_exe archive)
- IMPORT 直接外部编译好的二进制target,自生不会build 任何文件
参考代码如下
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION /path/to/libfoo.a)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe foo)
gitlab.kitware.com/cmake/commu…
- INTERFACE 一组属性的集合,不依赖磁盘上任何文件(理论上,也可强行设置),自身不会build,但是可以设置属性,以供其他target linked to, 比较适合只有头文件的库,因为他们自生不需要编译,依赖其他库去编译。 stackoverflow.com/questions/3… cmake.org/cmake/help/… mariobadr.com/creating-a-…
导出cmake文件
以供outside project 使用
export
需要搭配BUILD_INTERFACE使用 这里cmake文件使用的build目录
add_library(show SHARED src/show.cpp)
install(TARGETS show EXPORT ExportTarget)# add each target you want export to a export group
target_include_directories(show PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) # use build interface
export(EXPORT ExportTarget FILE ${PROJECT_BINARY_DIR}/ExportTarget.cmake) # export
参考链接 build_interface export
install(EXPORT)
需要搭配INSTALL_INTERFACE使用 这里cmake文件使用的install目录
add_library(show SHARED src/show.cpp)
install(TARGETS show EXPORT ExportTarget) # add each target you want export to a export group
target_include_directories(show PUBLIC $<INSTALL_INTERFACE:include>) # use install interface
install(EXPORT ExportTarget DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake) # install
config
上述两个方案需用用户去include(xxx.cmake),为了支持find_modules()这个函数,需要生成xx-config.cmake文件。其实config.cmake文件内部其实就是include(xxx.cmake)。
include(CMakePackageConfigHelpers)
# 在build下先使用configure_package函数生成一个文件,再使用install file的形式拷贝到对应的目录
configure_package_config_file("xx.cmake.in" ExportTargetConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/cmake)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ExportTargetConfig.cmake" DESTINATION "${CMAKE_INSTALL_PREFIX}/cmake")
VScode
cmake-variants.json用来指定一些项目变量 例如用户自定义的一些变量,多与项目有关。 参考示例: vector-of-bool.github.io/docs/vscode… gist.github.com/nfbot/6de22…
系统默认有
Debug,Release,RelWithDebInfo,MinSizeRel这四种,如果用户在.vscode目录下创建了上述文件,则系统的则被覆盖。
cmake-kits.json用来指定使用哪个编译器或者交叉编译工具 参考示例: vector-of-bool.github.io/docs/vscode…
系统默认有一个
cmake-kits.json,如果用户在.vscode目录下创建了上述文件,则系统与用户的同时存在。 如果修改系统的json文件,其配置应该与具体项目无关。
settings.json也能够传递一些变量 参考示例: vector-of-bool.github.io/docs/vscode…
make
定义变量 objects = main.o kbd.o command.o display.o
使用变量 $objects
目标(target)
target ... : prerequisites ... command
- 文件中的第一个目标文件(target),作为最终目标
- 目标生成的条件:1. target不存在 2. 依赖文件更新过。
依赖(prerequisites)
- 会递归依赖
- 需要将头文件加入依赖,因为头文件不参与编译,头文件的修改可能导致不重新编译
- 依赖并非必须,例如
clean
规则(command)
- 定义任意shell命令
- 规则可进行隐形推导
其他
- 规则前面需要
tab .phony告诉make 这是一个伪目标,不用检查依赖关系,始终运行rules 典型事例就是当文件中存在clean文件时,其rules就不执行- 内置符号的含义
all: library.cpp main.cpp
$@evaluates toall$<evaluates tolibrary.cpp$^evaluates tolibrary.cpp main.cpp$?evaluates to the newer one(updated prerequistites)
- 自动推导,对于.o 文件make会自动识别,并自己推导命令
Ninja
- common build
cmake .. -G Ninja && ninja - show all targets
ninja -t targets - show graph
ninja -t graph mytarget | dot -Tpng -ograph.png - build single target
ninja xx - clean
ninja clean
与make指令很类似,默认多线程编译
windows上使用
- 把Ninja.exe添加到环境变量。
- 把cl.exe 添加到环境变量,建议使用
native tools command prompt
linux上使用
sudo apt instal ninja-build
不同make工具对比
| make | Ninja | msbuild | |
|---|---|---|---|
| OS | Linux | Both | Windows |
| build command | make | ninja | msbuild |
| build file | Makefile | ninja.build | xxx.sln |
| cmake enable | ON | ON | ON |
| advantage | cmake default | cross-platform,fast | cmake default |