diff --git a/.github/workflows/build_and_test_qnx.yml b/.github/workflows/build_and_test_qnx.yml index 15908abfa..d570185dd 100644 --- a/.github/workflows/build_and_test_qnx.yml +++ b/.github/workflows/build_and_test_qnx.yml @@ -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 diff --git a/MODULE.bazel b/MODULE.bazel index 8bc74450f..3f7f5635b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -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 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") @@ -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( @@ -89,7 +93,7 @@ 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, @@ -97,7 +101,7 @@ score_gcc.toolchain( ) 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, @@ -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") @@ -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", diff --git a/quality/integration_testing/environments/qnx8_qemu/init_x86_64.build b/quality/integration_testing/environments/qnx8_qemu/init_x86_64.build index 360eb095c..5517f17c1 100644 --- a/quality/integration_testing/environments/qnx8_qemu/init_x86_64.build +++ b/quality/integration_testing/environments/qnx8_qemu/init_x86_64.build @@ -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 ### diff --git a/score/mw/com/test/api/BUILD b/score/mw/com/test/api/BUILD index 5fed65605..bae72fdb4 100644 --- a/score/mw/com/test/api/BUILD +++ b/score/mw/com/test/api/BUILD @@ -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"], deps = [ ":api", "@score_baselibs//score/mw/log", diff --git a/score/mw/com/test/basic_rust_api/consumer_async_apis/BUILD b/score/mw/com/test/basic_rust_api/consumer_async_apis/BUILD index 5ffb69fd0..fd68f3601 100644 --- a/score/mw/com/test/basic_rust_api/consumer_async_apis/BUILD +++ b/score/mw/com/test/basic_rust_api/consumer_async_apis/BUILD @@ -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__", ], diff --git a/score/mw/com/test/basic_rust_api/consumer_sync_apis/BUILD b/score/mw/com/test/basic_rust_api/consumer_sync_apis/BUILD index 5ba488718..cc76dc3b7 100644 --- a/score/mw/com/test/basic_rust_api/consumer_sync_apis/BUILD +++ b/score/mw/com/test/basic_rust_api/consumer_sync_apis/BUILD @@ -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__", ], diff --git a/score/mw/com/test/basic_rust_api/producer_app/BUILD b/score/mw/com/test/basic_rust_api/producer_app/BUILD index 0a5c714ae..2eb7e5eaf 100644 --- a/score/mw/com/test/basic_rust_api/producer_app/BUILD +++ b/score/mw/com/test/basic_rust_api/producer_app/BUILD @@ -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__", ], diff --git a/score/mw/com/test/common_test_resources/BUILD b/score/mw/com/test/common_test_resources/BUILD index 1e2d0d6eb..360a5f6ce 100644 --- a/score/mw/com/test/common_test_resources/BUILD +++ b/score/mw/com/test/common_test_resources/BUILD @@ -168,6 +168,7 @@ cc_gtest_unit_test( srcs = [ "command_line_parser_test.cpp", ], + target_compatible_with = ["@platforms//os:linux"], deps = [ ":command_line_parser", ], diff --git a/score/mw/com/test/partial_restart/proxy_restart_shall_not_affect_other_proxies/consumer.cpp b/score/mw/com/test/partial_restart/proxy_restart_shall_not_affect_other_proxies/consumer.cpp index 10abeab00..7dd32e149 100644 --- a/score/mw/com/test/partial_restart/proxy_restart_shall_not_affect_other_proxies/consumer.cpp +++ b/score/mw/com/test/partial_restart/proxy_restart_shall_not_affect_other_proxies/consumer.cpp @@ -20,6 +20,10 @@ #include +#if defined(__QNXNTO__) +#include +#endif + namespace score::mw::com::test { @@ -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) { @@ -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"; diff --git a/third_party/score_baselibs/BUILD b/third_party/score_baselibs/BUILD new file mode 100644 index 000000000..0197d75b3 --- /dev/null +++ b/third_party/score_baselibs/BUILD @@ -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"]) diff --git a/third_party/score_baselibs/restore_qnx8_poll_workaround.patch b/third_party/score_baselibs/restore_qnx8_poll_workaround.patch new file mode 100644 index 000000000..ed2db78b2 --- /dev/null +++ b/third_party/score_baselibs/restore_qnx8_poll_workaround.patch @@ -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(expected_length.value())); ++ ++ return buffer.first(static_cast(read_result.value())); + } + + void AbortableBlockingReader::SignalStop() noexcept +@@ -249,37 +250,57 @@ + const NonBlockingFileDescriptor& file_descriptor) noexcept + { + std::array 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(fds[0].revents) & static_cast(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(fds[0].revents) & static_cast(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(fds[1].revents) & ++ (static_cast(POLLIN) | static_cast(POLLRDNORM))) != 0U) ++ { ++ return {}; ++ } + +- return {}; ++ // No event yet — timeout or spurious wake. Loop again (QNX8 workaround). ++ } + } + + score::cpp::expected, Error>