Para que serve find_package () se você precisar especificar CMAKE_MODULE_PATH de qualquer maneira?

167

Estou tentando obter um sistema de compilação multiplataforma trabalhando com o CMake. Agora o software tem algumas dependências. Eu os compilei e os instalei no meu sistema.

Alguns arquivos de exemplo que foram instalados:

-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake

Agora o CMake possui um find_package()que abre um Find*.cmakearquivo e pesquisa a biblioteca no sistema e define algumas variáveis ​​como SomeLib_FOUNDetc.

Meu CMakeLists.txt contém algo como isto:

set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)

O primeiro comando define onde o CMake pesquisa depois do Find*.cmakee adicionei o diretório de SomeLibonde ele FindSomeLib.cmakepode ser encontrado, para que find_package()funcione conforme o esperado.

Mas isso é meio estranho, porque uma das razões pelas quais find_package()existe é fugir de caminhos codificados que não sejam de plataforma cruzada.

Como isso geralmente é feito? Devo copiar o cmake/diretório de SomeLibpara o meu projeto e definir o CMAKE_MODULE_PATHrelativamente?

MarcDefiant
fonte
Esse padrão parece muito estranho para mim. As bibliotecas que usam o CMake não devem expor seu módulo 'find' dessa maneira. Como você criou uma maneira de encontrar esse "SomeLib"? E qual é a lib?
SirDarius
2
Algo semelhante é feito em cmake.org/Wiki/… . E é OGRE.
MarcDefiant
2
A seção que você vincula menciona isso: "Como o CMake (atualmente) não o envia, você deverá enviá-lo dentro do seu projeto". Foi o que fiz no flvmeta para encontrar o LibYAML (consulte github.com/noirotm/flvmeta/tree/master/cmake/modules ). O caminho do módulo aponta para esse diretório, dentro do meu projeto.
SirDarius
3
Eu costumo copiar módulos FindXXX ao meu projeto e conjunto CMAKE_MODULE_PATH (se os módulos não presentes no CMake é claro), eu também vi esse padrão muitas vezes em outros projetos
szx

Respostas:

214

O comando find_packagepossui dois modos: Modulemode e Configmode. Você está tentando usar o Modulemodo quando realmente precisa do Configmodo.

Modo de módulo

Find<package>.cmakearquivo localizado dentro do seu projeto. Algo assim:

CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake

CMakeLists.txt conteúdo:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES

include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})

Observe que CMAKE_MODULE_PATHtem alta prioridade e pode ser útil quando você precisar reescrever o Find<package>.cmakearquivo padrão .

Modo de configuração (instalação)

<package>Config.cmakearquivo localizado fora e produzido por install comando de outro projeto (Foo por exemplo).

foo biblioteca:

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Foo)

add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)

Versão simplificada do arquivo de configuração:

> cat FooConfig.cmake 
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")

Por projeto padrão instalado no CMAKE_INSTALL_PREFIXdiretório:

> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake

Modo de configuração (uso)

Use find_package(... CONFIG)para incluir FooConfig.cmakeno destino importado foo:

> cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(Boo)

# import library target `foo`
find_package(Foo CONFIG REQUIRED)

add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a

Observe que o destino importado é altamente configurável. Veja minha resposta .

Atualizar

Comunidade
fonte
1
Sua resposta é ótima. No entanto, o exemplo no github é mais complexo do que pode ser IMO. No caso comum em que um subdiretório (módulo) exporta um único artefato, digamos uma lib junto com os cabeçalhos, você não precisa gerar * Config.cmake personalizado. Como resultado, a configuração pode ser reduzida significativamente. Acho que vou fazer um exemplo semelhante.
Dimitris
2
@ Dimitris Sim, isso pode ser simplificado um pouco. Atualizei o exemplo do github e agora ele não é usado configure_package_config_file. A propósito, se você tiver outras sugestões, pode me enviar uma solicitação de recebimento.
1
@rusio Aqui está o meu exemplo . Ele suporta compilação monolítica (todos os módulos da pasta raiz) ou compilação autônoma (cada módulo separadamente, requer instalação).
Dimitris
1
@ Dimitris Ok, agora eu vejo. Normalmente, o arquivo que você "otimiza" serve para carregar coisas extras, como find_dependency . Eu acho que é um bom modelo para começar, então vou mantê-lo, mesmo que não seja usado de fato. O restante do código parece mais simples, porque faltam algumas funcionalidades, como versão, exportação para dll, layout bin/lib(tente instalar o executável e executá-lo no Windows). E os namespaces parecem muito bonitos, então eu os manterei também :) Também adicionei monolithicbuild.
1
Cada um de seus exemplos foi muito útil para mim. Obrigado a ambos!
Zmb
2

