当前位置: 首页 > 工具软件 > Vala_CMake > 使用案例 >

CMake基本概念

强保臣
2023-12-01

        CMake可通过与平台无关的CMakeList.txt来定制整个编译流程,然后根据目标用户的平台进一步生成适配该平台的Makefile来编译构建工程的。

        用CMake编译时,首先执行cmake path,将会按CMakeLists.txt所指定的规则生成Makefile,其中path为CMakeLists.txt所在的路径。然后使用make命令按照生成Makefile进行编译。

1. CMakeLists.txt

1.1 基本格式

cmake_minimum_required(VERSION 3.10)

#set the project name
project(Tutorial)

#add the executable
add_executable(Tutorial tutorial.cxx)

1.2 环境变量

cmake-variables

BINARY

  • CMAKE_BINARY_DIR :binary files的主路径

  • CMAKE_CURRENT_BINARY_DIR: 当前编译的路径

  • PROJECT_BINARY_DIR: 当前工程编译的路径

SOURCE

  • CMAKE_SOURCE_DIR:  源码主路径
  • CMAKE_CURRENT_SOURCE_DIR: 当前子工程的源码路径
  • PROJECT_SOURCE_DIR:  当前编译工程的源码路径

NAME

  •  PROJECT_NAME: 当前project name
  • CMAKE_PROJECT_NAME: 第一个被创建的project name

 1.3 命令

  • include(file/module)

        Load and run CMake code from a file or module

        如果是module,cmake将会在CMAKE_MODULE_PATH或Cmake module的路径下找module.cmake

  • set(source, a.cpp b.cpp c.cpp)

        设置source变量为a, b,c.cpp, 之后${source}指代a, b, c.cpp

  • target_include_directories(target  PRIVATE/PUBLIC/INTERFACE  include)

        编译该target时会将include路径下的头文件编译进去

        PRIVATE:  该include只被加载到该target中;

        PUBLIC:     若target为library,该include不仅会编译进该library,其他任何链接该library的 target都会加载该include;

        INTERFACE:  该include可以被添加到任何链接该library的target的include directories, 但该target本身并不会被编译

  • target_link_libraries(target PRIVATE libaray)

        编译该target时会将libaray链接进去

  • add_library(libaray STATIC/SHARED/ALIAS source)

        依赖source生成library

        参数可选static, shared, 分别生成静态库libxxx.a或动态库libxxxx.so

        alias: 类似于target别名,用来指代target;

        add_library(project_name INTERFACE) : 当创建的library只依赖头文件时,用INTERFACE来创建无任何输出的target。

  • install (TARGETS/DIRECTORY/FILES  source DESTINATION dest)

        install一般在放在CMakeLists.txt的最后一个模块,编译完成后,执行make install, 可以将source copy到dest中, 其中source可以是targets, directory,或者files。

         另外,如果是library a, 因为是未编译完成的target,所以,要用TARGETS模式,且用"a LIBRARY"来指代编译生成的libaray a。

        若dest非绝对路径,将默认放在base install location下的dest下。the base install location是由CMAKE_INSTALL_PREFIX来控制,一般可通过"cmake .. -DCMAKE_INSTALL_PREFIX = ${CMAKE_BINARY_DIR}"来设置,或者运行"make install DESTDIR=/tmp/stage", 将会创建安装路径${DESTDIR}/${CMAKE_INSTALL_PREFIX}做为base install location.

  • 编译类型

        一般有以下的编译类型:

        Release:   编译时添加"-O3 -DNDEBUG" flags

        Debug: "-g"

        MinSizeRel: "-Os -DNDEBUG"

        RelWithDebInfo:  "-O2 -g -DNDEBUG"

        编译类型可以通过"cmake .. -DCMAKE_BUILD_TYPE=Release"来指定。若没有指定具体的类型,可用以下方式来设置默认的编译类型。

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message("Setting build type to 'RelWithDebInfo' as none was specified.")
    set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
    # Set the possible values of build type for cmake-gui
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
      "MinSizeRel" "RelWithDebInfo")
endif()
  • 编译flag

        CMAKE_CXX_FLAGS: c++

        CMAKE_C_FLAGS: c

        CMAKE_LINKER_FLAGS: linker flags

        设定方式一:set

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)

        其中,"CACHE STRING "Set C++ Compiler Flags" FORCE"用于使CMake flags可以在CMakeCache.txt中进行设置。

        设定方式二:target_complie_definitions

