CMake for Linux

2016年4月12日 由 Creater 留言 »

CMake是一个跨平台的构建系统生成工具。它使用平台无关的CMake清单文件CMakeLists.txt,指定工程的构建过程;源码树的每个路径下都有这个文件。CMake产生一个适用于具体平台的构建系统,用户使用这个系统构建自己的工程。

一个工程或项目作管理时,咱们在Linux/unix或ELinux下采用Makefile,CodeBlocks,KDevelop, Eclipse,scons等管理,在Windows下则使用VS。他们可以明确哪些目录的那些文件需要被编译,以及编译成什么目标文件。

以最熟悉的Makefile为例,它明确说明了需要编译什么(应用程序,动态库,静态库),由什么编译器产生,需要的头文件和库文件目录,依赖的库文件等等。 但当项目足够庞大,这个维护工作就非常复杂。

于是就有人想到采用一个Makefile的生成工具,这其实就是Autotool和socos的角色。只需要告知依赖关系,他们就可以生成Makefile。 可以说,他们就是Makefile的生成器,极大地简化了手工维护Makefile的工作。

CMake则更进一步,它不光能够为Linux平台生成Makefile,还做到了跨平台,在Linux/Unix平台,它可以生成Makefile,还可以为Codeblocks,Eclipse等工具生成工程文件。 在Windows平台,可以生成vs工程文件,在iOS下,也可以生成xcode工程。

基础学习:

