Руководство для сопровождения

В этом документе описываются политики, рекомендации и лучшие практики по внесению вкладов в vcpkg.

Она предназначена для выполнения той же роли, что и:

Цели проектирования курируемого реестра

Порты должны устанавливаться одновременно

Порт, включенный в курируемый реестр, должен успешно создаваться в присутствии всех остальных портов в реестре. Все сочетания портов регулярно тестируются автоматизированной системой CI vcpkg.

Порт не должен выполнять указанные ниже действия.

Исключения создаются для:

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

Порты, которые не соответствуют этой политике, не могут быть приняты в курированный реестр.

Подсказка

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

Порты должны быть проверены как минимум в одной официальной триплет.

Все порты в курируемом реестре должны быть протестированы в CI как минимум для одного официального триплета.

Исключения создаются для:

  • Временные обстоятельства, например:
    • Без обновления инфраструктуры непрерывной интеграции порт не может быть собран.
    • Порт не строится из-за недавнего критического изменения в вышестоящей зависимости и выполняется работа по устранению ситуации.
  • Порты, которые предшествуют этой политике и имеют заметное количество пользователей и участников.

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

Подсказка

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

Упакованные проекты должны быть стабильными и активно поддерживаться

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

Проект считается неактивным, если:

  • Ответственные за проект объявили, что он заброшен.
  • Архивируется или больше не принимает взносы.
  • Сопровождающие не отвечают или недоступны.
  • В течение длительного времени никаких значимых изменений не было сделано.

Исключения создаются для базовых проектов, которые считаются зрелыми и стабильными и часто не получают изменений. Например: zlib.

Упакованные проекты должны быть зрелыми

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

Проект считается достаточно зрелым для курированного реестра, если одно из этих утверждений истинно:

  • Проект выпущен не менее шести месяцев назад.
  • Проект демонстрирует по крайней мере шесть месяцев активной общественной разработки.
  • Проект является официальным компонентом другого проекта, удовлетворяющего предыдущим требованиям. Например, новая библиотека Boost или компонент Qt.
  • Проект демонстрирует эквивалентную зрелость требованиям предыдущей версии в каком-либо другом аспекте.

Ниже приведены некоторые показатели незрелости проекта:

  • Проект не отображается в поисковых системах.
  • Проект часто переименовывается.
  • Конфликтует с другими библиотеками.

Подсказка

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

Структура PR

Создавайте отдельные pull requests по каждому порту

Меньшие запросы на pull request (PR) проще просматривать. Пулл-реквесты должны вносить изменения в один порт. Это также сокращает время ожидания результатов непрерывной интеграции (CI).

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

Избегайте тривиальных изменений в нетронутых файлах

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

Использование отличительных имен портов

Имя порта должно указывать на его содержимое.

Поиск имени порта в поисковой системе или специализированных браузерах пакетов, таких как Repology, должен привести к соответствующему проекту.

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

Исключения делаются для портов, которые упаковывают проект с сильной связью с именем порта. Например: libpng, openssl, или zlib.

Для соответствия этой политике новые порты с неоднозначными именами могут использовать префикс, например:

  • Владелец репозитория, имя пользователя или организация. Пример: google-cloud-cpp. Для портов проектов GitHub владелец GitHub является приемлемым однозначным префиксом <github owner>-<repository name>.
  • Имя набора, к которому принадлежит пакет: boost-dll Только если пакет является официальным компонентом такого программного набора.

Пример: неоднозначное имя порта

Порт с именем ip считается неоднозначным, так как:

  • имя слишком короткое,
  • имя — это распространённое слово, и
  • Имя не связано с любым уникальным проектом.

Чтобы определить, является ли имя неоднозначным, удалите следующие распространенные префиксы и суффиксы, используемые проектами C++ и открытым кодом из имени порта:

  • cpp
  • free
  • lib
  • open
  • числительные

Например: ip-cpp, libip и ip5 являются неоднозначными, потому что они сокращаются до той же ip основы.

Ограничение переименований портов

Чтобы избежать путаницы для пользователей, порты нельзя переименовать до одного года после последнего переименования.

Использование черновиков PR GitHub

Черновики PR на GitHub — отличный способ получить отзывы от CI или людей о работе, которая еще не готова к слиянию. Большинство новых PR должны быть открыты пока в виде черновиков и преобразованы в обычные PR после успешной проверки CI.

Дополнительные сведения о черновых запросах на подтверждение в GitHub см. в статье Введение в черновые запросы на подтверждение.

Команда vcpkg может преобразовать ваш PR в черновик во время процесса проверки. Обычно это запрос на изменение вашего кода или комментарии, указывающие, когда следует пометить PR как готовый к рассмотрению.

