---
tags: [CANN]
doc_type: knowledge-note
abstract: "理解最小工程、目标属性、编译选项和生成器表达式。"
返回目录: "[[计算机系/昇腾与CANN/大模型与算子专题学习导航|大模型与算子专题学习导航]]"
专题目录: "[[计算机系/昇腾与CANN/CMake与Ascend C编译入门|CMake与Ascend C编译入门]]"
下一篇: "[[计算机系/昇腾与CANN/02 Ascend C源码组织与编译运行|02 Ascend C源码组织与编译运行]]"
前置知识: "[[计算机系/昇腾与CANN/Ascend C算子基础整理|Ascend C算子基础整理]]"
---

# 01 CMake最小配置与编译选项

理解最小工程、目标属性、编译选项和生成器表达式。

## 1. CMake、编译器与构建工具

CMake 读取 `CMakeLists.txt`，根据源文件、编译选项和依赖关系生成构建规则，再由构建工具执行编译与链接。

```text
CMakeLists.txt：描述目标、源码、选项和依赖
        ↓ CMake 配置与生成
构建规则：例如 Makefile 或 Ninja 文件
        ↓ Make / Ninja 等构建工具执行
编译器和链接器处理源码
        ↓
可执行程序或库
```

| 名称 | 是什么 | 作用 |
| --- | --- | --- |
| C、C++、Ascend C | 编程语言 | 表达程序逻辑 |
| `C`、`CXX`、`ASC` | CMake 中的语言标识 | 为源文件启用对应语言的构建规则 |
| GCC、Clang、毕昇编译器等 | 编译器或编译器工具链 | 处理源代码并产生目标代码 |
| Make、Ninja | 构建工具 | 按依赖关系执行构建命令 |
| CMake | 构建配置与生成工具 | 描述工程，并生成适用于所选构建工具的规则 |

`gcc`、`g++` 通常指 GCC 工具链中的编译驱动命令。`g++` 常用于 C++ 编译和链接。**`CXX` 表示 C++ 语言，并不指定必须使用 `g++`。** 具体编译器由工具链、环境、CMake 配置与缓存等决定。

## 2. 一个 Ascend C 工程的配置

文件名是 **`CMakeLists.txt`**，括号使用英文半角字符。

```cmake
cmake_minimum_required(VERSION 3.16)

find_package(ASC REQUIRED)

project(kernel_samples LANGUAGES ASC CXX)

add_executable(demo
    add_custom.asc
)

target_compile_options(demo PRIVATE
    $<$<COMPILE_LANGUAGE:ASC>:--npu-arch=dav-2201>
)
```

这段配置的含义是：加载 ASC 构建支持，为 `kernel_samples` 工程启用 ASC 和 C++，创建名为 `demo` 的可执行目标，并在编译该目标的 ASC 源文件时传入指定 NPU 架构的选项。

使用它的前提是开发环境已经提供能被 CMake 找到的 ASC 包及相应编译工具。`find_package` 不会自动安装这些工具。

### Notebook 中的 `%%writefile`

在 Jupyter/IPython Notebook 中，可以用下面的单元格魔法命令，将单元格其余内容写入指定文件：

```text
%%writefile Sources/02.04/CMakeLists.txt
```

它应放在 Notebook 单元格首行，后面再接上面的 CMake 配置。**`%%writefile` 不属于 CMake 语法，不应写进 `CMakeLists.txt` 文件本身。**目标父目录需要存在；默认会覆盖同名文件。

直接在编辑器中创建 `CMakeLists.txt` 时，只需保存 CMake 配置，不需要这一行。

## 3. 逐行理解配置

### 3.1 `cmake_minimum_required`：最低 CMake 版本

```cmake
cmake_minimum_required(VERSION 3.16)
```

要求运行配置的 CMake 版本至少为 `3.16`，同时建立相应的 CMake 策略兼容基线。

这里指定的是 **CMake 版本**，与 C++ 标准版本、CANN 版本、编译器版本是不同概念。所使用的 ASC 包也可能提出更高的版本要求。

