Зачем использовать выражение генератора для связывания библиотеки объектов?

#cmake

#cmake

Вопрос:

Я понимаю назначение библиотек объектов, но почему для связывания его с целью необходимо использовать выражение генератора?

 add_library(myObjects OBJECT a.cpp b.cpp)
add_library(mainLib ${other_srcs}

lt;TARGET_OBJECTS:myObjects>) # Why not "PRIVATE myObjects" just like other files and targets?

Насколько я понимаю, выражения генератора полезны для оценки вещей, доступных только на этапе генерации. Почему это может быть так, что библиотеки объектов, которые представляют собой просто набор объектных файлов, и их местоположение не будут известны на этапе настройки?

Ответ №1:

В дополнение к тому, что уже сказал @Alex Reinking, расположение объектного файла само по себе может зависеть от конфигурации, например, с генератором «Ninja Multi-Config» объектные файлы цели t находятся в <build-dir>/t/CMakeFiles/t.dir/<config>/<path-of-source-file-relative-to-dir-in-which-target-is-defined>/<src-file-name>.o

Итак, вы точно находитесь в ситуации, когда вы знаете местоположение только во время генерации, отсюда и необходимость выражения генератора.

Комментарии:

1. Интересно, почему то же самое не относится к не объектным библиотекам, созданным в середине конвейера.

2. @User10482 Что именно вы имеете в виду? Для библиотек, не являющихся объектами, конечное местоположение также разрешается только во время генерации, точно по той же причине — это может зависеть от конфигурации. Таким образом, не объектные библиотеки ведут себя точно так же.

3. Да, но им не нужно выражение генератора, как OBJECT библиотеке. Я могу связать их через target_link_libraries .

4. @User10482 Как объяснил @KamilCuk в своем ответе, вы можете использовать target_link_libraries с OBJECT библиотеками с 3.12. Однако по техническим причинам вам все еще иногда требуется $<TARGET_OBJECTS:tgt> выражение генератора. Если у вас есть OBJECT библиотека o_base и две библиотеки объектов o1 , и o2 обе они ссылаются на o_base (не имеет значения, ссылаются ли они PRIVATE ly или PUBLIC ly) и исполняемый файл, который ссылается на оба o1 , и o2 вам все равно нужно будет вручную добавить $<TARGET_OBJECTS:o_base> в исполняемый файл.

5. Спасибо за быстрые ответы. Итак, это сводится к проектному решению, принятому разработчиками CMake, поскольку они смогли удалить это выражение генератора в случае, приведенном @KamilCuk.

Ответ №2:

add_library(mainLib ${other_srcs} $<TARGET_OBJECTS:MyObjects>) # Почему не «MyObjects», как и другие файлы и целевые объекты?

Итак:

   add_library(mainLib ${other_srcs} myObjects)
 

Потому что это означало бы попытку найти файл с именем myObjects и скомпилировать его. Файл не имеет расширений, поэтому cmake завершит работу, потому что файл не существует, и он все равно не знает, как его скомпилировать.

Если вы имели в виду:

   add_library(mainLib ${other_srcs})
  target_link_libraries(mainLib PUBLIC myObjects)
 

link_libraries Зависимость от библиотек объектов не работала в более старом cmake, когда OBJECT были введены библиотеки.

В документации по библиотекам объектов явно упоминается форма add_library(... $<TARGET_OBJECTS:...) и отмечается, что, поскольку cmkae 3.12 target_link_libraries может использоваться с библиотеками объектов.

Комментарии:

1. Однако связывание библиотеки объектов с target_link_libraries не всегда устраняет необходимость в $<TARGET_OJBECTS:tgt> выражении генератора. Если у вас есть цель A , которая ссылается на библиотеку объектов Obj1 , которая сама ссылается на библиотеку объектов Obj2 , которую вам нужно будет использовать target_sources(A PRIVATE $<TARGET_OBJECTS:Obj2>) в дополнение к target_link_libraries(A PRIVATE Obj1) , потому что объекты не наследуются транзитивно.

2. Ах, да, это то, что я имел в виду. Отредактировано. Итак, разработчики решили использовать выражение генератора, потому link_libraries что оно не работало в течение начального периода времени?

3. the developers decided to roll спросите их. because link_libraries did not work during the initial time period? cmake 3.11 не поддерживался taget_link_libraries с библиотеками объектов. Но это просто удобство, все же использование add_library( $<TARGET_OBJECTS было бы предпочтительным способом.


Ответ №3:

Почему это может быть так, что библиотеки объектов, которые представляют собой просто набор объектных файлов, и их местоположение не будут известны на этапе настройки?

Просто: target_sources команда может использоваться для добавления источников (и, следовательно, объектов) к цели в любой момент. Если бы переменная была расширена немедленно, можно было бы пропустить объекты, которые были добавлены позже.

В сборках с несколькими конфигурациями во время настройки также неизвестно, ссылаетесь ли вы на объекты Debug или Release.


Представьте, что add_library(target a.cpp b.cpp) создана переменная с именем target_OBJECTS containing /path/to/a.o;/path/to/b.o . Затем следующая последовательность демонстрирует проблему:

 add_library(target a.cpp b.cpp)
add_executable(main main.cpp ${target_OBJECTS})       # (1)

# ... later ...
target_sources(target PRIVATE c.cpp)

# ... later ...
add_executable(utility util.cpp ${target_OBJECTS})    # (2)
 

Теперь, как бы (2) узнал об /path/to/c.o этом? Вы могли бы target_sources обновить эту переменную, но теперь у одной объектной библиотеки есть зависимые элементы, которые видят разные ее подмножества. Это ужасно сбивает с толку. Более того, (1) не будет видно c.o , что, возможно, и желательно в этом примере, но это не всегда так. Возможно, ваш код просто организован для определения пользовательских команд для последующего создания сгенерированных источников.

Придание этому императивному, а не декларативному типу $<TARGET_OBJECTS:target> , налагает на код CMake структуру, аналогичную требованию топологической сортировки целевых объектов в Makefile. Это также изменяет значение с «объектных файлов для этой цели» на «объектные файлы для этой цели на данный момент времени выполнения CMake«, что, строго говоря, более сложно рассуждать, не предлагая никаких выразительных преимуществ.

Если вы замените этот код поддержкой привязки библиотеки объектов CMake 3.12 , это выглядит еще более неприятно:

 add_library(target a.cpp b.cpp)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE target)            # (1)

# ... later ...
target_sources(target PRIVATE c.cpp)

# ... later ...
add_executable(utility util.cpp)
target_link_libraries(utility PRIVATE target)         # (2)
 

Теперь, глядя на любой вызов target_link_libraries(... target) , можно было бы разумно ожидать, что будут добавлены одни и те же объекты.

Комментарии:

1. Если аргументами для target_sources являются объектные файлы, указанные на этапе настройки, они должны быть доступны тогда. Точно так же, как цели для общих и статических библиотек, не так ли?

2. Я обновил свой ответ примером, дополнительно демонстрирующим проблему. Неверно , что пути к объектным файлам известны во время настройки, во всяком случае, из-за генераторов с несколькими конфигурациями.