Se você estiver executando cmakepara gerar a SomeLibsi mesmo (digamos, como parte de uma superbuild), considere usar o Registro de Pacotes do Usuário . Isso não requer caminhos codificados e é multiplataforma. No Windows (incluindo o mingw64), ele funciona através do registro. Se você examinar como a lista de prefixos de instalação é construída pelo CONFIGmodo dos find_packages () comando , verá que o Registro de Pacotes do Usuário é um dos elementos.

Breve tutorial

Associe os destinos SomeLibnecessários fora desse projeto externo, adicionando-os a um conjunto de exportação nos CMakeLists.txtarquivos em que são criados:

add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)

Crie um XXXConfig.cmakearquivo SomeLibnele ${CMAKE_CURRENT_BUILD_DIR}e armazene esse local no Registro de Pacotes do Usuário adicionando duas chamadas para exportar () ao CMakeLists.txtassociado a SomeLib:

export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib)                                                    # Store location of SomeLibConfig.cmake

Emita seu find_package(SomeLib REQUIRED)comando no CMakeLists.txtarquivo do projeto que depende SomeLibsem que os "caminhos codificados por hardware que não sejam de plataforma cruzada" mexam com o CMAKE_MODULE_PATH.

Quando pode ser a abordagem correta

Essa abordagem provavelmente é mais adequada para situações em que você nunca usará o software a jusante do diretório de construção (por exemplo, você está compilando de forma cruzada e nunca instala nada em sua máquina, ou está construindo o software apenas para executar testes no o diretório build), pois ele cria um link para um arquivo .cmake na saída "build", que pode ser temporária.

Mas se você nunca estiver realmente instalando SomeLibno seu fluxo de trabalho, a chamada EXPORT(PACKAGE <name>)permitirá que você evite o caminho codificado. E, é claro, se você estiver instalando SomeLib, provavelmente conhece sua plataforma CMAKE_MODULE_PATHetc., então a excelente resposta do @ user2288008 o ajudará a entender.

Ryan Feeley
fonte
1

Você não precisa especificar o caminho do módulo em si. O CMake é enviado com seu próprio conjunto de scripts find_package internos e sua localização é no CMAKE_MODULE_PATH padrão.

O caso de uso mais normal para projetos dependentes que foram CMakeified seria usar o comando external_project do CMake e incluir o arquivo Use [Project] .cmake do subprojeto. Se você só precisa do script Find [Project] .cmake, copie-o do subprojeto e para o código-fonte do seu próprio projeto e não precisará aumentar o CMAKE_MODULE_PATH para encontrar o subprojeto no nível do sistema.

zjm555
fonte
12
their location is in the default CMAKE_MODULE_PATHpor padrão, CMAKE_MODULE_PATHestá vazio
Pode confirmar o comentário de @ user2288008 em 2018. CMAKE_MODULE_PATHestá vazio no Windows.
Jeroen
É uma variável específica do projeto, para os módulos enviados com o seu projeto. "Por padrão, está vazio, deve ser definido pelo projeto." cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway
1

Como isso geralmente é feito? Devo copiar o cmake/diretório de SomeLib no meu projeto e definir CMAKE_MODULE_PATH relativamente?

Se você não confia no CMake para ter esse módulo, então - sim, faça isso - mais ou menos: Copie find_SomeLib.cmakee suas dependências para o seu cmake/diretório. É o que faço como substituto. É uma solução feia embora.

Observe que os FindFoo.cmakemódulos são uma espécie de ponte entre dependência de plataforma e independência de plataforma - eles procuram em vários locais específicos da plataforma para obter caminhos em variáveis ​​cujos nomes são independentes de plataforma.

einpoklum
fonte