### 3.2 `find_package`：查找并加载 ASC 包

```cmake
find_package(ASC REQUIRED)
```

- `ASC` 是要查找的包名。
- `REQUIRED` 表示此依赖必需，找不到会中止配置。
- 找到后，CMake 加载包提供的构建配置；相关配置用于接入 ASC 语言及其编译支持。

它查找的是包的 CMake 集成，而不是“寻找所有 CANN 功能”。包配置可能进一步设置模块路径、编译规则或其他依赖，具体由该版本的包实现决定。

这份配置把它放在 `project(... LANGUAGES ASC ...)` 前面，是为了先加载 ASC 的语言支持，再让 `project` 启用 ASC。ASC 并非普通 CMake 安装默认就具备的语言支持。

常见的包发现方式包括设置 `CMAKE_PREFIX_PATH` 或包对应的配置目录，例如 `ASC_DIR`。具体路径应指向实际安装中的包配置位置，不能仅凭包名推断目录。

### 3.3 `project`：工程名与语言

```cmake
project(kernel_samples LANGUAGES ASC CXX)
```

| 参数 | 含义 |
| --- | --- |
| `kernel_samples` | 工程名称 |
| `LANGUAGES` | 后续列出需要启用的语言 |
| `ASC` | Ascend C 构建支持所定义的语言 |
| `CXX` | C++ |

如果还需要以 C 语言编译 `.c` 文件，应显式启用 `C`，例如：

```cmake
project(kernel_samples LANGUAGES ASC CXX C)
```

启用 `CXX` 不代表把 `C` 也列入工程语言。工程名也不决定输出程序名：这里工程叫 `kernel_samples`，后面创建的可执行目标叫 `demo`。

`ASC` 与 `CXX` 是构建语言分类，Host 与 Device 是执行位置分类，两者不能直接一一对应。普通 `.cpp` 通常使用 C++ 编译规则；`.asc` 可以同时包含 Host 入口、核函数启动和 Device 核函数，由 ASC 工具链处理相应部分。因此，不能简单理解为“所有 Host 代码都由 CXX 编译，所有 Device 代码都由 ASC 编译”。

### 3.4 `add_executable`：创建可执行目标

```cmake
add_executable(demo
    add_custom.asc
)
```

`demo` 是**目标名称**，`add_custom.asc` 是这个目标的源文件。相对源文件路径通常相对于当前 `CMakeLists.txt` 所在的源码目录解析。

这条命令把目标写入构建规则，在执行构建时才真正编译、链接。

**它不会创建一个叫 `demo` 的输出文件夹。**对于常见的 Linux 单配置构建，使用 `build` 作为构建目录时，顶层目标通常生成 `build/demo`；Windows 可执行文件通常有 `.exe` 后缀，多配置生成器还可能增加 `Debug` 或 `Release` 子目录。

若希望明确将可执行文件放进构建目录下的 `bin`：

```cmake
set_target_properties(demo PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
```

多配置生成器还可能在该目录下增加配置子目录。

文件名叫 `add_custom.asc` 并不保证它可以独立生成可执行程序。完整目标需要程序入口和必要依赖；如果文件只有 [算子基础](Ascend%20C算子基础整理.md)中的 `add_custom` 核函数，还需要补齐 Host 入口及调用代码。

### 3.5 `target_compile_options`：为目标增加编译选项

```cmake
target_compile_options(demo PRIVATE
    $<$<COMPILE_LANGUAGE:ASC>:--npu-arch=dav-2201>
)
```

这条命令为 `demo` 添加编译选项。它不是预处理宏定义，也不是链接选项。

| 要设置的内容 | 对应命令 |
| --- | --- |
| 编译选项 | `target_compile_options` |
| 预处理宏，如 `DEBUG_MODE=1` | `target_compile_definitions` |
| 头文件搜索目录 | `target_include_directories` |
| 链接库与目标依赖 | `target_link_libraries` |
| 链接选项 | `target_link_options` |

