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
21 changes: 19 additions & 2 deletions .github/workflows/build_and_test_qnx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,25 @@ jobs:
SCORE_QNX_USER: ${{ secrets.SCORE_QNX_USER }}
SCORE_QNX_PASSWORD: ${{ secrets.SCORE_QNX_PASSWORD }}
run: |
bazel build --config qnx_arm64 -- //score/... -//score/mw/com/dependability/... -//score/mw/com/performance_benchmarks/... -//docs/... -//score/mw/com/design/...
bazel build --config=qnx -- //score/...
- name: Install qemu
run: |
sudo apt-get update
sudo apt-get install -y qemu-system
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Allow unprivileged user namespaces
run: |
sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
- name: Test targets
env:
SCORE_QNX_USER: ${{ secrets.SCORE_QNX_USER }}
SCORE_QNX_PASSWORD: ${{ secrets.SCORE_QNX_PASSWORD }}
run: |
bazel test --config=qnx -- //score/mw/com/test/... #todo also execute unit tests
- name: Cleanup QNX License
if: ${{ !cancelled() }}
run: sudo rm -rf ${{ env.LICENSE_DIR }}
# TODO Run tests on QNX QEMU
30 changes: 17 additions & 13 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "rules_python", version = "1.8.5")
bazel_dep(name = "rules_rust", version = "0.68.1-score")

# Patch baselibs with a QNX8 workaround for poll
# until a fix is provided by the QNX SDP
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK this fix should be available in 8.0.4, if we add this information here it will be easier to remember when to remove the patch.

bazel_dep(name = "score_baselibs", version = "0.2.7")
single_version_override(
module_name = "score_baselibs",
patch_strip = 1,
patches = ["//third_party/score_baselibs:restore_qnx8_poll_workaround.patch"],
)

bazel_dep(name = "score_baselibs_rust", version = "0.1.0")
bazel_dep(name = "score_bazel_platforms", version = "0.1.2")
bazel_dep(name = "score_crates", version = "0.0.9", repo_name = "score_communication_crate_index")
Expand Down Expand Up @@ -63,12 +72,7 @@ gcc_toolchains.toolchain(
)
use_repo(gcc_toolchains, "gcc_toolchain_x86_64")

bazel_dep(name = "score_bazel_cpp_toolchains", version = "0.3.0", dev_dependency = True)
git_override(
module_name = "score_bazel_cpp_toolchains",
commit = "79982259df6dfa9d82cc458163a6d9b08bd24471",
remote = "https://github.com/eclipse-score/bazel_cpp_toolchains.git",
)
bazel_dep(name = "score_bazel_cpp_toolchains", version = "0.5.1", dev_dependency = True)

