Skip to content
Merged
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
33 changes: 32 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@ jobs:
run: |
docker run --rm liblpm-go:ci

# C# bindings test
test-csharp-bindings:
name: Test C# Bindings
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build C# container
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile.csharp
push: false
load: true
tags: liblpm-csharp:ci
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run C# tests
run: |
docker run --rm liblpm-csharp:ci

# Lua bindings test
test-lua-bindings:
name: Test Lua Bindings
Expand Down Expand Up @@ -310,7 +339,7 @@ jobs:
ci-summary:
name: CI Summary
runs-on: ubuntu-latest
needs: [build-and-test, test-cpp-bindings, test-go-bindings, test-lua-bindings, test-perl-bindings, test-php-bindings, test-python-bindings, code-quality]
needs: [build-and-test, test-cpp-bindings, test-go-bindings, test-csharp-bindings, test-lua-bindings, test-perl-bindings, test-php-bindings, test-python-bindings, code-quality]
if: always()

steps:
Expand All @@ -320,6 +349,7 @@ jobs:
echo "Build and test: ${{ needs.build-and-test.result }}"
echo "C++ bindings: ${{ needs.test-cpp-bindings.result }}"
echo "Go bindings: ${{ needs.test-go-bindings.result }}"
echo "C# bindings: ${{ needs.test-csharp-bindings.result }}"
echo "Lua bindings: ${{ needs.test-lua-bindings.result }}"
echo "Perl bindings: ${{ needs.test-perl-bindings.result }}"
echo "PHP bindings: ${{ needs.test-php-bindings.result }}"
Expand All @@ -330,6 +360,7 @@ jobs:
if [[ "${{ needs.build-and-test.result }}" == "failure" ]] || \
[[ "${{ needs.test-cpp-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-go-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-csharp-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-lua-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-perl-bindings.result }}" == "failure" ]] || \
[[ "${{ needs.test-php-bindings.result }}" == "failure" ]] || \
Expand Down
85 changes: 85 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ option(WITH_DPDK_BENCHMARK "Build DPDK comparison benchmark" OFF)
option(WITH_EXTERNAL_LPM_BENCHMARK "Build benchmarks with external LPM libraries" OFF)
option(BUILD_GO_WRAPPER "Build Go wrapper and bindings" OFF)
option(BUILD_CPP_WRAPPER "Build C++ wrapper and bindings" OFF)
option(BUILD_CSHARP_WRAPPER "Build C# wrapper and bindings" OFF)
option(BUILD_LUA_WRAPPER "Build Lua wrapper and bindings" OFF)
option(BUILD_PERL_WRAPPER "Build Perl wrapper and bindings" OFF)
option(BUILD_PHP_WRAPPER "Build PHP wrapper and bindings" OFF)
Expand Down Expand Up @@ -421,6 +422,85 @@ if(BUILD_GO_WRAPPER)
endif()
endif()

# C# wrapper
if(BUILD_CSHARP_WRAPPER)
find_program(DOTNET_EXECUTABLE dotnet)
if(DOTNET_EXECUTABLE)
# Get .NET version
execute_process(
COMMAND ${DOTNET_EXECUTABLE} --version
OUTPUT_VARIABLE DOTNET_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "Found .NET SDK: ${DOTNET_VERSION}")

# Custom target to build C# wrapper
add_custom_target(csharp_wrapper ALL
COMMAND ${CMAKE_COMMAND} -E echo "Building C# wrapper..."
COMMAND ${DOTNET_EXECUTABLE} build --configuration Release
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/csharp
DEPENDS lpm
COMMENT "Building C# wrapper and bindings"
)

# Copy native library to runtimes directory after building
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CSHARP_NATIVE_LIB_DIR "runtimes/linux-x64/native")
set(CSHARP_NATIVE_LIB_NAME "liblpm.so")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(CSHARP_NATIVE_LIB_DIR "runtimes/win-x64/native")
set(CSHARP_NATIVE_LIB_NAME "lpm.dll")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(CSHARP_NATIVE_LIB_DIR "runtimes/osx-x64/native")
set(CSHARP_NATIVE_LIB_NAME "liblpm.dylib")
endif()

