CMake: неверный порядок ссылок импортированных целей - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть следующий сценарий:

  • Я импортирую две готовые библиотеки в свой проект (libA, libB)
  • libB зависит от libA
  • Исполняемый файл зависит как от libA, так и от libB

Однако относительный порядок ссылок в моем файле link.txt неверен

/usr/bin/c++     CMakeFiles/bin.dir/main.cpp.o  -o bin ../libA.a ../libB.a

Я ожидаю, что libA.a будет указан после libB.a.CMakeLists.txt выглядит примерно так: 1024 *

cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})

add_library(MY::libA IMPORTED INTERFACE)
set_target_properties(MY::libA PROPERTIES INTERFACE_LINK_LIBRARIES "${lib_dir}/libA.a")

add_library(MY::libB IMPORTED INTERFACE)
set_target_properties(MY::libB PROPERTIES INTERFACE_LINK_LIBRARIES "MY::libA;${lib_dir}/libB.a")

add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC MY::libB MY::libA)

Ниже приведено описание моих попыток решить проблему.Некоторые безуспешно, некоторые с успехом, но используют модификации, которые делают код бесполезным для производственной среды.

Успешные попытки:

  • Устранить зависимость bin от libA (т.е. замените последнюю строку на target_link_libraries(bin PUBLIC MY::libB). Это работает, но я не могу удалить зависимость в реальном коде.
  • Замените целевой тип IMPORTED INTERFACE на IMPORTED STATIC. Используйте IMPORTED_LOCATION вместо INTERFACE_LINK_LIBRARIES ииспользуйте target_link_libraries для выражения зависимости libB от libA. В этом случае link.txt дает: [...] -o bin ../libA.a ../libB.a ../libA.a. Как только я возвращаю целевой тип для libB, порядок ссылок снова нарушается.однако в производственной среде conan создает одну из целей как IMPORTED INTERFACE.

Попытки безуспешны (как описано выше):

  • Создайте отдельнуюIMPORTED target (используйте IMPORTED_LOCATION) для каждой библиотеки и сгруппируйте их в INTERFACE target
  • Посыпьте код с помощью ADD_DEPENDENCIES
  • Удалите libA из INTERFACE_LINK_LIBRARIESв строке 9 используйте вместо него target_link_libraries(MY::libB INTERFACE MY::libA). Тот же результат.

Пример кода, который показывает ту же ошибку, используя INTERFACES в качестве строительного блока

cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})

# libA
add_library(MY::libA_file1 IMPORTED STATIC)
set_target_properties(MY::libA_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libA.a")

add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE MY::libA_file1)

# libB
add_library(MY::libB_file1 IMPORTED STATIC)
set_target_properties(MY::libB_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libB.a")

add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE MY::libB_file1 libA)


add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)

1 Ответ

0 голосов
/ 27 декабря 2018

Вы неправильно думаете о INTERFACE_LINK_LIBRARIES свойстве как "содержимом" цели библиотеки, которая упорядочена по вызову target_link_libraries.

Использование

target_link_libraries(MY::libB INTERFACE MY::libA)

Вы устанавливаете связь между целевыми объектами библиотеки MY::libB и MY::libA.То есть «содержимое» цели MY::libB должно стоять перед «содержимым» цели MY::libA в командной строке связывания.

Но INTERFACE_LINK_LIBRARIES свойство равно НЕ «содержание» библиотеки цели!Это просто дополнительная зависимость от ссылки.

Как противоположность, IMPORTED_LOCATION (для не INTERFACE IMPORTED target) является «содержимым»библиотека, и target_link_libraries влияет на ее порядок.

Кажется, что вы не можете добавить зависимость ссылки для библиотеки, используя INTERFACE target для библиотеки.Для этой цели вы должны использовать целевую библиотеку IMPORTED :

# Collect libraries related to 'libA'
file(GLOB libs_A "${lib_dir}/libA*.a")
# For each library create IMPORTED target with IMPORTED_LOCATION property.
set(libs_A_targets)
foreach(lib_A ${libs_A})
    # Form a unique name for the IMPORTED target: subtarget_A_*
    string(REGEX REPLACE "^${lib_dir}/libA([^.]*).a$" "subtarget_A_\\1" lib_A_target ${lib_A})
    # Create a target with this name
    add_library(${lib_A_target} STATIC IMPORTED)
    set_target_properties(${lib_A_target} PROPERTIES IMPORTED_LOCATION ${lib_A})
    # And add the target into the list
    list(APPEND libs_A_targets ${lib_A_target})
endforeach()

# In a similar way collect libraries for libB.
set(lib_B_targets ...)

# Now link each libB* library with each libA*.
foreach(lib_B_target ${libs_B_targets})
    target_link_libraries(${lib_B_target} INTERFACE ${libs_A_targets})
endforeach()

# Now interface libraries, which combine libA* and libB*, can be created
add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE ${libs_A_targets})

add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE ${libs_B_targets})

# Now these INTERFACE libraries can be linked into an executable in any order
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...