Use bibliotecas habilitadas para CMake em seu projeto CMake


Resumo

Neste protocolo, eu explico como reutilizar bibliotecas habilitadas para CMake em outro projeto habilitado para CMake quando as primeiras não estão instaladas em seu sistema, mas no Github ou em um subdiretório.

Nota : Existe um par de acompanhamentos para esta solução: II e III .


O problema

Muitas vezes eu uso bibliotecas minhas habilitadas para CMake em novos projetos. Muitas vezes também, essas bibliotecas não são instaladas no meu sistema, mas em algum lugar no Github ou Bitbucket.

Mesmo se as bibliotecas não estiverem instaladas, o CMake facilita o seu uso: você copia a biblioteca em uma pasta na árvore de origem do projeto (ou usa submódulos Git ou qualquer outro) e depois usa add_subdirectoriespara incluir o projeto. Considere, por exemplo, este projeto de amostra A com as bibliotecas B e C, em que C depende de B. A árvore de origem resultante será algo assim:

A/
CMakeLists.txt
B
/
CMakeLists.txt
C
/
CMakeLists.txt // depends on B

Onde CMakeLists.txt de A teria add_subdirectories(B)e add_subdirectories(C). No entanto, essa abordagem tem algumas desvantagens. Notavelmente, C provavelmente tentaria encontrar B por meio de find_package(B)e isso não funcionará fora da caixa por alguns motivos:

  • find_package(B)no CMakeLists.txt de C não encontrará B a menos que você forneça algumas dicas sobre onde procurar (por exemplo, apontando CMAKE_MODULE_PATHpara o arquivo de configuração de B). Isso implica apontar C para A de alguma forma (feio e, no pior dos casos, não é possível se não tivermos o módulo) ou definir CMAKE_MODULE_PATHA, o que poderia ser uma solução aceitável.

  • Se eventualmente conseguirmos que C encontre B, CMake reclamará de um “alvo duplicado” vindo de B. O problema surge porque B definirá alvos que mais tarde serão “importados” novamente por C. Isso acontece porque A, B e C são compartilhando o mesmo “namespace” em relação a variáveis ​​e destinos. Este problema pode ser contornado, mas requer a mudança de C, o que, como mencionado anteriormente, não é desejável.

A solução

Depois de tentar várias soluções, a que funciona para mim é a seguinte:

  • Etapa 1: use em ExternalProject_Addvez de add_subdirectories.
  • Etapa 2: Use o recurso do CMake para exportar a configuração de B.install(EXPORT)
  • Etapa 3: Use o CMake para adicionar a configuração de B ao “registro do pacote”, onde C pode encontrá-la posteriormente.export(PACKAGE)

Este procedimento é simples no sentido de que não precisamos modificar C para encontrar B. O “registro de pacote” é um dos lugares onde o CMake procura por arquivos de configuração de pacote. No entanto, ainda temos que modificar B ou qualquer projeto do qual dependemos para que seu arquivo de configuração seja exportado, o que é, em geral, uma boa prática.

Etapa 1: adicionar projetos externos

No CMakeLists.txt de A, substituímos as add_subdirectorieschamadas por:

include(ExternalProject)

ExternalProject_Add (
B

GIT_REPOSITORY
"<git_repo_url>"
GIT_TAG
"<git_tag_commit_or_branch>"
UPDATE_COMMAND
""
INSTALL_COMMAND
"" )

ExternalProject_Add (
C

GIT_REPOSITORY
"<git_repo_url>"
GIT_TAG
"<git_tag_commit_or_branch>"
UPDATE_COMMAND
""
INSTALL_COMMAND
"" )

Você também pode usar SVN ou CVS, ou incluir as fontes diretamente em sua árvore de fontes e apontar SOURCE_DIRparâmetros para elas (dê uma olhada em ExternalProject_Add ).

UPDATE_COMMANDe as INSTALL_COMMANDlinhas evitarão usar os procedimentos padrão de atualização e instalação, que geralmente não são necessários.

Etapa 2: exportar uma configuração

Se quisermos que C veja B por meio de find_package, devemos exportar a configuração de B. Para conseguir isso, fazemos o seguinte no CMakeLists.txt de B:

  • Adicionar alvos a um grupo de exportação :
install ( TARGETS <targets> ... EXPORT <export_name> )
  • Exporte o grupo de exportação (sic). CMake irá escrever um arquivo de configuração da árvore de construção atual:
export ( TARGETS <targets> FILE <file> )

Use ou , caso contrário, o arquivo de configuração não será encontrado.${CMAKE_BINARY_DIR}/<package name>-config.cmake${CMAKE_BINARY_DIR}/<package name>Config.cmake

Etapa 3: Exportar a árvore de construção para o registro do pacote

Adicione a árvore de construção ao registro do pacote CMake encontrado em sistemas unix por padrão:.cmake/packages/<package>/

export ( PACKAGE <package_name> )

Depois de concluir todas essas etapas, no CMakeLists.txt de C, você pode fazer como se B fosse um pacote normal:

find_package ( <package_name> )

A chamada anterior implica na find_packageassinatura “no module” ou “config”. A assinatura não pesquisa o registro do pacote.find_package( <package_name> MODULE )

Por favor, não hesite em compartilhar comigo se você encontrou a melhor maneira de atingir o mesmo objetivo!

Referências