Compartir a través de


Guía del mantenedor

En este documento se describen las directivas, las directrices y los procedimientos recomendados que se deben seguir al realizar contribuciones a vcpkg.

Está pensado para servir el mismo rol que:

Objetivos de diseño del registro curado

Los puertos deben instalarse simultáneamente

Un puerto incluido en el registro mantenido debe compilarse correctamente en presencia de todos los demás puertos del registro. La combinación completa de puertos se prueba regularmente mediante la CI automatizada de vcpkg.

Un puerto no debe realizar ninguna de las acciones siguientes:

Las excepciones se realizan para:

  • Puertos que reemplazan a otro en calidad oficial. Un proyecto sucesor.
  • Puertos en los que no hay ninguna superposición de plataforma. Por ejemplo, un reemplazo exclusivo para Windows de un proyecto de Linux que no admite Windows.
  • Puertos que preceden a esta directiva y tienen un número demostrablemente significativo de usuarios y colaboradores.

Los puertos que no se ajustan a esta directiva no se pueden aceptar en el registro mantenido.

Sugerencia

Si el puerto no se puede incluir en el registro mantenido, considere la posibilidad de publicar un registro personalizado o usar un puerto de superposición.

Los puertos deben probarse en al menos un triplete oficial

Todos los puertos del registro mantenido deben probarse en CI para al menos un triplete oficial.

Las excepciones se realizan para:

  • Circunstancias temporales, como:
    • El puerto no se construye sin una actualización de la infraestructura de integración continua.
    • El puerto no se construye debido a un cambio disruptivo reciente en una dependencia upstream y se está trabajando para resolver la situación.
  • Puertos que preceden a esta directiva y tienen un número demostrablemente significativo de usuarios y colaboradores.

Los nuevos puertos que no se pueden probar en al menos un triplete oficial no se aceptan en el registro curado. Los puertos existentes que ya no se ajustan a esta política y que no están exentos temporalmente se eliminan del registro.

Sugerencia

Si el puerto no se puede incluir en el registro mantenido, considere la posibilidad de publicar un registro personalizado o usar un puerto de superposición.

Los proyectos empaquetados deben ser estables y mantenidos activamente

Los proyectos empaquetados en el registro mantenido deben estar en mantenimiento activo. Los puertos para proyectos inactivos se pueden anular de la lista.

Un proyecto se considera inactivo si:

  • Sus mantenedores han declarado el proyecto abandonado.
  • Se archiva o ya no acepta contribuciones.
  • Los mantenedores no responden o no son accesibles.
  • No se han realizado cambios significativos en mucho tiempo.

Las excepciones se realizan para proyectos fundamentales que se consideran maduros y estables y no reciben cambios a menudo. Por ejemplo: zlib.

Los proyectos empaquetados deben estar maduros

Los proyectos empaquetados en el registro mantenido deben estar maduros y destinados al consumo por parte de los usuarios de vcpkg. Los proyectos destinados a uso personal deben publicarse en registros personalizados.

Un proyecto se considera lo suficientemente maduro para el registro curado, si se cumple una de estas declaraciones:

  • El proyecto tiene un lanzamiento de al menos seis meses de antigüedad.
  • El proyecto muestra al menos seis meses de desarrollo público activo.
  • El proyecto es un componente oficial de otro proyecto que cumple los requisitos anteriores. Por ejemplo, una nueva librería Boost o componente Qt.
  • El proyecto muestra una madurez equivalente a los requisitos anteriores en alguna otra capacidad.

Algunos indicadores de inmadez del proyecto son:

  • El proyecto no aparece en los motores de búsqueda.
  • El proyecto tiene cambios de nombre frecuentes.
  • Entra en conflicto con otras bibliotecas.

Sugerencia

Si el puerto no se puede incluir en el registro mantenido, considere la posibilidad de publicar un registro personalizado o usar un puerto de superposición.

Estructura de PR

Haga pull requests separadas por puerto

Las solicitudes de incorporación de cambios (PR) más pequeñas son más fáciles de revisar. Las solicitudes de incorporación de cambios deben realizar cambios en un único puerto. Esto también reduce los tiempos de espera de los resultados de integración continua (CI).

Limitar las solicitudes de incorporación de cambios a un único puerto puede no ser posible en algunos casos. Por ejemplo, al cambiar un puerto, es necesario actualizar o aplicar revisiones a los consumidores finales.

Evitar cambios triviales en archivos sin modificar

No realice cambios triviales en un puerto sin modificar, por ejemplo: reformatear, cambiar el nombre de variables o corregir errores tipográficos. Cualquier cambio que no afecte a la salida de la instalación de un puerto se considera trivial. Los cambios triviales consumen el tiempo de proceso que podría aprovecharse mejor en otras tareas.

Usar nombres de puerto distintivos

El nombre de un puerto debe indicar su contenido.

