.. _tutorial_bazel:

==========================
Experimental Bazel Support
==========================


Introduction
------------

EVerest offers support for `Bazel <https://bazel.build/>`.
With Bazel, you can efficiently build, test and deploy software projects of any
size.

For EVerest developers, Bazel offers a couple advantages:

* While developing features, that span multiple modules, Bazel can swiftly
  rebuild only the necessary parts of the project.
* If you have already setup Bazel in your project, EVerest framework can be
  integrated with it.

This tutorial will guide you through the process of setting up and using 
Bazel in your EVerest projects.

Getting Started
---------------

To install Bazel, it's recommended to use bazelisk, which is a tool that
downloads and runs the correct version of Bazel for your project.
You can install bazelisk by following the
`instructions on the official GitHub repository <https://github.com/bazelbuild/bazelisk?tab=readme-ov-file#installation>`.

.. note::
    Bazelisk provides a `bazel` command, and the rest of this tutorial will
    refer to it as `bazel`.

C/C++ Compilers:
At the moment, Bazel will use the default C/C++ compilers on your system.
If it is not the desired compiler to build EVerest, you can set the environment
variables `CC` and `CXX` to the desired compiler.

All other dependencies are fetched by Bazel as needed.

Using Bazel Commands
--------------------

Once Bazel is configured, you can use various Bazel commands to build, test
and run.

Most useful commands are:

* `bazel build //...` - Build all targets in the project.
* `bazel test //...` - Run all tests in the project.

Dependency Management
---------------------

There are a few different ways of managing dependencies in EVerest.

* Dependencies that CMake takes from the system (e.g. boost).
  These dependencies are configured in the `third_party/bazel/repos.bzl` file.
* Dependencies that are described in the `./dependencies.yaml` file. These
  dependencies are pulled automatically by Bazel with help of `edm` tool.
* Rust dependencies are managed by cargo, and are described in the `Cargo.toml`
  file. Cargo dependencies are automatically picked up by Bazel.

Defining C++ EVerest Modules
----------------------------

Let's assume, you have a module named `Example` with a single interface
implementation named `example`. This module depends on the `sigslot` library.
For a more realistic scenario, the module has two extra files `utils.cpp` and
`utils.hpp`.

.. code-block:: bash

    modules/
    └── Example
        ├── BUILD.bazel
        ├── CMakeLists.txt
        ├── Example.cpp
        ├── Example.hpp
        ├── manifest.yaml
        ├── utils.cpp
        ├── utils.hpp
        └── example/
            ├── exampleImpl.cpp
            └── exampleImpl.hpp

The `manifest.yaml` file for the module looks like this:

.. code-block:: yaml

    description: Simple example module written in C++
    provides:
    example:
        interface: example
        description: This implements an example interface that uses multiple framework features
        ...
    requires:
    kvs:
        interface: kvs
    enable_external_mqtt: true
    metadata:
    license: https://opensource.org/licenses/Apache-2.0
    authors:
        - Example Authors

To build this module with Bazel, you need to create a `BUILD.bazel` file in the
module directory, next to the `manifest.yaml` file.

In the `BUILD.bazel`, use predefined macros to define the module:

.. code-block:: python
    
    load("//modules:module.bzl", "cc_everest_module")

    cc_everest_module(
        # Name of the module, must be the same as the directory name.
        name = "Example",
        deps = [
            # List of libraries, that module depends on.
            # In CMakeLists.txt these are typically added as `target_link_libraries`.
            # The should be listed in the `third_party/bazel/repos.bzl` file.
            # Note that header-only libraries should be added here as well.
            "@sigslot//:sigslot",
        ],
        impls = [
            # List of implementations in the module.
            # This should correspond to the list of keys in the 
            # `provides` section of the manifest.yaml file.
            "example",
        ],
        # List of additional source files of the module.
        #
        # Here you only have to list the files that are not autogenerated.
        # The mandatory module files are added automatically.
        srcs = [
            "utils.cpp",
            "utils.hpp",
        ],
        # Alternatively, you can use `glob` function to list files.
        # srcs = glob(
        #     [
        #         "*.cpp",
        #         "*.hpp",
        #     ],
        # ),
    )


Defining Rust EVerest Modules
-----------------------------

To define a Rust module in EVerest, you need to create a BUILD.bazel file in
the module directory.
Generic `rust_binary` and `rust_test` are used at the moment.

.. code-block:: python

    load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
    load("@everest_core_crate_index//:defs.bzl", "all_crate_deps")
    load("@rules_rust//cargo:defs.bzl", "cargo_build_script")

    # cargo_build_script describes to Bazel how to run autogeneration of the code.
    # This should be pretty-much the same for every module
    cargo_build_script(
        name = "build_script",
        srcs = ["build.rs"],
        edition="2021",
        build_script_env = {
            # This is the path relative to the module directory.
            "EVEREST_CORE_ROOT": "../..",
        },
        data = [
            "manifest.yaml",
            "@everest-core//interfaces",
            "@everest-core//types",
        ],
        deps = all_crate_deps(build = True),
    )

    # The module is described as a rust_binary at the moment.
    rust_binary(
        # Name of the module, must be the same as the directory name.
        name = "RsIskraMeterBinary",
        # List of source files of the module.
        # In most cases this glob should be enough.
        srcs = glob(["src/*.rs"]),
        # Rust language edition, used in this module.
        edition="2021",
        # Bazel makes distinctions between dependencies needed on different
        # stages of the build. This is the list of proc_macro dependencies.
        # In most cases this is enough 
        proc_macro_deps = all_crate_deps(proc_macro = True),
        visibility = ["//visibility:public"],
        # List of "normal" dependencies.
        # all_crate_deps will add all the dependencies from the Cargo.toml file.
        # We need as well to add framework, bridge, and the result of the build_script.
        # Typically this is enough.
        deps = all_crate_deps(normal = True) + [
            ":build_script",
            "//lib/everest/framework/everestrs/everestrs:everestrs_sys",
            "//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
        ],
    )

----

**Authors**: Evgeny Petrov
