diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ab6fa1..fb523b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,32 @@ jobs: - name: Test run: ctest --test-dir build --output-on-failure --timeout 600 + system-sodium: + name: ubuntu-24.04 system sodium + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - name: Install libsodium + run: sudo apt-get update && sudo apt-get install -y libsodium-dev + + - name: Configure + run: cmake -B build -DCMAKE_BUILD_TYPE=Release -DNETCODE_SYSTEM_SODIUM=ON -DBUILD_SHARED_LIBS=ON + + - name: Build + run: cmake --build build --parallel + + - name: Test + run: ctest --test-dir build --output-on-failure --timeout 600 + + - name: Install + run: | + cmake --install build --prefix stage + test -f stage/include/netcode.h + test -f stage/lib/libnetcode.so || test -L stage/lib/libnetcode.so + echo "install layout ok" + sanitizers: name: sanitizers (asan+ubsan) runs-on: ubuntu-24.04 diff --git a/BUILDING.md b/BUILDING.md index 2402864..e656e84 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -24,6 +24,20 @@ Then you can run binaries like this: For a debug build, use `-DCMAKE_BUILD_TYPE=Debug` and a separate build directory, e.g. `-B build-debug`. +## Installing, shared libraries, and system libsodium + +By default netcode builds as a static library against the vendored libsodium subset, and nothing needs to be installed. For packaging (e.g. homebrew), three options change that: + + cmake -B build -DCMAKE_BUILD_TYPE=Release \ + -DNETCODE_SYSTEM_SODIUM=ON \ + -DBUILD_SHARED_LIBS=ON + cmake --build build --parallel + cmake --install build --prefix /some/prefix + +- `NETCODE_SYSTEM_SODIUM=ON` links the system libsodium instead of the vendored copy (they are interchangeable — the vendored subset is a byte-identical slice of upstream). +- `BUILD_SHARED_LIBS=ON` builds `libnetcode` as a shared library. +- `cmake --install` installs `netcode.h` and the library (`NETCODE_INSTALL=OFF` disables the install target, e.g. when embedding netcode as a subproject). + ## Sanitizers and fuzzing To build everything with AddressSanitizer and UndefinedBehaviorSanitizer, configure with `-DNETCODE_SANITIZE=ON` and run the tests as usual: diff --git a/CLAUDE.md b/CLAUDE.md index f190ef6..a5c4d4a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,10 @@ independent implementations (C#, Go, Rust, TypeScript). `netcode_test` target compiles netcode.c into itself with `NETCODE_ENABLE_TESTS`, so it links only sodium. `-DNETCODE_SANITIZE=ON` adds ASan+UBSan (sodium gets ASan only); `-DNETCODE_FUZZ=ON` builds the `fuzz/` harnesses (libFuzzer where available, else a - standalone file replayer). CI (`.github/workflows/ci.yml`) builds and tests Debug + + standalone file replayer). For packaging: `-DNETCODE_SYSTEM_SODIUM=ON` links the + system libsodium instead of the vendored copy, `-DBUILD_SHARED_LIBS=ON` builds + libnetcode shared, and `cmake --install` installs netcode.h + the library + (this is the homebrew configuration, covered by a CI leg). CI (`.github/workflows/ci.yml`) builds and tests Debug + Release on Linux x64, Linux arm64, macOS Apple Silicon, and Windows x64 (MSVC + a MinGW leg), plus a Linux ASan+UBSan leg and a bounded smoke-fuzz leg. A separate nightly workflow (`.github/workflows/scheduled.yml`) runs deep fuzzing with an diff --git a/CMakeLists.txt b/CMakeLists.txt index cf5eece..8f2ef6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(netcode LANGUAGES C CXX) +project(netcode VERSION 1.3.1 LANGUAGES C CXX) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) @@ -32,6 +32,8 @@ endif() option(NETCODE_SANITIZE "Build with AddressSanitizer and UndefinedBehaviorSanitizer" OFF) option(NETCODE_FUZZ "Build the fuzz targets" OFF) +option(NETCODE_SYSTEM_SODIUM "Link against the system libsodium instead of the vendored copy" OFF) +option(NETCODE_INSTALL "Generate the install target (netcode.h and the netcode library)" ON) # sanitizers apply to the whole build. the vendored crypto is exempted from UBSan # below (third-party SIMD code uses intentional type punning / unaligned access that @@ -46,34 +48,66 @@ if(NETCODE_SANITIZE) endif() endif() -# vendored libsodium subset, amalgamated into a single header + source pair. -# see sodium/NOTES.md for how it is generated and validated. - -add_library(sodium STATIC sodium/sodium.c sodium/sodium.h) -target_include_directories(sodium PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/sodium) -if(NOT MSVC) - target_compile_options(sodium PRIVATE - -Wall -Wextra - -Wno-unused-parameter - -Wno-unused-function - -Wno-unknown-pragmas - -Wno-unused-variable - -Wno-type-limits) - if(NETCODE_SANITIZE) - target_compile_options(sodium PRIVATE -fno-sanitize=undefined) +if(NETCODE_SYSTEM_SODIUM) + + # link the system libsodium (e.g. homebrew or apt) instead of the vendored copy. + # the vendored subset is a byte-identical slice of upstream, so the two are + # interchangeable. package managers prefer this so their libsodium supplies the + # crypto and receives security updates. + + find_path(NETCODE_SODIUM_INCLUDE_DIR sodium.h REQUIRED) + find_library(NETCODE_SODIUM_LIBRARY sodium REQUIRED) + + add_library(sodium INTERFACE) + target_include_directories(sodium INTERFACE ${NETCODE_SODIUM_INCLUDE_DIR}) + target_link_libraries(sodium INTERFACE ${NETCODE_SODIUM_LIBRARY}) + +else() + + # vendored libsodium subset, amalgamated into a single header + source pair. + # see sodium/NOTES.md for how it is generated and validated. + + add_library(sodium STATIC sodium/sodium.c sodium/sodium.h) + target_include_directories(sodium PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/sodium) + set_target_properties(sodium PROPERTIES POSITION_INDEPENDENT_CODE ON) + if(NOT MSVC) + target_compile_options(sodium PRIVATE + -Wall -Wextra + -Wno-unused-parameter + -Wno-unused-function + -Wno-unknown-pragmas + -Wno-unused-variable + -Wno-type-limits) + if(NETCODE_SANITIZE) + target_compile_options(sodium PRIVATE -fno-sanitize=undefined) + endif() endif() + endif() -# the netcode library +# the netcode library. static by default; -DBUILD_SHARED_LIBS=ON builds it shared +# (the vendored sodium objects are position independent, so they fold in either way) -add_library(netcode STATIC netcode.c netcode.h) +add_library(netcode netcode.c netcode.h) target_include_directories(netcode PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_options(netcode PRIVATE ${NETCODE_WARNINGS}) target_link_libraries(netcode PUBLIC sodium) +set_target_properties(netcode PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}) if(WIN32) target_link_libraries(netcode PUBLIC ws2_32 iphlpapi) endif() +if(NETCODE_INSTALL) + include(GNUInstallDirs) + install(TARGETS netcode + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES netcode.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif() + # examples and long-running harnesses foreach(example client server client_server soak profile) @@ -118,10 +152,8 @@ if(NETCODE_FUZZ) foreach(fuzzer fuzz_read_packet fuzz_connect_token fuzz_parse_address) add_executable(${fuzzer} fuzz/${fuzzer}.c) - target_include_directories(${fuzzer} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/sodium) - target_link_libraries(${fuzzer} PRIVATE sodium) + target_include_directories(${fuzzer} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(${fuzzer} PRIVATE sodium) # provides the sodium include dir, vendored or system if(WIN32) target_link_libraries(${fuzzer} PRIVATE ws2_32 iphlpapi) endif()