| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- # Test if compile errors are produced where necessary.
- cmake_minimum_required(VERSION 3.8...3.25)
- project(compile-error-test CXX)
- set(fmt_headers "
- #include <fmt/format.h>
- #include <fmt/xchar.h>
- #include <fmt/ostream.h>
- #include <iostream>
- ")
- set(error_test_names "")
- set(non_error_test_content "")
- # For error tests (we expect them to produce compilation error):
- # * adds a name of test into `error_test_names` list
- # * generates a single source file (with the same name) for each test
- # For non-error tests (we expect them to compile successfully):
- # * adds a code segment as separate function to `non_error_test_content`
- function (expect_compile name code_fragment)
- cmake_parse_arguments(EXPECT_COMPILE "ERROR" "" "" ${ARGN})
- string(MAKE_C_IDENTIFIER "${name}" test_name)
- if (EXPECT_COMPILE_ERROR)
- file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/${test_name}.cc" "
- ${fmt_headers}
- void ${test_name}() {
- ${code_fragment}
- }
- ")
- set(error_test_names_copy "${error_test_names}")
- list(APPEND error_test_names_copy "${test_name}")
- set(error_test_names "${error_test_names_copy}" PARENT_SCOPE)
- else()
- set(non_error_test_content "
- ${non_error_test_content}
- void ${test_name}() {
- ${code_fragment}
- }" PARENT_SCOPE)
- endif()
- endfunction ()
- # Generates a source file for non-error test with `non_error_test_content` and
- # CMake project file with all error and single non-error test targets.
- function (run_tests)
- set(cmake_targets "")
- foreach(test_name IN LISTS error_test_names)
- set(cmake_targets "
- ${cmake_targets}
- add_library(test-${test_name} ${test_name}.cc)
- target_link_libraries(test-${test_name} PRIVATE fmt::fmt)
- ")
- endforeach()
- file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/non_error_test.cc" "
- ${fmt_headers}
- ${non_error_test_content}
- ")
- set(cmake_targets "
- ${cmake_targets}
- add_library(non-error-test non_error_test.cc)
- target_link_libraries(non-error-test PRIVATE fmt::fmt)
- ")
- file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/CMakeLists.txt" "
- cmake_minimum_required(VERSION 3.8...3.25)
- project(tests CXX)
- add_subdirectory(${FMT_DIR} fmt)
- ${cmake_targets}
- ")
- set(build_directory "${CMAKE_CURRENT_BINARY_DIR}/test/build")
- file(MAKE_DIRECTORY "${build_directory}")
- execute_process(
- COMMAND
- "${CMAKE_COMMAND}"
- "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
- "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
- "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
- "-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
- "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}"
- "-DFMT_DIR=${FMT_DIR}"
- "${CMAKE_CURRENT_BINARY_DIR}/test"
- WORKING_DIRECTORY "${build_directory}"
- RESULT_VARIABLE result_var
- OUTPUT_VARIABLE output_var
- ERROR_VARIABLE output_var)
- if (NOT result_var EQUAL 0)
- message(FATAL_ERROR "Unable to configure:\n${output_var}")
- endif()
- foreach(test_name IN LISTS error_test_names)
- execute_process(
- COMMAND
- "${CMAKE_COMMAND}" --build "${build_directory}" --target "test-${test_name}"
- WORKING_DIRECTORY "${build_directory}"
- RESULT_VARIABLE result_var
- OUTPUT_VARIABLE output_var
- ERROR_QUIET)
- if (result_var EQUAL 0)
- message(SEND_ERROR "No compile error for \"${test_name}\":\n${output_var}")
- endif ()
- endforeach()
- execute_process(
- COMMAND
- "${CMAKE_COMMAND}" --build "${build_directory}" --target "non-error-test"
- WORKING_DIRECTORY "${build_directory}"
- RESULT_VARIABLE result_var
- OUTPUT_VARIABLE output_var
- ERROR_VARIABLE output_var)
- if (NOT result_var EQUAL 0)
- message(SEND_ERROR "Compile error for combined non-error test:\n${output_var}")
- endif ()
- endfunction ()
- # Check if the source file skeleton compiles.
- expect_compile(check "")
- expect_compile(check-error "compilation_error" ERROR)
- # Formatting a wide character with a narrow format string is forbidden.
- expect_compile(wide-character-narrow-format-string "fmt::format(L\"{}\", L'a');")
- expect_compile(wide-character-narrow-format-string-error "fmt::format(\"{}\", L'a');" ERROR)
- # Formatting a wide string with a narrow format string is forbidden.
- expect_compile(wide-string-narrow-format-string "fmt::format(L\"{}\", L\"foo\");")
- expect_compile(wide-string-narrow-format-string-error "fmt::format(\"{}\", L\"foo\");" ERROR)
- # Formatting a narrow string with a wide format string is forbidden because
- # mixing UTF-8 with UTF-16/32 can result in an invalid output.
- expect_compile(narrow-string-wide-format-string "fmt::format(L\"{}\", L\"foo\");")
- expect_compile(narrow-string-wide-format-string-error "fmt::format(L\"{}\", \"foo\");" ERROR)
- expect_compile(cast-to-string "
- struct S {
- operator std::string() const { return std::string(); }
- };
- fmt::format(\"{}\", std::string(S()));
- ")
- expect_compile(cast-to-string-error "
- struct S {
- operator std::string() const { return std::string(); }
- };
- fmt::format(\"{}\", S());
- " ERROR)
- # Formatting a function
- expect_compile(format-function "
- void (*f)();
- fmt::format(\"{}\", fmt::ptr(f));
- ")
- expect_compile(format-function-error "
- void (*f)();
- fmt::format(\"{}\", f);
- " ERROR)
- # Formatting an unformattable argument should always be a compile time error
- expect_compile(format-lots-of-arguments-with-unformattable "
- struct E {};
- fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E());
- " ERROR)
- expect_compile(format-lots-of-arguments-with-function "
- void (*f)();
- fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f);
- " ERROR)
- if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
- # Compile-time argument type check
- expect_compile(format-string-number-spec "
- #ifdef FMT_HAS_CONSTEVAL
- fmt::format(\"{:d}\", 42);
- #endif
- ")
- expect_compile(format-string-number-spec-error "
- #ifdef FMT_HAS_CONSTEVAL
- fmt::format(\"{:d}\", \"I am not a number\");
- #else
- #error
- #endif
- " ERROR)
- expect_compile(print-string-number-spec-error "
- #ifdef FMT_HAS_CONSTEVAL
- fmt::print(\"{:d}\", \"I am not a number\");
- #else
- #error
- #endif
- " ERROR)
- expect_compile(print-stream-string-number-spec-error "
- #ifdef FMT_HAS_CONSTEVAL
- fmt::print(std::cout, \"{:d}\", \"I am not a number\");
- #else
- #error
- #endif
- " ERROR)
- # Compile-time argument name check
- expect_compile(format-string-name "
- #if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
- using namespace fmt::literals;
- fmt::print(\"{foo}\", \"foo\"_a=42);
- #endif
- ")
- expect_compile(format-string-name-error "
- #if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
- using namespace fmt::literals;
- fmt::print(\"{foo}\", \"bar\"_a=42);
- #else
- #error
- #endif
- " ERROR)
- endif ()
- # Run all tests
- run_tests()
|