edm

edm stands for EVerest dependency manager. It helps you orchestrating the dependencies between the different EVerest repositories.

Note

The EDM tool was developed at a time when the EVerest source code was organized in many different repositories. Since 2026, EVerest has essentially changed to a (quasi-)mono-repository layout. Manual installation of EDM is usually not necessary to work with recent versions of EVerest.

Dependency Manager for EVerest

Install and Quick Start

To install the edm dependency manager for EVerest you have to perform the following steps.

Please make sure you are running a sufficiently recent version of Python3 (>=3.6) and that you are able to install Python packages from source. See the python3 command below for upgrading the required packages. Refer to the Python Installing Packages documentation for indepth guidance if any problems arise. You may want to create and activate a virtual environment using venv

python3 -m venv venv
source venv/bin/activate

before executing the commands below.

python3 -m pip install --upgrade pip setuptools wheel jstyleson jsonschema

Python packages needed to run edm

The following Python3 packages are needed to run edm. If you install edm using this guide they will be installed automatically.

  • Python >= 3.6

  • Jinja2 >= 3.0

  • PyYAML >= 5.4

Installing edm

Now you can clone this repository and install edm:

git clone https://github.com/EVerest/EVerest.git
cd EVerest/applications/dependency_manager
python3 -m pip install . --break-system-packages

or in short

python3 -m pip install git+https://github.com/EVerest/EVerest.git@main#subdirectory=applications/dependency_manager --break-system-packages

Note

Alternatively, you can also install edm in a python virtual environment. Make sure edm is available in your PATH after the installation. You can verify this by running edm --version.

Next you could run

edm init --workspace ~/checkout/everest-workspace

This creates a workspace in the ~/checkout/everest-workspace directory from the most recent release of EVerest. If you want the most recent main you can use:

edm init main --workspace ~/checkout/everest-workspace

The workspace will have the following structure containing all current dependencies for EVerest:

everest-workspace/
├── everest-cmake
├── EVerest
├── everest-dev-environment
├── everest-framework
├── everest-sqlite
├── everest-utils
├── Josev
├── libcbv2g
├── libevse-security
├── libfsm
├── libiso15118
├── liblog
├── libnfc-nci
├── libocpp
├── libslac
├── libtimer
└── workspace-config.yaml

The workspace-config.yaml contains a copy of the config that was used to create this workspace.

Enabling CPM_SOURCE_CACHE and setting PATH

The EVerest dependency manager uses CPM for its CMake integration. This means you can and should set the CPM_SOURCE_CACHE environment variable. This makes sure that dependencies that you do not manage in the workspace are not re-downloaded multiple times. For detailed information and other useful environment variables please refer to the CPM Documentation.

Also set the PATH variable:

export CPM_SOURCE_CACHE=$HOME/.cache/CPM
export PATH=$PATH:/home/$(whoami)/.local/bin

Building EVerest

Make sure you have installed ev-cli first. You can now use the following commands to build the repository EVerest:

cd ~/checkout/everest-workspace/EVerest
mkdir build
cd build
cmake ..
make -j$(nproc) install

Setting up and updating a workspace

For letting edm do the work of setting up an initial EVerest workspace, do this:

edm init --workspace ~/checkout/everest-workspace

If you are currently in the everest-workspace directory the following command has the same effect:

edm init

For using a dedicated release version, you can do this:

edm init 2023.7.0

In this example, version 2023.7.0 is pulled from the server. This will only work if your previous code is not in a “dirty” state.

Using the edm CMake module and dependencies.yaml

To use edm from CMake you have to add the following line to the top-level CMakeLists.txt file in the respective source repository:

find_package(EDM REQUIRED)

To define dependencies you can now add a dependencies.yaml file to your source repository. It should look like this:

---
sigslot:
  git: https://github.com/palacaze/sigslot
  git_tag: v1.2.3
  cmake_condition: "EVEREST_DEPENDENCY_ENABLED_SIGSLOT"
  options:
    - "SIGSLOT_COMPILE_EXAMPLES OFF"
    - "SIGSLOT_COMPILE_TESTS OFF"
pugixml:
  git: https://github.com/zeux/pugixml
  git_tag: v1.15
  cmake_condition: "EVEREST_DEPENDENCY_ENABLED_PUGIXML"

If you want to conditionally include some dependencies, e.g. for testing, you can do this in the following way:

catch2:
  git: https://github.com/catchorg/Catch2.git
  git_tag: v3.4.0
  cmake_condition: "BUILD_TESTING"

