diff --git a/.github/actions/check-coverage/action.yml b/.github/actions/check-coverage/action.yml
new file mode 100644
index 000000000..6089b2112
--- /dev/null
+++ b/.github/actions/check-coverage/action.yml
@@ -0,0 +1,57 @@
+name: Check Coverage Results
+description: 'Extracts a summary of the code coverage results'
+
+inputs:
+ binary-dir:
+ description: 'Directory containing binary files'
+ required: true
+ source-dir:
+ description: 'Directory containing source code files'
+ default: ./source
+
+outputs:
+ ncov_lines:
+ description: 'Actual number of uncovered lines'
+ value: ${{ steps.stats.outputs.ncov_lines }}
+ ncov_functions:
+ description: 'Actual number of uncovered functions'
+ value: ${{ steps.stats.outputs.ncov_functions }}
+ ncov_branches:
+ description: 'Actual number of uncovered branches'
+ value: ${{ steps.stats.outputs.ncov_branches }}
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Capture Results
+ shell: bash
+ run: lcov
+ --capture --rc lcov_branch_coverage=1
+ --include '${{ github.workspace }}/*'
+ --directory '${{ inputs.binary-dir }}'
+ --output-file '${{ inputs.binary-dir }}/coverage.info' |
+ tee '${{ inputs.binary-dir }}/lcov_out.txt'
+
+ - name: Generate HTML
+ shell: bash
+ run: genhtml
+ '${{ inputs.binary-dir }}/coverage.info'
+ --branch-coverage
+ --output-directory '${{ inputs.binary-dir }}/lcov-html' |
+ tee '${{ inputs.binary-dir }}/genhtml_out.txt'
+
+ - name: Extract Overall Summary
+ shell: bash
+ run: xsltproc --html
+ '${{ inputs.source-dir }}/.github/actions/check-coverage/lcov-output.xslt'
+ '${{ inputs.binary-dir }}/lcov-html/index.html' |
+ tee '${{ inputs.binary-dir }}/lcov-summary.xml'
+
+ - name: Extract Stats
+ id: stats
+ shell: bash
+ run: grep -A 3 "Overall coverage rate" '${{ inputs.binary-dir }}/genhtml_out.txt' |
+ grep -oP '\([0-9]+ of [0-9]+.*\)' |
+ tr -d '()' |
+ awk '{print "ncov_" $4 "=" $3 - $1}' |
+ tee -a $GITHUB_OUTPUT
diff --git a/.github/actions/check-coverage/lcov-output.xslt b/.github/actions/check-coverage/lcov-output.xslt
new file mode 100644
index 000000000..77e2ea4dd
--- /dev/null
+++ b/.github/actions/check-coverage/lcov-output.xslt
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ X
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LCOV Report
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/workflows/local_unit_test.yml b/.github/workflows/local_unit_test.yml
deleted file mode 100644
index a14a4f1c2..000000000
--- a/.github/workflows/local_unit_test.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: "Local Unit Test"
-
-on:
- push:
- pull_request:
-
-jobs:
- #Checks for duplicate actions. Skips push actions if there is a matching or duplicate pull-request action.
- check-for-duplicates:
- runs-on: ubuntu-latest
- # Map a step output to a job output
- outputs:
- should_skip: ${{ steps.skip_check.outputs.should_skip }}
- steps:
- - id: skip_check
- uses: fkirc/skip-duplicate-actions@master
- with:
- concurrent_skipping: 'same_content'
- skip_after_successful_duplicate: 'true'
- do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]'
-
- Local-Unit-Test:
- needs: check-for-duplicates
- if: ${{ needs.check-for-duplicates.outputs.should_skip != 'true' }}
- runs-on: ubuntu-20.04
- timeout-minutes: 15
-
- steps:
- - name: Install coverage tools
- run: sudo apt-get install lcov -y
-
- - name: Checkout submodule
- uses: actions/checkout@v3
-
- - name: Set up for build
- run: |
- cp Makefile.sample Makefile
- make ENABLE_UNIT_TESTS=true PERMISSIVE_MODE=true prep
-
- - name: Build the code
- run: make -j
-
- # Baseline lcov and run all tests
- - name: Test
- run: make test
-
- - name: Calculate coverage
- run: make lcov | tee lcov_out.txt
-
- - name: Confirm 100% line coverage
- run: |
- if [[ `grep -A 3 "Overall coverage rate" lcov_out.txt | grep lines` != *"100.0%"* ]]; then
- grep -A 3 "Overall coverage rate" lcov_out.txt
- echo "Lacks 100.0% line unit test coverage"
- exit -1
- fi
-
- - name: Confirm absolute line coverage
- run: |
- # Current best possible branch coverage is all but 4, with associated issues for each missing case
- missed_branches=4
- coverage_nums=$(grep -A 3 "Overall coverage rate" lcov_out.txt | grep branches | grep -oP "[0-9]+[0-9]*")
-
- diff=$(echo $coverage_nums | awk '{ print $4 - $3 }')
- if [ $diff -gt $missed_branches ]
- then
- grep -A 3 "Overall coverage rate" lcov_out.txt
- echo "More than $missed_branches branches missed"
- exit -1
- fi
diff --git a/.github/workflows/standalone-build.yml b/.github/workflows/standalone-build.yml
new file mode 100644
index 000000000..c66564a8b
--- /dev/null
+++ b/.github/workflows/standalone-build.yml
@@ -0,0 +1,120 @@
+name: Build and Test Standalone OSAL package
+
+on:
+ workflow_dispatch:
+ pull_request:
+
+defaults:
+ run:
+ shell: bash
+
+env:
+ allowed_ncov_lines: 0
+ allowed_ncov_branches: 4
+ allowed_ncov_functions: 0
+
+jobs:
+
+ build-and-test:
+ name: Build and Execute Tests
+
+ strategy:
+ fail-fast: false
+ matrix:
+ build-type: [Debug, Release]
+ base-os: [ubuntu-22.04, ubuntu-20.04]
+
+ runs-on: ${{ matrix.base-os }}
+
+ steps:
+
+ - name: Checkout OSAL
+ uses: actions/checkout@v3
+ with:
+ path: source
+
+ - name: Install Coverage Analysis Tools
+ if: ${{ matrix.build-type == 'Debug' && matrix.base-os == 'ubuntu-20.04' }}
+ run: sudo apt-get install -y lcov xsltproc && echo "run_lcov=TRUE" >> $GITHUB_ENV
+
+ - name: Set up debug environment
+ if: ${{ matrix.build-type == 'Debug' }}
+ run: |
+ echo "is_debug=TRUE" >> $GITHUB_ENV
+ echo "is_release=FALSE" >> $GITHUB_ENV
+ echo "build_tgt=all" >> $GITHUB_ENV
+ echo "DESTDIR=${{ github.workspace }}/staging-debug" >> $GITHUB_ENV
+
+ - name: Set up release environment
+ if: ${{ matrix.build-type == 'Release' }}
+ run: |
+ echo "is_debug=FALSE" >> $GITHUB_ENV
+ echo "is_release=TRUE" >> $GITHUB_ENV
+ echo "build_tgt=install" >> $GITHUB_ENV
+ echo "DESTDIR=${{ github.workspace }}/staging-release" >> $GITHUB_ENV
+
+ - name: Set up build
+ run: cmake
+ -DCMAKE_BUILD_TYPE=${{ matrix.build-type }}
+ -DENABLE_UNIT_TESTS=${{ env.is_debug }}
+ -DOSAL_OMIT_DEPRECATED=${{ env.is_debug }}
+ -DOSAL_VALIDATE_API=${{ env.is_release }}
+ -DOSAL_INSTALL_LIBRARIES=${{ env.is_release }}
+ -DOSAL_CONFIG_DEBUG_PERMISSIVE_MODE=${{ env.is_debug }}
+ -DOSAL_SYSTEM_BSPTYPE=generic-linux
+ -DCMAKE_PREFIX_PATH=/usr/lib/cmake
+ -DCMAKE_INSTALL_PREFIX=/usr
+ -S source
+ -B build
+
+ - name: Build OSAL
+ working-directory: build
+ run: make ${{ env.build_tgt }} -j2
+
+ - name: Validate API
+ if: ${{ matrix.build-type == 'Release' }}
+ working-directory: build
+ run: make osal_apicheck
+
+ - name: Execute Tests
+ if: ${{ matrix.build-type == 'Debug' }}
+ working-directory: build
+ run: ctest --output-on-failure -j4 2>&1 | tee ../ctest.log
+
+ - name: Check Coverage
+ id: stats
+ if: ${{ env.run_lcov == 'TRUE' }}
+ uses: ./source/.github/actions/check-coverage
+ with:
+ binary-dir: build
+
+ - name: Enforce coverage function minimum
+ if: ${{ always() && steps.stats.outputs.ncov_functions > env.allowed_ncov_functions }}
+ run: |
+ echo "::error::Too many uncovered functions (${{ steps.stats.outputs.ncov_functions }})"
+ /bin/false
+
+ - name: Enforce coverage line minimum
+ if: ${{ always() && steps.stats.outputs.ncov_lines > env.allowed_ncov_lines }}
+ run: |
+ echo "::error::Too many uncovered lines (${{ steps.stats.outputs.ncov_lines }})"
+ /bin/false
+
+ - name: Enforce coverage branch minimum
+ if: ${{ always() && steps.stats.outputs.ncov_branches > env.allowed_ncov_branches }}
+ run: |
+ echo "::error::Too many uncovered branches (${{ steps.stats.outputs.ncov_branches }})"
+ /bin/false
+
+ - name: Assemble Results
+ if: ${{ always() }}
+ run: |
+ if [ -s ctest.log ]; then
+ echo '
CTest Execution
' >> $GITHUB_STEP_SUMMARY
+ echo '' >> $GITHUB_STEP_SUMMARY
+ cat ctest.log >> $GITHUB_STEP_SUMMARY
+ echo '
' >> $GITHUB_STEP_SUMMARY
+ fi
+ if [ -s 'build/lcov-summary.xml' ]; then
+ cat 'build/lcov-summary.xml' >> $GITHUB_STEP_SUMMARY
+ fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47a8c10cc..8a75b1c07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## Development Build: v6.0.0-rc4+dev205
+- fixup API headers for C++
+- workflow to validate OSAL API
+- See and
+
## Development Build: v6.0.0-rc4+dev199
- Modify unreachable branch in OS_ObjectIdAllocateNew
- See
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 38a39952f..f65266481 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -97,7 +97,15 @@ if (CMAKE_VERSION VERSION_GREATER 3.13)
cmake_policy(SET CMP0079 NEW)
endif()
-project(OSAL C)
+option(OSAL_VALIDATE_API "Validate the OSAL API headers as part of the build" OFF)
+
+# If validation is selected, this requires a C++ compiler
+set(OSAL_LANG C)
+if (OSAL_VALIDATE_API)
+ list(APPEND OSAL_LANG CXX)
+endif()
+
+project(OSAL ${OSAL_LANG})
# define a custom property to track relationship between BSP and OS
# this should be set on BSP "impl" targets to indicate the correct OS impl to go with it
@@ -478,3 +486,42 @@ if (OSAL_INSTALL_LIBRARIES)
)
endif()
+
+if (OSAL_VALIDATE_API)
+
+ # Validate the API header files individually
+ file(GLOB OSAL_API_HEADERS ${OSAL_SOURCE_DIR}/src/os/inc/*.h)
+ set(OSAL_APICHECK_SOURCES)
+ set(OSAL_APICHECK_DIR "${CMAKE_CURRENT_BINARY_DIR}/apicheck")
+
+ foreach(HDR_FILE ${OSAL_API_HEADERS})
+ get_filename_component(HDR "${HDR_FILE}" NAME)
+ string(MAKE_C_IDENTIFIER "${HDR}" HDR_ID)
+ configure_file(${OSAL_SOURCE_DIR}/check_header.c.in ${OSAL_APICHECK_DIR}/check_${HDR_ID}.c)
+ list(APPEND OSAL_APICHECK_SOURCES_C ${OSAL_APICHECK_DIR}/check_${HDR_ID}.c)
+ configure_file(${OSAL_SOURCE_DIR}/check_header.cpp.in ${OSAL_APICHECK_DIR}/check_${HDR_ID}.cpp)
+ list(APPEND OSAL_APICHECK_SOURCES_CXX ${OSAL_APICHECK_DIR}/check_${HDR_ID}.cpp)
+ endforeach(HDR_FILE ${OSAL_API_HEADERS})
+ add_library(osal_apicheck_C STATIC EXCLUDE_FROM_ALL ${OSAL_APICHECK_SOURCES_C})
+ add_library(osal_apicheck_CXX STATIC EXCLUDE_FROM_ALL ${OSAL_APICHECK_SOURCES_CXX})
+ if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ target_compile_options(osal_apicheck_C PUBLIC -std=c99 -pedantic -Wall -Werror)
+ endif()
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ target_compile_options(osal_apicheck_CXX PUBLIC -std=c++03 -pedantic -Wall -Werror)
+ endif()
+
+ # This causes the check to compile with the same set of defines and include dirs as specified
+ # in the "INTERFACE" properties of the actual module
+ target_link_libraries(osal_apicheck_C PUBLIC
+ osal_public_api
+ )
+ target_link_libraries(osal_apicheck_CXX PUBLIC
+ osal_public_api
+ )
+
+ add_custom_target(osal_apicheck)
+ add_dependencies(osal_apicheck osal_apicheck_C)
+ add_dependencies(osal_apicheck osal_apicheck_CXX)
+
+endif()
diff --git a/check_header.c.in b/check_header.c.in
new file mode 100644
index 000000000..e452d823f
--- /dev/null
+++ b/check_header.c.in
@@ -0,0 +1,4 @@
+#include "@HDR@"
+
+/* A no-op function so this compilation unit is not empty */
+void CheckHeader_@HDR_ID@(void) {}
diff --git a/check_header.cpp.in b/check_header.cpp.in
new file mode 100644
index 000000000..4be090c57
--- /dev/null
+++ b/check_header.cpp.in
@@ -0,0 +1,7 @@
+extern "C"
+{
+#include "@HDR@"
+}
+
+/* An empty class */
+class CheckHeader_@HDR_ID@ {};
diff --git a/src/os/inc/osapi-clock.h b/src/os/inc/osapi-clock.h
index d41291daf..50cc82c6d 100644
--- a/src/os/inc/osapi-clock.h
+++ b/src/os/inc/osapi-clock.h
@@ -147,7 +147,8 @@ static inline int64 OS_TimeGetTotalSeconds(OS_time_t tm)
*/
static inline OS_time_t OS_TimeFromTotalSeconds(int64 tm)
{
- return (OS_time_t) {.ticks = (tm * OS_TIME_TICKS_PER_SECOND)};
+ OS_time_t ostm = {tm * OS_TIME_TICKS_PER_SECOND};
+ return ostm;
}
/*-------------------------------------------------------------------------------------*/
@@ -180,7 +181,8 @@ static inline int64 OS_TimeGetTotalMilliseconds(OS_time_t tm)
*/
static inline OS_time_t OS_TimeFromTotalMilliseconds(int64 tm)
{
- return (OS_time_t) {.ticks = (tm * OS_TIME_TICKS_PER_MSEC)};
+ OS_time_t ostm = {tm * OS_TIME_TICKS_PER_MSEC};
+ return ostm;
}
/*-------------------------------------------------------------------------------------*/
@@ -213,7 +215,8 @@ static inline int64 OS_TimeGetTotalMicroseconds(OS_time_t tm)
*/
static inline OS_time_t OS_TimeFromTotalMicroseconds(int64 tm)
{
- return (OS_time_t) {.ticks = (tm * OS_TIME_TICKS_PER_USEC)};
+ OS_time_t ostm = {tm * OS_TIME_TICKS_PER_USEC};
+ return ostm;
}
/*-------------------------------------------------------------------------------------*/
@@ -250,7 +253,8 @@ static inline int64 OS_TimeGetTotalNanoseconds(OS_time_t tm)
*/
static inline OS_time_t OS_TimeFromTotalNanoseconds(int64 tm)
{
- return (OS_time_t) {.ticks = (tm / OS_TIME_TICK_RESOLUTION_NS)};
+ OS_time_t ostm = {tm / OS_TIME_TICK_RESOLUTION_NS};
+ return ostm;
}
/*-------------------------------------------------------------------------------------*/
@@ -462,7 +466,8 @@ static inline OS_time_t OS_TimeAssembleFromSubseconds(int64 seconds, uint32 subs
*/
static inline OS_time_t OS_TimeAdd(OS_time_t time1, OS_time_t time2)
{
- return ((OS_time_t) {time1.ticks + time2.ticks});
+ OS_time_t ostm = {time1.ticks + time2.ticks};
+ return ostm;
}
/*-------------------------------------------------------------------------------------*/
@@ -476,7 +481,8 @@ static inline OS_time_t OS_TimeAdd(OS_time_t time1, OS_time_t time2)
*/
static inline OS_time_t OS_TimeSubtract(OS_time_t time1, OS_time_t time2)
{
- return ((OS_time_t) {time1.ticks - time2.ticks});
+ OS_time_t ostm = {time1.ticks - time2.ticks};
+ return ostm;
}
/**@}*/
diff --git a/src/os/inc/osapi-file.h b/src/os/inc/osapi-file.h
index 512157fb5..45ed3f719 100644
--- a/src/os/inc/osapi-file.h
+++ b/src/os/inc/osapi-file.h
@@ -106,7 +106,7 @@ typedef enum
{
OS_FILE_FLAG_NONE = 0x00,
OS_FILE_FLAG_CREATE = 0x01,
- OS_FILE_FLAG_TRUNCATE = 0x02,
+ OS_FILE_FLAG_TRUNCATE = 0x02
} OS_file_flag_t;
/*
diff --git a/src/os/inc/osapi-idmap.h b/src/os/inc/osapi-idmap.h
index bffa74567..3f1fb8e14 100644
--- a/src/os/inc/osapi-idmap.h
+++ b/src/os/inc/osapi-idmap.h
@@ -102,10 +102,12 @@ static inline unsigned long OS_ObjectIdToInteger(osal_id_t object_id)
static inline osal_id_t OS_ObjectIdFromInteger(unsigned long value)
{
#ifdef OSAL_OMIT_DEPRECATED
- return (osal_id_t) {value};
+ osal_id_t idv = {(uint32)value};
#else
- return (osal_id_t)value;
+ osal_id_t idv = (osal_id_t)value;
#endif
+
+ return idv;
}
/*-------------------------------------------------------------------------------------*/
diff --git a/src/os/inc/osapi-macros.h b/src/os/inc/osapi-macros.h
index 8db135955..ab1354a8a 100644
--- a/src/os/inc/osapi-macros.h
+++ b/src/os/inc/osapi-macros.h
@@ -32,6 +32,15 @@
#include "osconfig.h"
#include "common_types.h"
+/*
+ * C++ does not support variadic macros until C++11
+ * These macros should only be used from C code, not headers
+ * or inline functions. This ifdef prevents the C++ compiler
+ * from throwing an error about these definitions - as a result
+ * these macros are NOT available in C++ source files.
+ */
+#ifndef __cplusplus
+
#ifdef OSAL_CONFIG_BUGCHECK_DISABLE
/**
@@ -145,4 +154,6 @@
*/
#define BUGCHECK_VOID(cond) BUGCHECK(cond, )
+#endif /* __cplusplus */
+
#endif /* OSAPI_MACROS_H */
diff --git a/src/os/inc/osapi-select.h b/src/os/inc/osapi-select.h
index 304dbc623..70dff8282 100644
--- a/src/os/inc/osapi-select.h
+++ b/src/os/inc/osapi-select.h
@@ -57,7 +57,7 @@ typedef enum
OS_STREAM_STATE_BOUND = 0x01, /**< @brief whether the stream is bound */
OS_STREAM_STATE_CONNECTED = 0x02, /**< @brief whether the stream is connected */
OS_STREAM_STATE_READABLE = 0x04, /**< @brief whether the stream is readable */
- OS_STREAM_STATE_WRITABLE = 0x08, /**< @brief whether the stream is writable */
+ OS_STREAM_STATE_WRITABLE = 0x08 /**< @brief whether the stream is writable */
} OS_StreamState_t;
/** @defgroup OSAPISelect OSAL Select APIs
diff --git a/src/os/inc/osapi-version.h b/src/os/inc/osapi-version.h
index 50b2ea02f..f72ed4b73 100644
--- a/src/os/inc/osapi-version.h
+++ b/src/os/inc/osapi-version.h
@@ -34,7 +34,7 @@
/*
* Development Build Macro Definitions
*/
-#define OS_BUILD_NUMBER 199
+#define OS_BUILD_NUMBER 205
#define OS_BUILD_BASELINE "v6.0.0-rc4"
/*