A che serve find_package () se è necessario specificare CMAKE_MODULE_PATH comunque?


167

Sto cercando di far funzionare un sistema di build multipiattaforma con CMake. Ora il software ha alcune dipendenze. Li ho compilati io stesso e li ho installati sul mio sistema.

Alcuni file di esempio che sono stati installati:

-- 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

Ora CMake ha un file find_package()che apre un Find*.cmakefile e cerca la libreria sul sistema e definisce alcune variabili come SomeLib_FOUNDecc.

My CMakeLists.txt contiene qualcosa del genere:

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

Il primo comando definisce dove CMake cerca dopo Find*.cmakee ho aggiunto la directory di SomeLibdove è FindSomeLib.cmakepossibile trovare, quindi find_package()funziona come previsto.

Ma questo è un po 'strano perché uno dei motivi per cui find_package()esiste è quello di allontanarsi da percorsi codificati in modo non multipiattaforma.

Come si fa di solito? Devo copiare la cmake/directory di SomeLibnel mio progetto e impostare CMAKE_MODULE_PATHrelativamente?


Questo schema mi sembra molto strano. Le librerie che usano CMake non dovrebbero esporre il loro modulo 'trova' in questo modo. Come ti è venuto in mente un modo per trovare quel "SomeLib"? E quale lib è?
SirDarius,

2
Qualcosa di simile viene fatto in cmake.org/Wiki/… . Ed è OGRE.
MarcDefiant,

2
La sezione a cui ti colleghi menziona questo: "Poiché CMake (attualmente) non lo spedisce, dovrai spedirlo all'interno del tuo progetto." Questo è quello che ho fatto in flvmeta per trovare LibYAML (vedi github.com/noirotm/flvmeta/tree/master/cmake/modules ). Il percorso del modulo punta a questa directory, all'interno del mio progetto.
SirDarius,

3
Di solito copio i moduli FindXXX nel mio progetto e imposto CMAKE_MODULE_PATH (se quei moduli non sono presenti in CMake ovviamente), ho visto questo schema molte volte in altri progetti
szx

Risposte:


214

Il comando find_packageha due modalità: Modulemodalità e Configmodalità. Stai tentando di utilizzare la Modulemodalità quando in realtà hai bisogno della Configmodalità.

Modalità modulo

Find<package>.cmakefile situato all'interno del progetto. Qualcosa come questo:

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

CMakeLists.txt soddisfare:

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})

Si noti che CMAKE_MODULE_PATHha priorità alta e può essere utile quando è necessario riscrivere il Find<package>.cmakefile standard .

Modalità di configurazione (installa)

<package>Config.cmakefile situato all'esterno e prodotto dal install comando di un altro progetto ( Fooad esempio).

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)

Versione semplificata del file di configurazione:

> 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}")

Di default progetto installato nella CMAKE_INSTALL_PREFIXdirectory:

> 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

Modalità di configurazione (uso)

Utilizzare find_package(... CONFIG)per includere FooConfig.cmakecon destinazione importata 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

Si noti che la destinazione importata è altamente configurabile. Vedere la mia risposta .

Aggiornare


1
La tua risposta è fantastica Tuttavia, l'esempio su github è più complesso che può essere IMO. Nel caso comune in cui una sottodirectory (modulo) esporta un singolo artefatto, diciamo una lib con le intestazioni, non è necessario generare * Config.cmake personalizzato. Di conseguenza, la configurazione può essere notevolmente ridotta. Penso che farò anch'io un esempio simile.
Dimitris,

2
@Dimitris Sì, può essere semplificato un po '. Ho aggiornato l'esempio di github quindi ora non lo usa configure_package_config_file. A proposito, se hai altri suggerimenti, puoi inviarmi una richiesta pull.

1
@rusio Ecco il mio esempio . Supporta build monolitiche (tutti i moduli dalla cartella principale) o build autonome (ogni modulo separatamente, richiede installazione).
Dimitris,

