CMake的简单例子

CMake 简介

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

1、编写CMakeLists.txt

2、执行命令:"cmake PATH"生成Makefile(PATH是CMakeLists.txt所在的目录)。

3、使用Make命令进行编译。

应用

源代码的目录结构

--CMakeSample
    |
    +--CMakeLists.txt
    |
    +--cmake
        |
        +--modules
            |
             +FindTHREAD_DB.cmake
    +--src
        |
        +--CMakeLists.txt
        |
        +--hello
            |
            +--CMakeLists.txt
            |
            +--hello.h
            |
            +--hello.cpp
         +--sample
             |
             +--CMakeLists.txt
             |
             +--main.cpp

在这个例子我模拟以下情况

1、在一个目录下生成一个静态库

2、在另一个目录中生成一个可执行程序,并引用了前一个静态库。

3、查找外部库,(这里例子是在系统的默认路径下查找,这是不必要的,纯粹是为介绍而设的,实际也没有使用)。

首先来编写根目录下的CMakeLists.txt

PROJECT(cmakesample)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
#Appends the cmake/modules path inside the MAKE_MODULE_PATH variable which stores the
# directories of additional CMake modules 
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})

ADD_SUBDIRECTORY(src)

1、PROJECT(cmakesample)命名项目的名称。

2、CMAKE_MINIMUM_REQUIRED(VERSION 2.6)声明了CMake的版本要求。

3、SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})指定了一些自己写的CMake模版所存放的地址,比如FindTHREAD_DB.cmake。其中PROJECT_SOURCE_DIR是代码的根目录,也可以使用cmakesample_SOURCE_DIR来替代,cmakesample就是项目名称。

4、ADD_SUBDIRECTORY(src)指定了代码所在的子目录。

接着来编写src的CMakeLists.txt

INCLUDE_DIRECTORIES(hello)
SUBDIRS(hello sample)

 1、INCLUDE_DIRECTORIES(hello)指定了头文件所在的目录,只有src下的子目录中的 CMakeLists.txt可用,假设外面还有一层跟src同一级别的目录,那么这句话对另一个目录下的CMakeLists.txt无效。其实这句话可以放在sample里的CMakeLists.txt里,不过此时需要改成INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/hello)。

2、SUBDIRS(hello sample)相当于分别写ADD_SUBDIRECTORY(hello),ADD_SUBDIRECTORY(sample)

接着我们写hello下的CMakeLists.txt

AUX_SOURCE_DIRECTORY(. DIR_HELLO_SRCS)
ADD_LIBRARY(hello STATIC ${DIR_HELLO_SRCS})
1、AUX_SOURCE_DIRECTORY(. DIR_HELLO_SRCS)把当天目录下的所有源码文件名赋给变量DIR_HELLO_SRCS。
2、ADD_LIBRARY(hello STATIC ${DIR_HELLO_SRCS})指定生成一个静态库。那如果要生成一个动态库呢,只要把STATIC改为SHARED就可以了。不过如果我们想同时生成静态库和动态库呢?直接把两句话放在一起?这是不行的,因为在CMake中每个target都必须有一个唯一的命名,于是我们把两个语句放在一起就在命名上冲突了。那么如何解决呢,其实CMake的target名并不一定要和输出文件一致的,于是我们可以通过为静态库和动态库设不同的target名,但把输出文件名设成一样来解决这个问题(其实文件名也是不完全一样的,一个是.a结尾,另一个是.so结尾)。于是我们可以这样来写:
ADD_LIBRARY(hello_static STATIC ${DIR_HELLO_SRCS})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

ADD_LIBRARY(hello_shared SHARED ${DIR_HELLO_SRCS})
SET_TARGET_PROPERTIES(hello_shared PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 

接下来我们来写sample下CMakeLists.txt

SET(DIR_SRCS main.cpp)
ADD_EXECUTABLE(sample ${DIR_SRCS})
TARGET_LINK_LIBRARIES(sample hello)

INSTALL(TARGETS sample RUNTIME DESTINATION bin)
1、SET(DIR_SRCS main.cpp)把变量DIR_SRCS赋值为"main.cpp"。也是通过AUX_SOURCE_DIRECTORY(. 
DIR_SRCS )来实现。
2、ADD_EXECUTABLE(sample ${DIR_SRCS})声明要生成一个叫sample的可执行程序。
3、TARGET_LINK_LIBRARIES(sample hello)声明需要链接libhello.a库(因为我上面把hello声明为静态库了)。
4、INSTALL(TARGETS sample RUNTIME DESTINATION bin)声明当运行make insall时把可执行程序安装在${PREFIX}/bin下。其中PREFIX的默认值为/usr/local/,也可以通过在运行cmake时指定 -DCMAKE_INSTALL_PREFIX:PATH="${CMAKE_INSTALL_PREFIX}"

最后我们写一下FindTHREAD_DB.cmake

#
# configuration to find DB_CXX library
#

# Include dir
find_path(THREAD_DB_INCLUDE_DIR
  NAMES thread_db.h
  PATHS /usr/include/ /usr/local/include/
)

# Finally the library itself
find_library(THREAD_DB_LIBRARY
  NAMES  thread_db
  PATHS /usr/lib/ /usr/local/lib/
)

1、find_path(THREAD_DB_INCLUDE_DIR NAMES thread_db.h PATHS /usr/include/ /usr/local/include/)在目录“/usr/include"和"/usr/local/include"下查找thread_db.h,如果找到则把目录名赋给THREAD_DB_INCLUDE_DIR

2、find_library(THREAD_DB_LIBRARY NAMES  thread_db PATHS /usr/lib/ /usr/local/lib/)在目录"/usr/lib"和"/usr/local/lib"下查找libthread_db.so,如果找到则把库文件的绝对路径赋给THREAD_DB_LIBRARY

为了纯粹测试,我们就修改./src/sample/CMakeLists.txt为:

FIND_PACKAGE(THREAD_DB REQUIRED)
MARK_AS_ADVANCED(
THREAD_DB_INCLUDE_DIR
THREAD_DB_LIBRARY
)
MESSAGE(STATUS ${THREAD_DB_INCLUDE_DIR}, ${THREAD_DB_LIBRARY})

SET(DIR_SRCS
    main.cpp)
ADD_EXECUTABLE(sample ${DIR_SRCS})
TARGET_LINK_LIBRARIES(sample hello)

INSTALL(TARGETS sample RUNTIME DESTINATION bin)
 
接着我们在项目的根目录下建立一个文件夹build。
进入build运行"cmake .."。有如下输出:
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- /home/dylan/Public/CMakeSample
-- /usr/include,/usr/lib/libthread_db.so
-- Configuring done
-- Generating done
-- Build files have been written to: /home/dylan/Public/CMakeSample/build
 其中" /usr/include,/usr/lib/libthread_db.so"就是MESSAGE(STATUS ${THREAD_DB_INCLUDE_DIR}, ${THREAD_DB_LIBRARY})的输出。
还有一点我要先建立build,然后再运行cmake ..的主要原因是这样就不会污染源代码,如果我不想要这些自动生成的文件时只要简单的删除build文件夹就可以了, 这也是我喜欢cmake的一个原因。
最后我们运行make。这时就会在./build/src/sample下生成一个可执行文件,./build/src/hello下生成一个静态库。

猜你喜欢

转载自dylanwu.iteye.com/blog/1391486