Закрытие неактивных PR

Команда vcpkg может закрыть запросы на вытягивание (PR), в которых отсутствует активность более чем 60 дней.

Для pull-запросов, готовых к проверке, отсчет начинается с последнего момента, когда поддерживающий проект vcpkg делает запрос на изменения или запрашивает уточнение. Если активность не осуществляется участником в течение 60 дней, PR считается неактуальным и может быть закрыт на усмотрение команды.

Запрос на слияние находится в состоянии проверки, когда:

  • Не имеет неудачных проверок PR, либо участник просит помощи или обосновывает причину сбоев.
  • Не имеет никаких ожидающих изменений или уточнений от поддержку vcpkg.
  • Нет конфликтов при слиянии.

PRs, которые неактивны в течение более 60 дней и не подлежат проверке, могут быть закрыты без проверки.

Портфайлы

Избегайте устаревших вспомогательных функций

В настоящее время не рекомендуется использовать следующие вспомогательные средства:

  • vcpkg_extract_source_archive_ex() следует заменить поддерживаемой перегрузкой vcpkg_extract_source_archive()ARCHIVE)
  • Устаревшая перегрузка vcpkg_extract_source_archive() без ARCHIVE должна быть заменена поддерживаемой перегрузкой ARCHIVE.
  • vcpkg_apply_patches() следует заменить аргументами PATCHES для вспомогательных функций извлечения (например, vcpkg_from_github())
  • vcpkg_build_msbuild() должно быть заменено vcpkg_install_msbuild()
  • vcpkg_copy_tool_dependencies() должно быть заменено vcpkg_copy_tools()
  • vcpkg_configure_cmake следует заменить vcpkg_cmake_configure() после удаления PREFER_NINJA
  • vcpkg_build_cmake должно быть заменено vcpkg_cmake_build()
  • vcpkg_install_cmake должно быть заменено vcpkg_cmake_install()
  • vcpkg_fixup_cmake_targets должно быть заменено vcpkg_cmake_config_fixup

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

{
  "name": "vcpkg-cmake",
  "host": true
},
{
  "name": "vcpkg-cmake-config",
  "host": true
}

Избегайте чрезмерных комментариев в портфайлах

В идеале портфайлы должны быть короткими, простыми и как можно более декларативными. Удалите все комментарии к плите, представленные create командой перед отправкой PR.

Используйте строчные буквы для шестнадцатеричных цифр

Многие функции vcpkg полагаются на сравнение строк шестнадцатеричных цифр. К некоторым примерам, но не ограничиваясь ими, относятся хэши SHA512, идентификаторы фиксации Git и хэши объектов дерева.

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

Чтобы обеспечить согласованность в следующих сценариях, необходимо в нижнем регистре:

  • Параметр SHA512 в вспомогательных функциях vcpkg.
  • Параметр REF в вспомогательных функциях vcpkg, если значение является шестнадцатеричной строкой.
  • Объект git-tree в файлах базы данных версий.
  • Объект sha512 в scripts/vcpkg-tools.json файле.
  • Другие места, где регистр шестнадцатеричной строки не имеет значения.

Порты не должны быть зависимыми от пути

Порты не должны изменять их поведение в зависимости от того, какие порты уже установлены в форме, которая изменит содержимое, которое устанавливает порт. Например, если:

> vcpkg install a
> vcpkg install b
> vcpkg remove a

и

> vcpkg install b

файлы, установленные с помощью b, должны быть такими же, независимо от влияния предыдущей установки a. Это означает, что порты не должны пытаться определить, предоставляется ли что-то в установленном дереве другим портом перед выполнением некоторых действий. Конкретная и распространенная причина такого поведения, зависящего от пути, описана ниже в разделе "При определении функций явно управляйте зависимостями".

Уникальное правило атрибуции портов

В всей системе vcpkg не предполагается, что пользователь будет использовать два порта одновременно, может предоставить один и тот же файл. Если порт пытается установить файл, уже предоставленный другим файлом, установка завершится ошибкой. Если порт хочет использовать чрезвычайно распространенное имя заголовка, например, он должен поместить эти заголовки в подкаталог, а не в include.

Это свойство регулярно проверяется непрерывной интеграцией, которая пытается установить все порты в реестре, что приведет к сбою FILE_CONFLICTS , если два порта предоставляют один и тот же файл.

Добавление экспорта CMake в неофициальное пространство имен

Основной идеал разработки vcpkg заключается в том, чтобы не создавать блокировку для пользователей. В системе сборки не должно быть разницы в зависимости от библиотеки из системы и в зависимости от библиотеки из vcpkg. В этом случае мы избегаем добавления экспорта или целевых объектов CMake в существующие библиотеки с "очевидным именем", чтобы разрешить upstreams добавлять собственные официальные экспорты CMake, не конфликтуя с vcpkg.