Here cmake_condition can be any string that CMake can use in an if() block. Please be aware that any variables you use here must be defined before a call to evc_setup_edm() is made in your CMakeLists.txt

Additionally you can set the EVEREST_MODIFY_DEPENDENCIES environment variable to a file containing modifications to the projects dependencies.yaml files when running cmake:

EVEREST_MODIFY_DEPENDENCIES=../dependencies_modified.yaml cmake -S . -B build

The dependencies_modified.yaml file can contain something along these lines:

nlohmann_json:
  git: null       # this makes edm look for nlohmann_json via find_package
libfmt:
  rename: fmt     # if find_package needs a different dependency name you can rename it
  git: null
catch2:
  git_tag: v1.2.3 # select a different git tag for a build

Selective package builds

EVerest is built and installed in five packages. EVEREST_PACKAGES selects the ones built from the tree; packages that are needed but not listed are taken from an installed prefix with find_package().

Package

Contents

base

log, timer, util, sqlite, evse_security, tls

iso15118

libiso15118 and the cbv2g EXI codec libraries

ocpp

libocpp

core

the framework libraries, code generation and the interface definitions; this is what out-of-tree module projects build against

runtime

the in-tree modules, applications and configurations (no CMake package)

The default builds all five. Only libocpp and what it needs, for example:

cmake -S . -B build -DEVEREST_PACKAGES="base;ocpp"

From an external project’s dependencies.yaml:

everest-core:
  git: https://github.com/EVerest/EVerest.git
  git_tag: 2026.10.0
  options:
    - "EVEREST_PACKAGES base;ocpp"

With DISABLE_EDM=ON the packages can also be built and installed one after the other into the same prefix:

cmake -S . -B b1 -DDISABLE_EDM=ON -DEVEREST_PACKAGES=base -DCMAKE_INSTALL_PREFIX=/opt/everest
cmake -S . -B b2 -DDISABLE_EDM=ON -DEVEREST_PACKAGES="iso15118;ocpp" \
  -DCMAKE_PREFIX_PATH=/opt/everest -DCMAKE_INSTALL_PREFIX=/opt/everest
cmake -S . -B b3 -DDISABLE_EDM=ON -DEVEREST_PACKAGES=core \
  -DCMAKE_PREFIX_PATH=/opt/everest -DCMAKE_INSTALL_PREFIX=/opt/everest
cmake -S . -B b4 -DDISABLE_EDM=ON -DEVEREST_PACKAGES=runtime \
  -DCMAKE_PREFIX_PATH=/opt/everest -DCMAKE_INSTALL_PREFIX=/opt/everest

With EDM, cmake --install installs what is needed to run EVerest (the manager, modules, libraries and configurations) but no CMake packages and no development files such as the generated headers. To build against EVerest with find_package(), install it from a build with DISABLE_EDM=ON; EDM projects add EVerest as a dependency instead.

Tests are built with BUILD_TESTING=ON for every package built from the tree. EVEREST_TEST_PACKAGES (default: all five) narrows that to selected packages, for example -DBUILD_TESTING=ON -DEVEREST_TEST_PACKAGES="core;runtime".

Framework thread pool scaling policy

The framework message handler uses a dynamically scaling thread pool for operation messages such as variable updates, commands, errors, GetConfig and ModuleReady messages. Its scaling policy can be selected at CMake configure time.

CMake options:

Option

Default

Description

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY

greedy

Selects the policy. Supported values are greedy, latency, conservative, fixed_size and custom.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_MIN_THREAD_COUNT

1

Minimum worker count of the pool. These workers are always running.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_MAX_THREAD_COUNT

6

Maximum worker count of the pool, independent of the CPU core count. The pool grows so that a handler blocked waiting for another message still lets that message be handled; such threads do not run in parallel, so the core count is not the right bound. Must not be below the minimum. It must exceed the number of handlers that may block on each other at the same time; a maximum equal to the minimum disables growth, so any such handler waits forever.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS

50

Maximum queued task wait time, in milliseconds, before the latency policy adds another worker.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_TICK_MS

5

Back-off, in milliseconds, before the latency policy’s supervisor re-evaluates a queued task it just added a worker for or decided not to add one for. It is not a periodic tick: an idle pool causes no supervisor wakeups.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD

3

Queue size threshold at which the fixed_size policy adds another worker.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_HEADER

(empty)

Header to include when the policy is custom.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_TYPE