1
@Dimitris Ok, ora vedo. Di solito il file che "ottimizzi" serve per caricare cose extra come find_dependency . Penso che sia un buon modello per iniziare, quindi lo terrò anche se in realtà non viene utilizzato. Il resto del codice sembra più semplice perché mancano alcune funzionalità come versione, esportazione per dll, layout con bin/lib(provare a installare eseguibile ed eseguirlo su Windows). E gli spazi dei nomi sembrano molto belli, quindi li terrò anche io :) Inoltre ho aggiunto monolithicbuild.

1
Ognuno dei tuoi esempi mi è stato molto utile. Grazie ad entrambi!
zmb,

2

Se stai correndo cmakeper generare SomeLibte stesso (ad esempio come parte di un superbuild), considera l'utilizzo del Registro pacchetti utente . Ciò non richiede percorsi codificati ed è multipiattaforma. Su Windows (incluso mingw64) funziona tramite il registro. Se si esamina il modo in cui l'elenco dei prefissi di installazione è costruito dalla CONFIGmodalità del comando find_packages () , si noterà che il registro pacchetti utente è uno degli elementi.

Breve guida

Associa gli obiettivi di SomeLibcui hai bisogno al di fuori di quel progetto esterno aggiungendoli a un set di esportazione nei CMakeLists.txtfile in cui sono stati creati:

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

Crea un XXXConfig.cmakefile per SomeLibal suo interno ${CMAKE_CURRENT_BUILD_DIR}e archivia questa posizione nel registro pacchetti utente aggiungendo due chiamate a export () a quelle CMakeLists.txtassociate a SomeLib:

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

find_package(SomeLib REQUIRED)Emetti il tuo comando nel CMakeLists.txtfile del progetto che dipende SomeLibsenza che i "percorsi codificati non multipiattaforma" armeggino con il CMAKE_MODULE_PATH.

Quando potrebbe essere l'approccio giusto

Questo approccio è probabilmente più adatto a situazioni in cui non utilizzerai mai il tuo software a valle della directory di compilazione (ad esempio, stai compilando in modo incrociato e non installi mai nulla sul tuo computer, o stai costruendo il software solo per eseguire test in la directory build), poiché crea un collegamento a un file .cmake nell'output "build", che può essere temporaneo.

Ma se non stai mai installando SomeLibnel tuo flusso di lavoro, la chiamata EXPORT(PACKAGE <name>)ti consente di evitare il percorso hardcoded. E, naturalmente, se stai installando SomeLib, probabilmente conosci la tua piattaforma CMAKE_MODULE_PATH, ecc., Quindi l'eccellente risposta di @ user2288008 ti coprirà.


1

Non è necessario specificare il percorso del modulo in sé. CMake viene fornito con il proprio set di script find_package integrati e la loro posizione è nel CMAKE_MODULE_PATH predefinito.

Il caso d'uso più normale per i progetti dipendenti che sono stati CMakeified sarebbe usare il comando external_project di CMake e quindi includere il file Use [Project] .cmake dal sottoprogetto. Se hai solo bisogno dello script Trova [Progetto] .cmake, copialo dal sottoprogetto e nel codice sorgente del tuo progetto, quindi non dovrai aumentare CMAKE_MODULE_PATH per trovare il sottoprogetto a livello di sistema.


12
their location is in the default CMAKE_MODULE_PATHper impostazione predefinita CMAKE_MODULE_PATHè vuoto

Posso confermare il commento di @ user2288008 nel 2018. CMAKE_MODULE_PATHè vuoto su Windows.
Jeroen,

È una variabile specifica del progetto, per i moduli spediti con il tuo progetto. "Per impostazione predefinita è vuoto, è progettato per essere impostato dal progetto." cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html
Farway

1

Come si fa di solito? Devo copiare la cmake/directory di SomeLib nel mio progetto e impostare relativamente CMAKE_MODULE_PATH?

Se non ti fidi che CMake abbia quel modulo, allora - sì, fallo - in un certo senso: copia il find_SomeLib.cmakee le sue dipendenze nella tua cmake/directory. Questo è ciò che faccio come fallback. È una brutta soluzione però.

Si noti che i FindFoo.cmakemoduli sono ciascuno una sorta di ponte tra dipendenza dalla piattaforma e indipendenza dalla piattaforma: guardano in vari luoghi specifici della piattaforma per ottenere percorsi in variabili il cui nome è indipendente dalla piattaforma.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.