1. 引言
在编程的世界中,我们经常会遇到各种工具和技术,它们在表面上看起来很复杂,但实际上,它们的工作原理很简单。为了更好地理解这些工具和技术,我们需要从心理学的角度来看待它们。正如卡尔·容格(Carl Jung)所说:“人们不是由他们的意识,而是由他们的潜意识所驱使。”同样,编程中的很多工具和技术也是由其背后的原理和机制所驱使的。
1.1 CMake 在现代 C++ 开发中的重要性
CMake(C++ Make)是一个开源的、跨平台的构建系统,它可以帮助开发者编写复杂的构建规则,并确保代码在不同的平台和环境中都能正确地编译和运行。但为什么我们需要 CMake 呢?
想象一下,你正在读一本名为《C++ Primer》(C++ 入门)的书,这本书详细介绍了 C++ 的基础知识和高级特性。但当你尝试在不同的操作系统或编译器上编译和运行书中的示例代码时,你可能会遇到各种问题。这时,你可能会想:“为什么编程要这么复杂?”
答案很简单:因为每个操作系统和编译器都有自己的特性和限制。正如心理学家阿布拉罕·马斯洛(Abraham Maslow)所说:“如果你只有一把锤子,你会把每个问题都看作是钉子。”同样,如果你只熟悉一个编译器或操作系统,你可能会认为所有的代码都应该按照这个编译器或操作系统的规则来写。
但是,CMake 提供了一个解决方案。它允许你编写一套构建规则,然后在不同的平台和环境中自动调整这些规则,以确保代码能够正确地编译和运行。这就像是你有了一个万能的工具箱,无论你面对什么样的问题,你都可以找到合适的工具来解决它。
1.2 环境变量的基本概念
环境变量(Environment Variables)是操作系统中用于存储各种配置信息的变量。它们通常用于存储诸如文件路径、系统设置和其他重要数据的值。
为什么我们需要环境变量呢?让我们从心理学的角度来看待这个问题。
当你在一个陌生的环境中,你可能会感到迷茫和不安。你不知道该去哪里,也不知道该做什么。但是,如果你有一个地图或指南,你就可以轻松地找到你的目的地。同样,在编程中,当代码需要访问某些外部资源(如文件或网络服务)时,它需要知道这些资源的位置。环境变量就像是这个“地图”或“指南”,它告诉代码如何找到这些资源。
例如,PATH
环境变量指定了系统在哪里查找可执行文件。当你在命令行中输入一个命令时,系统会在 PATH
环境变量中列出的目录中查找这个命令。如果没有 PATH
环境变量,你就需要为每个命令指定完整的路径,这会非常麻烦。
环境变量 | 描述 | 示例 |
PATH |
系统查找可执行文件的目录列表 | /usr/local/bin:/usr/bin:/bin |
HOME |
当前用户的主目录 | /home/username |
LANG |
系统的语言和字符集设置 | en_US.UTF-8 |
正如心理学家 B.F. 斯金纳(B.F. Skinner)所说:“行为是由其后果来控制的。”在编程中,我们的代码也是由其输入(如环境变量)来控制的。通过正确地设置和使用环境变量,我们可以确保代码的行为是可预测和可控的。
2. CMake 的工作原理
在编程的旅程中,理解工具的工作原理是至关重要的。这不仅可以帮助我们更有效地使用这些工具,还可以帮助我们避免常见的陷阱和错误。
2.1 CMake 的基本概念和工作流程
CMake(C++ Make)是一个构建工具,它可以帮助开发者编写和管理复杂的构建规则。但是,CMake 与其他构建工具有什么不同呢?
首先,我们需要理解 CMake 的核心概念:构建文件生成。与其他直接执行构建任务的工具不同,CMake 的主要任务是生成构建文件。这些构建文件然后可以被 make
、ninja
或其他构建工具使用,以实际编译和链接代码。
这种分离的设计思路与心理学中的分离关注点原则相吻合。这个原则告诉我们,为了更有效地处理复杂的问题,我们应该将其分解为更小、更具体的部分。CMake 通过将构建规则的定义与实际的构建过程分离,使得开发者可以更专注于编写规则,而不是关心如何执行它们。
2.1.1 CMakeLists.txt
CMake 的配置文件称为 CMakeLists.txt
。这个文件包含了所有的构建规则和设置。每当你想要添加一个新的源文件、库或可执行文件,或者更改构建选项时,你都需要修改这个文件。
例如,以下是一个简单的 CMakeLists.txt
文件,它定义了一个名为 hello
的可执行文件:
cmake_minimum_required(VERSION 3.10) project(HelloWorld) add_executable(hello main.cpp)
这个文件告诉 CMake,我们需要至少版本为 3.10 的 CMake,项目的名字是 HelloWorld
,并且我们想要创建一个名为 hello
的可执行文件,它的源代码是 main.cpp
。
2.1.2 CMake 的命令行工具
一旦你编写了 CMakeLists.txt
文件,你就可以使用 CMake 的命令行工具来生成构建文件。这个过程通常包括以下步骤:
- 创建一个空的构建目录。
- 在这个目录中运行
cmake
命令,并指定CMakeLists.txt
文件的路径。 - 使用
make
、ninja
或其他构建工具来编译和链接代码。
这种工作流程允许开发者在不同的环境和平台上使用相同的 CMakeLists.txt
文件,而不需要为每个环境或平台编写特定的构建规则。
2.2 如何在 CMakeLists.txt 中定义和使用变量
在 CMake 中,变量是存储信息的基本单位。这些信息可以是文件路径、编译选项或其他任何数据。正如心理学家乔治·米勒(George A. Miller)在其著名的论文《魔数七,加上或减去二》中所说,人类的短期记忆有限。为了处理大量的信息,我们需要将其组织成更小、更易于管理的块。在 CMake 中,变量就是这些“块”。
2.2.1 定义变量
在 CMakeLists.txt
文件中,你可以使用 set
命令来定义一个新的变量。例如:
set(SOURCE_FILES main.cpp util.cpp)
这个命令定义了一个名为 SOURCE_FILES
的变量,它包含了两个源文件的路径。
2.2.2 使用变量
一旦你定义了一个变量,你就可以在 CMakeLists.txt
文件中的任何地方使用它。为了使用一个变量,你需要在其名称前后加上 ${}
。例如:
add_executable(my_app ${SOURCE_FILES})
这个命令告诉 CMake,我们想要创建一个名为 my_app
的可执行文件,它的源代码是 SOURCE_FILES
变量中列出的文件。
3. Shell 的交互模式与非交互模式
在编程的世界中,Shell 是一个强大的工具,它允许我们与操作系统进行交互,执行命令和管理文件。但是,你是否知道 Shell 有多种运行模式,每种模式都有其特定的用途和行为?
3.1 交互式 vs. 非交互式 Shell 的定义
当我们谈论 Shell 的交互模式和非交互模式时,我们实际上是在谈论 Shell 如何与用户进行交互。
3.1.1 交互式 Shell
交互式 Shell 是指当你直接在终端或控制台中输入命令时使用的 Shell。在这种模式下,Shell 会显示一个提示符,等待你输入命令。当你输入命令并按下 Enter 键后,Shell 会执行该命令并显示结果。
这种直接的、实时的交互方式与心理学中的即时反馈原理相吻合。即时反馈可以增强学习和记忆,因为它允许我们立即看到我们的行为的结果。同样,当我们在交互式 Shell 中输入命令时,我们可以立即看到命令的输出,这有助于我们理解和记忆这些命令。
3.1.2 非交互式 Shell
非交互式 Shell 是指 Shell 脚本或其他程序调用 Shell 时使用的 Shell。在这种模式下,Shell 不会显示提示符,也不会等待用户输入。相反,它会按照脚本或程序中的指令执行命令,并返回结果。
这种模式与心理学中的条件反射原理相似。条件反射是一种学习过程,其中一个刺激(如铃声)与另一个刺激(如食物)反复配对,直到第一个刺激本身就能引起与第二个刺激相关的反应(如流口水)。同样,在非交互式 Shell 中,命令是预先定义的,它们会在特定的条件下自动执行,而不需要用户的干预。
3.2 初始化文件的加载顺序(如 ~/.bashrc
vs. ~/.bash_profile
)
当 Shell 启动时,它会加载一些初始化文件,这些文件包含了设置环境变量、定义函数和别名等的命令。但是,不同的 Shell 模式会加载不同的初始化文件。
3.2.1 交互式 Shell 的初始化文件
当你启动一个交互式登录 Shell(例如,当你首次登录系统时)时,Shell 会加载 ~/.bash_profile
或 ~/.profile
文件(取决于你的系统和 Shell 版本)。这些文件通常用于设置全局环境变量和启动程序。
当你启动一个交互式非登录 Shell(例如,打开一个新的终端窗口)时,Shell 会加载 ~/.bashrc
文件。这个文件通常用于定义函数、别名和其他只适用于当前 Shell 会话的设置。
3.2.2 非交互式 Shell 的初始化文件
当 Shell 以非交互模式运行(例如,执行 Shell 脚本)时,它不会加载任何初始化文件。这是因为在这种模式下,Shell 通常只执行脚本中的命令,而不需要任何额外的设置。
然而,你可以使用 BASH_ENV
环境变量来指定一个文件,Shell 会在非交互模式下加载这个文件。这可以用于设置脚本所需的特定环境变量或函数。
4. 环境变量的继承机制
环境变量(Environment Variables)是操作系统中用于存储配置信息的全局变量,它们为运行在计算机上的进程提供了关于计算机环境的信息。在 C/C++ 和嵌入式开发中,我们经常需要配置和使用这些变量来满足不同的开发和运行需求。
4.1 如何在 Shell 中设置和使用环境变量
在 Unix-like 系统中,我们通常使用 Shell 来设置和使用环境变量。例如,我们可以使用 export
命令来设置一个新的环境变量:
export MY_VARIABLE=value
当我们想要查看一个环境变量的值时,我们可以使用 echo
命令:
echo $MY_VARIABLE
这种方式的设置是暂时的,当你关闭 Shell 时,这些变量就会消失。为了使环境变量永久生效,我们通常会将它们添加到 ~/.bashrc
或 ~/.bash_profile
文件中。
“人们经常因为害怕改变而错过捕捉幸福的机会。” —— Leo Buscaglia
这句话提醒我们,环境变量的改变可能会影响到我们的程序行为,所以我们应该时刻注意它们的设置和使用。
4.2 子进程如何继承父进程的环境变量
当一个进程启动一个新的子进程时,子进程会继承父进程的环境变量。这是一个非常自然的过程,就像我们从父母那里继承了某些特征一样。
例如,当我们在一个 Shell 中设置了一个环境变量,然后从这个 Shell 启动一个新的程序,这个程序就可以访问我们设置的环境变量。
#include <iostream> #include <cstdlib> // for getenv int main() { const char* value = getenv("MY_VARIABLE"); if (value) { std::cout << "MY_VARIABLE: " << value << std::endl; } else { std::cout << "MY_VARIABLE is not set." << std::endl; } return 0; }
在这个 C++ 示例中,我们使用 getenv
函数来获取环境变量的值。这个函数是 C 标准库中的一个函数,它允许我们在程序中访问环境变量。
方法 | 描述 | 示例 |
export |
设置环境变量 | export MY_VARIABLE=value |
echo |
查看环境变量的值 | echo $MY_VARIABLE |
getenv |
在 C/C++ 中获取环境变量的值 | getenv("MY_VARIABLE") |
“我们不能改变现实,但我们可以改变我们对现实的看法。” —— Wayne Dyer
这句话告诉我们,尽管环境变量的值可能是固定的,但我们可以通过不同的方法和工具来查看和解释它们。
4.2.1 环境变量的覆盖
当子进程启动时,它会继承父进程的环境变量。但是,子进程可以修改它自己的环境变量,而不影响父进程。这就像我们可以选择遵循或忽略从父母那里学到的教训一样。
例如,我们可以在子进程中使用 export
命令来修改或设置新的环境变量,而这些变量的值不会影响到父进程。
5. CMake 中的 execute_process
命令
在 C++ 开发中,我们经常需要调用外部命令或脚本来完成某些任务,例如生成代码、检查依赖关系或执行测试。CMake 提供了一个非常强大的命令 execute_process
,允许我们在配置阶段执行外部进程。
5.1 execute_process
的基本用法
execute_process
命令允许我们在 CMakeLists.txt 文件中执行外部命令。它的基本语法如下:
execute_process( COMMAND cmd [arg1 [arg2 ...]] [WORKING_DIRECTORY dir] [RESULT_VARIABLE var] [OUTPUT_VARIABLE var] [ERROR_VARIABLE var] [INPUT_FILE file] [OUTPUT_FILE file] [ERROR_FILE file] [TIMEOUT seconds] [ENVIRONMENT var=value...] )
COMMAND
:要执行的命令及其参数。WORKING_DIRECTORY
:命令的工作目录。RESULT_VARIABLE
:用于存储命令返回值的变量。OUTPUT_VARIABLE
:用于存储命令的标准输出的变量。ERROR_VARIABLE
:用于存储命令的标准错误的变量。
例如,我们可以使用以下命令来获取 Git 仓库的当前分支:
execute_process( COMMAND git rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE ) message("当前分支: ${GIT_BRANCH}")
这个命令会在项目的源代码目录中执行 git rev-parse --abbrev-ref HEAD
,并将结果存储在 GIT_BRANCH
变量中。
5.2 如何使用 ENVIRONMENT
选项
当我们需要在特定的环境下执行命令时,可以使用 ENVIRONMENT
选项来设置或修改环境变量。例如,我们可能需要在特定的 PATH
下执行某个命令:
execute_process( COMMAND some_command ENVIRONMENT "PATH=/path/to/special/bin:${PATH}" )
这样,some_command
就会在一个包含 /path/to/special/bin
的 PATH
环境变量中执行。
【Linux 环境变量相关】深入理解Linux下 CMake、Shell 与环境变量的交互(二)https://developer.aliyun.com/article/1467705