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 buildctest --test-dir buildcmake --install build 上述命令完全不依赖具体平台。 经典Linux风格的四步命令:构建+编译+测试+安装 123456mkdir buildcd buildcmake ..make -j8ctestmake install 这里需要依赖make命令,主要命令都在build/中进行。 Windows平台使用MinGW风格的工具链,对应的四步命令:构建+...
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,它们发挥着类似的作用。 (nmake...
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. 头文件和库
头文件头文件的存在,目的是把接口和实现分离,便于多文件编程中的组织,比如 在多文件的项目中,把函数声明都集中到若干头文件中,在源文件中引用它们,便于跨文件的函数调用 在提供库的同时,我们也需要提供库的使用接口(头文件),通过头文件中的类和函数声明,用户可以知道如何使用这个库 在使用库的时候,首先需要在源代码中引用头文件,然后在链接步骤中链接需要的库文件 虽然这里给出了使用头文件的意义,但是众所周知的是,几乎没有其他现代语言采用了头文件的方式,这是 C/C++ 最大的历史包袱, 极大地受限于 C 语言设计时的历史背景以及当时的机器能力。 gcc 查找头文件gcc 在编译过程中,预处理环节需要 include 相应的头文件,这里存在一个问题:如何找到头文件? gcc 存在专门的选项:-Ipath,也可以写成-I path,带不带空格都可以,但是只能后接一个路径,如果使用多个路径就需要多个-I,例如 1gcc hello.c -I mydir1 -I mydir2 如果直接写完整路径加文件名,那么不存在查找文件的问题,但是如果使用的是不完整路径加文件名,则存在查找顺序的...
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),得到的文件是以汇编...
web自学6——React
前文记录了 npm 和 Vite。npm 解决第三方依赖管理问题,Vite 解决开发服务器、模块解析和生产构建问题。 但是即使用了 Vite,页面内部的交互逻辑仍然可以是原生 JavaScript 和 DOM API。比如 TodoList 中仍然可以写: 123const li = document.createElement("li");li.textContent = todo.text;ul.appendChild(li); 当页面非常简单时,这样写完全够用。但是当页面状态越来越复杂,就会遇到新的问题,这就是 React 要解决的部分。 1. 从 DOM 操作到状态同步如果 TodoList 只有添加和删除任务,原生 DOM 操作很直接。但是继续增加功能以后,例如编辑、筛选、统计、保存、批量操作,问题会逐渐出现: 同一份数据可能影响页面中的多个位置,例如列表、未完成数量、筛选结果; 增删改查以后,需要手动同步很多 DOM 节点; 表单状态、列表状态、筛选状态交织在一起; 页面拆成多个区域后,数据传递和事件处理容易混乱。 也就是说,原生 DOM 操...
Linux 常用命令
目录基础切换目录cd命令(change directory)用于改变目录: cd <path>:切换到<path>目录下,可以是相对路径或/开头的绝对路径; cd:缺省时会回到家目录~ cd .. 返回上一级目录 cd -可以回到上一次所处的位置 解释一下 Linux 中的路径表示规则: /开头的路径是绝对路径,表示从根目录开始,例如 /opt/xxx; 不以 / 开头的路径是相对路径,表示从当前目录开始,例如 a/b 特殊字符: . 表示当前目录 .. 表示上一级目录; ~ 表示当前用户的家目录,例如 /home/<username>。 注意:对于目录的表示,是否以 / 结尾的写法(例如 /opt 和 /opt/)在 cd 命令中没有区别,但是在某些命令中会存在区别,倾向于用前者指代目录自身,用后者指代目录中的内容。 可以考虑使用 zoxide 作为 cd 的现代替代品,它会维护一个目录数据库,并且让目录跳转更加智能。(其实有很多类似的 z 跳转小工具,但是大多都是在某个 shell 中实现的插件,也就是一组 shell 函数,...
web自学5——构建工具 Vite
前文记录了 JavaScript 模块化和 npm。模块化让我们可以拆分自己的代码,npm 让我们可以管理第三方依赖。 但 npm 安装的 package 并不能直接交给浏览器使用,这就引出了构建工具的需求。本文以 Vite 为例,简单了解前端构建工具解决的问题。 1. 构建工具构建工具主要解决的是:源码和浏览器最终运行的文件之间存在差距。 浏览器原生的模块导入要求路径是它能理解的 URL。例如相对路径可以: 1import { createTodoItem } from "./todo.js"; 完整 URL 也可以: 1import confetti from "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/+esm"; 但是 npm 安装的 package 通常会放在 node_modules/ 中。如果直接在浏览器里写: 1import lodash from "lodash"; 这里的 "lodash" 既不是...
Linux 基础学习笔记
目录结构与文件系统标准目录结构:FHS多数 Linux 发行版都遵循大致统一的目录层次约定,通常称为 FHS(Filesystem Hierarchy Standard)。不同发行版在细节上可能存在差异,但核心目录的用途大体一致。 /:根目录,整个系统目录树的起点。 /bin、/sbin:传统上存放系统启动和基本维护所需的可执行文件。 /bin 通常面向普通用户和系统基本命令,例如 cp、ls 等。 /sbin 通常存放系统管理相关命令,其中很多命令需要管理员权限才能执行。 /usr/bin、/usr/sbin:传统上存放系统运行后供用户和管理员使用的大量可执行文件,例如 git、wget 等。通过发行版包管理器安装的软件通常会把可执行文件放在这些目录下。 /lib、/usr/lib:存放共享库和相关运行时文件。 /lib 传统上服务于 /bin 和 /sbin 中的基础程序,也可能包含内核模块目录,例如 /lib/modules。 /usr/lib 传统上服务于 /usr/bin 和 /usr/sbin 中的程序。 在 64 位或多架构系统中,还可能存在 /lib64、/...
web自学4——依赖管理工具 nvm
在前文中,ES Module 解决了 JavaScript 文件之间如何拆分和引用的问题。如果只引用自己的文件,并且路径都是 ./todo.js 这种明确的相对路径,浏览器可以直接处理。 但是模块化还没有解决另一个问题:如果希望使用别人已经写好的库,应该从哪里下载,怎样记录版本,怎样保证不同机器上的依赖尽量一致? 这就是 npm 主要解决的问题。 第三方依赖对于简单页面,第三方依赖可以直接通过一个 URL 引入。例如在 HTML 中写: 1<script src="https://cdn.jsdelivr.net/npm/lodash@4/lodash.min.js"></script> 这种写法的意思是:浏览器直接去这个 URL 下载一份已经构建好的第三方 JS 文件,然后把它作为普通脚本执行。对于临时 demo 或很小的页面,这样做没有问题。 但是项目一旦变复杂,直接引用第三方脚本会遇到几个问题: 依赖分散在 HTML 或 JS 文件里,不容易集中查看; 版本号写在 URL 中,升级和回退都需要手动维护; 多人协作或换机器时,很...
