Добавить зависимость FreeType с cmake, поддерживающим все vcpkg / apt-get / brew

У меня есть проект, который зависит от FreeType и использует CMake в качестве системы сборки. CMake имеет встроенный модуль FindFreeType, который должен можно использовать таким образом, см., например, этот другой вопрос SO:

find_package(Freetype REQUIRED)
target_link_libraries(mylib ${FREETYPE_LIBRARIES})
target_include_directories(mylib PRIVATE ${FREETYPE_INCLUDE_DIRS})

Начиная с CMake 3.10, существует также импортированная цель Freetype::Freetype, поэтому мы можем избежать target_include_directories:

find_package(Freetype REQUIRED)
target_link_libraries(mylib Freetype::Freetype)

Это отлично работало в Ubuntu 18.04 с FreeType, установленным через apt install libfreetype6-dev. Я предполагаю, что он также работает в macOS, когда пакет установлен через homebrew (я еще не тестировал).

Однако в Windows я хочу позволить разработчикам полагаться на FreeType, установленный vcpkg:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg install freetype:x64-windows

На что они нацелятся, выполнив следующую команду CMake:

cmake .. -G "Visual Studio 15 2017" -A x64 -DCMAKE_TOOLCHAIN_FILE=C:/Users/Boris/vcpkg/scripts/buildsystems/vcpkg.cmake

К сожалению, указанная выше команда CMake не будет работать ни с одним из двух CMakeLists.txt в начале этого вопроса, потому что правильный способ найти FreeType и связать его с FreeType, когда он установлен через vcpkg, следующий:

find_package(freetype CONFIG REQUIRED) # `Freetype` works too, but vcpkg doc recommends `freetype`
target_link_libraries(mylib freetype)  # Here, all-lowercase is required

В частности, файл конфигурации freetype-config.cmake, предоставленный vcpkg, определяет цель freetype (не Freetype::Freetype, как встроенный модуль поиска) и не определяет никаких переменных FREETYPE_LIBRARIES или FREETYPE_INCLUDE_DIRS.

Что было бы правильным способом сохранить совместимость моего CMakeLists.txt как с «традиционными» способами поиска FreeType, так и с vcpkg?

Предполагая, что до CMake 3.10 я думаю о чем-то вроде:

if(DEFINED VCPKG_TARGET_TRIPLET)
    find_package(freetype CONFIG REQUIRED)
    set(FREETYPE_LIBRARIES freetype)
    set(FREETYPE_INCLUDE_DIRS "")
else()
    find_package(Freetype REQUIRED)
endif()
target_link_libraries(mylib ${FREETYPE_LIBRARIES})
target_include_directories(mylib PRIVATE ${FREETYPE_INCLUDE_DIRS})

Это кажется хорошей практикой? Есть идея получше?

Это выглядит некрасиво, и, кроме того, всегда существует вероятность того, что разработчик захочет использовать vcpkg для некоторых других зависимостей, но не для FreeType (например, явно предоставив вместо этого FREETYPE_DIR), поэтому этого трюка будет недостаточно во всех ситуациях, и нам понадобится еще один параметр CMake, например MYLIB_IGNORE_VCPKG_FREETYPE, который становится еще более уродливым.


person Boris Dalstein    schedule 04.03.2020    source источник


Ответы (2)


