Skip to content

Hands-on tutorial on building a static library (.a) with Bazel and linking it into a Zephyr based project.

License

Notifications You must be signed in to change notification settings

GatCode/bazel2zephyr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

license bazel zephyr


About

Since at this point in time, there is very little information available on how to take an already existing bazel project, cross-compile it via the ARM toolchain and then link it into a Zephyr project, this repository will give you a brief overview on how you can achieve this goal.

This approach is based on the bazel-embedded project, since this is by far the simplest way to compile the appropriate ARM binary. Please take a look at this project if you want to get further information on how you can use bazel to not only build the project itself but also to flash your ARM based device.

Structure

To be able to create an easy to follow hands-on example, the simplest way is to split the whole tutorial into three steps:

  • Step 0: Library Code
  • Step 1: Creation of the ARM binary with Bazel
  • Step 2: Integration of the ARM binary into Zephyr

Step 0: Library Code

Since every hands-on example requires code, I opted to create a very simple and understandable calculator library called CalculationMachine, which basically can only add two integer numbers and return the result as specified in the int sum(int a, int b); function.

This CalculationMachine is split into a header file and a source file as you can see below.

calculationMachine.hh
class CalculationMachine {
    public:
        int sum(int a, int b);
};
calculationMachine.cc
#include "calculationMachine.hh"

int CalculationMachine::sum(int a, int b){
   return a + b;
}

Now we need to tie everything together by creating a BUILD file, which tells Bazel what to compile. This file can be as simple as this:

cc_library(
    name = "calculationMachine",
    srcs = ["calculationMachine.cc"],
    hdrs = ["calculationMachine.hh"],
)

If we now also create an empty file called WORKSPACE, Bazel has everything which is needed to build the library and now you can test it out via the following command:

bazel build //... 

Note: You can find the whole CalculationMachine code of this step in the corresponding folder Step0.

Step 1: Creation of the ARM binary with Bazel

Since the created libcalculationMachine.a file by Bazel in the Step 0 is built with the host platform compiler/toolchain but we need to have a binary compiled on the ARM toolchain, we now have to get help from the bazel-embedded project and make some changes.

First, add the following code to the WORKSPACE file

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

git_repository(
    name = "bazel_embedded",
    commit = "d3cbe4eff9a63d3dee63067d61096d681daca33b",
    remote = "https://github.com/silvergasp/bazel-embedded.git",
    shallow_since = "1585022166 +0800",
)

load("@bazel_embedded//:bazel_embedded_deps.bzl", "bazel_embedded_deps")

bazel_embedded_deps()

load("@bazel_embedded//platforms:execution_platforms.bzl", "register_platforms")

register_platforms()

load(
    "@bazel_embedded//toolchains/compilers/gcc_arm_none_eabi:gcc_arm_none_repository.bzl",
    "gcc_arm_none_compiler",
)

gcc_arm_none_compiler()

load("@bazel_embedded//toolchains/gcc_arm_none_eabi:gcc_arm_none_toolchain.bzl", "register_gcc_arm_none_toolchain")

register_gcc_arm_none_toolchain()

load("@bazel_embedded//tools/openocd:openocd_repository.bzl", "openocd_deps")

openocd_deps()

Then, create a .bazelrc file and add the following lines:

# Enable toolchain resolution with cc
build --incompatible_enable_cc_toolchain_resolution

Now, the ARM toolchain integration should be complete and depending on the exact ARM chip (nrf5340 in my case) you are using, you can build the library via the following command:

bazel build //... --platforms=@bazel_embedded//platforms:cortex_m3     

🎉 we successfully compiled the libcalculationMachine.a on the ARM toolchain for the corresponding ARM chip!

Note: You can find the whole CalculationMachine code of this step in the corresponding folder Step1.

Step 2: Integration of the ARM binary into Zephyr

Generally speaking, to be able to integrate a static library (in this case) into Zephyr, we need to tell the CMakeLists.txt file where the .a file and the corresponding header file is located.

To make things easy, I assume you use a standard Zephyr Hello World example. There I created a folder named lib in the root of the Zephyr project, into which I copied the previously generated libcalculationMachine.a file and the calculationMachine.hh header file.

Now you should have a project structure which looks like this:

├── lib
│   ├── calculationMachine.hh
│   └── libcalculationmachine.a
│
├── src
│   └── main.cpp
│
├── CMakeLists.txt
│
└── prj.conf

To tie everything together, we now need to change the CMakeLists.txt file in the following way:

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bazel2zephyr)

FILE(GLOB app_sources src/*.cpp)
target_sources(app PRIVATE ${app_sources})

set(LIB_DIR  ${CMAKE_CURRENT_SOURCE_DIR}/lib)
include_directories(${LIB_DIR})

add_library(calculationMachine STATIC IMPORTED GLOBAL)
set_target_properties(calculationMachine PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/libcalculationMachine.a)
target_link_libraries(app PUBLIC calculationMachine)

If you want to find out why and how this is done in this way, please feel free to read PrimalCortex's great article about static libraries on a Zephyr RTOS Project.

🎉 we successfully integrated the CalculationMachine library into the Zephyr project!

The last step is now to include the header file and from now on, you are able to use the CalculationMachine library exactly the same as any other library:

#include <zephyr.h>
#include <sys/printk.h>
#include "calculationMachine.hh"

void main(void) {
	while(1) {
		CalculationMachine machine = CalculationMachine();
		printk("Sum of 5 and 3 = %d\n", machine.sum(5, 3));
		k_sleep(K_MSEC(1000));
	}
}

Note: You can find the whole CalculationMachine Zephyr code of this step in the corresponding folder Step2.

Limitations

This tutorial gives you a brief overview on the practical aspects of the Bazel cross-compillation. Please keep in mind that depending on the complexity of the existing Bazel project itself and the limited capabilities of the toolchain you are using, you might not be able to integrate the Bazel project into Zephyr without serious code adaptation or refactoring on the Bazel project side!

This Software is provided as-is!

Please feel free to adapt it to your needs and contribute to the project. I would be very grateful to include your improvements. Thanks for your support!

WARNING: Everyone is responsible for what he/she is doing! I am not responsible if you hurt yourself, torch your house or anything that kind trying to do build something new! You are doing everything at your own risk!

About

Hands-on tutorial on building a static library (.a) with Bazel and linking it into a Zephyr based project.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published