cmake_minimum_required(VERSION 3.15) project(nanoprintf) option(NPF_32BIT "Compile nanoprintf tests in 32-bit mode") option(NPF_PALAND "Compile and run the mpaland printf test suite") option(NPF_CLANG_ASAN "Compile and run tests with address sanitizer") option(NPF_CLANG_UBSAN "Compile and run tests with undefined behavior sanitizer") if (NPF_32BIT AND CMAKE_HOST_APPLE) message(FATAL_ERROR "Apple doesn't support 32-bit mode anymore.") endif() if ((NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND (NPF_CLANG_ASAN OR NPF_CLANG_UBSAN)) message(FATAL_ERROR "Can only use asan/ubsan on Clang configurations.") endif() if (NPF_CLANG_ASAN AND NPF_CLANG_UBSAN) message(FATAL_ERROR "Can only use one of asan/ubsan per configuration.") endif() if (NPF_32BIT AND NOT MSVC) set(NPF_32BIT_FLAG -m32) set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} ${NPF_32BIT_FLAG}) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${NPF_32BIT_FLAG}) endif() ################ Common compile flags set(CMAKE_C_STANDARD 17) set(CMAKE_CXX_STANDARD 20) if (MSVC) set(nanoprintf_common_flags /Wall /WX) else() set(CMAKE_C_FLAGS_DEBUG "-O0 -g3") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g3") set(CMAKE_C_FLAGS_RELEASE "-Os") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Os -g3") set(CMAKE_CXX_FLAGS_RELEASE "-Os") set(nanoprintf_common_flags -pedantic -Wall -Wextra -Wundef -Werror) set(nanoprintf_link_flags "") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") list(APPEND nanoprintf_common_flags -Weverything) if (NPF_CLANG_ASAN) list(APPEND nanoprintf_common_flags -fsanitize=address) list(APPEND nanoprintf_link_flags -fsanitize=address) elseif (NPF_CLANG_UBSAN) list(APPEND nanoprintf_common_flags -fsanitize=undefined) list(APPEND nanoprintf_link_flags -fsanitize=undefined) endif() if (CMAKE_HOST_APPLE) list(APPEND nanoprintf_common_flags -Wno-poison-system-directories) endif() else() list(APPEND nanoprintf_common_flags -Wconversion -Wshadow -Wfloat-equal -Wsign-conversion -Wswitch-enum -Wswitch-default) endif() endif() ################ Doctest add_library(libdoctest_main OBJECT tests/doctest_main.cc) target_compile_options(libdoctest_main PRIVATE ${nanoprintf_common_flags}) function(npf_test name files) add_executable(${name} ${files}) target_compile_definitions(${name} PRIVATE DOCTEST_CONFIG_SUPER_FAST_ASSERTS) target_compile_options(${name} PRIVATE ${nanoprintf_common_flags}) target_link_options(${name} PRIVATE ${nanoprintf_link_flags}) target_link_libraries(${name} libdoctest_main) # Doctest is slow, only build once. # Set up a target that automatically runs + timestamps successful tests. set(timestamp "${CMAKE_CURRENT_BINARY_DIR}/${name}.timestamp") add_custom_target(run_${name} ALL DEPENDS ${timestamp}) add_custom_command(OUTPUT ${timestamp} COMMAND ${name} -m COMMAND ${CMAKE_COMMAND} -E touch ${timestamp} DEPENDS ${name} COMMENT "Running ${name}") endfunction() ################ Language compilation tests function(npf_compilation_c_test target) add_library(${target} tests/compilation_c.c) target_compile_options(${target} PRIVATE ${nanoprintf_common_flags}) endfunction() # Test every combination of compatible flags. foreach(precision 0 1) foreach(float 0 1) if ((precision EQUAL 0) AND (float EQUAL 1)) continue() endif() foreach(fw 0 1) foreach(large 0 1) foreach(small 0 1) foreach(binary 0 1) foreach(wb 0 1) foreach(alt 0 1) set(test_name "") if (fw EQUAL 1) string(APPEND test_name "_fieldwidth") endif() if (precision EQUAL 1) string(APPEND test_name "_precision") endif() if (large EQUAL 1) string(APPEND test_name "_large") endif() if (small EQUAL 1) string(APPEND test_name "_small") endif() if (float EQUAL 1) string(APPEND test_name "_float") endif() if (binary EQUAL 1) string(APPEND test_name "_binary") endif() if (wb EQUAL 1) string(APPEND test_name "_writeback") endif() if (alt EQUAL 1) string(APPEND test_name "_altform") endif() # Run a simple compilation test set(compilation_test_name "npf_compile${test_name}_c") npf_compilation_c_test(${compilation_test_name}) target_compile_definitions( ${compilation_test_name} PRIVATE NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=${fw} NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=${precision} NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=${large} NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=${small} NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=${float} NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=${binary} NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=${wb} NANOPRINTF_USE_ALT_FORM_FLAG=${alt}) # Run conformance tests (c++) set(conformance_test_name "npf_conform${test_name}") npf_test(${conformance_test_name} tests/conformance.cc) target_compile_definitions( ${conformance_test_name} PRIVATE NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=${fw} NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=${precision} NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=${large} NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=${small} NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=${float} NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=${binary} NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=${wb} NANOPRINTF_USE_ALT_FORM_FLAG=${alt}) if (NPF_PALAND) set(paland_test_name "npf_paland${test_name}") npf_test(${paland_test_name} tests/mpaland-conformance/paland.cc) target_compile_definitions( ${paland_test_name} PRIVATE NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS=${fw} NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS=${precision} NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=${large} NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS=${small} NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS=${float} NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS=${binary} NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS=${wb} NANOPRINTF_USE_ALT_FORM_FLAG=${alt}) endif() endforeach() endforeach() endforeach() endforeach() endforeach() endforeach() endforeach() endforeach() # Test that nanoprintf compiles when no flags are set. npf_compilation_c_test(npf_c_default_flags) ################ Static compilation test add_executable(npf_static tests/static_nanoprintf.c tests/static_main.c) target_link_options(npf_static PRIVATE ${nanoprintf_link_flags}) ################# Examples add_executable(use_npf_directly examples/use_npf_directly/your_project_nanoprintf.cc examples/use_npf_directly/main.cc) target_link_options(use_npf_directly PRIVATE ${nanoprintf_link_flags}) add_executable(wrap_npf examples/wrap_npf/your_project_printf.h examples/wrap_npf/your_project_printf.cc examples/wrap_npf/main.cc) target_link_options(wrap_npf PRIVATE ${nanoprintf_link_flags}) add_executable(npf_include_multiple tests/include_multiple.c) target_compile_options(npf_include_multiple PRIVATE ${nanoprintf_common_flags}) target_link_options(npf_include_multiple PRIVATE ${nanoprintf_link_flags}) ############### Unit tests set(unit_test_files nanoprintf.h tests/unit_parse_format_spec.cc tests/unit_binary.cc tests/unit_bufputc.cc tests/unit_ftoa_nan.cc tests/unit_ftoa_rev.cc tests/unit_ftoa_rev_08.cc tests/unit_ftoa_rev_16.cc tests/unit_ftoa_rev_32.cc tests/unit_ftoa_rev_64.cc tests/unit_utoa_rev.cc tests/unit_snprintf.cc tests/unit_snprintf_safe_empty.cc tests/unit_vpprintf.cc) npf_test(unit_tests_normal_sized_formatters "${unit_test_files}") target_compile_definitions(unit_tests_normal_sized_formatters PRIVATE NANOPRINTF_USE_ALT_FORM_FLAG=1 NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=0) if (NPF_32BIT) target_compile_definitions(unit_tests_normal_sized_formatters PRIVATE NANOPRINTF_32_BIT_TESTS) endif() npf_test(unit_tests_large_sized_formatters "${unit_test_files}") target_compile_definitions(unit_tests_large_sized_formatters PRIVATE NANOPRINTF_USE_ALT_FORM_FLAG=1 NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS=1) if (NPF_32BIT) target_compile_definitions(unit_tests_normal_sized_formatters PRIVATE NANOPRINTF_32_BIT_TESTS) endif()