CMake: библиотека только для заголовков со сгенерированными файлами

У меня есть библиотека, которая должна содержать некоторые постоянные данные, введенные из содержимого не исходных файлов (в данном случае, код шейдера OpenGL). Чтобы достичь этого, я использую add_custom_command() для создания включаемых файлов, которые я могу затем #include добавить в свой код для инициализации константных статических переменных.

Это отлично работает с обычными библиотеками (статическими или общими), но теперь я хотел бы сделать свою библиотеку только заголовком. Способность C++ позволять статическим методам возвращать статические данные без риска дублирования этих данных в каждой единице трансляции («магическая статика») делает это возможным.

Однако проблема заключается в том, что CMake, по-видимому, предполагает, что библиотека INTERFACE (которая является функцией CMake, которую я использую для создания библиотек только для заголовков) не нуждается в сборке, что в данном случае неверно.

(Я понимаю, что моя библиотека не обязана быть только заголовком. В данном конкретном случае причина, по которой я этого хочу, заключается в том, что я хотел бы, чтобы библиотека, выполняющая OpenGL, оставалась независимой от какой-либо конкретной библиотеки привязки [ например, GLEW или GLee или новичок glbinding]. Оставив в моей библиотеке только заголовок, я могу оставить этот выбор пользователю — все, что ему нужно сделать, это #include заголовок библиотеки привязки перед моим.)

Кто-нибудь видит способ заставить CMake запускать пользовательские команды, генерирующие заголовки, самое позднее, когда создается потребительский проект?

РЕДАКТИРОВАТЬ: я только что понял, что мог бы получить «лучшее из обоих миров», если бы моя библиотека оставалась статической, но при этом сохранялся весь мой код кроме для постоянных данных в файлах заголовков. Таким образом, по-прежнему не будет необходимости выбирать конкретную библиотеку привязки OpenGL. Тем не менее, у библиотеки все еще есть преимущества только для заголовка - простота использования для одного - поэтому я оставляю свой вопрос открытым.

РЕДАКТИРОВАТЬ № 2: Вот соответствующая часть моего файла CMakeLists.txt (я удалил только зависимости библиотеки - все только заголовки - с конца):

set(SHADER_FILES "src/vertex.glsl" "src/fragment.glsl")

add_library(libGPCGUIGLRenderer INTERFACE)
target_sources(libGPCGUIGLRenderer INTERFACE ${SHADER_FILES})

target_include_directories(libGPCGUIGLRenderer BEFORE
  INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

# Embed shader files

source_group("Shader files" FILES ${SHADER_FILES})

set(GENERATED "${CMAKE_CURRENT_BINARY_DIR}/generated")
target_include_directories(libGPCGUIGLRenderer INTERFACE ${GENERATED})

# Find the GPC Bin2C utility
find_package(GPCBin2C REQUIRED)

# Add a custom target and a dependency for each shader file    
foreach(shader ${SHADER_FILES})
  get_filename_component(name "${shader}" NAME)
  set(shader_header "${GENERATED}/${name}.h")
  add_custom_command(
    OUTPUT ${shader_header}
    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${shader}
    COMMAND GPCBin2C --input=${CMAKE_CURRENT_SOURCE_DIR}/${shader} --output=${shader_header}
  )
  target_sources(libGPCGUIGLRenderer INTERFACE ${shader_header})
endforeach()

person JPNotADragon    schedule 10.02.2015    source источник


Ответы (3)


У меня сработало создание статической библиотеки с заголовками в качестве единственных источников. Это, конечно, только обходной путь.

  • Создание статической библиотеки только с файлами заголовков приводит к пустой библиотеке. Мой говорит !<arch> как единственный контент.
  • CMake автоматически установит правильные зависимости для подкаталогов.
  • Поскольку все источники являются заголовками, вам нужно указать CMake, какой язык компоновщика следует использовать.

Код:

set(OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated_include")
add_custom_command(
    OUTPUT "${OUTDIR}/outfile.h"
    # Replace the next two lines with a proper generating script.
    COMMAND mkdir -p ${OUTDIR}
    COMMAND touch ${OUTDIR}/outfile.h
)

# Note, I am only adding header files to the library.
add_library(generated-headers STATIC 
    "${OUTDIR}/outfile.h"
)
set_target_properties(generated-headers
    PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(generated-headers PUBLIC ${OUTDIR})

Используйте в других каталогах, как это:

# In any other directory of the same CMake project:
add_executable(main main.cpp)
target_link_libraries(main generated-headers)

Протестировано на CMake 3.2, 3.8 и 3.9. Использование генераторов Ninja и Make.

person Unapiedra    schedule 01.07.2017

Вы можете использовать target_sources в CMake 3.1, чтобы указать потребителям компилировать файлы интерфейса:

add_library(source_only INTERFACE)
target_sources(source_only INTERFACE foo.cpp) 

http://www.cmake.org/cmake/help/v3.1/command/target_sources.html

person steveire    schedule 10.02.2015
comment
Я уже использую target_sources() для добавления сгенерированных файлов заголовков в мою библиотеку INTERFACE. Но CMake их не генерирует — по-видимому, зависимость (и пользовательская команда для ее разрешения) не передается потребителю. - person JPNotADragon; 10.02.2015

Я столкнулся с аналогичными проблемами при попытке использовать Glad: https://github.com/Dav1dde/glad.

Он использует пользовательскую команду CMake для создания привязки, а это означает, что файлы, которые вам нужно включить в проект, использующий глад, не существуют, поэтому CMake не строит глад (который создал бы эти файлы)...

Я еще не пробовал, но пример 3 по следующей ссылке кажется хорошим решением, и я думаю, что он может сработать в вашем случае: https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/

person GPMueller    schedule 27.06.2016