Generate CMake export set regardless of FMT_INSTALL

export() was scoped inside if (FMT_INSTALL), so projects that pull in
fmt via add_subdirectory()/FetchContent without installing it had no
way to get the exported fmt::* targets. If such a project tries to
export its own targets that depend on fmt, CMake fails with "target
... requires target fmt that is not in any export set".

Hoist the target list/export name and the export() call itself out of
the FMT_INSTALL guard so the build-tree export file is always
generated; the install()-only pieces (config/version files, pkgconfig,
install(EXPORT ...)) stay behind the guard and installed behavior is
unchanged.

Fixes #4806
This commit is contained in:
flink 2026-07-15 03:52:46 -04:00
parent a79df4504c
commit b45847a867
3 changed files with 31 additions and 17 deletions

View File

@ -441,6 +441,16 @@ add_library(fmt::fmt-c ALIAS fmt-c)
set_target_properties(fmt-c PROPERTIES PUBLIC_HEADER include/fmt/fmt-c.h)
# Install targets.
set(targets_export_name fmt-targets)
set(INSTALL_TARGETS fmt fmt-header-only fmt-c)
if (FMT_MODULE)
list(APPEND INSTALL_TARGETS fmt-module)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION ${FMT_INC_DIR}/fmt)
endif ()
endif ()
if (FMT_INSTALL)
include(CMakePackageConfigHelpers)
set_verbose(
@ -454,7 +464,6 @@ if (FMT_INSTALL)
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets)
set_verbose(
FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
@ -485,15 +494,6 @@ if (FMT_INSTALL)
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in ${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only fmt-c)
if (FMT_MODULE)
list(APPEND INSTALL_TARGETS fmt-module)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION ${FMT_INC_DIR}/fmt)
endif ()
endif ()
# Install the library and headers.
install(
TARGETS ${INSTALL_TARGETS}
@ -504,13 +504,6 @@ if (FMT_INSTALL)
PUBLIC_HEADER DESTINATION ${FMT_INC_DIR}/fmt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ${INSTALL_FILE_SET})
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(
TARGETS ${INSTALL_TARGETS}
NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(
FILES ${project_config} ${version_config}
@ -528,6 +521,15 @@ if (FMT_INSTALL)
COMPONENT fmt_core)
endif ()
# Generate an export file in the build tree so that fmt can be used by
# add_subdirectory() or FetchContent without being installed. Use a
# namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(
TARGETS ${INSTALL_TARGETS}
NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
function (add_doc_target)
find_program(DOXYGEN doxygen PATHS "$ENV{ProgramFiles}/doxygen/bin"
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")

View File

@ -15,3 +15,12 @@ if (TARGET fmt::fmt-header-only)
target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_link_libraries(header-only-test fmt::fmt-header-only)
endif ()
# Test that a target publicly depending on fmt can itself be exported even
# though fmt is added via add_subdirectory and FMT_INSTALL is off (#4806).
add_library(export-test STATIC export-lib.cc)
target_link_libraries(export-test PUBLIC fmt::fmt)
export(
TARGETS export-test
NAMESPACE test::
FILE ${CMAKE_CURRENT_BINARY_DIR}/export-test-targets.cmake)

View File

@ -0,0 +1,3 @@
#include "fmt/base.h"
void greet(const char* name) { fmt::print("Hello, {}!\n", name); }