Для этого все конфигурации CMake, экспортируемые портом, которые не находятся в вышестоящей библиотеке, должны иметь unofficial- в качестве префикса. Любые дополнительные целевые объекты должны находиться в unofficial::<port>:: пространстве имен.

Это означает, что пользователь должен видеть следующее:

  • find_package(unofficial-<port> CONFIG) как способ получить в пакете unique-to-vcpkg
  • unofficial::<port>::<target> в качестве экспортированного целевого объекта из этого порта.

Примеры:

  • brotli создает пакет unofficial-brotli, производя целевой unofficial::brotli::brotli.

Каждый порт должен предоставить файл с именем copyright в папке ${CURRENT_PACKAGES_DIR}/share/${PORT}. Если содержимое лицензии пакета доступно в исходных файлах, этот файл должен быть создан вызовом vcpkg_install_copyright(). vcpkg_install_copyright при необходимости пакетирует несколько файлов авторских прав.

vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")

Старый метод вручную создать этот файл заключается в использовании встроенной команды CMake file. Это не рекомендуется использовать vcpkg_install_copyright в новых портах, но по-прежнему разрешено.

file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)

Если содержимое лицензии в вышестоящих исходных файлах не находится в текстовой форме (например, PDF-файл), copyright должно содержать пояснение о том, как пользователь может найти требования к лицензии. Если это возможно, он также должен включать ссылку на исходные исходные файлы, указывающие на это, чтобы пользователи могли проверить актуальность.

file(WRITE "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" [[As of 2023-07-25, according to
https://github.com/GPUOpen-LibrariesAndSDKs/display-library/blob/master/Public-Documents/README.md#end-user-license-agreement
this software is bound by the "SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT" PDF located at
https://github.com/GPUOpen-LibrariesAndSDKs/display-library/blob/master/Public-Documents/ADL%20SDK%20EULA.pdf
]])

Ограничения версий в портах

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

Переменные должны MAYBE_UNUSED_VARIABLES применяться по крайней мере к одной конфигурации

При добавлении новой переменной для MAYBE_UNUSED_VARIABLES молчания предупреждения во время шага конфигурации CMake необходимо добавить комментарий, объясняющий ситуацию при применении новой переменной. Если переменная не применяется в какой-либо конфигурации, скорее всего, существует базовая ошибка (например, имя неправильной переменной) и добавление ее не влияет на сборку.

vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
  FEATURES
    windowsfeature WINDOWS_OPTION
)

vcpkg_configure_cmake(
  SOURCE_PATH "${SOURCE_PATH}"
  OPTIONS
    ${FEATURE_OPTIONS}
  MAYBE_UNUSED_VARIABLES
    # Applies only on Windows
    WINDOWS_OPTION
)

Функции

Не используйте функции для реализации альтернативных вариантов

Функции должны рассматриваться как аддитивные функции. При port[featureA] установке и port[featureB] установке port[featureA,featureB] необходимо установить. Кроме того, если второй порт зависит от [featureA] третьего порта [featureB], установка второго и третьего портов должна быть удовлетворена.

Библиотеки в этой ситуации должны выбрать один из доступных вариантов, выраженных в vcpkg, и пользователи, которым требуется другой параметр, должны использовать порты наложения в это время.

Примеры, которые мы сегодня не стали бы принимать, но сохранили для обратной совместимости:

  • libgit2, libzip, open62541 все имеют функции для выбора TLS или крипто-бэкенда. curl имеет различные варианты криптографической бэкенд системы, но позволяет выбирать между ними во время выполнения, что означает, что вышеупомянутый принцип сохраняется.
  • darknet opencv2имеет opencv3функции для управления версией opencv, используемой для его зависимостей.

Функция может включать предварительный или бета-функционал.

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

Примеры:

  • Пакеты SDK Azure (формы azure-Xxx) имеют функцию public-preview .
  • imgui experimental-docking имеет функцию, которая активирует свою ветвь предварительного захвата, использующую коммит слияния, прикреплённый к каждому из их публичных нумерованных выпусков.

Функции по умолчанию не должны добавлять API

Замечание

Функция, включенная по умолчанию системой входящей сборки, не подразумевает, что эта функция должна быть добавлена в default-features элементы. Цель default-features состоит в том, чтобы, вместо моделирования решений, принимаемых вышестоящими уровнями, обеспечить удобство для пользователей классического режима.

Функции по умолчанию предназначены для обеспечения того, чтобы достаточно функциональная сборка библиотеки была установлена для клиентов, которые не знают, что они используют его. Если они не знают, что они используют библиотеку, они не могут знать, чтобы перечислить функции. Например, libarchive предоставляет функции, позволяющие алгоритмам сжатия использовать существующий универсальный интерфейс; если он создан без каких-либо таких функций, библиотека может не иметь служебной программы.

