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
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
99 changes: 64 additions & 35 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -56,28 +78,35 @@ target_compile_options(
-Werror
-Og
-ggdb
PUBLIC
-m32
-mfpmath=sse
-msse2
)
# 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}
)
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(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(
Expand Down
10 changes: 1 addition & 9 deletions n_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions n_ua.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
{

Expand Down
2 changes: 2 additions & 0 deletions scripts/check_libc_dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 11 additions & 3 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(Catch2)

# Add specific build flags for Catch2
if(TARGET Catch2)
# 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
Expand Down Expand Up @@ -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})

Expand Down Expand Up @@ -239,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()
Expand Down
Loading