Building source code#
Now that we have added the documentation for our component, we can continue by adding some source code (as shown in the following commit).
Let’s begin with the most relevant parts of the implementation, starting with the bazel src/BUILD file.
1cc_binary(
2 name = "scrample",
3 srcs = [
4 "assert_handler.cpp",
5 "assert_handler.h",
6 "main.cpp",
7 ],
8 visibility = ["//visibility:public"],
9 deps = [
10 ":sample_sender_receiver",
11 "@score_communication//score/mw/com",
12 "@boost.program_options",
13 "@score_baselibs//score/language/futurecpp",
14 "@score_baselibs//score/mw/log",
15 ],
16)
The cc_binary produces a cpp binary, by starting C/C++ compilation with the specified toolchain. We will define the toolchain later in this chapter. Let’s first look at the content of the cc_binary.
The attributes of cc_binary are documented in detail in the bazel documentation, and it is worth reviewing them. In this example, the rule includes three source files. The scrample binary depends on:
the communication module (IPC functionality)
the baselibs module (cpp language extension and logging)
The scrample target is marked with “public” visibility so that it can be used later in the reference integration module.
1cc_library(
2 name = "sample_sender_receiver",
3 srcs = [
4 "sample_sender_receiver.cpp",
5 ],
6 hdrs = [
7 "sample_sender_receiver.h",
8 ],
9 deps = [
10 ":datatype",
11 "@score_communication//score/mw/com",
12 "@score_baselibs//score/mw/log",
13 ],
14)
15
16cc_library(
17 name = "datatype",
18 srcs = [
19 "datatype.cpp",
20 ],
21 hdrs = [
22 "datatype.h",
23 ],
24 deps = [
25 "@score_communication//score/mw/com",
26 "@score_baselibs//score/language/futurecpp",
27 ],
28)
The sender/receiver logic and the datatype handling are implemented as separate cc_library targets to improve testability.
The scrample cc_binary depends on both libraries.
Now that the targets are defined, we can build the binary.
To do so, we must first specify the toolchain that compile and link the code. Since we want to run the application in QNX qemu environment, we will build it using the qcc toolchain. Before continuing, it is helpful to introduce two fundamental bazel concepts:bazel toolchains and bazel platforms.
A complete explanation is beyond the scope of this small tutorial, but the following brief overview is sufficient:
A bazel toolchains specifies which compiler toolchain and linker are used (e.g., qcc, gcc or llvm).
A bazel platforms defines the target cpu architecture (e.g., arm or x86).
For the scrample example, we want to use the qcc toolchain for x86_64 platform. To add qcc toolchain support to our module, we extend the MODULE.bazel file with the corresponding toolchain configuration.
1# Configure the target toolchain.
2bazel_dep(name = "score_toolchains_qnx", version = "0.0.2", dev_dependency=True)
3qnx = use_extension("@score_toolchains_qnx//:extensions.bzl", "toolchains_qnx", dev_dependency=True)
4qnx.sdp(
5 sha256 = "f2e0cb21c6baddbcb65f6a70610ce498e7685de8ea2e0f1648f01b327f6bac63",
6 strip_prefix = "installation",
7 url = "https://www.qnx.com/download/download/79858/installation.tgz",
8)
9use_repo(qnx, "toolchains_qnx_sdp")
10use_repo(qnx, "toolchains_qnx_qcc")
The score_toolchains_qnx module is referenced here as a dependency. It contains qnx toolchain, including compiler, linker, image creation tools, and their configuration for the Eclipse S-CORE project.
Tip
CI/CD pipeline uses its own QNX license when building the code with qnx. If you want to build the source code with qnx compiler locally, you must acquire a QNX 8.x “free for non commercial use” license and install QNX 8.x SDP as described in the QNX & QEMU set-up tutorial.
Since our application depends on baselibs and communication module (as defined in the src/BUILD file), we also need to add the dependencies to these modules into the MODULE.bazel file as well, as shown below:
1bazel_dep(name = "score-baselibs", version = "0.1.1")
2
3bazel_dep(name = "communication", version = "0.1.1")
1bazel_dep(name = "platforms", version = "0.0.11")
2
3bazel_dep(name = "score_bazel_platforms", version = "0.0.2")
4
5# TRLC dependency for requirements traceability
6bazel_dep(name = "trlc", version = "0.0.0")
7git_override(
8 module_name = "trlc",
9 commit = "ede35c4411d41abe42b8f19e78f8989ff79ad3d8",
10 remote = "https://github.com/bmw-software-engineering/trlc.git",
11)
In addition to the previously mentioned modules, we also need to add some modules, as listed above.
The scrample application does not use these modules directly, but the modules it depends on do. For example: - platforms and score_bazel_platforms are required by the qnx_toolchain module - tlrc is required by the communication module
Bazel modules don’t inherit transitive dependencies automatically. This means that you must always list all required module dependencies explicitly in your MODULE.bazel file. So far, we have assumed that our module depends on another bazel module providing the qcc toolchain. To actually use this toolchain, we now need to specify the platform and configure the usage of qcc toolchain in .bazelrc file.
1build:_common --@score_baselibs//score/mw/log/detail/flags:KUse_Stub_Implementation_Only=False
2build:_common --@score_baselibs//score/mw/log/flags:KRemote_Logging=False
3build:_common --@score_baselibs//score/json:base_library=nlohmann
4build:_common --@score_baselibs//score/memory/shared/flags:use_typedshmd=False
5build:_common --@score_communication//score/mw/com/flags:tracing_library=stub
6build:_common --cxxopt=-Wno-error=mismatched-new-delete
7
8build:x86_64-qnx --config=_common
9build:x86_64-qnx --noexperimental_merged_skyframe_analysis_execution
10build:x86_64-qnx --incompatible_enable_cc_toolchain_resolution
11build:x86_64-qnx --incompatible_strict_action_env
12build:x86_64-qnx --platforms=@score_bazel_platforms//:x86_64-qnx
13build:x86_64-qnx --extra_toolchains=@toolchains_qnx_qcc//:qcc_x86_64
Lines 1-6 define compiler options that apply to the source code itself.
These options must be used by all toolchains, because bazel builds not only your module, but also all dependent modules within the context of your bazel module’s configuration. For this reason, you must specify the compiler settings that are required to build your dependencies as well.
In line 8, we apply the common configuration to the qnx toolchain. Afterwards, we specify additional compiler and linker flags that apply only to the qnx toolchain.
Lines 12-13 (highlighted in the code snippet) configure bazel to use the qnx toolchain referenced earlier in the MODULE.bazel file.
We also build our code for the x86_64-qnx platform. This is important, since our qnx toolchain supports multiple platforms (e.g., arm and x86_64).
Finally, we can compile the code. During the build, we must explicitly select the configuration to use, as shown in the example:
1bazel build --config=x86_64-qnx //src:scrample
After the successfull compilation the binary can be normally found in the build folder:
1Target //src:scrample up-to-date:
2 bazel-bin/src/scrample
Now it is time to run the binary in the reference QNX QEMU image.