Следует тщательно учесть, должна ли функция быть включена по умолчанию, так как отключение функций по умолчанию является сложным.

Для отключения функции по умолчанию в качестве транзитивного потребителя требуется:

  • Все клиенты, явно отключающие функции по умолчанию через "default-features": false или включающие [core] в список функций в командной строке.
  • Именование транзитивной зависимости от vcpkg install командной строки или в виде прямой зависимости в манифесте верхнего уровня

В управляемом реестре vcpkg, если функция добавляет дополнительные API, исполняемые файлы или другие двоичные файлы, она должна быть отключена по умолчанию. Если сомневается, не помечайте функцию по умолчанию.

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

Если потребитель порта зависит только от основных функций этого порта, с высокой вероятностью их работа не должна нарушиться при включении функции. Это еще более важно, если альтернатива не контролируется потребителем напрямую, а параметрами компилятора, такими как /std:c++17 / -std=c++17.

Примеры, которые мы сегодня не стали бы принимать, но сохранили для обратной совместимости:

  • redis-plus-plus[cxx17] контролирует полифилл, но не включает настройку в установленное дерево.
  • ace[wchar] изменяет все API, чтобы они принимали const wchar_t*, а не const char*.

Возможность может заменить полифиллы псевдонимами, если замена интегрирована в установленное дерево.

Несмотря на приведенные выше обстоятельства, порты могут удалять полифиллы, если имеется соответствующая функция:

  1. Включение функции изменяет полизаполнения на псевдонимы сущности polyfilled
  2. Состояние полифила встраивается в заголовочные файлы, таким образом, ошибки несовпадения ABI вряд ли возникнут во время выполнения.
  3. Пользователь порта может написать код, который работает в обоих режимах, например, используя typedef, объявленный с применением полифила или без него.

Пример:

  • abseil[cxx17] заменяет absl::string_view на std::string_view; патч реализует требование выпечки.

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

set(USING_DOG 0)
message(STATUS "This version of LibContoso uses the Kittens backend. To use the Dog backend instead, create an overlay port of this with USING_DOG set to 1 and the `kittens` dependency replaced with `dog`.")
message(STATUS "This recipe is at ${CMAKE_CURRENT_LIST_DIR}")
message(STATUS "See the overlay ports documentation at https://github.com/microsoft/vcpkg/blob/master/docs/specifications/ports-overlay.md")

Методы сборки

Не используйте встроенные зависимости

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

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

Трудности в обновлениях: внедренные копии библиотек затрудняют отслеживание и применение обновлений, включая исправления безопасности, из вышестоящих проектов. Это приводит к потенциальным рискам безопасности и устаревшим зависимостям в экосистеме.

Конфликты символов: вендорные зависимости могут вызвать конфликты символов, если несколько пакетов содержат разные версии одной библиотеки.

Например, если пакет A включает библиотеку X (версия 1), а пакет B включает библиотеку X (версия 2), приложение, связывающее оба пакета, может столкнуться с ошибками во время выполнения или неопределённым поведением из-за конфликтующих символов.

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

Соответствие лицензированию: Включенные сторонние зависимости могут скрыть лицензирование встроенных библиотек, что потенциально нарушает их условия или создаёт проблемы совместимости.

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

Предпочитайте использовать CMake

Если доступны несколько систем сборки, предпочитайте использовать CMake. Кроме того, когда это уместно, можно упростить и улучшить поддержку, переписав альтернативные системы сборки в CMake с помощью file(GLOB) директив.

Примеры: abseil

Выбор статических или общих двоичных файлов

При создании библиотек CMake, vcpkg_cmake_configure() передаст правильное значение BUILD_SHARED_LIBS на основе запрошенного пользователем варианта.

Можно вычислить альтернативные параметры настройки с помощью string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" ...).

# portfile.cmake

string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" KEYSTONE_BUILD_STATIC)
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" KEYSTONE_BUILD_SHARED)

vcpkg_cmake_configure(
    SOURCE_PATH ${SOURCE_PATH}
    OPTIONS
        -DKEYSTONE_BUILD_STATIC=${KEYSTONE_BUILD_STATIC}
        -DKEYSTONE_BUILD_SHARED=${KEYSTONE_BUILD_SHARED}
)

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

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

Например, при исправлении библиотеки на основе CMake может быть достаточно добавить EXCLUDE_FROM_ALL к нежелательным целевым объектам и обернуть вызов install(TARGETS ...) в if(BUILD_SHARED_LIBS). Это будет проще и быстрее, чем переписывание или удаление каждой строки, которая упоминает нежелательный вариант текста.

