zoukankan      html  css  js  c++  java
  • cmake基础


    author: lunar
    date: Fri 25 Sep 2020 07:35:26 PM CST

    CMake 教程(基础篇)

    本系列笔记均来自与github仓库: https://github.com/ttroy50/cmake-examples

    CMake的使用流程

    一般先写好一个CMakeLists.txt,然后创建一个build目录用于存放二进制文件。来到build目录,使用命令cmake ..生成makefile,最后运行make all执行makefile生成项目的可执行文件。

    当然创建build目录的过程并不是必须的,但是一般都会讲build后的二进制文件置于build目录中以保持源码的整洁。

    最简单的一个CMakeLists.txt如下:

    cmake_minimum_required(VERSION 3.10)
    
    # set project name and version number
    project(Tutorial VERSION 1.0)
    
    add_executable(Tutorial turorial.cpp)
    

    add_executable命令用于构建一个可执行文件,第一个参数是可执行文件的名字,其后的参数为该可执行文件所依赖的源文件。

    一般在写中大型项目时,都会在当前项目的根目录写入CMakeLists.txt。而C/C++的include只能include当前目录和/usr/include里面的库。所以一般会设置一个include的起始路径:

    target_include_directories(Tutorial PUBLIC "${DIR}")
    

    设置CMake变量

    CMake有很多用于指示编译的variable或者说flag,进行设置的语法格式为

    set(variable value)
    

    具体有哪些变量呢?

    1. 设置C++标准:CMAKE_CXX_STANDARD

      C++存在多个标准,如C++98,C++11。这个变量就可以用于指示编译时遵循的C++标准。

      同时还要将变量CMAKE_CXX_STANDARD_REQUIRED设为True

    2. cmake下的根目录:CMAKE_BINARY_DIR,运行cmake命令的那个目录就是cmake下的根目录。

    这些都是内置变量,也可以通过同样的命令创建变量。

    使用CMake变量

    通过set命令创建的变量可以通过${variable}$的格式进行使用。

    file命令

    file命令支持通过正则表达式将一类文件作为变量,如将src目录下所有的cpp文件作为sources,可以用

    file(GLOB SOURCES "src/*.cpp")
    

    加入路径

    在直接使用命令行进行C/C++程序编译时可以使用-I选项添加源代码的目录,而在CMakeLists.txt 中我们可以借助target_include_directories()函数来完成这一点,语法格式为:

    target_include_directories(<target> [SYSTEM] [BEFORE]
      <INTERFACE|PUBLIC|PRIVATE> [items1...]
      [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
    

    示例:

    target_include_directories(target
    	PRIVATE ${PROJECT_SOURCE_DIR}/include
    )
    

    target必须是之前通过add_executable或者add_library函数创建的可执行对象。

    INTERFACE,PUBLIC,PRIVATE三个修饰符具体使用哪个决定于你头文件的包含关系,具体还请看这篇文章

    添加静态库

    add_library()函数用于根据一些源文件创建库

    add_library(hello_lib STATIC
    	src/hello.cpp
    )
    

    这个函数将会创建一个名为libhello_lib.a的静态库。

    添加动态库

    动态库,也被称为共享库(shared library). 动态库与静态库最大的区别在于静态库在编译期间就进行链接,而动态库在运行期间进行链接,并且可以被多个文件所共享。

    从空间上来看,动态库更省空间;从运行时间来看,静态库更快。

    添加动态库的函数与添加静态库一样,只要将STATIC改为SHARED

    为库添加路径

    也可以直接在创建库时添加一个路径下的所有文件进去:

    target_include_directories(hello_lib 
    	PUBLIC ${PROJECT_SOURCE_DIR}/include
    )
    

    添加的路径下的文件将会在两个地方被用到:

    • 当编译hello_lib库时
    • 当编译任何链接了hello_lib库的target时

    三个修饰符表示的作用域分别为:

    • PRIVATE: 路径下的文件只对于该库可见
    • INTERFACE:路径下的文件对于链接了该库的文件可见
    • PUBLIC:两者都有

    链接库

    通过target_link_libraries()函数进行库的链接

    示例:

    add_executable(target
    	src/main.cpp
    )
    
    target_link_libraries(target
    	PRIVATE
    		hello_lib
    )
    

    相当于编译器参数中的-rdynamic

    安装target

    CMake允许用户通过install函数创建一个用户可以通过make install命令运行来安装的target。

    示例:

    install(TARGETS install_binary
    	DESTINATION bin
    )
    

    这个地址bin是一个根目录下的子目录,而这个根目录由运行cmake命令时由参数-DCMAKE_INSTALL_PREFIX进行指定。

    比如:cmake .. -DCMAKE_INSTALL_PREFIX=/install/location

    则会安装在/install/location/bin目录下。

    安装库

    动态库也可以根据该命令进行安装

    install(TARGETS install_library
    	LIBRARY DESTINATION lib
    )
    

    如果没有设置-DCMAKE_INSTALL_PREFIX,默认的安装路径为/usr/local

    Build Type

    cmake有一系列内置变量Build Type(我也不知道这个翻译对不对)来控制编译的level。这个和用命令行编译时用的一些参数是对应的,对应结果如下:

    • Release: 如果是要发布的包,相当于-o3 -DNDEBUG参数;
    • Debug: 如果是用于debug,-g
    • MinSizeRel: 使得编译出来的包最小,-Os -DNDEBUG
    • RelWithDebInfo: 发布带有debug信息的包,-O2 -g -DNDEBUG

    这个-DNDEBUG参数,根据我的项目经验,如果使用了则会被检测到定义了一个名为NO_DEBUG的宏。所以在源代码中想要打印debug信息的话,就可以检测这个宏。如果没有定义这个宏,就打印一些东西;否则就什么都不做。

    这些Build Type在运行cmake时可以这样设置:

    cmake .. -DCMAKE_BUILD_TYPE=Release

    设置默认Build Type

    cmake原本的默认Build Type是不带任何编译器参数用于优化,你可以在CMakeLists.txt中设置默认:

    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()
    

    设置编译参数flags

    有两种方法用于设置flags:

    • 通过target_compile_definition()函数;
    • 使用内置变量CMAKE_C_FLAGS(C语言)和CMAKE_CXX_FLAGS(C++);

    在现代CMake中设置C ++标志的推荐方法是使用按目标标志,可以通过target_compile_definitions()函数将其填充到其他目标。target_compile_definitions()函数用于给一个target定义一个宏,其语法格式为

    target_compile_definitions(<target>
      <INTERFACE|PUBLIC|PRIVATE> [items1...]
      [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
    

    如果在item前面加上-D字样,该宏将会被移除。

    三个限定符则用于指定作用域。

    设置默认的C++编译参数

    可以以如下的格式设置参数

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

    导入第三方库

    CMake通过find_package()函数来寻找第三方库,这个函数会在CMAKE_MODULE_PATH下寻找格式为"FindXXX.cmake"的模块。在linux下,CMAKE_MODULE_PATH的默认地址为/usr/local/cmake/Modules

    函数示例:

    find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)

    参数说明:

    • Boost: 库名
    • 1.46.1:最小版本
    • REQUIRED: 如果找不到这个库则build失败
    • COMPONENTS:The list of libraries to find

    大多数成功导入的库会设置一个名为XXX_FOUND的变量(库名+FOUND),可以通过判断这个变量是否为true来判断库是否导入成功。

  • 相关阅读:
    delphi 中的异常处理结构
    delphi 获取系统临时文件夹路径
    delphi 之选择文件夹/目录路径的函数SelectDirectory
    delphi DevExpress 系列控件的一点感悟
    delphi 之DevExpress 汉化方法:
    SqlServer 查询表中字段的字段说明:
    delphi数据类型之句柄类型
    Delphi中之PChar, PWideChar, pAnsiChar三种数据类型的一点感悟
    求数值型数组中元素的最大值、最小值、平均值、总和等。
    杨辉三角的实现
  • 原文地址:https://www.cnblogs.com/lunar-ubuntu/p/13756301.html
Copyright © 2011-2022 走看看