> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 构建 C 示例程序

C 和 C++ 语言没有标准的包管理器，因此使用第三方库要求程序员自行设计依赖管理方案。

在执行指南的第一步之前，我们将讨论用户将 SDK 库依赖集成到项目中的可选方案。然后我们会选择其中一种方案并使用该方法构建示例程序。最后将简要展示第二种方法。

<Info>
  如果您不熟悉 CMake，可能需要先阅读 CMake 的["Using Dependencies Guide"](https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html)
</Info>

### SDK 源代码在哪里？

执行事件 C SDK 位于与执行层守护进程相同的源代码仓库中（[此处](https://github.com/category-labs/monad)），在子目录 [`category/event`](https://github.com/category-labs/monad/tree/main/category/event) 下。它有一个单独的 `CMakeLists.txt` 文件，可作为顶层项目文件，因此用户不需要构建完整的执行项目即可编译它。

SDK 的构建系统会生成一个名为 `libmonad_event.a` 的库，如果您更喜欢共享库则生成 `libmonad_event.so`。您还需要公共头文件。

### 我的代码如何使用 `libmonad_event.a`？

以下是三种不同的选择：

1. **预编译库** - 您可以自己构建库并将库文件（及其头文件）存储在某处，然后手动将其导入您的构建系统。如果您也使用 CMake，SDK 构建系统还会创建一个 CMake "config" 文件供 [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) 使用，以帮助导入

其他两个选项假设您的项目也使用 CMake：

2. **CMake 子项目集成** - 您的 CMake 项目可以将 SDK 作为子项目包含。这种情况下，您在自己的项目中下载执行仓库的源代码，然后调用 CMake 函数：
   ```text theme={null}
   add_subdirectory(<path-to-monad-repo>/category/event)
   ```
   这将把 SDK 的库目标（名为 `monad_event`）添加到您的父 CMake 项目中。将 SDK 代码添加到您的构建中的一种方式是使用 [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules)。另一种方式是使用 CMake 的 [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html) 模块。这些方法之间的三个主要区别是：
   1. 默认情况下，`FetchContent` 会在构建配置时将 git 仓库克隆到您的 CMake 构建树中，而 git submodule 在仓库级别集成到您的源代码树中
   2. 使用 `FetchContent` 时，您检出的版本由您在 `CMakeLists.txt` 文件中指定的 `GIT_TAG` 决定；对于 `git submodule`，则通过 git 命令管理
   3. 如果您获取的内容有自己的 CMake 构建系统（如 C SDK），`FetchContent` 会自动调用 `add_subdirectory` 将其添加到当前项目；在 git submodule 方式中，您需要手动执行此操作
3. **CMake `ExternalProject` 集成** - CMake 的 [`ExternalProject`](https://cmake.org/cmake/help/latest/module/ExternalProject.html) 模块类似于 `FetchContent`，但更加隔离；它会将 SDK 构建和安装到 CMake 构建树中的"暂存"目录。这使用完全独立的 CMake 调用，因此不会将 SDK 的 CMake 项目添加到您自己的项目中。这意味着，例如，您的 CMake 项目中不会自动拥有 `monad_event` 库目标 —— 您需要将其创建为导入目标。`ExternalProject` 有助于将您的构建系统与 SDK 的构建系统隔离，确保 SDK 的 CMake 配置和变量不会"泄漏"到父项目中

在本指南中，我们将使用 `FetchContent` 方法。它将整个过程封装为一个简单的、一站式的 `CMakeLists.txt` 文件，并且我们在该文件中添加了注释以解释您需要了解的一切。

对于我们的小型"开始使用"示例程序来说，这显然是最佳选择，但对于您的实际项目可能并不是最佳。本指南末尾简要展示了使用 `find_package` 的替代方法。

## 使用 `FetchContent` 构建示例程序

### 步骤 1：安装前置开发包

除 CMake（至少 3.23 版本）和 git 外，我们还需要较新的 C 编译器和两个第三方库。我们还将使用 [curl](https://curl.se/docs/manpage.html) 下载一些文件。

#### 必需的 C 编译器

C SDK 使用了 C23 的一些较新特性，需要 gcc-13 或 clang-19。如果 CMake 找到的默认编译器过旧，您需要通过设置 `CC` 环境变量或使用 CMake [工具链文件](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)指定备用 C 编译器。

CMake 选择的默认编译器通常是 `cc -v` 命令报告的那个。如果您需要使用不同的编译器，可以使用 bash 语法 `VAR=VALUE <command>` 在下一个命令的作用域中设置环境变量，例如：

```shell theme={null}
$ CC=gcc-15 cmake <args>
```

#### 必需的 C++ 编译器

本示例中未使用 C++，但 CMake 项目中有一些可选的 C++ 组件。因此，您必须安装 C++ 编译器，否则 CMake 配置步骤将失败。

<Info title="SDK 中的 C++">
  SDK 是用纯 C 编写的，但包含一些 C++ 头文件，可用于使用 `<format>` 库对事件类型进行"美化打印"。这些不是示例程序的一部分，它们需要完整的 C++23 范围格式化支持（`__cpp_lib_format_ranges` 特性测试宏），这一支持在 gcc 15.2 版本的 libstdc++ 中添加。这是一个较新的版本：gcc 15.2 首次出现在 Ubuntu 软件包仓库中是在 Ubuntu 25.10。

  您也可以使用 clang 配合 libc++（LLVM 的 C++ 标准库实现），从版本 19 开始它就有范围格式化支持。在"开始使用"指南的可选最后步骤中，展示了使用 clang-19 构建 C++ 程序的示例，用于构建 `monad-event-cli` 实用工具。
</Info>

#### 必需的第三方库

| 要求           | Ubuntu 包名        | 用途                                                       |
| ------------ | ---------------- | -------------------------------------------------------- |
| zstd 库       | libzstd-dev      | 快照 event ring 文件使用 zstd 压缩；需要 `libzstd` 来解压              |
| libhugetlbfs | libhugetlbfs-dev | `libhugetlbfs` 用于定位创建 event ring 共享内存文件的最佳 hugetlbfs 挂载点 |

`libzstd` 是硬性依赖；`libhugetlbfs` 在 Linux 上默认预期存在但可选。可通过将 CMake 选项 `MONAD_EVENT_USE_LIBHUGETLBFS` 设置为 `OFF` 手动关闭。

#### macOS 兼容性

实时数据需要 Linux 主机（因为执行层守护进程本身需要），但您可以在 macOS 上编译并运行处理历史数据的示例程序。这允许您在花费精力在 Linux 上搭建 Monad 节点之前"先试后买"并探索 SDK。

在这种情况下，您不需要 `libhugetlbfs`（这是仅 Linux 的库），但您需要较新的 XCode 工具链、`libzstd` 压缩库和 CMake。后两者不包含在默认的 XCode 开发工具中，因此您可能希望使用 [Homebrew](https://brew.sh/) 或 [MacPorts](https://www.macports.org/) 将其安装到系统上。至于 XCode 本身，自 16.3 版本以来的任何版本都应该可以工作，但仅使用 23.2 版本进行过测试（实际要求是 Apple Clang 17）。[^1]

[^1]: "Apple Clang"基于但不同于原始 LLVM [Clang](https://clang.llvm.org/)。Apple Clang 17 基于 LLVM/Clang 19，因此需求表中存在差异。

### 步骤 2：下载示例程序

首先，创建一个新目录并将示例程序源文件下载到其中。我们将使用示例目录 `~/src/event-sdk-example-c`

```shell theme={null}
$ mkdir -p ~/src/event-sdk-example-c
$ cd ~/src/event-sdk-example-c
$ curl -O https://raw.githubusercontent.com/category-labs/monad/refs/tags/release/exec-events-sdk-v1.1/category/event/example/eventwatch.c
```

现在您的新目录中应有一个名为 `eventwatch.c` 的文件。

### 步骤 3：添加 `CMakeLists.txt` 构建文件

在 `eventwatch.c` 旁边的目录中创建 `CMakeLists.txt` 文件，并将以下内容复制到其中：

```CMakeLists.txt theme={null}
cmake_minimum_required(VERSION 3.23)

project(eventwatch LANGUAGES C)

#
# SDK setup
#

include(FetchContent)

FetchContent_Declare(exec_events_c_sdk
    # The execution events C SDK is kept in the same git repository as the
    # execution daemon itself
    GIT_REPOSITORY https://github.com/category-labs/monad.git

    # The latest version of the SDK is available on a special release branch
    # of the execution repository
    GIT_TAG release/exec-events-sdk-v1.1

    # This will only download the SDK branch
    GIT_SHALLOW TRUE

    # This will disable the checkout of all git submodules; they are needed
    # for the full execution daemon to build, but not the SDK
    GIT_SUBMODULES ""

    # The top-level CMakeLists.txt builds the entire execution daemon; we don't
    # want that, so we specify SOURCE_SUBDIR to choose a CMakeLists.txt file
    # in a sudirectory to treat as the "top-level" file for the external
    # project; this only creates the monad_event library
    SOURCE_SUBDIR category/event)

# The SDK's build system also builds the same example we're building now, using
# the same target name ('eventwatch'). This is done as a CI check to ensure
# that the upstream project doesn't break the example program. We have to
# disable it, because it will conflict with the eventwatch target we're going
# to add below (CMake does not allow two targets with the same name)
set(MONAD_EVENT_BUILD_EXAMPLE OFF CACHE INTERNAL "")

# This will download the source code and call add_subdirectory, which will add
# the `monad_event` library target; this is the SDK target we need to link
FetchContent_MakeAvailable(exec_events_c_sdk)

#
# eventwatch example program target
#

add_executable(eventwatch eventwatch.c)
target_compile_options(eventwatch PRIVATE -Wall -Wextra -Wconversion -Werror)
target_link_libraries(eventwatch PRIVATE monad_event)
```

### 步骤 4：运行 CMake 并构建

#### 使用 `make` 和默认编译器运行 CMake：

```shell theme={null}
$ cmake -S ~/src/event-sdk-example-c -B ~/src/event-sdk-example-c/build
$ cd ~/src/event-sdk-example-c/build
$ make
```

#### 使用 `ninja` 和替代编译器运行 CMake：

以下是另一种可能的调用方式，它使用 `CC` 环境变量设置替代 C 编译器并使用 [Ninja](https://ninja-build.org/) 构建工具：

```shell theme={null}
$ CC=clang-19 cmake -S ~/src/event-sdk-example-c -B ~/src/event-sdk-example-c/build -G Ninja
$ cd ~/src/event-sdk-example-c/build
$ ninja
```

编译应产生一个名为 `eventwatch` 的可执行文件。尝试使用 `-h` 标志运行它以打印帮助。

```shell theme={null}
$ ./eventwatch -h
usage: eventwatch [-h] [<exec-event-ring>]

execution event observer example program

Options:
  -h | --help   print this message

Positional arguments:
  <exec-event-ring>   path of execution event ring shared memory file
                        [default: monad-exec-events]
```

如果一切成功，请继续[指南的下一步](/zh/execution-events/getting-started/snapshot)；或者如果您也对 Rust 感兴趣，请构建 [Rust 示例程序](/zh/execution-events/getting-started/rust)。得益于 Rust 的 [`#[derive(Debug)]`](https://doc.rust-lang.org/rust-by-example/hello/print/print_debug.html) 属性，Rust 示例程序打印的输出比 C 版本更有趣，因此 Rust 中的"开始使用"体验更好。您可以通过使用前述的 `std::formatter` 特化在 C++ 中做等价的事，但它们不在教程中。

您也可以继续本页的下一节，其中展示了与 C 库集成的另一种方式。

## 替代方法：本地安装，使用 `find_package` 查找

现在您已经看过了"一站式"教程，它解释了源代码组织、SDK 的 `CMakeLists.txt` 文件所在位置等，我们可以更简洁地展示另一种构建系统。本节中我们将：

* 将 SDK 安装到临时目录 `/tmp/sdk-install-demo`，它将具有传统的 `include` 和 `lib` 目录结构，还有一个包含 CMake `find_package` 配置文件的 `lib/cmake/category-labs` 目录
* 再次编译 `eventwatch.c`，这次使用 `find_package`，它将被指示在 `/tmp/sdk-install-demo` 中查找

### 步骤 1：构建并安装 `libmonad_event.a`

```shell theme={null}
$ git clone -b release/exec-events-sdk-v1.1 https://github.com/category-labs/monad.git \
  ~/src/monad-exec-events-sdk
$ cmake -S ~/src/monad-exec-events-sdk/category/event \
  -B ~/build/monad-exec-events-sdk-v1-release \
  -DCMAKE_INSTALL_PREFIX=/tmp/sdk-install-demo -DCMAKE_BUILD_TYPE=RelWithDebInfo
$ cmake --build ~/build/monad-exec-events-sdk-v1-release
$ cmake --install ~/build/monad-exec-events-sdk-v1-release
```

如果一切成功，您应该有一个已填充的 `/tmp/sdk-install-demo` 目录。

### 步骤 2：创建新目录并下载 `eventwatch.c`

```shell theme={null}
$ mkdir -p ~/src/event-sdk-example-c-find-package
$ cd ~/src/event-sdk-example-c-find-package
$ curl -O https://raw.githubusercontent.com/category-labs/monad/refs/tags/release/exec-events-sdk-v1.1/category/event/example/eventwatch.c
```

### 步骤 3：创建 `CMakeLists.txt`

添加一个带有以下内容的 `CMakeLists.txt` 文件：

```CMakeLists.txt theme={null}
cmake_minimum_required(VERSION 3.23)

project(eventwatch LANGUAGES C)

find_package(monad_exec_events_sdk REQUIRED
             PATHS /tmp/sdk-install-demo/lib/cmake/category-labs)

add_executable(eventwatch eventwatch.c)
target_compile_options(eventwatch PRIVATE -Wall -Wextra -Wconversion -Werror)
target_link_libraries(eventwatch PRIVATE monad_event)
```

### 步骤 4：构建并运行

```shell theme={null}
$ cmake -S . -B build
$ cmake --build build
$ build/eventwatch -h
```