Для проекта CMakeLists.txt со следующим содержимым:

add_library(contoso SHARED contoso.c)
add_library(contoso_static STATIC contoso.c)

install(TARGETS contoso contoso_static EXPORT ContosoTargets)

install(EXPORT ContosoTargets
  FILE ContosoTargets
  NAMESPACE contoso::
  DESTINATION share/contoso)

install(TARGETS) Необходимо исправить только строку.

add_library(contoso SHARED contoso.c)
add_library(contoso_static STATIC contoso.c)

if(BUILD_SHARED_LIBS)
  set_target_properties(contoso_static PROPERTIES EXCLUDE_FROM_ALL 1)
  install(TARGETS contoso EXPORT ContosoTargets)
else()
  set_target_properties(contoso PROPERTIES EXCLUDE_FROM_ALL 1)
  install(TARGETS contoso_static EXPORT ContosoTargets)
endif()

install(EXPORT ContosoTargets
  FILE ContosoTargets
  NAMESPACE contoso::
  DESTINATION share/contoso)

При определении функций явным образом управляйте зависимостями

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

set(CMAKE_DISABLE_FIND_PACKAGE_ZLIB ON)
set(CMAKE_REQUIRE_FIND_PACKAGE_ZLIB OFF)
if ("zlib" IN_LIST FEATURES)
  set(CMAKE_DISABLE_FIND_PACKAGE_ZLIB OFF)
  set(CMAKE_REQUIRE_FIND_PACKAGE_ZLIB ON)
endif()

vcpkg_cmake_configure(
  SOURCE_PATH ${SOURCE_PATH}
  OPTIONS
    -DCMAKE_DISABLE_FIND_PACKAGE_ZLIB=${CMAKE_DISABLE_FIND_PACKAGE_ZLIB}
    -DCMAKE_REQUIRE_FIND_PACKAGE_ZLIB=${CMAKE_REQUIRE_FIND_PACKAGE_ZLIB}
)

Приведенный ниже vcpkg_check_features() фрагмент кода эквивалентен.

vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
  FEATURES
    "zlib"    CMAKE_REQUIRE_FIND_PACKAGE_ZLIB
  INVERTED_FEATURES
    "zlib"    CMAKE_DISABLE_FIND_PACKAGE_ZLIB
)

vcpkg_cmake_configure(
    SOURCE_PATH ${SOURCE_PATH}
    OPTIONS
      ${FEATURE_OPTIONS}
)

ZLIB В фрагменте кода учитывается регистр. Дополнительные сведения см. в документации CMAKE_DISABLE_FIND_PACKAGE_<PackageName> и CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>.

Lib считается конфликтующим, если он выполняет одно из следующих действий:

  • Определите main
  • Определение malloc
  • Определите символы, которые также объявлены в других библиотеках

Конфликтующие библиотеки обычно задумываются по проекту и не считаются дефектом. Так как некоторые системы сборки ссылаются на все в каталоге lib, они должны быть перемещены в подкаталог с именем manual-link.

Установка предварительно созданных двоичных файлов

Порты, устанавливающие предварительно созданные (только двоичные) артефакты, разрешены, но настоятельно не рекомендуется, при условии, что они не блокируют изменение версий других портов. Сборка из источника предпочтительна, так как она учитывает все параметры vcpkg, которые изменяют компилятор или флаги.

Мы отклоним порты, которые соответствуют всем следующим условиям:

  1. Установка предварительно созданных двоичных файлов, а не сборка из источника и
  2. Эти двоичные файлы имеют (или требуют во время выполнения) зависимости, предоставляемые другими портами в курированном реестре, и
  3. Установленные артефакты входят в опубликованный домен ссылок vcpkg, то есть они устанавливают библиотеки/заголовки/CMake или метаданные pkg-config, к которым предполагается, что будут ссылаться нижестоящие порты или проекты пользователей.

Обоснование. Это сочетание эффективно блокирует ABI графа зависимостей до версий, используемых при создании предварительно созданного вышестоящего объекта. vcpkg не может безопасно обновлять (например, zlibopensslили аналогичные зависимости, не рискуя тонким разрывом ODR/ ABI для потребителей, связывающихся с предварительно созданной библиотекой, и пользователи могут пропустить критически важные исправления безопасности).

"Вводит опубликованный домен ссылки", как правило, означает любой из следующих вариантов:

  • Установка .lib, .a, .so или .dylib импорт библиотек, предназначенных для потребителей, чтобы делать ссылки на них.
  • Заголовочные файлы, которые ссылаются (непосредственно или через встроенный/шаблонный код) на символы, типы или макросы из других портов vcpkg.
  • Установка файлов конфигурации CMake и pkg-config, которые используют find_dependency() / Requires: на других портах vcpkg.