以V4L2_Utils模块作为例子学习.
V4L2_Utils模块在Linux 下包含2个.cpp文件,他们将用来编译成动态库,然后被另一个测试程序main.cpp调用。

  • 1.1: 把所有.cpp文件编译成一个可执行程序。
  • 这个例子,将不再建立动态或静态库,而是把main.cpp和另外两个cpp文件一起直接编译成一个可执行程序。
    CMakeLists.txt 内容如下:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    add_executable(V4L2_Utils main.cpp  v4l2_util.cpp  tran_data.cpp)
    

    讲解如下:
    a. cmake_minimum_required 设置一个工程所需要的最低CMake版本,如果CMake的当前版本低于指定的版本,它会停止处理工程文件,并报告错误
    cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]] [FATAL_ERROR])
    b. project (V4L2_Utils):表明工程名称。
    project (PROJECTNAME [CXX] [C] [Java])
    可以指定工程支持的语言,如果忽略,表明支持所有语言。
    c. add_executable(V4L2_Utils main.cpp v4l2_util.cpp tran_data.cpp)
    生成可执行文件:V4L2_Utils. 由main.cpp v4l2_util.cpp tran_data.cpp生成。
    add_executable([WIN32] [MACOSX_BUNDLE]
    [EXCLUDE_FROM_ALL]
    source1 source2 … sourceN)
    引入一个名为的可执行目标,该目标会由调用该命令时在源文件列表中指定的源文件来构建。对应于逻辑目标名字,并且在工程范围内必须是全局唯一的

    构建Unix/Linux标准Makefile
    在src目录下,创建build目录。进入build目录
    #mkdir build
    #cd build
    #cmake .. -G”Unix Makefiles”
    此时,声称标准Makefile。
    #make clean;make


    cmake语法如下:

    cmake [options]
    cmake [options]
    cmake [选项] <源码路径>
    cmake [选项] <现有构建路径>
    

    因为source 目录在../ 所以指定../为源码目录。
    -G:指定构建系统,当前只想创建Linux/Unix系统标准Makefile。 所以指定为Unix Makefile.
    具体支持何种构建系统:man cmake
    可以看到类似入下内容:

    Generators
    The following generators are available on this platform:
      Ninja                       = Generates build.ninja files (experimental).
      Unix Makefiles              = Generates standard UNIX makefiles.
      CodeBlocks - Ninja          = Generates CodeBlocks project files.
      CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
      Eclipse CDT4 - Ninja        = Generates Eclipse CDT 4.0 project files.
      Eclipse CDT4 - Unix Makefiles
                                  = Generates Eclipse CDT 4.0 project files.
      KDevelop3                   = Generates KDevelop 3 project files.
      KDevelop3 - Unix Makefiles  = Generates KDevelop 3 project files.
    

    这个CMakeLists.txt 内容中,构成可执行文件的文件个数很少,但如果源文件很多,则最好如Makefile中一样,指定一个源文件列表:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (SRC_LIST main.cpp v4l2_util.cpp tran_data.cpp)
    message (${SRC_LIST})
    add_executable(V4L2_Utils ${SRC_LIST})
  • 1.2: 编译一个静态库,并使用这个静态库和main.cpp,最终再生成一个可执行程序:
  • 即先生成libv4l2_utils.a.
    main.cpp使用libv4l2_utils.a中的符号并最终生成V4L2_Utils.
    CMakeLists.txt:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (LIB_SRC_LIST  v4l2_util.cpp tran_data.cpp)
    set (EXEC_SRC_LIST main.cpp)
    add_library(V4L2_Utils STATIC  ${LIB_SRC_LIST})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)
    

    说明:

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

    创建一个名为 name 的库文件,STATIC, SHARED指定为静态或动态库。
    库文件由源文件列表生成。
    add_library(V4L2_Utils STATIC ${LIB_SRC_LIST})
    生成一个静态库libV4l2_Utils.a 由v4l2_util.cpp tran_data.cpp生成
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    可执行程序由哪些.o组成。
    target_link_libraries(Test_V4L2 V4L2_Utils)
    可执行程序Test_V4L2需要链接库V4L2_Utils
    target_link_libraries( [item1 [item2 […]]] [[debug|optimized|general] ] …)
    将给定的库链接到目标target上。

  • 1.3: 编译一个动态库,并使用这个动态和main.cpp,最终再生成一个可执行程序:
  • cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (LIB_SRC_LIST  v4l2_util.cpp)
    set (EXEC_SRC_LIST main.cpp)
    add_library(V4L2_Utils SHARED  ${LIB_SRC_LIST})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)

    只变化了STATIC–>SHARED

  • 1.4: 指定include 路径:
  • main.cpp中,需要include “v4l2_util.h”,但这个头文件并不在src目录内,而是在include目录,include, src为同级别目录。则main.cpp会找不到头文件。所以,实际的CMakeLists.txt文件写法为:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (LIB_SRC_LIST  v4l2_util.cpp)
    set (EXEC_SRC_LIST main.cpp)
    set (INCLUDE_DIRECTORIES ../include)
    include_directories(${INCLUDE_DIRECTORIES})
    add_library(V4L2_Utils SHARED  ${LIB_SRC_LIST})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)

    将头文件目录添加进去。

  • 1.5: 添加第三方库和库路径:
  • 如果libV4L2.so是以第三方库形式出现。不需要编译,它放在resource目录下。则需要制定搜索库路径

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (EXEC_SRC_LIST main.cpp)
    set (INCLUDE_DIRECTORIES ../include)
    set (LINK_DIR ../../resource)
    include_directories(${INCLUDE_DIRECTORIES})
    link_directories(${LINK_DIR})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)

    说明:
    link_directories 指定连接器查找库的路径。
    link_directories(directory1 directory2 …)

  • 1.6:创建Release和Debug版本:
  • #set (CMAKE_BUILD_TYPE Release)
    set (CMAKE_BUILD_TYPE Debug)
    分别指定为Release或者Debug模式。区别在于:
    Release: -O3 -DNDEBUG
    Debug:-g
    也可以不加在txt内。在产生Makefile时才加入:
    cmake -DCMAKE_BUILD_TYPE=Release

  • 1.7: 增加编译和链接选项:
  • CMAKE_C_FLAGS
    CMAKE_CXX_FLAGS
    CMAKE_EXE_LINKER_FLAGS
    分别相当于:CFLAGS, CXXFLAGS, LDFLAGS。
    set (CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -DEMBED”)
    这种写法的好处是,不会覆盖CMAKE_CXX_FLAGS本来的信息。只是把需要添加的内容添加进去。

  • 1.8: 多目录结构的cmake 使用:
  • V4L2_Utils实际的目录结构其实并不是所有源码都存放在src目录内。
    它的目录结构是:
    src: 存放生成库的源码。v4l2_util.cpp, tran_data.cpp
    test:存放使用库的测试程序: main.cpp
    include: 存放头文件: v4l2_util.h
    resource: 存放第三方库
    build:存放编译过程的文件
    build/lib: 存放生成的libv4l2_utils.so
    build/bin:存放main.cpp所产生的测试程序可执行文件。
    此时,可以采用顶层目录和每个有源码的目录中均创建CMakeLists.txt的方式来处理(和Makefile处理方式类似)
    顶层目录的CMakeLists.txt 内容如下:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    add_subdirectory(src lib)
    add_subdirectory(test bin)

    说明:
    add_subdirectory(source_dir [binary_dir]
    [EXCLUDE_FROM_ALL])
    构建添加一个子路径。source_dir选项指定了CMakeLists.txt源文件和代码文件的位置。如果source_dir是一个相对路径,那么source_dir选项会被解释为相对于当前的目录,但是它也可以是一个绝对路径。binary_dir选项指定了输出文件的路径。如果binary_dir是相对路径,它将会被解释为相对于当前输出路径。
    请注意两个相对路径的不同。因为Sam是在Build目录内执行cmake ../
    所以src指的是当前 CMakeLists.txt所在路径下的src. 而bin, lib 指的是当前输出路径下的bin,lib. 也就是build/bin build/lib

    src CMakeLists.txt :
    set (LIB_SRC_LIST  v4l2_util.cpp)
    set (CMAKE_BUILD_TYPE Release)
    include_directories(../include)
    add_library(V4L2_Utils SHARED  ${LIB_SRC_LIST})
    test CMakeLists.txt :
    set (EXEC_SRC_LIST main.cpp)
    set (INCLUDE_DIRECTORIES ../include)
    set (LINK_DIR ../../resource)
    set (LINK_DIR "${LINK_DIR} ../../libs/")
    set (CMAKE_BUILD_TYPE Release)
    include_directories(${INCLUDE_DIRECTORIES})
    link_directories(${LINK_DIR})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)
    

    这个做法一直不满意,其实有更好的做法
    总结:
    有了以下这些选项,写过Makefile的人可以很容易的使用CMake了。
    生成可执行程序:
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    生成静态库
    add_library(V4L2_Utils STATIC ${LIB_SRC_LIST})
    生成动态库:
    add_library(V4L2_Utils SHARED ${LIB_SRC_LIST})
    指定头文件路径:  -L
    include_directories(${INCLUDE_DIRECTORIES})
    指定库文件路径: -L
    link_directories(${LINK_DIR})
    指定链接库: -l
    target_link_libraries(Test_V4L2 V4L2_Utils)
    CFLAGS,CXXFLAGS,LDFLAGS:
    CMAKE_C_FLAGS,CMAKE_CXX_FLAGS,CMAKE_EXE_LINKER_FLAGS

    注1:set 和message
    set用来指定变量
    如:
    set (SRC_LIST main.cpp)
    则变量SRC_LIST内容为main.cpp
    ${SRC_LIST} 则为取变量内容(与Bash类似)
    message:用来显示变量
    message (${SRC_LIST})

    注2:
    如何显示编译细节:
    方法1:
    在CMakeLists.txt中,
    set(CMAKE_VERBOSE_MAKEFILE ON)

    方法2:
    有时不希望修改CMakeLists.txt文件,则可以在创建Makefile时加入:
    cmake -DCMAKE_VERBOSE_MAKEFILE=ON

    方法3:
    有时连Makefile都不希望修改:
    make VERBOSE=1

    广告位

    发表评论

    你必须 登陆 方可发表评论.