虚拟机中CMake学习笔记

1、总流程

需要先创建CMakeLists.txt文档和main.cpp或main.c主程序
在文档中编写连接等指令

在这里插入图片描述

指令:CMake . (当前文件夹构建)
CMake … (上一层文件夹构建)
系统自动生成了如下的文件:
包括:CMakeCache.txt、CMakeFiles、cmake_install.cmake、Makefile等中间文件。


一、project

project (mytest)

这样就指定了当前的工程名称为mytest


二、include_directories()

将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径。

默认情况下,include_directories命令会将目录添加到列表最后,可以通过命令设置CMAKE_INCLUDE_DIRECTORIES_BEFORE变量为ON来改变它默认行为,将目录添加到列表前面。也可以在每次调用include_directories命令时使用AFTER或BEFORE选项来指定是添加到列表的前面或者后面。如果使用SYSTEM选项,会把指定目录当成系统的搜索目录。该命令作用范围只在当前的CMakeLists.txt。
在这里插入图片描述


三、add_library()

在这里插入图片描述

添加名为name的库,库的源文件可指定,也可用target_sources()后续指定。
库的类型是STATIC(静态库)/SHARED(动态库)/MODULE(模块库)之一。


四、add_executable()

在这里插入图片描述


五、target_link_libraries()

指定链接给定目标和/或其依赖项时要使用的库或标志。将传播链接库目标的使用要求。目标依赖项的使用要求会影响其自身源的编译。


六、set()

set(CMAKE_BUILD_TYPE “Debug”)
设置调试信息(查找程序错误)


七、install()

install()命令为项目生成安装规则,通过在源目录中调用install()命令指定的安装规则将在安装过程中按顺序执行。此命令有多种格式,分别对应不同的安装目标,如:二进制文件、动态库、静态库以及文件、目录、脚本等。基本命令格式如下:

在这里插入图片描述

上述6种命令格式,有部分参数使用方式和意义相同,在这里统一说明,后续不再单独说明。

参数如下:

DESTINATION

用于指定安装路径,可以是绝对路径,也可以是相对路径。
如果使用的是相对路径,那么需要配合使用CMAKE_INSTALL_PREFIX变量来指定路径前缀,该变量可以在CMakeLists.txt文件中设置,也可以通过cmake命令指定。由于cpack不支持绝对路径,所以cmake官方建议使用相对路径.

PERMISSIONS

指定安装文件的权限,这些权限包括:OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ,GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE,SETUID, 和 SETGID。

CONFIGURATIONS

指定安装规则适用的构建配置列表(DEBUG或RELEASE等).
注意:此参数需要在RUNTIME DESTINATION参数之前使用.

COMPONENT

指定与安装规则相关联的安装组件名称,如"runtime"或"development"。在对应组件的安装过程中,将仅执行与给定组件名称关联的安装规则。除非显示标记为EXCLUDE_FROM_ALL,否则将安装所有组件。如果未指定COMPONENT,则会创建默认组件“未Unspecified”。可以使用CMAKE_INSTALL_DEFAULT_COMPONENT_NAME变量来控制默认组件名称。

EXCLUDE_FROM_ALL

指定该文件从完整安装中排除,并且仅作为特定于组件的安装的一部分进行安装。

RENAME

重命名要安装的文件,此参数只在安装单个文件时有效。

OPTIONAL

声明此步骤是可选的,即如果要安装的文件不存在,不必报错,程序将继续向后执行。

安装目标文件

这里的目标文件包含很多中类型,比如动态库.so, 静态库.a,和执行二进制文件等。

在这里插入图片描述
在这里插入图片描述安装文件

这种方式用于说明为项目安装文件的规则。最常见的比如安装.so对应的头文件。

在这里插入图片描述FILE

要安装的文件,由当前目录下的相对路径指定。如果没有显式指定PERMISSIONS参数,默认情况下文件的权限为:OWNER_WRITE,
OWNER_READ, GROUP_READ, 和WORLD_READ。

PROGRAMS

与FILE类型的文件类似,但是多了OWNER_EXECUTE, GROUP_EXECUTE,
和WORLD_EXECUTE权限。注意,虽然多了EXECUTE权限,但是这与TARGETS类型的操作是不一样的,比如shell 脚本。

TYPE

