Cpp构建和编译笔记——9.CMake库的开发
这个环节我们从库的使用者切换为库的开发者,假设我们开发了一个库
Abc,那么如何处理 Abc 库的安装和导入?在导入环节,如何按照 modern cmake
的规范提供 AbcConfig.cmake?
尤其在这部分内容,网上教程非常混乱,CMake
官方文档写的非常复杂难懂,似乎是为超复杂的大型库开发所准备的,而且早期语法和
modern cmake
语法混杂在一起,难以分辨。本文将从简单的示例开始,吸取各个教程的内容,逐渐完善。
方式一:直接安装
我们首先考虑一种最无脑的安装方式:直接把可执行文件,库文件和头文件放到需要的地方去,并且不考虑复用。(CMake
不会导出它的配置信息文件)
CMake
支持这种直接无脑的安装,不需要我们真的在文件系统中进行手动拷贝。
示例如下
123install(TARGETS demo4lib1 demo4lib2 demo4exe1 demo4exe2)install(DIRECTORY include/Demo4 TYPE INCLUDE)install(FILES src/Demo4_b.h TYPE INCLUDE)
这里安装四个 targ ...
Cpp构建和编译笔记——8.CMake依赖管理
这一篇关注 CMake 的依赖管理,这是最重要的部分:由于 C++没有如
pip,npm 那样统一的包管理(既有历史原因,也是
C/C++的包管理需求太复杂导致的),在使用第三方库时通常需要使用源码编译安装,然后手动管理依赖,涉及到的
CMake 操作非常繁琐。
本文围绕以下内容展开:
项目安装命令
第三方库的使用:
库已经安装到本地,并且支持 CMake:需要导入库的信息
库并不在本地:需要从仓库拉取源码,合并到当前项目中
我们面临很多问题:
不同平台的影响:例如 windows 平台下的安装位置等非常混乱,而 Linux
则比较统一,使得安装和第三方库的导入都非常规范化
不同的语法风格:例如 CMake 早期的语法风格需要如何导入库,Modern
CMake 的语法风格需要如何导入库。CMake
为了适应不同的第三方库,提供了许多不同的接口,语法越来越混乱。
项目安装命令
单独的安装命令通常如下(当前位置是项目根目录,而非在 build
子文件夹中)
12345678(1)make install(2)cmake --build build --target insta ...
Cpp构建和编译笔记——7.CMake语法结构
在上一篇已经学习了作为一门语言的最基本内容,接下来是进一步的内容
流程控制(条件语句,循环语句)
函数和宏
cmake 脚本/模块(不是 CMakeLists,而是.cmake 文件)
条件判断
if 语句
最完整的 if 语法结构如下
1234567if(<condition>) <commands>elseif(<condition>) # optional block, can be repeated <commands>else() # optional block <commands>endif()
其中的 elseif 和 else 都是可选的,例如
1234567if(WIN32) message(STATUS "Now is windows")elseif(APPLE) message(STATUS "Now is Apple systens.")elseif(UNIX) message(STATUS "Now is UNIX ...
Cpp构建和编译笔记——6.CMake基本语法
CMake 和 make,shell 脚本一样,本质是一种 DSL 语言。在了解 CMake
的基本概念和用法之后,作为一种编程语言,还是得从最基本的变量,流程控制(for
循环,if 条件),函数等开始学习。在最开始,我们强调一点——CMake
作为一门语言是区分大小写的!只是具体到通常使用的内置命令/自定义函数/自定义宏,不区分大小写。
不得不说,CMake 这一类文本化的语言语法都非常反人类,而且CMake
的官方文档写的真是垃圾中的垃圾。
本文的主要内容
调试输出(IO)
变量
字符串操作
列表操作
数学表达式
调试输出
在 CMakeLists 中,有着和 echo 类似的用于向控制台输出信息的 message
命令
1message([<mode>] "message text")
其中的 mode 通常为
NOTICE:
缺省时默认的选项,表示正常输出到控制台的重要提示信息
WARNING: 表示输出到控制台的警告信息,但不会中断 CMake
的运行
STATUS: 表示正常输出到控制台的一般提示性信息,和 CMake
自动输出的提示 ...
Cpp构建和编译笔记——5.CMake与CMakeLists
关于 CMake 的内容可能比较多,计划是分成以下几部分:
Modern CMake 的基本使用
CMake 基本语法与变量
CMake 语法结构(条件,循环,函数,模块等)
CMake 依赖管理(作为库的使用者)
CMake 库的开发(作为库的开发者)
CMake 命令速查
完全CMake风格的四步命令:构建+编译+测试+安装 1234cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/path/to/install/cmake --build buildcmake --build build --target testcmake --build build --target install
上述命令完全不依赖具体平台。
经典Linux风格的四步命令:构建+编译+测试+安装 123456mkdir buildcd buildcmake ..make -j8ctestmake install
这里需要依赖make命令,主要命令都在build/中进行。
Windows平台使用M ...
Cpp构建和编译笔记——4.make与Makefile
make 介绍
make(GNU
make)是一个项目构建工具,用于方便地编译、链接多个源代码文件,自动决定哪些源文件需要重新编译,从而高效地构建自己地项目,普遍用于处理
c/c++项目,但也可以用于其他语言。
make 的通常用法是:
在项目目录下把编译链接的命令,写入
Makefile(指定文件名的一个文本文件,类似于 shell 脚本)
在项目命令下,执行make命令,会自动读取当前目录下的
Makefile 并解析执行
make命令可以后接参数:
make,相对于执行make all,通常代表从头编译整个项目
make install,通常代表把当前项目安装到系统中,还可以附加一些参数指定安装位置等,例如prefix=/usr/local
make clean,通常代表清理项目中的杂项文件,不包括源文件
注意这些只是约定俗成的作法,具体的命令行为其实完全取决于 Makefile
中写入的内容
这里的 make 是 GNU make,但是其实在各种 IDE
中,也都集成了一个类似的项目构建工具,例如 VS 的
nmake,它们发挥着类似的作用。
为什么用 make
对于简 ...
Cpp构建和编译笔记——3.gcc选项
未分类选项
-o outfile: 指定编译的输出文件名称,缺省时默认为
a.out
-std=c++11: 指定使用的 c++标准
优化相关
编译器的优化选项有 4 个级别
—O0: 默认情形,不进行优化(大写字母 O 后接数字 0)
-O1 -O:
较低的优化级别,编译器会尝试减少空间大小和优化程序的执行时间,但不执行需要消耗大量编译时间的优化
-O2:
较高的优化级别,牺牲更多编译时间来提高程序的性能
-O3:
最高的优化级别,宁愿牺牲空间来提升程序的执行速度
-Og: 主要使用-O1优化,
除了那些影响调试的部分
-Os: 侧重于优化文件的体积
注意:
这里优化通常不是压缩可执行文件的大小,指的是优化运行速度等,优化得到的可执行文件体积可能还更大
优化的必然代价就是编译时间更长,执行逻辑与源代码不再逐行对应,难以调试,因此
debug 模式最好不要用高等级的优化
调试相关
-g: 在编译的时候,同时产生基本的调试信息
-ggdb: 尽可能的生成 gdb
的可以使用的调试信息。重复使用-g和-ggdb是无用的,从结果看,gcc
会忽略-g,使-ggdb生效
-w: ...
Cpp构建和编译笔记——2.头文件和库
头文件
头文件的存在,目的是把接口和实现分离,便于多文件编程中的组织,比如
在多文件的项目中,把函数声明都集中到若干头文件中,在源文件中引用它们,便于跨文件的函数调用
在提供库的同时,我们也需要提供库的使用接口(头文件),通过头文件中的类和函数声明,用户可以知道如何使用这个库
在使用库的时候,首先需要在源代码中引用头文件,然后在链接步骤中链接需要的库文件
gcc 查找头文件
gcc 在编译过程中,预处理环节需要 include
相应的头文件,这里存在一个问题:如何找到头文件?
gcc
存在专门的选项:-Ipath,也可以写成-I path,带不带空格都可以,但是只能后接一个路径,如果使用多个路径就需要多个-I,例如
1gcc hello.c -I mydir1 -I mydir2
如果直接写完整路径加文件名,那么不存在查找文件的问题,但是如果使用的是不完整路径加文件名,则存在查找顺序的问题。在
include 语句中,双引号和尖括号引用头文件的查找顺序有一点区别:
双引号 include 的查找顺序:
使用#include的源文件所在的路径
-I 指定的路径
环境变量CPATH ...
Cpp构建和编译笔记——1.编译过程
打算开一个系列,写一些零零散散但自学了很久的东西——C/C++除了基本语法之外,还需要知道的东西——C++项目的编译与构建。
这一系列采用的编程环境为 Linux 系统,编译器为 gcc 11.2.0。(windows
下的 mingw
作为两种平台间的畸形产物,曾经给我的学习造成了极大的困扰,尤其是静态库动态库部分)
如果使用 IDE 比如 VS 或 CLion
的话,这些东西可能都不需要了解,同样也可以写好 C++,但是我不喜欢使用 IDE
这种糊里糊涂,不清楚到底发生了什么的编程方式,更喜欢先搞懂每一步发生了什么,然后可以为了提高效率而使用
IDE,而非一直保持一种稀里糊涂的状态。
1. 编译过程的拆分
对于 c/c++编程,从源代码文件变成可执行文件,大致需要以下几步:
预处理(Pre-Processing),预处理器(preprocessor)处理#include #define等内容,把头文件
copy 到源文件中等,注意这种 include 是递归的,并且这里存在一个问题:gcc
如何找到头文件。
编译(Compiling),得到的文件是以汇编语言写的,可读。
汇编(A ...
Linux 服务器配置笔记
记录一下我在服务器上鼓捣了什么。
家目录下的目录结构
家目录~为/home/username,主要包含
~/software 目前把第三方软件都安装在这里
按照每个软件分配次级目录 ,例如~/software/git就是 git
的安装位置
~/software/tmp
把软件的压缩包以及解压后的文件夹放置于此,因为服务器不能连接外网,压缩包通过
ftp 传入。
~/scripts
存放一些常用的脚本文件(添加到PATH)
~/pbshome 存放PBS作业提交时的快照
~/tmp 存放一些临时文件
配置文件
因为安装软件往往需要设置环境变量,因此在原本的.bashrc脚本最后加上一行,用
source 命令加载自己的.my_bashrc脚本:
1source ~/.my_bashrc
把频繁修改的内容写在这个.my_bashrc脚本中,把原本的.bashrc以及其他内容都在
Onedrive 上进行了备份。
例如当前(2024-6-05)的.my_bashrc内容如下
123456789101112131415161718192021222324252627282930313 ...