一步一步学Cmake 之 必学的二十个指令(1-10)

内容:

1. add_libray

2. option

3. execute_process

4. target_sources

5. add_custom_command

6. add_custom_target

7. function & macro

8. check_cxx_compiler_flag

9. configure_file

10.set_target_properties


一步一步学 CMake 系列文章

姊妹篇:一步一步学CMake 之 必学的二十个指令(11-20)

1. add_libray

该指令的主要作用就是将指定的源文件生成链接文件,然后添加到工程中去

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 [source2 ...])

name:表示库文件的名字,该库文件会根据命令里列出的源文件来创建。

[STATIC | SHARED | MODULE]

STATIC:表示生成静态库,编译时生成

SHARED:表示生成共享(动态)库,运行时被加载

MODULE:一种不会被链接到其它目标中的插件,但是可能会在运行时动态加载。

OBJECT: 可以用来编译的源代码列表中给定的add_library到对象文件,但是既不将它们归档到静态库中,也不进行链接

它们变成一个共享库中。主要是使用在既需要创建静态库和又需要共享库时。

举个栗子:

add_library(math "")

target_sources(math
  PRIVATE
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.cpp
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.cpp
  PUBLIC
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.hpp
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.hpp
  )

首先声明一个库目标文件,默认不包含任何源文件,也可以使用target_sources来构成源文件。这里并未值是 STATIC 还是 SHARED 类型的库文件,如果不指定,则默认为 STATIC 类型的。

2. option

为用户提供一个可选项,该命令为用户提供了一个在ON和OFF中做出选择的选项。如果没有指定初始值,将会使用OFF作为初值。这个option命令和你本地是否存在编译缓存的关系很大。所以,如果你有关于 option 的改变,那么请你务必清理 CMakeCache.txt 和 CMakeFiles 文件夹。 请使用标准的 [initial value] 值 ON 或者 OFF。

option(<option_variable> "help string"  [initial value])

<option_variable>      选项名.

"help    string"            提示用户的信息.

[initial    value]            变量出事值 ON or OFF .

举个栗子:

option(WITH_BLUEFS "libbluefs library" OFF)

构建过程中,会提示 libbluefs library 文字,等待用户输入(ON or OFF),输入的值保存到 WITH_BLUEFS 变量里。

3. execute_process

执行进程

这条命令可以执行一个或多个命令作为当前命令的子命令,将输出的结果保存到cmake变量或文件中,所有的进程使用单个的标准错误输出管道。

execute_process(COMMAND <cmd1> [args1...]]
                [COMMAND <cmd2> [args2...] [...]]
                [WORKING_DIRECTORY <directory>]
                [TIMEOUT <seconds>]
                [RESULT_VARIABLE <variable>]
                [OUTPUT_VARIABLE <variable>]
                [ERROR_VARIABLE <variable>]
                [INPUT_FILE <file>]
                [OUTPUT_FILE <file>]
                [ERROR_FILE <file>]
                [OUTPUT_QUIET]
                [ERROR_QUIET]
                [OUTPUT_STRIP_TRAILING_WHITESPACE]
                [ERROR_STRIP_TRAILING_WHITESPACE])

如果指定了WORKING_DIRECTORY,则指定的目录将作为子进程当前的工作目录。

如果指定了TIMEOUT值,则如果在指定的时间内(以秒为单位计算,允许有小数位)子进程执行仍未完成,则将会被中断。

如果指定了RESULT_VARIABLE变量,则最后命令执行的结果将保存在该变量中,它是最后一个子进程执行完后的返回值或描述某种错误信息的字符串。

如果指定了OUTPUT_VARIABLEERROR_VARIABLE变量,则该变量会分别保存标准输出和标准错误输出的内容。

如果指定的变量是同一个,则输出会按产生的先后顺序保存在该变量中。

如果指定了INPUT_FILE, OUTPUT_FILEERROR_FILE等文件名,则它们会分别与第一个子进程的标准输入,最后一个子进程的标准输出以及所有子进程的标准错误输出相关联。

如果指定了OUTPUT_QUIETERROR_QUIET,则会忽略标准输出和错误输出。

如果在同一管道中同时指定了多个OUTPUT_*或ERROR_*选项,则优先级顺序是未知的(应避免这种情况)。

如果未指定任何OUTPUT_*或ERROR_*选项,则命令CMake所在进程共享输出管道。

举个栗子:

# Execute a tiny Python script
execute_process(
  COMMAND
    ${PYTHON_EXECUTABLE} "-c" "print('Hello, world!')"
  RESULT_VARIABLE _status
  OUTPUT_VARIABLE _hello_world
  ERROR_QUIET
  OUTPUT_STRIP_TRAILING_WHITESPACE
  )

4. target_sources

往source文件中追加源文件, 比如:

add_executable(my_source "")

