diff dep/fmt/test/compile-error-test/CMakeLists.txt @ 343:1faa72660932

*: transfer back to cmake from autotools autotools just made lots of things more complicated than they should have and many things broke (i.e. translations)
author Paper <paper@paper.us.eu.org>
date Thu, 20 Jun 2024 05:56:06 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/fmt/test/compile-error-test/CMakeLists.txt	Thu Jun 20 05:56:06 2024 -0400
@@ -0,0 +1,241 @@
+# 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)
+
+# Check if user-defined literals are available
+include(CheckCXXSourceCompiles)
+set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
+check_cxx_source_compiles("
+  void operator\"\" _udl(long double);
+  int main() {}"
+  SUPPORTS_USER_DEFINED_LITERALS)
+set(CMAKE_REQUIRED_FLAGS )
+if (NOT SUPPORTS_USER_DEFINED_LITERALS)
+  set (SUPPORTS_USER_DEFINED_LITERALS OFF)
+endif ()
+
+# Make sure that compiler features detected in the header
+# match the features detected in CMake.
+if (SUPPORTS_USER_DEFINED_LITERALS)
+  set(supports_udl 1)
+else ()
+  set(supports_udl 0)
+endif ()
+expect_compile(udl-check "
+  #if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
+  # error
+  #endif
+")
+
+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()