Skip to content
Draft
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
58 changes: 58 additions & 0 deletions .github/scripts/build-c-api-bindings.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
# Copyright 2026 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Configure, build, install and package the C API bindings.
#
# Inputs (all optional, with defaults suitable for a local run):
# ENABLE_LVQ_LEANVEC ON to statically link the LVQ/LeanVec backend
# REQUIRE_LTO_ARCHIVE ON to fail (not warn) if the compiler can't consume the
# LTO archive; set in CI, left off for local builds
# SUFFIX artifact name suffix (e.g. -public-only)
# WORKSPACE repository root; defaults to this script's repo so it
# also runs outside the container

set -e

# In the manylinux/rockylinux containers the pinned gcc-toolset lives behind an
# scl profile script; harmless no-op on a plain runner.
source /etc/bashrc 2>/dev/null || true

# Repo root, derived from this script's location so no git metadata is needed.
WORKSPACE="${WORKSPACE:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
BUILD_DIR="${WORKSPACE}/build_c_api"
INSTALL_DIR="${WORKSPACE}/install_c_api"
ENABLE_LVQ_LEANVEC="${ENABLE_LVQ_LEANVEC:-OFF}"
REQUIRE_LTO_ARCHIVE="${REQUIRE_LTO_ARCHIVE:-OFF}"

echo "compiler: $(${CXX:-c++} --version | head -1)"

rm -rf "${BUILD_DIR}" "${INSTALL_DIR}"

cmake -B"${BUILD_DIR}" -S"${WORKSPACE}/bindings/c" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \
-DCMAKE_INSTALL_LIBDIR=lib \
-DSVS_BUILD_C_API_TESTS=ON \
-DSVS_RUNTIME_ENABLE_LVQ_LEANVEC="${ENABLE_LVQ_LEANVEC}" \
-DSVS_REQUIRE_LTO_ARCHIVE="${REQUIRE_LTO_ARCHIVE}"

cmake --build "${BUILD_DIR}" -j"$(nproc)"

# Install only the C API component: the dependency headers that a full install
# would also emit are not part of the shipped interface.
cmake --install "${BUILD_DIR}" --component C_API

tar -czf "${WORKSPACE}/svs-c-api${SUFFIX}.tar.gz" -C "${INSTALL_DIR}" .
echo "Packaged ${WORKSPACE}/svs-c-api${SUFFIX}.tar.gz"
1 change: 1 addition & 0 deletions .github/scripts/build-cpp-runtime-bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ CMAKE_ARGS=(
"-DCMAKE_INSTALL_PREFIX=/workspace/install_cpp_bindings"
"-DCMAKE_INSTALL_LIBDIR=lib"
"-DSVS_RUNTIME_ENABLE_LVQ_LEANVEC=${ENABLE_LVQ_LEANVEC:-ON}"
"-DSVS_REQUIRE_LTO_ARCHIVE=${REQUIRE_LTO_ARCHIVE:-OFF}"
"-DSVS_RUNTIME_ENABLE_IVF=ON"
"-DSVS_EXPERIMENTAL_CLANG_TIDY=ON"
)
Expand Down
85 changes: 85 additions & 0 deletions .github/scripts/test-c-api-bindings.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/bash
# Copyright 2026 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Integration test for the packaged C API: verifies the tarball is a usable
# package rather than just a successful compile. Runs against the artifact only,
# with no access to the build tree.
#
# Inputs:
# SUFFIX artifact name suffix (e.g. -public-only)
# WORKSPACE repository root; defaults to this script's repo so it also runs
# outside the container

set -e

# Match build-c-api-bindings.sh: pick up the container's pinned gcc-toolset.
source /etc/bashrc 2>/dev/null || true

# Repo root, derived from this script's location so no git metadata is needed.
WORKSPACE="${WORKSPACE:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
STAGE_DIR="${WORKSPACE}/c_api_integration"

# Prefer the artifact downloaded by the workflow, else a tarball built locally.
TARBALL="${WORKSPACE}/c_api_artifact/svs-c-api${SUFFIX}.tar.gz"
if [ ! -e "${TARBALL}" ]; then
TARBALL="${WORKSPACE}/svs-c-api${SUFFIX}.tar.gz"
fi

INSTALL_DIR="${STAGE_DIR}/install"
CONSUMER_BUILD="${STAGE_DIR}/consumer-build"