В более старых версиях vcpkg (‹январь 2020 г., см. vcpkg # 9311) использовалось должно появиться следующее сообщение при установке Freetype:

The package freetype is compatible with built-in CMake targets:

    find_package(Freetype REQUIRED)
    target_link_libraries(main PRIVATE Freetype::Freetype)

В текущих версиях вместо этого они рекомендуют find_package(freetype CONFIG REQUIRED), что гарантирует, что пакет конфигурации vcpkg/<...>/freetype-config.cmake имеет приоритет над пакетом встроенного модуля CMake FindFreetype.cmake.

Однако использование встроенного пакета модуля по-прежнему работает правильно: он найдет установленную vcpkg библиотеку и определит цель Freetype::Freetype (если CMake ›= 3.10), а не цель freetype.

Обратите внимание, что по умолчанию find_package(Freetype REQUIRED) сначала ищет пакеты модулей, а затем пакеты конфигурации. Однако пользователи могут установить CMAKE_FIND_PACKAGE_PREFER_CONFIG, который вместо этого будет использовать пакет конфигурации. Следовательно, надежный подход к определению Freetype::Freetype состоит в следующем:

find_package(Freetype MODULE REQUIRED)
target_link_libraries(mylib Freetype::Freetype)

Однако, если CMake ‹3.10, это все еще не определяет цель Freetype::Freetype. Один из вариантов в этом случае - предоставить копию более новой версии НайдитеFreetype.cmake в своем CMAKE_MODULE_PATH.

Для большей надежности вы можете даже назвать его Find<Mylib>Freetype.cmake в том маловероятном случае, когда ваш CMakeLists.txt используется как подкаталог другого проекта, который также изменяет CMAKE_MODULE_PATH и предоставляет несовместимый FindFreetype.cmake.

person Boris Dalstein    schedule 11.08.2020
comment
Пожалуйста, нет ..... IMPORTED_LOCATION $ {FREETYPE_LIBRARIES} неверно Переменные _LIBRARIES могут содержать следующее: optimized; somelib.lib; debug; someother.lib, что нарушает ваш код. - person Alexander Neumann; 14.08.2020
comment
@AlexanderNeumann Вы правы, спасибо за информацию. Я все еще многому из этих вещей учусь. Я обновил ответ, добавив более надежное решение. Я все еще считаю это решение уродливым, но, к сожалению, ваш ответ, похоже, не работает с CMake ‹3.10 :( - person Boris Dalstein; 14.08.2020
comment
на самом деле нет веской причины застрять в CMake трехлетней давности .... Единственный способ убедиться, что модули CMake стабильны в разных версиях cmake, - это продавать их самостоятельно, что является CMAKE_MODULE_PATH решением, которое вы описываете (это также делается например, VTK) Абсолютно лучшее решение - предоставить пользовательские Find<Project>Freetype.cmake с уникальными целевыми объектами в пространстве имен для вашего проекта (подход перенаправления). Таким образом можно избежать столкновения с целью. Но ваше решение по-прежнему не работает, поскольку пользователи могут установить CMAKE_FIND_PACKAGE_PREFER_CONFIG, который не устанавливает LIBRARY vars - person Alexander Neumann; 14.08.2020
comment
Срок службы @AlexanderNeumann Ubuntu 16.04 LTS истекает 30 апреля 2021 года. Он поставляется с cmake 3.5.1 (launchpad.net/ubuntu/xenial/+source/cmake). Я бы хотел, чтобы люди, использующие Ubuntu 16.04, могли легко компилировать мое приложение без необходимости получать новые версии CMake, официально не поддерживаемые в их системе. Нет причин заставлять людей использовать передовой CMake, когда существуют обходные пути. - person Boris Dalstein; 14.08.2020
comment
@AlexanderNeumann Однако твоя точка зрения насчет CMAKE_FIND_PACKAGE_PREFER_CONFIG верна. Я полагаю, мы можем обойти это, используя find_package(Freetype MODULE REQUIRED) или, как вы предлагаете, еще более надежный find_package(<Project>Freetype REQUIRED). - person Boris Dalstein; 14.08.2020
comment
@AlexanderNeumann Я обновил ответ, добавив дополнительную информацию, и удалил in-CMakeLists, что в любом случае было довольно уродливым. - person Boris Dalstein; 14.08.2020
comment
›Я бы хотел, чтобы люди, использующие Ubuntu 16.04, могли легко компилировать мое приложение без необходимости получать новые версии CMake, официально не поддерживаемые в их системе. Kitware фактически поддерживает его ..... apt.kitware.com, и я ожидаю, что разработчики Linux смогут установить более новую версию cmake. В противном случае они должны переключиться на окна;) Кроме того, для правильного использования vcpkg в основном требуется, по крайней мере, версия vcpkg cmake. Это не строго соблюдается vcpkg (но должно быть ....) из-за изменений в модулях cmakes. - person Alexander Neumann; 21.08.2020
comment
@AlexanderNeumann Любая ошибка расстраивает, даже если сообщение об ошибке ясно указывает, в чем проблема (например, CMake устарел). Как эксперты, мы склонны забывать, насколько это расстраивает и пугает. Моя цель - снизить входной барьер. Я предпочитаю делать предположение, что пользователи, пытающиеся скомпилировать мои приложения, не имеют опыта компиляции и нулевого знания о том, что такое cmake, потому что у всех есть первый раз. Очень важно иметь довольных пользователей, и мне буквально легче поддерживать CMake 3.1, чем объяснять, как обновить CMake для различных операционных систем;) - person Boris Dalstein; 21.08.2020

Это кажется хорошей практикой? Есть идея получше?

Не сомневайтесь в возможном диспетчере пакетов.

Сделайте следующее:

find_package(Freetype CONFIG) # should find freetype-config.cmake if available
find_package(Freetype REQUIRED) # Will not be executed if Freetype_FOUND ist already set
# if you do not want two find_package calls consider using CMAKE_FIND_PACKAGE_PREFER_CONFIG

Затем проверьте, существует ли цель Freetype::Freetype или freetype

 if(TARGET freetype AND NOT TARGET Freetype::Freetype)
     add_library(Freetype::Freetype ALIAS freetype) # target freetype is defined by freetype-targets.cmake
     # might need to add freetype to global scope if cmake errors here
     # alternativly if the above does not work for you you can use
     # add_library(Freetype::Freetype INTERFACE IMPORTED)
     # target_link_libraries(Freetype::Freetype INTERFACE freetype)
 endif()
 if(NOT TARGET Freetype::Freetype)
     # insert error here
     # or create the target correctly (see cmakes newer FindFreetype.cmake)
 endif()
 target_link_libraries(mylib PRIVATE Freetype::Freetype)

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

person Alexander Neumann    schedule 09.04.2020
comment
Эта великолепная идея! Спасибо и добро пожаловать в Stack Overflow :-) Я еще не пробовал, но я уже принимаю ответ, так как кажется, что он должен работать, и в противном случае я мог бы забыть вернуться и принять ответ. - person Boris Dalstein; 09.04.2020
comment
Наконец-то у меня появилось время. Были небольшие проблемы: нам нужен QUIET вместо REQUIRED в первом вызове, а установка ALIAS не была гладкой: нам нужен CMake 3.11+ для определения псевдонима для импортированной цели (и CMake 3.18+ для неглобальной импортированной цели. , хотя помогает IMPORTED_GLOBAL). Решение, которое сработало, заключалось в том, чтобы переопределить новую цель с помощью add_library (Freetype :: Freetype SHARED IMPORTED) и установить все ее свойства. Но потом я понял, что на самом деле в этом нет необходимости: я опубликовал новый ответ. В любом случае, спасибо за вашу помощь! - person Boris Dalstein; 12.08.2020