CMake package config files

Issue #235 resolved
Mathieu Guigue created an issue

When developing new projects that uses midas headers (midas.h) or libraries using cmake, one has to manually define the list of libraries to be included:

set(MIDAS_LIBRARIES) 
list(APPEND MIDAS_LIBRARIES      
     $ENV{MIDASSYS}/lib/libmfe.a     
     $ENV{MIDASSYS}/lib/libmidas.a 
)

Generally other packages (like ROOT Boost and others) tend to provide a CMake Config file as described here: https://cmake.org/cmake/help/v3.4/module/CMakePackageConfigHelpers.html
which can be included into the cmake projects via the find_package CMake command.
This file (e.g. FindRoot.cmake in the case of ROOT) is generated from a cmake.in file and defines the paths to the additional libraries and include files during the installation of that package.
A simple example can be found here: https://github.com/project8/katydid/blob/develop/KatydidConfig.cmake.in or here https://cmake.org/cmake/help/v3.4/module/CMakePackageConfigHelpers.html#example-generating-package-files

Would it be possible to generate such file for the Midas package in order to have a better integration into other packages that depends on Midas header and library?

Comments (19)

  1. Stefan Ritt

    Good idea. If you write a midas.cmake, I’m happy to test it and include it in the distribution. Not sure if we need a cmake.in file, maybe we can direcly write a static midas.cmake file. At the moment, all is based on the MIDASSYS environment variable pointing to the midas installation. Without this, you cannot compile midas, so it must already exist in your installation. Then you can simply derive the include and library files from that. Should be straight forward.

  2. Mathieu Guigue reporter

    I see. Using MIDASSYS env (see above) is actually how I did in the project I am working on.
    I will give it a try then.

    I guess this could be done also for ROOTANA (although this is a Make project without CMAKE): is there a support for CMake project inclusion? (maybe not the right place to ask this, but I give it a shot…)

  3. dd1

    Hi, Mathieu - please do provide an example of how to use this config file. This example should be setup to run from the Bitbucket automatic build. K.O.

  4. Mathieu Guigue reporter

    Hi K.O.,
    Not sure whether you are asking to provide an actual CMake project with a bunch of C++ files or just more instructions on how to integrate this into your own CMakeLists…
    Assuming this is the second thing you are looking for, using this config file is very simple: you just need to use the following two lines in your project CMakeLists.txt file:

    list(APPEND CMAKE_MODULE_PATH <path-to-midas-project>/cmake) 
    find_package(Midas REQUIRED)
    

    The first line defines where to find the MidasConfig.cmake file: here I put the path to the original location where the config file is located.
    The second line says that CMake should find the Midas config file.

    Does it make sense?
    Cheers

    Mathieu

  5. Stefan Ritt

    Hi Mathieu

    I merged your pull request as you might have seen, since it does not have any bad side effect. Then I had to do following changes:

    • Removed “MESSAGE (status ${MIDAS_LIBRARIES})” from CMakeLists.txt since this is just some debugging code for yourself
    • Remove objlib, objlib-c-compat, mana, rmana, mfeo, manao, rmanao from MIDAS_LIBRARIES in CMakeLists.txt

    The last had to be done since some of them are only “virtual” libraries which do not end up in the midas/lib directory, and your linker would complain. Not the some of the “libs” there contain a main() function. You cannot have several main() functions in a project. It also does not make sense to link against the same library in static AND dynamic form. So in the end I have

    set(MIDAS_LIBRARIES midas mfe)
    

    Then I checked how a typical CMakeLists.txt for an end-user would look like. My normal one is:

    cmake_minimum_required(VERSION 3.0)
    project(crfe)
    
    # Check for MIDASSYS environment variable
    if (NOT DEFINED ENV{MIDASSYS})
       message(SEND_ERROR "MIDASSYS environment variable not defined.")
    endif()
    
    set(CMAKE_CXX_STANDARD 11)
    set(MIDASSYS $ENV{MIDASSYS})
    
    if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
       set(LIBS -lpthread -lutil -lrt)
    endif()
    
    add_executable(crfe crfe.cxx)
    
    target_include_directories(crfe PRIVATE ${MIDASSYS}/include)
    target_link_libraries(crfe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})
    

    With your find_package, this looks like:

    cmake_minimum_required(VERSION 3.0)
    project(crfe)
    
    # Check for MIDASSYS environment variable
    if (NOT DEFINED ENV{MIDASSYS})
       message(SEND_ERROR "MIDASSYS environment variable not defined.")
    endif()
    
    set(CMAKE_CXX_STANDARD 11)
    set(MIDASSYS $ENV{MIDASSYS})
    
    if (${CMAKE_SYSTEM_NAME} MATCHES Linux)
       set(LIBS -lpthread -lutil -lrt)
    endif()
    
    add_executable(crfe crfe.cxx)
    
    list(APPEND CMAKE_MODULE_PATH ${MIDASSYS}/cmake)
    find_package(Midas REQUIRED)
    target_include_directories(crfe PRIVATE ${MIDAS_INCLUDE_DIRS})
    target_link_directories(crfe PRIVATE ${MIDAS_LIBRARY_DIRS})
    target_link_libraries(crfe ${MIDAS_LIBRARIES} ${LIBS})
    

    Everything including add_executable is the same. So where I have

    target_include_directories(crfe PRIVATE ${MIDASSYS}/include)
    target_link_libraries(crfe ${MIDASSYS}/lib/libmfe.a ${MIDASSYS}/lib/libmidas.a ${LIBS})
    

    you have

    list(APPEND CMAKE_MODULE_PATH ${MIDASSYS}/cmake)
    find_package(Midas REQUIRED)
    target_include_directories(crfe PRIVATE ${MIDAS_INCLUDE_DIRS})
    target_link_directories(crfe PRIVATE ${MIDAS_LIBRARY_DIRS})
    target_link_libraries(crfe ${MIDAS_LIBRARIES} ${LIBS})
    

    Which is three more lines of code and one more level of complexity (Like a MidasConfig.cmake.in to maintain and document).

    So I honestly do not see the advantage of using the find_package mechanics. I understand that packages like ROOT have dozens of include and library files, but midas has been designed slim in that respect, just one library and one object file (libmfe) if you build a front-end program. You might argue that I need MIDASSYS, but you need that environment variable to compile midas in the first place, so if the libraries are there, that environment variable is there, and checking for it ensures midas is installed. That “replaces” the “REQUIRED” in the find_package.

    So what is the reason why we need find_package at all? Am I missing anything?

    Stefan

  6. Mathieu Guigue reporter

    Hi Stefan,

    Thanks very much for the detailed answer!

    Indeed it doesn’t look like much once put together, someone who knows how to integrate additional Midas libraries might prefer to avoid this.
    I didn’t catch the fact that the versions don’t look like Major.Minor.Patch, so one cannot use the find_package as a version controller too…

    Personally I spent 2 hours trying to find out the right combinations of libraries links and paths (you saw all the things I put in the files initially… 🙂 ); so I guess I should have simply asked in the issues tracker for additional documentation.
    My thought was that having a midas-config.cmake generally avoids the user to deal with the dependency specificities, making the interface easier (even if potentially longer in the cmake file).
    Indeed in your example, the number of lines might increases with the number of executables or libraries…

    It is not my case since we rely on the definition of the MIDAS_LIBRARIES and other MIDAS variables making the integration in other projects a lot easier: indeed we are using CMake Package builder (similar to https://github.com/project8/scarab/blob/ef2af248c5db7c92c84952a57759ba0f145f6cd4/cmake/PackageBuilder.cmake ) which makes the full integration (header include and libraries targets) very simple:

    list(APPEND CMAKE_MODULE_PATH $ENV{MIDASSYS}/cmake)
    find_package(Midas REQUIRED)
    if (MIDAS_FOUND)
        pbuilder_add_ext_libraries (${MIDAS_LIBRARIES})
    else (MIDAS_FOUND)
        message(FATAL "Unable to find MIDAS")
    endif (MIDAS_FOUND)
    

    And then any new target (executable or libraries) will link things to the external libraries, which is a lot of pain removal 🙂
    Maybe this is interesting for you in general for your projects…

    Does it make sense?

    Cheers

    Mathieu

  7. Stefan Ritt

    I’m still not convinced about the benefit, but this is maybe because I don’t use a CMake Package builder. So as you have seen I merged your pull request into the develop branch, and I will keep it there for the people who might need it.

    This causes however some side effect: If one does a “make cmake” in the midas directory, then this command fails if a “cmake” subdirectory exists. I personally don’t use the traditional Makefile, but people who do will complain that their workflow is broken. So you either convince KO to change his Makefile target, or you rename the “cmake” subdirectory.

    Another point: You do in the MidasConfig.cmake.in detect ROOT and add ROOT_INCLUDES. Is it wise to mix midas and root here? Maybe the ROOT integration should be done completely separate from midas, so you use your find_package(ROOT …) which then includes your root include directories.

  8. dd1

    ok, I fixed broken “make cmake”, MidasConfig.cmake is created at top level instead of in subdirectory “cmake”. Please check that this is okey. Also please add it to “make clean”. Thank you. K.O.

  9. Stefan Ritt

    Please tell this to Mathieu.

    @Mathieu Guigue I will added you to the midas developer team, so you can directly commit to develop to fix this.

    Can you please send me your email address privately, the Bitbucket user database does not find “mguigue”.

    Stefan

  10. dd1

    Thank you for your improvement to midas. As one more hurdle to jump, please post a message to the midas forum with an example and an explanation how to use this new feature. From the midas forum we will integrate it into the main documentation on the midas wiki. Thanks. K.O.

  11. Log in to comment