Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# *******************************************************************************
# Copyright (c) 2026 Accenture
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

# Use vendored crate sources so the Rust build works fully offline.
# Run `cargo vendor libs/3rdparty/cargo-vendor` to regenerate the directory
# after changing dependencies or Cargo.lock.
[source.crates-io]
replace-with = "vendored"

[source.vendored]
directory = "libs/3rdparty/cargo-vendor"

# Force all cargo invocations (including those driven by corrosion/CMake)
# to build without network access.
[net]
offline = true
52 changes: 52 additions & 0 deletions .ci/cbindgen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# *******************************************************************************
# Copyright (c) 2026 Accenture
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

import subprocess
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent

# C headers generated from Rust FFI with cbindgen. cbindgen is deliberately not a
# build dependency (its dependency tree would bloat the vendored crates), so the
# headers are checked-in artifacts regenerated by this script. Add an entry here
# for every crate that ships a generated header.
CBINDGEN_HEADERS = [
{
"crate_dir": "libs/bsw/logger/rust",
"crate": "openbsw-logger",
"output": "libs/bsw/logger/rust/include/BswLogger.h",
},
]


def regenerate_headers():
for header in CBINDGEN_HEADERS:
crate_dir = REPO_ROOT / header["crate_dir"]
subprocess.run(
[
"cbindgen",
"--config", str(crate_dir / "cbindgen.toml"),
"--crate", header["crate"],
"--output", str(REPO_ROOT / header["output"]),
str(crate_dir),
],
check=True,
)
# Fail if a committed header was out of date with the current FFI.
subprocess.run(["git", "diff", "--exit-code"], check=True, cwd=REPO_ROOT)


