bazelembedded/bazel-embedded

Understanding the "Getting started" example code: register_execution_platforms() registers target platforms?

Closed this issue · 5 comments

Hi,
the following is not an issue or a bug, but a request for better understanding - hope "Issues" is the right place, I didn't find a mailing list of alike for bazel-embedded.

I was trying to walk down the logic that starts at

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

Following this call, it ends up at register_execution_platforms() which essentially passes platform() rules which define all sort of CMx CPUs.
As per Bazel's definition of an execution platform ("execution platform runs the build actions", see https://bazel.build/docs/platforms)
there seems to be a mismatch, at least in my understanding: A CMx is not an execution platform, it's a target platform.

Furthermore, if I look at an actual platform() rule, I find sth like this

platform(
     name = "cortex_m0",
     exec_properties = {
         "EXECUTOR": "cortex_m",
     },
     parents = [":cortex_m"],
)

exec_properties is a "map of strings that affect the way actions are executed remotely. Bazel makes no attempt to interpret this."
This kind of leads me to the assumption that "EXECUTOR" is some sort of tag or attribute which is used during the build actions
and which does "something" there. I haven't really found out what this "something" actually is or whether this assumption is true.

So, all in all:

  • I could use some help in understanding the execution versus target platform "mismatch" as explained above.
  • I would like to better understand what this "EXECUTOR" string is actually doing further down the line.

Hope this is the right place to ask these questions ... :-)
Thx
Arndt

there seems to be a mismatch, at least in my understanding: A CMx is not an execution platform, it's a target platform.

Yeah, this could do with some extra documentation on my end. I'll start by saying if you are using Bazel by itself, this attribute does absolutely nothing.

The EXECUTOR property was left over from when I was using on-device remote execution. So basically if you use remote execution Bazel allows you to configure your own target and execution platforms.

So this setup looked something like this;

flowchart LR

A[Bazel Client1] -->S[Remote execution server]
B[Bazel Client2] -->S
CN[Bazel ClientN] -->S

S-->WA[Worker 1, EXECUTOR=cortex_m0]
WA-->DA[Real device 1]

S-->WB[Worker 2, EXECUTOR=cortex_m0]
WB-->DB[Real device 2]

S-->WN[Worker N, EXECUTOR=cortex_m4]
WN-->DN[Real device N]

S-->WH[Worker for host tests EXECUTOR not defined]
Loading

This essentially means that you could have a pool of workers where multiple people could develop and run tests/apps directly on your specific device.

The solution is a little heavyweight but it allowed us to, share a limited number of prototypes of different revisions etc. between the CI server (which was just a regular bazel client) and each of us developers. Further, it means that on-device testing is cached, so if that test has already been run by the CI server it'll just return the cached result when you run it.

When each worker receives a job it executes the embedded binary using an execution wrapper. This could be like a shell script that uploads to the device via openocd and then pipes the serial output to stdout so you can see the test output.

The exec_property 'EXECUTOR' is not in itself special. And the dictionary is just transparently passed through to the remote execution server. Configuring your remote execution server is implementation-specific.

The remote execution framework I was using was bazel/buildfarm, which allows you to pass particular jobs to workers that advertise that they can handle these properties/values. This means that based on the platforms you can configure it to only tries to execute cortex_m0 binaries on available cortex_m0 executors etc.

In the case of using buildfarm. These exec_properties map to the config files here. So if you were to uncomment the 'platform' and change the properties key/value to match 'EXECUTOR = cortex_m4', you can configure your worker to match the platforms defined here. Or alternatively if you define your own platforms you could even do something like setting your own properties e.g. DEVICE='stm32f4-disco' if you have a binary specifically for that device.

The nice thing as well is that these properties are cumulative. So you can have an executor match depending on the board, as well as if that particular board supports hardware encryption. But then if you are running a test that doesn't need encryption it'll happily execute on either board.

I would be happy to provide some guidance if that sounds like something that you would use. But I will say that unless you are working in a relatively large team maintaining a system as described above is probably not worth your time. It's also really hard to create a default/open-source version of this setup as almost everyone would need a system that is specifically tailored to your team/product line.

Also re: mailing lists, I'm unlikely to start a list as I prefer live chat. Instead, I've just enabled GitHub discussions for this repository. But feel free to clarify anything here regarding exec_properties, and in future, we can use the live chat feature for Q/A.

Hi, Nathaniel
sorry for not coming back to this earlier, other priorities kicked in.
Thanks a lot for spending the time to write all this down and to make it clearer, it helped quite a bit.
I guess I need to digest this a little more, then I'll come back to the live chat thing you enabled.
Thx
Arndt

No worries :) I'll close this for now then. But feel free to ask further questions under the discussions tab.