zoukankan      html  css  js  c++  java
  • Cmake使用

    CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。

    cmake官网:http://www.cmake.org/

    cmake is the cross-platform, open-source build system. CMake is a family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files. CMake generates native makefiles and workspaces that can be used in the compiler environment of your choice.

       CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。

    CMake 使用方法

        CMake的所有的语句都写在一个叫:CMakeLists.txt的文件中。当CMakeLists.txt文件确定后,可以用ccmake命令对相关 的变量值进行配置。这个命令必须指向CMakeLists.txt所在的目录。配置完成之后,应用cmake命令生成相应的makefile(在Unix like系统下)或者 project文件(指定用window下的相应编程工具编译时)。

        其基本操作流程为:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. $> ccmake directory  
    2. $> cmake directory  
    3. $> make  


    其中directory为CMakeList.txt所在目录;

    • 第一条语句用于配置编译选项,如VTK_DIR目录 ,一般这一步不需要配置,直接执行第二条语句即可,但当出现错误时,这里就需要认为配置了,这一步才真正派上用场;
    • 第二条命令用于根据CMakeLists.txt生成Makefile文件;
    • 第三条命令用于执行Makefile文件,编译程序,生成可执行文件;

    CMake的执行就是这么简单,其难点在于如何编写CMakeLists.txt文件,下面结合例子简单介绍CMakeLists.txt的编写,看下面这个CMakeLists.txt

    一个简单的案例:

    建立一个简单的案例,档案树如下

    建立一个简单的案例,档案树如下
    
    ex1/
    main.c
    CMakeLists.txt

    main.c

    #include <stdio.h>
    
    int main()
    {
        printf("hello world. 
    ");
    }


    CMakeLists.txt

    cmake_minimum_required (VERSION 2.6)
    
    project(ex1)
    add_executable(ex1 main.c)



    当前的工作目录在 ex1 下,若所在的平台备有系统预设的编译器,执行下列命令即可:

    $ cmake .
    

    不要忽略后面的句号(.),这代表目前目录

    执行完毕后,CMake 会在目前的目录下产生相关的组态档、Cache 以及 makefile 或 IDE 专案。接着执行建置指令

    $ make
    

    或者开启产生的 IDE 专案,生成名为 ex1 的执行档。

    同样的,我们也可以使用 make clean 来清除产生出来的执行档。

    如果到目前为止一切顺利,表示目前编译环境设置正确。

    CMake 的语法非常单纯,由指令(command)和注解所组成,所有的空白、换行、tab 都没有特殊作用,仅为语汇元素的区隔。

    凡是由 # 字元开头一直到换行字元间的内容皆会被视为注解,不会有任何作用。

    指令为指令名称加上小括号,括号内可以有零或若干个参数,指令则依照出现在 CMakeLists 当中的顺序执行。所有指令名称大小写都一视同仁,例如 Command、COMMAND 皆视为同一个指令

    在撰写 CMakeLists 时可以使用变数(Variable)储存资料,严格说起来 CMake 只有字串和串列两种资料形态,数值和布林值本质上只是带有特殊意义的字串。变数大小写不同视为相异,内建的变数都必须要全大写。

    为了使读者易于区别,本书的自订变数采用全小写字母搭配底线命名,和 CMake 内建具有功能的变数做区别。不过一个常见的风格是一切都用大写命名,只要按下 caps lock 从头写到尾即可。

    CMake script 由一连串的指令(command)组成,每个指令可有零至多个参数。例如 message 指令常用来输出讯息:

    message(helloworld)
    

    会输出

    helloworld
    


    指令名称不区分大小写,所以 command()、COMMAND() 和 Command() 指的是同一个指令。但是请注意对于指令的参数而言,大小写差异会被视为不同。

    变数(variable)

    在 CMake 当中我们可以用 set() 指令来设定一个变数的值,变数会在第一次使用的时候自动初始化,无须宣告。提取变数值时通常必须在外面加上 ${} 符号,不过也有少数场合例外。

    set(var hello)
    message(${var})
    

    会输出

    hello
    

    变数值的代换甚至可以递回进行,在撰写复杂的功能时可能很有用。例如

    set(var hello)
    set(foo var)
    
    message(${foo})
    message(${${foo}})
    

    会输出

    var
    hello
    

    字串与串列[编辑]

    字串(string)是 CMake 当中的基本的资料型态,将字串用空白或分号分隔则表示串列(list)。

    set(foo this is a list)
    set(foo this;is;a;list)
    

    上面这两个指令作用完全相同,都是将变数 foo 值指定为一个串列,内含 this、is、a、list 四个字串。

    Quoted String[编辑]

    在字串当中需要空白、换行、分号等字元时,可以使用 " " 将文字内容框住,如此就会被解释成同一个字串。

    set(a alpha beta gamma)
    
    set(b "alpha beta gamma")
    
    set(c "alpha
    beta
    gamma"
    )
    
    message("a = ${a}")
    message("b = ${b}")
    message("c = ${c}")
    

    在 " " 中被 ${} 标记的变数值也会被代换,输出为

    a = alpha;beta;gamma
    b = alpha beta gamma
    c = alpha
    beta
    gamma
    

    请注意字串和串列的不同。

    • a 等于一个串列,内容为 alpha、beta、gamma 三个字串
    • b 等于一个字串,内容为 alpha beta gamma
    • c 等于一个字串,内容为以换行为分隔的 alpha beta gamma


    我们也可以利用 set 作字串串接:

    set(a "alpha beta gamma")
    set(b "${a} delta")
    set(c ${a} "delta")
    
    • b 等于一个字串,内容为 "alpha beta gamma dalta"
    • c 等于一个串列,内容为 alpha beta gamma 和 "dalta" 两个字串

    Escapes[编辑]

    CMake 大致上相容 C 语言当中的 Escape Sequence,如 等等。如欲表示 CMake 当中的特殊字元时也可用 标记。

    set(bar "alpha beta gamma")
    message("${bar}: ${bar}")
    

    上面的程式码输出

    ${bar}: alpha beta gamma
    
    cmake_minimum_required(VERSION 2.6)
    project(ex2)
    add_executable(ex2 main.c calc.c)


    本例的 CMakeLists.txt 内容只有三行,其中真正有作用的其实只有最后一行,前两行皆可省略。

    cmake_minimum_required(VERSION 2.6)
    

    这一行功能在确定目前使用的 CMake 版本符合需求,当版本不满足最低需求时会发出错误讯息。即使没加上这一检查仍然可以继续完成建置工作,只是 CMake 会发出警告。

    project(ex2)
    

    这一行指定专案的名称,指定专案名称最主要的目的在于启用几个和环境相关的变数,另外也会在 makefile 增加对应的 target。在这个简单的专案里并不会去利用这些变数,所以对建置没有任何影响。

    add_executable(ex2 main.c calc.c)
    

    这一行告诉 CMake 加入一个名为 ex2 之执行档 target,而且此执行档是由 main.c 和 calc.c 所编译成。在同一个 CMakeLists 里面可以加入多个不同的target,也不一定要和 project 同名

    当一个执行档所需的源代码很多时,可以透过变数在前面收集源代码列表,之后再用 add_executable() 加入。

    add_executable() 指令的完整格式如下:

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

    WIN32 和 MACOSX_BUNDLE 用于指定平台专属的执行档类型。举例来说,在 Windows 建立视窗程式时以 WinMain() 做为程式进入点,如果没有特别指定连结成视窗程式会发生 _mainCRTStartup 连结错误,解决的方式为:

    add_executable(exe_name WIN32 source1 source2 ... sourceN)
    

    在非 Windows 环境下参数 WIN32 会被自动忽略。

    如果加入了 EXCLUDE_FROM_ALL 表示此 target 不会在 make all 时主动建置,必须在 make 时手工指明或者是当其他 target 提出依赖需求时才会被建置。

    加入编译选项:

    加入 Preprocessor 定义[编辑]

    指令 add_definitions 可以用来加入 Preprocessor 定义,作用范围为所在资料夹下的编译单元。格式如下

    add_definitions("-DFOO -DBAR")
    

    这个指令原本只是单纯用来加入 Preprocessor 定义,但事实上差不多所有编译选项都可以透过 add_definitions 传给编译器。

    传入编译选项如: ADD_DEFINITIONS( -g -W -Wall -D_REENTRANT -D_FILE_OFFSET_BITS=64 )

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

    加入编译器搜寻路径[编辑]

    以下两个指令分别用来加入编译器寻找标头档、程式库的路径,在 gcc 相当于 -I 和 -L 选项

    include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
    link_directories(dir1 dir2 ...)
    

    include_directories(dir) 预设会将 dir 加入目前引入路径列表的后面,我们可以利用 AFTER 和 BEFORE 调整 dir 应该被加到列表的前端还是后端

     FIND_LIBRARY( ${var}
                    NAMES name1 [name2 …]
                    PATHS path1 [path2 …]
                    PATH_SUFFIXES suffix1 [suffix2 …] ):
    搜索一个外部的链接库文件,并将结果的全路径保存到var变量中。要搜索的链接库文件名字可能是name1,name2等;搜索路径为path1,path2等;此外还可以指定路径的后缀词为suffix1,suffix2等。因此,系统将尝试在path1/suffix1,path1/suffix2,path2/suffix1,path2/suffix2这些目录中搜索名为name1或name2的链接库文件,并将结果(路径和文件名)保存到var中。

    FIND_PACKAGE( name ):
    在指定的模块目录中搜索一个名为Find<name>.cmake(例如,FindOSG.cmake)的CMake脚本模块文件,执行其中的内容,以图搜索到指定的外部依赖库头文件和库文件位置。

    FIND_PATH( ${var}
                NAMES name1 [name2 …]
                PATHS path1 [path2 …]
                PATH_SUFFIXES suffix1 [suffix2 …] ):
    搜索一个包含name1,name2等文件的目录,并将此路径(不包含文件名)保存到var变量中,搜索路径为path1,path2等;此外还可以指定路径的后缀词为suffix1,suffix2等。通常可以使用此命令来确认外部头文件的依赖路径。

    FIND_PROGRAM( ${var}
                     NAMES name1 [name2 …]
                     PATHS path1 [path2 …]
                     PATH_SUFFIXES suffix1 [suffix2 …] ):
    搜索一个外部的可执行程序,并将结果的全路径保存到var变量中。要搜索的程序名字可能是name1,name2等;搜索路径为path1,path2等;此外还可以指定路径的后缀词为suffix1,suffix2等。

    INCLUDE( file ):
    在当前文件中包含另一个CMake脚本文件的内容。

    INCLUDE_DIRECTORIES( dir1 dir2 … ):
    指定编译器搜索头文件的依赖路径,可以添加多个。

    常用:

    add_executable(dbsrv ${dbsrv_SRCS})

    target_link_libraries(dbsrv msg_proto  xconfig  net ${REDIS_LIBRARY} ${MYSQL_LIBRARY} ${LUA_LIBRARY} -ldl)

    target_link_libraries  将给定的库链接到一个目标上。

      target_link_libraries(<target> [item1 [item2 [...]]] [[debug|optimized|general] <item>] ...)

    接前面的 Cmake学习笔记(一) 与 Cmake学习笔记(二) 继续学习 cmake 的使用。

    学习一下cmake的 finder。

    finder是神马东西?

    当编译一个需要使用第三方库的软件时,我们需要知道:

    去哪儿找头文件 .h

    对比GCC的 -I 参数

    去哪儿找库文件 (.so/.dll/.lib/.dylib/...)

    对比GCC的 -L 参数

    需要链接的库文件的名字

    对比GCC的 -l 参数

    这也是一个 finder 需要返回的最基本的信息。

    如何使用?

    比如说,我们需要一个第三方库 curl,那么我们的 CMakeLists.txt 需要指定头文件目录,和库文件,类似:

    include_directiories(/usr/include)
    target_link_libraries(myprogram curl)

    如果借助于cmake提供的finder会怎么样呢?使用cmake的Modules目录下的FindCURL.cmake,相应的 CMakeList.txt 文件:

    find_package(CURL REQUIRED)
    include_directories(${CURL_INCLUDE_DIR})
    target_link_libraries(curltest ${CURL_LIBRARY})

    或者

    find_package(CURL)
    if(CURL_FOUND)
    include_directories(${CURL_INCLUDE_DIR})
    target_link_libraries(curltest ${CURL_LIBRARY})
    else(CURL_FOUND)
    message(FATAL_ERROR "curl not found!")
    endif(CURL_FOUND)

    如果我们使用的finder,不是cmake自带的怎么办?

    • 放置位置:工程根目录下的 cmake/Modules/
    • 然后在 CMakeList.txt 中添加
    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")

    find_package如何工作

    find_package 将会在module路径下查找 Find<name>.cmake。首先它搜索 ${CMAKE_MODULE_PATH}中的所有路径,然后搜索 <CMAKE_ROOT>/share/cmake-x.y/Modules/

    如果这个文件未找到,它将会查找 <Name>Config.cmake 或 <lower-case-name>-config.cmake 文件。这两个文件是库文件安装时自己安装的,将自己的路径硬编码到其中。

    前者称为 module 模式,后者称为 config 模式

    每个模块一般都会提供一下几个变量

    • <name>_FOUND

    • <name>_INCLUDE_DIR 或 <name>_INCLUDES

    • <name>_LIBRARY 或 <name>_LIBRARIES 或 <name>_LIBS

    • <name>_DEFINITIONS

    编写finder

    • 首先使用 find_package 探测本软件包依赖的第三方库(参数 QUIETLY 和 REQUIRED应该被传递)
    • 如果 pkg-config 可用,则可以用其去探测include/library路径
    • 分别使用 find_path 和 find_library 查找头文件和库文件
      • pkg-config 提供的路径仅作为参考
      • CMake 有很多硬编码的路径
      • 结果放到 <name>_INCLUDE_DIR 和 <name>_LIBRARY (注意:单数而不是复数)

    • 设置 <name>_INCLUDE_DIRS 为 <name>_INCLUDE_DIR <dependency1>_INCLUDE_DIRS ...

    • 设置 <name>_LIBRARIES 为 <name>_LIBRARY <dependency1>_LIBRARIES ...

      • 依赖使用复数,包自身使用单数形式(由find_path和find_library提供)
    • 调用宏 find_package_handle_standard_args() 设置 <name>_FOUND 并打印或失败信息

    命令:aux_source_directory  查找在某个路径下的所有源文件。

      aux_source_directory(<dir> <variable>)

      搜集所有在指定路径下的源文件的文件名,将输出结果列表储存在指定的<variable>变量中。该命令主要用在那些使用显式模板实例化的工程上。模板实例化文件可以存储在Templates子目录下,然后可以使用这条命令自动收集起来;这样可以避免手工罗列所有的实例。

    示例:

    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)
     
    # 指定生成目标
    add_executable(Demo ${DIR_SRCS})
     

    这样,CMake 会将当前目录所有源文件的文件名赋值给变量 DIR_SRCS ,再指示变量 DIR_SRCS 中的源文件需要编译成一个名称为 Demo 的可执行文件。

    add_library指令:

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

      添加一个名为<name>的库文件,该库文件将会根据调用的命令里列出的源文件来创建。<name>对应于逻辑目标名称,而且在一个工程的全局域内必须是唯一的。待构建的库文件的实际文件名根据对应平台的命名约定来构造(比如lib<name>.a或者<name>.lib)。指定STATIC,SHARED,或者MODULE参数用来指定要创建的库的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接,在运行时被加载。MODULE库是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接。如果没有类型被显式指定,这个选项将会根据变量BUILD_SHARED_LIBS的当前值是否为真决定是STATIC还是SHARED。

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

    ADD_LIBRARY(base STATIC ${SRC_LIST1} ${SRC_LIST2} ${SRC_LIST3})

    #ADD_XXXX必须在TARGET_LINK_LIBRARIES前面,否则会报错
    TARGET_LINK_LIBRARIES(base pthread slog crypto)

    生成动态库ADD_LIBRARY(hello SHARED ${SHARED_LIBRARY})

    参考

    http://blog.csdn.net/u012150179/article/details/17852273

    http://www.cmake.org/cmake/help/examples.html

    https://zh.wikibooks.org/wiki/CMake_%E5%85%A5%E9%96%80/%E7%B0%A1%E5%96%AE%E7%9A%84%E7%AF%84%E4%BE%8B

    http://www.hahack.com/codes/cmake/ cmake入门实战

  • 相关阅读:
    网页添加提示音
    poj 2593和poj 2479
    HDU 1558 Segment set
    Ubuntu中conky的安装配置
    Codeforce C. Buns
    HDU 3952 Fruit Ninja
    IE8,IE9,IE10,FireFox 的CSS HACK
    HDU 1086 You can Solve a Geometry Problem too
    Ubuntu中Cairo Dock安装和设置
    Ubuntu 12.04 中安装和配置 Java JDK(转)
  • 原文地址:https://www.cnblogs.com/youxin/p/3679739.html
Copyright © 2011-2022 走看看