CMake

546 阅读1分钟

option

# 提供了一个在ON和OFF中做出选择的选项,默认为OFF
option(<option_variable> "描述性文字" [initial value])

option(protolib_BUILD_PROTOBUF "build protobuf" ON)
if (protolib_BUILD_PROTOBUF)
    ...
endif()

foreach

foreach (i IN LISTS num_list)
    message(STATUS ${i})
endforeach ()

list

# test1添加到变量O_LIST中,以列表的形式存储
list(APPEND O_LIST test1)
# test2添加到变量O_LIST中,以列表的形式存储
list(APPEND O_LIST test2)
# test3添加到变量O_LIST中,以列表的形式存储
list(APPEND O_LIST test3)

# 将O_LIST的length值设置到LEN中
list(LENGTH O_LIST LEN)

project

# 定义project名称
project(CMakeModule)

# 这样${PROJECT_NAME}的值就是CMakeModule了,默认值为Project
${PROJECT_NAME}

install

# 安装普通文件到指定目录,将hello.h拷贝到/usr/local/include下
install(FILES hello.h DESTINATION /usr/local/include)

# 安装目录到指定目录,将SharedLib目录及其子文件拷贝到/usr/local/dir下
install(DIRECTORY SharedLib DESTINATION /usr/local/dir)
# 只将SharedLib下的文件拷贝到/usr/local/dir下
install(DIRECTORY SharedLib/ DESTINATION /usr/local/dir)

# 安装目标文件到指定目录,这里是将libsharedHello.so安装到/usr/local/lib下
# 目标文件主要为静态库文件,共享库文件,可执行二进制文件等
install(TARGETS sharedHello DESTINATION /usr/local/lib)

function

# 定义函数,在内部使用arg1,需要以${arg1}这样的形式
function(<name> [arg1 [arg2 [arg3 ...]]])
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endfunction()

function(add_cc_executable EXECUTABLE_NAME EXECUTABLE_SOURCES)
    add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_SOURCES})
    target_link_libraries(${EXECUTABLE_NAME} handy_s)
    target_include_directories(${EXECUTABLE_NAME} PUBLIC ${PROJECT_SOURCE_DIR})
    install(TARGETS ${EXECUTABLE_NAME} DESTINATION ${CMAKE_INSTALL_NAME_DIR})
endfunction()

add_definitions

add_definitions(-DLINUX)

在代码中
#ifdef LINUX
...
#endif
就处于可执行状态

aux_source_directory

# 将dir目录下(不包括子目录)所有的源文件(不包括头文件)添加到变量variable中
aux_source_directory(<dir> <variable>)

# 将当前目录的所有源文件添加到SRCS变量中
aux_source_directory(. SRCS)

link_directories

LD_LIBRARY_PATH 用于指定除了默认的共享库路径外的其它路径
比如寻找libxxx.so,如果该文件不在省缺目录/usr/lib和/usr/local/lib下,就需要将libxxx.so的目录添加到LD_LIBRARY_PATH下

link_directories(directory1 directory2 ...)
就是将directory1 directory2添加到LD_LIBRARY_PATH下的cmake命令

find_file find_path find_library

# 查找xxx.yy文件的完整路径,并将(路径/xxx.yy)赋值给VAR
find_file(<VAR> xxx.yy [path1 path2 ...])

find_file(FLAME_CFG flame.cfg /steven/file)
FLAME_CFG的值为/steven/file/flame.cfg


# 查找xxx.h头文件的路径,并将(路径)赋值给VAR,优先去CMAKE_INCLUDE_PATH下搜索,再去默认路径下搜索,最后去path1 path2...下搜寻
# 如果不希望搜索CMAKE_INCLUDE_PATH,可以指定NO_CMAKE_PATH
# 如果不希望搜索默认路径,可以指定NO_DEFAULT_PATH
find_path(<VAR> xxx.h [path1 path2 ...])

find_path(CPIO cpio.h /steven/include NO_DEFAULT_PATH)
CPIO的值为/steven/include/cpio.h


# 查找libxxx.so库的路径,并将(路径/libxxx.so)赋值给VAR,优先去CMAKE_LIBRARY_PATH下搜索,再去默认路径下搜索,最后去path1 path2...下搜寻
# 如果不希望搜索CMAKE_LIBRARY_PATH,可以指定NO_CMAKE_PATH
# 如果不希望搜索默认路径,可以指定NO_DEFAULT_PATH
find_library(<VAR> libxxx.so [path1 path2 ...])

find_library(HGFS_LIB libhgfs.so /steven/lib)
HGFS_LIB的值为/steven/lib/libhgfs.so

add_custom_command vs add_custom_target

