阻止 optional-header-only 库的 CMake-generated makefile 在 header-only 模式下编译源文件

Preventing CMake-generated makefile for optional-header-only library from compiling source files in header-only mode

我有一个图书馆,既可以用作 header-only 图书馆,也可以用作传统图书馆。要启用此可选的 header-only 功能,如果在 header-only 模式下编译,库将包含 .cpp 个源文件。示例:

// Vector.hpp
// (Module file), intended to be included manually by the user
#ifndef LIBRARY_MODULE_VECTOR
#define LIBRARY_MODULE_VECTOR

#include "Library/Vector/Inc/Vector2.hpp"
#include "Library/Vector/Inc/Vector3.hpp"
#include "Library/Vector/Inc/VectorUtils.hpp"

#if defined(LIBRARY_HEADERONLY)
    #include "Library/Vector/Src/Vector2.cpp"
    #include "Library/Vector/Src/Vector3.cpp"
    #include "Library/Vector/Src/VectorUtils.cpp"
#endif

#endif

当用户在 his/her 项目之一中包含 Vector.hpp 时,如果定义了 LIBRARY_HEADERONLY,则将在 header 文件之后立即包含实现源文件。小心使用 inline 关键字以避免多次定义。

如果 LIBRARY_HEADERONLY 未定义,将编译 .cpp 个文件并且必须链接库。


我选择的构建系统是 CMake.

使用 CMake 标志,用户可以定义或取消定义 LIBRARY_HEADERONLY

CMakeLists.txt文件类似这样:

# (not shown) set flag and cache variables...

# Include library directory
include_directories("./${INCLUDE_DIRECTORY}")

# Glob all library header/source files
file(GLOB_RECURSE SRC_LIST "${INC_DIR}/*" "${SRC_DIR}/*")

# (Not shown) Check if header-only mode is enabled
# (from now on we assume header-only mode is enabled and that
# LIBRARY_HEADERONLY is defined)

# Use all source/header files as a library target
add_library(HEADER_ONLY_TARGET STATIC ${SRC_LIST})
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
install(DIRECTORY ${INC_DIR} DESTINATION .)

不幸的是,CMake-generated makefile 总是 编译 .cpp 文件,即使启用 header-only 模式并且目标是 HEADER_ONLY_TARGET.

如何防止 CMake-generated makefile 在 header-only 模式下编译源文件?

请注意,依赖于 CMake-generated 输出的 IDE,例如 Qt Creator,应该将 header 和源文件 作为项目的一部分显示。

如果我不通配任何源文件,而只通配 .hpp header 文件,CMake 会抱怨没有为库目标和依赖 CMake 的 IDE 选择源文件文件将不会显示任何项目。

if (Create_Header_Only)
    add_library(TargetName INTERFACE)
    # Clients will build with -DLIBRARY_HEADERONLY
    target_compile_definitions(TargetName INTERFACE LIBRARY_HEADERONLY)
else()
    add_library(TargetName STATIC thesource.cpp)
endif()

# Clients will build with -Iinclude
target_include_directories(TargetName INTERFACE include)

客户端代码只是:

add_executable(mine main.cpp)
target_link_libraries(mine TargetName)

另请参阅 Transitive Usage Requirements 以及所有其他关于创建包等的 CMake 手册

定义所有库类型并让消费者在它们之间进行选择的方法概述于:

Opt-in header-only libraries with CMake

类似于:

add_library(lib_shared SHARED ...)
add_library(lib_shared STATIC ...)
add_library(lib_iface INTERFACE)

以便消费者选择 link 到:

# target_link_libraries(consumer lib_static)
# target_link_libraries(consumer lib_shared)
target_link_libraries(consumer lib_iface)

为什么不将 HEADERS 和 SRC 文件的 GLOB 分开?您可以添加一个 dummy.cpp(一个空的 .cpp 文件)来创建 header-only 库。我的意思是:

# Glob all library header files
file(GLOB_RECURSE HEADER_ONLY_LIST "${INC_DIR}/*.hpp")

# Glob all library src files
file(GLOB_RECURSE SRC_ONLY_LIST "${SRC_DIR}/*.cpp")

# Check if LIBRARY_HEADERONLY is defined, so you can filter the target files
if(LIBRARY_HEADERONLY)
    set(SRC_LIST ${HEADER_ONLY_LIST} dummy.cpp) # I don't know if it's needed to add a dummy.cpp file with your set_target_properties(HEADER_ONLY_TARGET ...)
else()
    set(SRC_LIST ${HEADER_ONLY_LIST} ${SRC_ONLY_LIST})
endif(LIBRARY_HEADERONLY)

# Use all source/header files as a library target
add_library(HEADER_ONLY_TARGET STATIC ${SRC_LIST})
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
install(DIRECTORY ${INC_DIR} DESTINATION .)

无论如何,CMake 3.X.X 为您提供了创建 INTERFACE libraries (header-only ones).

的机会

尝试设置源文件的 HEADER_FILE_ONLY 属性 以防止构建它们,例如:

if (LIBRARY_HEADERONLY)
    set_source_files_properties(${SRC_LIST} PROPERTIES HEADER_FILE_ONLY 1)
    ...
endif()

另见 documentation