diff --git a/CMakeLists.txt b/CMakeLists.txt index b2985a7d0a5d..3b5ecffb642a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,24 @@ option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) option(FMT_FUZZ "Generate the fuzz target." OFF) option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) option(FMT_OS "Include core requiring OS (Windows/Posix) " ON) +option(FMT_MODULE "Build a module instead of a traditional library." OFF) + +set(FMT_CAN_MODULE OFF) +if (CMAKE_CXX_STANDARD GREATER_EQUAL 20) + if (MSVC AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30035)) # msvc 16.10-pre3 + set(FMT_CAN_MODULE ON) + endif () +endif () +if (FMT_CAN_MODULE) + if (MSVC) + set(FMT_BMI "${CMAKE_BINARY_DIR}/fmt.ifc") + set(BMI_BUILD_FLAGS /interface /ifcOutput ${FMT_BMI}) + set(BMI_USE_FLAGS /reference "\"fmt=${FMT_BMI}\"") + endif () +else () + set(FMT_MODULE OFF) + message(STATUS "The compiler doesn't support modules.") +endif () # Get version from core.h file(READ include/fmt/core.h core_h) @@ -188,7 +206,11 @@ endfunction() # Define the fmt library, its includes and the needed defines. add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h format-inl.h locale.h os.h ostream.h printf.h ranges.h) -if (FMT_OS) + +set(FMT_INTERFACE_UNIT src/fmt.cc) +if (FMT_MODULE) + set(FMT_SOURCES ${FMT_INTERFACE_UNIT}) +elseif (FMT_OS) set(FMT_SOURCES src/format.cc src/os.cc) else() set(FMT_SOURCES src/format.cc) @@ -214,6 +236,9 @@ endif () if (FMT_PEDANTIC) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) endif () +if (FMT_MODULE) + target_compile_options(fmt PRIVATE ${BMI_BUILD_FLAGS} INTERFACE ${BMI_USE_FLAGS}) +endif () target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) @@ -257,6 +282,20 @@ target_include_directories(fmt-header-only INTERFACE $ $) +if (FMT_CAN_MODULE AND FMT_TEST) + set (tstmod "modfmt") + add_library(${tstmod} STATIC ${FMT_INTERFACE_UNIT} ${FMT_HEADERS}) + target_compile_options(${tstmod} PRIVATE ${BMI_BUILD_FLAGS} INTERFACE ${BMI_USE_FLAGS}) + target_compile_features(${tstmod} PUBLIC ${FMT_REQUIRED_FEATURES}) + target_include_directories(${tstmod} PUBLIC + $ + $) + set_property(TARGET ${tstmod} APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${FMT_BMI}) + set_source_files_properties(${FMT_BMI} PROPERTIES GENERATED ON) + set_target_properties(${tstmod} PROPERTIES + LABEL ${CMAKE_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${tstmod}${CMAKE_STATIC_LIBRARY_SUFFIX}) +endif () + # Install targets. if (FMT_INSTALL) include(CMakePackageConfigHelpers) @@ -296,7 +335,11 @@ if (FMT_INSTALL) ${project_config} INSTALL_DESTINATION ${FMT_CMAKE_DIR}) - set(INSTALL_TARGETS fmt fmt-header-only) + if (FMT_MODULE) + set(INSTALL_TARGETS fmt) + else() + set(INSTALL_TARGETS fmt fmt-header-only) + endif() # Install the library and headers. install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f1dcf16a3b92..eb2487e8e40a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,7 +2,7 @@ add_subdirectory(gtest) set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc) add_library(test-main STATIC ${TEST_MAIN_SRC}) -target_link_libraries(test-main gtest fmt) +target_link_libraries(test-main PUBLIC gtest PRIVATE fmt) include(CheckCXXCompilerFlag) @@ -29,17 +29,27 @@ endfunction() # Adds a test. # Usage: add_fmt_test(name srcs...) function(add_fmt_test name) - cmake_parse_arguments(ADD_FMT_TEST "HEADER_ONLY" "" "" ${ARGN}) + set(options "HEADER_ONLY" "MODULE" "COMPILE_FAIL") + cmake_parse_arguments(ADD_FMT_TEST "${options}" "" "" ${ARGN}) set(sources ${name}.cc ${ADD_FMT_TEST_UNPARSED_ARGUMENTS}) - set(libs test-main) if (ADD_FMT_TEST_HEADER_ONLY) set(sources ${sources} ${TEST_MAIN_SRC} ../src/os.cc) set(libs gtest fmt-header-only) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables) endif () + elseif (ADD_FMT_TEST_MODULE) + set(libs test-main ${tstmod}) + get_target_property(bmi-dep ${tstmod} LABEL) + set_source_files_properties(${name}.cc PROPERTIES OBJECT_DEPENDS ${bmi-dep}) + else () + set(libs test-main fmt) + endif () + if (ADD_FMT_TEST_COMPILE_FAIL) + list (REMOVE_ITEM libs test-main) endif () + add_fmt_executable(${name} ${sources}) target_link_libraries(${name} ${libs}) @@ -50,7 +60,19 @@ function(add_fmt_test name) if (FMT_WERROR) target_compile_options(${name} PRIVATE ${WERROR_FLAG}) endif () - add_test(NAME ${name} COMMAND ${name}) + + if (ADD_FMT_TEST_COMPILE_FAIL) + set_target_properties(${name} PROPERTIES + EXCLUDE_FROM_ALL TRUE + EXCLUDE_FROM_DEFAULT_BUILD TRUE) + add_test(NAME ${name} COMMAND ${CMAKE_COMMAND} + --build . + --target ${name} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE) + else () + add_test(NAME ${name} COMMAND ${name}) + endif () endfunction() add_fmt_test(args-test) @@ -73,6 +95,11 @@ add_fmt_test(printf-test) add_fmt_test(ranges-test) add_fmt_test(scan-test) +if (FMT_CAN_MODULE) + add_fmt_test(module-test MODULE) + add_fmt_test(module-compile-fail MODULE COMPILE_FAIL) +endif () + if (NOT MSVC) # FMT_ENFORCE_COMPILE_STRING is not supported under MSVC due to compiler bugs. add_fmt_test(enforce-checks-test) diff --git a/test/module-compile-fail.cc b/test/module-compile-fail.cc new file mode 100644 index 000000000000..1f73bf3457a1 --- /dev/null +++ b/test/module-compile-fail.cc @@ -0,0 +1,13 @@ +import fmt; + +// the detail namespace must be invisible +using namespace fmt::detail; + +#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 192930035 +// bug in msvc 16.10-pre3: +// the namespace is visible even when it is neither +// implicitly nor explicitly exported +# error namespace 'fmt::detail' is visible! +#endif + +int main() {} diff --git a/test/module-test.cc b/test/module-test.cc new file mode 100644 index 000000000000..695b8d83721c --- /dev/null +++ b/test/module-test.cc @@ -0,0 +1,35 @@ +// Formatting library for C++ - module tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. +// +// Copyright (c) 2021 - present, Daniela Engert +// All Rights Reserved +// {fmt} module. + +#include "gtest-extra.h" // EXPECT_WRITE +import fmt; + +TEST(module_test, namespace) { + using namespace fmt; + ASSERT_TRUE(true); +} + +TEST(module_test, macros) { + bool macro_leaked = false; +#if defined(FMT_CORE_H_) || defined(FMT_FORMAT_H) + macro_leaked = true; +#endif +#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 192930035 +// bug in msvc 16.10-pre3: +// include guard macros leak from BMI + macro_leaked = false; +#endif + EXPECT_FALSE(macro_leaked); +} + +TEST(module_test, basic) { + EXPECT_EQ("42", fmt::to_string(42)); +}