Skip to content
Open
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
11 changes: 11 additions & 0 deletions .github/workflows/build-posix-latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,14 @@ jobs:
continue-on-error: true
- name: Test ${{ matrix.os }} ${{ matrix.config }}
run: ./build-tests.sh ${{ matrix.config }}

public-headers:
name: Public header gate (GCC/Clang)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install clang
run: sudo apt-get update && sudo apt-get install -y clang
- name: Compile each public header standalone under strict flags
run: bash tests/headers/check_public_headers.sh
10 changes: 10 additions & 0 deletions .github/workflows/test-win-latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,13 @@ jobs:
- name: Test ${{ matrix.arch }} ${{ matrix.build }}
shell: cmd
run: build-tests.cmd ${{ matrix.arch }} ${{ matrix.build }}

public-headers:
name: Public header gate (MSVC)
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Compile each public header standalone under /W4 /WX
shell: cmd
run: tests\headers\check_public_headers.cmd
1 change: 1 addition & 0 deletions lib/include/public/CompliantByDefaultFilterApi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "ctmacros.hpp"

#include <cstdint>
#include <vector>

namespace MAT_NS_BEGIN { namespace Modules { namespace Filtering
Expand Down
129 changes: 129 additions & 0 deletions tests/headers/check_public_headers.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
@echo off
REM Copyright (c) Microsoft Corporation. All rights reserved.
REM SPDX-License-Identifier: Apache-2.0
REM
REM Public header gate (MSVC). Compiles each public SDK header on its own under
REM /W4 /WX, mirroring how ONNX Runtime / Foundry Local compile their own C++
REM translation units on Windows. STL/Windows SDK headers are treated as external
REM (/external:W0) so only the SDK's headers are gated. Exits non-zero if any
REM header fails to compile or emits a warning. Also compiles mat.h as C11 (/TC).
setlocal enabledelayedexpansion

set "SCRIPT_DIR=%~dp0"
set "REPO_ROOT=%SCRIPT_DIR%..\.."
set "PUB=%REPO_ROOT%\lib\include\public"
set "C_API_HEADER=mat.h"

REM Fail fast if the public header directory is missing or miscomputed, otherwise
REM the header loop below would run zero times and the gate would silently "pass"
REM without compiling anything -- a false negative.
if not exist "%PUB%" (
echo error: public header directory not found: %PUB% 1>&2
exit /b 2
)

REM Enter the MSVC x64 developer environment via vswhere (portable across runners).
set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
if not exist "%VSWHERE%" (
echo error: vswhere.exe not found 1>&2
exit /b 2
)
set "VSPATH="
for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" -latest -products * -property installationPath`) do set "VSPATH=%%i"
if not defined VSPATH (
echo error: no Visual Studio installation found 1>&2
exit /b 2
)
call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat" >nul
if errorlevel 1 (
echo error: failed to initialize the MSVC environment 1>&2
exit /b 2
)

REM Unique work directory under the repository so concurrent invocations on the
REM same machine do not clobber each other's temporary translation units.
set "WORK=%REPO_ROOT%\.public-header-gate_%RANDOM%_%RANDOM%"
if exist "%WORK%" rmdir /s /q "%WORK%"
mkdir "%WORK%"
if errorlevel 1 (
echo error: failed to create work directory %WORK% 1>&2
exit /b 2
)

REM /W4 /WX matches ORT; /external:W0 suppresses platform/STL warnings so only our headers gate.
set "CXX_COMMON=/nologo /permissive- /W4 /WX /EHsc /experimental:external /external:anglebrackets /external:W0"
set "C_COMMON=/nologo /std:c11 /TC /W4 /WX /experimental:external /external:anglebrackets /external:W0"
set "FAIL=0"
set "TOTAL=0"

REM MSVC does not expose a /std:c++11 switch; /std:c++14 is its lowest selectable mode.
call :RunCxxHeaders c++14 /std:c++14 "cl (c++14, /W4 /WX)"
call :RunCxxHeaders c++17 /std:c++17 "cl (c++17, /W4 /WX)"
call :RunCHeader

rmdir /s /q "%WORK%" 2>nul

if "%FAIL%"=="1" (
echo Public header gate FAILED.
exit /b 1
)
echo Public header gate passed. ^(!TOTAL! checks^)
exit /b 0

:RunCxxHeaders
set "STD_NAME=%~1"
set "STD_FLAG=%~2"
set "LABEL=%~3"
set "OKC=0"
set "FAILC=0"
echo == %LABEL% ==
for %%h in ("%PUB%\*.hpp" "%PUB%\*.h") do (
set "NAME=%%~nxh"
REM Skip implementation-fragment headers not meant to be included standalone
REM (VariantType.hpp is included by Variant.hpp, which defines VariantMap/VariantArray first).
if /I not "!NAME!"=="VariantType.hpp" (
> "%WORK%\tu_!STD_NAME!.cpp" echo #include "!NAME!"
>> "%WORK%\tu_!STD_NAME!.cpp" echo int main^(^){return 0;}
cl %CXX_COMMON% %STD_FLAG% /I "%PUB%" /I "%REPO_ROOT%\lib\include" /Zs "%WORK%\tu_!STD_NAME!.cpp" > "%WORK%\err.txt" 2>&1
if errorlevel 1 (
echo FAIL: !NAME!
type "%WORK%\err.txt"
set "FAIL=1"
set /a FAILC+=1
) else (
set /a OKC+=1
set /a TOTAL+=1
)
)
)
REM No headers compiled means PUB matched nothing -- treat it as a failure rather
REM than a silent pass.
if "!OKC!"=="0" if "!FAILC!"=="0" (
echo error: no public headers found under %PUB% 1>&2
set "FAIL=1"
)
echo %LABEL%: !OKC! passed, !FAILC! failed
exit /b 0

:RunCHeader
set "LABEL=cl (C11 mat.h, /TC, /W4 /WX)"
echo == !LABEL! ==
if not exist "%PUB%\%C_API_HEADER%" (
echo error: C API header not found: %PUB%\%C_API_HEADER% 1>&2
set "FAIL=1"
echo !LABEL!: 0 passed, 1 failed
exit /b 0
)
> "%WORK%\tu_mat_c11.c" echo #include "%C_API_HEADER%"
>> "%WORK%\tu_mat_c11.c" echo int main^(void^){return 0;}
cl %C_COMMON% /I "%PUB%" /I "%REPO_ROOT%\lib\include" /Zs "%WORK%\tu_mat_c11.c" > "%WORK%\err.txt" 2>&1
if errorlevel 1 (
echo FAIL: %C_API_HEADER%
type "%WORK%\err.txt"
set "FAIL=1"
echo !LABEL!: 0 passed, 1 failed
) else (
set /a TOTAL+=1
echo !LABEL!: 1 passed, 0 failed
)
exit /b 0
158 changes: 158 additions & 0 deletions tests/headers/check_public_headers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env bash
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Public header gate (GCC/Clang).
#
# Verifies that every public SDK header is self-contained (compiles on its own,
# in any include order) and warning-clean under strict, consumer-representative
# warning flags. Downstream consumers such as ONNX Runtime / Foundry Local
# compile their own translation units -- which include these headers -- with
# -Wall -Wextra -Werror (plus -Wshorten-64-to-32 on Clang). This gate compiles
# each public header on its own as both C++11 and C++17, with no -isystem
# suppression, so any header issue surfaces here instead of at integration time.
# It also compiles the ABI-stable C API header (mat.h) as C11.
#
# Exits non-zero if any header fails to compile or emits a warning.

set -uo pipefail
shopt -s nullglob

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
PUB="$REPO_ROOT/lib/include/public"

# Fail fast if the public header directory is missing or miscomputed. With
# nullglob on, a bad PUB would make the header globs below expand to nothing and
# the gate would silently "pass" without compiling anything -- a false negative.
if [ ! -d "$PUB" ]; then
echo "error: public header directory not found: $PUB" >&2
exit 2
fi

# Implementation-fragment headers: intentionally included by another public
# header (which supplies their dependencies first) and not meant to be included
# standalone. They are exercised through their public entry point instead.
EXCLUDES=(
"VariantType.hpp" # included by Variant.hpp, which defines VariantMap/VariantArray first
)

# Mirror the strict warning flags third-party consumers build with
# (-Wall -Wextra -Werror, matching this repo's pipeline and ORT). Unused
# parameters are deliberately NOT suppressed: intentional ones use the
# UNREFERENCED_PARAMETER macro, which expands to (void)(...) on GCC/Clang, so the
# headers stay warning-clean without a blanket -Wno-unused-parameter that would
# hide a real consumer break (e.g. an override that leaves a parameter unused).
C_API_HEADER="mat.h"

is_excluded() {
local n="$1" e
for e in "${EXCLUDES[@]}"; do [ "$e" = "$n" ] && return 0; done
return 1
}

fail=0
tmp=""
for _ in 1 2 3 4 5; do
candidate="$REPO_ROOT/.public-header-gate.$$.$RANDOM"
if mkdir "$candidate" 2>/dev/null; then
tmp="$candidate"
break
fi
done
if [ -z "$tmp" ]; then
echo "error: failed to create work directory under $REPO_ROOT" >&2
exit 2
fi
trap 'rm -rf "$tmp"' EXIT

run_cxx_compiler() {
local cc="$1" std="$2" label="$3"
shift 3
local n_ok=0 n_fail=0 h name base tu out
echo "== $label =="
for h in "$PUB"/*.hpp "$PUB"/*.h; do
name="$(basename "$h")"
is_excluded "$name" && continue
base="${name%.*}"
tu="$tmp/tu_${base}_${std}.cpp"
printf '#include "%s"\nint main() { return 0; }\n' "$name" > "$tu"
if out="$("$cc" "-std=$std" -Wall -Wextra -Werror "$@" -I"$PUB" -I"$REPO_ROOT/lib/include" -fsyntax-only "$tu" 2>&1)"; then
n_ok=$((n_ok + 1))
else
n_fail=$((n_fail + 1)); fail=1
echo " FAIL: $name"
echo "$out" | grep -E 'error:|warning:' | head -4 | sed 's/^/ /'
fi
done
if [ $((n_ok + n_fail)) -eq 0 ]; then
echo " ERROR: no public headers found under $PUB"
fail=1
fi
echo " $label: $n_ok passed, $n_fail failed"
}

run_c_compiler() {
local cc="$1" label="$2"
shift 2
local tu out
echo "== $label =="
if [ ! -f "$PUB/$C_API_HEADER" ]; then
echo " ERROR: C API header not found: $PUB/$C_API_HEADER"
fail=1
echo " $label: 0 passed, 1 failed"
return
fi
tu="$tmp/tu_${C_API_HEADER%.h}_c11.c"
printf '#include "%s"\nint main(void) { return 0; }\n' "$C_API_HEADER" > "$tu"
if out="$("$cc" -std=c11 -Wall -Wextra -Werror "$@" -I"$PUB" -I"$REPO_ROOT/lib/include" -fsyntax-only "$tu" 2>&1)"; then
echo " $label: 1 passed, 0 failed"
else
fail=1
echo " FAIL: $C_API_HEADER"
echo "$out" | grep -E 'error:|warning:' | head -4 | sed 's/^/ /'
echo " $label: 0 passed, 1 failed"
fi
}

cxx_ran=0
if command -v g++ >/dev/null 2>&1; then
run_cxx_compiler g++ c++11 "g++ (c++11, -Wall -Wextra -Werror)"
run_cxx_compiler g++ c++17 "g++ (c++17, -Wall -Wextra -Werror)"
cxx_ran=1
fi
if command -v clang++ >/dev/null 2>&1; then
run_cxx_compiler clang++ c++11 "clang++ (c++11, + -Wshorten-64-to-32)" -Wshorten-64-to-32
run_cxx_compiler clang++ c++17 "clang++ (c++17, + -Wshorten-64-to-32)" -Wshorten-64-to-32
cxx_ran=1
fi

if [ "$cxx_ran" -eq 0 ]; then
echo "error: neither g++ nor clang++ was found" >&2
exit 2
fi

c_ran=0
if command -v gcc >/dev/null 2>&1; then
run_c_compiler gcc "gcc (c11 mat.h, -Wall -Wextra -Werror)"
c_ran=1
fi
if command -v clang >/dev/null 2>&1; then
run_c_compiler clang "clang (c11 mat.h, + -Wshorten-64-to-32)" -Wshorten-64-to-32
c_ran=1
fi
if [ "$c_ran" -eq 0 ] && command -v cc >/dev/null 2>&1; then
run_c_compiler cc "cc (c11 mat.h, -Wall -Wextra -Werror)"
c_ran=1
fi

if [ "$c_ran" -eq 0 ]; then
echo "error: no C compiler (gcc, clang, or cc) was found" >&2
exit 2
fi

if [ "$fail" -ne 0 ]; then
echo "Public header gate FAILED."
exit 1
fi
echo "Public header gate passed."
Loading