rm -rf "${STAGE_DIR}"
mkdir -p "${INSTALL_DIR}"
tar -xzf "${TARBALL}" -C "${INSTALL_DIR}"

echo "::group::Package contents"
find "${INSTALL_DIR}" -type f -o -type l | sort
echo "::endgroup::"

LIBDIR="${INSTALL_DIR}/lib"
LIB="${LIBDIR}/libsvs_c_api.so"
if [ ! -e "${LIB}" ]; then
echo "ERROR: ${LIB} missing from the package"
exit 1
fi

echo "::group::Strong exported symbols"
nm -D --defined-only "${LIB}" | awk '$2=="T"{print $3}' | sort
echo "::endgroup::"

# Only the documented svs_* C ABI may be exported with strong linkage. This also
# guards the statically linked LVQ/LeanVec backend against leaking symbols.
#
# std:: template instantiations (_ZNSt/_ZSt) are excluded: GCC emits some of these
# with strong linkage from the LTO archive, and they are standard-library code
# rather than SVS implementation detail. The check still catches any leak of an
# actual svs/proprietary internal.
LEAKED=$(nm -D --defined-only "${LIB}" | awk '$2=="T"{print $3}' \
| grep -v '^svs_' | grep -vE '^_Z+(N?)St' || true)
if [ -n "${LEAKED}" ]; then
echo "ERROR: non-svs_ symbols exported from the C API:"
echo "${LEAKED}"
exit 1
fi

# Build a standalone C project against the installed CMake package, the way a
# downstream integration would. Catches exported-target defects (a missing
# find_dependency, or a C++ requirement leaking onto a C consumer) that a
# build-tree-only test cannot see.
cmake -B"${CONSUMER_BUILD}" -S"${WORKSPACE}/bindings/c/tests/consumer" \
-DCMAKE_PREFIX_PATH="${INSTALL_DIR}"
cmake --build "${CONSUMER_BUILD}"

LD_LIBRARY_PATH="${LIBDIR}" "${CONSUMER_BUILD}/c_api_consumer"
47 changes: 47 additions & 0 deletions .github/scripts/test-c-api-unit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash
# Copyright 2026 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Run the C API unit tests and samples out of an existing build tree.
#
# Inputs:
# WORKSPACE repository root; defaults to this script's repo so it also runs
# outside the container

set -e

# Match build-c-api-bindings.sh: pick up the container's pinned gcc-toolset.
source /etc/bashrc 2>/dev/null || true

# Repo root, derived from this script's location so no git metadata is needed.
WORKSPACE="${WORKSPACE:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
BUILD_DIR="${WORKSPACE}/build_c_api"

# LVQ/LeanVec need a specific ISA. The tests already accept
# SVS_ERROR_UNSUPPORTED_HW (but never SVS_ERROR_NOT_IMPLEMENTED), so this is
# reported for triage rather than used to skip anything.
echo "vendor: $(grep -m1 vendor_id /proc/cpuinfo || echo unknown)"
echo "model: $(grep -m1 'model name' /proc/cpuinfo || echo unknown)"
echo "avx512: $(grep -o 'avx512[a-z_0-9]*' /proc/cpuinfo | sort -u | tr '\n' ' ')"

ctest --test-dir "${BUILD_DIR}" --output-on-failure --no-tests=error

# The samples are the only executable check that the public headers are usable
# from C and that an end-to-end build/search runs. They regressed to a non-zero
# exit once already, so they are part of the gate.
for sample in c_api_simple c_api_save_load c_api_dynamic; do
echo "::group::${sample}"
"${BUILD_DIR}/samples/${sample}"
echo "::endgroup::"
done
131 changes: 86 additions & 45 deletions .github/workflows/build-c-api-bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ name: Build and test C API bindings
on:
push:
branches:
- main
- main
pull_request:
workflow_dispatch:

Expand All @@ -26,57 +26,98 @@ permissions:

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
group: ${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true

jobs:
build:
name: ${{ matrix.cxx }}, ${{ matrix.build_type }}
build-c-api-bindings:
name: Build and unit tests for C API (${{ matrix.name }})
runs-on: ubuntu-22.04
strategy:
matrix:
build_type: [RelWithDebInfo]
cxx: [g++-11, g++-12, clang++-15]
# Mirrors build-cpp-runtime-bindings.yml.
include:
- cxx: g++-11
cc: gcc-11
- cxx: g++-12
cc: gcc-12
- cxx: clang++-15
cc: clang-15
- name: "with static library"
enable_lvq_leanvec: "ON"
require_lto: "ON"
suffix: ""
- name: "public only"
enable_lvq_leanvec: "OFF"
require_lto: "OFF"
suffix: "-public-only"
fail-fast: false

steps:
- uses: actions/checkout@v6

- name: Install OpenMP runtime
env:
CXX: ${{ matrix.cxx }}
run: |
sudo apt-get update
# The default libgomp shipped with GCC does not match the clang
# toolchain, so install the LLVM OpenMP runtime (libomp) for clang.
if [[ "${CXX}" == clang* ]]; then
sudo apt-get install -y libomp-15-dev
fi

- name: Configure build
working-directory: ${{ runner.temp }}
env:
CXX: ${{ matrix.cxx }}
CC: ${{ matrix.cc }}
TEMP_WORKSPACE: ${{ runner.temp }}
run: |
cmake -B${TEMP_WORKSPACE}/build -S${GITHUB_WORKSPACE}/bindings/c \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DSVS_BUILD_C_API_TESTS=ON

- name: Build C API, tests and samples
working-directory: ${{ runner.temp }}/build
run: make -j$(nproc)

- name: Run C API tests
env:
CTEST_OUTPUT_ON_FAILURE: 1
working-directory: ${{ runner.temp }}/build
run: ctest -C ${{ matrix.build_type }} --output-on-failure
- uses: actions/checkout@v6

- name: Build Docker image
run: docker build -t svs-manylinux228:latest -f docker/x86_64/manylinux228/Dockerfile .

- name: Build C API bindings in Docker container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
-e ENABLE_LVQ_LEANVEC=${{ matrix.enable_lvq_leanvec }} \
-e REQUIRE_LTO_ARCHIVE=${{ matrix.require_lto }} \
-e SUFFIX=${{ matrix.suffix }} \
svs-manylinux228:latest \
/bin/bash .github/scripts/build-c-api-bindings.sh

- name: Upload C API bindings artifacts
uses: actions/upload-artifact@v7
with:
name: svs-c-api${{ matrix.suffix }}
path: svs-c-api${{ matrix.suffix }}.tar.gz
retention-days: 7

# Run unit tests that were built as part of this job
- name: Run unit tests in Docker container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
svs-manylinux228:latest \
/bin/bash /workspace/.github/scripts/test-c-api-unit.sh

# Run integration tests against the packaged artifact. Eventually this should
# run the setup and test scope of the actual downstream integrations; for now it
# just confirms the tarball is functional - it installs, exports only the svs_*
# C ABI, and can be consumed from a standalone C project.
test:
name: Integration tests for C API (${{ matrix.name }})
needs: build-c-api-bindings
runs-on: ubuntu-22.04
strategy:
matrix:
include:
- name: "with static library"
suffix: ""
- name: "public only"
suffix: "-public-only"
fail-fast: false

steps:
- uses: actions/checkout@v6

- name: Build Docker image
run: docker build -t svs-manylinux228:latest -f docker/x86_64/manylinux228/Dockerfile .

# Need to download for a new job
- name: Download C API package
uses: actions/download-artifact@v8
with:
name: svs-c-api${{ matrix.suffix }}
path: c_api_artifact

- name: List available artifacts
run: ls -la c_api_artifact/

- name: Test packaged C API in Docker container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
-e SUFFIX=${{ matrix.suffix }} \
svs-manylinux228:latest \
/bin/bash .github/scripts/test-c-api-bindings.sh
3 changes: 3 additions & 0 deletions .github/workflows/build-cpp-runtime-bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ jobs:
include:
- name: "with static library"
enable_lvq_leanvec: "ON"
require_lto: "ON"
suffix: ""
- name: "public only"
enable_lvq_leanvec: "OFF"
require_lto: "OFF"
suffix: "-public-only"
fail-fast: false

Expand All @@ -57,6 +59,7 @@ jobs:
-v ${{ github.workspace }}:/workspace \
-w /workspace \
-e ENABLE_LVQ_LEANVEC=${{ matrix.enable_lvq_leanvec }} \
-e REQUIRE_LTO_ARCHIVE=${{ matrix.require_lto }} \
-e SUFFIX=${{ matrix.suffix }} \
svs-manylinux228:latest \
/bin/bash .github/scripts/build-cpp-runtime-bindings.sh
Expand Down
Loading
Loading