精华内容
参与话题
问答
  • cmake 教程

    2019-04-24 01:06:07
    NULL 博文链接:https://gangling.iteye.com/blog/2381913
  • Cmake 教程

    2013-06-03 23:46:46
    Cmake 教程
  • cmake教程

    千次阅读 2018-07-27 08:53:10
    ./test1 face.jpg frozen_graph.pb input MobilenetV1/Predictions/Reshape_1 实例: 先制作makefile文件 以便以后运行 ...CMakeLists.txt文件内容如下: ...cmake_minimum_required(VERSION 3.10) ...set(CMAKE_CXX...
    
        
    
    

    全栈工程师开发手册 (作者:栾鹏)
    架构系列文章

    基本使用

    一、 基本使用

    安装:下载二进制包后可直接解压使用

    从源码安装则执行命令:./bootstrap; make; make install——尝试执行bootstrap失败

    使用:cmake dir_path,生成工程文件或makefile文件

    二、 概念
    out-of-source build,与in-source build相对,即将编译输出文件与源文件放到不同目录中;

    三、 基本结构

    1,依赖CMakeLists.txt文件,项目主目标一个,主目录中可指定包含的子目录;

    2,在项目CMakeLists.txt中使用project指定项目名称,add_subdirectory添加子目录

    3,子目录CMakeLists.txt将从父目录CMakeLists.txt继承设置(TBD,待检验)

    cmake基本语法

    Cmake的输入是在源码目录下的CMakeLists.txt文件。这个文件可以用include或者 add_subdirectory 命令增加入其它的输入文件。

    语法

    CMakeList.txt文件是由注释、命令和空白字符组成。

    注释是由 # 开始,到行结尾。

    命令是由:命令名、(、空格分隔的参数、)组成。

    例如:command (args….)

    上面的command可以是一个命令名;或者是一个宏;也可以是一个函数名。

    args是以空格分隔的参数例表(如果参数中包含空格,则要加双引号)

    除了用于分隔参数的空白字符(空格、换行号、tabs)都是被忽略不计的。任何包含在双引号中的字符都做为一个参数。一个反斜杠用于转换码。

    命令名是大小写不敏感的。

    字符串(string)和字符串列表(lists)

    CMake的基本数据类型是字符串(string)。CMake也支持由字符串组成的字符串列表。字符串列表可以由;或空格分隔的组成。例如:下面设置变量var是等效的。

    set(var a;b;c)
    
    set(var a b c)
    

    字符串列表可以用 foreach命令叠代(iterated)或list命令操作。

    变量

    CMake支持简单的变量:字符串或字符串列表。用${VAR} 语法得到变量的引用。

    可以用一个set命令把一个字符串列表设置为一个变量,然后把这个变量传递给需要传递多参数的函数。例如:

    set(Foo a b c)
    
    command(${Foo})
    
    上面两行等效 command(a b c)
    
    

    如果你想传把一个字符串列表做为一个单独的参数传递给函数,用双引号包含它。例如:

    Command(“${Foo}”)
    
    等效于:command(“a b c”)
    

    环境变量:

    用$ENV{VAR}得到环境变量的引用

    设置环境变量:

    Set(ENV{VAR} /home)
    

    程序流控制

    CMake提供三种程序流控制结构:

    1、 条件声明:if

    # some_command will be called if the variable's value is not:
    # empty, 0, N, NO, OFF, FALSE, NOTFOUND, or -NOTFOUND. 
    if(var) 
       some_command(...) 
    endif(var)
    

    2、 循环控制:foreach 和 while

    set(VAR a b c) 
      # loop over a, b,c with the variable f 
    foreach(f ${VAR}) 
        message(${f}) 
    endforeach(f)
    

    3、 程序定义:宏或函数(函数在CMake2.6以后的版本才支持)。函数建立本地范围内的变量,宏用于全局范围内。

    # define a macro hello 
    macro(hello MESSAGE)
        message(${MESSAGE})
    endmacro(hello) 
    # call the macro with the string "hello world" 
    hello("hello world") 
    # define a function hello 
    function(hello MESSAGE)
        message(${MESSAGE}) 
    endfunction(hello)
    

    函数可以返回,可以用 return()命令返回。如果要从函数中返回值,只能通过参数返回:

    set命令中 PARENT_SCOPE表示传递给函数调用者所拥有的变量

    引号、字符串和escapes

    一个用双引号包含的字符串,是这个字符串的字面含义。一个字符串能包含多行。例如:

    set (MY_STRING "this is a string with a
      newline in
      it")
    

    也能在字符串中用转义字符

    set (VAR "
       hello 
      world 
      ")
    message ( "\${VAR} = ${VAR}")
      # prints out
      ${VAR} = 
        hello 
        world
    

    标准C的转义字符被支持

    message("\n\thello world")
    # prints out 
    hello world
    
    message(hell"o")   -> prints hell"o"
    message(hell"o")   -> prints hell"o" 
    message(hell\"o\")  -> prints hell"o"
    
    

    引号必须是匹配的

    message(hell"o)   -> produces this error:
    Parse error. Function missing ending ")". 
    Instead found unterminated string with text "o) 
    ". 
    
        message(hell\"o) -> prints hell"o
    

    正则表达式

    一些CMake命令(如if和 string),能使用正则表达式或使用正则表达式作为参数。一个简单的形式,一个正则表达式用于在一个序列的字符串中精确查找一个字符。然而在大多时候,一个精确查找是不知道的或者只是匹配最前或者最后字符。所以这里用正则表达式进行不同的转换。Cmake标准是可被描述的。这个描述是基于开源的正则表达式类(Texas Instruments)。

    ^ 匹配一行或一字符串开头
    $匹配一行或一字符串结尾
    .匹配单一字符或一个新行
     [ ]匹配括号中的任一字符
    [^ ] 匹配不在括号内的任一字符
    [-] 匹配指定范围内的字符
    * 匹配0次或多次
    + 匹配一次或多次
    ? 匹配0次或一次
    ()保存匹配的表达式并用随后的替换它
    

    CMAKE进阶

    内部变量

    CMAKE_C_COMPILER:指定C编译器

    CMAKE_CXX_COMPILER:

    CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项

    EXECUTABLE_OUTPUT_PATH:可执行文件的存放路径

    LIBRARY_OUTPUT_PATH:库文件路径

    CMAKE_BUILD_TYPE::build 类型(Debug, Release, …),CMAKE_BUILD_TYPE=Debug

    BUILD_SHARED_LIBS:Switch between shared and static libraries

    内置变量的使用:

    在CMakeLists.txt中指定,使用set

    cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

    命令

    project (HELLO) #指定项目名称,生成的VC项目的名称;

    使用${HELLO_SOURCE_DIR}表示项目根目录
    

    include_directories:指定头文件的搜索路径,相当于指定gcc的-I参数

    include_directories (${HELLO_SOURCE_DIR}/Hello)  #增加Hello为include目录
    

    link_directories:动态链接库或静态链接库的搜索路径,相当于gcc的-L参数

    link_directories (${HELLO_BINARY_DIR}/Hello)     #增加Hello为link目录
    

    add_subdirectory:包含子目录

    add_subdirectory (Hello)
    

    add_executable:编译可执行程序,指定编译,好像也可以添加.o文件

    add_executable (helloDemo demo.cxx demo_b.cxx)   #将cxx编译成可执行文件——
    

    add_definitions:添加编译参数

    add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义;
    add_definitions( “-Wall -ansi –pedantic –g”)
    

    target_link_libraries:添加链接库,相同于指定-l参数

    target_link_libraries(demo Hello) #将可执行文件与Hello连接成最终文件demo
    

    add_library:

    add_library(Hello hello.cxx)  #将hello.cxx编译成静态库如libHello.a
    

    add_custom_target:

    message( status|fatal_error, “message”):

    set_target_properties( … ): lots of properties… OUTPUT_NAME, VERSION, …

    link_libraries( lib1 lib2 …): All targets link with the same set of libs

    实例:

    先制作makefile文件 以便以后运行

    CMakeLists.txt文件内容如下:

    cmake_minimum_required(VERSION 3.10)
    project(cpptensorflow)
    set(CMAKE_CXX_STANDARD 11)
    link_directories(/home/lp/projects/safety/tensorflow_c/tensorflow_cpp/tensorflow)
    include_directories(
            /home/lp/projects/safety/tensorflow_c/tensorflow-master/tensorflow
            /home/lp/projects/safety/tensorflow_c/tensorflow-master/tensorflow/bazel-genfiles
            /home/lp/projects/safety/tensorflow_c/tensorflow-master/tensorflow/bazel-bin/tensorflow
            /home/lp/projects/safety/tensorflow_c/eigen3
    )
    add_executable(cpptensorflow main.cpp ann_model_loader.h model_loader_base.h ann_model_loader.cpp)
    target_link_libraries(cpptensorflow tensorflow_cc tensorflow_framework)
    

    其中 link_directories为指定优先搜索库的路径,也就是.so文件的路径 默认搜索 /usr/local/lib目录,
    该目录下有很多.so目录 include_directories为包含的unclude目录.
    add_executable参数为要生成的可执行文件的文件名, 要编译的cpp/h文件
    target_link_libraries为在lib目录下(包含系统lib目录/usr/local/lib和手动添加目录link_directories,)要包含的.so文件名.其中去掉文件名前面的lib字符串 当多个库存在依赖时,依赖库要写在后面 如果你添加了目标库,但是没有添加目标库的依赖库,就会报错,这个时候你需要将缺少的库也添加到target_link_libraries里面
    注意:库的名称是.so文件的文件名去掉前面的lib字符串

    在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

    编写 CMake 配置文件 CMakeLists.txt 。

    执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile, 其中, PATH 是 CMakeLists.txt 所在的目录。
    其中ccmake 和 cmake 的区别在于前者提供了一个交互式的界面。

    使用 make 命令进行编译。

    在CMakeLists.txt目录下执行

    mkdir  build   
    cd  build
    cmake ..
    make 
    

    这样就可以生成可执行文件了

    cmake 添加头文件目录,链接动态、静态库

    1, 添加头文件目录INCLUDE_DIRECTORIES

    语法:

    include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
    
    例如:
    include_directories(../../../thirdparty/comm/include)
    

    它相当于g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用。

    2, 添加需要链接的库文件目录LINK_DIRECTORIES

    语法:

    link_directories(directory1 directory2 ...)
    
    它相当于g++命令的-L选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH的路径的作用。
    
    示例:
    link_directories("/home/server/third/lib")
    
    

    3, 查找库所在目录FIND_LIBRARY

    A short-hand signature is:
    
    find_library (<VAR> name1 [path1 path2 ...])
    The general signature is:
    
    find_library (
              <VAR>
              name | NAMES name1 [name2 ...] [NAMES_PER_DIR]
              [HINTS path1 [path2 ... ENV var]]
              [PATHS path1 [path2 ... ENV var]]
              [PATH_SUFFIXES suffix1 [suffix2 ...]]
              [DOC "cache documentation string"]
              [NO_DEFAULT_PATH]
              [NO_CMAKE_ENVIRONMENT_PATH]
              [NO_CMAKE_PATH]
              [NO_SYSTEM_ENVIRONMENT_PATH]
              [NO_CMAKE_SYSTEM_PATH]
              [CMAKE_FIND_ROOT_PATH_BOTH |
               ONLY_CMAKE_FIND_ROOT_PATH |
               NO_CMAKE_FIND_ROOT_PATH]
             )
    
    示例:
    
    FIND_LIBRARY(RUNTIME_LIB rt /usr/lib  /usr/local/lib NO_DEFAULT_PATH)
    
    cmake会在目录中查找,如果所有目录中都没有,值RUNTIME_LIB就会被赋为NO_DEFAULT_PATH
     
    

    4, 添加需要链接的库文件路径LINK_LIBRARIES

    语法:

    link_libraries(library1 <debug | optimized> library2 ...)
    # 直接是全路径
    link_libraries(“/home/server/third/lib/libcommon.a”)
    # 下面的例子,只有库名,cmake会自动去所包含的目录搜索
    link_libraries(iconv)
    
    # 传入变量
    link_libraries(${RUNTIME_LIB})
    # 也可以链接多个
    link_libraries("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
    

    可以链接一个,也可以多个,中间使用空格分隔.

    5, 设置要链接的库文件的名称TARGET_LINK_LIBRARIES

    语法:

    target_link_libraries(<target> [item1 [item2 [...]]]
                          [[debug|optimized|general] <item>] ...)
    
    # 以下写法都可以: 
    target_link_libraries(myProject comm)       # 连接libhello.so库,默认优先链接动态库
    target_link_libraries(myProject libcomm.a)  # 显示指定链接静态库
    target_link_libraries(myProject libcomm.so) # 显示指定链接动态库
    
    # 再如:
    target_link_libraries(myProject libcomm.so)  #这些库名写法都可以。
    target_link_libraries(myProject comm)
    target_link_libraries(myProject -lcomm)
    
    

    6,为工程生成目标文件

    语法:

    add_executable(<name> [WIN32] [MACOSX_BUNDLE]
                   [EXCLUDE_FROM_ALL]
                   source1 [source2 ...])
    

    简单的例子如下:

    add_executable(demo
            main.cpp
    )
    

    7, 最后贴一个完整的例子

    
    cmake_minimum_required (VERSION 2.6)
    
    INCLUDE_DIRECTORIES(../../thirdparty/comm)
    
    FIND_LIBRARY(COMM_LIB comm ../../thirdparty/comm/lib NO_DEFAULT_PATH)
    FIND_LIBRARY(RUNTIME_LIB rt /usr/lib  /usr/local/lib NO_DEFAULT_PATH)
    
    link_libraries(${COMM_LIB} ${RUNTIME_LIB})
    
    ADD_DEFINITIONS(
    -O3 -g -W -Wall
     -Wunused-variable -Wunused-parameter -Wunused-function -Wunused
     -Wno-deprecated -Woverloaded-virtual -Wwrite-strings
     -D__WUR= -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DTIXML_USE_STL
    )
    
    
    add_library(lib_demo
            cmd.cpp
            global.cpp
            md5.cpp
    )
    
    link_libraries(lib_demo)
    add_executable(demo
            main.cpp
    )
    
    # link library in static mode
    target_link_libraries(demo libuuid.a)
    

    另外,使用cmake生成makefile之后,make edit_cache可以编辑编译选项。

    展开全文
  • CMake教程

    2019-11-30 15:53:09
    CMake教程提供了逐步指南,涵盖了CMake可以帮助解决的常见构建系统问题。 了解示例项目中各个主题如何协同工作将非常有帮助。 示例的教程文档和源代码可在CMake源代码树的Help/guide/tutorial目录中找到...

    版权声明:本文翻译自CMake tutorial v3.16》。未经作者允许严禁用于商业出版否则追究法律责任。网络转载请注明出处!!!


    CMake教程提供了逐步指南,涵盖了CMake可以帮助解决的常见构建系统问题。 了解示例项目中各个主题如何协同工作将非常有帮助。 示例的教程文档和源代码可在CMake源代码树的Help/guide/tutorial目录中找到。 每个步骤都有其自己的子目录,其中包含可以用作起点的代码。 教程示例是渐进式的,因此每个步骤都为上一步提供了完整的解决方案。

    1 基本起点(第1步)

    最基本的项目是从源代码文件构建一个可执行文件。 对于简单的项目,只需三行CMakeLists.txt文件。 这是本教程的起点。 在Step1目录中创建一个CMakeLists.txt文件,如下所示:

    cmake_minimum_required(VERSION 3.10)
    
    # set the project name
    project(Tutorial)
    
    # add the executable
    add_executable(Tutorial tutorial.cxx)
    

    请注意,此示例在CMakeLists.txt文件中使用小写的命令。 CMake支持大写,小写和大小写混合的命令。 Step1目录中提供了tutorial.cxx的源代码,可用于计算数字的平方根。

    1.1 添加版本号和配置头文件

    我们将添加的第一个功能是为我们的可执行文件和项目提供版本号。 虽然我们可以仅在源代码中执行此操作,但是使用CMakeLists.txt可以提供更大的灵活性。

    首先,修改CMakeLists.txt文件来设置版本号。

    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(Tutorial VERSION 1.0)
    
    
    ###早期版本的写法
    ###		project(Tutorial)
    ###		set (Tutorial_VERSION_MAJOR 1)
    ###		set (Tutorial_VERSION_MINOR 0)
    

    然后,配置一个头文件,将版本号传递给源代码:

    configure_file(TutorialConfig.h.in TutorialConfig.h)
    
    ###早期版本的写法
    ###		configure_file ("${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" "${PROJECT_BINARY_DIR}/TutorialConfig.h")
    

    由于配置的文件将被写入二进制树中,所以我们必须将该目录添加到搜索include文件的路径列表中。在CMakeLists.txt文件的末尾添加以下行:

    #必须在add_excutable之后
    target_include_directories(Tutorial PUBLIC  "${PROJECT_BINARY_DIR}")
    
    
    ###早期版本的写法:
    ###可以位于任意位置,一般放在add_excutable之前
    ###		include_directories("${PROJECT_BINARY_DIR}")
    

    使用您喜欢的编辑器在源码目录中创建TutorialConfig.h.in,内容如下:

    // the configured options and settings for Tutorial
    #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
    #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
    

    当CMake配置这个头文件时,@Tutorial_VERSION_MAJOR@@Tutorial_VERSION_MINOR@的值将被替换。

    接下来,修改tutorial.cxx以包括配置的头文件TutorialConfig.h。

    最后,通过更新tutorial.cxx来打印出版本号,如下所示:

      if (argc < 2) {
        // report version
        std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
                  << Tutorial_VERSION_MINOR << std::endl;
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
      }
    

    完整的CMakeLists.txt如下:

    cmake_minimum_required(VERSION 3.10)
    
    #set project name and version
    project(Tutorial VERSION 1.0)
    
    configure_file(TutorialConfig.h.in TutorialConfig.h)
    
    #add the executable
    add_executable(Tutorial tutorial.cxx)
    
    target_include_directories(Tutorial PUBLIC  "${PROJECT_BINARY_DIR}"  )
    

    1.2 指定c++标准

    接下来,通过在tutorial.cxx中用std::stod替换atof,将一些C ++ 11功能添加到我们的项目中。 同时,删除#include <cstdlib>

    const double inputValue = std::stod(argv[1]);
    

    我们需要在CMake代码中明确声明应使用正确的标志。 在CMake中启用对特定C ++标准的支持的最简单方法是使用CMAKE_CXX_STANDARD变量。 对于本教程,请将CMakeLists.txt文件中的CMAKE_CXX_STANDARD变量设置为11,并将CMAKE_CXX_STANDARD_REQUIRED设置为True:

    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(Tutorial VERSION 1.0)
    
    # specify the C++ standard
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    

    1.3 构建和测试

    运行cmake或cmake-gui以配置项目,然后使用所选的构建工具进行构建。

    例如,从命令行我们可以导航到CMake源代码树的Help /guide/tutorial目录并运行以下命令:

    mkdir Step1_build
    cd Step1_build
    cmake ../Step1
    cmake --build .
    

    导航到构建教程的目录(可能是make目录或Debug或Release构建配置子目录),然后运行以下命令:

    Tutorial 4294967296
    Tutorial 10
    Tutorial
    

    2 添加库(第2步)

    现在,我们将添加一个库到我们的项目中。 该库是我们自己的实现的用于计算数字的平方根的库。 可执行文件可以使用此库,而不是使用编译器提供的标准平方根函数。

    在本教程中,我们将库放入名为MathFunctions的子目录中。 该目录已包含头文件MathFunctions.h和源文件mysqrt.cxx。 源文件具有一个称为mysqrt的函数,该函数提供与编译器的sqrt函数类似的功能。

    将以下一行CMakeLists.txt文件添加到MathFunctions目录中:

    add_library(MathFunctions mysqrt.cxx)
    

    为了使用新的库,我们将在顶层CMakeLists.txt文件中添加add_subdirectory调用,以便构建该库。 我们将新的库添加到可执行文件,并将MathFunctions添加为include目录,以便可以找到mqsqrt.h头文件。 顶级CMakeLists.txt文件的最后几行现在应如下所示:

    # add the MathFunctions library
    add_subdirectory(MathFunctions)
    
    # add the executable
    add_executable(Tutorial tutorial.cxx)
    
    #必须位于add_excutable之后
    target_link_libraries(Tutorial PUBLIC MathFunctions)
    
    ###早期版本的写法
    ###		target_link_libraries(Tutorial MathFunctions)
    
    #add the binary tree to the search path for include files so that we will find TutorialConfig.h
    target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions")
    

    现在让我们将MathFunctions库设为可选。 虽然对于本教程而言确实不需要这样做,但是对于大型项目来说,这是很常见的。 第一步是向顶层CMakeLists.txt文件添加一个选项。

    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    
    # configure a header file to pass some of the CMake settings to the source code
    configure_file(TutorialConfig.h.in TutorialConfig.h)
    

    此选项将显示在CMake GUI和ccmake中,默认值ON,可由用户更改。 此设置将存储在缓存中,因此用户不必每次在构建目录上运行CMake时设置该值。

    下一个更改是使构建和链接MathFunctions库成为布尔选项。 为此,我们将顶层CMakeLists.txt文件的结尾更改为如下所示:

    if(USE_MYMATH)
      add_subdirectory(MathFunctions)
      list(APPEND EXTRA_LIBS MathFunctions)
      list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
    endif()
    
    # add the executable
    add_executable(Tutorial tutorial.cxx)
    
    #必须位于add_executable之后
    target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
    
    # add the binary tree to the search path for include files so that we will find TutorialConfig.h
    target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES})
    
    ###早期版本的写法
    ###		if(USE_MYMATH)
    ###		  	include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
    ###	  		add_subdirectory (MathFunctions)
    ###	  		set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
    ###		endif(USE_MYMATH)
    ###		include_directories("${PROJECT_BINARY_DIR}")
    ###		add_executable(Tutorial tutorial.cxx)
    ###		target_link_libraries(Tutorial ${EXTRA_LIBS})
    

    请注意,使用变量EXTRA_LIBS来收集任意可选库,以供以后链接到可执行文件中。 变量EXTRA_INCLUDES类似地用于可选的头文件。 当处理许多可选组件时,这是一种经典方法,我们将在下一步中介绍现代方法。

    对源代码的相应更改非常简单。 首先,如果需要,在tutorial.cxx中包含MathFunctions.h头文件:

    #ifdef USE_MYMATH
    #  include "MathFunctions.h"
    #endif
    

    然后,在同一文件中,使USE_MYMATH控制使用哪个平方根函数:

    #ifdef USE_MYMATH
      const double outputValue = mysqrt(inputValue);
    #else
      const double outputValue = sqrt(inputValue);
    #endif
    

    由于源代码现在需要USE_MYMATH,因此可以使用以下行将其添加到TutorialConfig.h.in中:

    #cmakedefine USE_MYMATH
    

    练习:为什么在USE_MYMATH选项之后配置TutorialConfig.h.in如此重要? 如果我们将两者倒置会怎样?

    运行cmake或cmake-gui以配置项目,然后使用所选的构建工具进行构建。 然后运行构建的Tutorial可执行文件。

    使用ccmake或CMake GUI更新USE_MYMATH的值。 重新生成并再次运行本教程。 sqrt或mysqrt哪个函数可提供更好的结果?

    完整的CMakeLists.txt文件如下:

    cmake_minimum_required(VERSION 3.5)                                                                                  
    # set the project name and version
    project(Tutorial VERSION 1.0)
     
    # specify the C++ standard
    set(CMAKE_CXX_STANDARD 11) 
    set(CMAKE_CXX_STANDARD_REQUIRED True)
     
    option(USE_MYMATH "Use tutorial provided math implementation" ON) 
     
    # configure a header file to pass some of the CMake settings to the source code
    configure_file(TutorialConfig.h.in TutorialConfig.h)
     
    if(USE_MYMATH)
    	add_subdirectory(MathFunctions)
    	list(APPEND EXTRA_LIBS MathFunctions)
    	list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
    endif()
     
    # add the executable
    add_executable(Tutorial tutorial.cxx)
    
    #必须位于add_executable之后
    target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})  
    
    # add the binary tree to the search path for include files so that we will find TutorialConfig.h
    target_include_directories(Tutorial PUBLIC  "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES}) 
    

    3 添加库的使用要求(第3步)

    使用要求可以更好地控制库或可执行文件的链接和include行,同时还可以更好地控制CMake内部目标的传递属性。 利用使用要求的主要命令是:

    • target_compile_definitions
    • target_compile_options
    • target_include_directories
    • target_link_libraries

    让我们从第2步中重构代码,以利用现代的CMake方法编写使用要求。 我们首先声明,链接到MathFunctions的任何东西都需要包括当前源码目录,而MathFunctions本身不需要。 因此,这可以成为INTERFACE使用要求。

    请记住,INTERFACE是指消费者需要的,而生产者不需要东西。 将以下行添加到MathFunctions/CMakeLists.txt的末尾:

    target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR)
    

    现在,我们已经指定了MathFunction的使用要求,我们可以安全地从顶级CMakeLists.txt中删除对EXTRA_INCLUDES变量的使用:

    if(USE_MYMATH)
      add_subdirectory(MathFunctions)
      list(APPEND EXTRA_LIBS MathFunctions)
    endif()
    ... ...
    ... ...
    
    target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
    

    4 安装与测试(第4步)

    现在,我们可以开始向项目添加安装规则和测试支持。

    4.1 安装规则

    安装规则非常简单:对于MathFunctions,我们要安装库和头文件,对于应用程序,我们要安装可执行文件和配置的头文件。

    因此,在MathFunctions/CMakeLists.txt的末尾添加:

    install(TARGETS MathFunctions DESTINATION lib)
    install(FILES MathFunctions.h DESTINATION include)
    

    然后在顶级cmakelt .txt的末尾添加

    install(TARGETS Tutorial DESTINATION bin)
    install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include)
    

    这就是创建本教程的基本本地安装所需的全部工作。

    运行cmake或cmake-gui以配置项目,然后使用所选的构建工具进行构建。 从命令行键入cmake --install进行安装(自3.15中引入,较早版本的CMake必须使用make install),或从IDE构建INSTALL目标。 这将安装适当的头文件,库和可执行文件。

    CMake变量CMAKE_INSTALL_PREFIX用于确定文件的安装根目录。 如果使用cmake --install,则可以通过--prefix参数指定自定义安装目录。 对于多配置工具,请使用--config参数指定配置。

    验证已安装的Tutorial可以运行。

    4.2 测试支持

    接下来,测试我们的应用程序。 在顶级CMakeLists.txt文件的末尾,我们可以启用测试,然后添加一些基本测试以验证应用程序是否正常运行。

    enable_testing()
    
    # does the application run
    add_test(NAME Runs COMMAND Tutorial 25)
    
    # does it sqrt of 25
    add_test (NAME Comp25 COMMAND Tutorial 25)
    set_tests_properties (Comp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")
    
    # does the usage message work?
    add_test(NAME Usage COMMAND Tutorial)
    set_tests_properties(Usage  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
    
    # define a function to simplify adding tests
    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)
    
    # do a bunch of result based tests
    do_test(Tutorial 4 "4 is 2")
    do_test(Tutorial 9 "9 is 3")
    do_test(Tutorial 5 "5 is 2.236")
    do_test(Tutorial 7 "7 is 2.645")
    do_test(Tutorial 25 "25 is 5")
    do_test(Tutorial -25 "-25 is [-nan|nan|0]")
    do_test(Tutorial 0.0001 "0.0001 is 0.01")
    
    ###早期版本的写法
    ###		include(CTest)
    ###		add_test (TutorialRuns Tutorial 25)
    ###
    ###		add_test (TutorialComp25 Tutorial 25)
    ###		set_tests_properties (TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")
    ###
    ###		add_test (TutorialUsage Tutorial)
    ###		set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
    ###
    ###		#define a macro to simplify adding tests, then use it
    ###		macro (do_test arg result)
    ###		  add_test (TutorialComp${arg} Tutorial ${arg})
    ###		  set_tests_properties (TutorialComp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result})
    ###		endmacro (do_test)
    ###
    ###		do_test(4 "4 is 2")
    ###		do_test(9 "9 is 3")
    ###		do_test(5 "5 is 2.236")
    ###		do_test(7 "7 is 2.645")
    ###		do_test(25 "25 is 5")
    ###		do_test(-25 "-25 is [-nan|nan|0]")
    ###		do_test(0.0001 "0.0001 is 0.01")
    
    

    第一个测试只是验证应用程序你能否运行,没有段错误或其他崩溃,并且返回值为零。 这是CTest测试的基本形式。

    下一个测试使用PASS_REGULAR_EXPRESSION测试属性来验证测试的输出是否包含某些字符串。 在这种情况下,验证在提供了错误数量的参数时是否打印了用法消息。

    最后,我们有一个名为do_test的函数,该函数运行应用程序并验证所计算的平方根对于给定输入是否正确。 对于do_test的每次调用,都会基于传递的参数将另一个测试添加到项目中,该测试具有名称,输入和预期结果。

    重新构建应用程序,然后cd到二进制目录并运行ctest -Nctest -VV。 对于多配置生成器(例如Visual Studio),必须指定配置类型。 例如,要在“调试”模式下运行测试,请从构建目录(而不是“调试”子目录!)中使用ctest -C Debug -VV。 或者,从IDE构建RUN_TESTS目标。

    早期版本的另一种写法是:

    
    

    5 添加系统自检(第5步)

    让我们考虑向我们的项目中添加一些代码,这些代码取决于目标平台可能不具备的功能。 对于此示例,我们将添加一些代码,具体取决于目标平台是否具有logexp函数。 当然,几乎每个平台都具有这些函数,但对于本教程而言,假设它们并不常见。

    如果平台具有logexp,那么我们将使用它们来计算mysqrt函数中的平方根。 我们首先使用顶级CMakeLists.txt中的CheckSymbolExists模块测试这些函数的可用性。 我们将在TutorialConfig.h.in中使用新定义,因此请确保在配置该文件之前进行设置。

    include(CheckSymbolExists)
    set(CMAKE_REQUIRED_LIBRARIES "m")
    check_symbol_exists(log "math.h" HAVE_LOG)
    check_symbol_exists(exp "math.h" HAVE_EXP)
    
    ###早期版本的写法
    ###		include (CheckFunctionExists)
    ###		check_function_exists (log HAVE_LOG)
    ###		check_function_exists (exp HAVE_EXP)
    
    

    现在,将这些定义添加到TutorialConfig.h.in中,以便我们可以从mysqrt.cxx中使用它们:

    // does the platform provide exp and log functions?
    #cmakedefine HAVE_LOG
    #cmakedefine HAVE_EXP
    

    修改mysqrt.cxx以包括cmath。 接下来,在mysqrt函数的同一文件中,我们可以使用以下代码提供基于logexp(如果在系统上可用)的替代实现(在return result;前不要忘记#endif!):

    #if defined(HAVE_LOG) && defined(HAVE_EXP)
      double result = exp(log(x) * 0.5);
      std::cout << "Computing sqrt of " << x << " to be " << result
                << " using log and exp" << std::endl;
    #else
      double result = x;
    

    运行cmake或cmake-gui来配置项目,然后使用所选的构建工具进行构建并运行Tutorial可执行文件。

    您会注意到,我们也没有使用logexp,即使我们认为它们应该是可用。 我们应该很快意识到,我们忘记在mysqrt.cxx中包含TutorialConfig.h

    我们还需要更新MathFunctions/CMakeLists.txt,以便mysqrt.cxx知道此文件的位置:

    target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_BINARY_DIR})
    

    完成此更新后,继续并再次构建项目,然后运行构建的Tutorial可执行文件。 如果仍未使用logexp,请从构建目录中打开生成的TutorialConfig.h文件。 也许它们在当前系统上不可用?

    哪个函数现在可以提供更好的结果,sqrtmysqrt

    5.1 指定编译定义

    除了在TutorialConfig.h中保存HAVE_LOGHAVE_EXP值,对我们来说还有更好的地方吗? 让我们尝试使用target_compile_definitions

    首先,从TutorialConfig.h.in中删除定义。 我们不再需要包含mysqrt.cxx中的TutorialConfig.hMathFunctions/CMakeLists.txt中的其他包含内容。

    接下来,我们可以将HAVE_LOGHAVE_EXP的检查移至MathFunctions/CMakeLists.txt,然后将这些值指定为PRIVATE编译定义。

    include(CheckSymbolExists)
    set(CMAKE_REQUIRED_LIBRARIES "m")
    check_symbol_exists(log "math.h" HAVE_LOG)
    check_symbol_exists(exp "math.h" HAVE_EXP)
    
    if(HAVE_LOG AND HAVE_EXP)
      target_compile_definitions(MathFunctions
                                 PRIVATE "HAVE_LOG" "HAVE_EXP")
    endif()
    

    完成这些更新后,继续并重新构建项目。 运行内置的Tutorial可执行文件,并验证结果与本步骤前面的内容相同。

    6 添加自定义命令和生成的文件(第6步)

    出于本教程的目的,假设我们决定不再使用平台logexp函数,而是希望生成一个可在mysqrt函数中使用的预计算值表。 在本节中,我们将在构建过程中创建表,然后将该表编译到我们的应用程序中。

    首先,让我们删除MathFunctions/CMakeLists.txt中对logexp函数的检查。 然后从mysqrt.cxx中删除对HAVE_LOGHAVE_EXP的检查。 同时,我们可以删除#include <cmath>

    MathFunctions子目录中,提供了一个名为MakeTable.cxx的新的源文件以生成表。

    查看完文件后,我们可以看到该表是作为有效的C++代码生成的,并且输出文件名作为参数传入。

    下一步是将适当的命令添加到MathFunctions/CMakeLists.txt文件中,以构建MakeTable可执行文件,然后在构建过程中运行它。 需要一些命令来完成此操作。

    首先,在MathFunctions/CMakeLists.txt的顶部,添加MakeTable的可执行文件,就像添加任何其他可执行文件一样。

    add_executable(MakeTable MakeTable.cxx)
    

    然后,我们添加一个自定义命令,该命令指定如何通过运行MakeTable生成Table.h

    add_custom_command(
      OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
      COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
      DEPENDS MakeTable
      )
    

    接下来,我们必须让CMake知道mysqrt.cxx依赖于生成的文件Table.h。 这是通过将生成的Table.h添加到库MathFunctions的源列表中来完成的。

    add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h )
    

    我们还必须将当前的二进制目录添加到include目录列表中,以便mysqrt.cxx可以找到并包含Table.h

    target_include_directories(MathFunctions
              INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
              PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
              )
    

    现在,我们来使用生成的表。 首先,修改mysqrt.cxx以包含Table.h。 接下来,我们可以重写mysqrt函数以使用该表:

    double mysqrt(double x)
    {
      if (x <= 0) {
        return 0;
      }
    
      // use the table to help find an initial value
      double result = x;
      if (x >= 1 && x < 10) {
        std::cout << "Use the table to help find an initial value " << std::endl;
        result = sqrtTable[static_cast<int>(x)];
      }
    
      // do ten iterations
      for (int i = 0; i < 10; ++i) {
        if (result <= 0) {
          result = 0.1;
        }
        double delta = x - (result * result);
        result = result + 0.5 * delta / result;
        std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
      }
    
      return result;
    }
    

    运行cmake或cmake-gui以配置项目,然后使用所选的构建工具进行构建。

    构建此项目时,它将首先构建MakeTable可执行文件。 然后它将运行MakeTable来生成Table.h。 最后,它将编译包括了Table.hmysqrt.cxx,以生成MathFunctions库。

    运行Tutorial可执行文件,并验证它是否正在使用该表。

    :wq# 7 构建一个安装程序(第7步)

    接下来,假设我们想将项目分发给其他人,以便他们可以使用它。 我们希望在各种平台上提供二进制和源代码。 这与我们之前在“安装和测试”(第4步)中进行的安装有些不同,在“安装和测试”中,我们是安装根据源代码构建的二进制文件。 在此示例中,我们将构建支持二进制安装和包管理功能的安装程序包。 为此,我们将使用CPack创建平台特定的安装程序。 具体来说,我们需要在顶级CMakeLists.txt文件的底部添加几行。

    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}")
    include(CPack)
    

    这就是全部需要做的事。 我们首先包含InstallRequiredSystemLibraries。 该模块将包括项目当前平台所需的任何运行时库。 接下来,我们将一些CPack变量设置为存储该项目的许可证和版本信息的位置。 版本信息是在本教程的前面设置的,并且license.txt已包含在此步骤的顶级源目录中。

    最后,我们包含CPack模块,该模块将使用这些变量和当前系统的其他一些属性来设置安装程序。

    下一步是以常规方式构建项目,然后在其上运行CPack。 要构建二进制发行版,请从二进制目录运行:

    cpack
    

    要指定生成器,请使用-G选项。 对于多配置构建,请使用-C指定配置。 例如:

    cpack -G ZIP -C Debug
    

    要创建源码分发,您可以输入:

    cpack --config CPackSourceConfig.cmake
    

    或者,运行make package或在IDE中右键单击Package目标和Build Project

    运行在二进制目录中找到的安装程序。 然后运行已安装的可执行文件,并验证其是否有效。

    8 添加Dashboard支持(第8步)

    添加支持以将测试结果提交到Dashboard非常容易。 我们已经在“测试支持”中为我们的项目定义了许多测试。 现在,我们只需要运行这些测试并将其提交到Dashboard即可。 为了包含对Dashboard的支持,我们在顶层CMakeLists.txt中包含了CTest模块。

    # enable dashboard scripting
    include(CTest)
    

    替换

    # enable testing
    enable_testing()
    

    CTest模块将自动调用enable_testing(),因此我们可以将其从CMake文件中删除。

    我们还需要在顶级目录中创建一个CTestConfig.cmake文件,在该目录中我们可以指定项目的名称以及提交Dashboard的位置。

    set(CTEST_PROJECT_NAME "CMakeTutorial")
    set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
    
    set(CTEST_DROP_METHOD "http")
    set(CTEST_DROP_SITE "my.cdash.org")
    set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
    set(CTEST_DROP_SITE_CDASH TRUE)
    

    CTest将在运行时读入该文件。 要创建一个简单的Dashboard,您可以运行cmake或cmake-gui来配置项目,但不构建它。 而是,将目录更改为二进制树,然后运行:

    ctest [-VV] -D Experimental
    

    请记住,对于多配置生成器(例如Visual Studio),必须指定配置类型:

    ctest [-VV] -C Debug -D Experimental
    

    或者,从IDE中构建Experimental目标。

    ctest将构建和测试项目,并将结果提交给Kitware公共仪表板Dashboard。 Dashboard的结果将被上传到Kitware的公共Dashboard:https://my.cdash.org/index.php?project=CMakeTutorial

    9 混合静态和动态库(第9步)

    在本节中,我们将展示如何使用BUILD_SHARED_LIBS变量来控制add_library的默认行为,并允许控制如何构建没有显式类型(STATIC,SHARED,MODULE或OBJECT)的库。

    为此,我们需要将BUILD_SHARED_LIBS添加到顶级CMakeLists.txt。 我们使用option命令,因为它允许用户可以选择该值是On还是Off。

    接下来,我们将重构MathFunctions使其成为使用mysqrt或sqrt封装的真实库,而不是要求调用代码执行此逻辑。 这也意味着USE_MYMATH将不会控制构建MathFuctions,而是将控制此库的行为。

    第一步是将顶级CMakeLists.txt的开始部分更新为:

    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(Tutorial VERSION 1.0)
    
    # specify the C++ standard
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    # control where the static and shared libraries are built so that on windows
    # we don't need to tinker with the path to run the executable
    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)
    
    # configure a header file to pass the version number only
    configure_file(TutorialConfig.h.in TutorialConfig.h)
    
    # add the MathFunctions library
    add_subdirectory(MathFunctions)
    
    # add the executable
    add_executable(Tutorial tutorial.cxx)
    target_link_libraries(Tutorial PUBLIC MathFunctions)
    

    既然我们已经使MathFunctions始终被使用,我们将需要更新该库的逻辑。 因此,在MathFunctions/CMakeLists.txt中,我们需要创建一个SqrtLibrary,当启用USE_MYMATH时有条件地对其进行构建。 现在,由于这是一个教程,我们将明确要求SqrtLibrary是静态构建的。

    最终结果是MathFunctions/CMakeLists.txt应该如下所示:

    # add the library that runs
    add_library(MathFunctions MathFunctions.cxx)
    
    # state that anybody linking to us needs to include the current source dir
    # to find MathFunctions.h, while we don't.
    target_include_directories(MathFunctions
                               INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                               )
    
    # should we use our own math functions
    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    if(USE_MYMATH)
    
      target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
    
      # first we add the executable that generates the table
      add_executable(MakeTable MakeTable.cxx)
    
      # add the command to generate the source code
      add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
        COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
        DEPENDS MakeTable
        )
    
      # library that just does sqrt
      add_library(SqrtLibrary STATIC
                  mysqrt.cxx
                  ${CMAKE_CURRENT_BINARY_DIR}/Table.h
                  )
    
      # state that we depend on our binary dir to find Table.h
      target_include_directories(SqrtLibrary PRIVATE
                                 ${CMAKE_CURRENT_BINARY_DIR}
                                 )
    
      target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
    endif()
    
    # define the symbol stating we are using the declspec(dllexport) when
    # building on windows
    target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
    
    # install rules
    install(TARGETS MathFunctions DESTINATION lib)
    install(FILES MathFunctions.h DESTINATION include)
    

    接下来,更新MathFunctions/mysqrt.cxx以使用mathfunctionsdetail命名空间:

    #include <iostream>
    
    #include "MathFunctions.h"
    
    // include the generated table
    #include "Table.h"
    
    namespace mathfunctions
    {
    namespace detail 
    {
    // a hack square root calculation using simple operations
    double mysqrt(double x)
    {
      if (x <= 0) 
        return 0;
    
      // use the table to help find an initial value
      double result = x;
      if (x >= 1 && x < 10) 
      {
        std::cout << "Use the table to help find an initial value " << std::endl;
        result = sqrtTable[static_cast<int>(x)];
      }
    
      // do ten iterations
      for (int i = 0; i < 10; ++i)
      {
        if (result <= 0) 
          result = 0.1;
        double delta = x - (result * result);
        result = result + 0.5 * delta / result;
        std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
      }
    
      return result;
    }
    }
    }
    

    我们还需要在tutorial.cxx中进行一些更改,以使其不再使用USE_MYMATH

    1. 始终包含MathFunctions.h
    2. 始终使用mathfunctions::sqrt
    3. 不要包含 cmath

    最后,更新 MathFunctions/MathFunctions.h以使用dll导出定义:

    #if defined(_WIN32)
    #  if defined(EXPORTING_MYMATH)
    #    define DECLSPEC __declspec(dllexport)
    #  else
    #    define DECLSPEC __declspec(dllimport)
    #  endif
    #else // non windows
    #  define DECLSPEC
    #endif
    
    namespace mathfunctions {
    double DECLSPEC sqrt(double x);
    }
    

    此时,如果您构建了所有内容,则会注意到链接失败,因为我们将没有位置独立代码的静态库与具有位置独立代码的库组合在一起。 解决方案是无论构建类型如何,都将SqrtLibrary的POSITION_INDEPENDENT_CODE目标属性显式设置为True。

     # state that SqrtLibrary need PIC when the default is shared libraries
      set_target_properties(SqrtLibrary PROPERTIES
                            POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                            )
    
      target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
    

    练习:我们修改了MathFunctions.h以使用dll导出定义。 使用CMake文档,您可以找到一个帮助器模块来简化此过程吗?

    10 添加生成器表达式(第10步)

    在构建系统生成期间会评估生成器表达式,以生成特定于每个构建配置的信息。

    在许多目标属性(例如LINK_LIBRARIES,INCLUDE_DIRECTORIES,COMPLIE_DEFINITIONS等)的上下文中允许生成器表达式。 在使用命令填充这些属性(例如target_link_libraries(),target_include_directories() ,target_compile_definitions()等)时,也可以使用它们。

    生成器表达式可用于启用条件链接,编译时使用的条件定义,条件包含目录等。 条件可以基于构建配置,目标属性,平台信息或任何其他可查询信息。

    生成器表达式有不同类型,包括逻辑,信息和输出表达式。

    逻辑表达式用于创建条件输出。 基本表达式是0和1表达式。$<0:...>导致空字符串,而<1:...>导致内容“…”。 它们也可以嵌套。

    生成器表达式的常见用法是有条件地添加编译器标志,例如用于语言级别或警告的标志。 一个不错的模式是将该信息与一个INTERFACE目标相关联,以允许该信息传播。 让我们从构造一个INTERFACE目标并指定所需的C++标准级别11开始,而不是使用CMAKE_CXX_STANDARD

    所以,下面的代码:

    # specify the C++ standard
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    

    将被替换为:

    add_library(tutorial_compiler_flags INTERFACE)
    target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
    

    接下来,我们为项目添加所需的编译器警告标志。 由于警告标志根据编译器的不同而不同,因此我们使用COMPILE_LANG_AND_ID生成器表达式来控制在给定一种语言和一组编译器ID的情况下应应用的标志,如下所示:

    set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU>")
    set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
    target_compile_options(tutorial_compiler_flags INTERFACE
     "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
     "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
    )
    

    查看此内容,我们看到警告标志封装在BUILD_INTERFACE条件内。 这样做是为了使我们已安装项目的使用者不会继承我们的警告标志。

    练习:修改MathFunctions/CMakeLists.txt,以便所有目标都具有对tutorial_compiler_flags target_link_libraries()调用。

    11 增加输出配置(第11步)

    在本教程的“安装和测试”(第4步)中,我们添加了CMake的功能,以安装项目的库和头文件。 在"构建安装程序"(第7步)期间,我们添加了打包此资料的功能,以便可以将其分发给其他人。

    下一步是添加必要的信息,以便其他CMake项目可以使用我们的项目,无论是从构建目录,本地安装还是打包的文件。

    第一步是更新我们的install(TARGETS)命令,不仅要指定DESTINATION,还要指定EXPORTEXPORT关键字生成并安装一个CMake文件,该文件包含用于从安装树中导入install命令中列出的所有目标的代码。 因此,让我们继续,通过更新MathFunctions/CMakeLists.txt中的install命令显式EXPORTMathFunctions库,如下所示:

    install(TARGETS MathFunctions tutorial_compiler_flags
            DESTINATION lib
            EXPORT MathFunctionsTargets)
    install(FILES MathFunctions.h DESTINATION include)
    

    现在我们已经导出了MathFunctions,我们还需要显式安装生成的MathFunctionsTargets.cmake文件。 这是通过将以下内容添加到顶级CMakeLists.txt的底部来完成的:

    install(EXPORT MathFunctionsTargets
      FILE MathFunctionsTargets.cmake
      DESTINATION lib/cmake/MathFunctions
    )
    

    此时,您应该尝试运行CMake。 如果一切设置正确,您将看到CMake将生成如下错误:

    Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains
    path:
    
      "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions"
    
    which is prefixed in the source directory.
    

    CMake试图说的是,在生成导出信息的过程中,它将导出与当前机器固有联系的路径,并且在其他机器上无效。 解决方案是更新MathFunctions target_include_directories,以了解从构建目录和install/包中使用它时需要不同的INTERFACE位置。 这意味着将MathFunctions的target_include_directories调用转换为:

    target_include_directories(MathFunctions
                               INTERFACE
                                $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                                $<INSTALL_INTERFACE:include>
                               )
    

    更新后,我们可以重新运行CMake并确认它不再发出警告。

    至此,我们已经正确地打包了CMake所需的目标信息,但仍然需要生成MathFunctionsConfig.cmake,以便CMake find_package命令可以找到我们的项目。 因此,我们继续将名为Config.cmake.in新文件添加到项目顶层项目的顶层目录,其内容如下:

    @PACKAGE_INIT@
    
    include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
    

    然后,要正确配置和安装该文件,请将以下内容添加到顶级CMakeLists.txt的底部:

    install(EXPORT MathFunctionsTargets
      FILE MathFunctionsTargets.cmake
      DESTINATION lib/cmake/MathFunctions
    )
    
    include(CMakePackageConfigHelpers)
    # generate the config file that is includes the exports
    configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
      "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
      INSTALL_DESTINATION "lib/cmake/example"
      NO_SET_AND_CHECK_MACRO
      NO_CHECK_REQUIRED_COMPONENTS_MACRO
      )
    # generate the version file for the config file
    write_basic_package_version_file(
      "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
      VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
      COMPATIBILITY AnyNewerVersion
    )
    
    # install the configuration file
    install(FILES
      ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
      DESTINATION lib/cmake/MathFunctions
      )
    

    至此,我们为项目生成了可重定位的CMake配置,可以在安装或打包项目后使用它。 如果我们也希望从构建目录中使用我们的项目,则只需将以下内容添加到顶级CMakeLists.txt的底部:

    export(EXPORT MathFunctionsTargets
      FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
    )
    

    通过此导出调用,我们现在生成一个Targets.cmake,允许在构建目录中配置的MathFunctionsConfig.cmake由其他项目使用,而无需安装它。

    12 导入一个CMake项目(消费者)

    本示例说明项目如何查找生成Config.cmake文件的其他CMake软件包。

    它还显示了在生成Config.cmake时如何声明项目的外部依赖关系。

    13 打包调试和发布(多个包)

    默认情况下,CMake的模型是一个构建目录仅包含一个配置,可以是Debug,Release,MinSizeRel或RelWithDebInfo。

    但是可以将CPack设置为同时捆绑多个构建目录,以构建一个包含同一项目的多个配置的软件包。

    首先,我们需要构建一个名为multi_config的目录,该目录将包含我们要打包在一起的所有构建。

    其次,在multi_config下创建一个debugrelease目录。 最后,您应该具有如下布局:

    ─ multi_config
        ├── debug
        └── release
    

    现在,我们需要设置调试和发布版本,这大致需要以下内容:

    cmake -DCMAKE_BUILD_TYPE=Debug ../../../MultiPackage/
    cmake --build .
    cd ../release
    cmake -DCMAKE_BUILD_TYPE=Release ../../../MultiPackage/
    cmake --build .
    cd ..
    

    既然调试和发行版本均已完成,我们就可以使用自定义的MultiCPackConfig.cmake文件将两个版本打包到一个发行版中。

    cpack --config ../../MultiPackage/MultiCPackConfig.cmake
    

    版权声明:本文翻译自CMake tutorial v3.16》。未经作者允许严禁用于商业出版否则追究法律责任。网络转载请注明出处!!!


    展开全文
  • CMake 教程

    万次阅读 2012-07-06 11:37:47
    CMake是一个跨平台的程序构建工具,比如起...本文件不介绍CMake的基本语法,下面是篇不错的入门教程: http://www.ibm.com/developerworks/cn/linux/l-cn-cmake/   一,首先构建比较简单的工程,来对CMake有个简单

    CMake是一个跨平台的程序构建工具,比如起自己编写Makefile方便很多。

    介绍:http://baike.baidu.com/view/1126160.htm

    本文件不介绍CMake的基本语法,下面是篇不错的入门教程:

    http://www.ibm.com/developerworks/cn/linux/l-cn-cmake/

     

    一,首先构建比较简单的工程,来对CMake有个简单的了解

    1.构建一个工程的时候,需要做的几件事情(如果下面几件事你知道怎么做了,多大的工程就都不是问题了):

    A.源代码在哪里?

    B.头文件在哪里?

    C.怎么生成静态或者动态库?

    D.程序链接的静态库在哪里?

    E.如果工程的代码存放在很多地方,那又该怎么找到它们呢?

     

    2.下面从一个最简单的HelloWorld开始,然后一步一步构建一个比较复杂的工程:

    A.工程文件如下(只有一个helloworld.cpp源文件):

    ciw@ubuntu:~/my_projects/test$ tree .
    └── helloworld.cpp

     

    B.这时候,我们要写一个CMakeLists.txt(就3句代码):

    PROJECT(hello)                                             #定义整个CMake的工程名
    FILE(GLOB SOURCE "./*.cpp")                      #告诉CMake:工程的源文件在哪里?
    ADD_EXECUTABLE(hello ${SOURCE})         #告诉CMake:我们要生成可执行文件(hello),并且源文件是由上面定义的

    这样就可以在目录下生成一个hello可执行文件了。

     

    3.下面,我们讲一个带头文件的,而且文件都是存放在不同地方:

    A.代码分布如下(main.cpp是main函数,里面调用了hello.h声明的SayHello,定义在hello.cpp中):

    ciw@ubuntu:~/my_projects/test$ tree .
    ├── CMakeLists.txt
    ├── hello
    │   └── hello.cpp
    ├── include
    │   └── hello.h
    └── main
        └── main.cpp

    B.main的代码:

      #include "hello.h“
      int main()
       {
           SayHello();
           return 0;
       }

    C.CMakeLists.txt:

    1 PROJECT(hello_2)

    3 FILE(GLOB SOURCE_1 "${CMAKE_SOURCE_DIR}/main/*.cpp") //下面2句告诉CMake,源文件在哪里?main.cpp
    4 FILE(GLOB SOURCE_2 "${CMAKE_SOURCE_DIR}/hello/*.cpp")//程序必须链接到hello.cpp里面的SayHello

    6 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include/")//告诉CMake头文件在哪里?

    8 ADD_EXECUTABLE(hello_2 ${SOURCE_1} ${SOURCE_2})

     

    4.最后来一个复杂的工程:

    A.工程描述:

    该工程由一个静态库和一个可执行文件组成,头文件和源文件都不在同一个目录,并且CMake也是独立在一个目录

     

    B.代码分布:

    ciw@ubuntu:~/my_projects/test$ tree .
    test                                                         //工程根目录
    ├── cmake                                           //CMake目录:代码和CMake编译目录分离
    │   ├── bin
    │   │   └── main                                  //生成后的可执行文件
    │   ├── CMakeLists.txt
    │   ├── lib
    │   │   └── libhello.a                          //生成后的静态库
    │   └── src
    │       ├── CMakeLists.txt
    │       ├── hello
    │       │   └── CMakeLists.txt
    │       └── main
    │           └── CMakeLists.txt
    ├── hello                                              //hello静态库的源代码
    │   └── hello.cpp
    ├── include                                          //头文件
    │   └── hello.h
    └── main                                              //可执行文件的源代码目录
        └── main.cpp
    可以看出,CMake里面的每一个子目录都有一个CMakeLists.txt

    C.编译该工程的过程:

    (a).首先,需要给CMake的一个总入口,这个CMake设置一些全局的变量(cmake/CMakeLists.txt):

    PROJECT(hello_3)

    SET(PROJECT_ROOT_PATH "${CMAKE_SOURCE_DIR}/../")                 #工程的根目录,即test
    SET(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin/")    #可执行生成后存放的目录(CMAKE_SOURCE_DIR是cmake目录)
    SET(LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/lib/")             #静态库生成后存放的目录

    INCLUDE_DIRECTORIES("${PROJECT_ROOT_PATH}/include/")            #告诉CMake头文件在哪里?

    LINK_DIRECTORIES("${CMAKE_SOURCE_DIR}/lib/")                             #告诉CMake静态库在哪里?

    ADD_SUBDIRECTORY(src)                                                                     #多目录,把src目录加进来,src里面才是真正编译main和hello的

    (b).src目录下的CMakeList.txt,这个CMake只是简单地把main目录和hello目录链接起来:

    ADD_SUBDIRECTORY(main)
    ADD_SUBDIRECTORY(hello)

    (c).hello静态库:

    FILE(GLOB SOURCE_1 "${PROJECT_ROOT_PATH}/hello/*.cpp")      #告诉CMake源文件在哪里?
    ADD_LIBRARY(hello STATIC ${SOURCE_1})                                       #告诉CMake生成的是一个静态库

     

    (d).main可执行文件:

    FILE(GLOB SOURCE_1 "${PROJECT_ROOT_PATH}/main/*.cpp")     #告诉CMake源文件在哪里?

    ADD_EXECUTABLE(main ${SOURCE_1})                                          #告诉CMake生成一个main可执行文件
    TARGET_LINK_LIBRARIES(main hello)                                              #告诉CMake静态库在哪里?

     

    整个编译的过程就是这样了,最主要的是记住上面”构建一个工程的时候,需要做的几件事情:头文件,源文件,静态库这个元素“,那么再复杂的工程都是这样一步一步构建的

    展开全文
  • Cmake教程

    千次阅读 2013-03-13 10:24:36
    CMake 简介 CMake 是一个跨平台的自动化建构系统,它使用一个名为 CMakeLists.txt 的文件来描述构建过程,可以产生标准的构建文件,如 Unix 的 Makefile 或Windows Visual C++ 的 projects/workspaces 。文件 ...

    CMake 简介

    CMake 是一个跨平台的自动化建构系统,它使用一个名为 CMakeLists.txt 的文件来描述构建过程,可以产生标准的构建文件,如 Unix 的 Makefile 或Windows Visual C++ 的 projects/workspaces 。文件 CMakeLists.txt 需要手工编写,也可以通过编写脚本进行半自动的生成。CMake 提供了比 autoconfig 更简洁的语法。在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

    1. 编写 CmakeLists.txt
    2. 执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile ( PATH  CMakeLists.txt 所在的目录 )
    3. 使用 make 命令进行编译。

    第一个工程

    现假设我们的项目中只有一个源文件 main.cpp


    清单 1 源文件 main.cpp
    1 #include<iostream>
    2 
    3 int main()
    4 {
    5     std::cout<<"Hello word!"<<std::endl;
    6     return 0;
    7 }
    

    为了构建该项目,我们需要编写文件 CMakeLists.txt 并将其与 main.cpp 放在 同一个目录下:


    清单 2 CMakeLists.txt
    1 PROJECT(main)
    2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
    3 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
    4 ADD_EXECUTABLE(main ${DIR_SRCS})
    

    CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的,符号"#"后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。例如对于清单2的 CMakeLists.txt 文件:第一行是一条命令,名称是 PROJECT ,参数是 main ,该命令表示项目的名称是 main 。第二行的命令限定了 CMake 的版本。第三行使用命令 AUX_SOURCE_DIRECTORY 将当前目录中的源文件名称赋值给变量 DIR_SRCS 。 CMake 手册中对命令 AUX_SOURCE_DIRECTORY 的描述如下:

    aux_source_directory(<dir> <variable>) 

    该命令会把参数 <dir> 中所有的源文件名称赋值给参数 <variable> 。 第四行使用命令 ADD_EXECUTABLE 指示变量 DIR_SRCS 中的源文件需要编译 成一个名称为 main 的可执行文件。

    完成了文件 CMakeLists.txt 的编写后需要使用 cmake 或 ccmake 命令生成Makefile 。 ccmake 与命令 cmake 的不同之处在于 ccmake 提供了一个图形化的操作界面。cmake 命令的执行方式如下:

    cmake [options] <path-to-source> 
    

    这里我们进入了 main.cpp 所在的目录后执行 “cmake .” 后就可以得到 Makefile 并使用 make 进行编译,如下图所示。


    图 1. camke 的运行结果
    camke 的运行结果 

    处理多源文件目录的方法

    CMake 处理源代码分布在不同目录中的情况也十分简单。现假设我们的源代码分布情况如下:


    图 2. 源代码分布情况
    源代码分布情况 

    其中 src 目录下的文件要编译成一个链接库。

    第一步,项目主目录中的 CMakeLists.txt

    在目录 step2 中创建文件 CMakeLists.txt 。文件内容如下:


    清单 3 目录 step2 中的 CMakeLists.txt
    1 PROJECT(main)
    2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 
    3 ADD_SUBDIRECTORY( src )
    4 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
    5 ADD_EXECUTABLE(main ${DIR_SRCS}  )
    6 TARGET_LINK_LIBRARIES( main Test )
    

    相对于清单 2,该文件添加了下面的内容: 第三行,使用命令 ADD_SUBDIRECTORY 指明本项目包含一个子目录 src 。第六行,使用命令 TARGET_LINK_LIBRARIES 指明可执行文件 main 需要连接一个名为Test的链接库 。

    第二步,子目录中的 CmakeLists.txt

    在子目录 src 中创建 CmakeLists.txt。文件内容如下:


    清单 4. 目录 src 中的 CmakeLists.txt
    1 AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)
    2 ADD_LIBRARY ( Test ${DIR_TEST1_SRCS})
    

    在该文件中使用命令 ADD_LIBRARY 将 src 目录中的源文件编译为共享库。

    第三步,执行 cmake

    至此我们完成了项目中所有 CMakeLists.txt 文件的编写,进入目录 step2 中依次执行命令 “cmake .” 和 “make” 得到结果如下:


    图3. 处理多源文件目录时 cmake 的执行结果
    处理多源文件目录时 cmake 的执行结果 

    在执行 cmake 的过程中,首先解析目录 step2 中的 CMakeLists.txt ,当程序执行命令 ADD_SUBDIRECTORY( src ) 时进入目录 src 对其中的 CMakeLists.txt 进行解析。

    在工程中查找并使用其他程序库的方法

    在开发软件的时候我们会用到一些函数库,这些函数库在不同的系统中安装的位置可能不同,编译的时候需要首先找到这些软件包的头文件以及链接库所在的目录以便生成编译选项。例如一个需要使用博克利数据库项目,需要头文件db_cxx.h 和链接库 libdb_cxx.so ,现在该项目中有一个源代码文件 main.cpp ,放在项目的根目录中。

    第一步,程序库说明文件

    在项目的根目录中创建目录 cmake/modules/ ,在 cmake/modules/ 下创建文件 Findlibdb_cxx.cmake ,内容如下:


    清单 5. 文件 Findlibdb_cxx.cmake
    01 MESSAGE(STATUS "Using bundled Findlibdb.cmake...")
    02 
    03 FIND_PATH(
    04   LIBDB_CXX_INCLUDE_DIR
    05   db_cxx.h 
    06   /usr/include/ 
    07   /usr/local/include/ 
    08   )
    09 
    10 FIND_LIBRARY(
    11   LIBDB_CXX_LIBRARIES NAMES  db_cxx
    12   PATHS /usr/lib/ /usr/local/lib/
    13   )
    
    
    

    文件 Findlibdb_cxx.cmake 的命名要符合规范: FindlibNAME.cmake ,其中NAME 是函数库的名称。Findlibdb_cxx.cmake 的语法与 CMakeLists.txt 相同。这里使用了三个命令: MESSAGE , FIND_PATH 和 FIND_LIBRARY 。

    • 命令 MESSAGE 将参数的内容输出到终端
    • 命令 FIND_PATH 指明头文件查找的路径,原型如下 
      find_path(<VAR> name1 [path1 path2 ...]) 
      该命令在参数 path* 指示的目录中查找文件 name1 并将查找到的路径保存在变量 VAR 中。清单538行的意思是在 /usr/include/  /usr/local/include/ 中查找文件db_cxx.h ,并将 db_cxx.h 所在的路径保存在 LIBDB_CXX_INCLUDE_DIR 中。
    • 命令 FIND_LIBRARY  FIND_PATH 类似,用于查找链接库并将结果保存在变量中。清单51013行的意思是在目录/usr/lib/  /usr/local/lib/ 中寻找名称为 db_cxx 的链接库,并将结果保存在 LIBDB_CXX_LIBRARIES 

    第二步, 项目的根目录中的 CmakeList.txt

    在项目的根目录中创建 CmakeList.txt :


    清单 6. 可以查找链接库的 CMakeList.txt
    01 PROJECT(main)
    02 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
    03 SET(CMAKE_SOURCE_DIR .)
    04 SET(CMAKE_MODULE_PATH ${CMAKE_ROOT}/Modules ${CMAKE_SOURCE_DIR}/cmake/modules) 
    05 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
    06 ADD_EXECUTABLE(main ${DIR_SRCS})
    07 
    08 FIND_PACKAGE( libdb_cxx REQUIRED)
    09 MARK_AS_ADVANCED(
    10 LIBDB_CXX_INCLUDE_DIR
    11 LIBDB_CXX_LIBRARIES
    12 )
    13 IF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
    14 MESSAGE(STATUS "Found libdb libraries")
    15    INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})
    16     MESSAGE( ${LIBDB_CXX_LIBRARIES} )
    17     TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES}
    18 )
    19 ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
    
    

    在该文件中第4行表示到目录 ./cmake/modules 中查找 Findlibdb_cxx.cmake ,8-19 行表示查找链接库和头文件的过程。第8行使用命令 FIND_PACKAGE 进行查找,这条命令执行后 CMake 会到变量 CMAKE_MODULE_PATH 指示的目录中查找文件 Findlibdb_cxx.cmake 并执行。第13-19行是条件判断语句,表示如果 LIBDB_CXX_INCLUDE_DIR 和 LIBDB_CXX_LIBRARIES 都已经被赋值,则设置编译时到 LIBDB_CXX_INCLUDE_DIR 寻找头文件并且设置可执行文件 main 需要与链接库 LIBDB_CXX_LIBRARIES 进行连接。

    第三步,执行 cmake

    完成 Findlibdb_cxx.cmake 和 CMakeList.txt 的编写后在项目的根目录依次执行 “cmake . ” 和 “make ” 可以进行编译,结果如下图所示:


    图 4. 使用其他程序库时 cmake 的执行结果
    使用其他程序库时 cmake 的执行结果 

    使用 cmake 生成 debug 版和 release 版的程序

    在 Visual Studio 中我们可以生成 debug 版和 release 版的程序,使用 CMake 我们也可以达到上述效果。debug 版的项目生成的可执行文件需要有调试信息并且不需要进行优化,而 release 版的不需要调试信息但需要优化。这些特性在 gcc/g++ 中是通过编译时的参数来决定的,如果将优化程度调到最高需要设置参数-O3,最低是 -O0 即不做优化;添加调试信息的参数是 -g -ggdb ,如果不添加这个参数,调试信息就不会被包含在生成的二进制文件中。

    CMake 中有一个变量 CMAKE_BUILD_TYPE ,可以的取值是 Debug Release RelWithDebInfo 和 MinSizeRel。当这个变量值为 Debug 的时候,CMake 会使用变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项生成 Makefile ,当这个变量值为 Release 的时候,工程会使用变量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项生成 Makefile。

    现假设项目中只有一个文件 main.cpp ,下面是一个可以选择生成 debug 版和 release 版的程序的 CMakeList.txt :


    清单 7
    1 PROJECT(main)
    2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
    3 SET(CMAKE_SOURCE_DIR .)
    4 
    5 SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
    6 SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
    7 
    8 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
    9 ADD_EXECUTABLE(main ${DIR_SRCS})
    

    第 5 和 6 行设置了两个变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_CXX_FLAGS_RELEASE, 这两个变量是分别用于 debug 和 release 的编译选项。 编辑 CMakeList.txt 后需要执行 ccmake 命令生成 Makefile 。在进入项目的根目录,输入 "ccmake ." 进入一个图形化界面,如下图所示:


    图 5. ccmake 的界面
    ccmake 的界面 

    按照界面中的提示进行操作,按 "c" 进行 configure ,这时界面中显示出了配置变量 CMAKE_BUILD_TYPE 的条目。如下图所示:


    图 6. 执行了 configure 以后 ccmake 的界面
    执行了 configure 以后 ccmake 的界面 

    下面我们首先生成 Debug 版的 Makefile :将变量 CMAKE_BUILD_TYPE 设置为 Debug ,按 "c" 进行 configure ,按 "g" 生成 Makefile 并退出。这时执行命令 find * | xargs grep "O0" 后结果如下:


    清单 8 find * | xargs grep "O0"的执行结果
    CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O0 -Wall -g -ggdb 
    CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O0 -Wall -g -ggdb 
    CMakeFiles/main.dir/main.cpp.o -o main -rdynamic 
    CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
    

    这个结果说明生成的 Makefile 中使用了变量 CMAKE_CXX_FLAGS_DEBUG 作为编译时的参数。

    下面我们将生成 Release 版的 Makefile :再次执行命令 "ccmake ." 将变量CMAKE_BUILD_TYPE 设置为 Release ,生成 Makefile 并退出。执行命令 find * | xargs grep "O0" 后结果如下:


    清单 9 find * | xargs grep "O0"的执行结果
    CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
    

    而执行命令 find * | xargs grep "O3" 后结果如下:


    清单 10. find * | xargs grep "O3"的执行结果
    CMakeCache.txt:CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
    CMakeCache.txt:CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
    CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O3 -Wall 
    CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O3 -Wall 
    CMakeFiles/main.dir/main.cpp.o -o main -rdynamic 
    CMakeLists.txt:SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
    

    这两个结果说明生成的 Makefile 中使用了变量 CMAKE_CXX_FLAGS_RELEASE 作为编译时的参数。




    Cmake条件编译宏定义,环境变量宏定义。

    #ifdef SENSOR_GPS
    #include "sensor_gps.h"
    #endif
    CMakeLists.txt中的写法:
    add_definitions(-DSENSOR_GPS)



    cmake 中写法:

    add_definitions(-DENABLE_V_STATUS_REPORT)

    cpp中写法:

    #ifdef ENABLE_V_STATUS_REPORT

          fun();

    #endif









    展开全文
  • CMAKE 教程

    2016-01-20 19:27:00
    前言cmake 已经开发了 5,6 年的时间,如果没有 KDE4,也许不会有人或者 Linux 发行版本重视cmake,因为除了 Kitware 似乎没有人使用它。通过 KDE4 的选型和开发,cmake 逐渐进入了人们的视线,在实际的使用过程中,...

空空如也

1 2 3 4 5 ... 20
收藏数 2,664
精华内容 1,065
关键字:

cmake教程