cmake用法详解

两盒软妹~` 提交于 2019-12-21 00:00:49

1.cmake用法详解

①编写CMakeLists.txt

②执行cmake PATH生成Makefile(PATH是cmakelists.txt所在路径)

③make编译

假设我们的项目中只有一个源文件 main.cpp,CMakeLists.txt文件如下所示:

1 PROJECT(main)
 
2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

//aux_source_directory(<dir> <variable>)把参数 <dir> 中所有的源文件名称赋值给参数 <variable>
3 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
 
//指示变量 DIR_SRCS 中的源文件需要编译 成一个名称为 main 的可执行文件
4 ADD_EXECUTABLE(main ${DIR_SRCS})

假设我们的源代码分布情况如下:

step2 dir(main.cpp、src(test1.h、test1.cpp))其中 src 目录下的文件要编译成一个链接库,则CMakeLists.txt文件如下所示:

//目录 step2 中的 CMakeLists.txt
PROJECT(main)

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
 
ADD_SUBDIRECTORY( src )//ADD_SUBDIRECTORY 指明本项目包含一个子目录 src

AUX_SOURCE_DIRECTORY(. DIR_SRCS)

ADD_EXECUTABLE(main ${DIR_SRCS})

TARGET_LINK_LIBRARIES( main Test )//指明可执行文件 main 需要连接一个名为Test的链接库
//目录 src 中的 CmakeLists.txt
AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)

ADD_LIBRARY ( Test ${DIR_TEST1_SRCS})//将 src 目录中的源文件编译为共享库

在执行 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 ,内容如下:

//文件Findlibdb_cxx.cmake
MESSAGE(STATUS "Using bundled Findlibdb.cmake...")//MESSAGE 会将参数的内容输出到终端。

// FIND_PATH 指明头文件查找的路径
//find_path(<VAR> name1 [path1 path2 ...]) 该命令在参数 path* 指示的目录中查找文件 name1 并将查找到的路径保存在变量 VAR中
FIND_PATH(
LIBDB_CXX_INCLUDE_DIR
db_cxx.h 
/usr/include/ 
/usr/local/include/ 
)

//查找链接库并将结果保存在变量中
FIND_LIBRARY(
LIBDB_CXX_LIBRARIES NAMES  db_cxx
PATHS /usr/lib/ /usr/local/lib/
)

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

01 PROJECT(main)
 
02 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
 
03 SET(CMAKE_SOURCE_DIR .)

//到目录 ./cmake/modules 中查找 Findlibdb_cxx.cmake
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})
 
0708 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})
 
19 ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)

然后在根目录编译cmake ..和make

 ·设置编译类型

add_executable(demo demo.cpp) # 生成可执行文件
add_library(common STATIC util.cpp) # 生成静态库
add_library(common SHARED util.cpp) # 生成动态库或共享库

add_library 默认生成是静态库,通过以上命令生成文件名字:

在 Linux 下是:demo、libcommon.a、libcommon.so

在 Windows 下是:demo.exe、common.lib、common.dll


 ·指定编译包含的源文件

明确指定包含哪些源文件:

add_library(demo demo.cpp test.cpp util.cpp)

搜索所有的 cpp 文件:

aux_source_directory(. SRC_LIST) # 搜索当前目录下的所有.cpp文件
add_library(demo ${SRC_LIST})

自定义搜索规则:

file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
add_library(demo ${SRC_LIST})
# 或者
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
# 或者
aux_source_directory(. SRC_LIST)
aux_source_directory(protocol SRC_PROTOCOL_LIST)
add_library(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})

· 查找指定的库文件

find_library(VAR name path)查找到指定的预编译库,并将它的路径存储在变量中。

find_library( # Sets the name of the path variable.
              log-lib
 
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

 ·设置包含的目录

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_BINARY_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)

或

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")

·设置链接库搜索目录

link_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/libs
)

或

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/libs")

·设置 target 需要链接的库

target_link_libraries( # 目标库
                       demo
 
                       # 目标库需要链接的库
                       # log-lib 是上面 find_library 指定的变量名
                       ${log-lib} )

在 Windows 下,系统会根据链接库目录,搜索xxx.lib 文件,Linux 下会搜索 xxx.so 或者 xxx.a 文件,如果都存在会优先链接动态库(so 后缀)。

·指定链接动态库或静态库

target_link_libraries(demo libface.a) # 链接libface.a
target_link_libraries(demo libface.so) # 链接libface.so

·指定全路径

target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.so)

·指定链接多个库

target_link_libraries(demo
    ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a
    boost_system.a
    boost_thread
    pthread)

·设置变量的值

set(SRC_LIST main.cpp test.cpp)
add_executable(demo ${SRC_LIST})

·包含其它 cmake 文件

include(./common.cmake) # 指定包含文件的全路径
include(def) # 在搜索路径中搜索def.cmake文件
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # 设置include的搜索路径

2.CMakeLists常用变量

  • 一般一个代码文件夹包含:src、build、bin、include、lib、CMakeLists.txt

PROJECT_SOURCE_DIR:工程的根目录
PROJECT_BINARY_DIR:运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build
PROJECT_NAME:返回通过 project 命令定义的项目名称
CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRENT_BINARY_DIR:target 编译目录
CMAKE_CURRENT_LIST_DIR:CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_LINE:当前所在的行
CMAKE_MODULE_PATH:定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块
EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置(set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin))
LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置(set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib))

BUILD_SHARED_LIBS:这个开关用来控制默认的库编译方式,如果不进行设置,使用 add_library 又没有指定库类型的情况下,默认编译生成的库都是静态库。如果 set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库
CMAKE_C_FLAGS:设置 C 编译选项,也可以通过指令 add_definitions() 添加
CMAKE_CXX_FLAGS:设置 C++ 编译选项,也可以通过指令 add_definitions() 添加

 


3.CMakeLists.txt编写

·复杂项目目录结构如下(多个目录,多个源文件,多个项目):

demo 根目录下的 CMakeLists.txt 文件如下:

cmake_minimum_required (VERSION 2.8)
project(demo)
aux_source_directory(. DIR_SRCS)
# 添加math子目录
add_subdirectory(math)
# 指定生成目标
add_executable(demo ${DIR_SRCS})
# 添加链接库
target_link_libraries(demo MathFunctions)

math 目录下的 CMakeLists.txt 文件如下:

aux_source_directory(. DIR_LIB_SRCS)
# 生成链接库
add_library(MathFunctions ${DIR_LIB_SRCS})

·自定义编译选项:

cmake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算,否则就调用标准库中的数学函数库。
修改根目录下的 CMakeLists.txt 文件如下:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_BINARY_DIR}/config.h"
    )
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
        "Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
    include_directories ("${PROJECT_SOURCE_DIR}/math")
    add_subdirectory (math)
    set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})
  • configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 cmake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。
  • option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON 。根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库。

修改 main.cc 文件,让其根据 USE_MYMATH 的预定义值来决定是否调用标准库还是MathFunctions 库:

#include "config.h"
#ifdef USE_MYMATH
    #include "math/MathFunctions.h"
#else
    #include <math.h>
#endif
 
int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
 
#ifdef USE_MYMATH
    printf("Now we use our own Math library. \n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library. \n");
    double result = pow(base, exponent);
#endif
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}

编写 config.h.in 文件
注意 main.cc 的第一行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:

#cmakedefine USE_MYMATH

这样 cmake 会自动根据 CMakeLists.txt 配置文件中的设置自动生成 config.h 文件。

 

参考

https://blog.csdn.net/afei__/article/details/81201039

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!