1- cmake_minimum_required (VERSION 3.8...3.26 )
1+ cmake_minimum_required (VERSION 3.8...3.28 )
22
33# Fallback for using newer policies on CMake <3.12.
4- if (${CMAKE_VERSION} VERSION_LESS 3.12)
4+ if (${CMAKE_VERSION} VERSION_LESS 3.12)
55 cmake_policy (VERSION ${CMAKE_MAJOR_VERSION} .${CMAKE_MINOR_VERSION} )
6- endif ()
6+ endif ()
77
88# Determine if fmt is built as a subproject (using add_subdirectory)
99# or if it is the master project.
@@ -36,6 +36,12 @@ function(enable_module target)
3636 endif ()
3737endfunction ()
3838
39+ set (FMT_USE_CMAKE_MODULES FALSE )
40+ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND
41+ CMAKE_GENERATOR STREQUAL "Ninja" )
42+ set (FMT_USE_CMAKE_MODULES TRUE )
43+ endif ()
44+
3945# Adds a library compiled with C++20 module support.
4046# `enabled` is a CMake variables that specifies if modules are enabled.
4147# If modules are disabled `add_module_library` falls back to creating a
@@ -53,6 +59,7 @@ function(add_module_library name)
5359 if (NOT ${${AML_IF} })
5460 # Create a non-modular library.
5561 target_sources (${name} PRIVATE ${AML_FALLBACK} )
62+ set_target_properties (${name} PROPERTIES CXX_SCAN_FOR_MODULES OFF )
5663 return ()
5764 endif ()
5865
@@ -62,48 +69,53 @@ function(add_module_library name)
6269 target_compile_options (${name} PUBLIC -fmodules-ts)
6370 endif ()
6471
65- # `std` is affected by CMake options and may be higher than C++20.
66- get_target_property (std ${name} CXX_STANDARD)
67-
68- if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
69- set (pcms)
70- foreach (src ${sources} )
71- get_filename_component (pcm ${src} NAME_WE )
72- set (pcm ${pcm} .pcm)
73-
74- # Propagate -fmodule-file=*.pcm to targets that link with this library.
75- target_compile_options (
76- ${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR} /${pcm} )
77-
78- # Use an absolute path to prevent target_link_libraries prepending -l
79- # to it.
80- set (pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR} /${pcm} )
81- add_custom_command (
82- OUTPUT ${pcm}
83- COMMAND ${CMAKE_CXX_COMPILER}
84- -std=c++${std} -x c++-module --precompile -c
85- -o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR} /${src}
86- "-I$<JOIN:$<TARGET_PROPERTY:${name} ,INCLUDE_DIRECTORIES>,;-I>"
87- # Required by the -I generator expression above.
88- COMMAND_EXPAND_LISTS
89- DEPENDS ${src} )
90- endforeach ()
91-
92- # Add .pcm files as sources to make sure they are built before the library.
93- set (sources )
94- foreach (pcm ${pcms} )
95- get_filename_component (pcm_we ${pcm} NAME_WE )
96- set (obj ${pcm_we} .o)
97- # Use an absolute path to prevent target_link_libraries prepending -l.
98- set (sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR} /${obj} )
99- add_custom_command (
100- OUTPUT ${obj}
101- COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name} ,COMPILE_OPTIONS>
102- -c -o ${obj} ${pcm}
103- DEPENDS ${pcm} )
104- endforeach ()
105- endif ()
106- target_sources (${name} PRIVATE ${sources} )
72+ if (FMT_USE_CMAKE_MODULES)
73+ target_sources (${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
74+ FILES ${sources} )
75+ else ()
76+ # `std` is affected by CMake options and may be higher than C++20.
77+ get_target_property (std ${name} CXX_STANDARD)
78+
79+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
80+ set (pcms)
81+ foreach (src ${sources} )
82+ get_filename_component (pcm ${src} NAME_WE )
83+ set (pcm ${pcm} .pcm)
84+
85+ # Propagate -fmodule-file=*.pcm to targets that link with this library.
86+ target_compile_options (
87+ ${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR} /${pcm} )
88+
89+ # Use an absolute path to prevent target_link_libraries prepending -l
90+ # to it.
91+ set (pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR} /${pcm} )
92+ add_custom_command (
93+ OUTPUT ${pcm}
94+ COMMAND ${CMAKE_CXX_COMPILER}
95+ -std=c++${std} -x c++-module --precompile -c
96+ -o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR} /${src}
97+ "-I$<JOIN:$<TARGET_PROPERTY:${name} ,INCLUDE_DIRECTORIES>,;-I>"
98+ # Required by the -I generator expression above.
99+ COMMAND_EXPAND_LISTS
100+ DEPENDS ${src} )
101+ endforeach ()
102+
103+ # Add .pcm files as sources to make sure they are built before the library.
104+ set (sources )
105+ foreach (pcm ${pcms} )
106+ get_filename_component (pcm_we ${pcm} NAME_WE )
107+ set (obj ${pcm_we} .o)
108+ # Use an absolute path to prevent target_link_libraries prepending -l.
109+ set (sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR} /${obj} )
110+ add_custom_command (
111+ OUTPUT ${obj}
112+ COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name} ,COMPILE_OPTIONS>
113+ -c -o ${obj} ${pcm}
114+ DEPENDS ${pcm} )
115+ endforeach ()
116+ endif ()
117+ target_sources (${name} PRIVATE ${sources} )
118+ endif ()
107119endfunction ()
108120
109121include (CMakeParseArguments)
@@ -150,9 +162,10 @@ option(FMT_INSTALL "Generate the install target." ON)
150162option (FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT} )
151163option (FMT_FUZZ "Generate the fuzz target." OFF )
152164option (FMT_CUDA_TEST "Generate the cuda-test target." OFF )
153- option (FMT_OS "Include core requiring OS (Windows/Posix) " ON )
165+ option (FMT_OS "Include OS-specific APIs. " ON )
154166option (FMT_MODULE "Build a module instead of a traditional library." OFF )
155167option (FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF )
168+ option (FMT_UNICODE "Enable Unicode support." ON )
156169
157170if (FMT_TEST AND FMT_MODULE)
158171 # The tests require {fmt} to be compiled as traditional library
@@ -162,23 +175,23 @@ set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
162175if (FMT_SYSTEM_HEADERS)
163176 set (FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM )
164177endif ()
165- if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS" )
178+ if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS" )
166179 set (FMT_TEST OFF )
167180 message (STATUS "MSDOS is incompatible with gtest" )
168- endif ()
181+ endif ()
169182
170- # Get version from core .h
171- file (READ include /fmt/core .h core_h )
172- if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])" )
173- message (FATAL_ERROR "Cannot get FMT_VERSION from core .h." )
183+ # Get version from base .h
184+ file (READ include /fmt/base .h base_h )
185+ if (NOT base_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])" )
186+ message (FATAL_ERROR "Cannot get FMT_VERSION from base .h." )
174187endif ()
175188# Use math to skip leading zeros if any.
176189math (EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1} )
177190math (EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2} )
178191math (EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3} )
179192join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR} .${CPACK_PACKAGE_VERSION_MINOR} .
180193 ${CPACK_PACKAGE_VERSION_PATCH} )
181- message (STATUS "Version : ${FMT_VERSION} " )
194+ message (STATUS "{fmt} version : ${FMT_VERSION} " )
182195
183196message (STATUS "Build type: ${CMAKE_BUILD_TYPE} " )
184197
@@ -225,7 +238,13 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
225238 endif ()
226239 if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
227240 set (PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
228- -Wnull-dereference -Wduplicated-cond)
241+ -Wduplicated-cond)
242+ # Workaround for GCC regression
243+ # [12/13/14/15 regression] New (since gcc 12) false positive null-dereference in vector.resize
244+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860
245+ if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
246+ set (PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnull-dereference)
247+ endif ()
229248 endif ()
230249 set (WERROR_FLAG -Werror)
231250endif ()
@@ -274,20 +293,21 @@ function(add_headers VAR)
274293endfunction ()
275294
276295# Define the fmt library, its includes and the needed defines.
277- add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
296+ add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
278297 format-inl.h os.h ostream.h printf.h ranges.h std.h
279298 xchar.h)
280299set (FMT_SOURCES src/format.cc)
281- if (FMT_OS)
282- set (FMT_SOURCES ${FMT_SOURCES} src/os.cc)
283- endif ()
284300
285301add_module_library(fmt src/fmt.cc FALLBACK
286- ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst
302+ ${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
287303 IF FMT_MODULE)
288304add_library (fmt::fmt ALIAS fmt)
289305if (FMT_MODULE)
290306 enable_module(fmt)
307+ elseif (FMT_OS)
308+ target_sources (fmt PRIVATE src/os.cc)
309+ else ()
310+ target_compile_definitions (fmt PRIVATE FMT_OS=0)
291311endif ()
292312
293313if (FMT_WERROR)
@@ -303,7 +323,7 @@ else ()
303323 message (WARNING "Feature cxx_std_11 is unknown for the CXX compiler" )
304324endif ()
305325
306- target_include_directories (fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
326+ target_include_directories (fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE PUBLIC
307327 $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR} /include >
308328 $<INSTALL_INTERFACE:${FMT_INC_DIR} >)
309329
@@ -312,7 +332,15 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
312332set_target_properties (fmt PROPERTIES
313333 VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
314334 PUBLIC_HEADER "${FMT_HEADERS} "
315- DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX} " )
335+ DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX} "
336+
337+ # Workaround for Visual Studio 2017:
338+ # Ensure the .pdb is created with the same name and in the same directory
339+ # as the .lib. Newer VS versions already do this by default, but there is no
340+ # harm in setting it for those too. Ignored by other generators.
341+ COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR} "
342+ COMPILE_PDB_NAME "fmt"
343+ COMPILE_PDB_NAME_DEBUG "fmt${FMT_DEBUG_POSTFIX} " )
316344
317345# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
318346# property because it's not set by default.
@@ -326,15 +354,26 @@ if (BUILD_SHARED_LIBS)
326354endif ()
327355if (FMT_SAFE_DURATION_CAST)
328356 target_compile_definitions (fmt PUBLIC FMT_SAFE_DURATION_CAST)
329- endif ()
357+ endif ()
330358
331359add_library (fmt-header-only INTERFACE )
332360add_library (fmt::fmt-header-only ALIAS fmt-header-only)
333361
362+ if (NOT MSVC )
363+ # Unicode is always supported on compilers other than MSVC.
364+ elseif (FMT_UNICODE)
365+ # Unicode support requires compiling with /utf-8.
366+ target_compile_options (fmt PUBLIC $<$<AND :$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC >>:/utf-8>)
367+ target_compile_options (fmt-header-only INTERFACE $<$<AND :$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC >>:/utf-8>)
368+ else ()
369+ target_compile_definitions (fmt PUBLIC FMT_UNICODE=0)
370+ endif ()
371+
334372target_compile_definitions (fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
335373target_compile_features (fmt-header-only INTERFACE cxx_std_11)
336374
337- target_include_directories (fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
375+ target_include_directories (fmt-header-only
376+ ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE INTERFACE
338377 $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR} /include >
339378 $<INSTALL_INTERFACE:${FMT_INC_DIR} >)
340379
@@ -379,30 +418,77 @@ if (FMT_INSTALL)
379418
380419 set (INSTALL_TARGETS fmt fmt-header-only)
381420
421+ set (INSTALL_FILE_SET)
422+ if (FMT_USE_CMAKE_MODULES)
423+ set (INSTALL_FILE_SET FILE_SET fmt DESTINATION "${FMT_INC_DIR} /fmt" )
424+ endif ()
425+
382426 # Install the library and headers.
383- install (TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
427+ install (TARGETS ${INSTALL_TARGETS}
428+ COMPONENT fmt-core
429+ EXPORT ${targets_export_name}
384430 LIBRARY DESTINATION ${FMT_LIB_DIR}
385431 ARCHIVE DESTINATION ${FMT_LIB_DIR}
386432 PUBLIC_HEADER DESTINATION "${FMT_INC_DIR} /fmt"
387- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )
433+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
434+ ${INSTALL_FILE_SET} )
388435
389436 # Use a namespace because CMake provides better diagnostics for namespaced
390437 # imported targets.
391438 export (TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
392439 FILE ${PROJECT_BINARY_DIR} /${targets_export_name} .cmake)
393440
394441 # Install version, config and target files.
395- install (
396- FILES ${project_config} ${version_config }
397- DESTINATION ${FMT_CMAKE_DIR} )
442+ install (FILES ${project_config} ${version_config}
443+ DESTINATION ${FMT_CMAKE_DIR }
444+ COMPONENT fmt-core )
398445 install (EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
399- NAMESPACE fmt::)
446+ NAMESPACE fmt::
447+ COMPONENT fmt-core)
400448
401- install (FILES "${pkgconfig} " DESTINATION "${FMT_PKGCONFIG_DIR} " )
449+ install (FILES "${pkgconfig} " DESTINATION "${FMT_PKGCONFIG_DIR} "
450+ COMPONENT fmt-core)
402451endif ()
403452
453+ function (add_doc_target)
454+ find_program (DOXYGEN doxygen
455+ PATHS "$ENV{ProgramFiles} /doxygen/bin"
456+ "$ENV{ProgramFiles\( x86\) }/doxygen/bin" )
457+ if (NOT DOXYGEN)
458+ message (STATUS "Target 'doc' disabled because doxygen not found" )
459+ return ()
460+ endif ()
461+
462+ find_program (MKDOCS mkdocs)
463+ if (NOT MKDOCS)
464+ message (STATUS "Target 'doc' disabled because mkdocs not found" )
465+ return ()
466+ endif ()
467+
468+ set (sources )
469+ foreach (source api.md index.md syntax.md get -started.md fmt.css fmt.js)
470+ set (sources ${sources} doc /${source} )
471+ endforeach ()
472+
473+ add_custom_target (
474+ doc
475+ COMMAND
476+ ${CMAKE_COMMAND}
477+ -E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR} /support/python
478+ ${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR} /support/mkdocs.yml
479+ # MkDocs requires the site dir to be outside of the doc dir.
480+ --site-dir ${CMAKE_CURRENT_BINARY_DIR} /doc -html
481+ --no -directory -urls
482+ SOURCES ${sources} )
483+
484+ include (GNUInstallDirs)
485+ install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} /doc -html/
486+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR} /doc /fmt
487+ COMPONENT fmt-doc OPTIONAL )
488+ endfunction ()
489+
404490if (FMT_DOC)
405- add_subdirectory ( doc )
491+ add_doc_target( )
406492endif ()
407493
408494if (FMT_TEST)
@@ -432,13 +518,12 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
432518 string (REPLACE "*" ".*" line "${line} " )
433519 set (ignored_files ${ignored_files} "${line} $" "${line} /" )
434520 endforeach ()
435- set (ignored_files ${ignored_files}
436- /.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
521+ set (ignored_files ${ignored_files} /.git /build /doxyxml .vagrant)
437522
438523 set (CPACK_SOURCE_GENERATOR ZIP)
439524 set (CPACK_SOURCE_IGNORE_FILES ${ignored_files} )
440525 set (CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION} )
441526 set (CPACK_PACKAGE_NAME fmt)
442- set (CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR} /README.rst )
527+ set (CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR} /README.md )
443528 include (CPack)
444529endif ()
0 commit comments