target_sources(my_source
PRIVATE
subSource.cpp)

当然如果你在根目录通过add_executable定义了一个输出目标,也可以可以在子目录中用target_sources命令往这个输出目标里面追加源文件。

举个栗子:

add_library(math "")

target_sources(math
  PRIVATE
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.cpp
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.cpp
  PUBLIC
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.hpp
    ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.hpp
  )

注意:C++的源文件指定为Private,是因为源文件只是在构建库文件是使用,头文件指定为Public是因为构建和编译时都会使用。

5. add_custom_command

为生成的构建系统添加一条自定义的构建规则。

两种使用方法,方法一:

增加一个自定义命令用来产生一个输出。

 add_custom_command(OUTPUT output1 [output2 ...]
                     COMMAND command1[ARGS] [args1...]
                     [COMMAND command2 [ARGS] [args2...] ...]
                     [MAIN_DEPENDENCYdepend]
                     [DEPENDS[depends...]]
                     [IMPLICIT_DEPENDS<lang1> depend1 ...]
                     [WORKING_DIRECTORYdir]
                     [COMMENT comment] [VERBATIM] [APPEND])

举个栗子:

set(wrap_BLAS_LAPACK_sources
  ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.hpp
  ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.cpp
  ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.hpp
  ${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.cpp
  )

add_custom_command(
  OUTPUT
    ${wrap_BLAS_LAPACK_sources}
  COMMAND
    ${CMAKE_COMMAND} -E tar xzf ${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gz
  COMMAND
    ${CMAKE_COMMAND} -E touch ${wrap_BLAS_LAPACK_sources}
  WORKING_DIRECTORY
    ${CMAKE_CURRENT_BINARY_DIR}
  DEPENDS
    ${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gz
  COMMENT
    "Unpacking C++ wrappers for BLAS/LAPACK"
  VERBATIM
  )

set命令是声明一个变量wrap_BLAS_LAPACK_sources来保存源文件的名字,此时这些文件还不存在,因为这些文件在压缩包里存着,那我们就需要将压缩包进行解压,将解压得到的文件赋值给wrap_BLAS_LAPACK_sources。

add_custom_command    命令就是自定义解压指令,将解压的文件赋值给wrap_BLAS_LAPACK_sources变量。

OUTPUT                                输出,输出到wrap_BLAS_LAPACK_sources变量。

WORKING_DIRECTORY   指定在哪个目录下执行该命令。

DEPENDS         列出了自定义的这条命令的依赖项。

COMMENT        备注(提示信息),在构建时会打印出来。

VERBATIM                             告诉CMake为指定的生成器和平台生成正确的命令,来确保其是与平台无关的,不依赖的平台的。

方法二:

add_custom_command(TARGET target
                     PRE_BUILD | PRE_LINK| POST_BUILD
                     COMMAND command1[ARGS] [args1...]
                     [COMMAND command2[ARGS] [args2...] ...]
                     [WORKING_DIRECTORYdir]
                     [COMMENT comment][VERBATIM])

add_custom_command的局限性:

1. It    will    only    be    valid    if    all    of    the    targets    depending    on    its    output    are    specified
in    the    same     CMakeLists.txt .

2. Using    the    same    output    as     add_custom_command     for    different,    independent    targets
might    re-execute    the    custom    commands    rule.    This    may    cause    conflicts    and
should    be    avoided.

第二个局限性可以通过引入add_dependencies来避免,但是恰当的方法是使用add_custom_target

6. add_custom_target

7. function & macro

function(<name> [arg1 [arg2 [arg3 ...]]])
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endfunction(<name>)
macro(<name> [arg1 [arg2 [arg3 ...]]])
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endmacro(<name>)

相同点

CMake的 macro 和 function 都没有返回值。

和不同点:

CMake中函数的主要区别是变量的作用域不同。通俗的讲:如果更改宏内的变量值,那么宏外相同名称的变量值也会跟着更改;如果更改函数体内的变量值,那么函数体外相同名称的变量值并不会跟着更改。当然,如果想搞更改函数内外的变量值,必须使用关键字PARENT_SCOPE说明。

还不懂?举个栗子:

macro(add_catch_test)
  math(EXPR num_macro_calls "${num_macro_calls} + 1")
endmacro()

set(num_macro_calls 0)

add_catch_test()
add_catch_test()

message(STATUS "in total there were ${num_macro_calls} calls to add_catch_test")

两次调用add_catch_test()宏之后,num_macro_calls的值将会变成2。最终将输出:

--    in    total    there    were    2    calls    to    add_catch_test

如果将宏add_catch_test改为函数之后,代码也能正常执行,但是最终返回的结果不是2,而是0。如果想搞更改函数内外的变量值,必须使用关键字PARENT_SCOPE说明:

set(variable_visible_outside "some value" PARENT_SCOPE)

8. check_cxx_compiler_flag

判断C++编译器是否支持 某flag,flag可以是 -g, -std=c++11等。

check_cxx_compiler_flag(<flag> <var>)

举个栗子:

include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-std=c++11" COMPILER_SUPPORTS_CXX11)

unset(COMPILER_SUPPORTS_CXX11 CACHE)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
        message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support")
endif()

使用check_cxx_compiler_flag宏之前需要包含CheckCXXCompilerFlag定义,检查的结果会存储在内部缓存里,并以COMPILER_SUPPORTS_CXX11命名,所以使用该变量之前最好unset一下

check_c_compiler_flag使用方法类似,只是用于C语言。

9. configure_file

configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])

拷贝 input文件 到 其它地方output,并更改其中的内容。

input 文件中@VAR@ 或者 ${VAR} 引用的变量值 替换成 当前的变量值(注:CMake中的变量值)或者空串当变量未定义。input 默认是在 Source 文件夹里,也就是 ${CMAKE_CURRENT_SOURCE_DIR}/inputoutput 默认是在 Binary 文件夹里,也就是 ${CMAKE_CURRENT_BINARY_DIR}/output。

举个栗子:

configure_file(config.h.in config.h @ONLY)

输入文件为 config.h.in

#pragma once

#define NUMBER_OF_LOGICAL_CORES   @_NUMBER_OF_LOGICAL_CORES@
#define NUMBER_OF_PHYSICAL_CORES  @_NUMBER_OF_PHYSICAL_CORES@
#define TOTAL_VIRTUAL_MEMORY      @_TOTAL_VIRTUAL_MEMORY@
#define AVAILABLE_VIRTUAL_MEMORY  @_AVAILABLE_VIRTUAL_MEMORY@
#define TOTAL_PHYSICAL_MEMORY     @_TOTAL_PHYSICAL_MEMORY@
#define AVAILABLE_PHYSICAL_MEMORY @_AVAILABLE_PHYSICAL_MEMORY@
#define IS_64BIT                  @_IS_64BIT@
#define HAS_FPU                   @_HAS_FPU@
#define HAS_MMX                   @_HAS_MMX@
#define HAS_MMX_PLUS              @_HAS_MMX_PLUS@
#define OS_NAME                  "@_OS_NAME@"
#define OS_RELEASE               "@_OS_RELEASE@"
#define OS_VERSION               "@_OS_VERSION@"
#define OS_PLATFORM              "@_OS_PLATFORM@"

那么输出文件为config.h

#pragma once

#define NUMBER_OF_LOGICAL_CORES   8
#define NUMBER_OF_PHYSICAL_CORES  4
#define TOTAL_VIRTUAL_MEMORY      16345
#define AVAILABLE_VIRTUAL_MEMORY  16345
#define TOTAL_PHYSICAL_MEMORY     16008
#define AVAILABLE_PHYSICAL_MEMORY 10178
#define IS_64BIT                  1
#define HAS_FPU                   1
#define HAS_MMX                   1
#define HAS_MMX_PLUS              0
#define OS_NAME                  "Linux"
#define OS_RELEASE               "4.8.0-36-generic"
#define OS_VERSION               "#36~16.04.1-Ubuntu SMP Sun Feb 5 09:39:57 UTC 2017"
#define OS_PLATFORM              "x86_64"

@ONLY 啥意思
限制变量的替换,因为 configure_file 命令会把输入文件中@或 $ 类型的变量都会替换成其变量值。@ONLY的意思是只替换@类型的变量,而不替换$类型的变量。这在配置 ${VAR} 语法的脚本时是非常有用的。

10.set_target_properties

为目标文件设置属性,语法如下:

set_target_properties(target1 target2 ...
                      PROPERTIES prop1 value1
                      prop2 value2 ...)

举个栗子:

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe LANGUAGES CXX)

# generate an object library from sources
add_library(message-objs
  OBJECT
    Message.hpp
    Message.cpp
  )

# shared library
add_library(message-shared
  SHARED
    $<TARGET_OBJECTS:message-objs>
  )
set_target_properties(message-shared
  PROPERTIES
    OUTPUT_NAME "message"
  )

# static library
add_library(message-static
  STATIC
    $<TARGET_OBJECTS:message-objs>
  )
set_target_properties(message-static
  PROPERTIES
    OUTPUT_NAME "message"
  )

add_executable(hello-world hello-world.cpp)
target_link_libraries(hello-world message-static)

set_target_properties(message-shared PROPERTIES OUTPUT_NAME "message" )

set_target_properties(message-static PROPERTIES OUTPUT_NAME "message" )

把静态库 message-static 和 动态库 message-shared 的名字属性都改为"message"。

继续学:一步一步学CMake 之 必学的二十个指令(11-20)

猜你喜欢

转载自blog.csdn.net/wanzew/article/details/104102499