Resumo:
Neste protip eu explico como nossa biblioteca pode ser incluída em outros projetos sem ter que instalá-la. Iremos gerar um arquivo de configuração que funcionará independentemente da biblioteca estar instalada ou não.
Nota: leia os artigos anteriores para contextualizar: I e II .
Se você leu meu artigo anterior e seu acompanhamento , deve ser muito fácil ter um projeto de várias bibliotecas instalado e funcionando,
apesar de todo o código clichê. Mas como podemos fazer com que seja fácil de integrar em outros projetos de várias bibliotecas? A mecânica para permitir que outras pessoas encontrem sua biblioteca são fáceis: forneça um arquivo chamado <name>-config.cmake
, <name>Config.cmake
ou Find<name.cmake
em seu pacote, defina variáveis nele informando onde encontrar as bibliotecas, cabeçalhos, etc, e pronto. Mas como escrever um arquivo de configuração que é realocável e funciona quando você instala sua biblioteca ou quando você usa apenas suas fontes?
Esta é uma descrição do que funcionou para mim. Eu omito o código CMake não relacionado à geração do arquivo de configuração.
Implementação
Vou assumir que o pacote que estou configurando se chama “foo”. Os caminhos entre os colchetes angulares devem ser
substituídos por caminhos personalizados para os arquivos correspondentes. foo_LIBRARIES
tem que ser definido também com .add_library/add_executable
Em primeiro lugar, usaremos CMakePackageConfigHelpers :
include ( CMakePackageConfigHelpers )
Este pacote nos permite escrever arquivos de configuração que podem ser realocados, ou seja, onde os caminhos não são codificados.
Em seguida, podemos escrever o arquivo de configuração que será usado se o pacote não estiver instalado :
# In my case the folder of includes can be any source folder,
# a practice very extended (in opposition to a single folder
# containing all the headers).
set ( foo_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" )
# Destination of the installed config files (relative path):
set ( CMAKE_CONFIG_DEST "share/cmake/Modules" )
# We configure our template. The template is described later.
configure_package_config_file (
"<PATH TO THE CONFIG TEMPLATE>/foo-config.cmake.in"
# Important to write in CMAKE_BINARY_DIR if you want the registry
# mechanism to work:
"${CMAKE_BINARY_DIR}/foo-config.cmake"
INSTALL_DESTINATION "${CMAKE_CONFIG_DEST}"
PATH_VARS foo_INCLUDE_DIRS )
# This file is included in our template:
export ( TARGETS foo_LIBRARIES FILE "${CMAKE_BINARY_DIR}/fooTargets.cmake" )
export ( PACKAGE foo )
Para notar que eu uso, configure_package_config_file
mas o antigo configure_file teria funcionado bem aqui.
Até agora nós escrevemos apenas a parte correspondente ao uso in-source de nosso pacote, mas ainda temos que escrever o arquivo de configuração que será distribuído e instalado com nosso pacote. O processo é semelhante ao anterior, mas um pouco mais simples:
# We redefine this variable, using this time a relative path:
set ( foo_INCLUDE_DIRS "include" )
# We write in the 'export' folder in order not to collide with
# the previous config file:
configure_package_config_file (
"<PATH TO THE CONFIG TEMPLATE>/foo-config.cmake.in"
"${CMAKE_BINARY_DIR}/export/foo-config.cmake.cmake"
INSTALL_DESTINATION "${CMAKE_CONFIG_DEST}"
PATH_VARS foo_INCLUDE_DIRS )
install (
EXPORT export
DESTINATION ${CMAKE_CONFIG_DEST} FILE "fooTargets.cmake" )
Como você pode apreciar, as configurações in-source e instaladas são simétricas, quase iguais, mas ainda temos que usar 2 arquivos de configuração diferentes.
A única parte que falta é o modelo :foo-config.cmake.in
@PACKAGE_INIT@
set_and_check ( foo_INCLUDE_DIRS "@PACKAGE_foo_INCLUDE_DIRS@")
include ( "${CMAKE_CURRENT_LIST_DIR}/fooTargets.cmake" )
@PACKAGE_INIT@
será inicializado por configure_package_config_file
e precisamos apenas
definir as variáveis do nosso projeto (usando set_and_check
, definido em @PACKAGE_INIT@
) e incluir nossos destinos.
Conclusão
Mostrei como podemos gerar dois arquivos de configuração, um para uso in-source e outro para uso “instalado”. No entanto, os dois arquivos usam o mesmo modelo e a criação de ambos os arquivos é quase idêntica. Espero que no futuro possamos usar o mesmo arquivo de configuração para os dois casos. Também no futuro acho que não precisaremos definir nossa variável, tornando-a ainda mais simples.*_INCLUDE_DIRS
Espero que o protocolo tenha sido útil! Como você gera seus arquivos de configuração? Pode-se fazer melhor?