cmake是什么
CMake是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。
CMake和makefile
Cmake是用来makefile的一个工具:读入所有源文件之后,自动生成makefile。
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程
- 编写 CMake 配置文件 CMakeLists.txt 。
- 执行命令 cmake PATH 生成 Makefile。其中, PATH 是 CMakeLists.txt 所在的目录。
- 使用 make 命令进行编译。
CmakeList
常用命令
1. 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.4.1)
2. 设置项目名称
project(demo)
3. 指定编译器
CMAKE_CXX_COMPILER | cxx编译器
CMAKE_C_COMPILER | c编译器
4. 设置编译类型
add_executable(demo demo.cpp) # 生成可执行文件
add_library(common STATIC util.cpp) # 生成静态库
add_library(common SHARED util.cpp) # 生成动态库或共享库
add_library 默认生成是静态库,通过以上命令生成文件名字,
在 Linux 下是:
demo
libcommon.a
libcommon.so
5. set
set用于给一般变量,缓存变量,环境变量赋值。
5.1 Set赋值给一般变量
set(FOO “x”)。 #FOO作用域为当前作用域。
set(FOO "x" PARENT_SCOPE) #FOO作用域跳上一级。
5.2 Set赋值给缓存变量
什么是缓存变量,缓存变量可以理解为当第一次运行cmake时,这些变量缓存到一份文件中(即编译目录下的CMakeCache.txt)。当再次运行cmake时,这些变量会直接使用缓存值。缓存变量在整个cmake运行过程中都可以起作用。相当于一个全局变量,我们在同一个 cmake 工程中都可以使用。
当使用CACHE时,且缓存(cache)中没有该变量时,变量被创建并存入缓存(cache)中,如果原缓存(cache)中有该变量,也不会改变原缓存中该变量的值,除非后面使用FORCE。
set(FOO, "x" CACHE <type>) #原缓存中没有FOO则将FOO赋值为x且存入cache中。原缓存中有FOO则不做任何改变,即便原cache中FOO存的不是x。
set(FOO, "x" CACHE <type><docstring> FORCE) #即便原cache中存在FOO也会创建另一个FOO
使用CACHE的同时,要设定和,可以理解为所存入变量类型,为变量的描述。
分为以下几种类型:
BOOL:有ON/OFF,两种取值
FILEPATH:文件的全路径
PATH:目录路径
STRING:字符串
INTERNAL:字符串
5.3 Set赋值给环境变量
set(ENV{<variable>} [<value>])
- variable:只能有一个
- value:一般来说,只有一个,为空时,将清除之前设置的变量值,多个时,取值最近的一个,之后的值将被忽略
message(WARNING $ENV{JAVA_HOME}) #JAVA_HOME是windows中设置的变量
# D:\SoftWare\Java\jdk1.8.0_191
set(ENV{JAVA_HOME} 1)
message(WARNING $ENV{JAVA_HOME})
# 1
set(ENV{DEFINE} DEFINE DEFINE2 DEFINE3) #自定义的变量
message(WARNING $ENV{DEFINE})
# DEFINE
set(a) #没有value值,a值清空
message(WARNING ${a})
# 空
5.4 set 追加值
set(SRC_LIST main.cpp)
set(SRC_LIST ${SRC_LIST} test.cpp)
6. 指定编译包含的源文件
6.1 明确指定包含哪些源文件
add_library(demo demo.cpp test.cpp util.cpp)
set(COMMON
config.cpp
util.cpp
)
add_library(common ${COMMON})
6.2 搜索所有的 cpp 文件
aux_source_directory(dir VAR) 发现一个目录下所有的源代码文件并将列表存储在一个变量中。
aux_source_directory(. SRC_LIST) # 搜索当前目录下的所有.cpp文件
add_library(demo ${SRC_LIST})
7. 引入外部依赖包
find_package(name path)查找到指定的预编译库,并将它的路径存储在变量中。
每个以Find<LibaryName>.cmake
命名的文件都可以帮我们找到一个包
每一个模块都会定义以下几个变量
<LibaryName>_FOUND
<LibaryName>_INCLUDE_DIR or <LibaryName>_INCLUDES
<LibaryName>_LIBRARY or <LibaryName>_LIBRARIES
find_package(CURL)
add_executable(curltest curltest.cc)
if(CURL_FOUND)
target_include_directories(clib PRIVATE ${CURL_INCLUDE_DIR})
target_link_libraries(curltest ${CURL_LIBRARY})
else(CURL_FOUND)
message(FATAL_ERROR ”CURL library not found”)
endif(CURL_FOUND)
8. 指定头文件路径
8.1 target_include_directories
指定编译给定目标时要使用的包含目录。名称<target>
必须是由命令创建的,例如add_executable()或add_library()生成的target
target_include_directories(t x/y) 具有目标范围——它将 x/y 添加到目标 t 的包含路径中。
关键字
public: 头文件可以提供给依赖的库和自己使用
interface: 头文件提供给依赖的库使用,但自己不使用,场景:封装一个sdk,供外部使用的头文件和内部使用的头文件,这时候就很有用了
private:头文件自己使用,不提供给外部使用
target_include_directories(kylin-mobile-assistant PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${KYSDKBASE_PKG_INCLUDE_DIRS}
)
8.2 include_directories
include_directories(x/y) 影响目录范围。 此 CMakeList 中的所有目标,以及在其调用点之后添加的所有子目录中的目标,都会将路径 x/y 添加到它们的包含路径中。
9. 设置链接库搜索目录
target_link_directories(classificationlist PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/libs
)
link_directories(
${CMAKE_CURRENT_SOURCE_DIR}/libs
)
10. 设置 target 需要链接的库
target_link_libraries(kylin-mobile-assistant PUBLIC
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Network
${KYSDKBASE_PKG_LIBRARIES}
common
connectmanage
projection
filetransfer
ui
)
10.1 指定链接动态库或静态库
target_link_libraries(demo libface.a) # 链接libface.a
target_link_libraries(demo libface.so) # 链接libface.so
10.2 指定全路径
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.so)
11. 打印日志
message(STATUS ...)
message(${PROJECT_SOURCE_DIR}) # 打印变量
message("build with debug mode")
message(WARNING "this is warnning message")
message(FATAL_ERROR "this build has many error") # FATAL_ERROR 会导致编译失败
# eg: message(STATUS "HELLO WORLD")
12. 逻辑控制
# 条件控制
if(XXX)
else(XXX)
endif(XXX)
# for
foreach(item ${TEST})
//${item}
endforeach(item)
13. add_executable
使用指定的源文件来生成目标可执行文件。
add_executable(kylin-mobile-assistant
main.cpp
)
14. add_subdirectory
添加一个子目录并构建该子目录。
set(SUBDIRECTORIES_MAIN
common
connectmanage
filetransfer
projection
ui
)
foreach (SUBDIRECTORY_MAIN ${SUBDIRECTORIES_MAIN})
add_subdirectory(${SUBDIRECTORY_MAIN})
endforeach ()
15. install
在cmake
的时候,最常见的几个步骤就是:
mkdir build && cd build
cmake ..
make
make install
install用于指定在安装时运行的规则。它可以用来安装很多内容,可以包括目标二进制、动态库、静态库以及文件、目录、脚本等:
install(TARGETS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
该命令的一些参数的含义:
DESTINATION:指定磁盘上要安装文件的目录;
PERMISSIONS:指定安装文件的权限。有效权限是OWNER_READ,OWNER_WRITE,OWNER_EXECUTE,GROUP_READ,GROUP_WRITE,GROUP_EXECUTE,WORLD_READ,WORLD_WRITE,WORLD_EXECUTE,SETUID和SETGID;
CONFIGURATIONS:指定安装规则适用的构建配置列表(DEBUG或RELEASE等);
EXCLUDE_FROM_ALL:指定该文件从完整安装中排除,仅作为特定于组件的安装的一部分进行安装;
OPTIONAL:如果要安装的文件不存在,则指定不是错误。
install(FILES kylin-mobile-assistant DESTINATION /usr/bin/kylin-mobile-assistant/ PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mobile-assistant.desktop DESTINATION /usr/share/applications/ PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/third_party/scrcpy-server DESTINATION /usr/bin/kylin-mobile-assistant)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/config DESTINATION /usr/bin/kylin-mobile-assistant/)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/keymap DESTINATION /usr/bin/kylin-mobile-assistant/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sndcpy DESTINATION /usr/bin/kylin-mobile-assistant/ PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
16. pkg_conf
什么是pkg-config
简单理解,pkg-config根据.pc
结尾的文件做依赖配置。
pkg-config的基本使用
pkg-config默认会在以下路径中查找指定的包(库)对应的.pc文件:
/usr/lib/pkgconfig
目录
/usr/share/pkgconfig
目录
/usr/local/lib/pkgconfig
目录
/usr/local/share/pkgconfig
目录
PKG_CONFIG_PATH
环境变量里的目录(可通过export PKG_CONFIG_PATH=XXX
来修改)
给pkg-config传入的.pc文件绝对路径
CMake中使用pkg-config
1. 安装pkg-config
安装pkg-config,并确保在CMake中能找到它的可执行文件。
sudo apt install pkg-config
2. CMakeLists.txt中使用pkg-config
在CMakeLists.txt中:
find_package(PkgConfig)
pkg_check_module(MyDepName REQUIRED xxx) # 当给出REQUIRED参数时,如果找不到模块,命令将失败并报错。
其中MyDepName
是自行起的名字,可以和原始包的名字不一样
找到pkg-config可执行文件并添加pkg_get_variable()、pkg_check_modules()和pkg_search_module()命令。以下变量也将被设置:
- PKG_CONFIG_FOUND:如果找到pkg-config可执行文件
- PKG_CONFIG_EXECUTABLE:pkg-config程序的路径
- PKG_CONFIG_VERSION_STRING:pkg-config版本
3. pkg_check_module
执行时返回以下变量,常用
< XXX > _FOUND:如果模块存在,则设置为1
< XXX > _LIBRARIES:库
< XXX > _LINK_LIBRARIES:库及其绝对路径
< XXX > _LIBRARY_DIRS:库的路径
< XXX > _INCLUDE_DIRS:头文件路径
< XXX > _VERSION:模块版本
find_package(PkgConfig REQUIRED)
pkg_check_modules(KYSDKQTWIDGETS_PKG
kysdk-qtwidgets
)
target_include_directories(storagelist PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${KYSDKQTWIDGETS_PKG_INCLUDE_DIRS}
)
target_link_directories(classificationlist PUBLIC
${KYSDKQTWIDGETS_PKG_LIBRARY_DIRS}
)
target_link_libraries(storagelist PUBLIC
${KYSDKQTWIDGETS_PKG_LIBRARIES}
)
Cmake中重要的变量
关键字 | 含义 |
---|---|
CMAKE_CURRENT_SOURCE_DIR | CMakeLists.txt所在的路径 |
CMAKE_CURRENT_BINARY_DIR | 编译的路径 |
PROJECT_NAME | 项目名称 设置了project之后就可以使用该值 |
CMAKE_CXX_FLAGS | 编译参数 |
CMAKE_C_FLAGS | 编译参数 |
CMAKE_STATIC_LINKER_FLAGS | 静态链接参数 |
CMAKE_SHARED_LINKER_FLAGS | 动态链接参数 |
CMAKE_CXX_COMPILER | cxx编译器 |
CMAKE_C_COMPILER | c编译器 |
CMAKE_AR | 汇编器 |
CMAKE_LINKER | 连接器 |
EXECUTABLE_OUTPUT_PATH | 可执行文件生成的路径 |
LIBRARY_OUTPUT_PATH | 库生成的路径 |
BUILD_SHARED_LIBS | 指定add_library() 的默认编译属性,动态库还是静态库【默认】 |