指定安装文件的类型。TYPE和DESTINATION 必须至少有一个被指定。
cmake支持的TYPE类型如下:

在这里插入图片描述
在这里插入图片描述


八、PROJECT_SOURCE_DIR 与 PROJECT_BINARY_DIR内置变量

在这里插入图片描述


九、message()函数

message :为用户显示一条消息。

可以用下述可选的关键字指定消息的类型:

() = 重要消息;
 STATUS = 非重要消息;
 WARNING = CMake 警告, 会继续执行;
 AUTHOR_WARNING = CMake 警告 (dev), 会继续执行;
 SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤;
 FATAL_ERROR = CMake 错误, 终止所有处理过程

例:输出错误 FATAL_ERROR

message(FATAL_ERROR "
FATAL: In-source builds are not allowed.
       You should create a separate directory for build files.
")

十、gcc中的-w -W和-Wall选项

-w的意思是关闭编译时的警告,也就是编译后不显示任何warning,因为有时在编译之后编译器会显示一些例如数据转换之类的警告,这些警告是我们平时可以忽略的。
-Wall选项意思是编译后显示所有警告。
-W选项类似-Wall,会显示警告,但是只显示编译器认为会出现错误的警告。
在编译一些项目的时候可以-W和-Wall选项一起使用。

举个例子:

#include <stdio.h>
 
void main()
{
        int a=1.0*4;
        return 0;
}

直接编译
gcc -o test_w_wall testwwall.c

在这里插入图片描述

只显示这一个警告,下面使用-w选项。
gcc -w -o test_w_wall testwwall.c 不会显示任何警告,直接编译成功。

gcc -Wall -o test_w_wall testwwall.c
在这里插入图片描述

显示了所有的警告,比之前不使用任何选项多出了变量a未使用这个警告,也多出了main函数的返回值不是int型。

gcc -W -o test_w_wall testwwall.c

在这里插入图片描述

只显示了没有返回值的main函数不应该有return一个值这个警告。

gcc -W -Wall test_w_wall testwwall.c
在这里插入图片描述

比单独使用-W多出了变量为使用这个警告,比-Wall选项少了一个看起来重复的main函数返回值不是int这个警告。

之前看了一篇国外程序员写的博客,说编译时不使用-W -Wall选项的是stupid的,所以编译时还是尽量带上吧。


十一、gcc -O0 -O1 -O2 -O3 四级优化选项及每级分别做什么优化

gcc 提供了为了满足用户不同程度的的优化需要,提供了近百种优化选项,用来对{编译时间,目标文件长度,执行效率}这个三维模型进行不同的取舍和平衡。

优化的方法不一而足,总体上将有以下几类:1)精简操作指令;2)尽量满足cpu的流水操作;3)通过对程序行为地猜测,重新调整代码的执行顺序;4)充分使用寄存器;5)对简单的调用进行展开等等。想全部了解这些编译选项,并在其中挑选适合的选项进行优化,无疑像个噩梦般的过程。单从gnu的官方网站上得到的手册来看,描述依然比较苍白,不足以完全了解选项的使用范围和原理。

-O0: 不做任何优化,这是默认的编译选项。

-O和-O1优化会消耗少多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。

-O2会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。

-O3在-O2的基础上进行更多的优化,例如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化。

-Os主要是对代码大小的优化,我们基本不用做更多的关心。

通常各种优化都会打乱程序的结构,让调试工作变得无从着手。并且会打乱执行顺序,依赖内存操作顺序的程序需要做相关处理才能确保程序的正确性。

优化代码有可能带来的问题

1.调试问题:正如上面所提到的,任何级别的优化都将带来代码结构的改变。例如:对分支的合并和消除,对公用子表达式的消除,对循环内load/store操作的替换和更改等,都将会使目标代码的执行顺序变得面目全非,导致调试信息严重不足。

2.内存操作顺序改变所带来的问题:在O2优化后,编译器会对影响内存操作的执行顺序。例如:-fschedule-insns允许数据处理时先完成其他的指令;-fforce-mem有可能导致内存与寄存器之间的数据产生类似脏数据的不一致等。对于某些依赖内存操作顺序而进行的逻辑,需要做严格的处理后才能进行优化。例如,采用volatile关键字限制变量的操作方式,或者利用barrier迫使cpu严格按照指令序执行的。