Допустимые (но по-прежнему не рекомендуется) сценарии:

  • Вспомогательные инструменты, предназначенные для использования только на хосте (исполняемые файлы), применяемые на этапе сборки, результаты работы которых используются, но которые не включаются в зависимые порты, при условии, что они либо интегрируют свои зависимости в виде приватных компонентов, либо полагаются только на стандартные системные библиотеки среды выполнения.
  • Полностью автономные предварительно собранные библиотеки, которые статически связывают все зависимости OSS и не предоставляют свои символы или типы через установленные заголовки или экспортируемые интерфейсы (потребители не могут отслеживать их или зависеть от транзитивного ABI).
  • Пакеты, доступные только для данных, прошивки или активов, не связанные с пользовательским кодом.

Запрещенные примеры:

  • Предварительно созданный libfoo, который устанавливает lib/libfoo.lib и заголовки, включая <zlib.h>, и скомпилирован против определённой версии zlib; потребители затем ссылаются на libfoo, ожидая совместимости.
  • Предварительно собранный SDK, который устанавливает файл пакета CMake с вызовом find_dependency(OpenSSL), в то время как двоичный файл был скомпилирован с более старой версией OpenSSL.

Устранение рисков и альтернатив:

  • Предоставьте сборку из источника с помощью исходных скриптов или добавьте тонкую оболочку CMake.
  • Попросите основной проект опубликовать исходный релиз или воспроизводимые инструкции по сборке; укажите ссылку на проблему или PR в основном проекте в комментарии portfile.cmake или vcpkg.json.
  • Используйте оверлей-порт или частный реестр для предварительно построенных решений, специфичных для организации, если они не соответствуют этим правилам.

Управление версиями

Следуйте общим соглашениям для "version" поля

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

Если вышестоящий проект не публиковал выпуск уже некоторое время, не изменяйте схему версионирования порта version-date, чтобы получить последние изменения. Эти фиксации могут включать изменения, которые не готовы к рабочей среде. Вместо этого попросите вышестоящий репозиторий опубликовать новый выпуск.

Обновите поле "port-version" в манифесте файла измененных портов.

vcpkg использует это поле для определения того, является ли данный порт устаревшим и следует изменять при изменении поведения порта.

Наше соглашение состоит в том, чтобы использовать поле "port-version" для изменений в порте, которые не изменяют версию наверху, и сбрасывать "port-version" обратно до нуля при обновлении этой версии.

Пример:

  • В настоящее время версия пакета Zlib — 1.2.1, без явного "port-version" (эквивалент "port-version" равен 0).
  • Вы обнаружили, что развернут неправильный файл авторских прав, и исправили это в portfile.
  • Следует обновить это поле в файле манифеста с "port-version" на 1.

Дополнительные сведения см. в документации по версионированию.

Обновите файлы версий в versions/ любых измененных портах

vcpkg использует набор файлов метаданных для управления версиями. Эти файлы находятся в следующих расположениях:

  • ${VCPKG_ROOT}/versions/baseline.json, (этот файл является общим для всех портов) и
  • ${VCPKG_ROOT}/versions/${first-letter-of-portname}-/${portname}.json (по одному на порт).

Например, для zlib соответствующие файлы:

  • ${VCPKG_ROOT}/versions/baseline.json
  • ${VCPKG_ROOT}/versions/z-/zlib.json

Мы ожидаем, что каждый раз при обновлении порта вы также обновляете файлы версий.

Рекомендуемый способ обновления этих файлов — выполнить x-add-version команду, например:

vcpkg x-add-version zlib

При одновременном обновлении нескольких портов можно выполнить следующую команду:

vcpkg x-add-version --all

для обновления файлов для всех измененных портов одновременно.

Дополнительные сведения см. в статьях справочника по версионированию и реестров.

Исправление

vcpkg — это решение для упаковки, а не окончательные владельцы компонентов, которые мы развертываем. В некоторых случаях необходимо применять исправления для улучшения совместимости компонентов с платформами или совместимости компонентов друг с другом.

  • Мы хотим избежать исправлений, которые:
    • upstream, вероятно, не согласится с
    • вызвать уязвимости или сбои
    • Мы не можем поддерживать обновления вышестоящей версии
    • достаточно значительный, чтобы привести к конфликтам лицензий с самим репозиторием vcpkg

Уведомите владельцев апстрима о соответствующих патчах.

Если исправление может оказаться полезным в вышестоящей части, вышестоящей части должно быть уведомлено о содержимом исправления. (Исправления, которые применяют специфическое для vcpkg поведение, не связанное с исходным проектом, например удаление встроенной зависимости, не требуют оповещения.)

