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/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@ {};