La búsqueda del nombre del puerto en un motor de búsqueda o exploradores de paquetes especializados, como Repology, debería conducir a su proyecto correspondiente.

Los puertos con nombres cortos o con nombre después de palabras comunes requieren desambiguación. Esto solo se aplica al nombre del puerto en el registro curado, el nombre del proyecto empaquetado y su contenido no son necesarios para cumplir con esta política.

Las excepciones se hacen para los puertos que empaquetan un proyecto con una fuerte asociación con el nombre de su puerto. Por ejemplo: libpng, openssl, o zlib.

Para cumplir con esta directiva, los nuevos puertos con nombres ambiguos pueden usar un prefijo, como:

  • Propietario, nombre de usuario o organización del repositorio. Ejemplo: google-cloud-cpp. Para los puertos de los proyectos de GitHub, el propietario de GitHub es un prefijo <github owner>-<repository name> no ambiguo aceptable.
  • Nombre de un conjunto al que pertenece el paquete: boost-dll. Solo si el paquete es un componente oficial de este conjunto de aplicaciones.

Ejemplo: Nombre de puerto ambiguo

Un puerto con el nombre ip se considera ambiguo porque:

  • el nombre es demasiado corto,
  • el nombre es una palabra común y
  • el nombre no está fuertemente asociado a ningún proyecto singular.

Para determinar si un nombre es ambiguo, quite los siguientes prefijos comunes y sufijos usados por C++ y proyectos de código abierto del nombre del puerto:

  • cpp
  • free
  • lib
  • open
  • números

Por ejemplo: ip-cpp, libip y ip5 son ambiguos porque se reducen a la misma ip raíz.

Limitar el cambio de nombre del puerto

Para evitar confusiones para los usuarios, los puertos no se pueden cambiar de nombre hasta después de un año de su último cambio.

Utilice los borradores de GitHub

Los borradores de PRs de GitHub son una gran manera de obtener retroalimentación de CI o humana sobre el trabajo que aún no está listo para fusionar. La mayoría de los nuevos PR deben ser abiertos como borradores y convertidos en PR normales una vez que el CI pasa.

Para obtener más información acerca de GitHub Draft PRs, consulte Introducción a los borradores de pull requests.

El equipo de vcpkg puede convertir el PR a borrador durante el proceso de revisión. Normalmente, con una solicitud para realizar cambios en el código o comentarios que indican cuándo marcar el PR como listo para revisión.

Cerrar solicitudes de incorporación de cambios inactivas

El equipo de vcpkg puede cerrar las solicitudes de incorporación de cambios (PR) que no han tenido actividad durante más de 60 días.

En el caso de las solicitudes de incorporación de cambios en un estado revisable, la cuenta atrás comienza desde la última vez que un mantenedor de vcpkg realiza una solicitud de cambios o solicita aclaración. Si el colaborador no realiza ninguna actividad dentro de 60 días, la PR (solicitud de incorporación de cambios) se considera obsoleta y puede cerrarse a discreción del equipo.

Un PR está en un estado revisable cuando:

  • No tiene comprobaciones de pr erróneas o el colaborador solicita ayuda o justifica el motivo de los errores.
  • No tiene ningún cambio ni aclaración solicitados pendientes del mantenedor de vcpkg.
  • No tiene conflictos de combinación.

Las solicitudes de incorporación de cambios que estén inactivas durante más de 60 días y no se encuentren en estado revisable, pueden cerrarse sin haber sido revisadas.

Portfiles

Evitar funciones auxiliares en desuso

En este momento, los siguientes helpers están obsoletos:

Algunas de las funciones ayudantes de sustitución están en "puertos de herramientas" para permitir a los consumidores fijar su comportamiento en versiones específicas, para permitir bloquear el comportamiento de los ayudantes en una versión concreta. Los puertos de herramientas deben agregarse al puerto "dependencies", de la siguiente manera:

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

Evitar comentarios excesivos en archivos de configuración de puertos

Idealmente, los archivos de puerto deben ser cortos, sencillos y tan declarativos como sea posible. Elimine cualquier comentario introducido por el comando create antes de enviar un PR.

Uso de minúsculas para cadenas de dígitos hexadecimales

Muchas de las características de vcpkg se basan en la comparación de cadenas de dígitos hexadecimales. Algunos ejemplos incluyen, entre otros, hashes SHA512, identificadores de confirmación de Git y hashes de objetos de árbol.

Internamente, vcpkg utiliza la normalización de minúsculas para las comparaciones de tales valores donde el casing es irrelevante. Sin embargo, las herramientas construidas sobre la infraestructura de vcpkg pueden no tener las mismas consideraciones. Por este motivo, se requieren cadenas hexadecimales

para ser minúsculas para la coherencia en los siguientes escenarios:

  • Parámetro SHA512 en las funciones auxiliares de vcpkg.
  • El parámetro REF en las funciones auxiliares de vcpkg, cuando su valor es una cadena hexadecimal.
  • El objeto git-tree en archivos de base de datos de versiones.
  • Objeto sha512 del scripts/vcpkg-tools.json archivo.
  • Otros lugares donde el uso de mayúsculas/minúsculas en la cadena hexadecimal no es importante.

Los puertos no deben ser dependientes de la ruta de acceso

Los puertos no deben cambiar su comportamiento en función de los puertos que ya están instalados en un formulario que cambiaría el contenido que instala ese puerto. Por ejemplo, dado:

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

y

> vcpkg install b

los archivos instalados por b deben ser los mismos, independientemente de la influencia de la instalación anterior de a. Esto significa que los puertos no deben intentar detectar si otro puerto proporciona algo en el árbol instalado antes de realizar alguna acción. A continuación se describe una causa específica y común del comportamiento "dependiente de la ruta de acceso" en "Al definir características, controlar explícitamente las dependencias".

Regla de atribución de puerto única

En todo el sistema vcpkg, no se espera que un usuario use dos puertos simultáneamente para proporcionar el mismo archivo. Si un puerto intenta instalar un archivo ya proporcionado por otro archivo, se producirá un error en la instalación. Si un puerto quiere usar un nombre muy común para un encabezado, por ejemplo, debe colocar esos encabezados en un subdirectorio en lugar de en include.

Esta propiedad se comprueba periódicamente mediante ejecuciones de integración continua que intentan instalar todos los puertos del Registro, lo que producirá un error si FILE_CONFLICTS dos puertos proporcionan el mismo archivo.

Añadir exportaciones CMake en un espacio de nombres no oficial.

Un diseño básico ideal de vcpkg es no crear "bloqueo" para los usuarios. En el sistema de construcción, no debe haber ninguna diferencia entre depender de una biblioteca del sistema y depender de una biblioteca de vcpkg. Para ello, evitamos añadir exportaciones CMake u objetivos a las bibliotecas existentes con "el nombre obvio", para permitir que las upstreams añadan sus propias exportaciones CMake oficiales sin entrar en conflicto con vcpkg.

Para ello, las configuraciones de CMake que el puerto exporta, y que no están en la biblioteca original, deben llevar unofficial- como prefijo. Cualquier objetivo adicional debe estar en el espacio de nombres unofficial::<port>::.

Esto significa que el usuario debería ver lo siguiente:

  • find_package(unofficial-<port> CONFIG) como la forma de llegar al paquete exclusivo de vcpkg
  • unofficial::<port>::<target> como un objetivo exportado de ese puerto.

Ejemplos:

  • brotli crea el unofficial-brotli paquete, lo que genera el destino unofficial::brotli::brotli.

Cada puerto tiene que proporcionar un archivo denominado copyright en la carpeta ${CURRENT_PACKAGES_DIR}/share/${PORT}. Si el contenido de licencia de un paquete está disponible en sus archivos de origen, este archivo se debe crear mediante una llamada a vcpkg_install_copyright(). vcpkg_install_copyright también agrupa varios archivos de copyright si es necesario.

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

Un método anterior para crear manualmente este archivo es con el comando integrado de file CMake. Esto se desaconseja en favor de vcpkg_install_copyright en los nuevos puertos, pero todavía está permitido.

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

Si el contenido de la licencia en los archivos de origen ascendentes no está en formato de texto (por ejemplo, un archivo PDF), copyright debe contener una explicación sobre cómo un usuario puede encontrar los requisitos de licencia. Si es posible, también debe incluir un vínculo a los archivos de origen originales que indican esto, por lo que los usuarios pueden comprobar si está actualizado.

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
]])

Restricciones de versión en puertos

Por lo general, se deben evitar restricciones de versión dentro de los puertos, ya que pueden dificultar la evolución independiente de los proyectos. Agregar estas restricciones solo se permite cuando hay una justificación bien documentada, como la incompatibilidad probada con versiones anteriores específicas. Estas restricciones no deben usarse simplemente para mantener la paridad con proyectos independientes.

Las variables de MAYBE_UNUSED_VARIABLES deben aplicarse a al menos una configuración

Al agregar una nueva variable para MAYBE_UNUSED_VARIABLES silenciar una advertencia durante el paso de configuración de CMake, debe agregar un comentario que explique el caso cuando se aplique la nueva variable. Si una variable no se aplica en ninguna configuración, es muy probable que exista un error subyacente (por ejemplo, un nombre de variable mal escrito) y agregarlo no tenga ningún efecto real en la compilación.

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
)

Características

No usar características para implementar alternativas

Las características deben tratarse como funcionalidad de adición. Si port[featureA] instala e port[featureB] instala , port[featureA,featureB] debe instalarse. Además, si un segundo puerto depende de [featureA] y un tercer puerto depende de [featureB], la instalación de ambos, el segundo y el tercero, debe tener sus dependencias satisfechas.

Las bibliotecas en esta situación deben elegir una de las opciones disponibles tal como se expresa en vcpkg, y los usuarios que deseen una configuración diferente deben utilizar los puertos superpuestos en este momento.

Los ejemplos existentes que no aceptaríamos hoy conservamos por motivos de compatibilidad con versiones anteriores:

  • libgit2, libzip, open62541 todas tienen características para seleccionar un back-end TLS o criptográfico. curl tiene diferentes opciones de backend de criptografía, pero permite seleccionar entre ellas en tiempo de ejecución, lo que significa que se mantiene el principio anterior.
  • darknet tiene opencv2, opencv3características para controlar qué versión de opencv se va a usar para sus dependencias.

Una característica puede interactuar con la versión preliminar o la funcionalidad beta

A pesar de lo anterior, si hay una rama en versión preliminar o similar en la que la funcionalidad de vista previa tiene una alta probabilidad de no interrumpir la funcionalidad que no es de versión preliminar (por ejemplo, sin eliminaciones de API), una característica es aceptable para modelar esta configuración.

Ejemplos:

  • Los SDK de Azure (con el formato azure-Xxx) tienen una public-preview característica.
  • imgui tiene una característica experimental-docking que activa su rama de acoplamiento de vista previa que utiliza un merge commit adjunto a cada una de sus versiones públicas numeradas.

Las características predeterminadas no deben agregar API

Nota:

Una característica habilitada de forma predeterminada por el sistema de compilación ascendente no implica que la característica se debe agregar a las default-features entradas. Dado que el propósito previsto de default-features no es modelar las decisiones tomadas en la etapa previa, sino proporcionar comodidad a los usuarios del modo clásico.

Las características predeterminadas están diseñadas para garantizar que se instale una compilación razonablemente funcional de una biblioteca para los clientes que no saben que lo usan. Si no saben que usan una biblioteca, no pueden saber enumerar las características. Por ejemplo, libarchive expone características que habilitan algoritmos de compresión en una interfaz genérica existente; si se compila sin ninguna de estas características, es posible que la biblioteca no tenga ninguna utilidad.

Uno debe considerar cuidadosamente si una característica debe estar activada de forma predeterminada, ya que deshabilitar las características predeterminadas es compleja.

La deshabilitación de una característica predeterminada como consumidor "transitivo" requiere:

  • Todos los clientes deshabilitan explícitamente las características por defecto a través de "default-features": false o incluyendo [core] en la lista de características en la línea de comandos.
  • Asignar un nombre a la dependencia transitiva en la vcpkg install línea de comandos o como una dependencia directa en el manifiesto de nivel superior

En el registro mantenido de vcpkg, si la característica agrega API adicionales, ejecutables u otros archivos binarios, debe estar desactivada de forma predeterminada. En caso de duda, no marque una característica como predeterminada.

No usar características para controlar alternativas en interfaces publicadas

Si un consumidor de un puerto depende solo de la funcionalidad central de ese puerto, con alta probabilidad no deben ser rotos por la activación de la característica. Esto es aún más importante cuando el consumidor no controla directamente la alternativa, sino mediante la configuración del compilador como /std:c++17 / -std=c++17.

Los ejemplos existentes que no aceptaríamos hoy conservamos por motivos de compatibilidad con versiones anteriores:

  • redis-plus-plus[cxx17] controla un polyfill pero no incorpora la configuración en el árbol instalado.
  • ace[wchar] cambia todas las API para aceptar const wchar_t* en lugar de const char*.

Una característica puede reemplazar polyfills con alias siempre que el reemplazo se incorpore al árbol instalado.

No obstante lo anterior, los puertos pueden eliminar los polyfills con una feature, siempre y cuando:

  1. La activación de la función cambie los polyfills por alias de la entidad polyfilled
  2. El estado del polyfill está integrado en las cabeceras instaladas, de tal forma que los errores de tiempo de ejecución "imposibles" de desajuste ABI son improbables
  3. Es posible que un consumidor del puerto escriba código que funcione en ambos modos, por ejemplo mediante una definición de tipo que sea polirrellenada o no.

Ejemplo:

  • abseil[cxx17] cambia absl::string_view a un reemplazo o std::string_view; el parche implementa el requisito de baking.

Si es fundamental exponer las alternativas subyacentes, se recomienda proporcionar mensajes en tiempo de compilación para indicar al usuario cómo copiar el puerto en una superposición privada:

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")

Técnicas de construcción

No usar dependencias proporcionadas

No use copias embebidas de bibliotecas. Todas las dependencias deben dividirse y empaquetarse por separado para que se puedan actualizar y mantener.

Las dependencias proporcionadas por el proveedor presentan varios desafíos que entran en conflicto con los objetivos de vcpkg de proporcionar un sistema de administración de paquetes confiable, coherente y fácil de mantener:

Dificultad en las actualizaciones: las copias insertadas de bibliotecas dificultan el seguimiento y la aplicación de actualizaciones, incluidas las revisiones de seguridad, de los proyectos ascendentes. Esto conduce a posibles riesgos de seguridad y dependencias obsoletas en el ecosistema.

Conflictos de símbolos: las dependencias de proveedor pueden provocar conflictos de símbolos cuando varios paquetes incluyen versiones diferentes de la misma biblioteca.

Por ejemplo: Si package A vendors Library X (versión 1) y Package B vendors Library X (versión 2), una aplicación que vincula ambos paquetes puede experimentar errores en tiempo de ejecución o un comportamiento indefinido debido a símbolos conflictivos.

Al empaquetar las dependencias por separado, vcpkg garantiza que se use una sola versión de una biblioteca en todos los paquetes, lo que elimina dichos conflictos.

Cumplimiento de licencias: las dependencias incorporadas pueden ocultar las licencias de las bibliotecas insertadas, lo que podría violar sus términos o crear incompatibilidades.

Mayor carga de mantenimiento: mantener las dependencias proporcionadas sincronizadas con sus versiones ascendentes requiere un esfuerzo manual significativo y, a menudo, conduce a un trabajo duplicado entre paquetes.

Preferir el uso de CMake

Cuando haya varios sistemas de compilación disponibles, prefiera usar CMake. Además, cuando sea correspondiente, puede ser más fácil y más manejable reescribir sistemas de compilación alternativos en CMake mediante directivas file(GLOB).

Ejemplos: abseil

Elegir archivos binarios estáticos o compartidos

Al compilar bibliotecas de CMake, vcpkg_cmake_configure() pasará el valor correcto para BUILD_SHARED_LIBS en función de la variante solicitada por el usuario.

Puede calcular parámetros de configuración alternativos mediante 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}
)

Si una biblioteca no ofrece opciones de configuración para seleccionar la variante de compilación, la compilación debe ser modificada. Cuando se parchea una compilación, siempre se debe intentar maximizar la mantenibilidad futura del puerto. Típicamente esto significa minimizar el número de líneas que necesitan ser tocadas para arreglar el problema en cuestión.

Ejemplo: Aplicación de revisiones a una biblioteca de CMake para evitar la creación de variantes no deseadas

Por ejemplo, al aplicar revisiones a una biblioteca basada en CMake, puede ser suficiente agregar EXCLUDE_FROM_ALL a destinos no deseados y encapsular la install(TARGETS ...) llamada en un if(BUILD_SHARED_LIBS). Esto será más corto que envolver o borrar cada línea que menciona la variante no deseada.

Para un proyecto CMakeLists.txt con el siguiente contenido:

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)

Solo es necesario aplicar revisiones a la install(TARGETS) línea.

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)

Al definir características, controle explícitamente las dependencias.

Al definir una característica que captura una dependencia opcional, asegúrese de que la dependencia no se usará accidentalmente cuando la característica no esté habilitada explícitamente.

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}
)

El fragmento de código siguiente que usa vcpkg_check_features() es equivalente.

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 en el fragmento distingue entre mayúsculas y minúsculas. Para obtener más información, consulte la documentación CMAKE_DISABLE_FIND_PACKAGE_<PackageName> y CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>.

Una lib se considera en conflicto si realiza alguna de las acciones siguientes:

  • Definir main
  • Definir malloc
  • Definir símbolos que también se declaran en otras bibliotecas

Las librerías conflictivas son típicamente por diseño y no se consideran un defecto. Dado que algunos sistemas de compilación se vinculan con todo en el directorio lib, estos deben moverse a un subdirectorio denominado manual-link.

Instalación de archivos binarios creados previamente

Los puertos que instalan artefactos creados previamente (solo binarios) se permiten, aunque se desaconsejan encarecidamente, siempre y cuando no bloqueen eficazmente el cambio de las versiones de otros puertos. Se prefiere compilar a partir del origen porque respeta todas las configuraciones de vcpkg que cambian el compilador o las banderas.

Rechazaremos los puertos que cumplan todas las condiciones siguientes:

  1. Instalación de archivos binarios creados previamente en lugar de compilar desde el origen y
  2. Esos archivos binarios tienen (o requieren en tiempo de ejecución) dependencias proporcionadas por otros puertos del registro mantenido y
  3. Los artefactos instalados entran en el dominio de enlace publicado de vcpkg, es decir, instalan bibliotecas/encabezados/CMake o metadatos pkg-config con los que se espera que los puertos descendentes o los proyectos de usuario se vinculen.

Justificación: esta combinación bloquea eficazmente la ABI del grafo de dependencias a las versiones usadas cuando se generó el precompilado de origen. vcpkg no puede actualizar de forma segura (por ejemplo) zlib, openssl o dependencias similares sin riesgo de interrupciones sutiles de ODR o ABI para los consumidores que enlazan con la biblioteca precompilada, y los usuarios pueden perder parches de seguridad críticos.

"Entra en el dominio de vínculo publicado" normalmente significa cualquiera de:

  • Instalación de .lib, .a, .so o .dylib o bibliotecas de importación destinadas a que los consumidores las vinculen.
  • Cabeceras de envío que hacen referencia (directamente o a través de código en línea o de plantilla) a símbolos, tipos o macros de otros puertos vcpkg.
  • Instalación de archivos config/pkg-config de CMake que invocan find_dependency() / Requires: en otros puertos vcpkg.

Escenarios permitidos (pero aún desaconsejados):

  • Las herramientas auxiliares exclusivas del host (ejecutables) utilizadas en tiempo de compilación cuyas salidas se utilizan, pero que no son vinculadas directamente por puertos dependientes, siempre que incorporen dependencias de forma privada o solo dependan de bibliotecas en tiempo de ejecución del sistema ubicuas.
  • Bibliotecas completamente autónomas precompiladas que enlazan estáticamente todas las dependencias de software de código abierto Y no exponen sus símbolos o tipos a través de archivos de cabecera instalados o interfaces exportadas (los consumidores no pueden observar o depender de una ABI transitiva).
  • Paquetes de solo datos, firmware, o de recursos no vinculados al código de usuario.

Ejemplos prohibidos:

  • Un libfoo precompilado que instala lib/libfoo.lib junto con encabezados, incluidos <zlib.h>, y se compiló contra una versión específica de zlib; luego los consumidores se vinculan contra libfoo con expectativa de compatibilidad.
  • Un SDK preconstruido que instala un archivo de paquete de CMake que llama find_dependency(OpenSSL) mientras el binario se compiló con un lanzamiento anterior de OpenSSL.

Mitigaciones y alternativas:

  • Proporcione una compilación desde la fuente mediante scripts de upstream o agregue un simple contenedor de CMake.
  • Pida a upstream que publique una versión basada en origen o instrucciones de compilación reproducibles; vincule el problema o la solicitud de incorporación de cambios ascendentes en un comentario en portfile.cmake o vcpkg.json.
  • Use un puerto de superposición o un registro privado para precompilaciones específicas de la organización que no puedan cumplir estas reglas.

Control de versiones

Siga las convenciones comunes para el "version" campo.

Al crear un puerto, siga la convención de control de versiones que usa el autor del paquete. Al actualizar el puerto, siga usando la misma convención a menos que la cadena ascendente indique lo contrario. Para obtener una explicación completa de nuestras convenciones, consulte nuestra documentación de control de versiones.

Si la cadena ascendente no ha publicado una versión en un tiempo, no cambie el esquema de control de versiones del puerto a version-date para obtener los cambios más recientes. Estas confirmaciones pueden incluir cambios que no están listos para producción. En su lugar, pida al repositorio ascendente que publique una nueva versión.

Actualice el campo "port-version" en el fichero de manifiesto de cualquier puerto modificado.

vcpkg usa este campo para determinar si un puerto determinado está obsoleto y se debe cambiar cada vez que cambia el comportamiento del puerto.

Nuestra convención es usar el campo "port-version" para cambios en el puerto que no cambien la versión upstream, y poner el "port-version" a cero cuando se haga una actualización de la versión upstream.

Por ejemplo:

  • La versión del paquete de Zlib es actualmente 1.2.1, sin "port-version" explícito (equivalente a un "port-version" de 0).
  • Ha descubierto que se ha implementado el archivo de copyright incorrecto y lo ha corregido en el archivo portfile.
  • Debe actualizar el "port-version" campo del archivo de manifiesto a 1.

Consulte la documentación de control de versiones para obtener más información.

Actualice los ficheros de versión en versions/ de cualquier puerto modificado

vcpkg usa un conjunto de archivos de metadatos para alimentar su característica de control de versiones. Estos archivos se encuentran en las siguientes ubicaciones:

  • ${VCPKG_ROOT}/versions/baseline.json, (este archivo es común a todos los puertos) y
  • ${VCPKG_ROOT}/versions/${first-letter-of-portname}-/${portname}.json (uno por puerto).

Por ejemplo, para zlib los archivos pertinentes son:

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

Esperamos que cada vez que actualice un puerto, también actualice sus archivos de versión.

El método recomendado para actualizar estos archivos es ejecutar el x-add-version comando, por ejemplo:

vcpkg x-add-version zlib

Si está actualizando varios puertos al mismo tiempo, en su lugar puede ejecutar:

vcpkg x-add-version --all

para actualizar los archivos de todos los puertos modificados a la vez.

Para obtener más información, consulte los artículos Referencia de control de versiones y Registros.

Aplicación de revisión

vcpkg es una solución de empaquetado, no los propietarios finales de los componentes que implementamos. En algunos casos, es necesario aplicar revisiones para mejorar la compatibilidad de los componentes con plataformas o la compatibilidad de los componentes entre sí.

  • Queremos evitar parches que:
    • no estén de acuerdo con el upstream
    • causen vulnerabilidades o fallos
    • seamos incapaces de mantener a través de las actualizaciones de la versión upstream
    • son lo suficientemente grandes como para provocar el entrelazamiento de licencias con el propio repositorio vcpkg.

Notificar a los propietarios de upstream los parches relevantes para upstream

Si un parche puede ser útil para el desarrollador, este debe ser notificado de su contenido. (Los parches que aplican un comportamiento específico de vcpkg no relacionado con upstream, como la devolución de una dependencia, no requieren notificación).

Para evitar situaciones en las que upstream no esté de acuerdo con el parche, esperaremos al menos 30 días para aplicar dichos parches.

Omitiremos este período de espera si tenemos alta confianza de que el cambio es correcto. Ejemplos de parches de alta confianza incluyen, pero no se limitan a:

  • La aceptación de aguas arriba como un parche (por ejemplo, backporting un cambio específico de un pull request aguas arriba ha fusionado).
  • Añadir #include que faltan.
  • Correcciones de código de producto pequeñas y obvias (por ejemplo, inicializando una variable sin inicializar).
  • Deshabilitación de componentes irrelevantes en vcpkg de la compilación, como pruebas o ejemplos.

Preferir alternativas antes que aplicar parches

Es preferible establecer opciones en una llamada a vcpkg_configure_xyz() en lugar de aplicar revisiones directamente a la configuración.

Opciones comunes que permiten evitar la aplicación de revisiones:

  • [MSBUILD] <PropertyGroup> ajustes dentro del archivo de proyecto puede ser anulado a través de parámetros /p:.
  • [CMAKE] Las llamadas a find_package(XYz) en scripts de CMake se pueden deshabilitar a través de -DCMAKE_DISABLE_FIND_PACKAGE_XYz=ON
  • [CMAKE] Las variables de caché (declaradas como set(VAR "value" CACHE STRING "Documentation") o option(VAR "Documentation" "Default Value")) se pueden invalidar simplemente pasandolas en la línea de comandos como -DVAR:STRING=Foo. Una excepción notable es si el FORCE parámetro se pasa a set(). Para obtener más información, consulte la documentación de CMake set .

Preferir la descarga de parches aprobados sobre la comprobación de ellos en el puerto

Si se puede obtener un fichero de parche aprobado o fusionado de aguas arriba, los puertos deberían intentar descargarlos y aplicarlos en lugar de tenerlos como parte de los ficheros del puerto. Este proceso se prefiere porque:

  • Confirma que upstream ha aceptado los cambios del parche
  • Simplifica el proceso de revisión cambiando las responsabilidades ascendentes
  • Reduce el tamaño del repositorio de vcpkg para los usuarios que no usan el parche.
  • Evita conflictos de licencias con el repositorio vcpkg.

Las revisiones deben descargarse desde un punto de conexión estable para evitar conflictos de SHA. Al descargar archivos de revisión desde una solicitud de incorporación de cambios o un commit desde GitHub y GitLab, el parámetro ?full_index=1 debe anexarse a la dirección URL de descarga.

Ejemplos:

  • 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

Prefiere parchear antes que anular valores VCPKG_<VARIABLE>

Algunas variables con VCPKG_<VARIABLE> prefijo tienen un equivalente CMAKE_<VARIABLE>. Sin embargo, no todos ellos se pasan a la construcción interna del paquete (ver implementación: Windows toolchain).

Considere el ejemplo siguiente:

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

Usando las cadenas de herramientas integradas de vcpkg esto funciona, porque el valor de VCPKG_<LANG>_FLAGS se reenvía a la variable CMAKE_LANG_FLAGS apropiada. Sin embargo, una cadena de herramientas personalizada que no conoce las vcpkgvariables no las reenvía.

Por este motivo, es preferible aplicar revisiones al sistema de compilación directamente al establecer CMAKE_<LANG>_FLAGS.

Minimizar parches

Al realizar cambios en una biblioteca, intente minimizar la diferencia final. Esto significa que no debe volver a formatear el código fuente ascendente al realizar cambios que afecten a una región. Al deshabilitar un condicional, es mejor agregar un AND FALSE o un && 0 a la condición que eliminar cada línea del condicional. Si una región grande necesita ser desactivada, es más corto añadir un if(0) o #if 0 alrededor de la región en lugar de borrar cada línea del parche.

No agregue revisiones si el puerto está obsoleto y la actualización del puerto a una versión más reciente publicada solucionaría el mismo problema. vcpkg prefiere actualizar puertos a parchear versiones obsoletas.

Esto ayuda a reducir el tamaño del repositorio vcpkg, así como a mejorar la probabilidad de que el parche se aplique a futuras versiones de código.

No implementar características en parches

El propósito de aplicar revisiones en vcpkg es habilitar la compatibilidad con compiladores, bibliotecas y plataformas. No se trata de implementar nuevas características sin seguir el procedimiento adecuado de Open Source (presentar un Issue/PR/etc.).

No compile pruebas, documentos o ejemplos de forma predeterminada

Al enviar un puerto nuevo, compruebe si hay opciones como BUILD_TESTS o WITH_TESTS y POCO_ENABLE_SAMPLES asegúrese de que los archivos binarios adicionales están deshabilitados. Esto minimiza los tiempos de compilación y las dependencias del usuario medio.

Opcionalmente, puede agregar una test característica que permita compilar las pruebas, pero esto no debe estar en la Default-Features lista.

Permitir que los usuarios existentes de la biblioteca cambien a vcpkg

No añada CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS

A menos que el autor de la biblioteca ya lo use, no deberíamos usar esta funcionalidad de CMake porque interactúa mal con las plantillas de C++ y interrumpe determinadas características del compilador. Las bibliotecas que no proporcionan un archivo .def y no usan declaraciones __declspec() simplemente no admiten compilaciones compartidas para Windows y deben marcarse como tales:

if(VCPKG_TARGET_IS_WINDOWS)
    vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
endif()

No cambie el nombre de los archivos binarios fuera de los nombres proporcionados por la cadena ascendente.

Esto significa que si la biblioteca ascendente tiene nombres diferentes en la versión y depuración (libx frente a libxd), la biblioteca de depuración no debe cambiarse a libx. Viceversa, si la biblioteca upstream tiene el mismo nombre en release y debug, no debemos introducir un nuevo nombre.

Advertencia importante:

  • Las variantes estáticas y compartidas a menudo deben cambiarse a un esquema común. Esto permite a los consumidores usar un nombre común e ignorar las conexiones posteriores. Esto es seguro porque solo hacemos que uno a la vez esté disponible.

Si una biblioteca genera archivos de integración de CMake (foo-config.cmake), el cambio de nombre debe realizarse mediante la aplicación de revisiones a la compilación de CMake en lugar de simplemente llamar a file(RENAME) en los archivos o LIB de salida.

Por último, los archivos DLL en Windows nunca se deben cambiar de nombre después de la compilación porque interrumpe los LIB generados.

Manifiestos

Es necesario dar formato al archivo de manifiesto. Use el siguiente comando para dar formato a todos los archivos de manifiesto:

> vcpkg format-manifest --all

Trillizos

No aceptamos solicitudes para agregar tripletas no comunitarias en este momento. La promoción del estado de comunidad a triplete completo se basa principalmente en el presupuesto para disponer de hardware que permita probar estos tripletes. Además, estará orientada por las métricas proporcionadas por vcpkg para maximizar la probabilidad de que lo que la gente realmente utiliza sea completamente probado.

Añadiremos tripletes comunitarios si:

  • está demostrado que la gente utilizará realmente ese triplete comunitario; y,
  • no sabemos si dicha tripleta está rota.

Por ejemplo, no agregamos un triplete en https://github.com/microsoft/vcpkg/pull/29034 porque el autor estaba intentando "completar el conjunto" en lugar de indicar que realmente usarían esa cosa, y no agregamos linux-dynamic hasta que se creó la solución patchelf para hacer que los resultados sean reubicables.

Notas útiles de implementación

Los archivos de puerto se ejecutan en modo de script

Aunque portfile.cmake y CMakeLists.txt comparten una sintaxis común y construcciones principales del lenguaje CMake (también conocido como "Comandos de scripting"), los portfiles se ejecutan en "Modo de script", mientras que los archivos CMakeLists.txt se ejecutan en "Modo de proyecto". La diferencia más importante entre estos dos modos es que "Modo de script" no tiene los conceptos de "Cadena de herramientas", "Language" y "Target". Los comportamientos, incluidos los comandos de scripting, que dependen de estas construcciones (por ejemplo CMAKE_CXX_COMPILER, , CMAKE_EXECUTABLE_SUFFIX, CMAKE_SYSTEM_NAME) no serán correctos.

Los portfiles tienen acceso directo a las variables establecidas en el archivo de tripletas, pero los CMakeLists.txt no (aunque a menudo se produce una traducción: VCPKG_LIBRARY_LINKAGE frente a BUILD_SHARED_LIBS).

Los archivos de puerto y las compilaciones de Project invocadas por los archivos port se ejecutan en distintos procesos. Conceptualmente:

+----------------------------+       +------------------------------------+
| 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)     |
+----------------------------+       +------------------------------------+

Para determinar el host en un archivo de puerto, las variables estándar de CMake están bien (CMAKE_HOST_WIN32).

Para determinar el destino en un archivo de puerto, se deben usar las variables de triplete de vcpkg (VCPKG_CMAKE_SYSTEM_NAME).

Consulte también nuestra triplet documentation para obtener una enumeración completa de las posibles opciones de configuración.