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_subdirectories
para 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, apontandoCMAKE_MODULE_PATH
para 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 definirCMAKE_MODULE_PATH
A, 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_Add
vez deadd_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_subdirectories
chamadas 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_DIR
parâmetros para elas (dê uma olhada em ExternalProject_Add ).
UPDATE_COMMAND
e as INSTALL_COMMAND
linhas 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_package
assinatura “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!