美文网首页JNIJNIC++
CMake使用总结

CMake使用总结

作者: 荷包蛋酱 | 来源:发表于2017-03-02 15:55 被阅读2697次

    1.安装

     $sudo apt-get install cmake


    2.示例:简单的文件目录

        sample |——Demo  (盛放可执行程序binary directory)

                                |——CMakeLists.txt (内容为:include_directories                                                                                                      (${HELLO_SOURCE_DIR}/Hello                                                                                                    #确认compiler能在Hello 库中找到它include的库

                                                                 link_directories (${HELLO_BINARY_DIR}/Hello)                                                                          #确认一旦built时,linker能找到Hello库

                                                                add_executable (helloDemo demo.cxx demo_b.cxx)                                                                  #可执行文件叫做helloDemo,它的源码文件是"demo.cxx"                                                              #和"demo_b.cxx"

                                                               target_link_libraries (helloDemo Hello)                                                                                          #link可执行文件helloDemo 到Hello lib       )

                    | ——Hello  (盛放源代码source directory)

                                |——CMakeLists.txt (内容为:add_library (Hello hello.cxx)                                                                                              #创建Hello 库,源文件为hello.cxx)                      

                    |——CMakeLists.txt  (内容为:project (HELLO) #工程名

                                                                      add_subdirectory (Hello) # 子目录,路径为                                                                                   ${HELLO_SOURCE_DIR})

                                                                      add_subdirectory (Demo) #子目录 ,路径为                                                                                 ${HELLO_BINARY_DIR}   )


    3.CMake 执行过程

          CMake在主目录执行时,会处理该目录下CMakeLists.txt文件,然后进入到子目录,处理子目录下的CMakeLists.txt.

          从字面上看,我们差不多可以理解这三个文件的涵义。第一个CMakeLists.txt文件指定包含Hello和Demo两个子目录。第二个Hello中的CMakeLists.txt文件则指定生成Hello库文件,第三个Demo中的CMakeLists.txt文件则是生成一个可执行文件helloDemo,另外两个附加语句则用来指明头文件路径以及所要链接的库。虽然要写三个CMakeLists文件,但每个文件都非常简单,总共算起来,并不比一个Makefile文件多多少。更重要的是,这其中隐含着linux哲学:分而治之。每个模块自行编写配置文件,只负责自己份内的事务,所以可扩展性好。在进行大型项目开发,就可以体现出优势了。

          CMakeLists.txt相当于定义了一套生成Makefile文件的规则,下面就可以生成Makefile文件了,命令如下:

    $cmake .

    表示当前目录,如果CMakeLists.txt不在当前目录,请在cmake后面指定。命令执行后,在主目录下和Demo、Hello子目录下均会生成一个Makefile文件,有了这个文件,我们就可以敲入make编译目标程序了。


    4.项目文件组织

          我们从一个sample入手,了解了CMake的基本用法和语法。但这个例子与实际开发还有一段距离,主要存在以下几点问题:                                                                                                        1.生成的二进制程序和源程序混在一起                                                                                           2.使用gcc进程程序编译,而不是使用交叉编译工具                                                                       3.为指定编译选项,通常会生成debug版本供调试用,release版本用于发布

          一个项目,通常包含若干子模块。比如上一篇的sample,我们可以认为它包含两个子模块,Hello为程序库,Demo为主程序。很少有项目会把目标二进制文件和源程序放在一起的,通常会建立一个bin目录,存放生成的二进制文件,发布程序则放在release。根据我在项目开发中的习惯,将目录结构修改如下:

    CMakeSample

    |--- release  (存放程序发布相关文件,包括程序文件、脚本、参数等。)

    |--- doc(项目开发中的相关文档,如设计说明以及通过doxgen等工具从代码中生成的文档。)

    |--- lib(存放项目中使用的第三方库)

    |--- source(自己编写的库不放在lib,应该作为项目的一个模块放在source目录下。)

    |--- include (包含整个项目中使用的公共头文件,若子模块中的头文件仅被它使用,不放)

    |--- bin (bin目录存放编译后的调试版本代码。)

    |--- Hello

    |--- Demo

    其它的子目录则为各模块的代码及头文件。

    按照以上目录结构,将Hello下的hello.h移到include目录,因为这个头文件被Demo模块包含。这个sample中未使用第三方库,所以暂时为空。


    5.CMake的内置变量

    从上文中我们知道,通过set语句可以自定义变量,然而,CMake还包含大量的内置变量,这些变量和自定义变量的用法没有区别,下面就列出一些常用的变量:

    CMAKE_C_COMPILER

    指定C编译器,通常,CMake运行时能够自动检测C语言编译器。进行嵌入式系统开发时,通常需要设置此变量,指定交叉编译器。

    CMAKE_CXX_COMPILER

    指定C++编译器

    CMAKE_C_FLAGS

    指定编译C文件时编译选项,比如-g指定产生调试信息。也可以通过add_definitions命令添加编译选项。

    EXECUTABLE_OUTPUT_PATH

    指定可执行文件存放的路径

    LIBRARY_OUTPUT_PATH

    指定库文件放置的路径

    CMAKE_BUILD_TYPE     build 类型(Debug, Release),-DCMAKE_BUILD_TYPE=Debug

    BUILD_SHARED_LIBS     Switch between shared and static libraries

    内置变量的使用:

    >> 在CMakeLists.txt中指定,使用set

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


    6.CMake的常用命令

    除了内置变量,我们还可以通过命令来修改编译选项,现将一些常用的命令列出来:

    include_directories

    指定头文件的搜索路径,相当于指定gcc编译器的-I参数

    link_directories

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

    add_subdirectory

    包含子目录,当工程包含多个子目录时,此命令有用

    add_definitions

    添加编译参数,比如add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义

    add_executable

    编译可执行程序

    target_link_libraries

    指定链接库,相同于指定-l参数

    7.CMake语法介绍

    CMake语法非常简单,包含注释、命令和空格。以#开头的行为注释行,命令则由命令名、括号及以空格进行分隔的参数组成。命令可以是诸如add_library这样的内置命令,也可以是子定义的宏或者函数。CMake的输入是主目录下的CMakeLists.txt文件,该文件可以使用include或者add_directory命令添加其它的输入文件。

    命令的形式如下:

    command (args ...)

    其中command为命令名,args为空格分隔的参数列表,如果参数中包含空格,使用双引号引起来。命令不区分大小写。

    lists and strings. CMake的基本数据类型为字符串,字符串又可以组成list类型,有两种方式:一种通过分号分隔,一种通过空格分隔。比如以下例子给VAR赋了同样的值:

    set(VAR a;b;c)    set(VAR a b c)

    字符串列表主要用于foreach进行迭代,有些命令也用于对list进行处理。

    CMake支持字符串和list类型的简单变量,变量以${VAR}形式引用。多个参数可以用set命令组成一个list,命令将展开list,例如:

    set(Foo a b c)

    command(${Foo})

    等价于

    command(a b c)

    如果你希望将list当作一个参数传递给命令,就应该用双引号把list引起来,如command("${Foo}")等价于command("a b c")

    流程控制

    写CMakeLists.txt文件就象写一个简单的程序,CMake提供了三种流程控制结构:

    条件语句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)

    循环结构

    set(VAR a b c)

    # loop over a, b,c with the variable f

    foreach(f ${VAR})

    some_command(${f})

    endforeach(f)

    宏和函数,函数在2.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)

    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

    8. FAQ

    1)  怎样获得一个目录下的所有源文件

    >> aux_source_directory( )

    >> 将dir中所有源文件(不包括头文件)保存到变量variable中,然后可以add_executable (ss7gw ${variable})这样使用。

    2)  怎样指定项目编译目标

    >>  project命令指定

    3)  怎样添加动态库和静态库

    >> target_link_libraries命令添加即可

    4)  怎样在执行CMAKE时打印消息

    >> message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

    >> 注意大小写

    5)  怎样指定头文件与库文件路径

    >> include_directories与link_directories

    >>可以多次调用以设置多个路径

    >> link_directories仅对其后面的targets起作用

    6)  怎样区分debug、release版本

    >>建立debug/release两目录,分别在其中执行cmake -DCMAKE_BUILD_TYPE=Debug(或Release),需要编译不同版本时进入不同目录执行make即可;

    Debug版会使用参数-g;Release版使用-O3 –DNDEBUG

    >> 另一种设置方法——例如DEBUG版设置编译参数DDEBUG

    IF(DEBUG_mode)

    add_definitions(-DDEBUG)

    ENDIF()

    在执行cmake时增加参数即可,例如cmake -D DEBUG_mode=ON

    7)  怎样设置条件编译

    例如debug版设置编译选项DEBUG,并且更改不应改变CMakelist.txt

    >> 使用option command,eg:

    option(DEBUG_mode "ON for debug or OFF for release" ON)

    IF(DEBUG_mode)

    add_definitions(-DDEBUG)

    ENDIF()

    >> 使其生效的方法:首先cmake生成makefile,然后make edit_cache编辑编译选项;Linux下会打开一个文本框,可以更改,该完后再make生成目标文件——emacs不支持make edit_cache;

    >> 局限:这种方法不能直接设置生成的makefile,而是必须使用命令在make前设置参数;对于debug、release版本,相当于需要两个目录,分别先cmake一次,然后分别make edit_cache一次;

    >> 期望的效果:在执行cmake时直接通过参数指定一个开关项,生成相应的makefile——可以这样做,例如cmake –DDEBUGVERSION=ON

    8)  怎样添加编译宏定义

    >> 使用add_definitions命令,见命令部分说明

    9)  怎样添加编译依赖项

    用于确保编译目标项目前依赖项必须先构建好

    >>add_dependencies

    10)        怎样指定目标文件目录

    >> 建立一个新的目录,在该目录中执行cmake生成Makefile文件,这样编译结果会保存在该目录——类似

    >> SET_TARGET_PROPERTIES(ss7gw PROPERTIES

    RUNTIME_OUTPUT_DIRECTORY "${BIN_DIR}")

    11)        很多文件夹,难道需要把每个文件夹编译成一个库文件?

    >> 可以不在子目录中使用CMakeList.txt,直接在上层目录中指定子目录

    12)        怎样设定依赖的cmake版本

    >>cmake_minimum_required(VERSION 2.6)

    13)        相对路径怎么指定

    >> ${projectname_SOURCE_DIR}表示根源文件目录,${ projectname _BINARY_DIR}表示根二进制文件目录?

    14)        怎样设置编译中间文件的目录

    >> TBD

    15)        怎样在IF语句中使用字串或数字比较

    >>数字比较LESS、GREATER、EQUAL,字串比STRLESS、STRGREATER、STREQUAL,

    >> Eg:

    set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)

    set(AAA abc)

    IF(AAA STREQUAL abc)

    message(STATUS "true")   #应该打印true

    ENDIF()

    16)        更改h文件时是否只编译必须的cpp文件

    >> 是

    17)        机器上安装了VC7和VC8,CMAKE会自动搜索编译器,但是怎样指定某个版本?

    >> TBD

    18)        怎样根据OS指定编译选项

    >> IF( APPLE ); IF( UNIX ); IF( WIN32 )

    19)        能否自动执行某些编译前、后命令?

    >> 可以,TBD

    20)        怎样打印make的输出

    make VERBOSE=1

    相关文章

      网友评论

        本文标题:CMake使用总结

        本文链接:https://www.haomeiwen.com/subject/msahgttx.html