From 2d13c085bfa52208a7dbe4c25e6d76631733524c Mon Sep 17 00:00:00 2001 From: Matthew McGowan Date: Mon, 23 Feb 2026 06:46:09 -0800 Subject: [PATCH 1/6] build: Add macOS build support for unit tests Add conditional CMake configuration for macOS (APPLE): - Skip -m32 and Linux-specific linker flags - Define HAVE_STRLCPY/HAVE_STRLCAT (macOS provides these natively) - Exclude n_str.c (strlcpy/strlcat bundled implementations) - Suppress AppleClang-specific warnings in upstream code - Use -flat_namespace for dylib and test executables to enable FFF test fake symbol interposition All changes are guarded by if(APPLE)/if(NOT APPLE) so Linux CI behavior is completely unchanged. --- CMakeLists.txt | 65 +++++++++++++++++++++++++++++++++------------ test/CMakeLists.txt | 6 ++++- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01bd3fa6..68d6bc58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,28 +56,59 @@ target_compile_options( -Werror -Og -ggdb - PUBLIC - -m32 - -mfpmath=sse - -msse2 ) +if(NOT APPLE) + target_compile_options( + note_c + PUBLIC + -m32 + -mfpmath=sse + -msse2 + ) +endif() target_include_directories( note_c PUBLIC ${NOTE_C_SRC_DIR} ) -target_link_directories( - note_c - PUBLIC - /lib32 - /usr/lib32 - /usr/lib/gcc/x86_64-linux-gnu/12/32 -) -target_link_options( - note_c - PUBLIC - -m32 - -Wl,-melf_i386 -) +if(NOT APPLE) + target_link_directories( + note_c + PUBLIC + /lib32 + /usr/lib32 + /usr/lib/gcc/x86_64-linux-gnu/12/32 + ) + target_link_options( + note_c + PUBLIC + -m32 + -Wl,-melf_i386 + ) +endif() +if(APPLE) + target_compile_definitions( + note_c + PUBLIC + HAVE_STRLCPY + HAVE_STRLCAT + ) + # Suppress warnings in upstream code that AppleClang treats as errors + target_compile_options( + note_c + PRIVATE + -Wno-deprecated-non-prototype + -Wno-unused-but-set-variable + -Wno-strict-prototypes + ) + # macOS provides strlcpy/strlcat natively; exclude the bundled implementations + set_source_files_properties(${NOTE_C_SRC_DIR}/n_str.c PROPERTIES HEADER_FILE_ONLY TRUE) + # Use flat namespace so test fakes (FFF) can interpose library symbols + target_link_options( + note_c + PRIVATE + -Wl,-flat_namespace + ) +endif() if(NOTE_C_LOW_MEM) target_compile_definitions( diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f4a0c38a..4873ae5c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,7 +17,7 @@ FetchContent_Declare( FetchContent_MakeAvailable(Catch2) # Add specific build flags for Catch2 -if(TARGET Catch2) +if(TARGET Catch2 AND NOT APPLE) target_compile_options(Catch2 PUBLIC -m32 @@ -47,6 +47,10 @@ macro(add_test TEST_NAME) PRIVATE -Og PRIVATE -ggdb ) + if(APPLE) + # Allow test fakes (FFF) to interpose symbols from the note_c dylib + target_link_options(${TEST_NAME} PRIVATE -Wl,-flat_namespace) + endif() list(APPEND TEST_TARGETS ${TEST_NAME}) From 1cdf7a1a1417135c5859f363784c45e5ea734a1e Mon Sep 17 00:00:00 2001 From: Matthew McGowan Date: Mon, 9 Mar 2026 10:51:44 -0700 Subject: [PATCH 2/6] build: Add macOS build support for unit tests Use check_symbol_exists() to detect system-provided strlcpy/strlcat (macOS, *BSD) and conditionally exclude n_str.c and define HAVE_STRLCPY/ HAVE_STRLCAT guards. This avoids collisions with the platform's fortified string macros that prevented compilation on macOS. Guard the 32-bit x86 compile/link flags behind a Linux x86 check instead of if(NOT APPLE), making the build portable to any non-x86 platform. Suppress two AppleClang warnings-as-errors in upstream code (-Wstrict-prototypes, -Wunused-but-set-variable). Add -Wl,-flat_namespace on macOS for both the library and test executables so FFF test fakes can interpose dylib symbols. --- CMakeLists.txt | 99 ++++++++++++++++++++++++--------------------- test/CMakeLists.txt | 4 +- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68d6bc58..ed36cf3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,26 +27,48 @@ option(NOTE_C_SINGLE_PRECISION "Use single precision for JSON floating point num option(NOTE_C_HEARTBEAT_CALLBACK "Enable heartbeat callback support." OFF) set(NOTE_C_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +# Detect whether the system already provides strlcpy/strlcat (macOS, *BSD). +# When present, we skip the bundled implementations in n_str.c and define +# guards so note.h doesn't redeclare them (which collides with the platform's +# fortified macros). +include(CheckSymbolExists) +check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY) +check_symbol_exists(strlcat "string.h" HAVE_STRLCAT) + add_library(note_c SHARED) -target_sources( - note_c - PRIVATE - ${NOTE_C_SRC_DIR}/n_atof.c - ${NOTE_C_SRC_DIR}/n_b64.c - ${NOTE_C_SRC_DIR}/n_cjson.c - ${NOTE_C_SRC_DIR}/n_cjson_helpers.c - ${NOTE_C_SRC_DIR}/n_cobs.c - ${NOTE_C_SRC_DIR}/n_const.c - ${NOTE_C_SRC_DIR}/n_ftoa.c - ${NOTE_C_SRC_DIR}/n_helpers.c - ${NOTE_C_SRC_DIR}/n_hooks.c - ${NOTE_C_SRC_DIR}/n_i2c.c - ${NOTE_C_SRC_DIR}/n_md5.c - ${NOTE_C_SRC_DIR}/n_printf.c - ${NOTE_C_SRC_DIR}/n_request.c - ${NOTE_C_SRC_DIR}/n_serial.c - ${NOTE_C_SRC_DIR}/n_str.c + +set(NOTE_C_SOURCES + ${NOTE_C_SRC_DIR}/n_atof.c + ${NOTE_C_SRC_DIR}/n_b64.c + ${NOTE_C_SRC_DIR}/n_cjson.c + ${NOTE_C_SRC_DIR}/n_cjson_helpers.c + ${NOTE_C_SRC_DIR}/n_cobs.c + ${NOTE_C_SRC_DIR}/n_const.c + ${NOTE_C_SRC_DIR}/n_ftoa.c + ${NOTE_C_SRC_DIR}/n_helpers.c + ${NOTE_C_SRC_DIR}/n_hooks.c + ${NOTE_C_SRC_DIR}/n_i2c.c + ${NOTE_C_SRC_DIR}/n_md5.c + ${NOTE_C_SRC_DIR}/n_printf.c + ${NOTE_C_SRC_DIR}/n_request.c + ${NOTE_C_SRC_DIR}/n_serial.c ) +# n_str.c provides weak strlcpy/strlcat. On systems that already have them +# the file won't compile because the platform headers define these as +# fortified macros that conflict with the function definitions. +if(NOT HAVE_STRLCPY OR NOT HAVE_STRLCAT) + list(APPEND NOTE_C_SOURCES ${NOTE_C_SRC_DIR}/n_str.c) +endif() +target_sources(note_c PRIVATE ${NOTE_C_SOURCES}) + +if(HAVE_STRLCPY) + target_compile_definitions(note_c PUBLIC HAVE_STRLCPY) +endif() +if(HAVE_STRLCAT) + target_compile_definitions(note_c PUBLIC HAVE_STRLCAT) +endif() + target_compile_options( note_c PRIVATE @@ -57,20 +79,23 @@ target_compile_options( -Og -ggdb ) -if(NOT APPLE) - target_compile_options( - note_c - PUBLIC - -m32 - -mfpmath=sse - -msse2 +# Suppress warnings in upstream code that AppleClang treats as errors. +if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") + target_compile_options(note_c PRIVATE + -Wno-strict-prototypes + -Wno-unused-but-set-variable ) endif() +# The Linux CI runs tests in 32-bit mode. These flags are x86-specific and +# unavailable on Apple toolchains or non-x86 architectures. +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") + target_compile_options(note_c PUBLIC -m32 -mfpmath=sse -msse2) +endif() target_include_directories( note_c PUBLIC ${NOTE_C_SRC_DIR} ) -if(NOT APPLE) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") target_link_directories( note_c PUBLIC @@ -86,28 +111,8 @@ if(NOT APPLE) ) endif() if(APPLE) - target_compile_definitions( - note_c - PUBLIC - HAVE_STRLCPY - HAVE_STRLCAT - ) - # Suppress warnings in upstream code that AppleClang treats as errors - target_compile_options( - note_c - PRIVATE - -Wno-deprecated-non-prototype - -Wno-unused-but-set-variable - -Wno-strict-prototypes - ) - # macOS provides strlcpy/strlcat natively; exclude the bundled implementations - set_source_files_properties(${NOTE_C_SRC_DIR}/n_str.c PROPERTIES HEADER_FILE_ONLY TRUE) # Use flat namespace so test fakes (FFF) can interpose library symbols - target_link_options( - note_c - PRIVATE - -Wl,-flat_namespace - ) + target_link_options(note_c PRIVATE -Wl,-flat_namespace) endif() if(NOTE_C_LOW_MEM) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4873ae5c..460bbda8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,8 +16,8 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(Catch2) -# Add specific build flags for Catch2 -if(TARGET Catch2 AND NOT APPLE) +# Add specific build flags for Catch2 (32-bit mode for Linux x86 CI only) +if(TARGET Catch2 AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") target_compile_options(Catch2 PUBLIC -m32 From ca24782266ea0e9c67a0325e03b068aaf440f95e Mon Sep 17 00:00:00 2001 From: Matthew McGowan Date: Mon, 9 Mar 2026 10:56:40 -0700 Subject: [PATCH 3/6] fix: Resolve AppleClang warnings-as-errors - n_ua.c: add void to NoteUserAgent() prototype to fix -Wstrict-prototypes (empty parens means unspecified args in C) - n_helpers.c: remove unused variables j and lastLengthCount that triggered -Wunused-but-set-variable With these fixes the -Wno-* suppressions added in the previous commit are no longer needed and are removed from CMakeLists.txt. --- CMakeLists.txt | 7 ------- n_helpers.c | 10 +--------- n_ua.c | 4 ++-- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed36cf3e..67d05e11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,13 +79,6 @@ target_compile_options( -Og -ggdb ) -# Suppress warnings in upstream code that AppleClang treats as errors. -if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") - target_compile_options(note_c PRIVATE - -Wno-strict-prototypes - -Wno-unused-but-set-variable - ) -endif() # The Linux CI runs tests in 32-bit mode. These flags are x86-specific and # unavailable on Apple toolchains or non-x86 architectures. if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") diff --git a/n_helpers.c b/n_helpers.c index 3c02cd61..3a46c2e3 100644 --- a/n_helpers.c +++ b/n_helpers.c @@ -2403,7 +2403,7 @@ uint32_t NoteMemAvailable(void) objHeader *lastObj = NULL; static long int maxsize = 35000; for (long int i=maxsize; i>=(long int)sizeof(objHeader); i=i-sizeof(objHeader)) { - for (long int j=0;; j++) { + for (;;) { objHeader *thisObj; thisObj = (objHeader *) _Malloc(i); if (thisObj == NULL) { @@ -2416,16 +2416,8 @@ uint32_t NoteMemAvailable(void) } // Free the objects backwards - long int lastLength = 0; - long int lastLengthCount = 0; uint32_t total = 0; while (lastObj != NULL) { - if (lastObj->length != lastLength) { - lastLength = lastObj->length; - lastLengthCount = 1; - } else { - lastLengthCount++; - } objHeader *thisObj = lastObj; lastObj = lastObj->prev; total += thisObj->length; diff --git a/n_ua.c b/n_ua.c index 91815291..c270373b 100644 --- a/n_ua.c +++ b/n_ua.c @@ -92,9 +92,9 @@ __attribute__((weak)) void NoteUserAgentUpdate(J *ua) */ /**************************************************************************/ #if defined(_MSC_VER) -J *NoteUserAgent() +J *NoteUserAgent(void) #else -__attribute__((weak)) J *NoteUserAgent() +__attribute__((weak)) J *NoteUserAgent(void) #endif { From 79246117ed29827ab2bb0a8a406c9b3975e59604 Mon Sep 17 00:00:00 2001 From: Matthew McGowan Date: Mon, 9 Mar 2026 12:15:00 -0700 Subject: [PATCH 4/6] ci: Add macOS build verification job to CI pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `run_macos_unit_tests` job that configures and builds note-c with tests on macos-latest. Tests are not run via ctest because FFF symbol interposition does not fully work on macOS — even with -flat_namespace, intra-library calls are resolved at link time and cannot be faked. --- .github/workflows/ci.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 207dc3ef..c845f41e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,6 +195,30 @@ jobs: run: | docker run --rm --volume $(pwd):/note-c/ --workdir /note-c/ --entrypoint ./scripts/run_cppcheck.sh ghcr.io/blues/note_c_ci:latest + run_macos_unit_tests: + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure + run: cmake -B build -DNOTE_C_BUILD_TESTS=ON + + - name: Build + run: cmake --build build --parallel $(sysctl -n hw.ncpu) + + # NOTE: ctest is not run here. A subset of tests that rely on FFF + # symbol interposition fail on macOS due to two-level namespace + # linking. The build step above verifies that all sources and tests + # compile and link successfully. + # + # Even with -Wl,-flat_namespace, intra-library calls within the + # note_c dylib are resolved at link time as direct calls, so FFF + # fakes in the test executable cannot interpose them. Fully fixing + # this would require linking note-c as a static library for tests + # or using a different mocking approach on macOS. + publish_ci_image: runs-on: ubuntu-latest # Make sure unit tests unit tests passed before publishing. From 868e001793792b423bf3ccf02be92ed3661bc5e2 Mon Sep 17 00:00:00 2001 From: Matthew McGowan Date: Mon, 9 Mar 2026 12:25:35 -0700 Subject: [PATCH 5/6] fix: resolve CI failures for strlcpy/strlcat on newer glibc - Add `strlcpy` and `strlcat` to the libc dependency whitelist so the `check_libc_dependencies` job passes when `n_str.c` is excluded on systems that provide these functions natively (glibc 2.38+). - Only add `n_str.c` to the lcov coverage exclude list when the file is actually compiled, avoiding an lcov error on unused patterns. --- scripts/check_libc_dependencies.sh | 2 ++ test/CMakeLists.txt | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/check_libc_dependencies.sh b/scripts/check_libc_dependencies.sh index f3c7fadc..498f3686 100755 --- a/scripts/check_libc_dependencies.sh +++ b/scripts/check_libc_dependencies.sh @@ -30,6 +30,8 @@ LIBC_WHITELIST=( "strchr" "strcmp" "strlen" + "strlcpy" # bundled in n_str.c; also available in glibc 2.38+ + "strlcat" # bundled in n_str.c; also available in glibc 2.38+ "strncmp" "strstr" "strtol" # required by atoi in NoteGenEnvInt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 460bbda8..a74e6d23 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -243,8 +243,12 @@ if(NOTE_C_COVERAGE) # ourselves, so we don't care about coverage for them. set( EXCLUDE_FROM_COVERAGE - "n_atof.c;n_b64.c;n_cjson.c;n_ftoa.c;n_md5.c;n_str.c" + "n_atof.c;n_b64.c;n_cjson.c;n_ftoa.c;n_md5.c" ) + # n_str.c is only compiled when the system lacks strlcpy/strlcat. + if(NOT HAVE_STRLCPY OR NOT HAVE_STRLCAT) + list(APPEND EXCLUDE_FROM_COVERAGE "n_str.c") + endif() foreach(EXCLUDE_FILE ${EXCLUDE_FROM_COVERAGE}) string(APPEND LCOV_EXCLUDE "--exclude '*/${EXCLUDE_FILE}' ") endforeach() From 6dedcdaf97a6edf04a7a1832eae222fda7824985 Mon Sep 17 00:00:00 2001 From: Matthew McGowan Date: Mon, 9 Mar 2026 11:27:26 -0700 Subject: [PATCH 6/6] build: Add note_c_lib static library target for downstream consumers Add a clean note_c_lib static library target that downstream projects (e.g. note-posix) can consume via add_subdirectory + target_link_libraries without pulling in the unit test harness, 32-bit cross-compile flags, or FFF interposition settings. - Bump cmake_minimum_required to 3.21 for `PROJECT_IS_TOP_LEVEL` - Default `NOTE_C_BUILD_TESTS` to ON only when note-c is the top-level project; OFF when included as a subdirectory - Move the existing note_c shared library (with -Werror, 32-bit flags, NOTE_C_TEST, etc.) inside the NOTE_C_BUILD_TESTS guard - Add test/integration/ with a standalone CMake project that verifies `note_c_lib` links correctly via `add_subdirectory` --- CMakeLists.txt | 189 +++++++++++++++++++++++--------- test/integration/CMakeLists.txt | 12 ++ test/integration/link_test.c | 39 +++++++ 3 files changed, 187 insertions(+), 53 deletions(-) create mode 100644 test/integration/CMakeLists.txt create mode 100644 test/integration/link_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 67d05e11..2eaa1a70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) cmake_policy(SET CMP0095 NEW) if ("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") @@ -16,7 +16,7 @@ if(NOT EXISTS ${PROJECT_BINARY_DIR}/.gitignore) endif() option(NOTE_C_BUILD_DOCS "Build docs." OFF) -option(NOTE_C_BUILD_TESTS "Build tests." ON) +option(NOTE_C_BUILD_TESTS "Build tests." ${PROJECT_IS_TOP_LEVEL}) option(NOTE_C_COVERAGE "Compile for test NOTE_C_COVERAGE reporting." OFF) option(NOTE_C_LOW_MEM "Build the library tailored for low memory usage." OFF) option(NOTE_C_MEM_CHECK "Run tests with Valgrind." OFF) @@ -36,8 +36,6 @@ include(CheckSymbolExists) check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY) check_symbol_exists(strlcat "string.h" HAVE_STRLCAT) -add_library(note_c SHARED) - set(NOTE_C_SOURCES ${NOTE_C_SRC_DIR}/n_atof.c ${NOTE_C_SRC_DIR}/n_b64.c @@ -60,57 +58,31 @@ set(NOTE_C_SOURCES if(NOT HAVE_STRLCPY OR NOT HAVE_STRLCAT) list(APPEND NOTE_C_SOURCES ${NOTE_C_SRC_DIR}/n_str.c) endif() -target_sources(note_c PRIVATE ${NOTE_C_SOURCES}) +# --------------------------------------------------------------------------- +# note_c_lib — static library for downstream consumers. +# +# Projects that include note-c as a submodule can simply do: +# +# add_subdirectory(note-c) +# target_link_libraries(my_app PRIVATE note_c_lib) +# +# This target carries the include path and platform compile definitions +# (HAVE_STRLCPY, etc.) but no test flags, 32-bit cross-compile options, +# or other CI-specific settings. +# --------------------------------------------------------------------------- +add_library(note_c_lib STATIC ${NOTE_C_SOURCES}) +target_include_directories(note_c_lib PUBLIC ${NOTE_C_SRC_DIR}) if(HAVE_STRLCPY) - target_compile_definitions(note_c PUBLIC HAVE_STRLCPY) + target_compile_definitions(note_c_lib PUBLIC HAVE_STRLCPY) endif() if(HAVE_STRLCAT) - target_compile_definitions(note_c PUBLIC HAVE_STRLCAT) -endif() - -target_compile_options( - note_c - PRIVATE - -Wall - -Wextra - -Wpedantic - -Werror - -Og - -ggdb -) -# The Linux CI runs tests in 32-bit mode. These flags are x86-specific and -# unavailable on Apple toolchains or non-x86 architectures. -if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") - target_compile_options(note_c PUBLIC -m32 -mfpmath=sse -msse2) -endif() -target_include_directories( - note_c - PUBLIC ${NOTE_C_SRC_DIR} -) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") - target_link_directories( - note_c - PUBLIC - /lib32 - /usr/lib32 - /usr/lib/gcc/x86_64-linux-gnu/12/32 - ) - target_link_options( - note_c - PUBLIC - -m32 - -Wl,-melf_i386 - ) -endif() -if(APPLE) - # Use flat namespace so test fakes (FFF) can interpose library symbols - target_link_options(note_c PRIVATE -Wl,-flat_namespace) + target_compile_definitions(note_c_lib PUBLIC HAVE_STRLCAT) endif() if(NOTE_C_LOW_MEM) target_compile_definitions( - note_c + note_c_lib PUBLIC NOTE_C_LOW_MEM ) @@ -119,7 +91,7 @@ else() # about an empty translation unit, so we only add it to the build if # NOTE_C_LOW_MEM is false. target_sources( - note_c + note_c_lib PRIVATE ${NOTE_C_SRC_DIR}/n_ua.c ) @@ -127,7 +99,7 @@ endif() if(NOTE_C_NO_LIBC) target_link_options( - note_c + note_c_lib PRIVATE -nostdlib -nodefaultlibs @@ -137,7 +109,7 @@ endif() if(NOTE_NODEBUG) target_compile_definitions( - note_c + note_c_lib PUBLIC NOTE_NODEBUG ) @@ -145,7 +117,7 @@ endif() if(NOTE_C_SHOW_MALLOC) target_compile_definitions( - note_c + note_c_lib PUBLIC NOTE_C_SHOW_MALLOC ) @@ -153,7 +125,7 @@ endif() if(NOTE_C_SINGLE_PRECISION) target_compile_definitions( - note_c + note_c_lib PUBLIC NOTE_C_SINGLE_PRECISION ) @@ -161,13 +133,124 @@ endif() if(NOTE_C_HEARTBEAT_CALLBACK) target_compile_definitions( - note_c + note_c_lib PUBLIC NOTE_C_HEARTBEAT_CALLBACK ) endif() +# --------------------------------------------------------------------------- +# note_c — shared library used by the unit test harness. +# +# This target adds -Werror, 32-bit cross-compile flags (Linux CI), FFF +# interposition support (macOS), and the NOTE_C_TEST define. It is NOT +# intended for downstream consumption. +# --------------------------------------------------------------------------- if(NOTE_C_BUILD_TESTS) + add_library(note_c SHARED ${NOTE_C_SOURCES}) + target_include_directories(note_c PUBLIC ${NOTE_C_SRC_DIR}) + + if(HAVE_STRLCPY) + target_compile_definitions(note_c PUBLIC HAVE_STRLCPY) + endif() + if(HAVE_STRLCAT) + target_compile_definitions(note_c PUBLIC HAVE_STRLCAT) + endif() + + target_compile_options( + note_c + PRIVATE + -Wall + -Wextra + -Wpedantic + -Werror + -Og + -ggdb + ) + # The Linux CI runs tests in 32-bit mode. These flags are x86-specific and + # unavailable on Apple toolchains or non-x86 architectures. + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") + target_compile_options(note_c PUBLIC -m32 -mfpmath=sse -msse2) + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i[3-6]86") + target_link_directories( + note_c + PUBLIC + /lib32 + /usr/lib32 + /usr/lib/gcc/x86_64-linux-gnu/12/32 + ) + target_link_options( + note_c + PUBLIC + -m32 + -Wl,-melf_i386 + ) + endif() + if(APPLE) + # Use flat namespace so test fakes (FFF) can interpose library symbols + target_link_options(note_c PRIVATE -Wl,-flat_namespace) + endif() + + if(NOTE_C_LOW_MEM) + target_compile_definitions( + note_c + PUBLIC + NOTE_C_LOW_MEM + ) + else() + # This file is empty if NOTE_C_LOW_MEM is defined, which leads to a warning + # about an empty translation unit, so we only add it to the build if + # NOTE_C_LOW_MEM is false. + target_sources( + note_c + PRIVATE + ${NOTE_C_SRC_DIR}/n_ua.c + ) + endif() + + if(NOTE_C_NO_LIBC) + target_link_options( + note_c + PRIVATE + -nostdlib + -nodefaultlibs + LINKER:--no-undefined + ) + endif() + + if(NOTE_NODEBUG) + target_compile_definitions( + note_c + PUBLIC + NOTE_NODEBUG + ) + endif() + + if(NOTE_C_SHOW_MALLOC) + target_compile_definitions( + note_c + PUBLIC + NOTE_C_SHOW_MALLOC + ) + endif() + + if(NOTE_C_SINGLE_PRECISION) + target_compile_definitions( + note_c + PUBLIC + NOTE_C_SINGLE_PRECISION + ) + endif() + + if(NOTE_C_HEARTBEAT_CALLBACK) + target_compile_definitions( + note_c + PUBLIC + NOTE_C_HEARTBEAT_CALLBACK + ) + endif() + # Including CTest here rather than in test/CMakeLists.txt allows us to run # ctest from the root build directory (e.g. build/ instead of build/test/). # We also need to set MEMORYCHECK_COMMAND_OPTIONS before including this. diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt new file mode 100644 index 00000000..e7973225 --- /dev/null +++ b/test/integration/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.21) +project(note_c_integration_test LANGUAGES C) + +# Pull in note-c as a subdirectory, the way a downstream project would. +# NOTE_C_BUILD_TESTS defaults to OFF because we are not the top-level project. +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. note-c) + +add_executable(link_test link_test.c) +target_link_libraries(link_test PRIVATE note_c_lib) + +enable_testing() +add_test(NAME link_test COMMAND link_test) diff --git a/test/integration/link_test.c b/test/integration/link_test.c new file mode 100644 index 00000000..56870f50 --- /dev/null +++ b/test/integration/link_test.c @@ -0,0 +1,39 @@ +// Integration test: verify that note_c_lib links correctly and key +// symbols are callable from a downstream project. + +#include +#include +#include +#include "note.h" + +int main(void) +{ + // Exercise a representative set of note-c API functions to prove + // the static library links and the public headers are usable. + + // Register standard malloc/free so note-c can allocate memory. + NoteSetFnDefault(malloc, free, NULL, NULL); + + // NoteNewRequest creates a JSON request object. + J *req = NoteNewRequest("hub.status"); + if (req == NULL) { + fprintf(stderr, "NoteNewRequest returned NULL\n"); + return 1; + } + + // JAddStringToObject exercises cJSON helpers. + JAddStringToObject(req, "test", "value"); + + // Read it back. + const char *val = JGetString(req, "test"); + if (val == NULL || strcmp(val, "value") != 0) { + fprintf(stderr, "JGetString mismatch\n"); + JDelete(req); + return 1; + } + + JDelete(req); + + printf("note_c_lib link test passed\n"); + return 0; +}