score_gcc = use_extension("@score_bazel_cpp_toolchains//extensions:gcc.bzl", "gcc", dev_dependency = True)
score_gcc.toolchain(
Expand All @@ -89,15 +93,15 @@ score_gcc.toolchain(
)
score_gcc.toolchain(
name = "score_qcc_x86_64_toolchain",
sdp_version = "8.0.0",
sdp_version = "8.0.3",
target_cpu = "x86_64",
target_os = "qnx",
use_default_package = True,
version = "12.2.0",
)
score_gcc.toolchain(
name = "score_qcc_aarch64_toolchain",
sdp_version = "8.0.0",
sdp_version = "8.0.3",
target_cpu = "aarch64",
target_os = "qnx",
use_default_package = True,
Expand All @@ -123,9 +127,9 @@ toolchains_qnx = use_extension(
dev_dependency = True,
)
toolchains_qnx.sdp(
sha256 = "f2e0cb21c6baddbcb65f6a70610ce498e7685de8ea2e0f1648f01b327f6bac63",
strip_prefix = "installation",
url = "https://www.qnx.com/download/download/79858/installation.tgz",
sha256 = "9039fd6a4a639f06ea977afb93963a6fe8f8c46db727066709370d999c7232e0",
strip_prefix = "",
url = "https://www.qnx.com/download/download/87174/installation_qnx_803_260305.tar.xz",
)
use_repo(toolchains_qnx, "toolchains_qnx_ifs")

Expand Down Expand Up @@ -204,9 +208,9 @@ download_file(
urls = ["https://github.com/muttleyxd/clang-tools-static-binaries/releases/download/master-2da3e7b/clang-format-19_linux-amd64"],
)

bazel_dep(name = "aspect_rules_lint", version = "2.2.0", dev_dependency = True)
bazel_dep(name = "aspect_rules_lint", version = "2.3.0", dev_dependency = True)
bazel_dep(name = "google_benchmark", version = "1.9.5", dev_dependency = True)
bazel_dep(name = "buildifier_prebuilt", version = "8.2.1.2", dev_dependency = True)
bazel_dep(name = "buildifier_prebuilt", version = "8.5.1", dev_dependency = True)
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
git_override(
module_name = "hedron_compile_commands",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ sshd # SSH daemon (ITF test harness communic
sysctl # View/modify kernel parameters

/usr/lib/ssh/sftp-server=${QNX_TARGET}/${PROCESSOR}/usr/libexec/sftp-server # SFTP server (file transfer for ITF)
/usr/libexec/sshd-session=${QNX_TARGET}/${PROCESSOR}/usr/libexec/sshd-session # SSH session handler (required by OpenSSH 9.8+)

#############################################
### PCI COMPONENTS ###
Expand Down
1 change: 1 addition & 0 deletions score/mw/com/test/api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cc_gtest_unit_test(
name = "generated_api_test",
srcs = ["generated_api_test.cpp"],
features = COMPILER_WARNING_FEATURES,
target_compatible_with = ["@platforms//os:linux"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a comment with the ticket to have QNX unit tests enabled? In this way it will be harder to forget about removing this once we have the ability to run them on QNX.

deps = [
":api",
"@score_baselibs//score/mw/log",
Expand Down
11 changes: 7 additions & 4 deletions score/mw/com/test/basic_rust_api/consumer_async_apis/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ rust_binary(
# When Bazel builds this binary in exec config (py_test build with rule cfg=exec), the Rust linker
# defaults to PIE and rejects non-PIC archives. -no-pie disables PIE; -lstdc++ is needed because
# the link_std_cpp_lib CC feature is not activated in exec config.
rustc_flags = [
"-Clink-arg=-no-pie",
"-Clink-arg=-lstdc++",
],
rustc_flags = select({
"@platforms//os:linux": [
"-Clink-arg=-no-pie",
"-Clink-arg=-lstdc++",
],
"//conditions:default": [],
}),
visibility = [
"//score/mw/com/test/basic_rust_api:__subpackages__",
],
Expand Down
11 changes: 7 additions & 4 deletions score/mw/com/test/basic_rust_api/consumer_sync_apis/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ rust_binary(
# When Bazel builds this binary in exec config (py_test build with rule cfg=exec), the Rust linker
# defaults to PIE and rejects non-PIC archives. -no-pie disables PIE; -lstdc++ is needed because
# the link_std_cpp_lib CC feature is not activated in exec config.
rustc_flags = [
"-Clink-arg=-no-pie",
"-Clink-arg=-lstdc++",
],
rustc_flags = select({
"@platforms//os:linux": [
"-Clink-arg=-no-pie",
"-Clink-arg=-lstdc++",
],
"//conditions:default": [],
}),
visibility = [
"//score/mw/com/test/basic_rust_api:__subpackages__",
],
Expand Down
11 changes: 7 additions & 4 deletions score/mw/com/test/basic_rust_api/producer_app/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ rust_binary(
# When Bazel builds this binary in exec config (py_test build with rule cfg=exec), the Rust linker
# defaults to PIE and rejects non-PIC archives. -no-pie disables PIE; -lstdc++ is needed because
# the link_std_cpp_lib CC feature is not activated in exec config.
rustc_flags = [
"-Clink-arg=-no-pie",
"-Clink-arg=-lstdc++",
],
rustc_flags = select({
"@platforms//os:linux": [
"-Clink-arg=-no-pie",
"-Clink-arg=-lstdc++",
],
"//conditions:default": [],
}),
visibility = [
"//score/mw/com/test/basic_rust_api:__subpackages__",
],
Expand Down
1 change: 1 addition & 0 deletions score/mw/com/test/common_test_resources/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ cc_gtest_unit_test(
srcs = [
"command_line_parser_test.cpp",
],
target_compatible_with = ["@platforms//os:linux"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a comment with the ticket to have QNX unit tests enabled? In this way it will be harder to forget about removing this once we have the ability to run them on QNX.

deps = [
":command_line_parser",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@

#include <string>

#if defined(__QNXNTO__)
#include <sys/procmgr.h>
#endif

namespace score::mw::com::test
{

Expand Down Expand Up @@ -102,6 +106,11 @@ void PerformFirstConsumerActions(CheckPointControl& check_point_control, score::
// LoLa requires that processes shall have distinct UIDs.
// After fork. Parent and child proccess will have same UID.
// setuid is used to make child proccess UID distinct.
#if defined(__QNXNTO__)
// Grant pathspace ability for nonroot domain before setuid, so that
// resmgr_attach() (used by LoLa message passing) works after setuid.
procmgr_ability(0, PROCMGR_ADN_NONROOT | PROCMGR_AOP_ALLOW | PROCMGR_AID_PATHSPACE, PROCMGR_AID_EOL);
#endif
const auto ret_setuid = setuid(kUidFirstConsumer);
if (ret_setuid != 0)
{
Expand Down Expand Up @@ -191,10 +200,15 @@ void PerformSecondConsumerActions(CheckPointControl& check_point_control,
//***************************************************
// Step (1)- setuid
//***************************************************
const auto ret_setuid = setuid(kUidSecondConsumer);
// LoLa requires that processes shall have distinct UIDs.
// After fork. Parent and child proccess will have same UID.
// setuid is used to make child proccess UID distinct.
#if defined(__QNXNTO__)
// Grant pathspace ability for nonroot domain before setuid, so that
// resmgr_attach() (used by LoLa message passing) works after setuid.
procmgr_ability(0, PROCMGR_ADN_NONROOT | PROCMGR_AOP_ALLOW | PROCMGR_AID_PATHSPACE, PROCMGR_AID_EOL);
#endif
const auto ret_setuid = setuid(kUidSecondConsumer);
if (ret_setuid != 0)
{
std::cerr << "Second Consumer Step (1): set uid fails: " << errno << std::strerror(errno) << "\n";
Expand Down
13 changes: 13 additions & 0 deletions third_party/score_baselibs/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# 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
# *******************************************************************************
exports_files(["restore_qnx8_poll_workaround.patch"])
110 changes: 110 additions & 0 deletions third_party/score_baselibs/restore_qnx8_poll_workaround.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
diff --git a/score/os/utils/abortable_blocking_reader.cpp b/score/os/utils/abortable_blocking_reader.cpp
--- a/score/os/utils/abortable_blocking_reader.cpp
+++ b/score/os/utils/abortable_blocking_reader.cpp
@@ -208,22 +208,23 @@
return score::cpp::make_unexpected(Error::createFromErrno(EINVAL));
}

- const auto data_available = WaitForData(file_descriptor);
-
- if (!data_available.has_value())
+ // Wait until data is available (or timeout/abort). This function loops internally on QNX8.
+ const auto wait_status = WaitForData(file_descriptor);
+ if (!wait_status.has_value())
{
- return score::cpp::make_unexpected(data_available.error());
+ return score::cpp::make_unexpected(wait_status.error());
}

// Suppressed here as it is safely used:
// Provided error check and safeguard to ensure file descriptor is valid before reading.
// NOLINTNEXTLINE(score-banned-function) see comment above
- const auto expected_length = unistd_->read(file_descriptor, buffer.data(), buffer.size());
- if ((!expected_length.has_value()) || (expected_length.value() < 0))
+ const auto read_result = unistd_->read(file_descriptor, buffer.data(), buffer.size());
+ if ((!read_result.has_value()) || (read_result.value() < 0))
{
- return score::cpp::make_unexpected(expected_length.error());
+ return score::cpp::make_unexpected(read_result.error());
}
- return buffer.first(static_cast<std::size_t>(expected_length.value()));
+
+ return buffer.first(static_cast<std::size_t>(read_result.value()));
}

void AbortableBlockingReader::SignalStop() noexcept
@@ -249,37 +250,57 @@
const NonBlockingFileDescriptor& file_descriptor) noexcept
{
std::array<struct pollfd, 2> fds{};
- constexpr std::int8_t kNoTimeout{-1};
+
+#ifdef __QNXNTO__
+ // QNX8 workaround until Ticket-221150 is resolved : add finite timeout so we don't sleep forever if the
+ // resource manager fails to wake pollers; keep true blocking elsewhere.
+ constexpr std::int8_t kPollTimeoutMs = 50; // tuneable: 10–100 ms
+#else
+ constexpr std::int8_t kPollTimeoutMs = -1; // infinite blocking on other OSes
+#endif

fds[0].fd = stop_read_file_descriptor_;
// Suppress "AUTOSAR C++14 M5-0-21" rule findings. This rule declares: "Bitwise operators shall only be
// applied to operands of unsigned underlying type."
// Rationale: Macro POLLIN does not affect the sign of the result.
// coverity[autosar_cpp14_m5_0_21_violation]
- fds[0].events = POLLIN;
+ fds[0].events = POLLIN; // self-pipe for abort

fds[1].fd = file_descriptor.GetUnderlying();
+ // QNX8 often reports only POLLRDNORM for readable inotify fds.
// Rationale: see comment above
// coverity[autosar_cpp14_m5_0_21_violation]
- fds[1].events = POLLIN;
+ fds[1].events = (POLLIN | POLLRDNORM);

- // coverity[autosar_cpp14_m5_0_6_violation]
- const auto poll_result = syspoll_->poll(fds.data(), fds.size(), kNoTimeout);
+ while (true)
+ {
+ // coverity[autosar_cpp14_m5_0_6_violation]
+ const auto poll_result = syspoll_->poll(fds.data(), fds.size(), kPollTimeoutMs);

- if (!poll_result.has_value())
- {
- return score::cpp::make_unexpected(poll_result.error());
- }
+ if (!poll_result.has_value())
+ {
+ return score::cpp::make_unexpected(poll_result.error());
+ }

- // Check return event flag for stop_read_file_descriptor_
- // Rationale: see comment above
- // coverity[autosar_cpp14_m5_0_21_violation]
- if ((static_cast<std::uint32_t>(fds[0].revents) & static_cast<std::uint32_t>(POLLIN)) != 0U)
- {
- return score::cpp::make_unexpected(Error::createFromErrno(EINTR));
- }
+ // Check return event flag for stop_read_file_descriptor_
+ // Rationale: see comment above
+ // coverity[autosar_cpp14_m5_0_21_violation]
+ if ((static_cast<std::uint32_t>(fds[0].revents) & static_cast<std::uint32_t>(POLLIN)) != 0U)
+ {
+ return score::cpp::make_unexpected(Error::createFromErrno(EINTR));
+ }
+
+ // If the watched fd is readable, we're ready; otherwise it was a timeout/spurious wake.
+ // Rationale: see comment above
+ // coverity[autosar_cpp14_m5_0_21_violation]
+ if ((static_cast<std::uint32_t>(fds[1].revents) &
+ (static_cast<std::uint32_t>(POLLIN) | static_cast<std::uint32_t>(POLLRDNORM))) != 0U)
+ {
+ return {};
+ }

- return {};
+ // No event yet — timeout or spurious wake. Loop again (QNX8 workaround).
+ }
}

score::cpp::expected<std::pair<NonBlockingFileDescriptor, NonBlockingFileDescriptor>, Error>