十二、CMAKE_C_FLAGS和CMAKE_CXX_FLAGS常用变量

CMAKE_C_FLAGS

设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加。

CMAKE_CXX_FLAGS

设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加。


十三、gcc/g++ 编译选项 march

-march=navite navite表示允许编译器自动探测目标架构(即:编译主机)并生成针对目标架构优化的目标代码。

注意:
问题说明:在core-i5主机上编译程序,并打包,放到 core-i3 的设备上出现段错误:
“xxx received signal SIGILL, Illegal instruction”

经查,是因为在编译时指定了 -march=native 选项,该选项使得编译器在生成目标文件时,针对当前编译主机(core-i5)进行了优化,导致在 core-i3 上无法正常执行。(前提:i5 和 i3 的系统版本、编译器版本、依赖库版本均完全相同)


十四、-std=c++11用法

-std=c++11设置为使用C++11标准


十五、cmake:CheckCXXCompilerFlag

作用: 检查 CXX 编译器是否支持给定的标志。

注意:必须先include(CheckCXXCompilerFlag)

例子1:在检查当前编译器是否支持c++11

CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x"COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  add_definitions(-DCOMPILEDWITHC11)
  message(STATUS "Using flag -std=c++11.") 
elseif(COMPILER_SUPPORTS_CXX0X)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
  add_definitions(-DCOMPILEDWITHC0X)
  message(STATUS "Using flag -std=c++0x.")
else()
  message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

十六、include指令

include 指令用来载入并运行来自于文件或模块的 CMake 代码


十七、CMake–List用法

list(LENGTH <list><output variable>)
list(GET <list> <elementindex> [<element index> ...]<output variable>)
list(APPEND <list><element> [<element> ...])
list(FIND <list> <value><output variable>)
list(INSERT <list><element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value>[<value> ...])
list(REMOVE_AT <list><index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)

LENGTH          返回list的长度

GET           返回list中index的element到value中

APPEND         添加新element到list中

FIND           返回list中element的index,没有找到返回-1

INSERT           将新element插入到list中index的位置

REMOVE_ITEM      从list中删除某个element

REMOVE_AT       从list中删除指定index的element

REMOVE_DUPLICATES 从list中删除重复的element

REVERSE         将list的内容反转

SORT           将list按字母顺序排序

LIST与SET命令类似,即使列表本身是在父域中定义的,LIST命令也只会在当前域创建新的变量,要想将这些操作的结果向上传递,需要通过SET PARENT_SCOPE, SET CACHE INTERNAL或运用其他值域扩展的方法。

注意:cmake中的list是以分号隔开的一组字符串。可以使用set命令创建一个列表。例如:set(var a b c d e)创建了一个这样的列表:a;b;c;d;e。 set(var “a b c d e”)创建了一个字符串或只有一个元素的列表。

当指定index时,如果为大于或等于0的值,它从列表的开始处索引,0代表列表的第一个元素。如果为小于或等于-1的值,它从列表的结尾处索引,-1代表列表的最后一个元素。


十八、find_package()的用法

使用find_package引入外部依赖包

1、通过Cmake内置模块引入依赖包

为了方便我们在项目中引入外部依赖包,cmake官方为我们预定义了许多寻找依赖包的Module,他们存储在path_to_your_cmake/share/cmake-/Modules目录下。每个以Find.cmake命名的文件都可以帮我们找到一个包。

我们以curl库为例,假设我们项目需要引入这个库,从网站中请求网页到本地,我们看到官方已经定义好了FindCURL.cmake。所以我们在CMakeLists.txt中可以直接用find_pakcage进行引用。

find_package(CURL)
add_executable(curltest curltest.cc)
if(CURL_FOUND)
    target_include_directories(clib PRIVATE ${CURL_INCLUDE_DIR})
    target_link_libraries(curltest ${CURL_LIBRARY})
else(CURL_FOUND)
    message(FATAL_ERROR ”CURL library not found”)
endif(CURL_FOUND)

对于系统预定义的 Find< LibaryName >.cmake 模块,使用方法一般如上例所示。

每一个模块都会定义以下几个变量:

< LibaryName >_FOUND