add_custom_command(TARGET csharp_wrapper POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_CURRENT_SOURCE_DIR}/bindings/csharp/${CSHARP_NATIVE_LIB_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:lpm>
${CMAKE_CURRENT_SOURCE_DIR}/bindings/csharp/${CSHARP_NATIVE_LIB_DIR}/${CSHARP_NATIVE_LIB_NAME}
COMMENT "Copying native library to C# runtimes directory"
)

# Custom target to test C# wrapper
add_custom_target(csharp_test
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}"
${DOTNET_EXECUTABLE} test --configuration Release --no-build
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/csharp
DEPENDS csharp_wrapper
COMMENT "Testing C# wrapper"
)

# Custom target to pack NuGet package
add_custom_target(csharp_pack
COMMAND ${DOTNET_EXECUTABLE} pack --configuration Release -o ${CMAKE_CURRENT_BINARY_DIR}/nupkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/csharp
DEPENDS csharp_wrapper
COMMENT "Creating NuGet package"
)

# Custom target to run C# examples
add_custom_target(csharp_example
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$ENV{LD_LIBRARY_PATH}"
${DOTNET_EXECUTABLE} run --configuration Release --project LibLpm.Examples
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bindings/csharp
DEPENDS csharp_wrapper
COMMENT "Running C# examples"
)

message(STATUS "C# wrapper targets added:")
message(STATUS " make csharp_wrapper - Build C# wrapper")
message(STATUS " make csharp_test - Run C# tests")
message(STATUS " make csharp_pack - Create NuGet package")
message(STATUS " make csharp_example - Run C# examples")
else()
message(WARNING ".NET SDK not found. C# wrapper will not be built.")
message(WARNING "Install .NET SDK to build the wrapper: https://dotnet.microsoft.com/download")
endif()
endif()

# Lua wrapper
if(BUILD_LUA_WRAPPER)
add_subdirectory(bindings/lua)
Expand Down Expand Up @@ -576,6 +656,11 @@ if(BUILD_CPP_WRAPPER)
else()
message(STATUS " Build C++ wrapper: OFF")
endif()
if(BUILD_CSHARP_WRAPPER AND DOTNET_EXECUTABLE)
message(STATUS " Build C# wrapper: ON")
else()
message(STATUS " Build C# wrapper: OFF")
endif()
if(BUILD_LUA_WRAPPER)
message(STATUS " Build Lua wrapper: ON")
else()
Expand Down
52 changes: 52 additions & 0 deletions bindings/csharp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Build results
[Dd]ebug/
[Rr]elease/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio files
.vs/
*.user
*.userosscache
*.sln.docstates

# NuGet packages
*.nupkg
*.snupkg
nupkg/

# Test results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.trx
TestResults/

# Coverage
*.coverage
*.coveragexml
coverage/

# JetBrains Rider
.idea/

# VS Code
.vscode/

# macOS
.DS_Store