target_compile_definitions(cmake_examples_compile_flags
     PRIVATE EX3)

        上述方式将在编译target时,添加-DEX3,即定义了EX3的宏。

        一旦设定了编译flag,所有targets将会按设定的flag进行编译。

  • find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)

        Boost: 要找的library name

        1.46.1:要找的Boost最低版本

        REQUIRED: 表示这个是必需的,如果找不到将会失败;

        COMPONENTS: the list of components to find in the library

        如果找到对应的package,将会创建XX_FOUND变量,可以用这个变量来检测是否找到对应的package, 用XX_INCLUDE_DIRS来指代找到的package的头文件的位置, 如: Boost_INCLUDE_DIRS。用XX_LIBRARY来指代library的位置。

 if(Boost_FOUND)
     message ("boost found")
     include_directories(${Boost_INCLUDE_DIRS})
 else()
     message (FATAL_ERROR "Cannot find Boost")
 endif()
  • 编译器设定

        CMAKE_C_COMPILER: c

        CMAKE_CXX_COMPILER: C++

        CMAKE_LINKER:  用于编译时链接binary        

  • 设置c++ standard

        方式一:

 include(CheckCXXCompilerFlag)
 CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
 CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
                                                                                                                                                                                                          
 # check results and add flag
 if(COMPILER_SUPPORTS_CXX11)#
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
 elseif(COMPILER_SUPPORTS_CXX0X)#
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
 else()
     message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
 endif()

        include(CheckCXXCompilerFlag):  告诉cmake把这个函数包含进来以使用;

        cmake支持尝试用传进CHECK_CXX_COMPILER_FLAG中的任何flags进行编译,并把结果放在传进这个函数的变量中,用于判断当前环境是否支持该flags。

        方式二:

set(CMAKE_CXX_STANDARD 11)

        设置CMAKE_CXX_STANDARD后, CMake将会在编译时设置合适的,与设置最接近的flags,并把将其作用于所有targets。

        方式三:

target_compile_features(target PUBLIC cxx_auto_type)

message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")

    cmake自动选择合适的编译flags。

    可以用${CMAKE_CXX_COMPILE_FEATURES}来列出所有的features。

  • add_subdirectory(subdir)

        添加subdir,并按subdir下的CMakeLists.txt进行编译;

        当用project()创建project时,CMake将会自动为该project创建一系列的变量,这些变量将可以被其他project或主project使用。

  • configure_file(ver.h.in ${PROJECT_BIANARY_DIR}/ver.h)

        根据ver.h.in构建生成ver.h, 其中ver.h.in可以使用cmake及CMakeLists.txt中定义的变量,如,${PROJECT_BIANARY_DIR};

1.4 控制流程

  • if..elseif..else

if(<condition>)
  message("do stuff")
elseif(<condition>)
  message("do other stuff")
else()
  message("do other other stuff")
endif()
  • Loops

set(list 1 2 bar)
foreach(var ${list} foo)
    message("the val is "${var})
endforeach()

#运行结果: 
the val is 1
the val is 2
the val is bar
the val is foo

1.5 modules, functions and macros

        functions与macros用法是类似的,但不同的是functions有自己的scope,但macros没有,因此定义在macros中的变量将会在全scope中被用到,因此,macros适合较为简短精炼的function。

        function与macros另一点不同的地方是参数传递的方式,macros的参数不是设定为variables,而是间接引用,或者可理解为直接展开,因此,要注意避免;

macro(print_list my_list)
  foreach(var IN LISTS my_list)
    message("${var}")
  endforeach()
endmacro()

set(my_list a b c d)
set(my_list_of_numbers 1 2 3 4)
print_list(my_list_of_numbers)
# prints:
# a
# b
# c
# d

1.6 protobuf

        protocol buffers是一种数据序列化格式,使用.proto来描述数据,可用protobuf编译器将其转化为c++的源码。

PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS AddressBook.proto)

 # Add an executable
 add_executable(protobuf_example
     main.cpp
     ${PROTO_SRCS}
     ${PROTO_HDRS})
 
 target_include_directories(protobuf_example
     PUBLIC
     ${PROTOBUF_INCLUDE_DIRS}
     ${CMAKE_CURRENT_BINARY_DIR}
 )

 # link the exe against the libraries
 target_link_libraries(protobuf_example
     PUBLIC
     ${PROTOBUF_LIBRARIES}
  ) 

        在编译target时,会启动protobuf compiler。

        在生成的FindProtobuf.cmake中存着各种可用的protobuf变量,如:

        PROTOBUF_INCLUDE_DIRS: protobuf 头文件

        PROTOBUF_LIBRARIES:  protobuf library

CMake入门实战​​​​​​effective modern cmake

cmake-example

Cmake tutorial

CMake reference documentation

cmake资料汇总

Cpp presentations

 类似资料: