darcamo/cmake-integration
GitHub: darcamo/cmake-integration
这是一个Emacs插件,用于集成CMake以简化C++项目的开发工作流。
Stars: 23 | Forks: 10
# Emacs 的 CMake 集成
此包通过利用 CMake 的项目信息,为 Emacs 与基于 CMake 的 C++ 项目提供无缝集成。它简化了常见的开发任务,例如:
- **预设管理:** 轻松选择并应用 CMake 的配置、构建、测试和打包预设。
- **目标编译:** 通过单个按键绑定编译项目中的特定目标。
- **调试:** 可轻松使用经典的 gdb mi 接口或通过 [dape](https://github.com/svaante/dape) 调用 gdb 来开始调试您的可执行目标。
- **Conan 集成:** 直接从 Emacs 管理 Conan 包和远程仓库,调用 `conan install` 等操作。
- **语言服务器:** 帮助语言服务器在项目中正确工作。
## 核心概念
- **依赖原生 Emacs 功能:** 此包将内置 `project` 包识别的内容视为一个项目。它假定顶层 `CMakeLists.txt` 位于项目根目录。如果根目录还包含 `CMakePresets.json` 和/或 `CMakeUserPresets.json`,则会读取它们以收集预设名称。
- **CMake 文件 API:** 此包依赖 CMake 的文件 API 来查询项目信息,例如可用目标、构建配置和预设。这允许对您的项目进行动态和准确的自省。
- **CMake 预设:** CMake 预设提供了一种标准化的方式来配置构建环境。此包允许您高效地选择和应用这些预设。
- 简化使用 C++ 时的常见操作,如配置项目、编译目标、运行可执行文件等。
## 安装
此包在 GitHub 上可用。您可以使用您偏好的 Emacs 包管理器进行安装:
- **使用 use-package 的示例(配合 elpaca):**
(use-package cmake-integration
:ensure (cmake-integration :type git :host github
:repo "darcamo/cmake-integration"))
- **使用 `vc` 后端的 use-package:**
(use-package cmake-integration
:vc (:url "https://github.com/darcamo/cmake-integration.git"
:rev :newest))
## 使用
此包提供了一个瞬态菜单 (`cmake-integration-transient`) 以便快速访问其功能。为了获得高效的工作流,建议将常用命令绑定到自定义按键上。

注意:尝试使用瞬态菜单,以更好地了解所有功能。
### 基本工作流
1. **配置项目:**
- 使用 `cmake-integration-select-configure-preset` 选择一个 CMake 配置预设。它会列出所有可用的配置预设,并提示您选择一个。
- **提示:** 如果预设定义了 `displayName` 字段,则使用其值作为注释。
- **提示:** CMake 支持多种预设类型(配置、构建、测试、打包)。当您选择一个配置预设时,`cmake-integration` 将自动尝试为其他类型选择匹配的预设。
- 使用 `cmake-integration-cmake-reconfigure` 配置项目。这会为 CMake 文件 API 生成必要的文件。
- **重要:** 在目标相关功能生效之前,您必须至少配置一次项目。
2. **编译目标:**
- 使用 `cmake-integration-select-current-target` 选择一个目标。
- 使用 `cmake-integration-save-and-compile-last-target` 编译上次选择的目标。
- 您也可以使用 `cmake-integration-save-and-compile` 在单个命令中选择目标并编译它。
3. **运行目标:**
- 使用 `cmake-integration-run-last-target` 执行已编译的可执行文件。
- **提示:** 如果您想自定义程序的执行方式,请参阅 `cmake-integration-program-launcher-function` 变量的文档。
- 如果您需要向可执行文件传递任何命令行参数,请使用 `cmake-integration-run-last-target-with-arguments` 指定自定义命令行参数,然后运行可执行文件。后续对 `cmake-integration-run-last-target` 的任何调用也将使用这些参数。
4. **调试目标:**
- 使用 `cmake-integration-debug-last-target` 调试上次编译的可执行文件。
- **提示:** 如果您想自定义程序的执行方式(例如使用 [dape](https://github.com/svaante/dape) 代替 Emacs 中的原生 gdb),请参阅 `cmake-integration-debug-launcher-function` 变量的文档。特别是对于 dape,您可能还有兴趣在您的 Emacs 配置中调用 `cmake-integration-setup-dape`。这将向 `dape-configs` 变量添加一个使用 `cmake-integration` 信息的配置。
5. **运行测试:**
- 您始终可以选择一个包含测试可执行文件的目标并像往常一样运行它。
- 如果您想使用 CTest 运行测试,请使用 `cmake-integration-run-ctest`。
### 示例按键绑定
以下 Emacs Lisp 代码演示了如何将大多数常用命令绑定到方便的按键上,主要是在 `c++-mode` 内。
```
(use-package cmake-integration
:bind (:map c++-mode-map
([f5] . cmake-integration-transient) ;; Open main transient menu
([M-f9] . cmake-integration-select-current-target) ;; Ask for target
([f9] . cmake-integration-save-and-compile-last-target) ;; Recompile last target
([C-f9] . cmake-integration-run-ctest) ;; Run CTest
([f10] . cmake-integration-run-last-target) ;; Run last target (with saved args)
([S-f10] . kill-compilation) ;; Stop compilation
([C-f10] . cmake-integration-debug-last-target) ;; Debug last target
([M-f10] . cmake-integration-run-last-target-with-arguments) ;; Run last target with custom args
([M-f8] . cmake-integration-select-configure-preset) ;; Select and configure preset
([f8] . cmake-integration-cmake-reconfigure) ;; Reconfigure with last preset
))
```
您也可以将这些按键绑定添加到全局映射中,但这会使它们在没有使用 CMake 项目时也可用,这并不是很有用。更方便的方法是使用 `cmake-integration-project-mode`。这是一个除了拥有专用按键映射和钩子之外没有任何功能的次要模式。您可以将上面代码片段中的 `c++-mode-map` 替换为 `cmake-integration-project-mode-map`,然后在您希望按键绑定激活的任何缓冲区中启用 `cmake-integration-project-mode`。
提示:一种自动在属于 CMake 项目的任何缓冲区中启用 `cmake-integration-project-mode` 的方法是启用 `global-cmake-integration-project-mode`。
## 额外配置
### 自定义目标可执行文件的运行方式
默认情况下,使用 `cmake-integration-run-last-target` 运行目标可执行文件时,进程在编译缓冲区中运行。如果程序需要从用户读取输入,请使用不同的“启动器函数”来自定义 `cmake-integration-program-launcher-function` 变量。这应该是一个接收两个参数的函数:应运行的命令和用于缓冲区的名称。
`cmake-integration-program-launcher-function` 变量也可以设置为以下三个符号之一,它们映射到可用的内置启动器函数,应涵盖大多数用例:
- `'compilation`(默认值):使用 Emacs 的 `compile` 功能在编译缓冲区中运行命令。这不允许与正在执行的程序进行输入交互。
- `'comint`:使用 Emacs 的 `compile` 功能,并将 comint 参数设置为 `t`,在 comint 缓冲区中运行命令。同时切换到该缓冲区。这允许输入交互。
- `'eshell`:使用 `eshell` 在 eshell 缓冲区中运行命令。
如果这三个选项之一不够,那么将 `cmake-integration-program-launcher-function` 设置为一个函数是可行的方法。例如,如果您想使用 Emacs 中众多终端模拟器中的任何一个,那么您可以轻松实现一个用于它的启动器函数,其中 `ci-eshell-program-launch-function` 函数的源代码可以作为起点。即使您不了解 Emacs lisp,在向 LLM 提问时提供 `ci-eshell-program-launch-function` 的源代码作为上下文也应该能给出可运行的代码。例如,向 LLM 提出以下提示
```
(defun vterm-program-launch-function (command &optional buffer-name)
"Launch COMMAND in a vterm buffer named BUFFER-NAME (defaults to *vterm*)."
(unless (featurep 'vterm)
(user-error "vterm is not available"))
(let ((buffer-name (or buffer-name "*vterm*")))
(unless (get-buffer buffer-name)
(vterm buffer-name))
(let ((vterm-buffer (get-buffer buffer-name)))
(with-current-buffer vterm-buffer
(goto-char (point-max))
(vterm-send-string command)
(vterm-send-return))
(pop-to-buffer vterm-buffer))))
```
您可以在您的 Emacs 配置中添加此代码,并通过以下方式将 `cmake-integration-program-launcher-function` 设置为 `'vterm-program-launch-function'`:
```
(setq cmake-integration-program-launcher-function 'vterm-program-launch-function)
```
### 自定义调试器的运行方式
默认情况下,使用 `cmake-integration-debug-last-target` 调试目标可执行文件时,使用 Emacs 原生的 gdb 集成。您可以使用不同的启动器函数来自定义 `cmake-integration-debug-launcher-function` 以更改此行为。这应该是一个接收三个参数的函数:被调试可执行文件的路径、传递给它的命令行参数的字符串以及工作目录。
`cmake-integration-debug-launcher-function` 变量也可以设置为以下两个符号之一,它们映射到可用的内置启动器函数:
- `classic-gdb`(默认值):使用 Emacs 中的原生 gdb 集成来调试当前目标。
- `dape`:使用 [dape](https://github.com/svaante/dape) 来调试当前目标。
- **注意**:这不会修改您的 `dape-configs` 变量。如果您想向 `dape-configs` 添加一个与 `cmake-integration` 集成的配置,请在您的 Emacs 配置中添加对 `cmake-integration-setup-dape` 的调用。
通过创建自定义的调试启动器函数可以轻松添加其他选项。下面的代码实现了一个使用 gdbgui 进行调试的简单启动器函数。
```
(defun gdbgui-debug-launch-function (executable-path &optional args run-dir)
"Debug EXECUTABLE-PATH with gdbgui, passign ARGS and using RUN-DIR as cwd."
(let* ((default-directory (or run-dir default-directory))
(gdbgui-args (format "%s %s" executable-path
(or args ""))))
(start-process "gdbgui" "*gdbgui*" "gdbgui" gdbgui-args))
;; Start the browser to show the gdbgui interface
(browse-url "http://127.0.0.1:5000/")
)
```
**注意**:完成调试后,您需要自行停止 `gdbgui` 进程。您可以通过调用 Emacs 的 `list-processes` 命令并从中停止 `gdbgui` 进程来实现。
### 构建目录
默认情况下,如果您不使用预设,则 `cmake-integration` 假设构建文件夹命名为 `build`。如果您的项目使用不同的构建目录,请相应地设置 `cmake-integration-build-dir` 变量。
如果您使用预设,则构建文件夹取自所选配置预设中的 `binaryDir` 字段,因为这是编译时实际使用的构建文件夹。`cmake-integration` 可以理解配置预设 `binaryDir` 字段中的几个宏替换。支持的是 `${sourceDir}` 和 `${presetName}`。将来如果需要,可能会添加其他替换。
**提示:** 将 `binaryDir` 设置为类似 `"${sourceDir}/build/${presetName}"` 的内容是为不同预设分离构建文件夹的简单方法。
注意:使用预设时会缓存构建文件夹,以避免再次从预设文件读取信息。当您更改预设时,此缓存会自动清除。如果您需要在不更改预设的情况下清除缓存(即,您编辑了预设文件中的 `binaryDir` 字段),请调用 `cmake-integration-refresh-build-folder-cache` 函数。
### 在补全期间隐藏某些目标
当调用 `cmake-integration-save-and-compile` 时,会显示一个目标列表,允许用户选择一个进行编译、运行等。在许多情况下,您可能只对特定的目标子集感兴趣,例如可执行目标。为了简化此过程,可以使用几个变量来过滤补全期间显示的目标。这些变量是:
- `cmake-integration-include-subproject-targets-during-completion`
- `cmake-integration-hide-library-targets-during-completion`
- `cmake-integration-hide-utility-targets-during-completion`
默认情况下,包含子项目,并且不隐藏任何目标。您只需在想要减少补全期间显示的目标数量时配置这些变量,这对于提高大型项目的性能很有用(主要是在初始运行期间,因为目标列表之后会被缓存)。此外,即使这些变量设置为过滤目标列表,您也可以通过按 `C-u C-u` 在执行 `cmake-integration-save-and-compile` 之前临时覆盖它们并显示所有目标。
### 项目配置(非版本控制项目或单体仓库)
`cmake-integration` 使用 Emacs 的 `project` 基础设施来确定项目根目录(Git 仓库会被自动检测)。如果您不使用版本控制或需要在 Git 仓库内自定义根目录,可以使用 `.project` 文件。Emacs 的 `project` 包可以通过将包含空 `.project` 文件(实际上任何具有特定名称的文件)的目录识别为项目根目录来进行扩展,方法是将该文件名添加到 `project-vc-extra-root-markers` 列表中(参见 `project-vc-extra-root-markers` 变量的文档)。
以下 Emacs Lisp 代码演示了如何实现这一点:
```
(add-to-list 'project-vc-extra-root-markers ".project")
```
### 加速获取目标列表
获取目标列表可能是 `cmake-integration` 中最慢的操作,特别是当目标很多或您通过 TRAMP 远程工作时。这是因为 `cmake-integration` 为每个目标读取并解析一个单独的 JSON 文件,以便在补全列表中使用其目标类型进行注释。如果此过程对您的用例来说太慢,并且您宁愿避免性能开销,可以将 `cmake-integration-annotate-targets` 设置为 `nil`。要仅将此设置应用于特定项目,请将其配置为目录局部变量。
注意:首次检索后会缓存此信息。此缓存也可以适当时保存/恢复(参见 [持久化状态](#persisting-state) 部分)。如果由于某种原因需要清除缓存,请调用 `cmake-integration-refresh-target-cache` 函数。
## 与语言服务器集成
`cmake-integration` 支持 `clangd` 和 `qmlls` 语言服务器(将来可能会添加更多)。此功能允许它在构建目录更改时更新这些服务器使用的配置文件,确保它们正确运行。有关更多详细信息,请参阅变量 `cmake-integration-setup-clangd` 和 `cmake-integration-setup-qmlls`。
## 与 Conan 包管理器集成
### 运行 Conan
- **在 CMake 配置期间:** 当您运行 `cmake-integration-cmake-configure-with-preset` 或 `cmake-integration-cmake-reconfigure` 时,传递一个前缀参数 (`C-u`) 将在使用 CMake 配置之前,首先在构建目录中执行 `conan install`。
- **独立运行:** 使用 `cmake-integration-run-conan` 在最后使用的构建文件夹中执行 `conan install`。
### Conan 参数和配置文件
- **`cmake-integration-conan-arguments`:** 设置此变量以向 `conan install` 传递参数。默认值为 `--build missing`。
- **`cmake-integration-conan-profile`:** 配置此变量以指定 Conan 配置文件。它可以是:
- 包含配置文件名称的字符串(例如 `"default"`)。
- 将 CMake 预设名称映射到 Conan 配置文件名称的 alist(例如 `'(("my-cmake-preset" . "my-conan-profile"))`)。
### 管理 Conan 远程仓库
使用 `cmake-integration-conan-manage-remotes` 与 Conan 远程仓库交互:
- **添加远程仓库 (`a` 或 `+`):** 提示输入远程仓库名称和 URL。
- **删除远程仓库 (`D`):** 删除选中的或标记的远程仓库。
- **切换启用状态 (`` 或 `f`):** 启用或禁用远程仓库。
- **切换 SSL 验证 (`v`):** 切换远程仓库的 SSL 验证。
- **访问 Conan 命令 (`c`):** 打开用于其他 Conan 操作的瞬态菜单。
### 显示 Conan 缓存和搜索仓库
- **`cmake-integration-conan-list-packages-in-local-cache`:** 列出本地安装的 Conan 包。
- **`cmake-integration-conan-search`:** 在配置的 Conan 仓库中搜索包。
这两个函数都在表格列表缓冲区中显示结果,提供过滤、标记项目 (`m`)、删除标记的项目(触发 `conan remove` 从缓存中删除配方)、以及将项目规格复制到 kill ring (`w`) 以在 `conanfile.txt` 中使用等操作。您还可以标记要添加到 `conanfile.txt` 的库(用 `i` 标记并用 `x` 进行更改,或使用 `I` 直接添加)。目前,此功能不支持向 `conanfile.py` 添加依赖项。

## 调试
要调试可执行目标:
- 使用 `cmake-integration-debug-last-target`。此命令将所有当前命令行参数传递给调试器内的可执行文件,并根据 `cmake-integration-run-working-directory` 设置工作目录。
## 截图
**注意:** 以下截图使用了 `doom-material-dark` 主题,并配合 `vertico` 和 `marginalia` 以获得增强的补全界面。
- **选择配置预设:** 当调用 `cmake-integration-cmake-configure-with-preset` 时,您将看到一个可用配置预设的列表,包括一个“无预设”选项。如果可用,预设的 `displayName` 将显示为注释。

- **选择目标:** 当调用 `cmake-integration-save-and-compile` 时,系统将提示您选择一个目标。目标类型(可执行文件或库)以注释形式指示。

对于多配置生成器(如 Ninja Multi-Config),目标选择可能如下所示:

“all”和“clean”目标始终可用。
## 使用 TRAMP 远程执行
`cmake-integration` 包支持使用 TRAMP 进行远程连接,从而实现无缝集成。但是,您可能需要修改 `tramp-remote-path` 变量,以确保可执行文件在远程系统上可访问。
例如,当将 `cmake-integration` 与 Docker 和 TRAMP 一起使用时,如果 Conan 位于 Docker 容器内的 `/home/ubuntu/.local/bin` 目录中,请将以下行添加到您的 Emacs 配置中:
```
(add-to-list 'tramp-remote-path "/home/ubuntu/.local/bin")
```
## 持久化状态
如果您希望自动保存和恢复状态,请启用全局次要模式 `cmake-integration-automatic-persistence-mode`。该模式安装了 advice,以便选择预设、目标、运行可执行文件、调用 CTest 和类似操作都会自动保持持久化数据同步。
手动命令仍然可用于自定义工作流:
- `cmake-integration-save-state`
- `cmake-integration-restore-state`
- `cmake-integration-maybe-restore-state`
如果您偶尔想要持久化状态而不启用自动模式(例如,作为项目切换钩子的一部分),这些命令会很有用。
## 您可能感兴趣的其他包
- [CMake 预设模式](https://github.com/darcamo/cmakepresets-mode)
- 编辑 CMake 预设文件时的小可用性改进
标签:Bash脚本, CMake构建系统, CMake集成, Conan包管理, C++开发, C++开发工具, C++语言, Dape调试, Emacs插件, Emacs编辑器, GDB调试, Linux 内核安全, LSP协议, SOC Prime, 代码编辑器扩展, 包管理集成, 开发工具, 构建系统集成, 构建自动化, 编译功能, 编辑器插件, 自动化构建, 语言服务器支持, 调试功能, 运行目标, 项目管理, 预设管理