# Native libraries (populated during build)
# Don't ignore runtimes/ structure, but ignore actual binaries
runtimes/**/native/*.so
runtimes/**/native/*.dll
runtimes/**/native/*.dylib

# Project-specific
*.received.*
102 changes: 102 additions & 0 deletions bindings/csharp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
cmake_minimum_required(VERSION 3.16)

# C# wrapper for liblpm
project(liblpm_csharp VERSION 2.0.0)

# Find .NET SDK
find_program(DOTNET_EXECUTABLE dotnet)

if(NOT DOTNET_EXECUTABLE)
message(FATAL_ERROR "dotnet not found. Install .NET SDK to build C# wrapper.")
endif()

# Get .NET version
execute_process(
COMMAND ${DOTNET_EXECUTABLE} --version
OUTPUT_VARIABLE DOTNET_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "Found .NET SDK: ${DOTNET_VERSION}")

# Find or import lpm library
if(NOT TARGET lpm)
set(LPM_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..")
if(EXISTS "${LPM_ROOT_DIR}/include/lpm.h")
message(STATUS "Using liblpm from parent project")
else()
find_library(LPM_LIBRARY NAMES lpm)
if(LPM_LIBRARY)
message(STATUS "Found system liblpm: ${LPM_LIBRARY}")
else()
message(FATAL_ERROR "liblpm not found. Build the main project first or install liblpm.")
endif()
endif()
endif()

# Configuration
set(CSHARP_BUILD_CONFIG "$<IF:$<CONFIG:Debug>,Debug,Release>")

# Build C# wrapper
add_custom_target(csharp_wrapper ALL
COMMAND ${DOTNET_EXECUTABLE} build --configuration ${CSHARP_BUILD_CONFIG}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS lpm
COMMENT "Building C# wrapper"
VERBATIM
)

# Test C# wrapper
add_custom_target(csharp_test
COMMAND ${DOTNET_EXECUTABLE} test --configuration ${CSHARP_BUILD_CONFIG} --no-build
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS csharp_wrapper
COMMENT "Testing C# wrapper"
VERBATIM
)

# Pack NuGet package
add_custom_target(csharp_pack
COMMAND ${DOTNET_EXECUTABLE} pack --configuration Release -o ${CMAKE_CURRENT_BINARY_DIR}/nupkg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS csharp_wrapper
COMMENT "Creating NuGet package"
VERBATIM
)

# Run examples
add_custom_target(csharp_example
COMMAND ${DOTNET_EXECUTABLE} run --configuration ${CSHARP_BUILD_CONFIG} --project LibLpm.Examples
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS csharp_wrapper
COMMENT "Running C# examples"
VERBATIM
)

# Copy native library to runtimes directory for local development
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(NATIVE_LIB_DIR "runtimes/linux-x64/native")
set(NATIVE_LIB_NAME "liblpm.so")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(NATIVE_LIB_DIR "runtimes/win-x64/native")
set(NATIVE_LIB_NAME "lpm.dll")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(NATIVE_LIB_DIR "runtimes/osx-x64/native")
set(NATIVE_LIB_NAME "liblpm.dylib")
endif()

add_custom_command(TARGET csharp_wrapper POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/${NATIVE_LIB_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:lpm>
${CMAKE_CURRENT_SOURCE_DIR}/${NATIVE_LIB_DIR}/${NATIVE_LIB_NAME}
COMMENT "Copying native library to runtimes directory"
VERBATIM
)

message(STATUS "C# wrapper configuration:")
message(STATUS " .NET SDK: ${DOTNET_VERSION}")
message(STATUS " Build targets:")
message(STATUS " make csharp_wrapper - Build C# wrapper")
message(STATUS " make csharp_test - Run C# tests")
message(STATUS " make csharp_pack - Create NuGet package")
message(STATUS " make csharp_example - Run examples")
46 changes: 46 additions & 0 deletions bindings/csharp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Dockerfile for C# bindings CI testing
FROM mcr.microsoft.com/dotnet/sdk:8.0

# Install C build tools for native library compilation
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gcc \
cmake \
ninja-build \
git \
pkg-config \
libc6-dev \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /build

# Copy the entire liblpm project
COPY . /build/

# Initialize submodules and build liblpm native library
RUN git config --global --add safe.directory /build && \
if [ -f .gitmodules ]; then git submodule update --init --recursive; fi && \
mkdir -p build && cd build && \
cmake -DCMAKE_BUILD_TYPE=Release -GNinja .. && \
ninja && \
ninja install && \
ldconfig

# Copy native library to runtimes directory
RUN mkdir -p /build/bindings/csharp/runtimes/linux-x64/native && \
cp /build/build/liblpm.so /build/bindings/csharp/runtimes/linux-x64/native/

# Build and test C# bindings
WORKDIR /build/bindings/csharp

# Restore dependencies
RUN dotnet restore

# Build in Release mode
RUN dotnet build --configuration Release --no-restore

# Run tests
RUN dotnet test --configuration Release --no-build --verbosity normal

# Default command: run tests
CMD ["dotnet", "test", "--configuration", "Release", "--verbosity", "normal"]
Loading
Loading