Чтобы избежать ситуаций, когда верхний эшелон не согласен с исправлением, мы будем ждать по крайней мере 30 дней, чтобы внедрить такие исправления.

Мы пропустим этот период ожидания, если у нас есть высокая уверенность в том, что изменение правильно. Примеры исправлений высокой достоверности включают, но не ограничиваются следующими примерами:

  • Принятие upstream в качестве исправления (например, резервное копирование определенного изменения из вышестоящего запроса на вытягивание объединено).
  • Добавление отсутствующих #includes.
  • Небольшие и очевидные исправления кода продукта (например, инициализация неинициализированной переменной).
  • Отключение компонентов сборки, таких как тесты или примеры, неуместные в vcpkg.

Предпочитать варианты по сравнению с исправлением

Предпочтительнее задать параметры в вызове vcpkg_configure_xyz(), а не напрямую изменять настройки.

Распространенные варианты, позволяющие избежать исправления:

  • [MSBUILD] <PropertyGroup> Параметры внутри файла проекта можно переопределить с помощью /p: параметров
  • [CMAKE] find_package(XYz) Вызовы в скриптах CMake можно отключить с помощью -DCMAKE_DISABLE_FIND_PACKAGE_XYz=ON
  • [CMAKE] Переменные кэша (объявленные как set(VAR "value" CACHE STRING "Documentation") или option(VAR "Documentation" "Default Value")) можно переопределить, просто передав их в командной строке как -DVAR:STRING=Foo. Одно из важных исключений заключается в том, что параметр FORCE передается в set(). Дополнительные сведения см. в документации по CMake set

Предпочитайте скачивание утвержденных исправлений вместо их интеграции в порт.

Если утвержденный или объединенный файл исправлений можно получить из вышестоящей части, порты должны попытаться скачать их и применить их вместо того, чтобы они были в составе файлов портов. Этот процесс предпочтичен, так как он:

  • Подтверждает, что основная ветка приняла изменения в патче.
  • Упрощает процесс проверки, перекладывая ответственность на более ранний этап.
  • Уменьшает размер репозитория vcpkg для пользователей, которые не используют исправление
  • Избегает конфликтов лицензий с репозиторием vcpkg

Исправления следует скачать из стабильной конечной точки, чтобы избежать конфликтов SHA. При скачивании файлов патчей из pull request или коммита из GitHub и GitLab параметр ?full_index=1 должен быть добавлен в URL-адрес скачивания.

Примеры:

  • https://github.com/google/farmhash/pull/40.diff?full_index=1
  • https://github.com/linux-audit/audit-userspace/commit/f8e9bc5914d715cdacb2edc938ab339d5094d017.patch?full_index=1
  • https://gitlab.kitware.com/paraview/paraview/-/merge_requests/6375.diff?full_index=1

Предпочесть переопределение значений исправлений VCPKG_<VARIABLE>

Некоторые переменные, префиксированные VCPKG_<VARIABLE>, имеют эквиваленты с CMAKE_<VARIABLE>. Однако не все из них передаются во внутреннюю сборку пакета (см. реализацию: цепочку инструментов Windows).

Рассмотрим следующий пример:

set(VCPKG_C_FLAGS "-O2 ${VCPKG_C_FLAGS}")
set(VCPKG_CXX_FLAGS "-O2 ${VCPKG_CXX_FLAGS}")

Используя vcpkgвстроенную цепочку инструментов, это работает, так как значение VCPKG_<LANG>_FLAGS пересылается в соответствующую CMAKE_LANG_FLAGS переменную. Но пользовательский цепочка инструментов, которая не знает о vcpkgпеременных, не перенаправит их.

Из-за этого предпочтительнее исправить систему сборки непосредственно при настройке CMAKE_<LANG>_FLAGS.

Минимизация исправлений

При внесении изменений в библиотеку старайтесь свести к минимуму окончательный дифф. Это означает, что при внесении изменений, влияющих на регион, не следует переформатировать вышестоящий исходный код. При отключении условного условия лучше добавить AND FALSE или && 0 в условие, чем удалить каждую строку условного. Если большой регион необходимо отключить, то он короче, чтобы добавить if(0) или #if 0 вокруг региона вместо удаления каждой строки в исправлении.

Не добавляйте исправления, если порт устарел и обновляет порт до более новой выпущенной версии, решит ту же проблему. vcpkg предпочитает обновлять порты, а не исправлять устаревшие версии.

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

Не реализуйте функции в исправлениях

Целью исправления в vcpkg является обеспечение совместимости с компиляторами, библиотеками и платформами. Не следует реализовывать новые функции в соответствии с правильной процедурой Open Source (отправка проблемы/PR/т. д.).

