cmake

What use is find_package() when you need to specify CMAKE_MODULE_PATH?


I'm trying to get a cross-plattform build system working using CMake. Now the software has a few dependencies. I compiled them myself and installed them on my system.

Some example files which got installed:

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

Now CMake has a find_package() which opens a Find*.cmake file and searches after the library on the system and defines some variables like SomeLib_FOUND etc.

My CMakeLists.txt contains something like this:

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

The first command defines where CMake searches after the Find*.cmake and I added the directory of SomeLib where the FindSomeLib.cmake can be found, so find_package() works as expected.

But this is kind of weird because one of the reasons why find_package() exists is to get away from non-cross-plattform hard coded paths.

How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?


Solution

  • Command find_package has two modes: Module mode and Config mode. You are trying to use Module mode when you actually need Config mode.

    Module mode

    Find<package>.cmake file located within your project. Something like this:

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

    CMakeLists.txt content:

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

    Note that CMAKE_MODULE_PATH has high priority and may be usefull when you need to rewrite standard Find<package>.cmake file.

    Config mode (install)

    <package>Config.cmake file located outside and produced by install command of other project (Foo for example).

    foo library:

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

    Simplified version of config file:

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

    By default project installed in CMAKE_INSTALL_PREFIX directory:

    > 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
    

    Config mode (use)

    Use find_package(... CONFIG) to include FooConfig.cmake with imported target 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
    

    Note that imported target is highly configurable. See my answer.

    Update