< LibaryName >_INCLUDE_DIR or < LibaryName >_INCLUDES < LibaryName >_LIBRARY
or < LibaryName >_LIBRARIES

你可以通过< LibaryName >_FOUND 来判断模块是否被找到,如果没有找到,按照工程的需要关闭 某些特性、给出提醒或者中止编译,上面的例子就是报出致命错误并终止构建。 如果
< LibaryName >_FOUND 为真,则将< LibaryName >_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES

2、通过find_package引入非官方的库(该方式只对支持cmake编译安装的库有效)

假设此时我们需要引入glog库来进行日志的记录,我们在Module目录下并没有找到 FindGlog.cmake。所以我们需要自行安装glog库,再进行引用。

安装

# clone该项目
git clone https://github.com/google/glog.git 
# 切换到需要的版本 
cd glog
git checkout v0.40  

# 根据官网的指南进行安装
cmake -H. -Bbuild -G "Unix Makefiles"
cmake --build build
cmake --build build --target install

此时我们便可以通过与引入curl库一样的方式引入glog库了

find_package(GLOG)
add_executable(glogtest glogtest.cc)
if(GLOG_FOUND)
    # 由于glog在连接时将头文件直接链接到了库里面,所以这里不用显示调用target_include_directories
    target_link_libraries(glogtest glog::glog)
else(GLOG_FOUND)
    message(FATAL_ERROR ”GLOG library not found”)
endif(GLOG_FOUND)

对于原生支持Cmake编译和安装的库通常会安装Config模式的配置文件到对应目录,这个配置文件直接配置了头文件库文件的路径以及各种cmake变量供find_package使用。而对于非由cmake编译的项目,我们通常会编写一个Find< LibraryName >.cmake,通过脚本来获取头文件、库文件等信息。通常,原生支持cmake的项目库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。


十九、Cmake命令之include_directories介绍

include_directories ([AFTER|BEFORE] [SYSTEM] dir1 [dir2 …])
将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径。

默认情况下,include_directories命令会将目录添加到列表最后,可以通过命令设置CMAKE_INCLUDE_DIRECTORIES_BEFORE变量为ON来改变它默认行为,将目录添加到列表前面。也可以在每次调用include_directories命令时使用AFTER或BEFORE选项来指定是添加到列表的前面或者后面。如果使用SYSTEM选项,会把指定目录当成系统的搜索目录。该命令作用范围只在当前的CMakeLists.txt。


二十、cmake设置生成文件的位置(ACTIVE,LIBRART等)

本文主要分析cmake中4个变量的区别:

CMAKE_ARCHIVE_OUTPUT_DIRECTORY:默认存放静态库的文件夹位置;
CMAKE_LIBRARY_OUTPUT_DIRECTORY:默认存放动态库的文件夹位置;
LIBRARY_OUTPUT_PATH:默认存放库文件的位置,如果产生的是静态库并且没有指定CMAKE_ARCHIVE_OUTPUT_DIRECTORY则存放在该目录下,动态库也类似;
CMAKE_RUNTIME_OUTPUT_DIRECTORY:存放可执行软件的目录;


二十一、cmake中CMAKE_RUNTIME_OUTPUT_DIRECTORY

CMAKE_RUNTIME_OUTPUT_DIRECTORY:存放可执行软件的目录


二十二、make的-j命令(加速Linux程序编译)

make -j

既然IO不是瓶颈,那CPU就应该是一个影响编译速度的重要因素了。

用make -j带一个参数,可以把项目在进行并行编译,比如在一台双核的机器上,完全可以用make
-j4,让make最多允许4个编译命令同时执行,这样可以更有效的利用CPU资源。

还是用Kernel来测试:

make: 40分16秒

make -j4:23分16秒

make -j8:22分59秒

由此看来,在多核CPU上,适当的进行并行编译还是可以明显提高编译速度的。但并行的任务不宜太多,一般是以CPU的核心数目的两倍为宜。

不过这个方案不是完全没有cost的,如果项目的Makefile不规范,没有正确的设置好依赖关系,并行编译的结果就是编译不能正常进行。如果依赖关系设置过于保守,则可能本身编译的可并行度就下降了,也不能取得最佳的效果。


2、B站讲解视频

在这里插入图片描述

在if里面变量名不用加¥符号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Is_LiuYiZheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值