# 自定义目标,该目标实质上并不生成可执行文件(可以理解为生成了不可见的可执行文件),而只是为了执行`构建时候设置的命令`,如果目标有依赖,则优先执行依赖的那些目标
add_custom_target(Name [ALL] [COMMAND command2 [args2...] ...] [DEPENDS depend depend depend ... ])
# 自定义命令,一般只是创建了命令,而不会自动执行
add_custom_command(OUTPUT output1 COMMAND command1[ARGS] [args1...] [COMMAND command2 [ARGS] [args2...] ...] [DEPENDS[depends...]]


# 只通过add_custom_target执行命令
add_custom_target(mkJP ALL /bin/mkdir Japan)
默认不会执行目标文件,需要使用命令 cmake --target <Name>执行。但是如果指定了ALL参数,那么在生成上述目标文件时也会执行目标文件

# 只通过add_custom_command执行命令
add_custom_command(TARGET sharedHello COMMAND /bin/mkdir China)

# add_custom_command配合add_custom_target执行文件
add_custom_command(OUTPUT mkItaly COMMAND /bin/mkdir Italy)
add_custom_target(mkEU ALL COMMAND /bin/mkdir Germany DEPENDS mkItaly)

include_directories vs target_include_directories

# 具体参考了https://stackoverflow.com/a/51968659

# 相同点:
比如log.h的完整路径是/usr/local/include/Log/log.h

在CMakeList.txt中添加
include_directories(/usr/local/include)
或者
target_include_directories(xxx /usr/local/include)

这样在代码中可以直接这样调用
#include "Log/log.h"

# 不同点:
include_directories(../include ${SOME_OTHER_PATH}/include)

add_library(math SHARED ${MATH_SOURCES})
target_include_directories(math math_include)

add_executable(calculator ${MYCALCULATOR_SOURCES})
target_include_directories(calculator calc_include)
1. 在这里math是一个共享库文件,calculator是一个可执行文件,通过include_directories添加的
"../include"和"${SOME_OTHER_PATH}/include"目录对math和calculator都是可见的
2. 通过target_include_directories添加的"math_include"只对math可见
3. 通过target_include_directories添加的"calc_include"只对calculator可见

SHARED lib

# 扩展名为libxxx.so或xxx.dll
|-SharedLib
    |-CMakeList.txt
    |-hello.h
    |-hello.cpp

cmake_minimum_required(VERSION 3.5)
project (SharedLib)

set(SRC hello.cpp)
# 生成链接文件,这里是设置库的类型为SHARED和名称为sharedHello
add_library(sharedHello SHARED ${SRC})

# 头文件安装在/home/vaad/CPros/CMakeModule/include
INSTALL(FILES hello.h DESTINATION /home/vaad/CPros/CMakeModule/include)
# 库文件安装在/home/vaad/CPros/CMakeModule/lib下
INSTALL(TARGETS sharedHello LIBRARY DESTINATION /home/vaad/CPros/CMakeModule/lib)

STATIC lib

# 扩展名为libxxx.a或xxx.lib
|-CMakeModule
    |-StaticLib
        |-CMakeList.txt
        |-hello.h
        |-hello.cpp
    |-CMakeList.txt
    |-main.cpp
   
# StaticLib-CMakeList.txt
cmake_minimum_required(VERSION 3.5)
project (StaticLib)

set(SRCS hello.cpp)
# 生成链接文件,这里是设置库的类型为STATIC和名称为staticHello
add_library(staticHello STATIC ${SRCS})

# CMakeModule-CMakeList.txt
cmake_minimum_required(VERSION 3.5)
project(CMakeModule)

set(CMAKE_CXX_STANDARD 14)

add_executable(CMakeModule main.cpp)

# 执行StaticLib下的CMakeList.txt(StaticLib下必须有CMakeList.txt文件)
# 在这里StaticLib/CMakeList.txt会生成静态库文件,并将该库文件libstaticHello.a放在了build/StaticLibOut目录下
add_subdirectory(StaticLib StaticLibOut)
# 然后将静态库链接至CMakeModule
target_link_libraries(CMakeModule staticHello)

INTERFACE|PUBLIC|PRIVATE

# sub CMakeList.txt
target_include_directories(target1 INTERFACE|PUBLIC|PRIVATE item1)
PRIVATE         表示item1可以被target1使用,不可以被其它依赖target1的模块使用
INTERFACE       表示item1不可以被target1使用,可以被其它依赖target1的模块使用
PUBLIC          表示item1可以被target1使用,也可以被其它依赖target1的模块使用

# CMakeList.txt
target_link_libraries(target2 INTERFACE|PUBLIC|PRIVATE target1)
如果这里target2想使用target1#item1属性,
    那么item1的修饰符只能是INTERFACE或PUBLIC,
    并且target1的修饰符只能是PRIVATE或PUBLIC