Search code examples
c++conan

How do I make conan generate a FindXXX.cmake?


I have been creating packages with conan for all the libraries one of the projects I work on uses. I've created a conanfile.py for each and all is well. I've created a conanfile.txt for a dummy code to make sure all is working as expected. I've run

conan instal .. --build=missing

And that has compiled all the packages. I can use ${CONAN_INCLUDE_DIRS} and ${CONAN_LIBS} in my CMake files. However I would like to have conan as an optional way of doing things, using Find_package(...) as a way to obtain library locations, linking and include details.

So I was intrigued to see

New in Conan 0.6! Now conan provides automatic support for CMake find_package without creating a custom FindXXX.cmake file for each package (conan 0.5).

So I thought it should work. But no FindXXX.cmake file is generated.

Here is one of my conanfile.py as an example for OpenMPI:

from conans import ConanFile
import os
from conans.tools import download
from conans.tools import unzip
from conans import CMake
from conans import ConfigureEnvironment

class OpenMPIConan(ConanFile):
    name = "OpenMPI"
    version = "2.0.0"
    generators = "cmake"
    settings = "os", "arch", "compiler", "build_type"
    url="https://www.open-mpi.org/software/ompi/v2.0/"
    license="https://www.open-mpi.org/community/license.php"
    source_url = "https://www.open-mpi.org/software/ompi/v2.0/downloads/openmpi-2.0.0.tar.bz2"
    unzipped_path = "openmpi-2.0.0/"


    def source(self):
        self.zip_name = os.path.basename(self.source_url)
        download(self.source_url, self.zip_name)
        unzip(self.zip_name)
        os.unlink(self.zip_name)

    def build(self):
        self.run("%s/%s/configure --enable-mpi-thread-multiple --enable-mpi-cxx --prefix=%s CC=clang CXX=clang++" % (self.conanfile_directory, self.unzipped_path, self.package_folder))
        self.run("%s make -j 8 install" % env.command_line)

    def package(self):
        self.copy("*.h", dst="include", src="install/include")
        self.copy("*.lib", dst="lib", src="install/lib")
        self.copy("*.a", dst="lib", src="install/lib")

    def package_info(self):
        self.cpp_info.libs = ["mpi", "mpi_cxx"]

Why isn't there a FineOpenMPI.cmake file created? How can I make sure it gets created?

PS: If I understand correctly, there is no need for the package method.


Solution

  • EDIT: Since Conan 1.14, there is a cmake_find_package_multi generator that actually will generate for you the XXXCMake.cmake with transitive targets for your requirements. See here


    Conan donesn't create FindXXX.cmake automatically. The "new in 0.6" message means that for common packages, the FindXXX.cmake provided by CMake installation (kitware) should work.

    That's because from conan 0.6 we set the CONAN_CMAKE_MODULE_PATH and CMAKE_PREFIX_PATH variables to the root folder of the packages, so the CMake find_library function should be able to find the libraries there.

    But sadly it's not working always and we didn't document this process correctly. I will update the docs soon. It only works automatically if there is an "official" findXXX, and not in all cases, because sometimes the official findXXX.cmake file search for libraries with different file names than the build system is creating (not prepared to search file names for all settings conan can handle or library package libraries with different names, or sometimes findXXX search in fixed paths like c:\OpenSSL and things like that).

    So, in your specific case with OpenMPI there is no an official FindOpenMPI.cmake file supplied by CMake installation, so you will need to create it and add it in your conan package. Let's see an example with the ZLIB library conan's package:

    1. Create a file named FindOpenMPI.cmake and save it in your conan package root folder. I usuarlly recommend to copy the original FindXXX.cmake file from kitware if provided (folder Modules/Find***.cmake). And modify it a little to help finding our library files. If not provided (your case) it's not a problem, take a look to this example:
    find_path(ZLIB_INCLUDE_DIR NAMES zlib.h PATHS ${CONAN_INCLUDE_DIRS_ZLIB})
    find_library(ZLIB_LIBRARY NAMES ${CONAN_LIBS_ZLIB} PATHS ${CONAN_LIB_DIRS_ZLIB})
    
    set(ZLIB_FOUND TRUE)  
    set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
    set(ZLIB_LIBRARIES ${ZLIB_LIBRARY})
    mark_as_advanced(ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
    

    In the first line we are indicating to CMake the path where our headers should be found CONAN_INCLUDE_DIRS_XXX, then the same for the library names with CONAN_LIBS_XXX and the paths where the libs are CONAN_LIB_DIRS_XXX.

    1. In your conanfile.py file add the FindOpenMPI.cmake to the exports:

      exports = ["FindOpenMPI.cmake"]

    2. In the package method, copy the FindOpenMPI.cmake file to the root:

      self.copy("FindOpenMPI.cmake", ".", ".")

    I suggest to create a clean FindOpenMP.cmake file very similar to the ZLIB example we have seen above and try if it works.

    Your CMakeLists.txt could look like this:

    include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    CONAN_BASIC_SETUP()
    
    find_package("OpenMPI")
    
    IF(OpenMPI_FOUND)
        ADD_EXECUTABLE(xxxxxx source1.c)
        include_directories(${OpenMPI_INCLUDE_DIRS})
        TARGET_LINK_LIBRARIES(xxxxxx ${OpenMPI_LIBRARIES})
    ELSE()
        MESSAGE(FATAL_ERROR "OpenMPI not found")
    ENDIF()