## 4. `PRIVATE`、`PUBLIC` 与 `INTERFACE`

这些关键字描述配置的**使用与传播范围**，与 C++ 类中的访问权限不同。

以 `target_compile_options` 为例：

| 关键字 | 当前目标自身使用 | 作为使用要求传给依赖它的目标 |
| --- | --- | --- |
| `PRIVATE` | 是 | 否 |
| `PUBLIC` | 是 | 是 |
| `INTERFACE` | 否 | 是 |

“依赖它”通常是指其他目标通过 `target_link_libraries` 使用这个库目标。传播机制在库工程中特别常见。

在 `demo` 中使用 `PRIVATE`，表示编译选项用于 `demo` 自身，不作为对外使用要求传播。它并不表示“定义了一个私有属性”或“其他源文件看不见”。

例如一个普通 C++ 库要让使用者找到公开头文件：

```cmake
add_library(helper STATIC helper.cpp)

target_include_directories(helper PUBLIC
    "${CMAKE_CURRENT_SOURCE_DIR}/include"
)

target_link_libraries(demo PRIVATE helper)
```

`helper` 的公开头文件路径会成为 `demo` 编译时的使用要求。这里 `demo` 使用 `PRIVATE` 链接，不会阻止它接收 `helper` 的公开要求，只是不再将该依赖作为自己的公开接口传播。

## 5. 生成器表达式：只给 ASC 文件传参

```cmake
$<$<COMPILE_LANGUAGE:ASC>:--npu-arch=dav-2201>
```

这是 CMake 的**生成器表达式**，在生成构建规则时结合当前目标、语言等上下文求值。

分成两层看：

```text
$<COMPILE_LANGUAGE:ASC>
    判断当前编译语言是否为 ASC

$<条件:内容>
    条件为真时产生“内容”，否则产生空内容
```

因此它表达的是：

```text
如果正在编译 ASC 源文件：
    添加 --npu-arch=dav-2201
否则：
    不添加这个选项
```

如果 `demo` 同时包含 `.asc` 和 `.cpp` 文件，这个选项只针对语言为 ASC 的编译步骤，避免把 NPU 专用参数直接传给普通 C++ 编译器。

`--npu-arch=dav-2201` 是传给 ASC 编译工具的目标架构选项。`dav-2201` 对应这里采用的 A2/A3 架构目标。它决定编译面向的架构，**不负责选择运行时的第几张卡**。

与 [算子基础](Ascend%20C算子基础整理.md)的代码对照：

| 配置或调用 | 所处阶段 | 作用 |
| --- | --- | --- |
| `--npu-arch=dav-2201` | 编译 | 指定目标 NPU 架构 |
| `aclrtSetDevice(0)` | 运行 | 选择编号为 0 的设备 |
| `hello_world<<<8, 0, stream>>>()` | 运行 | 在指定流中启动核函数，指定 block 数等参数 |

### 5.1 处理器型号与架构宏

`__NPU_ARCH__` 是编译环境提供的架构宏，用于在源码中判断编译目标架构。它与构建参数有关，但二者形式不同：`--npu-arch=dav-2201` 是编译选项，`__NPU_ARCH__` 的对应数值为 `2201`。

| 昇腾 AI 处理器型号 | `__NPU_ARCH__` |
| --- | --- |
| Atlas A3 训练系列产品／Atlas A3 推理系列产品 | `2201` |
| Atlas A2 训练系列产品／Atlas A2 推理系列产品 | `2201` |
| Atlas 200I/500 A2 推理产品 | `3002` |
| Atlas 推理系列产品 | `2002` |
| Atlas 训练系列产品 | `1001` |

产品名称中的“A2”不足以单独确定架构。例如，Atlas A2 训练／推理系列与 Atlas 200I/500 A2 推理产品对应的架构值不同，需要看完整型号。

这张表给出架构宏的对应关系，不能仅把每个数值拼接成 `dav-数值`，就认定当前工具链支持该编译参数。具体选项和可用 API 还取决于编译工具链及目标产品。