(empty)

Fully-qualified C++ policy type to use when the policy is custom.

EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_INCLUDE_DIR

(empty)

Additional include directory for the custom policy header.

Two conditions the pool cannot resolve itself are logged by the module as warnings: a handler thread that cannot be started (the pool keeps retrying; after five seconds of failures the module exits like a crash), and a message that has waited more than a second with every handler thread busy, which means the maximum is too low for the handlers that wait for each other.

The built-in policies are:

Policy

Behavior

greedy

Default. Adds a worker whenever a submitted task would otherwise wait for a busy worker, so below the maximum no task is ever left queued behind blocked workers. No supervisor thread; the pool size follows the number of outstanding handlers and surplus workers retire after the idle timeout.

latency

Adds workers when queued work has waited longer than the framework latency threshold, checked by a supervisor thread that sleeps while the pool is idle.

conservative

Adds workers only when the queue depth significantly exceeds the current worker count.

fixed_size

Adds workers once the queue size reaches the configured EVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD.

Example: selecting the latency policy for a full EVerest build

cmake -S . -B build \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY=latency \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS=50 \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_TICK_MS=5
cmake --build build

Example: selecting fixed-size scaling

cmake -S . -B build \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY=fixed_size \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD=3
cmake --build build

Example: using a custom policy

cmake -S . -B build \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY=custom \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_HEADER=my_policy.hpp \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_TYPE=my_project::MyPolicy \
  -DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_INCLUDE_DIR=/path/to/include
cmake --build build

A custom policy must provide the same interface as the built-in policies:

struct MyPolicy {
    static constexpr std::optional<std::chrono::milliseconds> supervisor_tick =
        std::nullopt;

    static bool should_grow(
        std::size_t current_workers,
        std::size_t queue_size,
        std::optional<std::chrono::steady_clock::time_point> oldest_arrival);
};

should_grow is evaluated on a submission below the thread limit only if the submitted task may wait, that is, if the queue is now longer than the number of workers waiting at it or about to; a task that an idle worker takes at once never grows the pool, so queue_size is at least 1 whenever the policy is asked. A policy with a supervisor_tick additionally gets a supervisor thread that re-evaluates should_grow for tasks that queue behind busy workers, backing off one tick after each evaluation. It sleeps while the queue is empty and, at the thread limit, until a worker retires. If the policy also provides

static std::chrono::steady_clock::time_point next_check(
    std::chrono::steady_clock::time_point oldest_arrival);

the supervisor sleeps until that time instead of waking every tick. next_check must return a time no later than the first time should_grow can become true for a queue whose oldest task arrived at oldest_arrival (an earlier time is harmless: the supervisor then retries every tick), and it must be non-decreasing in oldest_arrival. LatencyScaling provides it.

Create a workspace config from an existing directory tree

Suppose you already have a directory tree that you want to save into a config file. You can do this with the following command:

edm --create-config custom-config.yaml

This is a short form of:

edm --create-config custom-config.yaml --include-remotes https://github.com/EVerest/*

and only includes repositories from the EVerest namespace. You can add as many remotes to this list as you want.

For example, if you only want to include certain repositories you can use the following command.

edm --create-config custom-config.yaml --include-remotes https://github.com/EVerest/everest* https://github.com/EVerest/ext-switchev-iso15118.git

If you want to include all repositories, including external dependencies, in the config you can use the following command:

edm --create-config custom-config.yaml --external-in-config

Git information at a glance

You can get a list of all git repositories in the current directory and their state using the following command:

edm --git-info --git-fetch

If you want to know the state of all repositories in a workspace you can use the following command:

edm --workspace ~/checkout/everest-workspace --git-info --git-fetch

This creates output that is similar to the following example:

[edm]: Git info for "~/checkout/everest-workspace":
[edm]: Using git-fetch to update remote information. This might take a few seconds.
[edm]: "everest-dev-environment" @ branch: main [remote: origin/main] [behind 6] [clean]
[edm]: "everest-framework" @ branch: main [remote: origin/main] [dirty]
[edm]: "everest-deploy-devkit" @ branch: main [remote: origin/main] [clean]
[edm]: "libtimer" @ branch: main [remote: origin/main] [dirty]
[edm]: 2/4 repositories are dirty.

Further information can be seen as shell output by calling edm with parameter -h or –help.


Authors: Kai-Uwe Hermann, Stefan Wahren, Andreas Heinrich, Manuel Ziegler