По умолчанию не создавайте тесты и документы и примеры

При отправке нового порта проверьте все параметры, например BUILD_TESTS или WITH_TESTS или POCO_ENABLE_SAMPLES убедитесь, что дополнительные двоичные файлы отключены. Это сводит к минимуму время сборки и зависимости для среднего пользователя.

При необходимости можно добавить test функцию, которая позволяет создавать тесты, однако это не должно быть в списке Default-Features .

Разрешить существующим пользователям библиотеки переключиться на vcpkg

Не добавляйте CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS

Если автор библиотеки уже не использует его, мы не должны использовать эту функцию CMake, так как она плохо взаимодействует с шаблонами C++ и нарушает определенные функции компилятора. Библиотеки, которые не предоставляют файл .def и не используют объявления __declspec() просто не поддерживают общие сборки для Windows и должны быть помечены следующим образом:

if(VCPKG_TARGET_IS_WINDOWS)
    vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
endif()

Не переименовывать двоичные файлы вне имен, заданных источником

Это означает, что если в вышестоящей библиотеке есть разные имена в выпуске и отладке (libx и libxd), то библиотека отладки не должна быть переименована в libx. Наоборот, если в библиотеке на более высоком уровне то же самое имя используется как в релизной версии, так и в отладочной версии, мы не должны вводить новое имя.

Важное предостережение:

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

Если библиотека создает файлы интеграции CMake (foo-config.cmake), переименование необходимо сделать путем исправления самой сборки CMake, а не просто вызывать file(RENAME) выходные архивы или ЛИБ.

Наконец, файлы DLL в Windows никогда не следует переименовывать после сборки, так как это нарушает работу сгенерированных файлов LIB.

Манифесты

Нам требуется отформатировать файл манифеста. Используйте следующую команду для форматирования всех файлов манифеста:

> vcpkg format-manifest --all

Тройня

В настоящее время мы не принимаем запросы на добавление триплетов, не относящихся к сообществу. Продвижение от сообщества до полного тройного состояния в основном основано на бюджете оборудования для тестирования таких тройных и будет управляться метриками, отправленными vcpkg, чтобы максимально повысить вероятность того, что люди на самом деле используют полностью протестированы.

Если мы добавим триплеты сообщества:

  • Показано, что люди действительно будут использовать этот триплет сообщества; и
  • Мы не знаем, сломана ли такая тройка.

Например, мы не добавили триплет в https://github.com/microsoft/vcpkg/pull/29034, так как автор просто пытался "завершить набор", а не указывал, что они на самом деле будут использовать такую вещь, и мы не добавили linux-dynamic до тех пор, пока не было создано решение patchelf, чтобы сделать результаты перемещаемыми.

Полезные заметки о реализации

Портфайлы выполняются в режиме скрипта

Хотя portfile.cmake и CMakeLists.txt используют общий синтаксис и основные конструкции языка CMake (также известны как "команды сценариев"), портфайлы выполняются в режиме скрипта, в то время как файлы CMakeLists.txt выполняются в режиме проекта. Самое важное различие между этими двумя режимами заключается в том, что "Режим скрипта" не имеет концепций "Цепочка инструментов", "Язык" и "Целевой". Любое поведение, включая команды сценариев, которые зависят от этих конструкций (напримерCMAKE_CXX_COMPILER, CMAKE_EXECUTABLE_SUFFIXCMAKE_SYSTEM_NAME) не будут правильными.

Портфайлы имеют прямой доступ к переменным, заданным в триплетном файле, но CMakeLists.txt не имеют к ним доступа (хотя часто происходит подстановка VCPKG_LIBRARY_LINKAGEBUILD_SHARED_LIBS).

Портфайлы и сборки Project, вызываемые портфайлами, выполняются в разных процессах. Концептуально:

+----------------------------+       +------------------------------------+
| CMake.exe                  |       | CMake.exe                          |
+----------------------------+       +------------------------------------+
| Triplet file               | ====> | Toolchain file                     |
| (x64-windows.cmake)        |       | (scripts/buildsystems/vcpkg.cmake) |
+----------------------------+       +------------------------------------+
| Portfile                   | ====> | CMakeLists.txt                     |
| (ports/foo/portfile.cmake) |       | (buildtrees/../CMakeLists.txt)     |
+----------------------------+       +------------------------------------+

Чтобы определить узел в портфайле, стандартные переменные CMake подходят. (CMAKE_HOST_WIN32).

Чтобы определить целевой объект в портфайле, следует использовать переменные vcpkg triplet (VCPKG_CMAKE_SYSTEM_NAME).

См. также нашу документацию о триплетах, в которой перечислены все возможные параметры.