# gcc & make & cmake

## 编译

### 安装 gcc (多版本共存)

* 阿里云镜像：<http://mirrors.aliyun.com/gnu/gcc/>

参考博客: [Linux下编译安装GCC 4.9.4 - Caosiyang's Blog](https://caosiyang.github.io/posts/2016/05/04/installing-gcc/)

```
# 下载gcc源码解压后的目录假定为gcc-4.9.4
cd gcc-4.9.4
sh ./contrib/download_prerequisites
cd ..
mkdir build-gcc-4.9.4
cd build-gcc-4.9.4
# gcc-4.9.4将会安装至/usr/local/gcc-4.9.4/目录下
../gcc-4.9.4/configure --prefix=/usr/local/gcc-4.9.4/ --enable-checking=release --enable-languages=c,c++ --disable-multilib
make -j4
make install
```

### 关于头文件与库的搜索路径

```
C_INCLUDE_PATH  # C 头文件库搜索路径, 备注: 系统本身的不在这个变量里
CPLUS_INCLUDE_PATH  # C++ 头文件库搜索路径, 备注: 系统本身的不在这个变量里
LD_LIBRARY_PATH  # 动态链接库搜索路径, 备注: 系统本身的不在这个变量里
LIBRARY_PATH  # # 静态链接库搜索路径, 备注: 系统本身的不在这个变量里
```

为什么通常会需要配置 `LD_LIBRARY_PATH` 而不需要配置 `C_INCLUDE_PATH` 和 `CPLUS_INCLUDE_PATH`, 例如:

* 安装 CUDA
* 安装 openCV
* 安装 tensorRT

### 关于 `ldconfig`

注：以下均为简单理解，并非准确理解

在 Linux 下，默认动态链接库的搜索路径保存在 `/etc/ld.so.conf` 中，默认动态链接库的访问使用缓存机制，存放在 `/etc/ld.so.cache` （二进制格式），如果在默认路径下添加了动态链接库，则需要使用 `ldconfig` 更新默认路径里的动态链接库至 `/etc/ld.so.cache`。具体来说，可能会遇到需要使用 `ldconfig` 命令的场景例如安装 `mysql` 时，默认会将 mysql 的库文件安装到 `/usr/local/mysql/lib`，这个目录一般是在默认的动态链接库搜索路径下，因此由于缓存机制的存在，如果在不执行 `ldconfig` 时，在需要使用 mysql 相关的动态链接库时，会报找不到库的错误。

`ldconfig -v` 用于查看已经缓存的动态链接库。

另外，ldconfig 是系统层面的一些机制，在用户层面，也可以配置 `LD_LIBRARY_PATH` 来添加库目录

### 示例(杂录)

**例子1**

目录结构及文件内容

```
main.cc
cheader/
  - MathFunctions.cc
  - MathFunctions.h

// main.cc文件
#include "MathFunctions.h"
```

编译方法

**单条指令编译**

```bash
gcc main.cc cheader/MathFunctions.cc -I cheader/ -o Demo
```

* `-I` 选项用于增加include目录
* `-o` 选项用于指定编译输出的文件位置

**先编译链接库，再利用链接库编译应用**

```bash
# 编译动态链接库: 两种都可以
gcc -shared -o cheader/libMathFunctions.so -fPIC cheader/MathFunctions.cc
# cd cheader && gcc -shared -o libMathFunctions.so -fPIC MathFunctions.cc && cd ..

# 使用动态链接库
# 方式一：此处main.cc与cheader/libMathFunctions.so顺序不能乱
gcc -I cheader -o Demo main.cc cheader/libMathFunctions.so
# 方式二：(待修改)可以找到链接库但找不到符号?
gcc -I cheader -o Demo -Lcheader -l MathFunctions main.cc
```

* `-L`用于增加静态/动态链接库的搜索路径，`-l`用于指定静态/动态链接库的具体名称(注意不含lib前缀及文件扩展名)。[stackoverflow问答的解释](https://stackoverflow.com/questions/71544910/usr-bin-ld-cannot-find-lname-of-the-library-while-compiling-with-gcc)
* 可以使用 gcc 的组件 nm 命令查看 .so 文件中的符号。[stackoverflow](https://stackoverflow.com/questions/43256459/g-undefined-reference-although-symbol-is-present-in-so-file)

  ```bash
  nm --demangle --defined-only --extern-only cheader/libMathFunctions.so
  # 或
  nm -D cheader/libMathFunctions.so
  ```

### 编译步骤、动态链接库与静态链接库

编译步骤拆解为：

* 编译预处理（pre-processing）：将 `#include` 处理好，宏展开等。使用 `-E` 指定，生成文件后缀名习惯用 `.i`。
* 编译（compiling）：转换为汇编代码。使用 `-S` 指定，生成文件后缀名习惯用 `.s`。
* 汇编（assembling）：将汇编代码转换为目标文件。使用 `-c` 指定，生成文件后缀名习惯用 `.o`。
* 链接（linking）：将目标文件进行链接，最终生成可执行文件
  * 可以把多个目标文件打包为一个作为函数库，这一过程可以借助 `ar` 命令来完成。函数库分为动态链接库与静态链接库

参考资料：

* 不确定好坏的资料：<https://tldp.org/HOWTO/Program-Library-HOWTO/index.html>

## Make

```bash
# 下面的命令用于观察具体的执行命令
# 然而使用 CMake 生成 Makefile, 接下来使用 make -n 命令以获取具体步骤, 往往不会深入到 gcc 命令
make --dry-run  # make -n
make VERBOSE=1
make --dry-run VERBOSE=1
```

## CMake

资源：

* 官方Tutorial：[链接](https://cmake.org/cmake/help/latest/guide/tutorial/index.html)
* cmake-demo：[Gitbook](https://www.hahack.com/codes/cmake/)，[Github](https://github.com/wzpan/cmake-demo)
* cmake-examples：[Github](https://github.com/ttroy50/cmake-examples)

### 项目结构与 cmake 基本命令

CMake 用于生成 Makefile (如果使用 make 的话), 典型的编译步骤如下

```bash
# 步骤一: 配置(Configure)
# 生成 Makefile 文件 (准确地说会根据平台不同有所变化)
mkdir build && cd build && cmake ..
# 或者是
cmake -B build -S .. && cd build
# 加上下面的参数, 会额外生成 compile_commands.json, 可以观察到相应的编译命令
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..

# 步骤二: 构建(Build)
# (1) 可以直接用 make 命令
make
# 只展示编译步骤不执行, 但得不到想要的 gcc 编译命令
make --dry-run
# 或者简写为
make -n

# (2) 或者用 cmake 命令
cmake --build .
# 只展示编译步骤不执行, 但得不到想要的 gcc 编译命令
cmake --build . -- --dry-run

# 步骤三: 安装(Install), 可选, cmake --build 命令在 cmake>=3.15 才能使用, 之前的版本只能用 make --install
# 如果在配置环节不指定 -DCMAKE_INSTALL_PREFIX=/path/to/you/wish, 默认会安装到 /usr/local/bin, /usr/local/include, /usr/local/lib 这些路径
cmake --install .

# 指定安装目录既可以在 Configure 阶段指定 -DCMAKE_INSTALL_PREFIX=/path/to/you/wish
# 也可以在 Install 阶段
cmake --install . --prefix "/path/to/you/wish"

# (TODO: 不确定含义) 如果使用 multi-configuration tools, 可以使用这个
cmake --install . --config Release

# 也可以用下面的方式代替 cmake --install . 但 cmake --install . 更具兼容性和移植性
# cmake --build . --target install
```

一种典型的项目组织如下

```
root/
  - src/      # 源文件
  - include/  # 头文件
  - build/    # out-of-tree build
  - CMakeLists.txt
```

备注: 有些项目源文件和头文件不会分离, 而是放在同一级目录下, 也是常见的

### CMakeLists.txt 语法

#### 重要语法索引 (cheetsheet)

初学先概览, 回头再复习. TODO: 此部分内容来自 GPT, 回头再做确认(似乎不太靠谱, 很多出现在 CMake 官方 tutorial 中的命令这里没有, 许多这里出现的, CMake 官方 tutorial 中没有).

* 项目信息
  * `cmake_minimum_required(VERSION X.Y)`: 指定 CMake 的最低版本要求。
  * `project(<project_name>)`: 定义项目的名称，可以可选地指定语言（C、C++等）。
* 源文件与目标
  * `add_executable(<target> <source1> <source2> ...)`: 创建一个可执行目标。
  * `add_library(<target> <source1> <source2> ...)`: 创建一个库目标（静态库或共享库）。
  * `aux_source_directory(<directory> <variable>)`: 查找指定目录中的所有源文件，并将它们的名称存入变量。
* 依赖关系
  * `target_link_libraries(<target> <library1> <library2> ...)`: 指定目标所依赖的库。
  * `add_subdirectory(<directory>)`: 添加子目录，通常用于包含其他 CMakeLists.txt 文件。
* 变量和选项
  * `set(<variable> <value>)`: 定义或设置变量。
  * `option(<option_name> <description> <default>)`: 定义一个布尔选项，用户可以选择该选项。
  * `if(<condition>) ... endif()`: 条件语句，用于控制构建时的行为。
  * `foreach(<var> <items>) ... endforeach()`: 迭代语句，用于遍历列表中的每一项。
* 包含和查找模块
  * `find_package(<package_name> [REQUIRED])`: 查找并加载指定的包。
  * `include_directories(<directory>)`: 添加包含目录。
  * `link_directories(<directory>)`: 添加链接目录。
* 生成规则和安装
  * `install(TARGETS <target> DESTINATION <directory>)`: 指定安装目标及其安装位置。
  * `file(<command> <args>)`: 处理文件的多种操作，例如复制、删除、生成等。
* 配置和构建选项
  * `set(CMAKE_BUILD_TYPE <type>)`: 设置构建类型，例如 Debug 或 Release。
  * `set(CMAKE_CXX_STANDARD <version>)`: 设置 C++ 标准。
* 脚本与宏
  * `macro(<name> <args>) ... endmacro()`: 定义一个宏，可以多次调用。
  * `function(<name> <args>) ... endfunction()`: 定义一个函数，具有局部作用域。
* 自定义命令和目标
  * `add_custom_command(...)`: 定义自定义构建命令。
  * `add_custom_target(...)`: 定义自定义构建目标。
* 其他
  * `message(<message>)`: 打印消息到标准输出。
  * `include(<module>)`: 包含其他 CMake 文件。

`add_library`, `add_executable` 分别表示创建一个库/可执行文件以及其所需要的源文件, 它们之后可能会跟上 `target_link_libraries`, `target_include_directories`, `target_compile_definitions`, 分别表示为了生成这个库/可执行文件需要的其他库名,头文件目录,编译时宏定义.

这类 `target_xxx` 的命令还有:

* `target_compile_features`: 用于指定 C++ 标准, 例如: `target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)`
* `target_compile_options`: 直接指定编译选项, 但需要手动解决跨平台问题. 例如: `target_compile_options(tutorial_compiler_flags INTERFACE -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused)`. 所谓跨平台, 指的是例如指定 C++ 标准, 使用 `gcc` 编译器, 写法是 `gcc -std=c++11 ...`, 而 `MSVC` 编译器的写法是 `cl /std:c++11`. 因此一般来说, 使用 `target_compile_options` 时可能会见到下面的写法:

```cmake
# 下面的生成器表达式语法需要 >=3.15
cmake_minimum_required(VERSION 3.15)

# 这个是一个“虚拟”的库,用于添加编译选项
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

# COMPILE_LANG_AND_ID 是 cmake 一个内置的生成器表达式, 生成器表达式的语法是 $<xxx:yyy>, 而 COMPILE_LANG_AND_ID 的语法是
# $<COMPILE_LANG_AND_ID:lang, compiler_id>
# 其中 lang 代表编程语言, compiler_id 是编译器名(可以有多个,用逗号隔开), 如果检查到编译器, 这一项的值为"1",否则为"0"

set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")

# "$<1:...>" 的值为 "...", "$<0:...>" 的值为空字符串 ""
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
  "$<${msvc_cxx}:-W3>"
)
```

特殊变量: `PROJECT_SOURCE_DIR`, `PROJECT_BINARY_DIR`, `CMAKE_CURRENT_SOURCE_DIR`

#### 官方 Tutorial 笔记

链接 (本文写作时的版本为`cmake==3.31.0-rc2`): <https://cmake.org/cmake/help/latest/guide/tutorial>

Step1: A Basic Starting Point

主要介绍了单个源文件和头文件的情形, 但内容其实比较多

(1) 首先介绍了 `project`, `cmake_minimum_required`, `add_executable` 命令, 以及使用 cmake 命令行工具进行配置和构建

(2) 然后使用 `set` 命令配置 cmake 内置的变量 `CMAKE_CXX_STANDARD` 和 `CMAKE_CXX_STANDARD_REQUIRED`, 从而实现对 C/C++标准 的管控

(3) 最后补充介绍了 `project` 中可以定义项目的版本号, 在这之后的 `CMakeLists.txt` 内容里就可以使用 `<PROJECT-NAME>_VERSION_MAJOR` 和 `<PROJECT-NAME>_VERSION_MINOR` 内置变量, 为了将这两个变量变得可以在源码中使用(源码中用宏来获取这两个变量), 可以采用如下方案: 配置 `config.h.in`, 在这个模板文件中使用 `@<PROJECT-NAME>_VERSION_MAJOR@` 的语法用于宏值的定义, 并且在 `CMakeLists.txt` 中使用 `configure_file` 命令使得 cmake 在构建项目时生成真正的 `config.h` 文件, 最后使用 `target_include_directories` 让可执行文件的编译过程正确地 include 出于 `PROJECT_BINARY_DIR` 的 `config.h` 文件.

Step2: Adding a Library

主要介绍了多个源文件的情形, 且库代码位于单独的目录内, 且包含有自己的 `CMakeLists.txt` 文件.

(1) 内层 `CMakeLists.txt` 引入了 `add_library` 命令, 而外层的 `CMakeLists.txt` 为了使得最终的 main 程序能链接库, 需要使用 `add_subdirectory` 用于执行得到库, 使用 `target_link_libraries` 来让 main 链接到库, 使用 `target_include_directories` 使得 main 在编译时能识别到库的头文件

(2) 演示了如何利用 `option` 来为 cmake 在配置阶段增加 `-D<OPTION_NAME>=<value>` 来控制编译行为. 在官方的例子中, 场景是这样的, 我们的库是一个计算 `sqrt` 的函数, 而 main 程序希望实现如下功能: 如果希望使用我们自己实现的数学库, 那么就编译出该数学库, 并且 main 程序链接并使用其实现; 如果希望使用官方的数学库 `cmath`, 那么就不编译我们自己的数学库. 具体实现方式如下: TODO

Step3: Adding Usage Requirements for a Library

主要是优化 Step1 和 Step2 中的一些写法: 在 Step1 中, C/C++标准 的管控是使用 `set` 命令全局管控 `CMAKE_CXX_STANDARD` 和 `CMAKE_CXX_STANDARD_REQUIRED` 来实现的, 这样就不能实现不同模块采用不同的 C/C++标准; 在 Step2 中, 外层的 main 不仅需要链接库, 还需要通过 `target_include_directories` 添加库文件目录.

Step4: Adding Generator Expressions

本部分感觉不是核心用法, Generator Expressions 是 `CMakeLists.txt` 的脚本特性

Step5: Installing and Testing

引入 cmake 项目安装, 以及单元测试, TODO: 此处代码移除

```bash
# 首先 config
mkdir build && cd build && cmake ..
# 然后可以 install/test/pack
ctest
cpack
```

Step6: Adding Support for a Testing Dashboard

本部分感觉不是核心用法, 单元测试可以更花哨, 可以有个仪表盘来展示

Step7: Adding System Introspection

本部分与 Step6 都使用了 `include` 语法, 来引入 `/path/to/cmake/Modules` 中的其他“脚本”文件

Step8: Adding a Custom Command and Generated File

本部分实现了一个特殊功能: 首先编写一段 C 代码, 其执行后会生成一个头文件, 整个项目的构建过程是先得到这个头文件 (通过自定义命令来实现: `add_custom_command`), 然后其他的库/可执行文件可能需要使用这个头文件

Step9: Packaging an Installer

本部分介绍了项目打包, 具体介绍了使用 cpack 的打包方式, 打包为 `.tar.gz` 文件(二进制安装), 操作比较简单, `CMakeLists.txt` 加几行配置即可. 其他的项目打包和发布方式待探索

Step10: Selecting Static or Shared Libraries

前序步骤所有的库都是使用的静态库(当然也可以在 `add_library` 是设定为动态库, 但前面没有明确提到), 本部分介绍怎么“优雅”地配置以生成动态链接库

Step11: Adding Export Configuration

TODO: 似乎是让本项目在其他项目中能使用, TODO

Step12: Packaging Debug and Release

TODO

#### 单文件

**引入**: `cmake_minimum_required`, `project`, `add_executable`, `aux_source_directory`

最简单的情况:

* 一个源文件和头文件, 用于编译得到一个可执行文件(引入 `cmake_minimum_required`, `project`, `add_executable`)
* 多个源文件和头文件在同一目录, 最终得到一个可执行文件(引入 `aux_source_directory`)

#### 库与链接

**引入**: `add_library`, `target_link_libraries`, `target_include_directories`, `add_subdirectory`

```cmake
# cmake_minimum_required
# project (Demo)
add_subdirectory(math)

# math/CMakeLists.txt 包含如下内容
# aux_source_directory(. DIR_LIB_SRCS)
# add_library (MathFunctions ${DIR_LIB_SRCS})

add_executable(Demo main.cc)
target_link_libraries(Demo MathFunctions)
# 类似地, 也有一个 target_include_directories 来指定 include 目录
# target_include_directories(Demo PUBLIC "${PROJECT_BINARY_DIR}")
```

注意: `add_executable` 用于指定可执行文件依赖的源文件, 而 `target_link_libraries` 用于指定可执行文件依赖的库文件. 并且 `add_executable` 必须在 `target_link_libraries` **之前**. 虽然按照标准的 gcc 编译流程, 编译步骤确实是:

(1) 先编译并打包得到库文件 `libMathFunctions.a` (2) 使用源文件 `main.c` 编译得到 `main.o` (3) 使用 `main.o` 和 `libMathFunctions.a` 链接得到 `Demo`

但 CMakeLists.txt 的语法里不关心 `main.o` 这种中间产物, 它只关系为了生成 `Demo`, 它需要哪些源文件 (对应于 `add_executable`), 哪些库文件 (对应于 `target_link_libraries`)

`add_subdirectory` 命令表示“执行”子目录的 `CMakeLists.txt`: 父目录的 `CMakeLists.txt` 在 `add_subdirectory` 之前通过 `set` 定义的变量, 在子目录中的 `CMakeLists.txt` 是可见的, 并且在子目录 `CMakeLists.txt` 中定义的变量, 在父目录 `add_subdirectory` 之后, 对父目录的 `CMakeLists.txt` 也是可见的

#### configure\_file

`configure_file`: 根据模板文件生成宏定义头文件, 模板文件名通常是 `config.h.in`, 而宏定义头文件名一般是 `config.h`, 在使用 cmake 生成 Makefile 时, 会自动得到这个 `config.h` (与编译选项无关). 在 C/C++ 源代码中可以去通过 `#include "config.h"` 从而确定 `config.h` 中的宏定义

`CMakeLists.txt`:

```cmake
cmake_minimum_required(VERSION 3.10)
# 自动生成如下变量: Demo_VERSION_MAJOR 是 1, Demo_VERSION_MINOR 是 0
project(Demo VERSION 1.0)

set(USE_MYMATH ON)

configure_file (config.h.in config.h)
# 也可以更明确地这么写, PROJECT_SOURCE_DIR 和 PROJECT_BINARY_DIR 是预定义变量
# configure_file ("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/config.h")

# 添加可执行文件的源文件
add_executable(Demo demo.cxx)
# 明确添加 include 目录
target_include_directories(Demo PUBLIC "${PROJECT_BINARY_DIR}")
```

`demo.cxx`

```c++
#include <iostream>
#include "config.h"

int main(int argc, char* argv[])
{
#ifdef USE_MYMATH
    std::cout << "USE_MYMATH defined" << std::endl;
#else
    std::cout << "USE_MYMATH undefined" << std::endl;
#endif
    std::cout << " Version " << VERSION_MAJOR << "." << VERSION_MINOR << std::endl;
    return 0;
}
```

`config.h.in` 的写法如下:

```
// 这是注释:
// 如果在 CMakeLists.txt 的逻辑里, USE_MYMATH 变量的值为 ON, 那么生成的 config.h 里将包含 #define USE_MYMATH
// 如果在 CMakeLists.txt 的逻辑里, USE_MYMATH 变量的值为 OFF, 那么生成的 config.h 里将包含 /* #undef USE_MYMATH */
#cmakedefine USE_MYMATH
// 生成的 config.h 中包含宏和值: VERSION_MAJOR:1, VERSION_MINOR:0
#define VERSION_MAJOR @Demo_VERSION_MAJOR@
#define VERSION_MINOR @Demo_VERSION_MINOR@
```

#### 自定义编译选项

TODO: 搞完整

* `option` + `add_definitions`
* `option` + `config.h.in` + `include "config.h"`

**引入**: `option`, `add_definition`, `if`

前面已经看到过 cmake 像这样 `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` 的选项, 事实上, cmake 可以自定义编译选项. 涉及到的相关 cmake 语法有

* `option`: 增加一个 cmake 的编译选项, 也就是增加一个这种用法 `cmake -D<选项名>=<值>`
* `add_definitions`: 将编译选项直接传递给编译器, 也就是利用 gcc 的 `-D<宏名>=<值>` 参数, 例如: `gcc -DDEBUG main.c -o main`

```bash
cmake -DUSE_MYMATH=ON ..
```

为此需要在 CMakeLists.txt 中包含如下内容

```cmake
# USE_MYMATH 的默认设置为 ON, 也就是定义 USE_MYMATH 这个宏
option (USE_MYMATH "Use provided math implementation" ON)
add_definitions(-DUSE_MYMATH)

# 根据宏来确定编译方式
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/math")
  add_subdirectory (math)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
```

C 代码中可以这样使用

```c
#include <stdio.h>
#include <stdlib.h>

#ifdef USE_MYMATH
  #include "MathFunctions.h"
#else
  #include <math.h>
#endif

int main(int argc, char *argv[])
{
    if (argc < 3){printf("Usage: %s base exponent \n", argv[0]); return 1;}
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
#ifdef USE_MYMATH
    printf("Now we use our own Math library. \n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library. \n");
    double result = pow(base, exponent);
#endif
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}
```

#### 安装与测试

CMake 官方 Tutorial-5

```bash
mkdir Step5_build
mkdir Step5_install
cd Step5_install
# 设置为 ON 或者 OFF 会决定 libSqrtLibrary.a 是否被安装
cmake -DUSE_MYMATH=OFF ../Step5
cmake --build .
cmake --install . --prefix=/path/to/Step5_install

# libtutorial_compiler_flags.a 不被安装是因为它被这样声明为了 INTERFACE
# add_library(tutorial_compiler_flags INTERFACE)
```

***

TODO: 基本已确认

关于 cmake 命令与 CMakeLists.txt 内的相对路径问题: CMakeLists.txt 的写法只需关注它本身与源码的相对路径, 而不必关心 cmake 命令行的参数. 这一点和 python 有区别 (python 代码里的 import 需要与 python 作为启动脚本时的当前目录需要相配)

```
CMakeLists.txt
main.cc
MathFunctions.cc
MathFunctions.h
```

`CMakeLists.txt` 的文件内容如下:

```cmake
cmake_minimum_required (VERSION 2.8)
project (Demo2)

# 查找目录下的所有源文件, 并将名称保存到 DIR_SRCS 变量, 在这个例子里是 main.cc MathFunctions.cc
aux_source_directory(. DIR_SRCS)

# 指定生成目标, 表示生成的可执行文件名为 Demo
# 在这个例子里, 等价于 add_executable(Demo main.cc MathFuctions.cc)
add_executable(Demo ${DIR_SRCS})
```

使用 `cmake .` 和 `mkdir build && cd build && cmake ..` 或者 `cmake -B build -S .` 均可以成功, 后两者的 Makefile 在 `build/Makefile`

### `CMAKE_CURRENT_SOURCE_DIR` 总是指向 `CMakeLists.txt` 所在目录, 而 `CMAKE_CURRENT_BINARY_DIR` 总是指向生产的文件目录


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://buxianchen.gitbook.io/notes/tool/make.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
