Skip to main content

Building the C example program

The C and C++ languages do not have a standard package manager, so using a third-party library requires the programmer to come up with their own dependency management scheme.

Before we perform the first step of the guide, we'll discuss the options the user has for integrating the SDK library dependency into their project. Then we'll pick one of these options and build the example program using that method. A second method will be briefly shown at the end.

info

If you are not familiar with CMake, you may want to read CMake's "Using Dependencies Guide" first

Where is the SDK source code?

The execution event C SDK lives in the same source code repository as the execution daemon (here), in the subdirectory category/event. It has a separate CMakeLists.txt file that can act as a top-level project file, so that users do not need to build the full execution project in order to compile it.

The SDK's build system produces a library called libmonad_event.a, or libmonad_event.so if you prefer shared libraries. You will also need the public header files.

Here are three different options:

  1. Precompiled library - you could build the library yourself and store the library file (and its headers) somewhere, then import it into your build system manually. The SDK build system also creates a CMake "config" file for use with find_package to help import it, if you are also using CMake

The other two options assume your project is also using CMake:

  1. CMake subproject integration - your CMake project can include the SDK as a subproject. In this case, you download the source code of the execution repository as part of your own project, and call the CMake function:

    add_subdirectory(<path-to-monad-repo>/category/event)

    This will add the SDK's library target (called monad_event) into your parent project. One way to add the SDK code to your project is to use a git submodule. Another way is to use CMake's FetchContent module. The three main differences between these approaches are:

    1. By default, FetchContent will clone a git repository into your CMake build tree at build configuration time, whereas a git submodule integrates into your source tree at the repository level
    2. With FetchContent, the version you check out is specified by the GIT_TAG you specify in a CMakeLists.txt file; for git submodule, it is managed via git commands
    3. If the content you are fetching has its own CMake buildsystem (as the C SDK does), FetchContent will automatically call add_subdirectory to add it to the current project; in the git submodule approach, you need to do this yourself
  2. CMake ExternalProject integration - CMake's ExternalProject module is similar to FetchContent, but is more isolated; this will build and install the SDK into a "staging" directory somewhere in your CMake build tree. This uses a completely separate CMake invocation, so it will not add the SDK's CMake project into your own. This means, for example, that you will not automatically have a monad_event library target in your own CMake project -- you would need to create one as an imported target. ExternalProject helps isolate your build system from the SDK's build system, ensuring that CMake configuration and variables from the SDK can't "leak" into your parent project

In this guide, we'll use the FetchContent approach. This encapsulates the entire process as a simple, all-in-one CMakeLists.txt file, and we comment that file to explain everything you need to know.

This is the clear best choice for our tiny "Getting started" example program, but it might not be best for your real project. At the end of this guide, an alternative method using find_package is briefly shown.

Building the example program with FetchContent

Step 1: download the example program

First, create a new directory and download the example program source file into it. We'll use the example directory ~/src/event-sdk-example-c

$ mkdir ~/src/event-sdk-example-c
$ cd ~/src/event-sdk-example-c
$ curl -O https://raw.githubusercontent.com/category-labs/monad/refs/heads/release/exec-events-sdk-v1.x/category/event/example/eventwatch.c

You should now have a file called eventwatch.c in your new directory.

Step 2: add a CMakeLists.txt build file

Create a CMakeLists.txt file in the directory alongside eventwatch.c and copy these contents into it:

cmake_minimum_required(VERSION 3.21)
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 git@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.x
# 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)

Step 3: run CMake with a recent enough C compiler

note

The C SDK uses some recent features from C23, and requires either gcc-13 or clang-19. If the default compiler found by CMake is too old, you will need to specify an alternative C compiler by setting the CC environment variable or using a CMake toolchain file. The default compiler chosen by CMake is usually the one reported by the output of the command cc -v.

CMake with make and the default compiler:

$ cmake -S ~/src/event-sdk-example-c -B ~/src/event-sdk-example-c/build
$ cd ~/src/event-sdk-example-c/build
$ make

CMake with ninja and an alternate compiler:

Here is another possible invocation, which sets an alternative C compiler using the CC environment variable and uses the Ninja build tool:

$ CC=gcc-15 cmake -S ~/src/event-sdk-example-c -B ~/src/event-sdk-example-c/build -G Ninja
$ cd ~/src/event-sdk-example-c/build
$ ninja

The compilation should produce an executable file called eventwatch. Try running it with the -h flag to print the help.

$ ./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]

If all was successful, continue on to the last step in the guide or if you are also interested in Rust, build the Rust example program. You can also continue on to the next section, which shows a different way of integrating with the library.

Alternative: install locally, find with find_package

Now that you have seen the "all-in-one" tutorial, which explains the source code organization, where the SDK's CMakeLists.txt file is, etc., it is easy to show an alternative kind of build system without as much commentary. In this section we will:

  • Install the SDK to the temporary directory /tmp/sdk-install-demo, which will have the traditional include and lib directory structure, but also a lib/cmake/category-labs directory containing the config files for CMake's find_package

  • Compile eventwatch.c again, this time using find_package which will be instructed to look in /tmp/sdk-install-demo

Step 1: build and install libmonad_event.a

$ git clone -b release/exec-events-sdk-v1.x 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 -G Ninja \
-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

If all is successful, you should have a populated /tmp/sdk-install-demo directory.

Step 2: create a new directory and download eventwatch.c

$ mkdir ~/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/heads/release/exec-events-sdk-v1.x/category/event/example/eventwatch.c

Step 3: create CMakeLists.txt

Add a CMakeLists.txt file with this content:

cmake_minimum_required(VERSION 3.21)
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)

Step 4: build and run

$ cmake -S . -B build -G Ninja
$ cmake --build build
$ build/eventwatch