if __name__ == "__main__":
try:
regenerate_headers()
except subprocess.CalledProcessError as e:
print(f"Command failed with exit code {e.returncode}")
sys.exit(e.returncode)
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# All third-party imports: exclude from GitHub language statistics so the
# language bar reflects OpenBSW's own code rather than its dependencies.
libs/3rdparty/** linguist-vendored

# The cargo vendor bundle is machine-generated and never hand-edited, so also
# collapse it in diffs/PRs. (The C++ libs above are patched in place, so their
# diffs stay visible.)
libs/3rdparty/cargo-vendor/** -diff
26 changes: 26 additions & 0 deletions .github/workflows/cbindgen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# *******************************************************************************
# Copyright (c) 2026 Accenture
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

name: cbindgen header check

on: [ merge_group, push, pull_request ]

jobs:
cbindgen:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Docker Environment
uses: ./.github/actions/setup-docker-env

- name: Run the cbindgen.py inside the docker container
run: DOCKER_UID=$(id -u) DOCKER_GID=$(id -g) DOCKER_HISTORY=/dev/null docker compose run --rm development python3 .ci/cbindgen.py
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ __pycache__/
build/
cmake-build*
code_coverage/
/target/
compile_commands.json
doc/api/DoxygenWarningLog.txt
doc/api/doc-coverage.info
Expand Down
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ resolver = "3"

members = [
"executables/referenceApp/rustHelloWorld",
"libs/bsw/logger/rust",
"libs/rust/panic_handler",
"libs/rust/console_out",
]
Expand All @@ -23,7 +24,18 @@ documentation = "https://eclipse-openbsw.github.io/openbsw/"

# Workspace dependencies - add new Rust crates here as they are developed
[workspace.dependencies]
# External crates
cbindgen = "0.29.4"
log = "0.4"
paste = "1"
# NOTE: cbindgen is intentionally NOT a dependency here. C headers are generated
# out-of-band via the cbindgen CLI (see .ci/cbindgen.py); keeping it out of the
# build graph keeps the vendored dependency tree (libs/3rdparty/cargo-vendor)
# tiny. The cbindgen CLI version lives in docker/development/Dockerfile.

# openbsw-internal crates
openbsw_console_out = { path = "libs/rust/console_out" }
openbsw-logger = { path = "libs/bsw/logger/rust" }
openbsw_panic_handler = { path = "libs/rust/panic_handler" }

# Similar to C++ we always build optimized (matching C++ -O2)
Expand Down
4 changes: 4 additions & 0 deletions doc/dev/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def replace_placeholders(app, doctree, docname):
gcc_arm = props["tool"].get("gcc-arm-none-eabi", "x.x")
llvm_arm = props["tool"].get("llvm-arm", "x.x")
ubuntu_version = props["tool"].get("ubuntu_version", "x.x")
rust_version = props["tool"].get("rust_version", "x.x")

for node in doctree.traverse(nodes.Text):
text = node.astext()
Expand All @@ -67,6 +68,9 @@ def replace_placeholders(app, doctree, docname):
elif "Ubuntu-x.x" in text:
new_text = new_text.replace("x.x", ubuntu_version)

if "default-toolchain" in text and "x.x" in text:
new_text = new_text.replace("x.x", rust_version)

if new_text != text:
node.parent.replace(node, nodes.Text(new_text))

Expand Down
4 changes: 2 additions & 2 deletions doc/dev/learning/setup/setup_posix_build.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ To build OpenBSW with Rust components for the POSIX platform, you need:
1. The build tools set up as described above (gcc, cmake, make)
2. The Rust compiler

Install Rust 1.90.0 to be compatible with the CI builds:
Install Rust :prop:`tool:rust_version` to be compatible with the CI builds:

.. code-block:: bash

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.90.0
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain x.x

Then build using the Rust preset:

Expand Down
4 changes: 2 additions & 2 deletions doc/dev/learning/setup/setup_s32k148_ubuntu_build.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ To build OpenBSW with Rust components for the S32K148 platform, you need:
1. The GCC toolchain set up as described in `Using the GCC toolchain`_ above (including CC and CXX environment variables)
2. The Rust compiler with the ARM cross-compilation target

Install Rust 1.90.0 to be compatible with the CI builds and add the ARM target:
Install Rust :prop:`tool:rust_version` to be compatible with the CI builds and add the ARM target:

.. code-block:: bash

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.90.0
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain x.x
rustup target add thumbv7em-none-eabihf

Then, with the GCC toolchain environment variables set, build using the Rust preset:
Expand Down
1 change: 1 addition & 0 deletions doc/dev/properties.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ tool:
s32ds_arm_version: "2.2"
gcc-arm-none-eabi: "14.3.rel1"
llvm-arm: "19.1.1"
rust_version: "1.96.0"
8 changes: 6 additions & 2 deletions docker/development/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ RUN gem install esr-rim
ENV RUSTUP_HOME=/opt/rustup
ENV CARGO_HOME=/opt/cargo
ENV PATH=/opt/cargo/bin:${PATH}
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.90.0 --profile minimal \
# /opt/cargo and /opt/rustup are populated as root during the image build. Make
# them world-accessible so the runtime UID injected via DOCKER_UID can use them.
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.96.0 --profile minimal \
&& rustup component add rustfmt \
&& rustup target add thumbv7em-none-eabihf \
&& cargo install cbindgen --version 0.27.0 --locked
&& cargo install cbindgen --version 0.29.4 --locked \
&& chmod -R a+rwX /opt/cargo /opt/rustup

RUN python3 -m venv /opt/venv
ENV PATH=/opt/venv/bin:${PATH}
Expand Down
3 changes: 2 additions & 1 deletion executables/referenceApp/application/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ endif ()
if (BUILD_RUST)
target_compile_definitions(app.referenceApp PRIVATE BUILD_RUST)
# Link against Rust static library(should be only one static library)
target_link_libraries(app.referenceApp PRIVATE rust_hello_world)
target_link_libraries(app.referenceApp PRIVATE openbsw_logger
rust_hello_world)
# Add include directory for Rust C/C++ headers
target_include_directories(
app.referenceApp
Expand Down
8 changes: 8 additions & 0 deletions executables/referenceApp/application/include/logger/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@

#pragma once

#include <logger/ComponentMapping.h>

#ifdef BUILD_RUST
#include <util/logger/Logger.h>

DECLARE_LOGGER_COMPONENT(RUST)
#endif

namespace logger
{
void init();
Expand Down
16 changes: 15 additions & 1 deletion executables/referenceApp/application/src/app/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
#include <cstdio>

#ifdef BUILD_RUST
#include "app/DemoLogger.h"

#include <BswLogger.h>
#include <rust_hello_world.h>
#endif

Expand Down Expand Up @@ -183,6 +186,15 @@ class IdleHandler : private ::async::RunnableType

::console::init();
::console::enable();

#ifdef BUILD_RUST
// Route any `log::*!` calls (e.g. from third-party Rust crates that use the
// `log` facade) to the RUST component. First-party Rust code uses the
// `bsw_*!` macros and resolves its component index directly via the C++
// `bsw_logger_component_<NAME>()` getter emitted by DEFINE_LOGGER_COMPONENT.
::logger::set_default_component(::util::logger::RUST);
::logger::install_rust_logging();
#endif
}

void start() { ::async::execute(AsyncAdapter::TASK_IDLE, *this); }
Expand Down Expand Up @@ -228,12 +240,14 @@ void run()
{
staticInit();
etl::print("hello\r\n");

idleHandler.init();
#ifdef BUILD_RUST
etl::print("Hello Rust!\r\n");
rust_hello_world();
etl::print("Rust add(3, 4) = {}\r\n", static_cast<unsigned int>(rust_add(3U, 4U)));
#endif
idleHandler.init();

AsyncAdapter::run(AsyncAdapter::StartAppFunctionType::create<&startApp>());
}

Expand Down
2 changes: 2 additions & 0 deletions executables/referenceApp/application/src/logger/logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ DEFINE_LOGGER_COMPONENT(ETHERNET);
DEFINE_LOGGER_COMPONENT(DOIP);
DEFINE_LOGGER_COMPONENT(UDS);
DEFINE_LOGGER_COMPONENT(LWIP);
DEFINE_LOGGER_COMPONENT(RUST);

#include <async/AsyncBinding.h>
#include <console/AsyncCommandWrapper.h>
Expand All @@ -65,6 +66,7 @@ LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, GLOBAL, ::util::format::Color::DEFAULT_COL
LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, LIFECYCLE, ::util::format::Color::DARK_GRAY)
LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, CONSOLE, ::util::format::Color::DEFAULT_COLOR)
LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, SAFETY, ::util::format::Color::DEFAULT_COLOR)
LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, RUST, ::util::format::Color::GREEN)
#ifdef PLATFORM_SUPPORT_CAN
LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, CAN, ::util::format::Color::LIGHT_BLUE)
#ifdef PLATFORM_SUPPORT_TRANSPORT
Expand Down
5 changes: 5 additions & 0 deletions executables/referenceApp/rustHelloWorld/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@ edition = "2024"
crate-type = ["staticlib"]

[dependencies]
# External crates
log.workspace = true

# openbsw-internal crates
openbsw_console_out.workspace = true
openbsw-logger.workspace = true
openbsw_panic_handler.workspace = true
12 changes: 11 additions & 1 deletion executables/referenceApp/rustHelloWorld/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,21 @@ extern crate openbsw_panic_handler;

use core::fmt::Write;
use openbsw_console_out::Console;
use openbsw_logger::{bsw_debug, bsw_info, declare_logger_component};

/// Prints "Hello from Rust!" to BSP stdout via putByteToStdout.
use log::info;

declare_logger_component!(DEMO);

/// Prints "Hello from Rust!" to BSP stdout via putByteToStdout. Also prints some rust log messages,
/// mapped to the DEMO logging component.
#[unsafe(no_mangle)]
pub extern "C" fn rust_hello_world() {
bsw_debug!(DEMO, "Rust FFI called 🦀");
write!(Console, "Hello from Rust!\r\n").ok();
bsw_info!(DEMO, "Rust FFI completed");
// Plain log::info! routes through the default component (e. g. RUST) configured from C++.
info!("Hello from rust crate without logger mapping");
}

/// Adds two numbers and returns the result.
Expand Down
1 change: 1 addition & 0 deletions libs/3rdparty/cargo-vendor/log/.cargo-checksum.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"fe75b2d660e4f5cecfc6e8926b75370a30752399a8d0293a90902fcd3be8caaa",".github/workflows/main.yml":"ec945c9615b558fa1125e012369675daa107997389f25ad70d923898aaf2dd0c","CHANGELOG.md":"a216e6550df9437781c01c09783c59276245b0121d3d39b874675148d412d361","Cargo.lock":"7439a99151c8c47a4df860560ff30531aff5fb80dc7583d3e25652da288b1a38","Cargo.toml":"1471858c37a27ccb84d7274816ead00082c0e4acbbb28acc547a6329e3bdaf22","Cargo.toml.orig":"f9bcbe8c7872abcca77b6a3f8b0346835c17326baa861fa8a0f47b2c9d957ef1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"486c6cf85f99a2a3ea14dbd5fc6d6075fdc047a0dbf2b93682595db0de6a115f","benches/value.rs":"b613ff353d3cf0ef8cb98e4ca461ea929b8ba553fe299f2eb2942d77a5b1b6a0","src/__private_api.rs":"c1b8541fe7b7906a654ad1c23a643c516a3b735de4fbd1d07ca7a7a5754b74ac","src/kv/error.rs":"6dae12424164c33b93915f5e70bd6d99d616c969c8bfb543806721dd9b423981","src/kv/key.rs":"9037de97791fa0fb6def6c44ada324351d10ed0f4db8015c9ac4e90504141b87","src/kv/mod.rs":"a62c4dbfbcf28fc755c3662ea4a1e79268edc15311d5ca97b7f0325cc2dfb43f","src/kv/source.rs":"0f7c3a89c8998fe3805ae35f3e87778bca30a444da9f67964e674028f7864625","src/kv/value.rs":"b6df78e50ca912f2d97fdb1155408b538669e5be237b1f9471f9443c9d01f748","src/lib.rs":"5f47680950577eb5213a99b022f7789c2cf22dc3d6ddacda94a54f8e035c6ca2","src/macros.rs":"34c367a645483e21eee4c7846d0efbf97c29a52156d56536c82cdfe1d226a54d","src/serde.rs":"ead8df70c3ca3fafeed062f7a42d9b6f87bc7826055d71d8c4b77a0a25b57634","tests/integration.rs":"0980b3bd85d36863bc9f355e80bc7cf7987d2599adbc87e8e0082861a08a1097","tests/macros.rs":"a94f3cc181c9ecb30af6b5ca8bd2b4e5accc93689c0eb19051b8479a298dc21b","triagebot.toml":"a135e10c777cd13459559bdf74fb704c1379af7c9b0f70bc49fa6f5a837daa81"},"package":"5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"}
6 changes: 6 additions & 0 deletions libs/3rdparty/cargo-vendor/log/.cargo_vcs_info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "b1e2df7bce7a1b685aa9bfd1db0a5cac1f0fc27d"
},
"path_in_vcs": ""
}
Loading
Loading