简体   繁体   中英

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

I have a library that can both be used as an header-only library and as a traditional library. To enable this optional-header-only functionality, the library includes .cpp source files if compiled in header-only mode. Example:

// 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

When the user includes Vector.hpp in one of his/her projects, if LIBRARY_HEADERONLY is defined, the implementation source files will be included right after the header files. Careful usage of the inline keyword is applied to avoid multiple definitions.

If LIBRARY_HEADERONLY is undefined, the .cpp files will be compiled and the library will have to be linked.


My build system of choice is CMake .

Using a CMake flag, the user can define or undefine LIBRARY_HEADERONLY .

The CMakeLists.txt file is similar to this:

# (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 .)

Unfortunately, the CMake-generated makefile always compiles the .cpp files, even when header-only mode is enabled and the target is HEADER_ONLY_TARGET .

How can I prevent the CMake-generated makefile from compiling the source files in header-only mode?

Note that IDEs reliant on CMake-generated output, such as Qt Creator, should display both the header and source files as part of the project.

If I don't glob any source file, but only the .hpp header files, CMake will complain that no source files were selected for a library target, and IDEs that rely on CMake files will not display any item.

Try setting the source files' HEADER_FILE_ONLY property to prevent them from building, eg:

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

Also see the documentation .

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)

Client code just does:

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

See also Transitive Usage Requirements and all the rest of those CMake manuals regarding creating packages, etc.

An approach of defining all library types and letting the consumer choose between them is outlined in:

Opt-in header-only libraries with CMake

Something like:

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

So that the consumer makes the choice of which to link to:

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

Why do you not separate the GLOB for HEADERS and SRC files? You could add a dummy.cpp (a empty .cpp file) for creating header-only libraries. I mean:

# 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 .)

Anyway, CMake 3.XX gives you the chance for creating INTERFACE libraries (header-only ones) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM