Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este documento descreve políticas, diretrizes e práticas recomendadas a seguir ao fazer contribuições para vcpkg.
Ele destina-se a servir a mesma função que:
- Manual de Política do Debian,
- Diretrizes de Mantenedor do Homebrew e
- Manual de Receitas de Fórmulas do Homebrew.
Objetivos de design do registro curado
As portas devem ser instaladas simultaneamente
Uma porta incluída no registro curado deve compilar com sucesso na presença de todas as outras portas no registro. Toda a combinação de portas é testada regularmente pela CI automatizada do vcpkg.
Uma porta não deve executar nenhuma das seguintes ações:
- Instale arquivos que entram em conflito com outra porta.
- Instale símbolos e definições pertencentes a outro pacote, exceto por bibliotecas vinculadas explicitamente com escopo estreito descritas em Colocar libs conflitantes em um
manual-linkdiretório. - Teste a presença ou ausência de outra porta. Exceto por declarar uma dependência.
Exceções são feitas para:
- Portas que substituem outra porta oficialmente. Isso inclui casos em que:
- O mesmo projeto publica um componente sucessor.
- Outro projeto assume a manutenção de um componente abandonado, inclusive por meio de um fork.
- Outro projeto publica um componente sucessor.
- Portas em que não há sobreposição de plataforma. Por exemplo, uma substituição somente Windows para um componente do Linux que não dá suporte a Windows.
- Portas que precedem esta política e possuem um número comprovadamente significativo de usuários e colaboradores.
os mantenedores do vcpkg avaliam as exceções de direito adquirido caso a caso. Por exemplo, boringssl foi adicionado antes que os registries personalizados estivessem disponíveis e permanece no registro coletado porque ele ainda tem usuários significativos, embora openssl seja o provedor canônico para a família de bifurcações do OpenSSL. A aceitação anterior por si só não garante uma exceção.
Portas que não estão em conformidade com esta política não podem ser aceitas no registro curado.
Dica
Se sua porta não puder ser incluída no registro curado, considere publicar um registro personalizado ou usar uma porta overlay.
As portas devem ser testadas em pelo menos um triplet oficial
Todas as portas no registro curado devem ser testadas em CI para pelo menos um triplet oficial.
Exceções são feitas para:
- Circunstâncias temporárias, como:
- A porta não compila sem uma atualização na infraestrutura de CI.
- A porta não compila devido a uma alteração recente que quebra a compatibilidade em uma dependência upstream e há trabalho em andamento para resolver a situação.
- Portas que precedem esta política e possuem um número comprovadamente significativo de usuários e colaboradores.
Novas portas que não podem ser testadas em pelo menos um triplet oficial não são aceitas no registro curado. Portas existentes que não estão mais em conformidade com esta política e não se qualificam para uma exceção listada acima são removidas do registro. Por exemplo, se o teste de CI foi desativado em todos os triplets oficiais suportados por uma porta e nenhuma exceção se aplica, a porta é removida do registro.
Dica
Se sua porta não puder ser incluída no registro curado, considere publicar um registro personalizado ou usar uma porta overlay.
Os componentes empacotados devem ser estáveis e mantidos ativamente
Os componentes empacotados no registro curado devem estar em manutenção ativa. Portas para componentes inativos podem ser removidas do registro.
Um componente será considerado inativo se:
- Seus mantenedores declararam o componente abandonado.
- Seu repositório de origem é arquivado ou não aceita mais contribuições.
- Os mantenedores não estão respondendo ou inacessíveis.
- Nenhuma alteração significativa foi feita há muito tempo.
Exceções são feitas para componentes fundamentais que são considerados maduros e estáveis e que não recebem alterações com frequência.
Por exemplo: zlib.
Os componentes empacotados devem ser maduros
Os componentes empacotados no registro curado devem ser maduros e destinados ao consumo por usuários de vcpkg. Os componentes destinados ao uso pessoal devem ser publicados em registries personalizados.
Um componente é considerado maduro o suficiente para o registro com curadoria, se uma dessas condições for verdadeira:
- O componente tem uma release com pelo menos seis meses de idade.
- O componente demonstra pelo menos seis meses de desenvolvimento público ativo.
- O componente é parte oficial de um projeto com pelo menos seis meses de lançamentos ou desenvolvimento público ativo. Por exemplo, uma nova biblioteca Boost ou um componente Qt.
- O componente demonstra maturidade equivalente aos requisitos anteriores em alguma outra capacidade.
Alguns indicadores de imaturidade do componente são:
- O componente não aparece nos mecanismos de pesquisa.
- O componente tem renomeações frequentes.
- Conflitos com outras bibliotecas.
Dica
Se sua porta não puder ser incluída no registro curado, considere publicar um registro personalizado ou usar uma porta overlay.
Estrutura de relações públicas
Fazer solicitações de pull separadas por porta
Pull requests (PRs) menores são mais fáceis de revisar. As solicitações de pull devem fazer alterações em uma única porta. Isso também reduz os tempos de espera para os resultados da CI (Integração Contínua).
Limitar PRs a uma única porta pode não ser possível em alguns casos. Por exemplo, ao alterar uma porta, os consumidores downstream devem ser atualizados ou ter patches aplicados.
Evitar alterações cosméticas não relacionadas
Não faça alterações cosméticas não relacionadas em uma adaptação do código que, de outra forma, não foi modificada, como reformatação, renomeação de variáveis ou correção de erros de digitação. Essas alterações consomem tempo de computação melhor usado em outro lugar. As alterações que melhoram a correção, a compatibilidade, a confiabilidade ou a reprodutibilidade do build não são consideradas triviais apenas porque os arquivos instalados permanecem inalterados.
Usar nomes de porta distintos
O nome de uma porta deve ser um indicativo de seu conteúdo.
A busca pelo nome da porta em um mecanismo de busca ou em navegadores de pacotes especializados, como Repology, deve levar ao componente correspondente.
Portos com nomes curtos ou de palavras comuns exigem desambiguação. Isso se aplica apenas ao nome do port no registro com curadoria; o nome do componente empacotado e seu conteúdo não precisam estar em conformidade com essa política.
Exceções são feitas para portas que empacotam um componente com forte associação ao nome da porta. Por exemplo: libpng, openssl, ou zlib.
Para estar em conformidade com essa política, novas portas com nomes ambíguos podem usar um prefixo, como:
- O proprietário, o nome de usuário ou a organização do repositório. Exemplo:
google-cloud-cpp. Para portas de componentes hospedados no GitHub, o proprietário do GitHub é um prefixo inequívoco aceitável<github owner>-<repository name>. - O nome de um pacote ao qual o pacote pertence:
boost-dll. Somente se o pacote for um componente oficial dessa suíte.
Exemplo: Nome da porta ambígua
Uma porta com o nome ip é considerada ambígua porque:
- O nome é muito curto,
- o nome é uma palavra comum e
- o nome não está fortemente associado a nenhum componente singular.
Para determinar se um nome é ambíguo, remova os seguintes prefixos e sufixos comuns usados por componentes C++ e de código aberto do nome da porta:
cppfreelibopen- numerais
Por exemplo: ip-cpp, libip e ip5 são ambíguos porque são reduzidos à mesma raiz ip.
Limitar renomeações de porta
Para evitar confusão para os usuários, uma porta não deve ser renomeada mais de uma vez em um período de 12 meses.
Usar PRs de rascunho do GitHub
As PRs de rascunho do GitHub são uma ótima maneira de obter CI ou feedback humano sobre o trabalho que ainda não está pronto para mesclagem. A maioria das novas PRs deve ser aberta como rascunhos e convertida em PRs normais assim que o CI for aprovado.
Para mais informações sobre solicitações de pull de rascunho do GitHub, consulte Introdução às solicitações de pull de rascunho.
Um mantenedor vcpkg pode converter sua PR em rascunho durante o processo de revisão, geralmente ao solicitar alterações ou deixar comentários que indicam quando marcar a PR como pronta para revisão.
Fechar PRs inativos
Um mantenedor do vcpkg pode fechar uma PR depois que ela estiver aguardando uma ação do colaborador por mais de 60 dias. A contagem regressiva de inatividade é executada somente enquanto a ação do colaborador é necessária. Ele não é executado enquanto a PR aguarda a verificação do laboratório de build ou a revisão por um mantenedor vcpkg.
Um PR está em um estado revisível quando:
- Não possui verificações de PR com falha, ou o colaborador pede ajuda ou justifica o motivo das falhas.
- Não possui alterações ou esclarecimentos pendentes solicitados por um mantenedor do vcpkg.
- Não tem conflitos de mesclagem.
A contagem regressiva começa quando a PR entra em um estado que requer a ação do colaborador. Isso pode acontecer quando uma PR é aberta com problemas que o colaborador precisa resolver ou quando um mantenedor vcpkg solicita alterações ou esclarecimentos. Um mantenedor vcpkg pode marcar a PR como um rascunho ao solicitar a ação do colaborador.
Uma resposta substancial do contribuidor redefine a contagem regressiva. Se a resposta resolver todas as solicitações pendentes e o PR estiver pronto para revisão, a contagem regressiva para até que a ação do colaborador seja necessária novamente. Se o colaborador não responder dentro de 60 dias enquanto a ação for necessária, a PR será considerada obsoleta e poderá ser fechada a critério de um mantenedor vcpkg.
Portfiles
Evitar funções auxiliares preteridas
No momento, os seguintes auxiliares foram preteridos:
-
vcpkg_extract_source_archive_ex()deve ser substituído pela sobrecarga suportada devcpkg_extract_source_archive()(comARCHIVE) - A sobrecarga obsoleta de
vcpkg_extract_source_archive()semARCHIVEdeve ser substituída pela sobrecarga suportada comARCHIVE. -
vcpkg_apply_patches()deve ser substituído pelosPATCHESargumentos para os auxiliares de "extração" (por exemplo,vcpkg_from_github()) -
vcpkg_build_msbuild()deve ser substituído porvcpkg_install_msbuild() -
vcpkg_copy_tool_dependencies()deve ser substituído porvcpkg_copy_tools() -
vcpkg_configure_cmakedeve ser substituído porvcpkg_cmake_configure()após a remoçãoPREFER_NINJA -
vcpkg_build_cmakedeve ser substituído porvcpkg_cmake_build() -
vcpkg_install_cmakedeve ser substituído porvcpkg_cmake_install() -
vcpkg_fixup_cmake_targetsdeve ser substituído porvcpkg_cmake_config_fixup
Algumas das funções auxiliares de substituição estão em "portas de ferramentas" para permitir que os consumidores fixem seu comportamento em versões específicas e permitir o bloqueio do comportamento dos auxiliares em uma versão específica. É necessário adicionar as portas de ferramentas ao "dependencies" da sua porta, da seguinte forma:
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
Evite comentários excessivos em portfiles
Idealmente, os arquivos de porta devem ser curtos, simples e o mais declarativos possível.
Remova todos os comentários genéricos introduzidos pelo comando create antes de enviar um PR.
Usar letras minúsculas para números hexadecimais
Muitos dos recursos em vcpkg dependem da comparação de cadeias de caracteres de dígitos hexadecimal. Alguns exemplos incluem, entre outros, hashes SHA512, IDs de confirmação do Git e hashes de objeto de árvore.
Internamente, o vcpkg usa a normalização em letras minúsculas para comparações desses valores em que a maiúscula é irrelevante. No entanto, as ferramentas criadas com base na infraestrutura do vcpkg podem não fazer as mesmas considerações. Por esse motivo, precisamos de cadeias de caracteres hexadecimal
a ser reduzido para consistência nos cenários a seguir:
- O parâmetro
SHA512nas funções auxiliares do vcpkg. - O parâmetro
REFnas funções auxiliares do vcpkg, quando o valor é uma cadeia de caracteres hexadecimal. - O objeto
git-treenos arquivos de versão do banco de dados. - O objeto
sha512no arquivoscripts/vcpkg-tools.json. - Outros lugares onde a capitalização de caracteres hexadecimal não é importante.
As portas não devem ser dependentes de rotas
As portas não devem alterar seu comportamento com base em quais portas já estão instaladas de uma forma que altere o conteúdo que a porta instala. Por exemplo, considerando que:
> vcpkg install a
> vcpkg install b
> vcpkg remove a
e
> vcpkg install b
Os arquivos instalados pelo b devem ser os mesmos, independentemente da influência da instalação anterior do a. Isso significa que os sistemas de portas não devem tentar verificar se algo é disponibilizado na árvore instalada por outro sistema de portas antes de executar alguma ação. Uma causa específica e comum desse comportamento "dependente do caminho" é descrita abaixo em "Ao definir recursos, controle explicitamente as dependências".
Regra de atribuição de porta exclusiva
Em todo o sistema vcpkg, não é esperado que dois ports usados simultaneamente por um usuário forneçam o mesmo arquivo. Se um port tentar instalar um arquivo já fornecido por outro port, a instalação falhará. Se um port quiser usar um nome extremamente comum para um cabeçalho, por exemplo, ele deve colocar esses cabeçalhos em um subdiretório em vez de em include.
Essa propriedade é verificada regularmente por execuções de integração contínua que tentam instalar todas as portas no registro, o que falhará se FILE_CONFLICTS duas portas fornecerem o mesmo arquivo.
Adicionar exportações do CMake em um namespace não oficial-
Um ideal de design central do vcpkg é não criar "lock-in" para os usuários. No sistema de compilação, não deve haver diferença entre depender de uma biblioteca do sistema e depender de uma biblioteca do vcpkg. Para esse fim, evitamos adicionar exportações ou destinos do CMake a bibliotecas existentes com "o nome óbvio", para permitir que os upstreams adicionem suas próprias exportações oficiais do CMake sem entrar em conflito com o vcpkg.
Para esse fim, todas as configurações do CMake exportadas pela porta, que não estão na biblioteca upstream, devem ter unofficial- como prefixo. Todos os destinos adicionais devem estar no unofficial::<port>:: namespace.
Isso significa que o usuário deve ver:
-
find_package(unofficial-<port> CONFIG)como a maneira de obter o pacote exclusivo para vcpkg -
unofficial::<port>::<target>como um destino exportado dessa porta.
Exemplos:
-
brotlicria ounofficial-brotlipacote, produzindo o destinounofficial::brotli::brotli.
Instalar arquivo de direitos autorais
Cada porta deve fornecer um arquivo nomeado copyright na pasta ${CURRENT_PACKAGES_DIR}/share/${PORT}. Se o conteúdo da licença de um pacote estiver disponível em seus arquivos de origem, esse arquivo deverá ser criado por uma chamada para vcpkg_install_copyright().
vcpkg_install_copyright também agrupa vários arquivos de direitos autorais, se necessário.
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
Um método mais antigo para criar manualmente esse arquivo é com o comando integrado file do CMake. Isso é desencorajado em favor de vcpkg_install_copyright em novos portas, mas ainda é permitido.
file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
Se o conteúdo da licença nos arquivos de origem upstream não estiver em forma de texto (por exemplo, um arquivo PDF), copyright deve conter uma explicação de como um usuário pode encontrar os requisitos de licença. Se possível, ele também deve incluir um link para os arquivos de origem originais indicando isso, para que os usuários possam verificar se está atualizado.
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
]])
Restrições de versão em portas
Restrições de versão dentro das portas geralmente devem ser evitadas, pois podem dificultar a evolução independente dos componentes. A adição de tais restrições só é permitida quando há uma justificativa bem documentada, como incompatibilidade comprovada com versões anteriores específicas. Essas restrições não devem ser usadas apenas para manter a paridade com componentes independentes.
As variáveis em MAYBE_UNUSED_VARIABLES devem ser aplicadas a pelo menos uma configuração
Ao adicionar uma nova variável a MAYBE_UNUSED_VARIABLES a fim de silenciar um aviso durante a etapa de configuração do CMake, você deve adicionar um comentário explicando o caso em que a nova variável se aplica. Se uma variável não se aplicar em nenhuma configuração, é muito provável que exista um bug subjacente (por exemplo, um nome de variável com ortografia incorreta) e adicioná-la não terá nenhum efeito real no build.
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
windowsfeature WINDOWS_OPTION
)
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
${FEATURE_OPTIONS}
MAYBE_UNUSED_VARIABLES
WINDOWS_OPTION # Applies only on Windows
)
Recursos
Não use recursos para implementar alternativas
Os recursos devem ser tratados como funcionalidade aditiva. Se port[featureA] estiver instalado e port[featureB] estiver instalado, então port[featureA,featureB] precisa ser instalado. Além disso, se uma segunda porta depender de [featureA] e uma terceira porta depender de [featureB], é necessário satisfazer as dependências da instalação da segunda e da terceira portas.
As bibliotecas nessa situação devem escolher uma das opções disponíveis, conforme expresso em vcpkg, e os usuários que desejam uma configuração diferente devem usar portas de sobreposição neste momento.
Exemplos existentes que não aceitaríamos hoje retidos para retrocompatibilidade:
-
libgit2,libzip,open62541todos têm recursos para selecionar um back-end TLS ou cripto.curltem diferentes opções de back-end de criptografia, mas permite selecionar entre elas em tempo de execução, o que significa que o princípio acima é mantido. -
darknettemopencv2,opencv3, recursos para controlar qual versão do opencv deve ser usada para suas dependências.
Um recurso pode ativar a funcionalidade de prévia ou beta.
Não obstante o acima, se houver uma ramificação de visualização ou semelhante em que a funcionalidade de visualização tenha uma alta probabilidade de não interromper a funcionalidade de não visualização (por exemplo, sem remoções de API), um recurso será aceitável para modelar essa configuração.
Exemplos:
- Os SDKs do Azure (do formulário
azure-Xxx) têm umpublic-previewrecurso. -
imguipossui um recursoexperimental-dockingque ativa sua ramificação de versão prévia, utilizando um commit de mesclagem anexado a cada uma de suas versões numeradas públicas.
Os recursos padrão não devem adicionar APIs
Observação
Um recurso habilitado por padrão pelo sistema de build upstream não implica que o recurso deve ser adicionado às default-features entradas. Como a finalidade pretendida de default-features não é modelar as decisões tomadas pelo upstream, mas sim fornecer conveniência aos usuários do modo clássico.
Os recursos padrão destinam-se a garantir que uma compilação razoavelmente funcional de uma biblioteca seja instalada para clientes que não sabem que a estão usando. Se eles não sabem que estão usando uma biblioteca, eles não podem saber como listar recursos. Por exemplo, libarchive expõe recursos que permitem algoritmos de compactação a uma interface genérica existente; se construída sem nenhum desses recursos, a biblioteca pode não ter utilidade.
Aqui, os clientes incluem tanto projetos de usuário que consomem o grafo de pacotes quanto portas downstream dentro desse grafo. Ambos expressam seus requisitos de dependência por meio da mesma interface de recurso.
Considere cuidadosamente se um recurso deve estar ativado por padrão, pois desabilitar recursos padrão é complexo.
Para desabilitar recursos padrão para uma dependência transitiva:
- Todo cliente que depende da porta deve desabilitar seus recursos padrão em sua declaração de dependência usando
"default-features": false. - O projeto de nível superior também deve nomear a dependência transitiva diretamente, seja em seu manifesto com
"default-features": false, ou navcpkg installlinha de comando com[core].
Desabilitar os recursos padrão não impede que outro cliente solicite explicitamente qualquer um desses recursos.
Para essa política, uma API é qualquer interface exposta aos consumidores, incluindo interfaces de biblioteca, interfaces de linha de comando executáveis e outras interfaces binárias. Um recurso pode ser habilitado por padrão quando adiciona comportamento por trás de uma API existente, como suporte para algoritmos de compactação adicionais por meio de uma interface genérica existente.
No registro com curadoria do vcpkg, se um recurso adicionar uma API, ele deverá estar desativado por padrão. Em caso de dúvida, não marque um recurso como padrão.
Não use recursos para controlar alternativas em interfaces publicadas
Se um consumidor de uma porta depender apenas da funcionalidade essencial dessa porta, é muito provável que ele não seja prejudicado pela ativação do recurso. Isso é ainda mais importante quando a alternativa não é controlada diretamente pelo consumidor, mas por configurações do compilador como /std:c++17 / -std=c++17.
Exemplos existentes que não aceitaríamos hoje retidos para retrocompatibilidade:
-
redis-plus-plus[cxx17]controla um polyfill, mas não integra a configuração na árvore instalada. -
ace[wchar]altera todas as APIs para aceitarconst wchar_t*em vez deconst char*.
Um recurso pode substituir polyfills por aliases, desde que a substituição seja incorporada à árvore instalada
Não obstante o acima, as portas podem remover polyfills com um recurso, desde que:
- Ativar o recurso altera os polyfills para sinônimos da entidade polyfilled
- O estado do polyfill é incorporado aos cabeçalhos instalados, reduzindo a probabilidade de erros "impossíveis" de não correspondência de ABI de runtime.
- É possível que um consumidor da porta escreva código que funcione em ambos os modos, por exemplo, usando um typedef que tem pollyfill ou não
Exemplo:
-
abseil[cxx17]mudaabsl::string_viewpara uma substituição oustd::string_view; o patch implementa o requisito de geração.
Soluções recomendadas
Se for crítico expor as alternativas subjacentes, recomendamos fornecer mensagens no momento da compilação para instruir o usuário sobre como copiar a porta em uma sobreposição 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 construção
Não use dependências de fornecedor
Prefira empacotar dependências separadamente para que os usuários possam selecioná-las e atualizá-las por meio de vcpkg. As dependências incorporadas apresentam vários desafios:
- Dificuldade de atualização: As dependências incorporadas são mais difíceis de rastrear e atualizar, inclusive ao aplicar correções de segurança.
- Conflitos de símbolo: Uma dependência incorporada pode definir os mesmos símbolos que outra versão da dependência, causando erros de runtime ou comportamento indefinido.
- Conformidade de licenciamento: As dependências incorporadas podem obscurecer os requisitos de licenciamento ou criar problemas de compatibilidade de licença.
- Aumento da manutenção: Manter dependências incorporadas sincronizadas com suas versões upstream aumenta o trabalho de manutenção e pode duplicar o trabalho entre os ports.
Por esses motivos, elimine dependências de fornecedor sempre que prático. Dependências fornecidas não devem ser expostas ao domínio de link de um usuário.
Um domínio de link é o conjunto de símbolos que podem ser resolvidos entre si no runtime. Em Windows, cada DLL geralmente tem seu próprio domínio de link. Em plataformas com uma tabela de símbolos para todo o processo, o domínio de link geralmente inclui todo o processo.
Se um componente expor uma dependência incorporada ao mesmo domínio de link que uma dependência selecionada por meio de vcpkg, ambas as cópias poderão definir os mesmos símbolos. Isso viola a Regra de Uma Definição e pode causar erros de runtime ou comportamento indefinido. O problema só poderá aparecer quando as duas versões forem diferentes o suficiente para serem incompatíveis.
Uma dependência vendorizada só é permitida quando é totalmente isolada de domínios de link usados por ports downstream e aplicativos de usuário. Por exemplo, uma ferramenta de build somente de host pode conter uma dependência vinculada estaticamente se binários downstream não se vincularem à ferramenta e os símbolos da dependência não puderem entrar em seus domínios de link.
Dê preferência ao CMake
Quando vários sistemas de compilação estiverem disponíveis, prefira usar o CMake.
Além disso, quando apropriado, pode ser mais simples e fácil de manter reescrever sistemas de build alternativos no CMake usando diretivas file(GLOB).
Exemplos: abseil
Escolher binários estáticos ou compartilhados
Ao criar bibliotecas do CMake, vcpkg_cmake_configure() o passará o valor correto para BUILD_SHARED_LIBS com base na variante solicitada pelo usuário.
Você pode calcular parâmetros de configuração alternativos usando 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}
)
Se uma biblioteca não oferecer opções de configuração para selecionar a variante de compilação, a compilação deverá ser corrigida. Ao corrigir uma compilação, você deve sempre tentar maximizar a capacidade de manutenção futura do port. Normalmente, isso significa minimizar o número de linhas que precisam ser alteradas para corrigir o problema em questão.
Exemplo: aplicação de patch em uma biblioteca do CMake para evitar a criação de variantes indesejadas
Por exemplo, ao corrigir uma biblioteca baseada em CMake, pode ser suficiente adicionar EXCLUDE_FROM_ALL a destinos indesejados e encapsular a chamada install(TARGETS ...) em um if(BUILD_SHARED_LIBS). Isso será mais curto do que quebrar ou excluir todas as linhas que mencionam a variante indesejada.
Para um projeto CMakeLists.txt com os seguintes conteúdos:
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)
Substitua a install(TARGETS) linha por um bloco condicional que também exclua o destino indesejado do build padrão:
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)
Ao definir recursos, controle explicitamente as dependências
Ao definir um recurso que captura uma dependência opcional, certifique-se de que a dependência não será usada acidentalmente quando o recurso não estiver explicitamente habilitado.
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}
)
O snippet abaixo usando vcpkg_check_features() é 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 no trecho diferencia maiúsculas de minúsculas. Para obter mais informações, consulte a documentação CMAKE_DISABLE_FIND_PACKAGE_<PackageName> e CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>.
Colocar libs conflitantes em um manual-link diretório
Um arquivo de entrada do vinculador instalado em lib ou debug/lib, como um arquivo .lib, .a, .so ou .dylib, é considerado conflitante se ele fizer qualquer uma das seguintes ações:
- Define-se
main. - Define
malloc. - Define símbolos que também são definidos em outras bibliotecas.
Alguns arquivos de entrada do vinculador fornecem intencionalmente substituições com escopo estreito para símbolos que os consumidores definiriam ou obteriam em outro lugar. Por exemplo, o gtest-main.lib do Google Test fornece main para executáveis de teste cujos autores optam por usá-lo em vez de definir seu próprio ponto de entrada.
Coloque arquivos de entrada do vinculador conflitantes em lib/manual-link ou debug/lib/manual-link para que sistemas de build que vinculam arquivos automaticamente do diretório de biblioteca não os selecionem. Windows DLLs permanecem dentro de bin ou debug/bin; coloque suas bibliotecas de importação em manual-link quando a vinculação explícita for necessária. Os consumidores devem optar explicitamente pela vinculação desses arquivos e são responsáveis por evitar definições conflitantes. Essa exceção não é um mecanismo geral para empacotar provedores alternativos de outro componente, e mover um arquivo de entrada do vinculador para manual-link não isola seus símbolos do domínio de link do consumidor.
Instalando binários pré-compilados
Os ports que instalam artefatos predefinidos (somente binários) são permitidos, mas altamente desencorajados, desde que os artefatos não impeçam os usuários de selecionar ou atualizar dependências por meio do vcpkg. Compilar a partir do código-fonte é preferível porque respeita as configurações do vcpkg que controlam o compilador e os sinalizadores.
Rejeitaremos portas que atendam a todas as seguintes condições:
- Instalar binários pré-compilados em vez de criar a partir do código fonte e
- Esses binários incorporam ou exigem dependências de runtime fornecidas por outras portas no registro curado e
- Os artefatos instalados expõem essas dependências a um domínio de link que também pode conter versões selecionadas por meio de vcpkg.
Essa combinação bloqueia efetivamente o grafo de dependência para as versões usadas quando o upstream pré-compilado foi produzido.
Se vcpkg atualizar uma dependência como zlib ou openssl, as versões predefinidas e selecionadas pelo usuário poderão definir símbolos incompatíveis no mesmo domínio de link. Isso pode causar violações da One Definition Rule, incompatibilidades de ABI ou atualizações de segurança perdidas.
Um artefato expõe uma dependência sempre que essa dependência participa do domínio de link de um usuário, independentemente de o upstream pretender expô-la. Isso inclui:
- Bibliotecas
.lib,.a,.so,.dylibou de importação vinculáveis pelo consumidor que trazem a dependência para o domínio de link do usuário. - Cabeçalhos instalados que fazem referência (diretamente ou por meio de código embutido/de modelo) a símbolos, tipos ou macros de outras portas do vcpkg.
- Arquivos de configuração do CMake ou pkg-config instalados que invocam
find_dependency()ouRequires:em outros ports do vcpkg.
Cenários permitidos (mas ainda desencorajados):
- Ferramentas auxiliares somente host usadas no tempo de build cujas saídas são consumidas, mas contra as quais portas dependentes não fazem link, desde que isolem dependências agrupadas ou dependam apenas de bibliotecas onipresentes de runtime do sistema.
- Bibliotecas predefinidas cujas dependências agrupadas permanecem inteiramente em domínios de link separados e não aparecem em cabeçalhos instalados ou interfaces exportadas. A vinculação estática por si só não isola uma dependência do domínio de link de um usuário.
- Pacotes somente de dados, firmware ou ativos não vinculados ao código do usuário.
Exemplos proibidos:
- Uma biblioteca pré-compilada que vincula estaticamente uma cópia privada de
zlibe expõe seus símbolos a um domínio de link que também pode conter a versão dezlibselecionada por meio de vcpkg. - Um
libfoopré-compilado que instalalib/libfoo.libmais cabeçalhos, incluindo<zlib.h>e foi compilado com base em uma versãozlibespecífica; os consumidores então vinculam alibfooesperando compatibilidade. - Um SDK pré-definido que instala um arquivo de pacote do CMake chamando
find_dependency(OpenSSL)enquanto o binário foi compilado contra uma versão mais antiga do OpenSSL.
Mitigações/alternativas:
- Forneça uma compilação a partir do código-fonte usando scripts upstream ou adicione um wrapper CMake simples.
- Solicite ao upstream a publicação de uma versão baseada no código-fonte ou instruções de compilação reproduzíveis; vincule o problema/PR upstream em um comentário em
portfile.cmakeouvcpkg.json. - Use uma porta de overlay ou um registro privado para componentes pré-construídos específicos da organização que não podem satisfazer essas regras.
Controle de versão
Siga as convenções comuns para o "version" campo
Ao criar um novo port, siga a convenção de controle de versão usada pelo autor do pacote. Ao atualizar o port, continue a usar a mesma convenção, a menos que o upstream diga o contrário. Para obter uma explicação completa de nossas convenções, consulte nossa documentação de controle de versão.
Se o upstream não publica uma versão há algum tempo, não altere o esquema de controle de versão do port para version-date para obter as alterações mais recentes. Esses commits podem incluir alterações que não estão prontas para produção. Em vez disso, peça ao repositório upstream para publicar uma nova versão.
Atualizar o "port-version" campo para cada porta modificada
Cada PR que modifica um port deve produzir uma nova versão desse port. Os PRs são mesclados por squash, portanto, atualize a versão de cada porta modificada uma vez por PR, não uma vez por commit ou revisão.
Se a versão upstream não tiver sido alterada, incremente o "port-version" campo. Se a versão upstream tiver sido alterada, redefina "port-version" para zero removendo o campo ou definindo-o como 0.
Por exemplo:
- A versão do pacote do Zlib é atualmente
1.2.1, sem um"port-version"explícito (equivalente a um"port-version"de0). - Você descobriu que o arquivo de copyright errado foi implantado e corrigiu isso no portfile.
- Você deve atualizar o
"port-version"campo no arquivo de manifesto para1.
Consulte a documentação de controle de versão para obter mais informações.
Atualize os arquivos de versão em versions/ para cada porta modificada
O vcpkg usa um conjunto de arquivos de metadados para alimentar seu recurso de controle de versão. Esses arquivos estão localizados nos seguintes locais:
-
${VCPKG_ROOT}/versions/baseline.json, (este arquivo é comum a todos os ports) e -
${VCPKG_ROOT}/versions/${first-letter-of-portname}-/${portname}.json(um por porta).
Por exemplo, para zlib os arquivos relevantes são:
${VCPKG_ROOT}/versions/baseline.json${VCPKG_ROOT}/versions/z-/zlib.json
Para cada porta modificada em uma PR, atualize seus arquivos de versão para gravar a árvore Git final. Se você fizer mais alterações durante a revisão, execute vcpkg x-add-version <port-name> --overwrite-version para atualizar a entrada de versão não mesclada
sem incrementar "port-version" novamente.
O método recomendado para atualizar esses arquivos é executar o x-add-version comando, por exemplo:
vcpkg x-add-version zlib
Se você estiver atualizando várias portas ao mesmo tempo, em vez disso, poderá executar:
vcpkg x-add-version --all
para atualizar os arquivos de todas as portas modificadas de uma só vez.
Para obter mais informações, consulte os artigos Referência de controle de versão e Registros.
Aplicação de patch
O vcpkg é uma solução de empacotamento, não os proprietários finais dos componentes que implantamos. Precisamos aplicar patches em alguns casos para melhorar a compatibilidade dos componentes com as plataformas ou a compatibilidade dos componentes entre si.
- Queremos evitar patches que:
- o upstream discordaria
- Causar vulnerabilidades ou travamentos
- não seja possível manter as atualizações de versão upstream
- sejam grandes o suficiente para causar complicações de licença com o repositório vcpkg em si
Notificação upstream e períodos de espera
Os proprietários upstream são os árbitros finais do comportamento de seus componentes. Os mantenedores do vcpkg exigem notificação upstream para patches relevantes para upstream e para bugs identificados por ferramentas de revisão de código com IA em novas submissões de ports.
Correções relevantes para o upstream
Se uma correção puder ser útil para o upstream, os mantenedores do vcpkg exigem que o upstream seja notificado sobre o conteúdo da correção. Correções que aplicam comportamentos específicos do vcpkg não relacionados ao upstream, como a desvinculação de uma dependência, não exigem notificação.
Para evitar situações em que o upstream discorda do patch, os mantenedores vcpkg esperam pelo menos 30 dias após a notificação upstream antes de aplicar esses patches.
Os mantenedores vcpkg ignorarão esse período de espera se tiverem alta confiança de que a alteração está correta. Exemplos de patches de alta confiança incluem, mas não estão limitados a:
- A aceitação do upstream como um patch (por exemplo, o backport de uma alteração específica de uma solicitação de pull upstream foi mesclado).
- Adicionar
#includes ausentes. - Correções de código de produto pequenas e óbvias (por exemplo, inicializar uma variável não inicializada).
- Desativar os componentes irrelevantes no vcpkg do build, como testes ou exemplos.
Bugs identificados por IA em novas portas
O registro curado visa evitar o envio de código explorável conhecido, mas os mantenedores do vcpkg não podem auditar de forma abrangente o código-fonte de todos os componentes. As descobertas de revisão de código de IA não são, por si só, uma determinação final de que o código upstream é vulnerável.
Quando as ferramentas de revisão de código de IA identificam um bug durante a revisão de uma nova submissão de port, os mantenedores vcpkg exigem que o bug seja relatado para upstream, seguindo o processo de relatório de segurança do projeto para bugs potencialmente exploráveis. Este requisito se aplica mesmo quando a descoberta diz respeito a código upstream que a porta não corrige.
- Se o upstream determinar que o comportamento relatado não é um bug, os mantenedores do vcpkg aceitam essa determinação como final e não rejeitam o envio da nova porta com base nessa descoberta.
- Se o projeto upstream corrigir o bug, os mantenedores do vcpkg aceitam a versão corrigida ou um patch aprovado pelo upstream, sujeito à política de aplicação de patches e aos demais requisitos para novas portas.
- Se o upstream não responder dentro de 30 dias após o bug ser relatado, os mantenedores do vcpkg passam a tratar a constatação da IA como um problema explorável e fecham a nova submissão de port.
As exceções de alta confiança para o período de espera do patch não renunciam a esse requisito de relatório de bugs ou à necessidade de uma resposta upstream.
Dê preferência a opções em vez de correções
É preferível definir opções em uma chamada para vcpkg_configure_xyz() do que modificar as configurações diretamente.
Opções comuns que permitem evitar patches:
- [MSBUILD]
<PropertyGroup>As configurações dentro do arquivo de projeto podem ser substituídas por/p:parâmetros - [CMAKE] As chamadas para
find_package(XYz)em scripts CMake podem ser desativadas por meio de-DCMAKE_DISABLE_FIND_PACKAGE_XYz=ON - [CMAKE] As variáveis de cache (declaradas como
set(VAR "value" CACHE STRING "Documentation")ouoption(VAR "Documentation" "Default Value")) podem ser substituídas apenas passando-as na linha de comando como-DVAR:STRING=Foo. Uma exceção notável é se oFORCEparâmetro for passado paraset(). Para obter mais informações, consulte a documentação do CMakeset
Dê preferência a baixar patches aprovados em vez de verificá-los na porta
Se um patch aprovado ou mesclado puder ser obtido de um commit do upstream, baixe-o e aplique-o em vez de fazer commit dele no port. Este processo é preferível porque:
- Confirma que o upstream aceitou as alterações de patch.
- Simplifica o processo de revisão deslocando o ônus para etapas anteriores.
- Reduz o tamanho do repositório vcpkg para usuários que não estão usando o patch.
- Evita conflitos de licença com o repositório vcpkg.
Baixe patches apenas de URLs fixadas em um ID completo do commit, não de URLs de diff de solicitação de pull ou de solicitação de mesclagem cujo conteúdo pode ser alterado. Ao baixar um patch do commit de GitHub ou GitLab, acrescente ?full_index=1 à URL. Esse parâmetro solicita IDs completos de objetos no diff; não torna uma URL mutável imutável.
Por exemplo:
Dê preferência a aplicar patches em vez de substituir valores de VCPKG_<VARIABLE>
Algumas variáveis prefixadas com VCPKG_<VARIABLE> têm um equivalente CMAKE_<VARIABLE>.
No entanto, nem todos eles são passados para o build do pacote interno (consulte implementação: cadeia de ferramentas do Windows).
Considere o seguinte exemplo:
set(VCPKG_C_FLAGS "-O2 ${VCPKG_C_FLAGS}")
set(VCPKG_CXX_FLAGS "-O2 ${VCPKG_CXX_FLAGS}")
Usando as cadeias de ferramentas integradas de vcpkg, isso funciona, porque o valor de VCPKG_<LANG>_FLAGS é encaminhado para a variável apropriada CMAKE_<LANG>_FLAGS. No entanto, um conjunto de ferramentas personalizado que não esteja ciente das variáveis de vcpkg não as encaminhará.
Por causa disso, é preferível corrigir o sistema de compilação diretamente ao definir CMAKE_<LANG>_FLAGS.
Minimizar patches
Ao fazer alterações em uma biblioteca, esforce-se para minimizar a diferença final. Isso significa que você não deve reformatar o código-fonte upstream ao fazer alterações que afetem uma região. Ao desabilitar uma condicional, é melhor adicionar um AND FALSE ou && 0 à condição do que excluir todas as linhas da condicional. Se uma região grande precisar ser desabilitada, é mais curto adicionar um if(0) ou #if 0 em volta da região em vez de excluir todas as linhas do patch.
Não adicione patches se o port estiver desatualizado e atualizar o port para uma versão mais recente resolveria o mesmo problema. O vcpkg prefere atualizar os ports em vez de corrigir versões desatualizadas.
Isso ajuda a manter o tamanho do repositório vcpkg baixo, bem como melhora a probabilidade de que o patch seja aplicado a versões futuras do código.
Não implementar recursos em patches
O objetivo da aplicação de patch no vcpkg é habilitar a compatibilidade com compiladores, bibliotecas e plataformas. Não é para implementar novos recursos em vez de seguir o procedimento adequado de código aberto (enviar um problema / PR / etc).
Não crie testes/documentos/exemplos por padrão
Ao enviar uma nova porta, verifique se há opções como BUILD_TESTS ou ou WITH_TESTS e POCO_ENABLE_SAMPLES certifique-se de que os binários adicionais estejam desativados. Isso minimiza os tempos de compilação e as dependências para o usuário médio.
Opcionalmente, você pode adicionar um test recurso que permite a construção dos testes, no entanto, isso não deve estar na Default-Features lista.
Permitir que os usuários existentes da biblioteca mudem para vcpkg
Não adicione CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
A menos que o autor da biblioteca já a esteja usando, não devemos usar essa funcionalidade do CMake porque ela interage mal com modelos C++ e interrompe certos recursos do compilador. Bibliotecas que não fornecem um arquivo .def e não usam declarações __declspec() simplesmente não dão suporte a builds compartilhados para Windows e devem ser marcadas como tal:
if(VCPKG_TARGET_IS_WINDOWS)
vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
endif()
Não renomeie binários fora dos nomes fornecidos pelo upstream
Isso significa que, se a biblioteca upstream tiver nomes diferentes em release e debug (libx versus libxd), a biblioteca de depuração não deverá ser renomeada para libx. Vice-versa, se a biblioteca upstream tiver o mesmo nome em release e debug, não devemos introduzir um novo nome.
Advertência importante:
- Variantes estáticas e compartilhadas geralmente devem ser renomeadas para um esquema comum. Isso permite que os consumidores usem um nome comum e ignorem a ligação a jusante. Isso é seguro porque disponibilizamos apenas um de cada vez.
Se uma biblioteca gerar arquivos de integração do CMake (foo-config.cmake), a renomeação deverá ser feita aplicando patch na própria construção do CMake, em vez de simplesmente executar file(RENAME) nos arquivos de saída/LIBs.
Por fim, os arquivos DLL no Windows nunca devem ser renomeados após a compilação porque quebram as LIBs geradas.
Manifestos
Exigimos que o arquivo de manifesto seja formatado. Use o seguinte comando para formatar todos os arquivos de manifesto:
> vcpkg format-manifest --all
Trigêmeos
Não estamos aceitando solicitações para adicionar trigêmeos fora da comunidade no momento. A promoção de um status comunitário para um status de tripleto completo é baseada principalmente no orçamento disponível para adquirir hardware para testar esses tripletos e será impulsionada por métricas enviadas pelo vcpkg, a fim de maximizar a probabilidade de que o que as pessoas realmente utilizam seja totalmente testado.
Adicionaremos tripletos da comunidade se:
- É demonstrado que as pessoas realmente usarão esse conjunto tripleto da comunidade; e
- não sabemos que tal tripleto está quebrado.
Por exemplo, não adicionamos um tripleto https://github.com/microsoft/vcpkg/pull/29034 porque o autor estava apenas tentando "completar o conjunto" em vez de indicar que realmente usaria isso, e não adicionamos linux-dynamic até que a solução patchelf para tornar os resultados relocáveis fosse criada.
Notas úteis de implementação
Os arquivos de porta são executados no modo de script
Enquanto portfile.cmake's e CMakeLists.txt's compartilham uma sintaxe comum e as construções principais da linguagem CMake (também conhecidas como "Comandos de Script"), os portfiles rodam no "Modo Script", enquanto os CMakeLists.txt arquivos rodam no "Modo Projeto". A diferença mais importante entre esses dois modos é que o "Modo Script" não possui os conceitos de "Toolchain", "Language" e "Target". Quaisquer comportamentos, incluindo comandos de script, que dependem dessas construções (por exemplo CMAKE_CXX_COMPILER, , CMAKE_EXECUTABLE_SUFFIX, CMAKE_SYSTEM_NAME) não estarão corretos.
Portfiles têm acesso direto às variáveis definidas no arquivo tripleto, mas os CMakeLists.txts não têm (embora muitas vezes ocorra uma tradução que acontece -- VCPKG_LIBRARY_LINKAGE versus BUILD_SHARED_LIBS).
Portfiles e compilações de projeto invocadas por portfiles são executados em processos diferentes. Conceitualmente:
+----------------------------+ +------------------------------------+
| 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 o host em um arquivo de porta, as variáveis padrão do CMake são boas (CMAKE_HOST_WIN32).
Para determinar o destino em um arquivo de porta, as variáveis do tripleto do vcpkg devem ser usadas (VCPKG_CMAKE_SYSTEM_NAME).
Consulte também nossa documentação de triplet para obter uma enumeração completa das configurações possíveis.